go/types: collect type parameters of types and set up corresponding scopes

Change-Id: Ie63ffcdb8e3682b75f9863cea7987561772e6809
This commit is contained in:
Robert Griesemer 2019-06-28 16:31:51 -07:00
parent aa0fabb90e
commit db132013f4
5 changed files with 51 additions and 28 deletions

View File

@ -101,7 +101,7 @@ var tests = [][]string{
// Go 2 tests (type parameters and contracts)
{"testdata/typeparams.go2"},
//{"testdata/typeinst.go2"},
{"testdata/typeinst.go2"},
{"testdata/contracts.go2"},
}

View File

@ -546,6 +546,11 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
check.validType(obj.typ, nil)
})
if obj.IsParametrized() {
assert(obj.scope != nil)
check.scope = obj.scope // push type parameter scope
}
if tdecl.Assign.IsValid() {
// type alias declaration
@ -579,6 +584,10 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
}
if obj.IsParametrized() {
check.closeScope()
}
check.addMethodDecls(obj)
}
@ -778,6 +787,7 @@ func (check *Checker) declStmt(decl ast.Decl) {
case *ast.TypeSpec:
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
obj.scope, obj.tparams = check.collectTypeParams(check.scope, s, s.TParams)
// spec: "The scope of a type identifier declared inside a function
// begins at the identifier in the TypeSpec and ends at the end of
// the innermost containing block."

View File

@ -213,6 +213,9 @@ func (*Const) isDependency() {} // a constant may be a dependency of an initiali
// A TypeName represents a name for a (defined or alias) type.
type TypeName struct {
object
// TODO(gri) For Funcs, we have the type parameters on the signature. Revisit that decision.
scope *Scope // type parameter scope; or nil
tparams []*TypeName // type parameters from left to right; or nil
}
// NewTypeName returns a new type name denoting the given typ.
@ -223,7 +226,12 @@ type TypeName struct {
// argument for NewNamed, which will set the TypeName's type as a side-
// effect.
func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}}
return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, nil, nil}
}
// IsParametrized reports whether obj is a parametrized type.
func (obj *TypeName) IsParametrized() bool {
return len(obj.tparams) > 0
}
// IsAlias reports whether obj is an alias name for a type.

View File

@ -386,6 +386,7 @@ func (check *Checker) collectObjects() {
case *ast.TypeSpec:
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
obj.scope, obj.tparams = check.collectTypeParams(pkg.scope, s, s.TParams)
check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, tdecl: s})
default:
@ -471,6 +472,28 @@ func (check *Checker) collectObjects() {
}
}
func (check *Checker) collectTypeParams(parent *Scope, node ast.Node, list *ast.FieldList) (scope *Scope, tparams []*TypeName) {
if list.NumFields() == 0 {
return
}
scope = NewScope(parent, node.Pos(), node.End(), "type parameters")
check.recordScope(node, scope)
index := 0
for _, f := range list.List {
for _, name := range f.Names {
tpar := NewTypeName(name.Pos(), check.pkg, name.Name, nil)
NewTypeParam(tpar, index) // assigns type to tpar as a side-effect
check.declare(scope, name, tpar, scope.pos)
tparams = append(tparams, tpar)
index++
}
}
return
}
// resolveBaseTypeName returns the non-alias base type name for typ, and whether
// there was a pointer indirection to get to it. The base type name must be declared
// in package scope, and there can be at most one pointer indirection. If no such type

View File

@ -145,19 +145,20 @@ func (check *Checker) definedType(e ast.Expr, def *Named) (T Type) {
// funcType type-checks a function or method type.
func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, tpar *ast.FieldList, ftyp *ast.FuncType) {
// type parameters are in a scope enclosing the function scope
// TODO(gri) should we always have this extra scope?
if tpar.NumFields() != 0 {
check.scope = NewScope(check.scope, token.NoPos, token.NoPos, "function type parameters") // TODO(gri) replace with check.openScope call
defer check.closeScope()
// TODO(gri) record this scope
// TODO(gri) get rid of fake ast.Ident - only here to satisfy collectTypeParams
scope, tparams := check.collectTypeParams(check.scope, new(ast.Ident), tpar)
if scope != nil {
// TODO(gri) push/pop both (type parameter and function) scopes
check.scope = scope
// defer check.closeScope()
}
scope := NewScope(check.scope, token.NoPos, token.NoPos, "function")
scope = NewScope(check.scope, token.NoPos, token.NoPos, "function")
// TODO(gri) should we close this scope?
scope.isFunc = true
check.recordScope(ftyp, scope)
recvList, _ := check.collectParams(scope, recvPar, false)
tparams := check.collectTypeParams(check.scope, tpar)
params, variadic := check.collectParams(scope, ftyp.Params, true)
results, _ := check.collectParams(scope, ftyp.Results, false)
@ -418,25 +419,6 @@ func (check *Checker) arrayLength(e ast.Expr) int64 {
return -1
}
func (check *Checker) collectTypeParams(scope *Scope, list *ast.FieldList) (tparams []*TypeName) {
if list == nil {
return
}
index := 0
for _, f := range list.List {
for _, name := range f.Names {
tpar := NewTypeName(name.Pos(), check.pkg, name.Name, nil)
NewTypeParam(tpar, index) // assigns type to tpar as a side-effect
check.declare(scope, name, tpar, scope.pos)
tparams = append(tparams, tpar)
index++
}
}
return
}
func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) {
if list == nil {
return