aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDan Scales <danscales@google.com>2021-03-05 22:07:56 -0800
committerDan Scales <danscales@google.com>2021-03-10 17:36:55 +0000
commit1811aeae66bee899317403c92c83b56673919775 (patch)
tree974c886e65d96e8785accb9a6161d123b0f7ca7c /src
parent5edab39f490dd3cff7bf02101b2d37a90827fa6d (diff)
downloadgo-1811aeae66bee899317403c92c83b56673919775.tar.gz
go-1811aeae66bee899317403c92c83b56673919775.zip
cmd/compile: deal with helper generic types that add methods to T
Deal with cases like: 'type P[T any] T' (used to add methods to an arbitrary type T), In this case, P[T] has kind types.TTYPEPARAM (as does T itself), but requires more code to substitute than a simple TTYPEPARAM T. See the comment near the beginning of subster.typ() in stencil.go. Add new test absdiff.go. This test has a case for complex types (which I've commented out) that will only work when we deal better with Go builtins in generic functions (like real and imag). Remove change in fmt.go for TTYPEPARAMS that is no longer needed (since all TTYPEPARAMS have a sym) and was sometimes causing an extra prefix when formatting method names. Separate out the setting of a TTYPEPARAM bound, since it can reference the TTYPEPARAM being defined, so must be done separately. Also, we don't currently (and may not ever) need bounds after types2 typechecking. Change-Id: Id173057e0c4563b309b95e665e9c1151ead4ba77 Reviewed-on: https://go-review.googlesource.com/c/go/+/300049 Run-TryBot: Dan Scales <danscales@google.com> TryBot-Result: Go Bot <gobot@golang.org> Trust: Dan Scales <danscales@google.com> Trust: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
Diffstat (limited to 'src')
-rw-r--r--src/cmd/compile/internal/noder/stencil.go29
-rw-r--r--src/cmd/compile/internal/noder/types.go9
-rw-r--r--src/cmd/compile/internal/types/fmt.go2
-rw-r--r--src/cmd/compile/internal/types/type.go7
4 files changed, 39 insertions, 8 deletions
diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go
index 8001d6d398..071a2f44c2 100644
--- a/src/cmd/compile/internal/noder/stencil.go
+++ b/src/cmd/compile/internal/noder/stencil.go
@@ -553,7 +553,25 @@ func (subst *subster) typ(t *types.Type) *types.Type {
return subst.targs[i].Type()
}
}
- return t
+ // If t is a simple typeparam T, then t has the name/symbol 'T'
+ // and t.Underlying() == t.
+ //
+ // However, consider the type definition: 'type P[T any] T'. We
+ // might use this definition so we can have a variant of type T
+ // that we can add new methods to. Suppose t is a reference to
+ // P[T]. t has the name 'P[T]', but its kind is TTYPEPARAM,
+ // because P[T] is defined as T. If we look at t.Underlying(), it
+ // is different, because the name of t.Underlying() is 'T' rather
+ // than 'P[T]'. But the kind of t.Underlying() is also TTYPEPARAM.
+ // In this case, we do the needed recursive substitution in the
+ // case statement below.
+ if t.Underlying() == t {
+ // t is a simple typeparam that didn't match anything in tparam
+ return t
+ }
+ // t is a more complex typeparam (e.g. P[T], as above, whose
+ // definition is just T).
+ assert(t.Sym() != nil)
}
var newsym *types.Sym
@@ -591,6 +609,15 @@ func (subst *subster) typ(t *types.Type) *types.Type {
var newt *types.Type
switch t.Kind() {
+ case types.TTYPEPARAM:
+ if t.Sym() == newsym {
+ // The substitution did not change the type.
+ return t
+ }
+ // Substitute the underlying typeparam (e.g. T in P[T], see
+ // the example describing type P[T] above).
+ newt = subst.typ(t.Underlying())
+ assert(newt != t)
case types.TARRAY:
elem := t.Elem()
diff --git a/src/cmd/compile/internal/noder/types.go b/src/cmd/compile/internal/noder/types.go
index dfcf55d9c8..96bf75d594 100644
--- a/src/cmd/compile/internal/noder/types.go
+++ b/src/cmd/compile/internal/noder/types.go
@@ -180,11 +180,18 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
return types.NewInterface(g.tpkg(typ), append(embeddeds, methods...))
case *types2.TypeParam:
- tp := types.NewTypeParam(g.tpkg(typ), g.typ(typ.Bound()))
+ tp := types.NewTypeParam(g.tpkg(typ))
// Save the name of the type parameter in the sym of the type.
// Include the types2 subscript in the sym name
sym := g.pkg(typ.Obj().Pkg()).Lookup(types2.TypeString(typ, func(*types2.Package) string { return "" }))
tp.SetSym(sym)
+ // Set g.typs[typ] in case the bound methods reference typ.
+ g.typs[typ] = tp
+
+ // TODO(danscales): we don't currently need to use the bounds
+ // anywhere, so eventually we can probably remove.
+ bound := g.typ(typ.Bound())
+ *tp.Methods() = *bound.Methods()
return tp
case *types2.Tuple:
diff --git a/src/cmd/compile/internal/types/fmt.go b/src/cmd/compile/internal/types/fmt.go
index c59f62e302..e29c826bb7 100644
--- a/src/cmd/compile/internal/types/fmt.go
+++ b/src/cmd/compile/internal/types/fmt.go
@@ -318,7 +318,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
}
// Unless the 'L' flag was specified, if the type has a name, just print that name.
- if verb != 'L' && t.Sym() != nil && t != Types[t.Kind()] && t.Kind() != TTYPEPARAM {
+ if verb != 'L' && t.Sym() != nil && t != Types[t.Kind()] {
switch mode {
case fmtTypeID, fmtTypeIDName:
if verb == 'S' {
diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go
index d76d9b409f..ffaf755345 100644
--- a/src/cmd/compile/internal/types/type.go
+++ b/src/cmd/compile/internal/types/type.go
@@ -1742,12 +1742,9 @@ func NewInterface(pkg *Pkg, methods []*Field) *Type {
return t
}
-// NewTypeParam returns a new type param with the given constraint (which may
-// not really be needed except for the type checker).
-func NewTypeParam(pkg *Pkg, constraint *Type) *Type {
+// NewTypeParam returns a new type param.
+func NewTypeParam(pkg *Pkg) *Type {
t := New(TTYPEPARAM)
- constraint.wantEtype(TINTER)
- t.methods = constraint.methods
t.Extra.(*Interface).pkg = pkg
t.SetHasTParam(true)
return t