aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/types2/predicates.go
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2021-10-26 10:06:55 -0700
committerRobert Griesemer <gri@golang.org>2021-11-13 00:23:16 +0000
commit39bc666430b3340c3de0e815cfc1fbfc7b2f7e99 (patch)
treef34aaec1491a173ff99a3d071a5e349cb4e4a298 /src/cmd/compile/internal/types2/predicates.go
parentb69b2f63d65609b400b4a40ae01e4a48638f050f (diff)
downloadgo-39bc666430b3340c3de0e815cfc1fbfc7b2f7e99.tar.gz
go-39bc666430b3340c3de0e815cfc1fbfc7b2f7e99.zip
cmd/compile/internal/types2: underlying type of a type parameter is its constraint interface
Until now, the type checker operated with the definition that the underlying type of a type parameter is itself. This leads to some inconcistencies and caused us to disallow type declarations where the RHS is a stand-alone type parameter. This change implements an alernative definition: the underlying type of a type parameter is the underlying type of its constraint; i.e., the underlying type of a type parameter is always an interface (because constraints must be interfaces). This matches the theory closely and also resolves some inconsistencies. For example, we don't need to prohibit stand-alone type parameters on the RHS of a type declaration (though, for the sake of keeping the tests the same, we still do in this CL). We also get a clear understanding of what it would mean to use a type assertion or type switch on a type parameter (still disabled with this CL). Finally, the declaration of a type parameter now very closely matches the definition of an ordinary type. The main consequence is that the rules for assignment need to be slightly modified: even though a type parameter is an interface, we cannot simply assign to it per the rules for interfaces: the type parameter's type is fixed for the instantiation and we need to reflect that accordingly when checking for assignability. This CL does not enable the new mode, it implements it in parallel to the existing mode; the internal flag tparamIsIface is used to switch between the modes. The changes to the code are numerous, but straight-forward: when- ever we deal with an underlying type that might be a type parameter (or newly, an interface), we need to act slightly differently. For the time being this leads to some code duplication because the code supports both modes. While some of the code for the new mode seems more complicated (e.g., when we have an interface, the code checks that it is not the underlying type of a type parameter), in reality many of the extra checks are redundant and only present because of an abundance of caution: interfaces with specific type sets are not permitted as types for ordinary variables, and so even if we were to hit those cases w/o excluding type parameters the behavior would be the same. Runs all tests with tparamIsIface enabled and disabled. Current setting: disabled. Change-Id: I7bb6453f4fe2569d92face222058fb4e17b12f25 Reviewed-on: https://go-review.googlesource.com/c/go/+/359016 Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Griesemer <gri@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
Diffstat (limited to 'src/cmd/compile/internal/types2/predicates.go')
-rw-r--r--src/cmd/compile/internal/types2/predicates.go27
1 files changed, 19 insertions, 8 deletions
diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go
index ab490372fc..62db3861ed 100644
--- a/src/cmd/compile/internal/types2/predicates.go
+++ b/src/cmd/compile/internal/types2/predicates.go
@@ -47,13 +47,10 @@ func allNumericOrString(t Type) bool { return allBasic(t, IsNumeric|IsString) }
// for all specific types of the type parameter's type set.
// allBasic(t, info) is an optimized version of isBasic(structuralType(t), info).
func allBasic(t Type, info BasicInfo) bool {
- switch u := under(t).(type) {
- case *Basic:
- return u.info&info != 0
- case *TypeParam:
- return u.is(func(t *term) bool { return t != nil && isBasic(t.typ, info) })
+ if tpar, _ := t.(*TypeParam); tpar != nil {
+ return tpar.is(func(t *term) bool { return t != nil && isBasic(t.typ, info) })
}
- return false
+ return isBasic(t, info)
}
// hasName reports whether t has a name. This includes
@@ -122,7 +119,7 @@ func comparable(T Type, seen map[Type]bool) bool {
// assume invalid types to be comparable
// to avoid follow-up errors
return t.kind != UntypedNil
- case *Pointer, *Interface, *Chan:
+ case *Pointer, *Chan:
return true
case *Struct:
for _, f := range t.fields {
@@ -133,7 +130,13 @@ func comparable(T Type, seen map[Type]bool) bool {
return true
case *Array:
return comparable(t.elem, seen)
+ case *Interface:
+ if tparamIsIface && isTypeParam(T) {
+ return t.IsComparable()
+ }
+ return true
case *TypeParam:
+ assert(!tparamIsIface)
return t.iface().IsComparable()
}
return false
@@ -144,9 +147,17 @@ func hasNil(t Type) bool {
switch u := under(t).(type) {
case *Basic:
return u.kind == UnsafePointer
- case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
+ case *Slice, *Pointer, *Signature, *Map, *Chan:
+ return true
+ case *Interface:
+ if tparamIsIface && isTypeParam(t) {
+ return u.typeSet().underIs(func(u Type) bool {
+ return u != nil && hasNil(u)
+ })
+ }
return true
case *TypeParam:
+ assert(!tparamIsIface)
return u.underIs(func(u Type) bool {
return u != nil && hasNil(u)
})