mirror of https://github.com/golang/go.git
go/types: Moving from *ast.Objects to types.Objects (step 2).
Completely removed *ast.Objects from being exposed by the types API. *ast.Objects are still required internally for resolution, but now the door is open for an internal-only rewrite of identifier resolution entirely at type-check time. Once that is done, ASTs can be type-checked whether they have been created via the go/parser or otherwise, and type-checking does not require *ast.Object or scope invariants to be maintained externally. R=adonovan CC=golang-dev https://golang.org/cl/7096048
This commit is contained in:
parent
1a9a63961b
commit
94878070af
|
|
@ -163,7 +163,7 @@ func processFiles(filenames []string, allFiles bool) {
|
|||
}
|
||||
|
||||
func processPackage(fset *token.FileSet, files []*ast.File) {
|
||||
_, _, err := types.Check(fset, files)
|
||||
_, err := types.Check(fset, files)
|
||||
if err != nil {
|
||||
report(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,11 +74,11 @@ var Default = Context{
|
|||
// we have the scope moved from *ast.Scope to *Scope, only *Package
|
||||
// will be returned.
|
||||
//
|
||||
func (ctxt *Context) Check(fset *token.FileSet, files []*ast.File) (*ast.Package, *Package, error) {
|
||||
func (ctxt *Context) Check(fset *token.FileSet, files []*ast.File) (*Package, error) {
|
||||
return check(ctxt, fset, files)
|
||||
}
|
||||
|
||||
// Check is shorthand for Default.Check.
|
||||
func Check(fset *token.FileSet, files []*ast.File) (*ast.Package, *Package, error) {
|
||||
func Check(fset *token.FileSet, files []*ast.File) (*Package, error) {
|
||||
return Default.Check(fset, files)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
|
|||
|
||||
case _Recover:
|
||||
x.mode = value
|
||||
x.typ = emptyInterface
|
||||
x.typ = new(Interface)
|
||||
|
||||
case _Alignof:
|
||||
x.mode = constant
|
||||
|
|
|
|||
|
|
@ -21,17 +21,50 @@ type checker struct {
|
|||
files []*ast.File
|
||||
|
||||
// lazily initialized
|
||||
pkg *Package
|
||||
pkgscope *ast.Scope
|
||||
firsterr error
|
||||
initspec map[*ast.ValueSpec]*ast.ValueSpec // "inherited" type and initialization expressions for constant declarations
|
||||
funclist []function // list of functions/methods with correct signatures and non-empty bodies
|
||||
funcsig *Signature // signature of currently typechecked function
|
||||
pos []token.Pos // stack of expr positions; debugging support, used if trace is set
|
||||
pkg *Package // current package
|
||||
firsterr error // first error encountered
|
||||
idents map[*ast.Ident]Object // maps identifiers to their unique object
|
||||
objects map[*ast.Object]Object // maps *ast.Objects to their unique object
|
||||
initspecs map[*ast.ValueSpec]*ast.ValueSpec // "inherited" type and initialization expressions for constant declarations
|
||||
methods map[*TypeName]*Scope // maps type names to associated methods
|
||||
funclist []function // list of functions/methods with correct signatures and non-empty bodies
|
||||
funcsig *Signature // signature of currently typechecked function
|
||||
pos []token.Pos // stack of expr positions; debugging support, used if trace is set
|
||||
}
|
||||
|
||||
// lookup returns the unique Object denoted by the identifier.
|
||||
// For identifiers without assigned *ast.Object, it uses the
|
||||
// checker.idents map; for identifiers with an *ast.Object it
|
||||
// uses the checker.objects map.
|
||||
//
|
||||
// TODO(gri) Once identifier resolution is done entirely by
|
||||
// the typechecker, only the idents map is needed.
|
||||
//
|
||||
func (check *checker) lookup(ident *ast.Ident) Object {
|
||||
astObj := ident.Obj
|
||||
obj := check.idents[ident]
|
||||
|
||||
if obj != nil {
|
||||
assert(astObj == nil || check.objects[astObj] == nil || check.objects[astObj] == obj)
|
||||
return obj
|
||||
}
|
||||
|
||||
if astObj == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
obj = check.objects[astObj]
|
||||
if obj == nil {
|
||||
obj = newObj(astObj)
|
||||
check.idents[ident] = obj
|
||||
check.objects[astObj] = obj
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
type function struct {
|
||||
obj *ast.Object // for debugging/tracing only
|
||||
obj *Func // for debugging/tracing only
|
||||
sig *Signature
|
||||
body *ast.BlockStmt
|
||||
}
|
||||
|
|
@ -40,32 +73,20 @@ type function struct {
|
|||
// that need to be processed after all package-level declarations
|
||||
// are typechecked.
|
||||
//
|
||||
func (check *checker) later(obj *ast.Object, sig *Signature, body *ast.BlockStmt) {
|
||||
func (check *checker) later(f *Func, sig *Signature, body *ast.BlockStmt) {
|
||||
// functions implemented elsewhere (say in assembly) have no body
|
||||
if body != nil {
|
||||
check.funclist = append(check.funclist, function{obj, sig, body})
|
||||
check.funclist = append(check.funclist, function{f, sig, body})
|
||||
}
|
||||
}
|
||||
|
||||
// declare declares an object of the given kind and name (ident) in scope;
|
||||
// decl is the corresponding declaration in the AST. An error is reported
|
||||
// if the object was declared before.
|
||||
//
|
||||
// TODO(gri) This is very similar to the declare function in go/parser; it
|
||||
// is only used to associate methods with their respective receiver base types.
|
||||
// In a future version, it might be simpler and cleaner to do all the resolution
|
||||
// in the type-checking phase. It would simplify the parser, AST, and also
|
||||
// reduce some amount of code duplication.
|
||||
//
|
||||
func (check *checker) declare(scope *ast.Scope, kind ast.ObjKind, ident *ast.Ident, decl ast.Decl) {
|
||||
assert(ident.Obj == nil) // identifier already declared or resolved
|
||||
obj := ast.NewObj(kind, ident.Name)
|
||||
obj.Decl = decl
|
||||
ident.Obj = obj
|
||||
func (check *checker) declareIdent(scope *Scope, ident *ast.Ident, obj Object) {
|
||||
assert(check.lookup(ident) == nil) // identifier already declared or resolved
|
||||
check.idents[ident] = obj
|
||||
if ident.Name != "_" {
|
||||
if alt := scope.Insert(obj); alt != nil {
|
||||
prevDecl := ""
|
||||
if pos := alt.Pos(); pos.IsValid() {
|
||||
if pos := alt.GetPos(); pos.IsValid() {
|
||||
prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos))
|
||||
}
|
||||
check.errorf(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl))
|
||||
|
|
@ -73,7 +94,7 @@ func (check *checker) declare(scope *ast.Scope, kind ast.ObjKind, ident *ast.Ide
|
|||
}
|
||||
}
|
||||
|
||||
func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident, typ ast.Expr, rhs []ast.Expr, iota int) {
|
||||
func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spec *ast.ValueSpec, iota int) {
|
||||
if len(lhs) == 0 {
|
||||
check.invalidAST(pos, "missing lhs in declaration")
|
||||
return
|
||||
|
|
@ -81,38 +102,53 @@ func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident
|
|||
|
||||
// determine type for all of lhs, if any
|
||||
// (but only set it for the object we typecheck!)
|
||||
var t Type
|
||||
if typ != nil {
|
||||
t = check.typ(typ, false)
|
||||
var typ Type
|
||||
if spec.Type != nil {
|
||||
typ = check.typ(spec.Type, false)
|
||||
}
|
||||
|
||||
// len(lhs) > 0
|
||||
rhs := spec.Values
|
||||
if len(lhs) == len(rhs) {
|
||||
// check only lhs and rhs corresponding to obj
|
||||
var l, r ast.Expr
|
||||
for i, name := range lhs {
|
||||
if name.Obj == obj {
|
||||
if check.lookup(name) == obj {
|
||||
l = lhs[i]
|
||||
r = rhs[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
assert(l != nil)
|
||||
obj.Type = t
|
||||
switch obj := obj.(type) {
|
||||
case *Const:
|
||||
obj.Type = typ
|
||||
case *Var:
|
||||
obj.Type = typ
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
check.assign1to1(l, r, nil, true, iota)
|
||||
return
|
||||
}
|
||||
|
||||
// there must be a type or initialization expressions
|
||||
if t == nil && len(rhs) == 0 {
|
||||
if typ == nil && len(rhs) == 0 {
|
||||
check.invalidAST(pos, "missing type or initialization expression")
|
||||
t = Typ[Invalid]
|
||||
typ = Typ[Invalid]
|
||||
}
|
||||
|
||||
// if we have a type, mark all of lhs
|
||||
if t != nil {
|
||||
if typ != nil {
|
||||
for _, name := range lhs {
|
||||
name.Obj.Type = t
|
||||
switch obj := check.lookup(name).(type) {
|
||||
case *Const:
|
||||
obj.Type = typ
|
||||
case *Var:
|
||||
obj.Type = typ
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -127,82 +163,97 @@ func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident
|
|||
}
|
||||
}
|
||||
|
||||
// object typechecks an object by assigning it a type; obj.Type must be nil.
|
||||
// Callers must check obj.Type before calling object; this eliminates a call
|
||||
// for each identifier that has been typechecked already, a common scenario.
|
||||
// object typechecks an object by assigning it a type.
|
||||
//
|
||||
func (check *checker) object(obj *ast.Object, cycleOk bool) {
|
||||
assert(obj.Type == nil)
|
||||
|
||||
switch obj.Kind {
|
||||
case ast.Bad, ast.Pkg:
|
||||
func (check *checker) object(obj Object, cycleOk bool) {
|
||||
switch obj := obj.(type) {
|
||||
case *Package:
|
||||
// nothing to do
|
||||
|
||||
case ast.Con, ast.Var:
|
||||
// The obj.Data field for constants and variables is initialized
|
||||
// to the respective (hypothetical, for variables) iota value by
|
||||
// the parser. The object's fields can be in one of the following
|
||||
// states:
|
||||
// Type != nil => the constant value is Data
|
||||
// Type == nil => the object is not typechecked yet, and Data can be:
|
||||
// Data is int => Data is the value of iota for this declaration
|
||||
// Data == nil => the object's expression is being evaluated
|
||||
if obj.Data == nil {
|
||||
check.errorf(obj.Pos(), "illegal cycle in initialization of %s", obj.Name)
|
||||
case *Const:
|
||||
if obj.Type != nil {
|
||||
return // already checked
|
||||
}
|
||||
// The obj.Val field for constants is initialized to its respective
|
||||
// iota value by the parser.
|
||||
// The object's fields can be in one of the following states:
|
||||
// Type != nil => the constant value is Val
|
||||
// Type == nil => the constant is not typechecked yet, and Val can be:
|
||||
// Val is int => Val is the value of iota for this declaration
|
||||
// Val == nil => the object's expression is being evaluated
|
||||
if obj.Val == nil {
|
||||
check.errorf(obj.GetPos(), "illegal cycle in initialization of %s", obj.Name)
|
||||
obj.Type = Typ[Invalid]
|
||||
return
|
||||
}
|
||||
spec := obj.Decl.(*ast.ValueSpec)
|
||||
iota := obj.Data.(int)
|
||||
obj.Data = nil
|
||||
spec := obj.spec
|
||||
iota := obj.Val.(int)
|
||||
obj.Val = nil // mark obj as "visited" for cycle detection
|
||||
// determine spec for type and initialization expressions
|
||||
init := spec
|
||||
if len(init.Values) == 0 && obj.Kind == ast.Con {
|
||||
init = check.initspec[spec]
|
||||
if len(init.Values) == 0 {
|
||||
init = check.initspecs[spec]
|
||||
}
|
||||
check.valueSpec(spec.Pos(), obj, spec.Names, init.Type, init.Values, iota)
|
||||
check.valueSpec(spec.Pos(), obj, spec.Names, init, iota)
|
||||
|
||||
case ast.Typ:
|
||||
typ := &NamedType{AstObj: obj}
|
||||
case *Var:
|
||||
if obj.Type != nil {
|
||||
return // already checked
|
||||
}
|
||||
if obj.visited {
|
||||
check.errorf(obj.GetPos(), "illegal cycle in initialization of %s", obj.Name)
|
||||
obj.Type = Typ[Invalid]
|
||||
return
|
||||
}
|
||||
spec := obj.decl.(*ast.ValueSpec)
|
||||
obj.visited = true
|
||||
check.valueSpec(spec.Pos(), obj, spec.Names, spec, 0)
|
||||
|
||||
case *TypeName:
|
||||
if obj.Type != nil {
|
||||
return // already checked
|
||||
}
|
||||
typ := &NamedType{Obj: obj}
|
||||
obj.Type = typ // "mark" object so recursion terminates
|
||||
typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk))
|
||||
typ.Underlying = underlying(check.typ(obj.spec.Type, cycleOk))
|
||||
// typecheck associated method signatures
|
||||
if obj.Data != nil {
|
||||
scope := obj.Data.(*ast.Scope)
|
||||
if scope := check.methods[obj]; scope != nil {
|
||||
switch t := typ.Underlying.(type) {
|
||||
case *Struct:
|
||||
// struct fields must not conflict with methods
|
||||
for _, f := range t.Fields {
|
||||
if m := scope.Lookup(f.Name); m != nil {
|
||||
check.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, f.Name)
|
||||
check.errorf(m.GetPos(), "type %s has both field and method named %s", obj.Name, f.Name)
|
||||
// ok to continue
|
||||
}
|
||||
}
|
||||
case *Interface:
|
||||
// methods cannot be associated with an interface type
|
||||
for _, m := range scope.Objects {
|
||||
recv := m.Decl.(*ast.FuncDecl).Recv.List[0].Type
|
||||
for _, m := range scope.Entries {
|
||||
recv := m.(*Func).decl.Recv.List[0].Type
|
||||
check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name)
|
||||
// ok to continue
|
||||
}
|
||||
}
|
||||
// typecheck method signatures
|
||||
var methods []*Method
|
||||
for _, obj := range scope.Objects {
|
||||
mdecl := obj.Decl.(*ast.FuncDecl)
|
||||
sig := check.typ(mdecl.Type, cycleOk).(*Signature)
|
||||
params, _ := check.collectParams(mdecl.Recv, false)
|
||||
for _, obj := range scope.Entries {
|
||||
m := obj.(*Func)
|
||||
sig := check.typ(m.decl.Type, cycleOk).(*Signature)
|
||||
params, _ := check.collectParams(m.decl.Recv, false)
|
||||
sig.Recv = params[0] // the parser/assocMethod ensure there is exactly one parameter
|
||||
obj.Type = sig
|
||||
methods = append(methods, &Method{QualifiedName{nil, obj.Name}, sig})
|
||||
check.later(obj, sig, mdecl.Body)
|
||||
m.Type = sig
|
||||
methods = append(methods, &Method{QualifiedName{nil, m.Name}, sig})
|
||||
check.later(m, sig, m.decl.Body)
|
||||
}
|
||||
typ.Methods = methods
|
||||
obj.Data = nil // don't use obj.Data later, accidentally
|
||||
delete(check.methods, obj) // we don't need this scope anymore
|
||||
}
|
||||
|
||||
case ast.Fun:
|
||||
fdecl := obj.Decl.(*ast.FuncDecl)
|
||||
case *Func:
|
||||
if obj.Type != nil {
|
||||
return // already checked
|
||||
}
|
||||
fdecl := obj.decl
|
||||
// methods are typechecked when their receivers are typechecked
|
||||
if fdecl.Recv == nil {
|
||||
sig := check.typ(fdecl.Type, cycleOk).(*Signature)
|
||||
|
|
@ -215,12 +266,12 @@ func (check *checker) object(obj *ast.Object, cycleOk bool) {
|
|||
}
|
||||
|
||||
default:
|
||||
panic("unreachable")
|
||||
unreachable()
|
||||
}
|
||||
}
|
||||
|
||||
// assocInitvals associates "inherited" initialization expressions
|
||||
// with the corresponding *ast.ValueSpec in the check.initspec map
|
||||
// with the corresponding *ast.ValueSpec in the check.initspecs map
|
||||
// for constant declarations without explicit initialization expressions.
|
||||
//
|
||||
func (check *checker) assocInitvals(decl *ast.GenDecl) {
|
||||
|
|
@ -230,7 +281,7 @@ func (check *checker) assocInitvals(decl *ast.GenDecl) {
|
|||
if len(s.Values) > 0 {
|
||||
last = s
|
||||
} else {
|
||||
check.initspec[s] = last
|
||||
check.initspecs[s] = last
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -251,48 +302,36 @@ func (check *checker) assocMethod(meth *ast.FuncDecl) {
|
|||
if ptr, ok := typ.(*ast.StarExpr); ok {
|
||||
typ = ptr.X
|
||||
}
|
||||
// determine receiver base type name
|
||||
ident, ok := typ.(*ast.Ident)
|
||||
if !ok {
|
||||
// not an identifier - parser reported error already
|
||||
return // ignore this method
|
||||
}
|
||||
// determine receiver base type object
|
||||
var obj *ast.Object
|
||||
if ident, ok := typ.(*ast.Ident); ok && ident.Obj != nil {
|
||||
obj = ident.Obj
|
||||
if obj.Kind != ast.Typ {
|
||||
var tname *TypeName
|
||||
if obj := check.lookup(ident); obj != nil {
|
||||
obj, ok := obj.(*TypeName)
|
||||
if !ok {
|
||||
check.errorf(ident.Pos(), "%s is not a type", ident.Name)
|
||||
return // ignore this method
|
||||
}
|
||||
// TODO(gri) determine if obj was defined in this package
|
||||
/*
|
||||
if check.notLocal(obj) {
|
||||
check.errorf(ident.Pos(), "cannot define methods on non-local type %s", ident.Name)
|
||||
return // ignore this method
|
||||
}
|
||||
*/
|
||||
if obj.spec == nil {
|
||||
check.errorf(ident.Pos(), "cannot define method on non-local type %s", ident.Name)
|
||||
return // ignore this method
|
||||
}
|
||||
tname = obj
|
||||
} else {
|
||||
// If it's not an identifier or the identifier wasn't declared/resolved,
|
||||
// the parser/resolver already reported an error. Nothing to do here.
|
||||
// identifier not declared/resolved - parser reported error already
|
||||
return // ignore this method
|
||||
}
|
||||
// declare method in receiver base type scope
|
||||
var scope *ast.Scope
|
||||
if obj.Data != nil {
|
||||
scope = obj.Data.(*ast.Scope)
|
||||
} else {
|
||||
scope = ast.NewScope(nil)
|
||||
obj.Data = scope
|
||||
}
|
||||
check.declare(scope, ast.Fun, meth.Name, meth)
|
||||
}
|
||||
|
||||
func (check *checker) assocInitvalsOrMethod(decl ast.Decl) {
|
||||
switch d := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
if d.Tok == token.CONST {
|
||||
check.assocInitvals(d)
|
||||
}
|
||||
case *ast.FuncDecl:
|
||||
if d.Recv != nil {
|
||||
check.assocMethod(d)
|
||||
}
|
||||
scope := check.methods[tname]
|
||||
if scope == nil {
|
||||
scope = new(Scope)
|
||||
check.methods[tname] = scope
|
||||
}
|
||||
check.declareIdent(scope, meth.Name, &Func{Name: meth.Name.Name, decl: meth})
|
||||
}
|
||||
|
||||
func (check *checker) decl(decl ast.Decl) {
|
||||
|
|
@ -303,17 +342,13 @@ func (check *checker) decl(decl ast.Decl) {
|
|||
for _, spec := range d.Specs {
|
||||
switch s := spec.(type) {
|
||||
case *ast.ImportSpec:
|
||||
// nothing to do (handled by ast.NewPackage)
|
||||
// nothing to do (handled by check.resolve)
|
||||
case *ast.ValueSpec:
|
||||
for _, name := range s.Names {
|
||||
if obj := name.Obj; obj.Type == nil {
|
||||
check.object(obj, false)
|
||||
}
|
||||
check.object(check.lookup(name), false)
|
||||
}
|
||||
case *ast.TypeSpec:
|
||||
if obj := s.Name.Obj; obj.Type == nil {
|
||||
check.object(obj, false)
|
||||
}
|
||||
check.object(check.lookup(s.Name), false)
|
||||
default:
|
||||
check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
|
||||
}
|
||||
|
|
@ -323,42 +358,33 @@ func (check *checker) decl(decl ast.Decl) {
|
|||
if d.Recv != nil {
|
||||
return
|
||||
}
|
||||
obj := d.Name.Obj
|
||||
obj := check.lookup(d.Name)
|
||||
// Initialization functions don't have an object associated with them
|
||||
// since they are not in any scope. Create a dummy object for them.
|
||||
if d.Name.Name == "init" {
|
||||
assert(obj == nil) // all other functions should have an object
|
||||
obj = ast.NewObj(ast.Fun, d.Name.Name)
|
||||
obj.Decl = d
|
||||
d.Name.Obj = obj
|
||||
}
|
||||
if obj.Type == nil {
|
||||
check.object(obj, false)
|
||||
obj = &Func{Name: d.Name.Name, decl: d}
|
||||
check.idents[d.Name] = obj
|
||||
}
|
||||
check.object(obj, false)
|
||||
default:
|
||||
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
|
||||
}
|
||||
}
|
||||
|
||||
// iterate calls f for each package-level declaration.
|
||||
func (check *checker) iterate(f func(*checker, ast.Decl)) {
|
||||
for _, file := range check.files {
|
||||
for _, decl := range file.Decls {
|
||||
f(check, decl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A bailout panic is raised to indicate early termination.
|
||||
type bailout struct{}
|
||||
|
||||
func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (astpkg *ast.Package, pkg *Package, err error) {
|
||||
func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package, err error) {
|
||||
// initialize checker
|
||||
check := checker{
|
||||
ctxt: ctxt,
|
||||
fset: fset,
|
||||
files: files,
|
||||
initspec: make(map[*ast.ValueSpec]*ast.ValueSpec),
|
||||
ctxt: ctxt,
|
||||
fset: fset,
|
||||
files: files,
|
||||
idents: make(map[*ast.Ident]Object),
|
||||
objects: make(map[*ast.Object]Object),
|
||||
initspecs: make(map[*ast.ValueSpec]*ast.ValueSpec),
|
||||
methods: make(map[*TypeName]*Scope),
|
||||
}
|
||||
|
||||
// handle panics
|
||||
|
|
@ -369,7 +395,7 @@ func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (astpkg *ast.P
|
|||
err = check.firsterr
|
||||
default:
|
||||
// unexpected panic: don't crash clients
|
||||
// panic(p) // enable for debugging
|
||||
panic(p) // enable for debugging
|
||||
// TODO(gri) add a test case for this scenario
|
||||
err = fmt.Errorf("types internal error: %v", p)
|
||||
}
|
||||
|
|
@ -392,20 +418,20 @@ func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (astpkg *ast.P
|
|||
return pkg, err
|
||||
}
|
||||
}
|
||||
astpkg, pkg = check.resolve(imp)
|
||||
|
||||
// Imported packages and all types refer to types.Objects,
|
||||
// the current package files' AST uses ast.Objects.
|
||||
// Use an ast.Scope for the current package scope.
|
||||
pkg, methods := check.resolve(imp)
|
||||
check.pkg = pkg
|
||||
check.pkgscope = astpkg.Scope
|
||||
|
||||
// determine missing constant initialization expressions
|
||||
// and associate methods with types
|
||||
check.iterate((*checker).assocInitvalsOrMethod)
|
||||
// associate methods with types
|
||||
for _, m := range methods {
|
||||
check.assocMethod(m)
|
||||
}
|
||||
|
||||
// typecheck all declarations
|
||||
check.iterate((*checker).decl)
|
||||
for _, f := range check.files {
|
||||
for _, d := range f.Decls {
|
||||
check.decl(d)
|
||||
}
|
||||
}
|
||||
|
||||
// typecheck all function/method bodies
|
||||
// (funclist may grow when checking statements - do not use range clause!)
|
||||
|
|
|
|||
|
|
@ -234,8 +234,8 @@ func TestCheck(t *testing.T) {
|
|||
// Declare builtins for testing.
|
||||
// Not done in an init func to avoid an init race with
|
||||
// the construction of the Universe var.
|
||||
def(ast.Fun, "assert", &builtin{aType, _Assert, "assert", 1, false, true})
|
||||
def(ast.Fun, "trace", &builtin{aType, _Trace, "trace", 0, true, true})
|
||||
def(&Func{Name: "assert", Type: &builtin{_Assert, "assert", 1, false, true}})
|
||||
def(&Func{Name: "trace", Type: &builtin{_Trace, "trace", 0, true, true}})
|
||||
|
||||
// For easy debugging w/o changing the testing code,
|
||||
// if there is a local test file, only test that file.
|
||||
|
|
|
|||
|
|
@ -20,11 +20,6 @@ func assert(p bool) {
|
|||
}
|
||||
}
|
||||
|
||||
func unimplemented() {
|
||||
// enable for debugging
|
||||
// panic("unimplemented")
|
||||
}
|
||||
|
||||
func unreachable() {
|
||||
panic("unreachable")
|
||||
}
|
||||
|
|
@ -311,14 +306,9 @@ func writeType(buf *bytes.Buffer, typ Type) {
|
|||
writeType(buf, t.Elt)
|
||||
|
||||
case *NamedType:
|
||||
var s string
|
||||
switch {
|
||||
case t.Obj != nil:
|
||||
s := "<NamedType w/o object>"
|
||||
if t.Obj != nil {
|
||||
s = t.Obj.GetName()
|
||||
case t.AstObj != nil:
|
||||
s = t.AstObj.Name
|
||||
default:
|
||||
s = "<NamedType w/o object>"
|
||||
}
|
||||
buf.WriteString(s)
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (param
|
|||
if list == nil {
|
||||
return
|
||||
}
|
||||
var last *ast.Object
|
||||
var last *Var
|
||||
for i, field := range list.List {
|
||||
ftype := field.Type
|
||||
if t, _ := ftype.(*ast.Ellipsis); t != nil {
|
||||
|
|
@ -45,24 +45,24 @@ func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (param
|
|||
if len(field.Names) > 0 {
|
||||
// named parameter
|
||||
for _, name := range field.Names {
|
||||
obj := name.Obj
|
||||
obj.Type = typ
|
||||
last = obj
|
||||
params = append(params, &Var{Name: obj.Name, Type: typ})
|
||||
par := check.lookup(name).(*Var)
|
||||
par.Type = typ
|
||||
last = par
|
||||
copy := *par
|
||||
params = append(params, ©)
|
||||
}
|
||||
} else {
|
||||
// anonymous parameter
|
||||
obj := ast.NewObj(ast.Var, "")
|
||||
obj.Type = typ
|
||||
last = obj
|
||||
params = append(params, &Var{Name: obj.Name, Type: typ})
|
||||
par := &Var{Type: typ}
|
||||
last = nil // not accessible inside function
|
||||
params = append(params, par)
|
||||
}
|
||||
}
|
||||
// For a variadic function, change the last parameter's object type
|
||||
// from T to []T (this is the type used inside the function), but
|
||||
// keep the params list unchanged (this is the externally visible type).
|
||||
if isVariadic {
|
||||
last.Type = &Slice{Elt: last.Type.(Type)}
|
||||
if isVariadic && last != nil {
|
||||
last.Type = &Slice{Elt: last.Type}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -145,16 +145,7 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [
|
|||
case *Basic:
|
||||
fields = append(fields, &Field{QualifiedName{nil, t.Name}, typ, tag, true})
|
||||
case *NamedType:
|
||||
var name string
|
||||
switch {
|
||||
case t.Obj != nil:
|
||||
name = t.Obj.GetName()
|
||||
case t.AstObj != nil:
|
||||
name = t.AstObj.Name
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
fields = append(fields, &Field{QualifiedName{nil, name}, typ, tag, true})
|
||||
fields = append(fields, &Field{QualifiedName{nil, t.Obj.GetName()}, typ, tag, true})
|
||||
default:
|
||||
if typ != Typ[Invalid] {
|
||||
check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ)
|
||||
|
|
@ -519,8 +510,9 @@ func (check *checker) index(index ast.Expr, length int64, iota int) int64 {
|
|||
// For details, see comment in go/parser/parser.go, method parseElement.
|
||||
func (check *checker) compositeLitKey(key ast.Expr) {
|
||||
if ident, ok := key.(*ast.Ident); ok && ident.Obj == nil {
|
||||
ident.Obj = check.pkgscope.Lookup(ident.Name)
|
||||
if ident.Obj == nil {
|
||||
if obj := check.pkg.Scope.Lookup(ident.Name); obj != nil {
|
||||
check.idents[ident] = obj
|
||||
} else {
|
||||
check.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
|
||||
}
|
||||
}
|
||||
|
|
@ -594,7 +586,7 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand,
|
|||
var z operand
|
||||
z.mode = variable
|
||||
z.expr = nil // TODO(gri) can we do better here? (for good error messages)
|
||||
z.typ = par.Type.(Type)
|
||||
z.typ = par.Type
|
||||
|
||||
if arg != nil {
|
||||
check.expr(x, arg, z.typ, -1)
|
||||
|
|
@ -666,21 +658,17 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
|||
check.invalidOp(e.Pos(), "cannot use _ as value or type")
|
||||
goto Error
|
||||
}
|
||||
obj := e.Obj
|
||||
obj := check.lookup(e)
|
||||
if obj == nil {
|
||||
goto Error // error was reported before
|
||||
}
|
||||
if obj.Type == nil {
|
||||
check.object(obj, cycleOk)
|
||||
}
|
||||
switch obj.Kind {
|
||||
case ast.Bad:
|
||||
goto Error // error was reported before
|
||||
case ast.Pkg:
|
||||
check.object(obj, cycleOk)
|
||||
switch obj := obj.(type) {
|
||||
case *Package:
|
||||
check.errorf(e.Pos(), "use of package %s not in selector", obj.Name)
|
||||
goto Error
|
||||
case ast.Con:
|
||||
if obj.Data == nil {
|
||||
case *Const:
|
||||
if obj.Val == nil {
|
||||
goto Error // cycle detected
|
||||
}
|
||||
x.mode = constant
|
||||
|
|
@ -691,24 +679,24 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
|||
}
|
||||
x.val = int64(iota)
|
||||
} else {
|
||||
x.val = obj.Data
|
||||
x.val = obj.Val
|
||||
}
|
||||
case ast.Typ:
|
||||
case *TypeName:
|
||||
x.mode = typexpr
|
||||
if !cycleOk && underlying(obj.Type.(Type)) == nil {
|
||||
check.errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name)
|
||||
if !cycleOk && underlying(obj.Type) == nil {
|
||||
check.errorf(obj.spec.Pos(), "illegal cycle in declaration of %s", obj.Name)
|
||||
x.expr = e
|
||||
x.typ = Typ[Invalid]
|
||||
return // don't goto Error - need x.mode == typexpr
|
||||
}
|
||||
case ast.Var:
|
||||
case *Var:
|
||||
x.mode = variable
|
||||
case ast.Fun:
|
||||
case *Func:
|
||||
x.mode = value
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
x.typ = obj.Type.(Type)
|
||||
x.typ = obj.GetType()
|
||||
|
||||
case *ast.Ellipsis:
|
||||
// ellipses are handled explicitly where they are legal
|
||||
|
|
@ -877,8 +865,8 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
|||
// can only appear in qualified identifiers which are mapped to
|
||||
// selector expressions.
|
||||
if ident, ok := e.X.(*ast.Ident); ok {
|
||||
if obj := ident.Obj; obj != nil && obj.Kind == ast.Pkg {
|
||||
exp := obj.Data.(*Package).Scope.Lookup(sel)
|
||||
if pkg, ok := check.lookup(ident).(*Package); ok {
|
||||
exp := pkg.Scope.Lookup(sel)
|
||||
if exp == nil {
|
||||
check.errorf(e.Sel.Pos(), "cannot refer to unexported %s", sel)
|
||||
goto Error
|
||||
|
|
@ -1148,7 +1136,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
|||
for i, obj := range t.Values {
|
||||
x.mode = value
|
||||
x.expr = nil // TODO(gri) can we do better here? (for good error messages)
|
||||
x.typ = obj.Type.(Type)
|
||||
x.typ = obj.Type
|
||||
check.argument(sig, i, nil, x, passSlice && i+1 == n)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1182,7 +1170,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
|||
x.mode = novalue
|
||||
case 1:
|
||||
x.mode = value
|
||||
x.typ = sig.Results[0].Type.(Type)
|
||||
x.typ = sig.Results[0].Type
|
||||
default:
|
||||
x.mode = value
|
||||
x.typ = &Result{Values: sig.Results}
|
||||
|
|
|
|||
|
|
@ -347,10 +347,11 @@ func (p *gcParser) parseExportedName() (*Package, string) {
|
|||
func (p *gcParser) parseBasicType() Type {
|
||||
id := p.expect(scanner.Ident)
|
||||
obj := Universe.Lookup(id)
|
||||
if obj == nil || obj.Kind != ast.Typ {
|
||||
p.errorf("not a basic type: %s", id)
|
||||
if obj, ok := obj.(*TypeName); ok {
|
||||
return obj.Type
|
||||
}
|
||||
return obj.Type.(Type)
|
||||
p.errorf("not a basic type: %s", id)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ArrayType = "[" int_lit "]" Type .
|
||||
|
|
|
|||
|
|
@ -4,51 +4,65 @@
|
|||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
// An Object describes a named language entity such as a package,
|
||||
// constant, type, variable, function (incl. methods), or label.
|
||||
// All objects implement the Object interface.
|
||||
//
|
||||
type Object interface {
|
||||
anObject()
|
||||
GetName() string
|
||||
GetType() Type
|
||||
GetPos() token.Pos
|
||||
|
||||
anObject()
|
||||
}
|
||||
|
||||
// A Package represents the contents (objects) of a Go package.
|
||||
type Package struct {
|
||||
implementsObject
|
||||
Name string
|
||||
Path string // import path, "" for current (non-imported) package
|
||||
Scope *Scope // nil for current (non-imported) package for now
|
||||
Imports map[string]*Package // map of import paths to packages
|
||||
Scope *Scope // package-level scope
|
||||
Imports map[string]*Package // map of import paths to imported packages
|
||||
|
||||
spec *ast.ImportSpec
|
||||
}
|
||||
|
||||
// A Const represents a declared constant.
|
||||
type Const struct {
|
||||
implementsObject
|
||||
Name string
|
||||
Type Type
|
||||
Val interface{}
|
||||
|
||||
spec *ast.ValueSpec
|
||||
}
|
||||
|
||||
// A TypeName represents a declared type.
|
||||
type TypeName struct {
|
||||
implementsObject
|
||||
Name string
|
||||
Type Type // *NamedType or *Basic
|
||||
|
||||
spec *ast.TypeSpec
|
||||
}
|
||||
|
||||
// A Variable represents a declared variable (including function parameters and results).
|
||||
type Var struct {
|
||||
implementsObject
|
||||
Name string
|
||||
Type Type
|
||||
|
||||
visited bool // for initialization cycle detection
|
||||
decl interface{}
|
||||
}
|
||||
|
||||
// A Func represents a declared function.
|
||||
type Func struct {
|
||||
implementsObject
|
||||
Name string
|
||||
Type Type // *Signature or *Builtin
|
||||
|
||||
decl *ast.FuncDecl
|
||||
}
|
||||
|
||||
func (obj *Package) GetName() string { return obj.Name }
|
||||
|
|
@ -57,64 +71,84 @@ func (obj *TypeName) GetName() string { return obj.Name }
|
|||
func (obj *Var) GetName() string { return obj.Name }
|
||||
func (obj *Func) GetName() string { return obj.Name }
|
||||
|
||||
func (obj *Package) GetType() Type { return nil }
|
||||
func (obj *Package) GetType() Type { return Typ[Invalid] }
|
||||
func (obj *Const) GetType() Type { return obj.Type }
|
||||
func (obj *TypeName) GetType() Type { return obj.Type }
|
||||
func (obj *Var) GetType() Type { return obj.Type }
|
||||
func (obj *Func) GetType() Type { return obj.Type }
|
||||
|
||||
// All concrete objects embed implementsObject which
|
||||
// ensures that they all implement the Object interface.
|
||||
type implementsObject struct{}
|
||||
|
||||
func (*implementsObject) anObject() {}
|
||||
|
||||
// A Scope maintains the set of named language entities declared
|
||||
// in the scope and a link to the immediately surrounding (outer)
|
||||
// scope.
|
||||
//
|
||||
type Scope struct {
|
||||
Outer *Scope
|
||||
Elems []Object // scope entries in insertion order
|
||||
large map[string]Object // for fast lookup - only used for larger scopes
|
||||
}
|
||||
|
||||
// Lookup returns the object with the given name if it is
|
||||
// found in scope s, otherwise it returns nil. Outer scopes
|
||||
// are ignored.
|
||||
//
|
||||
func (s *Scope) Lookup(name string) Object {
|
||||
if s.large != nil {
|
||||
return s.large[name]
|
||||
}
|
||||
for _, obj := range s.Elems {
|
||||
if obj.GetName() == name {
|
||||
return obj
|
||||
func (obj *Package) GetPos() token.Pos { return obj.spec.Pos() }
|
||||
func (obj *Const) GetPos() token.Pos {
|
||||
for _, n := range obj.spec.Names {
|
||||
if n.Name == obj.Name {
|
||||
return n.Pos()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return token.NoPos
|
||||
}
|
||||
|
||||
// Insert attempts to insert an object obj into scope s.
|
||||
// If s already contains an object with the same name,
|
||||
// Insert leaves s unchanged and returns that object.
|
||||
// Otherwise it inserts obj and returns nil.
|
||||
//
|
||||
func (s *Scope) Insert(obj Object) Object {
|
||||
name := obj.GetName()
|
||||
if alt := s.Lookup(name); alt != nil {
|
||||
return alt
|
||||
}
|
||||
s.Elems = append(s.Elems, obj)
|
||||
if len(s.Elems) > 20 {
|
||||
if s.large == nil {
|
||||
m := make(map[string]Object, len(s.Elems))
|
||||
for _, obj := range s.Elems {
|
||||
m[obj.GetName()] = obj
|
||||
func (obj *TypeName) GetPos() token.Pos { return obj.spec.Pos() }
|
||||
func (obj *Var) GetPos() token.Pos {
|
||||
switch d := obj.decl.(type) {
|
||||
case *ast.Field:
|
||||
for _, n := range d.Names {
|
||||
if n.Name == obj.Name {
|
||||
return n.Pos()
|
||||
}
|
||||
}
|
||||
case *ast.ValueSpec:
|
||||
for _, n := range d.Names {
|
||||
if n.Name == obj.Name {
|
||||
return n.Pos()
|
||||
}
|
||||
}
|
||||
case *ast.AssignStmt:
|
||||
for _, x := range d.Lhs {
|
||||
if ident, isIdent := x.(*ast.Ident); isIdent && ident.Name == obj.Name {
|
||||
return ident.Pos()
|
||||
}
|
||||
s.large = m
|
||||
}
|
||||
s.large[name] = obj
|
||||
}
|
||||
return token.NoPos
|
||||
}
|
||||
func (obj *Func) GetPos() token.Pos { return obj.decl.Name.Pos() }
|
||||
|
||||
func (*Package) anObject() {}
|
||||
func (*Const) anObject() {}
|
||||
func (*TypeName) anObject() {}
|
||||
func (*Var) anObject() {}
|
||||
func (*Func) anObject() {}
|
||||
|
||||
// newObj returns a new Object for a given *ast.Object.
|
||||
// It does not canonicalize them (it always returns a new one).
|
||||
// For canonicalization, see check.lookup.
|
||||
//
|
||||
// TODO(gri) Once we do identifier resolution completely in
|
||||
// in the typechecker, this functionality can go.
|
||||
//
|
||||
func newObj(astObj *ast.Object) Object {
|
||||
name := astObj.Name
|
||||
typ, _ := astObj.Type.(Type)
|
||||
switch astObj.Kind {
|
||||
case ast.Bad:
|
||||
// ignore
|
||||
case ast.Pkg:
|
||||
unreachable()
|
||||
case ast.Con:
|
||||
return &Const{Name: name, Type: typ, Val: astObj.Data, spec: astObj.Decl.(*ast.ValueSpec)}
|
||||
case ast.Typ:
|
||||
return &TypeName{Name: name, Type: typ, spec: astObj.Decl.(*ast.TypeSpec)}
|
||||
case ast.Var:
|
||||
switch astObj.Decl.(type) {
|
||||
case *ast.Field, *ast.ValueSpec, *ast.AssignStmt: // these are ok
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
return &Var{Name: name, Type: typ, decl: astObj.Decl}
|
||||
case ast.Fun:
|
||||
return &Func{Name: name, Type: typ, decl: astObj.Decl.(*ast.FuncDecl)}
|
||||
case ast.Lbl:
|
||||
unreachable() // for now
|
||||
}
|
||||
unreachable()
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -265,9 +265,6 @@ func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res looku
|
|||
visited[typ] = true
|
||||
|
||||
// look for a matching attached method
|
||||
if typ.AstObj != nil {
|
||||
assert(typ.AstObj.Data == nil) // methods must have been moved to typ.Methods
|
||||
}
|
||||
for _, m := range typ.Methods {
|
||||
if name.IsSame(m.QualifiedName) {
|
||||
assert(m.Type != nil)
|
||||
|
|
@ -355,9 +352,6 @@ func lookupField(typ Type, name QualifiedName) (operandMode, Type) {
|
|||
typ = deref(typ)
|
||||
|
||||
if t, ok := typ.(*NamedType); ok {
|
||||
if t.AstObj != nil {
|
||||
assert(t.AstObj.Data == nil) // methods must have been moved to t.Methods
|
||||
}
|
||||
for _, m := range t.Methods {
|
||||
if name.IsSame(m.QualifiedName) {
|
||||
assert(m.Type != nil)
|
||||
|
|
|
|||
|
|
@ -182,14 +182,7 @@ func isIdentical(x, y Type) bool {
|
|||
// Two named types are identical if their type names originate
|
||||
// in the same type declaration.
|
||||
if y, ok := y.(*NamedType); ok {
|
||||
switch {
|
||||
case x.Obj != nil:
|
||||
return x.Obj == y.Obj
|
||||
case x.AstObj != nil:
|
||||
return x.AstObj == y.AstObj
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
return x.Obj == y.Obj
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,40 +7,39 @@ package types
|
|||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func (check *checker) declareObj(scope, altScope *ast.Scope, obj *ast.Object) {
|
||||
func (check *checker) declareObj(scope, altScope *Scope, obj Object) {
|
||||
alt := scope.Insert(obj)
|
||||
if alt == nil && altScope != nil {
|
||||
// see if there is a conflicting declaration in altScope
|
||||
alt = altScope.Lookup(obj.Name)
|
||||
alt = altScope.Lookup(obj.GetName())
|
||||
}
|
||||
if alt != nil {
|
||||
prevDecl := ""
|
||||
if pos := alt.Pos(); pos.IsValid() {
|
||||
if pos := alt.GetPos(); pos.IsValid() {
|
||||
prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos))
|
||||
}
|
||||
check.errorf(obj.Pos(), fmt.Sprintf("%s redeclared in this block%s", obj.Name, prevDecl))
|
||||
check.errorf(obj.GetPos(), fmt.Sprintf("%s redeclared in this block%s", obj.GetName(), prevDecl))
|
||||
}
|
||||
}
|
||||
|
||||
func resolve(scope *ast.Scope, ident *ast.Ident) bool {
|
||||
func (check *checker) resolveIdent(scope *Scope, ident *ast.Ident) bool {
|
||||
for ; scope != nil; scope = scope.Outer {
|
||||
if obj := scope.Lookup(ident.Name); obj != nil {
|
||||
ident.Obj = obj
|
||||
check.idents[ident] = obj
|
||||
return true
|
||||
}
|
||||
}
|
||||
// handle universe scope lookups
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO(gri) eventually resolve should only return *Package.
|
||||
func (check *checker) resolve(importer Importer) (*ast.Package, *Package) {
|
||||
func (check *checker) resolve(importer Importer) (pkg *Package, methods []*ast.FuncDecl) {
|
||||
// complete package scope
|
||||
pkgName := ""
|
||||
pkgScope := ast.NewScope(Universe)
|
||||
pkgScope := &Scope{Outer: Universe}
|
||||
|
||||
i := 0
|
||||
for _, file := range check.files {
|
||||
|
|
@ -57,9 +56,49 @@ func (check *checker) resolve(importer Importer) (*ast.Package, *Package) {
|
|||
check.files[i] = file
|
||||
i++
|
||||
|
||||
// collect top-level file objects in package scope
|
||||
for _, obj := range file.Scope.Objects {
|
||||
check.declareObj(pkgScope, nil, obj)
|
||||
// insert top-level file objects in package scope
|
||||
// (the parser took care of declaration errors)
|
||||
for _, decl := range file.Decls {
|
||||
switch d := decl.(type) {
|
||||
case *ast.BadDecl:
|
||||
// ignore
|
||||
case *ast.GenDecl:
|
||||
if d.Tok == token.CONST {
|
||||
check.assocInitvals(d)
|
||||
}
|
||||
for _, spec := range d.Specs {
|
||||
switch s := spec.(type) {
|
||||
case *ast.ImportSpec:
|
||||
// handled separately below
|
||||
case *ast.ValueSpec:
|
||||
for _, name := range s.Names {
|
||||
if name.Name == "_" {
|
||||
continue
|
||||
}
|
||||
pkgScope.Insert(check.lookup(name))
|
||||
}
|
||||
case *ast.TypeSpec:
|
||||
if s.Name.Name == "_" {
|
||||
continue
|
||||
}
|
||||
pkgScope.Insert(check.lookup(s.Name))
|
||||
default:
|
||||
check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
|
||||
}
|
||||
}
|
||||
case *ast.FuncDecl:
|
||||
if d.Recv != nil {
|
||||
// collect method
|
||||
methods = append(methods, d)
|
||||
continue
|
||||
}
|
||||
if d.Name.Name == "_" || d.Name.Name == "init" {
|
||||
continue // blank (_) and init functions are inaccessible
|
||||
}
|
||||
pkgScope.Insert(check.lookup(d.Name))
|
||||
default:
|
||||
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
|
||||
}
|
||||
}
|
||||
}
|
||||
check.files = check.files[0:i]
|
||||
|
|
@ -71,7 +110,7 @@ func (check *checker) resolve(importer Importer) (*ast.Package, *Package) {
|
|||
for _, file := range check.files {
|
||||
// build file scope by processing all imports
|
||||
importErrors := false
|
||||
fileScope := ast.NewScope(pkgScope)
|
||||
fileScope := &Scope{Outer: pkgScope}
|
||||
for _, spec := range file.Imports {
|
||||
if importer == nil {
|
||||
importErrors = true
|
||||
|
|
@ -97,25 +136,15 @@ func (check *checker) resolve(importer Importer) (*ast.Package, *Package) {
|
|||
// add import to file scope
|
||||
if name == "." {
|
||||
// merge imported scope with file scope
|
||||
// TODO(gri) Imported packages use Objects but the current
|
||||
// package scope is based on ast.Scope and ast.Objects
|
||||
// at the moment. Don't try to convert the imported
|
||||
// objects for now. Once we get rid of ast.Object
|
||||
// dependency, this loop can be enabled again.
|
||||
panic("cannot handle dot-import")
|
||||
/*
|
||||
for _, obj := range pkg.Scope.Elems {
|
||||
check.declareObj(fileScope, pkgScope, obj)
|
||||
}
|
||||
*/
|
||||
for _, obj := range pkg.Scope.Entries {
|
||||
check.declareObj(fileScope, pkgScope, obj)
|
||||
}
|
||||
} else if name != "_" {
|
||||
// declare imported package object in file scope
|
||||
// (do not re-use pkg in the file scope but create
|
||||
// a new object instead; the Decl field is different
|
||||
// for different files)
|
||||
obj := ast.NewObj(ast.Pkg, name)
|
||||
obj.Decl = spec
|
||||
obj.Data = pkg
|
||||
obj := &Package{Name: name, Scope: pkg.Scope, spec: spec}
|
||||
check.declareObj(fileScope, pkgScope, obj)
|
||||
}
|
||||
}
|
||||
|
|
@ -130,7 +159,7 @@ func (check *checker) resolve(importer Importer) (*ast.Package, *Package) {
|
|||
}
|
||||
i := 0
|
||||
for _, ident := range file.Unresolved {
|
||||
if !resolve(fileScope, ident) {
|
||||
if !check.resolveIdent(fileScope, ident) {
|
||||
check.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
|
||||
file.Unresolved[i] = ident
|
||||
i++
|
||||
|
|
@ -141,6 +170,5 @@ func (check *checker) resolve(importer Importer) (*ast.Package, *Package) {
|
|||
pkgScope.Outer = Universe // reset outer scope
|
||||
}
|
||||
|
||||
// TODO(gri) Once we have a pkgScope of type *Scope, only return *Package.
|
||||
return &ast.Package{Name: pkgName, Scope: pkgScope}, &Package{Name: pkgName, Imports: imports}
|
||||
return &Package{Name: pkgName, Scope: pkgScope, Imports: imports}, methods
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ package types
|
|||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
//"go/parser"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"testing"
|
||||
|
|
@ -76,60 +76,64 @@ func ResolveQualifiedIdents(fset *token.FileSet, pkg *ast.Package) error {
|
|||
}
|
||||
|
||||
func TestResolveQualifiedIdents(t *testing.T) {
|
||||
// parse package files
|
||||
fset := token.NewFileSet()
|
||||
files := make([]*ast.File, len(sources))
|
||||
for i, src := range sources {
|
||||
f, err := parser.ParseFile(fset, "", src, parser.DeclarationErrors)
|
||||
return
|
||||
// disabled for now
|
||||
/*
|
||||
// parse package files
|
||||
fset := token.NewFileSet()
|
||||
files := make([]*ast.File, len(sources))
|
||||
for i, src := range sources {
|
||||
f, err := parser.ParseFile(fset, "", src, parser.DeclarationErrors)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
files[i] = f
|
||||
}
|
||||
|
||||
// resolve package AST
|
||||
astpkg, pkg, err := Check(fset, files)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
files[i] = f
|
||||
}
|
||||
|
||||
// resolve package AST
|
||||
astpkg, pkg, err := Check(fset, files)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// check that all packages were imported
|
||||
for _, name := range pkgnames {
|
||||
if pkg.Imports[name] == nil {
|
||||
t.Errorf("package %s not imported", name)
|
||||
// check that all packages were imported
|
||||
for _, name := range pkgnames {
|
||||
if pkg.Imports[name] == nil {
|
||||
t.Errorf("package %s not imported", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(gri) fix this
|
||||
// unresolved identifiers are not collected at the moment
|
||||
// check that there are no top-level unresolved identifiers
|
||||
for _, f := range astpkg.Files {
|
||||
for _, x := range f.Unresolved {
|
||||
t.Errorf("%s: unresolved global identifier %s", fset.Position(x.Pos()), x.Name)
|
||||
// TODO(gri) fix this
|
||||
// unresolved identifiers are not collected at the moment
|
||||
// check that there are no top-level unresolved identifiers
|
||||
for _, f := range astpkg.Files {
|
||||
for _, x := range f.Unresolved {
|
||||
t.Errorf("%s: unresolved global identifier %s", fset.Position(x.Pos()), x.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// resolve qualified identifiers
|
||||
if err := ResolveQualifiedIdents(fset, astpkg); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// resolve qualified identifiers
|
||||
if err := ResolveQualifiedIdents(fset, astpkg); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// check that qualified identifiers are resolved
|
||||
ast.Inspect(astpkg, func(n ast.Node) bool {
|
||||
if s, ok := n.(*ast.SelectorExpr); ok {
|
||||
if x, ok := s.X.(*ast.Ident); ok {
|
||||
if x.Obj == nil {
|
||||
t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name)
|
||||
return false
|
||||
}
|
||||
if x.Obj.Kind == ast.Pkg && s.Sel != nil && s.Sel.Obj == nil {
|
||||
t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name)
|
||||
// check that qualified identifiers are resolved
|
||||
ast.Inspect(astpkg, func(n ast.Node) bool {
|
||||
if s, ok := n.(*ast.SelectorExpr); ok {
|
||||
if x, ok := s.X.(*ast.Ident); ok {
|
||||
if x.Obj == nil {
|
||||
t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name)
|
||||
return false
|
||||
}
|
||||
if x.Obj.Kind == ast.Pkg && s.Sel != nil && s.Sel.Obj == nil {
|
||||
t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name)
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return true
|
||||
})
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2013 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
|
||||
|
||||
// A Scope maintains the set of named language entities declared
|
||||
// in the scope and a link to the immediately surrounding (outer)
|
||||
// scope.
|
||||
//
|
||||
type Scope struct {
|
||||
Outer *Scope
|
||||
Entries []Object // scope entries in insertion order
|
||||
large map[string]Object // for fast lookup - only used for larger scopes
|
||||
}
|
||||
|
||||
// Lookup returns the object with the given name if it is
|
||||
// found in scope s, otherwise it returns nil. Outer scopes
|
||||
// are ignored.
|
||||
//
|
||||
func (s *Scope) Lookup(name string) Object {
|
||||
if s.large != nil {
|
||||
return s.large[name]
|
||||
}
|
||||
for _, obj := range s.Entries {
|
||||
if obj.GetName() == name {
|
||||
return obj
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Insert attempts to insert an object obj into scope s.
|
||||
// If s already contains an object with the same name,
|
||||
// Insert leaves s unchanged and returns that object.
|
||||
// Otherwise it inserts obj and returns nil.
|
||||
//
|
||||
func (s *Scope) Insert(obj Object) Object {
|
||||
name := obj.GetName()
|
||||
if alt := s.Lookup(name); alt != nil {
|
||||
return alt
|
||||
}
|
||||
s.Entries = append(s.Entries, obj)
|
||||
|
||||
// If the scope size reaches a threshold, use a map for faster lookups.
|
||||
const threshold = 20
|
||||
if len(s.Entries) > threshold {
|
||||
if s.large == nil {
|
||||
m := make(map[string]Object, len(s.Entries))
|
||||
for _, obj := range s.Entries {
|
||||
m[obj.GetName()] = obj
|
||||
}
|
||||
s.large = m
|
||||
}
|
||||
s.large[name] = obj
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -76,10 +76,10 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
|
|||
}
|
||||
|
||||
// lhs may or may not be typed yet
|
||||
obj := ident.Obj
|
||||
obj := check.lookup(ident)
|
||||
var typ Type
|
||||
if obj.Type != nil {
|
||||
typ = obj.Type.(Type)
|
||||
if t := obj.GetType(); t != nil {
|
||||
typ = t
|
||||
}
|
||||
|
||||
if rhs != nil {
|
||||
|
|
@ -94,7 +94,7 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
|
|||
typ = Typ[Invalid]
|
||||
if x.mode != invalid {
|
||||
typ = x.typ
|
||||
if obj.Kind == ast.Var && isUntyped(typ) {
|
||||
if _, ok := obj.(*Var); ok && isUntyped(typ) {
|
||||
if x.isNil() {
|
||||
check.errorf(x.pos(), "use of untyped nil")
|
||||
x.mode = invalid
|
||||
|
|
@ -103,15 +103,22 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
|
|||
}
|
||||
}
|
||||
}
|
||||
obj.Type = typ
|
||||
switch obj := obj.(type) {
|
||||
case *Const:
|
||||
obj.Type = typ
|
||||
case *Var:
|
||||
obj.Type = typ
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
}
|
||||
|
||||
if x.mode != invalid {
|
||||
var z operand
|
||||
switch obj.Kind {
|
||||
case ast.Con:
|
||||
switch obj.(type) {
|
||||
case *Const:
|
||||
z.mode = constant
|
||||
case ast.Var:
|
||||
case *Var:
|
||||
z.mode = variable
|
||||
default:
|
||||
unreachable()
|
||||
|
|
@ -122,12 +129,12 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
|
|||
}
|
||||
|
||||
// for constants, set their value
|
||||
if obj.Kind == ast.Con {
|
||||
assert(obj.Data == nil)
|
||||
if obj, ok := obj.(*Const); ok {
|
||||
assert(obj.Val == nil)
|
||||
if x.mode != invalid {
|
||||
if x.mode == constant {
|
||||
if isConstType(x.typ) {
|
||||
obj.Data = x.val
|
||||
obj.Val = x.val
|
||||
} else {
|
||||
check.errorf(x.pos(), "%s has invalid constant type", x)
|
||||
}
|
||||
|
|
@ -135,22 +142,23 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
|
|||
check.errorf(x.pos(), "%s is not constant", x)
|
||||
}
|
||||
}
|
||||
if obj.Data == nil {
|
||||
if obj.Val == nil {
|
||||
// set the constant to its type's zero value to reduce spurious errors
|
||||
switch typ := underlying(obj.Type.(Type)); {
|
||||
switch typ := underlying(obj.Type); {
|
||||
case typ == Typ[Invalid]:
|
||||
// ignore
|
||||
case isBoolean(typ):
|
||||
obj.Data = false
|
||||
obj.Val = false
|
||||
case isNumeric(typ):
|
||||
obj.Data = int64(0)
|
||||
obj.Val = int64(0)
|
||||
case isString(typ):
|
||||
obj.Data = ""
|
||||
obj.Val = ""
|
||||
case hasNil(typ):
|
||||
obj.Data = nilConst
|
||||
obj.Val = nilConst
|
||||
default:
|
||||
// in all other cases just prevent use of the constant
|
||||
obj.Kind = ast.Bad
|
||||
// TODO(gri) re-evaluate this code
|
||||
obj.Val = nilConst
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -159,7 +167,7 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
|
|||
// assignNtoM typechecks a general assignment. If decl is set, the lhs operands
|
||||
// must be identifiers. If their types are not set, they are deduced from the
|
||||
// types of the corresponding rhs expressions. iota >= 0 indicates that the
|
||||
// "assignment" is part of a constant declaration.
|
||||
// "assignment" is part of a constant/variable declaration.
|
||||
// Precondition: len(lhs) > 0 .
|
||||
//
|
||||
func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
|
||||
|
|
@ -187,7 +195,7 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
|
|||
x.mode = value
|
||||
for i, obj := range t.Values {
|
||||
x.expr = nil // TODO(gri) should do better here
|
||||
x.typ = obj.Type.(Type)
|
||||
x.typ = obj.Type
|
||||
check.assign1to1(lhs[i], nil, &x, decl, iota)
|
||||
}
|
||||
return
|
||||
|
|
@ -212,8 +220,15 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
|
|||
if iota >= 0 {
|
||||
// declaration
|
||||
for _, e := range lhs {
|
||||
if ident, ok := e.(*ast.Ident); ok {
|
||||
ident.Obj.Type = Typ[Invalid]
|
||||
if name, ok := e.(*ast.Ident); ok {
|
||||
switch obj := check.lookup(name).(type) {
|
||||
case *Const:
|
||||
obj.Type = Typ[Invalid]
|
||||
case *Var:
|
||||
obj.Type = Typ[Invalid]
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -411,16 +426,12 @@ func (check *checker) stmt(s ast.Stmt) {
|
|||
named := false // if set, function has named results
|
||||
for i, res := range sig.Results {
|
||||
if len(res.Name) > 0 {
|
||||
// a blank (_) result parameter is a named result parameter!
|
||||
// a blank (_) result parameter is a named result
|
||||
named = true
|
||||
}
|
||||
name := ast.NewIdent(res.Name)
|
||||
name.NamePos = s.Pos()
|
||||
// TODO(gri) Avoid creating new objects here once we
|
||||
// move away from ast.Objects completely.
|
||||
obj := ast.NewObj(ast.Var, res.Name)
|
||||
obj.Type = res.Type
|
||||
name.Obj = obj
|
||||
check.idents[name] = &Var{Name: res.Name, Type: res.Type}
|
||||
lhs[i] = name
|
||||
}
|
||||
if len(s.Results) > 0 || !named {
|
||||
|
|
@ -432,7 +443,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
|||
}
|
||||
|
||||
case *ast.BranchStmt:
|
||||
unimplemented()
|
||||
// TODO(gri) implement this
|
||||
|
||||
case *ast.BlockStmt:
|
||||
check.stmtList(s.List)
|
||||
|
|
@ -453,7 +464,9 @@ func (check *checker) stmt(s ast.Stmt) {
|
|||
tag := s.Tag
|
||||
if tag == nil {
|
||||
// use fake true tag value and position it at the opening { of the switch
|
||||
tag = &ast.Ident{NamePos: s.Body.Lbrace, Name: "true", Obj: Universe.Lookup("true")}
|
||||
ident := &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"}
|
||||
check.idents[ident] = Universe.Lookup("true")
|
||||
tag = ident
|
||||
}
|
||||
check.expr(&x, tag, nil, -1)
|
||||
|
||||
|
|
@ -519,7 +532,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
|||
// remaining syntactic errors are considered AST errors here.
|
||||
// TODO(gri) better factoring of error handling (invalid ASTs)
|
||||
//
|
||||
var lhs *ast.Object // lhs identifier object or nil
|
||||
var lhs *Var // lhs variable or nil
|
||||
var rhs ast.Expr
|
||||
switch guard := s.Assign.(type) {
|
||||
case *ast.ExprStmt:
|
||||
|
|
@ -534,7 +547,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
|||
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
|
||||
return
|
||||
}
|
||||
lhs = ident.Obj
|
||||
lhs = check.lookup(ident).(*Var)
|
||||
rhs = guard.Rhs[0]
|
||||
default:
|
||||
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ func (T5 /* ERROR "invalid receiver" */) m1() {}
|
|||
func (T5 /* ERROR "invalid receiver" */) m2() {}
|
||||
|
||||
// Methods associated with non-local or unnamed types.
|
||||
// func (int) m() {} TODO(gri) check for methods associated with external (not package-local) types
|
||||
func (int /* ERROR "non-local type" */ ) m() {}
|
||||
func ([ /* ERROR "expected" */ ]int) m() {}
|
||||
func (time /* ERROR "expected" */ .Time) m() {}
|
||||
func (x interface /* ERROR "expected" */ {}) m() {}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,6 @@ const (
|
|||
|
||||
// A Basic represents a basic type.
|
||||
type Basic struct {
|
||||
implementsType
|
||||
Kind BasicKind
|
||||
Info BasicInfo
|
||||
Size int64
|
||||
|
|
@ -80,14 +79,12 @@ type Basic struct {
|
|||
|
||||
// An Array represents an array type [Len]Elt.
|
||||
type Array struct {
|
||||
implementsType
|
||||
Len int64
|
||||
Elt Type
|
||||
}
|
||||
|
||||
// A Slice represents a slice type []Elt.
|
||||
type Slice struct {
|
||||
implementsType
|
||||
Elt Type
|
||||
}
|
||||
|
||||
|
|
@ -132,7 +129,6 @@ type Field struct {
|
|||
|
||||
// A Struct represents a struct type struct{...}.
|
||||
type Struct struct {
|
||||
implementsType
|
||||
Fields []*Field
|
||||
}
|
||||
|
||||
|
|
@ -147,19 +143,16 @@ func (typ *Struct) fieldIndex(name string) int {
|
|||
|
||||
// A Pointer represents a pointer type *Base.
|
||||
type Pointer struct {
|
||||
implementsType
|
||||
Base Type
|
||||
}
|
||||
|
||||
// A Result represents a (multi-value) function call result.
|
||||
type Result struct {
|
||||
implementsType
|
||||
Values []*Var // Signature.Results of the function called
|
||||
}
|
||||
|
||||
// A Signature represents a user-defined function type func(...) (...).
|
||||
type Signature struct {
|
||||
implementsType
|
||||
Recv *Var // nil if not a method
|
||||
Params []*Var // (incoming) parameters from left to right; or nil
|
||||
Results []*Var // (outgoing) results from left to right; or nil
|
||||
|
|
@ -200,7 +193,6 @@ const (
|
|||
|
||||
// A builtin represents the type of a built-in function.
|
||||
type builtin struct {
|
||||
implementsType
|
||||
id builtinId
|
||||
name string
|
||||
nargs int // number of arguments (minimum if variadic)
|
||||
|
|
@ -216,35 +208,36 @@ type Method struct {
|
|||
|
||||
// An Interface represents an interface type interface{...}.
|
||||
type Interface struct {
|
||||
implementsType
|
||||
Methods []*Method // TODO(gri) consider keeping them in sorted order
|
||||
}
|
||||
|
||||
// A Map represents a map type map[Key]Elt.
|
||||
type Map struct {
|
||||
implementsType
|
||||
Key, Elt Type
|
||||
}
|
||||
|
||||
// A Chan represents a channel type chan Elt, <-chan Elt, or chan<-Elt.
|
||||
type Chan struct {
|
||||
implementsType
|
||||
Dir ast.ChanDir
|
||||
Elt Type
|
||||
}
|
||||
|
||||
// A NamedType represents a named type as declared in a type declaration.
|
||||
type NamedType struct {
|
||||
implementsType
|
||||
// TODO(gri) remove AstObj once we have moved away from ast.Objects
|
||||
Obj Object // corresponding declared object (imported package)
|
||||
AstObj *ast.Object // corresponding declared object (current package)
|
||||
Underlying Type // nil if not fully declared yet; never a *NamedType
|
||||
Methods []*Method // TODO(gri) consider keeping them in sorted order
|
||||
Obj *TypeName // corresponding declared object
|
||||
Underlying Type // nil if not fully declared yet; never a *NamedType
|
||||
Methods []*Method // TODO(gri) consider keeping them in sorted order
|
||||
}
|
||||
|
||||
// All concrete types embed implementsType which
|
||||
// ensures that all types implement the Type interface.
|
||||
type implementsType struct{}
|
||||
|
||||
func (*implementsType) aType() {}
|
||||
func (*Basic) aType() {}
|
||||
func (*Array) aType() {}
|
||||
func (*Slice) aType() {}
|
||||
func (*Struct) aType() {}
|
||||
func (*Pointer) aType() {}
|
||||
func (*Result) aType() {}
|
||||
func (*Signature) aType() {}
|
||||
func (*builtin) aType() {}
|
||||
func (*Interface) aType() {}
|
||||
func (*Map) aType() {}
|
||||
func (*Chan) aType() {}
|
||||
func (*NamedType) aType() {}
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ import (
|
|||
|
||||
const filename = "<src>"
|
||||
|
||||
func makePkg(t *testing.T, src string) (*ast.Package, error) {
|
||||
func makePkg(t *testing.T, src string) (*Package, error) {
|
||||
file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
astpkg, _, err := Check(fset, []*ast.File{file})
|
||||
return astpkg, err
|
||||
pkg, err := Check(fset, []*ast.File{file})
|
||||
return pkg, err
|
||||
}
|
||||
|
||||
type testEntry struct {
|
||||
|
|
@ -110,7 +110,7 @@ func TestTypes(t *testing.T) {
|
|||
t.Errorf("%s: %s", src, err)
|
||||
continue
|
||||
}
|
||||
typ := underlying(pkg.Scope.Lookup("T").Type.(Type))
|
||||
typ := underlying(pkg.Scope.Lookup("T").GetType())
|
||||
str := typeString(typ)
|
||||
if str != test.str {
|
||||
t.Errorf("%s: got %s, want %s", test.src, str, test.str)
|
||||
|
|
|
|||
|
|
@ -12,160 +12,125 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
aType implementsType
|
||||
Universe *ast.Scope
|
||||
Unsafe *Package // package unsafe
|
||||
Universe *Scope
|
||||
Unsafe *Package
|
||||
universeIota *Const
|
||||
)
|
||||
|
||||
// Predeclared types, indexed by BasicKind.
|
||||
var Typ = [...]*Basic{
|
||||
Invalid: {aType, Invalid, 0, 0, "invalid type"},
|
||||
Invalid: {Invalid, 0, 0, "invalid type"},
|
||||
|
||||
Bool: {aType, Bool, IsBoolean, 1, "bool"},
|
||||
Int: {aType, Int, IsInteger, 0, "int"},
|
||||
Int8: {aType, Int8, IsInteger, 1, "int8"},
|
||||
Int16: {aType, Int16, IsInteger, 2, "int16"},
|
||||
Int32: {aType, Int32, IsInteger, 4, "int32"},
|
||||
Int64: {aType, Int64, IsInteger, 8, "int64"},
|
||||
Uint: {aType, Uint, IsInteger | IsUnsigned, 0, "uint"},
|
||||
Uint8: {aType, Uint8, IsInteger | IsUnsigned, 1, "uint8"},
|
||||
Uint16: {aType, Uint16, IsInteger | IsUnsigned, 2, "uint16"},
|
||||
Uint32: {aType, Uint32, IsInteger | IsUnsigned, 4, "uint32"},
|
||||
Uint64: {aType, Uint64, IsInteger | IsUnsigned, 8, "uint64"},
|
||||
Uintptr: {aType, Uintptr, IsInteger | IsUnsigned, 0, "uintptr"},
|
||||
Float32: {aType, Float32, IsFloat, 4, "float32"},
|
||||
Float64: {aType, Float64, IsFloat, 8, "float64"},
|
||||
Complex64: {aType, Complex64, IsComplex, 8, "complex64"},
|
||||
Complex128: {aType, Complex128, IsComplex, 16, "complex128"},
|
||||
String: {aType, String, IsString, 0, "string"},
|
||||
UnsafePointer: {aType, UnsafePointer, 0, 0, "Pointer"},
|
||||
Bool: {Bool, IsBoolean, 1, "bool"},
|
||||
Int: {Int, IsInteger, 0, "int"},
|
||||
Int8: {Int8, IsInteger, 1, "int8"},
|
||||
Int16: {Int16, IsInteger, 2, "int16"},
|
||||
Int32: {Int32, IsInteger, 4, "int32"},
|
||||
Int64: {Int64, IsInteger, 8, "int64"},
|
||||
Uint: {Uint, IsInteger | IsUnsigned, 0, "uint"},
|
||||
Uint8: {Uint8, IsInteger | IsUnsigned, 1, "uint8"},
|
||||
Uint16: {Uint16, IsInteger | IsUnsigned, 2, "uint16"},
|
||||
Uint32: {Uint32, IsInteger | IsUnsigned, 4, "uint32"},
|
||||
Uint64: {Uint64, IsInteger | IsUnsigned, 8, "uint64"},
|
||||
Uintptr: {Uintptr, IsInteger | IsUnsigned, 0, "uintptr"},
|
||||
Float32: {Float32, IsFloat, 4, "float32"},
|
||||
Float64: {Float64, IsFloat, 8, "float64"},
|
||||
Complex64: {Complex64, IsComplex, 8, "complex64"},
|
||||
Complex128: {Complex128, IsComplex, 16, "complex128"},
|
||||
String: {String, IsString, 0, "string"},
|
||||
UnsafePointer: {UnsafePointer, 0, 0, "Pointer"},
|
||||
|
||||
UntypedBool: {aType, UntypedBool, IsBoolean | IsUntyped, 0, "untyped boolean"},
|
||||
UntypedInt: {aType, UntypedInt, IsInteger | IsUntyped, 0, "untyped integer"},
|
||||
UntypedRune: {aType, UntypedRune, IsInteger | IsUntyped, 0, "untyped rune"},
|
||||
UntypedFloat: {aType, UntypedFloat, IsFloat | IsUntyped, 0, "untyped float"},
|
||||
UntypedComplex: {aType, UntypedComplex, IsComplex | IsUntyped, 0, "untyped complex"},
|
||||
UntypedString: {aType, UntypedString, IsString | IsUntyped, 0, "untyped string"},
|
||||
UntypedNil: {aType, UntypedNil, IsUntyped, 0, "untyped nil"},
|
||||
UntypedBool: {UntypedBool, IsBoolean | IsUntyped, 0, "untyped boolean"},
|
||||
UntypedInt: {UntypedInt, IsInteger | IsUntyped, 0, "untyped integer"},
|
||||
UntypedRune: {UntypedRune, IsInteger | IsUntyped, 0, "untyped rune"},
|
||||
UntypedFloat: {UntypedFloat, IsFloat | IsUntyped, 0, "untyped float"},
|
||||
UntypedComplex: {UntypedComplex, IsComplex | IsUntyped, 0, "untyped complex"},
|
||||
UntypedString: {UntypedString, IsString | IsUntyped, 0, "untyped string"},
|
||||
UntypedNil: {UntypedNil, IsUntyped, 0, "untyped nil"},
|
||||
}
|
||||
|
||||
var aliases = [...]*Basic{
|
||||
{aType, Byte, IsInteger | IsUnsigned, 1, "byte"},
|
||||
{aType, Rune, IsInteger, 4, "rune"},
|
||||
{Byte, IsInteger | IsUnsigned, 1, "byte"},
|
||||
{Rune, IsInteger, 4, "rune"},
|
||||
}
|
||||
|
||||
var predeclaredConstants = [...]*struct {
|
||||
kind BasicKind
|
||||
name string
|
||||
val interface{}
|
||||
}{
|
||||
{UntypedBool, "true", true},
|
||||
{UntypedBool, "false", false},
|
||||
{UntypedInt, "iota", zeroConst},
|
||||
{UntypedNil, "nil", nilConst},
|
||||
var predeclaredConstants = [...]*Const{
|
||||
{"true", Typ[UntypedBool], true, nil},
|
||||
{"false", Typ[UntypedBool], false, nil},
|
||||
{"iota", Typ[UntypedInt], zeroConst, nil},
|
||||
{"nil", Typ[UntypedNil], nilConst, nil},
|
||||
}
|
||||
|
||||
var predeclaredFunctions = [...]*builtin{
|
||||
{aType, _Append, "append", 1, true, false},
|
||||
{aType, _Cap, "cap", 1, false, false},
|
||||
{aType, _Close, "close", 1, false, true},
|
||||
{aType, _Complex, "complex", 2, false, false},
|
||||
{aType, _Copy, "copy", 2, false, true},
|
||||
{aType, _Delete, "delete", 2, false, true},
|
||||
{aType, _Imag, "imag", 1, false, false},
|
||||
{aType, _Len, "len", 1, false, false},
|
||||
{aType, _Make, "make", 1, true, false},
|
||||
{aType, _New, "new", 1, false, false},
|
||||
{aType, _Panic, "panic", 1, false, true},
|
||||
{aType, _Print, "print", 1, true, true},
|
||||
{aType, _Println, "println", 1, true, true},
|
||||
{aType, _Real, "real", 1, false, false},
|
||||
{aType, _Recover, "recover", 0, false, true},
|
||||
{_Append, "append", 1, true, false},
|
||||
{_Cap, "cap", 1, false, false},
|
||||
{_Close, "close", 1, false, true},
|
||||
{_Complex, "complex", 2, false, false},
|
||||
{_Copy, "copy", 2, false, true},
|
||||
{_Delete, "delete", 2, false, true},
|
||||
{_Imag, "imag", 1, false, false},
|
||||
{_Len, "len", 1, false, false},
|
||||
{_Make, "make", 1, true, false},
|
||||
{_New, "new", 1, false, false},
|
||||
{_Panic, "panic", 1, false, true},
|
||||
{_Print, "print", 1, true, true},
|
||||
{_Println, "println", 1, true, true},
|
||||
{_Real, "real", 1, false, false},
|
||||
{_Recover, "recover", 0, false, true},
|
||||
|
||||
{aType, _Alignof, "Alignof", 1, false, false},
|
||||
{aType, _Offsetof, "Offsetof", 1, false, false},
|
||||
{aType, _Sizeof, "Sizeof", 1, false, false},
|
||||
{_Alignof, "Alignof", 1, false, false},
|
||||
{_Offsetof, "Offsetof", 1, false, false},
|
||||
{_Sizeof, "Sizeof", 1, false, false},
|
||||
}
|
||||
|
||||
// commonly used types
|
||||
var (
|
||||
emptyInterface = new(Interface)
|
||||
)
|
||||
|
||||
// commonly used constants
|
||||
var (
|
||||
universeIota *ast.Object
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Universe scope
|
||||
Universe = ast.NewScope(nil)
|
||||
|
||||
// unsafe package and its scope
|
||||
Universe = new(Scope)
|
||||
Unsafe = &Package{Name: "unsafe", Scope: new(Scope)}
|
||||
|
||||
// predeclared types
|
||||
for _, t := range Typ {
|
||||
def(ast.Typ, t.Name, t)
|
||||
def(&TypeName{Name: t.Name, Type: t})
|
||||
}
|
||||
for _, t := range aliases {
|
||||
def(ast.Typ, t.Name, t)
|
||||
def(&TypeName{Name: t.Name, Type: t})
|
||||
}
|
||||
|
||||
// error type
|
||||
{
|
||||
err := &Method{QualifiedName{Name: "Error"}, &Signature{Results: []*Var{{Name: "", Type: Typ[String]}}}}
|
||||
def(ast.Typ, "error", &NamedType{Underlying: &Interface{Methods: []*Method{err}}})
|
||||
def(&TypeName{Name: "error", Type: &NamedType{Underlying: &Interface{Methods: []*Method{err}}}})
|
||||
}
|
||||
|
||||
// predeclared constants
|
||||
for _, t := range predeclaredConstants {
|
||||
obj := def(ast.Con, t.name, Typ[t.kind])
|
||||
obj.Data = t.val
|
||||
for _, c := range predeclaredConstants {
|
||||
def(c)
|
||||
}
|
||||
|
||||
// predeclared functions
|
||||
for _, f := range predeclaredFunctions {
|
||||
def(ast.Fun, f.name, f)
|
||||
def(&Func{Name: f.name, Type: f})
|
||||
}
|
||||
|
||||
universeIota = Universe.Lookup("iota")
|
||||
universeIota = Universe.Lookup("iota").(*Const)
|
||||
}
|
||||
|
||||
// Objects with names containing blanks are internal and not entered into
|
||||
// a scope. Objects with exported names are inserted in the unsafe package
|
||||
// scope; other objects are inserted in the universe scope.
|
||||
//
|
||||
func def(kind ast.ObjKind, name string, typ Type) *ast.Object {
|
||||
// insert non-internal objects into respective scope
|
||||
if strings.Index(name, " ") < 0 {
|
||||
// exported identifiers go into package unsafe
|
||||
if ast.IsExported(name) {
|
||||
var obj Object
|
||||
switch kind {
|
||||
case ast.Typ:
|
||||
obj = &TypeName{Name: name, Type: typ}
|
||||
case ast.Fun:
|
||||
obj = &Func{Name: name, Type: typ}
|
||||
default:
|
||||
unreachable()
|
||||
|
||||
}
|
||||
if Unsafe.Scope.Insert(obj) != nil {
|
||||
panic("internal error: double declaration")
|
||||
}
|
||||
} else {
|
||||
obj := ast.NewObj(kind, name)
|
||||
obj.Decl = Universe
|
||||
obj.Type = typ
|
||||
if typ, ok := typ.(*NamedType); ok {
|
||||
typ.AstObj = obj
|
||||
}
|
||||
if Universe.Insert(obj) != nil {
|
||||
panic("internal error: double declaration")
|
||||
}
|
||||
return obj
|
||||
}
|
||||
func def(obj Object) {
|
||||
name := obj.GetName()
|
||||
if strings.Index(name, " ") >= 0 {
|
||||
return // nothing to do
|
||||
}
|
||||
// fix Obj link for named types
|
||||
if typ, ok := obj.GetType().(*NamedType); ok {
|
||||
typ.Obj = obj.(*TypeName)
|
||||
}
|
||||
// exported identifiers go into package unsafe
|
||||
scope := Universe
|
||||
if ast.IsExported(name) {
|
||||
scope = Unsafe.Scope
|
||||
}
|
||||
if scope.Insert(obj) != nil {
|
||||
panic("internal error: double declaration")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue