diff --git a/go/types/api_test.go b/go/types/api_test.go index 0accef0761a..9b698a18bfb 100644 --- a/go/types/api_test.go +++ b/go/types/api_test.go @@ -588,6 +588,39 @@ func TestInitOrderInfo(t *testing.T) { } } +func TestMultiFileInitOrder(t *testing.T) { + fset := token.NewFileSet() + mustParse := func(src string) *ast.File { + f, err := parser.ParseFile(fset, "main", src, 0) + if err != nil { + t.Fatal(err) + } + return f + } + + fileA := mustParse(`package main; var a = 1`) + fileB := mustParse(`package main; var b = 2`) + + // The initialization order must not depend on the parse + // order of the files, only on the presentation order to + // the type-checker. + for _, test := range []struct { + files []*ast.File + want string + }{ + {[]*ast.File{fileA, fileB}, "[a = 1 b = 2]"}, + {[]*ast.File{fileB, fileA}, "[b = 2 a = 1]"}, + } { + var info Info + if _, err := new(Config).Check("main", fset, test.files, &info); err != nil { + t.Fatal(err) + } + if got := fmt.Sprint(info.InitOrder); got != test.want { + t.Fatalf("got %s; want %s", got, test.want) + } + } +} + func TestFiles(t *testing.T) { var sources = []string{ "package p; type T struct{}; func (T) m1() {}", diff --git a/go/types/initorder.go b/go/types/initorder.go index 2d236100e82..0fd567b2691 100644 --- a/go/types/initorder.go +++ b/go/types/initorder.go @@ -205,8 +205,8 @@ func (a nodeQueue) Swap(i, j int) { func (a nodeQueue) Less(i, j int) bool { x, y := a[i], a[j] // nodes are prioritized by number of incoming dependencies (1st key) - // and source positions (2nd key) - return x.in < y.in || x.in == y.in && x.obj.Pos() < y.obj.Pos() + // and source order (2nd key) + return x.in < y.in || x.in == y.in && x.obj.order() < y.obj.order() } func (a *nodeQueue) Push(x interface{}) { diff --git a/go/types/object.go b/go/types/object.go index 0bd59f16439..6865d74a9fe 100644 --- a/go/types/object.go +++ b/go/types/object.go @@ -31,9 +31,18 @@ type Object interface { // String returns a human-readable string of the object. String() string + // order reflects a package-level object's source order: if object + // a is before object b in the source, then a.order() < b.order(). + // order returns a value > 0 for package-level objects; it returns + // 0 for all other objects (including objects in file scopes). + order() uint32 + // isUsed reports whether the object was marked as 'used'. isUsed() bool + // setOrder sets the order number of the object. It must be > 0. + setOrder(uint32) + // setParent sets the parent scope of the object. setParent(*Scope) @@ -73,6 +82,8 @@ type object struct { pkg *Package name string typ Type + + order_ uint32 used bool } @@ -85,8 +96,10 @@ func (obj *object) Exported() bool { return ast.IsExported(obj.name) } func (obj *object) Id() string { return Id(obj.pkg, obj.name) } func (obj *object) String() string { panic("abstract") } -func (obj *object) isUsed() bool { return obj.used } +func (obj *object) order() uint32 { return obj.order_ } +func (obj *object) isUsed() bool { return obj.used } +func (obj *object) setOrder(order uint32) { assert(order > 0); obj.order_ = order } func (obj *object) setParent(parent *Scope) { obj.parent = parent } func (obj *object) sameId(pkg *Package, name string) bool { @@ -118,7 +131,7 @@ type PkgName struct { } func NewPkgName(pos token.Pos, pkg *Package, name string, imported *Package) *PkgName { - return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], false}, imported} + return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, false}, imported} } // Imported returns the package that was imported. @@ -134,7 +147,7 @@ type Const struct { } func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val exact.Value) *Const { - return &Const{object: object{nil, pos, pkg, name, typ, false}, val: val} + return &Const{object: object{nil, pos, pkg, name, typ, 0, false}, val: val} } func (obj *Const) Val() exact.Value { return obj.val } @@ -145,7 +158,7 @@ type TypeName struct { } func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName { - return &TypeName{object{nil, pos, pkg, name, typ, false}} + return &TypeName{object{nil, pos, pkg, name, typ, 0, false}} } // A Variable represents a declared variable (including function parameters and results, and struct fields). @@ -158,15 +171,15 @@ type Var struct { } func NewVar(pos token.Pos, pkg *Package, name string, typ Type) *Var { - return &Var{object: object{nil, pos, pkg, name, typ, false}} + return &Var{object: object{nil, pos, pkg, name, typ, 0, false}} } func NewParam(pos token.Pos, pkg *Package, name string, typ Type) *Var { - return &Var{object: object{nil, pos, pkg, name, typ, true}} // parameters are always 'used' + return &Var{object: object{nil, pos, pkg, name, typ, 0, true}} // parameters are always 'used' } func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool) *Var { - return &Var{object: object{nil, pos, pkg, name, typ, false}, anonymous: anonymous, isField: true} + return &Var{object: object{nil, pos, pkg, name, typ, 0, false}, anonymous: anonymous, isField: true} } func (obj *Var) Anonymous() bool { return obj.anonymous } @@ -186,7 +199,7 @@ func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func { if sig != nil { typ = sig } - return &Func{object{nil, pos, pkg, name, typ, false}} + return &Func{object{nil, pos, pkg, name, typ, 0, false}} } // FullName returns the package- or receiver-type-qualified name of diff --git a/go/types/ordering.go b/go/types/ordering.go index 0052f7c8cca..6bb98f2dc10 100644 --- a/go/types/ordering.go +++ b/go/types/ordering.go @@ -123,5 +123,5 @@ func orderedSetObjects(set map[Object]bool) []Object { type inSourceOrder []Object func (a inSourceOrder) Len() int { return len(a) } -func (a inSourceOrder) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() } +func (a inSourceOrder) Less(i, j int) bool { return a[i].order() < a[j].order() } func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] } diff --git a/go/types/resolver.go b/go/types/resolver.go index 4e0c48f4ee0..db4f7dbb2c4 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -110,6 +110,7 @@ func (check *Checker) declarePkgObj(ident *ast.Ident, obj Object, d *declInfo) { check.declare(check.pkg.scope, ident, obj) check.objMap[obj] = d + obj.setOrder(uint32(len(check.objMap))) } // collectObjects collects all file and package objects and inserts them @@ -341,6 +342,7 @@ func (check *Checker) collectObjects() { } info := &declInfo{file: fileScope, fdecl: d} check.objMap[obj] = info + obj.setOrder(uint32(len(check.objMap))) default: check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)