diff --git a/src/cmd/compile/internal/importer/iimport.go b/src/cmd/compile/internal/importer/iimport.go index 14e64891b8..453fa40f2d 100644 --- a/src/cmd/compile/internal/importer/iimport.go +++ b/src/cmd/compile/internal/importer/iimport.go @@ -665,7 +665,8 @@ func (r *importReader) doType(base *types2.Named) types2.Type { baseType := r.typ() // The imported instantiated type doesn't include any methods, so // we must always use the methods of the base (orig) type. - t := types2.Instantiate(pos, baseType, targs) + var check *types2.Checker // TODO provide a non-nil *Checker + t := check.Instantiate(pos, baseType, targs, nil, false) return t case unionType: diff --git a/src/cmd/compile/internal/noder/reader2.go b/src/cmd/compile/internal/noder/reader2.go index 89f224d389..92569ff843 100644 --- a/src/cmd/compile/internal/noder/reader2.go +++ b/src/cmd/compile/internal/noder/reader2.go @@ -224,7 +224,7 @@ func (r *reader2) doTyp() (res types2.Type) { obj, targs := r.obj() name := obj.(*types2.TypeName) if len(targs) != 0 { - return r.p.check.InstantiateLazy(syntax.Pos{}, name.Type(), targs) + return r.p.check.InstantiateLazy(syntax.Pos{}, name.Type(), targs, false) } return name.Type() diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go index 1c535387d4..74e3da3fe1 100644 --- a/src/cmd/compile/internal/types2/api_test.go +++ b/src/cmd/compile/internal/types2/api_test.go @@ -1862,7 +1862,9 @@ func TestInstantiate(t *testing.T) { } // instantiation should succeed (no endless recursion) - res := Instantiate(nopos, T, []Type{Typ[Int]}) + // even with a nil *Checker + var check *Checker + res := check.Instantiate(nopos, T, []Type{Typ[Int]}, nil, false) // instantiated type should point to itself if res.Underlying().(*Pointer).Elem() != res { diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go index 3377270ef8..0d9637e696 100644 --- a/src/cmd/compile/internal/types2/call.go +++ b/src/cmd/compile/internal/types2/call.go @@ -56,7 +56,7 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) { } // instantiate function signature - res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature) + res := check.Instantiate(x.Pos(), sig, targs, poslist, true).(*Signature) assert(res.tparams == nil) // signature is not generic anymore if inferred { check.recordInferred(inst, targs, res) @@ -326,7 +326,7 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T } // compute result signature - rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature) + rsig = check.Instantiate(call.Pos(), sig, targs, nil, true).(*Signature) assert(rsig.tparams == nil) // signature is not generic anymore check.recordInferred(call, targs, rsig) diff --git a/src/cmd/compile/internal/types2/instance.go b/src/cmd/compile/internal/types2/instance.go index b133fd1e65..65c2015507 100644 --- a/src/cmd/compile/internal/types2/instance.go +++ b/src/cmd/compile/internal/types2/instance.go @@ -16,6 +16,7 @@ type instance struct { 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 } @@ -25,7 +26,7 @@ type instance struct { func (t *instance) expand() Type { v := t.value if v == nil { - v = t.check.instantiate(t.pos, t.base, t.targs, t.poslist) + v = t.check.Instantiate(t.pos, t.base, t.targs, t.poslist, t.verify) if v == nil { v = Typ[Invalid] } diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index 85c897a909..b289607de6 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -9,71 +9,19 @@ import ( "fmt" ) -// Instantiate instantiates the type typ with the given type arguments. -// typ must be a *Named or a *Signature type, it must be generic, and -// its number of type parameters must match the number of provided type -// arguments. The result is a new, instantiated (not generic) type of -// the same kind (either a *Named or a *Signature). The type arguments -// are not checked against the constraints of the type parameters. -// Any methods attached to a *Named are simply copied; they are not -// instantiated. -func Instantiate(pos syntax.Pos, typ Type, targs []Type) (res Type) { - // TODO(gri) This code is basically identical to the prolog - // in Checker.instantiate. Factor. - var tparams []*TypeName - switch t := typ.(type) { - case *Named: - tparams = t.TParams() - case *Signature: - tparams = t.tparams - defer func() { - // If we had an unexpected failure somewhere don't panic below when - // asserting res.(*Signature). Check for *Signature in case Typ[Invalid] - // is returned. - if _, ok := res.(*Signature); !ok { - return - } - // If the signature doesn't use its type parameters, subst - // will not make a copy. In that case, make a copy now (so - // we can set tparams to nil w/o causing side-effects). - if t == res { - copy := *t - res = © - } - // After instantiating a generic signature, it is not generic - // anymore; we need to set tparams to nil. - res.(*Signature).tparams = nil - }() - - default: - panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ)) - } - - // the number of supplied types must match the number of type parameters - if len(targs) != len(tparams) { - panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, len(targs), len(tparams))) - } - - if len(tparams) == 0 { - return typ // nothing to do (minor optimization) - } - - smap := makeSubstMap(tparams, targs) - return (*Checker)(nil).subst(pos, typ, smap) -} - // InstantiateLazy is like Instantiate, but avoids actually // instantiating the type until needed. -func (check *Checker) InstantiateLazy(pos syntax.Pos, typ Type, targs []Type) (res Type) { +func (check *Checker) InstantiateLazy(pos syntax.Pos, typ Type, targs []Type, verify bool) (res Type) { base := asNamed(typ) if base == nil { panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ)) } return &instance{ - check: check, - pos: pos, - base: base, - targs: targs, + check: check, + pos: pos, + base: base, + targs: targs, + verify: verify, } } diff --git a/src/cmd/compile/internal/types2/sizeof_test.go b/src/cmd/compile/internal/types2/sizeof_test.go index a51d0c43d5..f7f191f629 100644 --- a/src/cmd/compile/internal/types2/sizeof_test.go +++ b/src/cmd/compile/internal/types2/sizeof_test.go @@ -33,7 +33,7 @@ func TestSizeof(t *testing.T) { {Chan{}, 12, 24}, {Named{}, 84, 160}, {TypeParam{}, 28, 48}, - {instance{}, 52, 96}, + {instance{}, 56, 104}, {top{}, 0, 0}, // Objects diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index 6e4e778b20..32cf527372 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -53,8 +53,24 @@ func (m *substMap) lookup(tpar *TypeParam) Type { return tpar } -func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, poslist []syntax.Pos) (res Type) { - if check.conf.Trace { +// Instantiate instantiates the type typ with the given type arguments +// targs. To check type constraint satisfaction, verify must be set. +// pos and posList correspond to the instantiation and type argument +// positions respectively; posList may be nil or shorter than the number +// of type arguments provided. +// typ must be a *Named or a *Signature type, and its number of type +// parameters must match the number of provided type arguments. +// The receiver (check) may be nil if and only if verify is not set. +// The result is a new, instantiated (not generic) type of the same kind +// (either a *Named or a *Signature). +// 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() { @@ -70,7 +86,7 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, poslis }() } - assert(len(poslist) <= len(targs)) + assert(len(posList) <= len(targs)) // TODO(gri) What is better here: work with TypeParams, or work with TypeNames? var tparams []*TypeName @@ -97,18 +113,19 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, poslis // anymore; we need to set tparams to nil. res.(*Signature).tparams = nil }() - default: - check.dump("%v: cannot instantiate %v", pos, typ) - unreachable() // only defined types and (defined) functions can be generic - + // only types and functions can be generic + panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ)) } // the number of supplied types must match the number of type parameters if len(targs) != len(tparams) { // TODO(gri) provide better error message - check.errorf(pos, "got %d arguments but %d type parameters", len(targs), len(tparams)) - return Typ[Invalid] + if check != nil { + check.errorf(pos, "got %d arguments but %d type parameters", len(targs), len(tparams)) + return Typ[Invalid] + } + panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, len(targs), len(tparams))) } if len(tparams) == 0 { @@ -118,15 +135,17 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, poslis smap := makeSubstMap(tparams, targs) // check bounds - for i, tname := range tparams { - // best position for error reporting - pos := pos - if i < len(poslist) { - pos = poslist[i] - } - // stop checking bounds after the first failure - if !check.satisfies(pos, targs[i], tname.typ.(*TypeParam), smap) { - break + if verify { + for i, tname := range tparams { + // best position for error reporting + pos := pos + if i < len(posList) { + pos = posList[i] + } + // stop checking bounds after the first failure + if !check.satisfies(pos, targs[i], tname.typ.(*TypeParam), smap) { + break + } } } diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index e861f7e784..d69dd3c496 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -423,12 +423,14 @@ func (check *Checker) instantiatedType(x syntax.Expr, targs []syntax.Expr, def * // create a new type instance rather than instantiate the type // TODO(gri) should do argument number check here rather than // when instantiating the type? + // TODO(gri) use InstantiateLazy here (cleanup) typ := new(instance) def.setUnderlying(typ) typ.check = check typ.pos = x.Pos() typ.base = base + typ.verify = true // evaluate arguments (always) typ.targs = check.typeList(targs)