[dev.typeparams] go/types: implement types.Instantiate

This is a straightforward port of CL 314773 to go/types.

Change-Id: If9e2d6d99790d694615389acbe6ccb3c8c0bd1da
Reviewed-on: https://go-review.googlesource.com/c/go/+/324729
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Rob Findley 2021-06-03 09:29:35 -04:00 committed by Robert Findley
parent 410fa4c75b
commit d7592ab424
2 changed files with 83 additions and 11 deletions

View file

@ -0,0 +1,63 @@
// Copyright 2021 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
import (
"fmt"
"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)
}

View file

@ -311,6 +311,9 @@ func (subst *subster) typ(typ Type) Type {
embeddeds, ecopied := subst.typeList(t.embeddeds)
if mcopied || types != t.types || ecopied {
iface := &Interface{methods: methods, types: types, embeddeds: embeddeds}
if subst.check == nil {
panic("internal error: cannot instantiate interfaces yet")
}
subst.check.posMap[iface] = subst.check.posMap[t] // satisfy completeInterface requirement
subst.check.completeInterface(token.NoPos, iface)
return iface
@ -330,12 +333,14 @@ func (subst *subster) typ(typ Type) Type {
}
case *Named:
subst.check.indent++
defer func() {
subst.check.indent--
}()
dump := func(format string, args ...interface{}) {
if trace {
// dump is for debugging
dump := func(string, ...interface{}) {}
if subst.check != nil && trace {
subst.check.indent++
defer func() {
subst.check.indent--
}()
dump = func(format string, args ...interface{}) {
subst.check.trace(subst.pos, format, args...)
}
}
@ -381,10 +386,12 @@ func (subst *subster) typ(typ Type) Type {
// before creating a new named type, check if we have this one already
h := instantiatedHash(t, newTargs)
dump(">>> new type hash: %s", h)
if named, found := subst.check.typMap[h]; found {
dump(">>> found %s", named)
subst.cache[t] = named
return named
if subst.check != nil {
if named, found := subst.check.typMap[h]; found {
dump(">>> found %s", named)
subst.cache[t] = named
return named
}
}
// create a new named type and populate caches to avoid endless recursion
@ -392,7 +399,9 @@ func (subst *subster) typ(typ Type) Type {
named := subst.check.newNamed(tname, t.underlying, t.methods) // method signatures are updated lazily
named.tparams = t.tparams // new type is still parameterized
named.targs = newTargs
subst.check.typMap[h] = named
if subst.check != nil {
subst.check.typMap[h] = named
}
subst.cache[t] = named
// do the substitution