[dev.typeparams] go/types: replace types2.Instantiate with Checker.Instantiate

This is a partial port of CL 333569 containing just changes to go/types.
Changes to the importer wil be made in a separate CL.

Change-Id: I9383e260b76402875ca6eb23c4478a6a3e8c1f0d
Reviewed-on: https://go-review.googlesource.com/c/go/+/335071
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
Rob Findley 2021-07-16 19:49:43 -04:00 committed by Robert Findley
parent 9e147c55b7
commit 41ff0aac13
7 changed files with 53 additions and 80 deletions

View file

@ -1833,7 +1833,9 @@ func TestInstantiate(t *testing.T) {
} }
// instantiation should succeed (no endless recursion) // instantiation should succeed (no endless recursion)
res := Instantiate(token.NoPos, T, []Type{Typ[Int]}) // even with a nil *Checker
var check *Checker
res := check.Instantiate(token.NoPos, T, []Type{Typ[Int]}, nil, false)
// instantiated type should point to itself // instantiated type should point to itself
if res.Underlying().(*Pointer).Elem() != res { if res.Underlying().(*Pointer).Elem() != res {

View file

@ -60,7 +60,7 @@ func (check *Checker) funcInst(x *operand, ix *typeparams.IndexExpr) {
} }
// instantiate function signature // 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 assert(res.tparams == nil) // signature is not generic anymore
if inferred { if inferred {
check.recordInferred(ix.Orig, targs, res) check.recordInferred(ix.Orig, targs, res)
@ -333,7 +333,7 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
} }
// compute result signature // 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 assert(rsig.tparams == nil) // signature is not generic anymore
check.recordInferred(call, targs, rsig) check.recordInferred(call, targs, rsig)

View file

@ -16,6 +16,7 @@ type instance struct {
base *Named // parameterized type to be instantiated base *Named // parameterized type to be instantiated
targs []Type // type arguments targs []Type // type arguments
poslist []token.Pos // position of each targ; for error reporting only 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 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 { func (t *instance) expand() Type {
v := t.value v := t.value
if v == nil { 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 { if v == nil {
v = Typ[Invalid] v = Typ[Invalid]
} }

View file

@ -9,71 +9,19 @@ import (
"go/token" "go/token"
) )
// 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 token.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 = &copy
}
// 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 // InstantiateLazy is like Instantiate, but avoids actually
// instantiating the type until needed. // instantiating the type until needed.
func (check *Checker) InstantiateLazy(pos token.Pos, typ Type, targs []Type) (res Type) { func (check *Checker) InstantiateLazy(pos token.Pos, typ Type, targs []Type, verify bool) (res Type) {
base := asNamed(typ) base := asNamed(typ)
if base == nil { if base == nil {
panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ)) panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
} }
return &instance{ return &instance{
check: check, check: check,
pos: pos, pos: pos,
base: base, base: base,
targs: targs, targs: targs,
verify: verify,
} }
} }

View file

@ -32,7 +32,7 @@ func TestSizeof(t *testing.T) {
{Chan{}, 12, 24}, {Chan{}, 12, 24},
{Named{}, 84, 160}, {Named{}, 84, 160},
{TypeParam{}, 28, 48}, {TypeParam{}, 28, 48},
{instance{}, 44, 88}, {instance{}, 48, 96},
{top{}, 0, 0}, {top{}, 0, 0},
// Objects // Objects

View file

@ -56,8 +56,24 @@ func (m *substMap) lookup(tpar *TypeParam) Type {
return tpar return tpar
} }
func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist []token.Pos) (res Type) { // Instantiate instantiates the type typ with the given type arguments
if trace { // 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 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.trace(pos, "-- instantiating %s with %s", typ, typeListString(targs))
check.indent++ check.indent++
defer func() { defer func() {
@ -73,7 +89,7 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
}() }()
} }
assert(len(poslist) <= len(targs)) assert(len(posList) <= len(targs))
// TODO(gri) What is better here: work with TypeParams, or work with TypeNames? // TODO(gri) What is better here: work with TypeParams, or work with TypeNames?
var tparams []*TypeName var tparams []*TypeName
@ -100,17 +116,19 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
// anymore; we need to set tparams to nil. // anymore; we need to set tparams to nil.
res.(*Signature).tparams = nil res.(*Signature).tparams = nil
}() }()
default: default:
check.dump("%v: cannot instantiate %v", pos, typ) // only types and functions can be generic
unreachable() // only defined types and (defined) 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 // the number of supplied types must match the number of type parameters
if len(targs) != len(tparams) { if len(targs) != len(tparams) {
// TODO(gri) provide better error message // TODO(gri) provide better error message
check.errorf(atPos(pos), _Todo, "got %d arguments but %d type parameters", len(targs), len(tparams)) if check != nil {
return Typ[Invalid] check.errorf(atPos(pos), _Todo, "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 { if len(tparams) == 0 {
@ -120,16 +138,18 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
smap := makeSubstMap(tparams, targs) smap := makeSubstMap(tparams, targs)
// check bounds // check bounds
for i, tname := range tparams { if verify {
// best position for error reporting for i, tname := range tparams {
pos := pos // best position for error reporting
if i < len(poslist) { pos := pos
pos = poslist[i] if i < len(posList) {
} pos = posList[i]
}
// stop checking bounds after the first failure // stop checking bounds after the first failure
if !check.satisfies(pos, targs[i], tname.typ.(*TypeParam), smap) { if !check.satisfies(pos, targs[i], tname.typ.(*TypeParam), smap) {
break break
}
} }
} }

View file

@ -413,12 +413,14 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) Typ
// create a new type instance rather than instantiate the type // create a new type instance rather than instantiate the type
// TODO(gri) should do argument number check here rather than // TODO(gri) should do argument number check here rather than
// when instantiating the type? // when instantiating the type?
// TODO(gri) use InstantiateLazy here (cleanup)
typ := new(instance) typ := new(instance)
def.setUnderlying(typ) def.setUnderlying(typ)
typ.check = check typ.check = check
typ.pos = ix.X.Pos() typ.pos = ix.X.Pos()
typ.base = base typ.base = base
typ.verify = true
// evaluate arguments (always) // evaluate arguments (always)
typ.targs = check.typeList(ix.Indices) typ.targs = check.typeList(ix.Indices)