]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile/internal/types2: merge instance and Named to eliminate...
authorRob Findley <rfindley@google.com>
Wed, 21 Jul 2021 16:12:22 +0000 (12:12 -0400)
committerRobert Findley <rfindley@google.com>
Wed, 28 Jul 2021 19:15:09 +0000 (19:15 +0000)
This is a port of CL 335929 to types2. It differs significantly from
that CL to handle lazy loading, which wasn't tested in go/types.
Additionally, the *Checker field was moved out of instance and back
onto Named. This way we can tell whether a Named type is uninstantiated
simply by checking whether Named.instance is non-nil, which simplified
the code considerably.

Fixes #46151

Change-Id: I617263bcfaa768ac5442213cecad8d567c2749fc
Reviewed-on: https://go-review.googlesource.com/c/go/+/336252
Trust: Robert Findley <rfindley@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
17 files changed:
src/cmd/compile/internal/types2/builtins.go
src/cmd/compile/internal/types2/check.go
src/cmd/compile/internal/types2/decl.go
src/cmd/compile/internal/types2/infer.go
src/cmd/compile/internal/types2/instance.go
src/cmd/compile/internal/types2/instantiate.go
src/cmd/compile/internal/types2/lookup.go
src/cmd/compile/internal/types2/named.go
src/cmd/compile/internal/types2/object.go
src/cmd/compile/internal/types2/predicates.go
src/cmd/compile/internal/types2/sanitize.go [deleted file]
src/cmd/compile/internal/types2/sizeof_test.go
src/cmd/compile/internal/types2/subst.go
src/cmd/compile/internal/types2/testdata/check/issues.go2
src/cmd/compile/internal/types2/typeparam.go
src/cmd/compile/internal/types2/typestring.go
src/cmd/compile/internal/types2/typexpr.go

index 2af2679d5ed5557749d1749ff86c6144744d6e4e..b9fcf3c898b265d4cae3aa8470e5ed56ad900945 100644 (file)
@@ -798,7 +798,7 @@ func hasVarSize(t Type) bool {
                }
        case *TypeParam:
                return true
-       case *Named, *Union, *instance, *top:
+       case *Named, *Union, *top:
                unreachable()
        }
        return false
index 071afef05878b3d2a2db42d82446163fc7e54f76..6bc965c4978883448afb94093f381dc96bba9374 100644 (file)
@@ -282,11 +282,6 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
        print("== recordUntyped ==")
        check.recordUntyped()
 
-       if check.Info != nil {
-               print("== sanitizeInfo ==")
-               sanitizeInfo(check.Info)
-       }
-
        check.pkg.complete = true
 
        // no longer needed - release memory
index 4f656e374aff3c8fd75125f7d210fd407bcecf1f..6ca8f75e9a9459ff58180989e9912011f5ae7c2c 100644 (file)
@@ -317,6 +317,8 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo {
                }
 
        case *Named:
+               t.expand()
+
                // don't touch the type if it is from a different package or the Universe scope
                // (doing so would lead to a race condition - was issue #35049)
                if t.obj.pkg != check.pkg {
@@ -349,9 +351,6 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo {
                        panic("internal error: cycle start not found")
                }
                return t.info
-
-       case *instance:
-               return check.validType(t.expand(), path)
        }
 
        return valid
@@ -557,7 +556,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
 
        // determine underlying type of named
        named.fromRHS = check.definedType(tdecl.Type, named)
-
+       assert(named.fromRHS != nil)
        // The underlying type of named may be itself a named type that is
        // incomplete:
        //
@@ -624,7 +623,8 @@ func (check *Checker) boundType(e syntax.Expr) Type {
 
        bound := check.typ(e)
        check.later(func() {
-               if _, ok := under(bound).(*Interface); !ok && bound != Typ[Invalid] {
+               u := under(bound)
+               if _, ok := u.(*Interface); !ok && u != Typ[Invalid] {
                        check.errorf(e, "%s is not an interface", bound)
                }
        })
@@ -692,7 +692,7 @@ func (check *Checker) collectMethods(obj *TypeName) {
                }
 
                if base != nil {
-                       base.expand() // TODO(mdempsky): Probably unnecessary.
+                       base.load() // TODO(mdempsky): Probably unnecessary.
                        base.methods = append(base.methods, m)
                }
        }
index b44ff7377a41d52500b04283a2cd7d9346816d8d..6e7a2177094458f9331ffa4e5db9d7d6f2a5b3b8 100644 (file)
@@ -342,9 +342,6 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
                // t must be one of w.tparams
                return t.index < len(w.tparams) && w.tparams[t.index].typ == t
 
-       case *instance:
-               return w.isParameterizedList(t.targs)
-
        default:
                unreachable()
        }
index 798d58811f536b4867c50863a3957544a3bb6bce..df0fc17ba7acc7f077c09ee7b62466cde92c6e26 100644 (file)
@@ -4,56 +4,40 @@
 
 package types2
 
+// TODO(rfindley): move this code to named.go.
+
 import "cmd/compile/internal/syntax"
 
-// An instance represents an instantiated generic type syntactically
-// (without expanding the instantiation). Type instances appear only
-// during type-checking and are replaced by their fully instantiated
-// (expanded) types before the end of type-checking.
+// instance holds position information for use in lazy instantiation.
+//
+// TODO(rfindley): come up with a better name for this type, now that its usage
+// has changed.
 type instance struct {
-       check   *Checker     // for lazy instantiation
        pos     syntax.Pos   // position of type instantiation; for error reporting only
-       base    *Named       // parameterized type to be instantiated
-       targs   []Type       // type arguments
        posList []syntax.Pos // position of each targ; for error reporting only
-       verify  bool         // if set, constraint satisfaction is verified
-       value   Type         // base[targs...] after instantiation or Typ[Invalid]; nil if not yet set
+       verify  bool         // if set, check constraint satisfaction upon instantiation
 }
 
-// expand returns the instantiated (= expanded) type of t.
-// The result is either an instantiated *Named type, or
-// Typ[Invalid] if there was an error.
-func (t *instance) expand() Type {
-       v := t.value
-       if v == nil {
-               v = t.check.Instantiate(t.pos, t.base, t.targs, t.posList, t.verify)
-               if v == nil {
-                       v = Typ[Invalid]
-               }
-               t.value = v
-       }
-       // After instantiation we must have an invalid or a *Named type.
-       if debug && v != Typ[Invalid] {
-               _ = v.(*Named)
+// expand ensures that the underlying type of n is instantiated.
+// The underlying type will be Typ[Invalid] if there was an error.
+func (n *Named) expand() {
+       if n.instance != nil {
+               // n must be loaded before instantiation, in order to have accurate
+               // tparams. This is done implicitly by the call to n.TParams, but making it
+               // explicit is harmless: load is idempotent.
+               n.load()
+               inst := n.check.instantiate(n.instance.pos, n.orig.underlying, n.TParams(), n.targs, n.instance.posList, n.instance.verify)
+               n.underlying = inst
+               n.fromRHS = inst
+               n.instance = nil
        }
-       return v
 }
 
-// expand expands a type instance into its instantiated
-// type and leaves all other types alone. expand does
-// not recurse.
+// expand expands uninstantiated named types and leaves all other types alone.
+// expand does not recurse.
 func expand(typ Type) Type {
-       if t, _ := typ.(*instance); t != nil {
-               return t.expand()
+       if t, _ := typ.(*Named); t != nil {
+               t.expand()
        }
        return typ
 }
-
-// expandf is set to expand.
-// Call expandf when calling expand causes compile-time cycle error.
-var expandf func(Type) Type
-
-func init() { expandf = expand }
-
-func (t *instance) Underlying() Type { return t }
-func (t *instance) String() string   { return TypeString(t, nil) }
index db398c656329cf16afd8af287a1cfa5bd6a02e29..1294b08490de20c2c5972e99325e6a0fe60898c2 100644 (file)
@@ -25,28 +25,6 @@ import (
 // Any methods attached to a *Named are simply copied; they are not
 // instantiated.
 func (check *Checker) Instantiate(pos syntax.Pos, typ Type, targs []Type, posList []syntax.Pos, verify bool) (res Type) {
-       if verify && check == nil {
-               panic("cannot have nil receiver if verify is set")
-       }
-
-       if check != nil && check.conf.Trace {
-               check.trace(pos, "-- instantiating %s with %s", typ, typeListString(targs))
-               check.indent++
-               defer func() {
-                       check.indent--
-                       var under Type
-                       if res != nil {
-                               // Calling under() here may lead to endless instantiations.
-                               // Test case: type T[P any] T[P]
-                               // TODO(gri) investigate if that's a bug or to be expected.
-                               under = res.Underlying()
-                       }
-                       check.trace(pos, "=> %s (under = %s)", res, under)
-               }()
-       }
-
-       assert(len(posList) <= len(targs))
-
        // TODO(gri) What is better here: work with TypeParams, or work with TypeNames?
        var tparams []*TypeName
        switch t := typ.(type) {
@@ -76,7 +54,10 @@ func (check *Checker) Instantiate(pos syntax.Pos, typ Type, targs []Type, posLis
                // only types and functions can be generic
                panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
        }
+       return check.instantiate(pos, typ, tparams, targs, posList, verify)
+}
 
+func (check *Checker) instantiate(pos syntax.Pos, typ Type, tparams []*TypeName, targs []Type, posList []syntax.Pos, verify bool) (res Type) {
        // the number of supplied types must match the number of type parameters
        if len(targs) != len(tparams) {
                // TODO(gri) provide better error message
@@ -86,6 +67,27 @@ func (check *Checker) Instantiate(pos syntax.Pos, typ Type, targs []Type, posLis
                }
                panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, len(targs), len(tparams)))
        }
+       if verify && check == nil {
+               panic("cannot have nil receiver if verify is set")
+       }
+
+       if check != nil && check.conf.Trace {
+               check.trace(pos, "-- instantiating %s with %s", typ, typeListString(targs))
+               check.indent++
+               defer func() {
+                       check.indent--
+                       var under Type
+                       if res != nil {
+                               // Calling under() here may lead to endless instantiations.
+                               // Test case: type T[P any] T[P]
+                               // TODO(gri) investigate if that's a bug or to be expected.
+                               under = res.Underlying()
+                       }
+                       check.trace(pos, "=> %s (under = %s)", res, under)
+               }()
+       }
+
+       assert(len(posList) <= len(targs))
 
        if len(tparams) == 0 {
                return typ // nothing to do (minor optimization)
@@ -115,19 +117,35 @@ func (check *Checker) Instantiate(pos syntax.Pos, typ Type, targs []Type, posLis
 // instantiating the type until needed. typ must be a *Named
 // type.
 func (check *Checker) InstantiateLazy(pos syntax.Pos, typ Type, targs []Type, posList []syntax.Pos, verify bool) Type {
-       base := asNamed(typ)
+       // Don't use asNamed here: we don't want to expand the base during lazy
+       // instantiation.
+       base := typ.(*Named)
+
        if base == nil {
                panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
        }
+       h := instantiatedHash(base, targs)
+       if check != nil {
+               // typ may already have been instantiated with identical type arguments. In
+               // that case, re-use the existing instance.
+               if named := check.typMap[h]; named != nil {
+                       return named
+               }
+       }
 
-       return &instance{
-               check:   check,
+       tname := NewTypeName(pos, base.obj.pkg, base.obj.name, nil)
+       named := check.newNamed(tname, base, nil, nil, nil) // methods and tparams are set when named is loaded.
+       named.targs = targs
+       named.instance = &instance{
                pos:     pos,
-               base:    base,
-               targs:   targs,
                posList: posList,
                verify:  verify,
        }
+
+       if check != nil {
+               check.typMap[h] = named
+       }
+       return named
 }
 
 // satisfies reports whether the type argument targ satisfies the constraint of type parameter
index ecf6926c0ab26bc4230037d9f2825289ae4f0aea..9e9d6dfb29b75ce3049b52c7449a2fb741289624 100644 (file)
@@ -119,7 +119,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
                                seen[named] = true
 
                                // look for a matching attached method
-                               named.expand()
+                               named.load()
                                if i, m := lookupMethod(named.methods, pkg, name); m != nil {
                                        // potential match
                                        // caution: method may not have a proper signature yet
index da098b58b7aba0fc65e9f43573dc261c7cda9d9f..a88aeb007793c8aad74208efcf690026233e710b 100644 (file)
@@ -10,12 +10,13 @@ import "sync"
 
 // A Named represents a named (defined) type.
 type Named struct {
-       check      *Checker    // for Named.under implementation; nilled once under has been called
+       check      *Checker
        info       typeInfo    // for cycle detection
        obj        *TypeName   // corresponding declared object
        orig       *Named      // original, uninstantiated type
        fromRHS    Type        // type (on RHS of declaration) this *Named type is derived from (for cycle reporting)
        underlying Type        // possibly a *Named during setup; never a *Named once set up completely
+       instance   *instance   // position information for lazy instantiation, or nil
        tparams    []*TypeName // type parameters, or nil
        targs      []Type      // type arguments (after instantiation), or nil
        methods    []*Func     // methods declared for this type (not the method set of this type); signatures are type-checked lazily
@@ -34,7 +35,19 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
        return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods)
 }
 
-func (t *Named) expand() *Named {
+func (t *Named) load() *Named {
+       // If t is an instantiated type, it derives its methods and tparams from its
+       // base type. Since we expect type parameters and methods to be set after a
+       // call to load, we must load the base and copy here.
+       //
+       // underlying is set when t is expanded.
+       //
+       // By convention, a type instance is loaded iff its tparams are set.
+       if len(t.targs) > 0 && t.tparams == nil {
+               t.orig.load()
+               t.tparams = t.orig.tparams
+               t.methods = t.orig.methods
+       }
        if t.resolve == nil {
                return t
        }
@@ -83,7 +96,7 @@ func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tpar
        if check != nil {
                check.later(func() {
                        switch typ.under().(type) {
-                       case *Named, *instance:
+                       case *Named:
                                panic("internal error: unexpanded underlying type")
                        }
                        typ.check = nil
@@ -104,10 +117,12 @@ func (t *Named) Orig() *Named { return t.orig }
 
 // TParams returns the type parameters of the named type t, or nil.
 // The result is non-nil for an (originally) parameterized type even if it is instantiated.
-func (t *Named) TParams() []*TypeName { return t.expand().tparams }
+func (t *Named) TParams() []*TypeName {
+       return t.load().tparams
+}
 
 // SetTParams sets the type parameters of the named type t.
-func (t *Named) SetTParams(tparams []*TypeName) { t.expand().tparams = tparams }
+func (t *Named) SetTParams(tparams []*TypeName) { t.load().tparams = tparams }
 
 // TArgs returns the type arguments after instantiation of the named type t, or nil if not instantiated.
 func (t *Named) TArgs() []Type { return t.targs }
@@ -116,10 +131,10 @@ func (t *Named) TArgs() []Type { return t.targs }
 func (t *Named) SetTArgs(args []Type) { t.targs = args }
 
 // NumMethods returns the number of explicit methods whose receiver is named type t.
-func (t *Named) NumMethods() int { return len(t.expand().methods) }
+func (t *Named) NumMethods() int { return len(t.load().methods) }
 
 // Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
-func (t *Named) Method(i int) *Func { return t.expand().methods[i] }
+func (t *Named) Method(i int) *Func { return t.load().methods[i] }
 
 // SetUnderlying sets the underlying type and marks t as complete.
 func (t *Named) SetUnderlying(underlying Type) {
@@ -129,18 +144,18 @@ func (t *Named) SetUnderlying(underlying Type) {
        if _, ok := underlying.(*Named); ok {
                panic("types2.Named.SetUnderlying: underlying type must not be *Named")
        }
-       t.expand().underlying = underlying
+       t.load().underlying = underlying
 }
 
 // AddMethod adds method m unless it is already in the method list.
 func (t *Named) AddMethod(m *Func) {
-       t.expand()
+       t.load()
        if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 {
                t.methods = append(t.methods, m)
        }
 }
 
-func (t *Named) Underlying() Type { return t.expand().underlying }
+func (t *Named) Underlying() Type { return t.load().underlying }
 func (t *Named) String() string   { return TypeString(t, nil) }
 
 // ----------------------------------------------------------------------------
@@ -153,6 +168,8 @@ func (t *Named) String() string   { return TypeString(t, nil) }
 // is detected, the result is Typ[Invalid]. If a cycle is detected and
 // n0.check != nil, the cycle is reported.
 func (n0 *Named) under() Type {
+       n0.expand()
+
        u := n0.Underlying()
 
        if u == Typ[Invalid] {
@@ -168,7 +185,7 @@ func (n0 *Named) under() Type {
        default:
                // common case
                return u
-       case *Named, *instance:
+       case *Named:
                // handled below
        }
 
@@ -199,12 +216,8 @@ func (n0 *Named) under() Type {
                var n1 *Named
                switch u1 := u.(type) {
                case *Named:
+                       u1.expand()
                        n1 = u1
-               case *instance:
-                       n1, _ = u1.expand().(*Named)
-                       if n1 == nil {
-                               u = Typ[Invalid]
-                       }
                }
                if n1 == nil {
                        break // end of chain
index 82297ff17f11ea035da3b572cade6c44a79c4081..48fd1e44de1a75d39603aabb4c4a343356f605eb 100644 (file)
@@ -475,6 +475,9 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
                if _, ok := typ.(*Basic); ok {
                        return
                }
+               if named, _ := typ.(*Named); named != nil && len(named.tparams) > 0 {
+                       writeTParamList(buf, named.tparams, qf, nil)
+               }
                if tname.IsAlias() {
                        buf.WriteString(" =")
                } else {
index f2215b36cb2ac1a0228e0a9621f81a500f65e2aa..e448ade9e57279a00d4fba6ce7264697b7f5c1c9 100644 (file)
@@ -10,7 +10,7 @@ package types2
 // isNamed may be called with types that are not fully set up.
 func isNamed(typ Type) bool {
        switch typ.(type) {
-       case *Basic, *Named, *TypeParam, *instance:
+       case *Basic, *Named, *TypeParam:
                return true
        }
        return false
@@ -21,7 +21,7 @@ func isNamed(typ Type) bool {
 func isGeneric(typ Type) bool {
        // A parameterized type is only instantiated if it doesn't have an instantiation already.
        named, _ := typ.(*Named)
-       return named != nil && named.obj != nil && named.TParams() != nil && named.targs == nil
+       return named != nil && named.obj != nil && named.targs == nil && named.TParams() != nil
 }
 
 func is(typ Type, what BasicInfo) bool {
@@ -144,8 +144,8 @@ func (p *ifacePair) identical(q *ifacePair) bool {
 // For changes to this code the corresponding changes should be made to unifier.nify.
 func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
        // types must be expanded for comparison
-       x = expandf(x)
-       y = expandf(y)
+       x = expand(x)
+       y = expand(y)
 
        if x == y {
                return true
diff --git a/src/cmd/compile/internal/types2/sanitize.go b/src/cmd/compile/internal/types2/sanitize.go
deleted file mode 100644 (file)
index 3d2323a..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package types2
-
-// sanitizeInfo walks the types contained in info to ensure that all instances
-// are expanded.
-//
-// This includes some objects that may be shared across concurrent
-// type-checking passes (such as those in the universe scope), so we are
-// careful here not to write types that are already sanitized. This avoids a
-// data race as any shared types should already be sanitized.
-func sanitizeInfo(info *Info) {
-       var s sanitizer = make(map[Type]Type)
-
-       // Note: Some map entries are not references.
-       // If modified, they must be assigned back.
-
-       for e, tv := range info.Types {
-               if typ := s.typ(tv.Type); typ != tv.Type {
-                       tv.Type = typ
-                       info.Types[e] = tv
-               }
-       }
-
-       for e, inf := range info.Inferred {
-               changed := false
-               for i, targ := range inf.TArgs {
-                       if typ := s.typ(targ); typ != targ {
-                               inf.TArgs[i] = typ
-                               changed = true
-                       }
-               }
-               if typ := s.typ(inf.Sig); typ != inf.Sig {
-                       inf.Sig = typ.(*Signature)
-                       changed = true
-               }
-               if changed {
-                       info.Inferred[e] = inf
-               }
-       }
-
-       for _, obj := range info.Defs {
-               if obj != nil {
-                       if typ := s.typ(obj.Type()); typ != obj.Type() {
-                               obj.setType(typ)
-                       }
-               }
-       }
-
-       for _, obj := range info.Uses {
-               if obj != nil {
-                       if typ := s.typ(obj.Type()); typ != obj.Type() {
-                               obj.setType(typ)
-                       }
-               }
-       }
-
-       // TODO(gri) sanitize as needed
-       // - info.Implicits
-       // - info.Selections
-       // - info.Scopes
-       // - info.InitOrder
-}
-
-type sanitizer map[Type]Type
-
-func (s sanitizer) typ(typ Type) Type {
-       if typ == nil {
-               return nil
-       }
-
-       if t, found := s[typ]; found {
-               return t
-       }
-       s[typ] = typ
-
-       switch t := typ.(type) {
-       case *Basic, *top:
-               // nothing to do
-
-       case *Array:
-               if elem := s.typ(t.elem); elem != t.elem {
-                       t.elem = elem
-               }
-
-       case *Slice:
-               if elem := s.typ(t.elem); elem != t.elem {
-                       t.elem = elem
-               }
-
-       case *Struct:
-               s.varList(t.fields)
-
-       case *Pointer:
-               if base := s.typ(t.base); base != t.base {
-                       t.base = base
-               }
-
-       case *Tuple:
-               s.tuple(t)
-
-       case *Signature:
-               s.var_(t.recv)
-               s.tuple(t.params)
-               s.tuple(t.results)
-
-       case *Union:
-               s.typeList(t.types)
-
-       case *Interface:
-               s.funcList(t.methods)
-               s.typeList(t.embeddeds)
-               // TODO(gri) do we need to sanitize type sets?
-               tset := t.typeSet()
-               s.funcList(tset.methods)
-               if types := s.typ(tset.types); types != tset.types {
-                       tset.types = types
-               }
-
-       case *Map:
-               if key := s.typ(t.key); key != t.key {
-                       t.key = key
-               }
-               if elem := s.typ(t.elem); elem != t.elem {
-                       t.elem = elem
-               }
-
-       case *Chan:
-               if elem := s.typ(t.elem); elem != t.elem {
-                       t.elem = elem
-               }
-
-       case *Named:
-               if debug && t.check != nil {
-                       panic("internal error: Named.check != nil")
-               }
-               t.expand()
-               if orig := s.typ(t.fromRHS); orig != t.fromRHS {
-                       t.fromRHS = orig
-               }
-               if under := s.typ(t.underlying); under != t.underlying {
-                       t.underlying = under
-               }
-               s.typeList(t.targs)
-               s.funcList(t.methods)
-
-       case *TypeParam:
-               if bound := s.typ(t.bound); bound != t.bound {
-                       t.bound = bound
-               }
-
-       case *instance:
-               typ = t.expand()
-               s[t] = typ
-
-       default:
-               unimplemented()
-       }
-
-       return typ
-}
-
-func (s sanitizer) var_(v *Var) {
-       if v != nil {
-               if typ := s.typ(v.typ); typ != v.typ {
-                       v.typ = typ
-               }
-       }
-}
-
-func (s sanitizer) varList(list []*Var) {
-       for _, v := range list {
-               s.var_(v)
-       }
-}
-
-func (s sanitizer) tuple(t *Tuple) {
-       if t != nil {
-               s.varList(t.vars)
-       }
-}
-
-func (s sanitizer) func_(f *Func) {
-       if f != nil {
-               if typ := s.typ(f.typ); typ != f.typ {
-                       f.typ = typ
-               }
-       }
-}
-
-func (s sanitizer) funcList(list []*Func) {
-       for _, f := range list {
-               s.func_(f)
-       }
-}
-
-func (s sanitizer) typeList(list []Type) {
-       for i, t := range list {
-               if typ := s.typ(t); typ != t {
-                       list[i] = typ
-               }
-       }
-}
index 22ef369683668e47b77c723a3b588458b1720f11..a62b7cb3e25fa07741aa3b5542e73797ae0ac8e9 100644 (file)
@@ -31,9 +31,8 @@ func TestSizeof(t *testing.T) {
                {Interface{}, 40, 80},
                {Map{}, 16, 32},
                {Chan{}, 12, 24},
-               {Named{}, 84, 160},
+               {Named{}, 88, 168},
                {TypeParam{}, 28, 48},
-               {instance{}, 56, 104},
                {top{}, 0, 0},
 
                // Objects
index 63b234a60e2253bc2b4818074e5c2ed8e11503f4..87e3e3018edc9882ab00974cf910fba3eed130c1 100644 (file)
@@ -26,12 +26,7 @@ func makeSubstMap(tpars []*TypeName, targs []Type) *substMap {
        assert(len(tpars) == len(targs))
        proj := make(map[*TypeParam]Type, len(tpars))
        for i, tpar := range tpars {
-               // We must expand type arguments otherwise *instance
-               // types end up as components in composite types.
-               // TODO(gri) explain why this causes problems, if it does
-               targ := expand(targs[i]) // possibly nil
-               targs[i] = targ
-               proj[tpar.typ.(*TypeParam)] = targ
+               proj[tpar.typ.(*TypeParam)] = targs[i]
        }
        return &substMap{targs, proj}
 }
@@ -83,6 +78,7 @@ func (check *Checker) subst(pos syntax.Pos, typ Type, smap *substMap) Type {
                // for recursive types (example: type T[P any] *T[P]).
                subst.typMap = make(map[string]*Named)
        }
+
        return subst.typ(typ)
 }
 
@@ -241,10 +237,13 @@ func (subst *subster) typ(typ Type) Type {
                named := subst.check.newNamed(tname, t, t.Underlying(), t.TParams(), t.methods) // method signatures are updated lazily
                named.targs = new_targs
                subst.typMap[h] = named
+               t.expand() // must happen after typMap update to avoid infinite recursion
 
                // do the substitution
                dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, new_targs)
                named.underlying = subst.typOrNil(t.Underlying())
+               dump(">>> underlying: %v", named.underlying)
+               assert(named.underlying != nil)
                named.fromRHS = named.underlying // for cycle detection (Checker.validType)
 
                return named
@@ -252,10 +251,6 @@ func (subst *subster) typ(typ Type) Type {
        case *TypeParam:
                return subst.smap.lookup(t)
 
-       case *instance:
-               // TODO(gri) can we avoid the expansion here and just substitute the type parameters?
-               return subst.typ(t.expand())
-
        default:
                unimplemented()
        }
index 1ede383ebe551cd413a5f7ffc5addade72d65411..e29357de0b8857d4e355f9381a58f293ea54457f 100644 (file)
@@ -74,8 +74,10 @@ func (u T2[U]) Add1() U {
     return u.s + 1
 }
 
+// TODO(rfindley): we should probably report an error here as well, not
+//                 just when the type is first instantiated.
 func NewT2[U any]() T2[U /* ERROR U has no constraints */ ] {
-    return T2[U /* ERROR U has no constraints */ ]{}
+    return T2[U]{}
 }
 
 func _() {
index 0aca227c0a7ac4925708164bed6c395e89b6a201..b66256cf00d224314f3b0d146eb48ce0d90d720c 100644 (file)
@@ -21,7 +21,8 @@ type TypeParam struct {
        id    uint64    // unique id, for debugging only
        obj   *TypeName // corresponding type name
        index int       // type parameter index in source order, starting at 0
-       bound Type      // *Named or *Interface; underlying type is always *Interface
+       // TODO(rfindley): this could also be Typ[Invalid]. Verify that this is handled correctly.
+       bound Type // *Named or *Interface; underlying type is always *Interface
 }
 
 // Obj returns the type name for the type parameter t.
index 44099133a03dd3b41f27e87f7cfb87cfc66cb458..74d2f1dc519a8be08bb0136e5062e9852510d8d8 100644 (file)
@@ -269,6 +269,9 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
                }
 
        case *Named:
+               if t.instance != nil {
+                       buf.WriteByte(instanceMarker)
+               }
                writeTypeName(buf, t.obj, qf)
                if t.targs != nil {
                        // instantiated type
@@ -294,13 +297,6 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
                }
                buf.WriteString(s + subscript(t.id))
 
-       case *instance:
-               buf.WriteByte(instanceMarker) // indicate "non-evaluated" syntactic instance
-               writeTypeName(buf, t.base.obj, qf)
-               buf.WriteByte('[')
-               writeTypeList(buf, t.targs, qf, visited)
-               buf.WriteByte(']')
-
        case *top:
                buf.WriteString("⊤")
 
index 83cefa19bae47ce988bca4a3035ff216daa7ad17..c55d5c093ab98be90274f42b2c2486af2fc99c8d 100644 (file)
@@ -446,7 +446,7 @@ func (check *Checker) instantiatedType(x syntax.Expr, targsx []syntax.Expr, def
        // make sure we check instantiation works at least once
        // and that the resulting type is valid
        check.later(func() {
-               t := typ.(*instance).expand()
+               t := expand(typ)
                check.validType(t, nil)
        })