mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
go/types: import instance.expand and subst.go from dev.go2go
Changes from dev.go2go: + A potentially latent bug is fixed when nilling out tparams in an instantiated signature (the resulting type could be Typ[Invalid]) + Support for pointer designation is removed + instantiatedHash is updated to use '[]' rather than '()' + Several TODOs were added for me to follow-up on, rather than address in this CL + Error callsites are updated. Deciding on error codes and better error messages is punted to a later CL These changes can be reviewed by comparing with Patchset #1 of this CL. Change-Id: Ib5869586b8395419013010e2085cab877727d2ef Reviewed-on: https://go-review.googlesource.com/c/go/+/276253 Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Trust: Robert Findley <rfindley@google.com> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
3a912f279f
commit
c8e73489c3
4 changed files with 567 additions and 9 deletions
|
|
@ -82,6 +82,7 @@ type Checker struct {
|
|||
objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
|
||||
impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
|
||||
posMap map[*Interface][]token.Pos // maps interface types to lists of embedded interface positions
|
||||
typMap map[string]*Named // maps an instantiated named type hash to a *Named type
|
||||
pkgCnt map[string]int // counts number of imported packages with a given name (for better error messages)
|
||||
|
||||
// information collected during type-checking of a set of package files
|
||||
|
|
@ -194,6 +195,7 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
|
|||
objMap: make(map[Object]*declInfo),
|
||||
impMap: make(map[importKey]*Package),
|
||||
posMap: make(map[*Interface][]token.Pos),
|
||||
typMap: make(map[string]*Named),
|
||||
pkgCnt: make(map[string]int),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
542
src/go/types/subst.go
Normal file
542
src/go/types/subst.go
Normal file
|
|
@ -0,0 +1,542 @@
|
|||
// Copyright 2018 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.
|
||||
|
||||
// This file implements instantiation of generic types
|
||||
// through substitution of type parameters by actual
|
||||
// types.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
// TODO(rFindley) decide error codes for the errors in this file, and check
|
||||
// if error spans can be improved
|
||||
|
||||
type substMap struct {
|
||||
// The targs field is currently needed for *Named type substitution.
|
||||
// TODO(gri) rewrite that code, get rid of this field, and make this
|
||||
// struct just the map (proj)
|
||||
targs []Type
|
||||
proj map[*TypeParam]Type
|
||||
}
|
||||
|
||||
// makeSubstMap creates a new substitution map mapping tpars[i] to targs[i].
|
||||
// If targs[i] is nil, tpars[i] is not substituted.
|
||||
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
|
||||
}
|
||||
return &substMap{targs, proj}
|
||||
}
|
||||
|
||||
func (m *substMap) String() string {
|
||||
return fmt.Sprintf("%s", m.proj)
|
||||
}
|
||||
|
||||
func (m *substMap) empty() bool {
|
||||
return len(m.proj) == 0
|
||||
}
|
||||
|
||||
func (m *substMap) lookup(tpar *TypeParam) Type {
|
||||
if t := m.proj[tpar]; t != nil {
|
||||
return t
|
||||
}
|
||||
return tpar
|
||||
}
|
||||
|
||||
func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist []token.Pos) (res Type) {
|
||||
if 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:
|
||||
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:
|
||||
check.dump("%v: cannot instantiate %v", pos, typ)
|
||||
unreachable() // only defined types and (defined) functions can be generic
|
||||
}
|
||||
|
||||
// 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(atPos(pos), 0, "got %d arguments but %d type parameters", len(targs), len(tparams))
|
||||
return Typ[Invalid]
|
||||
}
|
||||
|
||||
if len(tparams) == 0 {
|
||||
return typ // nothing to do (minor optimization)
|
||||
}
|
||||
|
||||
smap := makeSubstMap(tparams, targs)
|
||||
|
||||
// check bounds
|
||||
for i, tname := range tparams {
|
||||
tpar := tname.typ.(*TypeParam)
|
||||
iface := tpar.Bound()
|
||||
if iface.Empty() {
|
||||
continue // no type bound
|
||||
}
|
||||
|
||||
targ := targs[i]
|
||||
|
||||
// best position for error reporting
|
||||
pos := pos
|
||||
if i < len(poslist) {
|
||||
pos = poslist[i]
|
||||
}
|
||||
|
||||
// The type parameter bound is parameterized with the same type parameters
|
||||
// as the instantiated type; before we can use it for bounds checking we
|
||||
// need to instantiate it with the type arguments with which we instantiate
|
||||
// the parameterized type.
|
||||
iface = check.subst(pos, iface, smap).(*Interface)
|
||||
|
||||
// targ must implement iface (methods)
|
||||
// - check only if we have methods
|
||||
check.completeInterface(token.NoPos, iface)
|
||||
if len(iface.allMethods) > 0 {
|
||||
// If the type argument is a pointer to a type parameter, the type argument's
|
||||
// method set is empty.
|
||||
// TODO(gri) is this what we want? (spec question)
|
||||
if base, isPtr := deref(targ); isPtr && asTypeParam(base) != nil {
|
||||
check.errorf(atPos(pos), 0, "%s has no methods", targ)
|
||||
break
|
||||
}
|
||||
if m, wrong := check.missingMethod(targ, iface, true); m != nil {
|
||||
// TODO(gri) needs to print updated name to avoid major confusion in error message!
|
||||
// (print warning for now)
|
||||
// Old warning:
|
||||
// check.softErrorf(pos, "%s does not satisfy %s (warning: name not updated) = %s (missing method %s)", targ, tpar.bound, iface, m)
|
||||
if m.name == "==" {
|
||||
// We don't want to report "missing method ==".
|
||||
check.softErrorf(atPos(pos), 0, "%s does not satisfy comparable", targ)
|
||||
} else if wrong != nil {
|
||||
// TODO(gri) This can still report uninstantiated types which makes the error message
|
||||
// more difficult to read then necessary.
|
||||
check.softErrorf(atPos(pos), 0,
|
||||
"%s does not satisfy %s: wrong method signature\n\tgot %s\n\twant %s",
|
||||
targ, tpar.bound, wrong, m,
|
||||
)
|
||||
} else {
|
||||
check.softErrorf(atPos(pos), 0, "%s does not satisfy %s (missing method %s)", targ, tpar.bound, m.name)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// targ's underlying type must also be one of the interface types listed, if any
|
||||
if iface.allTypes == nil {
|
||||
continue // nothing to do
|
||||
}
|
||||
|
||||
// If targ is itself a type parameter, each of its possible types, but at least one, must be in the
|
||||
// list of iface types (i.e., the targ type list must be a non-empty subset of the iface types).
|
||||
if targ := asTypeParam(targ); targ != nil {
|
||||
targBound := targ.Bound()
|
||||
if targBound.allTypes == nil {
|
||||
check.softErrorf(atPos(pos), 0, "%s does not satisfy %s (%s has no type constraints)", targ, tpar.bound, targ)
|
||||
break
|
||||
}
|
||||
for _, t := range unpackType(targBound.allTypes) {
|
||||
if !iface.isSatisfiedBy(t) {
|
||||
// TODO(gri) match this error message with the one below (or vice versa)
|
||||
check.softErrorf(atPos(pos), 0, "%s does not satisfy %s (%s type constraint %s not found in %s)", targ, tpar.bound, targ, t, iface.allTypes)
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Otherwise, targ's type or underlying type must also be one of the interface types listed, if any.
|
||||
if !iface.isSatisfiedBy(targ) {
|
||||
check.softErrorf(atPos(pos), 0, "%s does not satisfy %s (%s or %s not found in %s)", targ, tpar.bound, targ, under(targ), iface.allTypes)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return check.subst(pos, typ, smap)
|
||||
}
|
||||
|
||||
// subst returns the type typ with its type parameters tpars replaced by
|
||||
// the corresponding type arguments targs, recursively.
|
||||
// subst is functional in the sense that it doesn't modify the incoming
|
||||
// type. If a substitution took place, the result type is different from
|
||||
// from the incoming type.
|
||||
func (check *Checker) subst(pos token.Pos, typ Type, smap *substMap) Type {
|
||||
if smap.empty() {
|
||||
return typ
|
||||
}
|
||||
|
||||
// common cases
|
||||
switch t := typ.(type) {
|
||||
case *Basic:
|
||||
return typ // nothing to do
|
||||
case *TypeParam:
|
||||
return smap.lookup(t)
|
||||
}
|
||||
|
||||
// general case
|
||||
subst := subster{check, pos, make(map[Type]Type), smap}
|
||||
return subst.typ(typ)
|
||||
}
|
||||
|
||||
type subster struct {
|
||||
check *Checker
|
||||
pos token.Pos
|
||||
cache map[Type]Type
|
||||
smap *substMap
|
||||
}
|
||||
|
||||
func (subst *subster) typ(typ Type) Type {
|
||||
switch t := typ.(type) {
|
||||
case nil:
|
||||
// Call typOrNil if it's possible that typ is nil.
|
||||
panic("nil typ")
|
||||
|
||||
case *Basic, *bottom, *top:
|
||||
// nothing to do
|
||||
|
||||
case *Array:
|
||||
elem := subst.typOrNil(t.elem)
|
||||
if elem != t.elem {
|
||||
return &Array{len: t.len, elem: elem}
|
||||
}
|
||||
|
||||
case *Slice:
|
||||
elem := subst.typOrNil(t.elem)
|
||||
if elem != t.elem {
|
||||
return &Slice{elem: elem}
|
||||
}
|
||||
|
||||
case *Struct:
|
||||
if fields, copied := subst.varList(t.fields); copied {
|
||||
return &Struct{fields: fields, tags: t.tags}
|
||||
}
|
||||
|
||||
case *Pointer:
|
||||
base := subst.typ(t.base)
|
||||
if base != t.base {
|
||||
return &Pointer{base: base}
|
||||
}
|
||||
|
||||
case *Tuple:
|
||||
return subst.tuple(t)
|
||||
|
||||
case *Signature:
|
||||
// TODO(gri) rethink the recv situation with respect to methods on parameterized types
|
||||
// recv := subst.var_(t.recv) // TODO(gri) this causes a stack overflow - explain
|
||||
recv := t.recv
|
||||
params := subst.tuple(t.params)
|
||||
results := subst.tuple(t.results)
|
||||
if recv != t.recv || params != t.params || results != t.results {
|
||||
return &Signature{
|
||||
rparams: t.rparams,
|
||||
// TODO(rFindley) why can't we nil out tparams here, rather than in
|
||||
// instantiate above?
|
||||
tparams: t.tparams,
|
||||
scope: t.scope,
|
||||
recv: recv,
|
||||
params: params,
|
||||
results: results,
|
||||
variadic: t.variadic,
|
||||
}
|
||||
}
|
||||
|
||||
case *Sum:
|
||||
types, copied := subst.typeList(t.types)
|
||||
if copied {
|
||||
// Don't do it manually, with a Sum literal: the new
|
||||
// types list may not be unique and NewSum may remove
|
||||
// duplicates.
|
||||
return NewSum(types)
|
||||
}
|
||||
|
||||
case *Interface:
|
||||
methods, mcopied := subst.funcList(t.methods)
|
||||
types := t.types
|
||||
if t.types != nil {
|
||||
types = subst.typ(t.types)
|
||||
}
|
||||
embeddeds, ecopied := subst.typeList(t.embeddeds)
|
||||
if mcopied || types != t.types || ecopied {
|
||||
iface := &Interface{methods: methods, types: types, embeddeds: embeddeds}
|
||||
subst.check.posMap[iface] = subst.check.posMap[t] // satisfy completeInterface requirement
|
||||
subst.check.completeInterface(token.NoPos, iface)
|
||||
return iface
|
||||
}
|
||||
|
||||
case *Map:
|
||||
key := subst.typ(t.key)
|
||||
elem := subst.typ(t.elem)
|
||||
if key != t.key || elem != t.elem {
|
||||
return &Map{key: key, elem: elem}
|
||||
}
|
||||
|
||||
case *Chan:
|
||||
elem := subst.typ(t.elem)
|
||||
if elem != t.elem {
|
||||
return &Chan{dir: t.dir, elem: elem}
|
||||
}
|
||||
|
||||
case *Named:
|
||||
subst.check.indent++
|
||||
defer func() {
|
||||
subst.check.indent--
|
||||
}()
|
||||
dump := func(format string, args ...interface{}) {
|
||||
if trace {
|
||||
subst.check.trace(subst.pos, format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
if t.tparams == nil {
|
||||
dump(">>> %s is not parameterized", t)
|
||||
return t // type is not parameterized
|
||||
}
|
||||
|
||||
var newTargs []Type
|
||||
|
||||
if len(t.targs) > 0 {
|
||||
// already instantiated
|
||||
dump(">>> %s already instantiated", t)
|
||||
assert(len(t.targs) == len(t.tparams))
|
||||
// For each (existing) type argument targ, determine if it needs
|
||||
// to be substituted; i.e., if it is or contains a type parameter
|
||||
// that has a type argument for it.
|
||||
for i, targ := range t.targs {
|
||||
dump(">>> %d targ = %s", i, targ)
|
||||
newTarg := subst.typ(targ)
|
||||
if newTarg != targ {
|
||||
dump(">>> substituted %d targ %s => %s", i, targ, newTarg)
|
||||
if newTargs == nil {
|
||||
newTargs = make([]Type, len(t.tparams))
|
||||
copy(newTargs, t.targs)
|
||||
}
|
||||
newTargs[i] = newTarg
|
||||
}
|
||||
}
|
||||
|
||||
if newTargs == nil {
|
||||
dump(">>> nothing to substitute in %s", t)
|
||||
return t // nothing to substitute
|
||||
}
|
||||
} else {
|
||||
// not yet instantiated
|
||||
dump(">>> first instantiation of %s", t)
|
||||
// TODO(rFindley) can we instead subst the tparam types here?
|
||||
newTargs = subst.smap.targs
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// create a new named type and populate caches to avoid endless recursion
|
||||
tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil)
|
||||
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
|
||||
subst.cache[t] = named
|
||||
|
||||
// do the substitution
|
||||
dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, newTargs)
|
||||
named.underlying = subst.typOrNil(t.underlying)
|
||||
named.orig = named.underlying // for cycle detection (Checker.validType)
|
||||
|
||||
return named
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
return typ
|
||||
}
|
||||
|
||||
// TODO(gri) Eventually, this should be more sophisticated.
|
||||
// It won't work correctly for locally declared types.
|
||||
func instantiatedHash(typ *Named, targs []Type) string {
|
||||
var buf bytes.Buffer
|
||||
writeTypeName(&buf, typ.obj, nil)
|
||||
buf.WriteByte('[')
|
||||
writeTypeList(&buf, targs, nil, nil)
|
||||
buf.WriteByte(']')
|
||||
|
||||
// With respect to the represented type, whether a
|
||||
// type is fully expanded or stored as instance
|
||||
// does not matter - they are the same types.
|
||||
// Remove the instanceMarkers printed for instances.
|
||||
res := buf.Bytes()
|
||||
i := 0
|
||||
for _, b := range res {
|
||||
if b != instanceMarker {
|
||||
res[i] = b
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
return string(res[:i])
|
||||
}
|
||||
|
||||
func typeListString(list []Type) string {
|
||||
var buf bytes.Buffer
|
||||
writeTypeList(&buf, list, nil, nil)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// typOrNil is like typ but if the argument is nil it is replaced with Typ[Invalid].
|
||||
// A nil type may appear in pathological cases such as type T[P any] []func(_ T([]_))
|
||||
// where an array/slice element is accessed before it is set up.
|
||||
func (subst *subster) typOrNil(typ Type) Type {
|
||||
if typ == nil {
|
||||
return Typ[Invalid]
|
||||
}
|
||||
return subst.typ(typ)
|
||||
}
|
||||
|
||||
func (subst *subster) var_(v *Var) *Var {
|
||||
if v != nil {
|
||||
if typ := subst.typ(v.typ); typ != v.typ {
|
||||
copy := *v
|
||||
copy.typ = typ
|
||||
return ©
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (subst *subster) tuple(t *Tuple) *Tuple {
|
||||
if t != nil {
|
||||
if vars, copied := subst.varList(t.vars); copied {
|
||||
return &Tuple{vars: vars}
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (subst *subster) varList(in []*Var) (out []*Var, copied bool) {
|
||||
out = in
|
||||
for i, v := range in {
|
||||
if w := subst.var_(v); w != v {
|
||||
if !copied {
|
||||
// first variable that got substituted => allocate new out slice
|
||||
// and copy all variables
|
||||
new := make([]*Var, len(in))
|
||||
copy(new, out)
|
||||
out = new
|
||||
copied = true
|
||||
}
|
||||
out[i] = w
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (subst *subster) func_(f *Func) *Func {
|
||||
if f != nil {
|
||||
if typ := subst.typ(f.typ); typ != f.typ {
|
||||
copy := *f
|
||||
copy.typ = typ
|
||||
return ©
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (subst *subster) funcList(in []*Func) (out []*Func, copied bool) {
|
||||
out = in
|
||||
for i, f := range in {
|
||||
if g := subst.func_(f); g != f {
|
||||
if !copied {
|
||||
// first function that got substituted => allocate new out slice
|
||||
// and copy all functions
|
||||
new := make([]*Func, len(in))
|
||||
copy(new, out)
|
||||
out = new
|
||||
copied = true
|
||||
}
|
||||
out[i] = g
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (subst *subster) typeList(in []Type) (out []Type, copied bool) {
|
||||
out = in
|
||||
for i, t := range in {
|
||||
if u := subst.typ(t); u != t {
|
||||
if !copied {
|
||||
// first function that got substituted => allocate new out slice
|
||||
// and copy all functions
|
||||
new := make([]Type, len(in))
|
||||
copy(new, out)
|
||||
out = new
|
||||
copied = true
|
||||
}
|
||||
out[i] = u
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -313,7 +313,6 @@ type Interface struct {
|
|||
allTypes Type // intersection of all embedded and locally declared types (TODO(gri) need better field name)
|
||||
|
||||
obj Object // type declaration defining this interface; or nil (for better error messages)
|
||||
|
||||
}
|
||||
|
||||
// unpack unpacks a type into a list of types.
|
||||
|
|
@ -669,6 +668,14 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
|||
return typ
|
||||
}
|
||||
|
||||
func (check *Checker) NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
||||
typ := &Named{check: check, obj: obj, orig: underlying, underlying: underlying, methods: methods}
|
||||
if obj.typ == nil {
|
||||
obj.typ = typ
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
// Obj returns the type name for the named type t.
|
||||
func (t *Named) Obj() *TypeName { return t.obj }
|
||||
|
||||
|
|
@ -713,16 +720,15 @@ func (t *Named) AddMethod(m *Func) {
|
|||
type TypeParam struct {
|
||||
check *Checker // for lazy type bound completion
|
||||
id uint64 // unique id
|
||||
ptr bool // pointer designation
|
||||
obj *TypeName // corresponding type name
|
||||
index int // parameter index
|
||||
bound Type // *Named or *Interface; underlying type is always *Interface
|
||||
}
|
||||
|
||||
// NewTypeParam returns a new TypeParam.
|
||||
func (check *Checker) NewTypeParam(ptr bool, obj *TypeName, index int, bound Type) *TypeParam {
|
||||
func (check *Checker) NewTypeParam(obj *TypeName, index int, bound Type) *TypeParam {
|
||||
assert(bound != nil)
|
||||
typ := &TypeParam{check: check, id: check.nextId, ptr: ptr, obj: obj, index: index, bound: bound}
|
||||
typ := &TypeParam{check: check, id: check.nextId, obj: obj, index: index, bound: bound}
|
||||
check.nextId++
|
||||
if obj.typ == nil {
|
||||
obj.typ = typ
|
||||
|
|
@ -783,8 +789,19 @@ type instance struct {
|
|||
// The result is either an instantiated *Named type, or
|
||||
// Typ[Invalid] if there was an error.
|
||||
func (t *instance) expand() Type {
|
||||
// TODO(rFindley) add this in a follow-up CL.
|
||||
panic("not implemented")
|
||||
v := t.value
|
||||
if v == nil {
|
||||
v = t.check.instantiate(t.pos, t.base, t.targs, t.poslist)
|
||||
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)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// expand expands a type instance into its instantiated
|
||||
|
|
|
|||
|
|
@ -333,9 +333,6 @@ func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited
|
|||
prev = b
|
||||
|
||||
if t, _ := p.typ.(*TypeParam); t != nil {
|
||||
if t.ptr {
|
||||
buf.WriteByte('*')
|
||||
}
|
||||
writeType(buf, t, qf, visited)
|
||||
} else {
|
||||
buf.WriteString(p.name)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue