]> Cypherpunks.ru repositories - gostls13.git/commitdiff
[dev.typeparams] go/types: merge instance and Named to eliminate sanitization
authorRob Findley <rfindley@google.com>
Mon, 19 Jul 2021 17:11:50 +0000 (13:11 -0400)
committerRobert Findley <rfindley@google.com>
Thu, 22 Jul 2021 14:00:49 +0000 (14:00 +0000)
Storing temporary syntactic information using an *instance type forces
us to be careful not to leak references to *instance in the checker
output. This is complex and error prone, as types are written in many
places during type checking.

Instead, temporarily pin the necessary syntactic information directly to
the Named type during the type checking pass. This allows us to avoid
having to sanitize references.

This includes a couple of small, unrelated changes that were made in the
process of debugging:
 - eliminate the expandf indirection: it is no longer necessary
 - include type parameters when printing objects

For #46151

Change-Id: I767e35b289f2fea512a168997af0f861cd242175
Reviewed-on: https://go-review.googlesource.com/c/go/+/335929
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>
15 files changed:
src/go/types/check.go
src/go/types/decl.go
src/go/types/infer.go
src/go/types/instance.go
src/go/types/instantiate.go
src/go/types/named.go
src/go/types/object.go
src/go/types/predicates.go
src/go/types/sanitize.go [deleted file]
src/go/types/sizeof_test.go
src/go/types/subst.go
src/go/types/testdata/check/issues.go2
src/go/types/typeparam.go
src/go/types/typestring.go
src/go/types/typexpr.go

index 4398475501c4878620c303d78392ede51bd0fbb7..b2d076dc680fc7456e8aff21c62d4c3ba01c6287 100644 (file)
@@ -273,10 +273,6 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
 
        check.recordUntyped()
 
-       if check.Info != nil {
-               sanitizeInfo(check.Info)
-       }
-
        check.pkg.complete = true
 
        // no longer needed - release memory
index e38124f077abd0307718e48012bd2344007e2c60..1195104b59dcacf3d14ed92e85b08e431d9398f0 100644 (file)
@@ -317,6 +317,7 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo {
                }
 
        case *Named:
+               t.complete()
                // 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 +350,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
@@ -607,6 +605,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, 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:
@@ -685,7 +684,8 @@ func (check *Checker) boundType(e ast.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, _Todo, "%s is not an interface", bound)
                }
        })
index 9faf7b75203bbe2679f065e165c633b2595237bd..774d2fd158f3a15ec41954703dcca5c9d704a2eb 100644 (file)
@@ -337,9 +337,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 25f1442881d509b9d271f7c1eb4ab54772a998f7..205cb470464dec232e8f3fdb79ca4eb0c3839f7e 100644 (file)
@@ -4,56 +4,39 @@
 
 package types
 
+// TODO(rfindley): move this code to named.go.
+
 import "go/token"
 
-// 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 a Checker along with syntactic information
+// information, for use in lazy instantiation.
 type instance struct {
-       check   *Checker    // for lazy instantiation
+       check   *Checker
        pos     token.Pos   // position of type instantiation; for error reporting only
-       base    *Named      // parameterized type to be instantiated
-       targs   []Type      // type arguments
        posList []token.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
 }
 
-// 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)
+// complete ensures that the underlying type of n is instantiated.
+// The underlying type will be Typ[Invalid] if there was an error.
+// TODO(rfindley): expand would be a better name for this method, but conflicts
+// with the existing concept of lazy expansion. Need to reconcile this.
+func (n *Named) complete() {
+       if n.instance != nil && len(n.targs) > 0 && n.underlying == nil {
+               check := n.instance.check
+               inst := 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.methods = n.orig.methods
        }
-       return v
 }
 
 // expand expands a type instance into its instantiated
 // type 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.complete()
        }
        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 99ffb9e6048c4a83191d281224af4ec97e852bfe..270652149f613ac84e61fe81828bc8a19dd1e02c 100644 (file)
@@ -25,29 +25,6 @@ import (
 // Any methods attached to a *Named are simply copied; they are not
 // instantiated.
 func (check *Checker) Instantiate(pos token.Pos, typ Type, targs []Type, posList []token.Pos, verify bool) (res Type) {
-       if verify && check == nil {
-               panic("cannot have nil receiver if verify is set")
-       }
-
-       if check != nil && 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) {
        case *Named:
@@ -77,6 +54,10 @@ func (check *Checker) Instantiate(pos token.Pos, typ Type, targs []Type, posList
                panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
        }
 
+       return check.instantiate(pos, typ, tparams, targs, posList, verify)
+}
+
+func (check *Checker) instantiate(pos token.Pos, typ Type, tparams []*TypeName, targs []Type, posList []token.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,29 @@ func (check *Checker) Instantiate(pos token.Pos, typ Type, targs []Type, posList
                }
                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 && 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?
 
        if len(tparams) == 0 {
                return typ // nothing to do (minor optimization)
@@ -120,15 +124,26 @@ func (check *Checker) InstantiateLazy(pos token.Pos, typ Type, targs []Type, pos
        if base == nil {
                panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
        }
+       h := instantiatedHash(base, targs)
+       if check != nil {
+               if named := check.typMap[h]; named != nil {
+                       return named
+               }
+       }
 
-       return &instance{
+       tname := NewTypeName(pos, base.obj.pkg, base.obj.name, nil)
+       named := check.newNamed(tname, base, nil, base.tparams, base.methods) // methods are instantiated lazily
+       named.targs = targs
+       named.instance = &instance{
                check:   check,
                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 4511f395e08e12eccd7ecf0b31065a439cd610e1..a500f5663ba5557bf8ea793dac317b5c46e77b83 100644 (file)
@@ -10,7 +10,7 @@ import "sync"
 
 // A Named represents a named (defined) type.
 type Named struct {
-       check      *Checker    // for Named.under implementation; nilled once under has been called
+       instance   *instance   // syntactic information for lazy instantiation
        info       typeInfo    // for cycle detection
        obj        *TypeName   // corresponding declared object
        orig       *Named      // original, uninstantiated type
@@ -65,7 +65,13 @@ func (t *Named) expand() *Named {
 
 // newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
 func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams []*TypeName, methods []*Func) *Named {
-       typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods}
+       var inst *instance
+       if check != nil {
+               inst = &instance{
+                       check: check,
+               }
+       }
+       typ := &Named{instance: inst, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods}
        if typ.orig == nil {
                typ.orig = typ
        }
@@ -83,10 +89,10 @@ 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
+                       typ.instance = nil
                })
        }
        return typ
@@ -153,6 +159,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.complete()
+
        u := n0.Underlying()
 
        if u == Typ[Invalid] {
@@ -168,17 +176,17 @@ func (n0 *Named) under() Type {
        default:
                // common case
                return u
-       case *Named, *instance:
+       case *Named:
                // handled below
        }
 
-       if n0.check == nil {
+       if n0.instance == nil || n0.instance.check == nil {
                panic("internal error: Named.check == nil but type is incomplete")
        }
 
        // Invariant: after this point n0 as well as any named types in its
        // underlying chain should be set up when this function exits.
-       check := n0.check
+       check := n0.instance.check
 
        // If we can't expand u at this point, it is invalid.
        n := asNamed(u)
@@ -199,12 +207,8 @@ func (n0 *Named) under() Type {
                var n1 *Named
                switch u1 := u.(type) {
                case *Named:
+                       u1.complete()
                        n1 = u1
-               case *instance:
-                       n1, _ = u1.expand().(*Named)
-                       if n1 == nil {
-                               u = Typ[Invalid]
-                       }
                }
                if n1 == nil {
                        break // end of chain
index 7913008814cc9bf5115b85fc280214cfcb8e6c3d..4ea2837ea7c2312aa7e8acef35a4188ab470a392 100644 (file)
@@ -429,6 +429,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 ea2bed720a19ec75b2023d32ba133b3355ff7ca8..ce350f4470daea8ae22dc45e2fa78ffda7b6b1b8 100644 (file)
@@ -10,7 +10,7 @@ package types
 // 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
@@ -159,8 +159,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/go/types/sanitize.go b/src/go/types/sanitize.go
deleted file mode 100644 (file)
index 62b91ef..0000000
+++ /dev/null
@@ -1,206 +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 types
-
-// 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
-               }
-       }
-
-       inferred := info.Inferred
-       for e, inf := range 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 {
-                       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:
-               panic("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 8c18de8675d815d387b74d525e7e3338573f88f1..fc548f7c5891d85ffdd5b92bcb985481b97af8d4 100644 (file)
@@ -32,7 +32,6 @@ func TestSizeof(t *testing.T) {
                {Chan{}, 12, 24},
                {Named{}, 84, 160},
                {TypeParam{}, 28, 48},
-               {instance{}, 48, 96},
                {top{}, 0, 0},
 
                // Objects
index ec85a6bfc49189afb6c1334a6f469b20d30d0ca4..42be508cd91c73f8b44b41d25f1b1679925735c9 100644 (file)
@@ -29,12 +29,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}
 }
@@ -86,6 +81,7 @@ func (check *Checker) subst(pos token.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)
 }
 
@@ -248,10 +244,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 = newTargs
                subst.typMap[h] = named
+               t.complete() // must happen after typMap update to avoid infinite recursion
 
                // do the substitution
                dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, newTargs)
                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
@@ -259,10 +258,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:
                panic("unimplemented")
        }
index c57f00230340d30fb8525be409a048ac1f1e9c6a..ce0d608216351b4b72a6959b03078b5cdb676c8c 100644 (file)
@@ -81,8 +81,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 type constraints */ ] {
-    return T2[U /* ERROR U has no type constraints */ ]{}
+    return T2[U]{}
 }
 
 func _() {
index e42c24f8cbe083d0a257d05a0a9d89e0e6fe78d6..bb5b28cdf8731a9e81d8bb38ab98f0529f25afa8 100644 (file)
@@ -24,7 +24,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
 }
 
 // NewTypeParam returns a new TypeParam.  bound can be nil (and set later).
index cba678588a9b2c9fac72ebca6fb82775f1596922..ef3808230ad8823c76bbb725ef0705a069da75a0 100644 (file)
@@ -295,13 +295,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 f2c4762a6b30fc967b9235cc8259956a5b148a48..a812ba6519ae4b7dded1af4031dd20f6058713d0 100644 (file)
@@ -436,7 +436,7 @@ func (check *Checker) instantiatedType(x ast.Expr, targsx []ast.Expr, def *Named
        // 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)
        })