mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
go/types, types2: swap object.color for Checker.objPathIdx
The type checker assigns types to objects. An object can be in 1 of 3 states, which are mapped to colors. This is stored as a field on the object. - white : type not known, awaiting processing - grey : type pending, in processing - black : type known, done processing With the addition of Checker.objPathIdx, which maps objects to a path index, presence in the map could signal grey coloring. White and black coloring can be signaled by presence of object.typ (a simple nil check). This change removes the object.color field and its associated methods, replacing it with an equivalent use of Checker.objPathIdx. Checker.objPathIdx is integrated into the existing push and pop methods, while slightly simplifying their signatures. Note that the concept of object coloring remains the same - we are merely changing and simplifying the mechanism which represents the colors. Change-Id: I91fb5e9a59dcb34c08ffc5b4ebc3f20a400094b6 Reviewed-on: https://go-review.googlesource.com/c/go/+/715840 Reviewed-by: Robert Griesemer <gri@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Mark Freeman <markfreeman@google.com>
This commit is contained in:
parent
9daaab305c
commit
ddd8558e61
14 changed files with 168 additions and 346 deletions
|
|
@ -171,12 +171,13 @@ type Checker struct {
|
||||||
usedPkgNames map[*PkgName]bool // set of used package names
|
usedPkgNames map[*PkgName]bool // set of used package names
|
||||||
mono monoGraph // graph for detecting non-monomorphizable instantiation loops
|
mono monoGraph // graph for detecting non-monomorphizable instantiation loops
|
||||||
|
|
||||||
firstErr error // first error encountered
|
firstErr error // first error encountered
|
||||||
methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
|
methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
|
||||||
untyped map[syntax.Expr]exprInfo // map of expressions without final type
|
untyped map[syntax.Expr]exprInfo // map of expressions without final type
|
||||||
delayed []action // stack of delayed action segments; segments are processed in FIFO order
|
delayed []action // stack of delayed action segments; segments are processed in FIFO order
|
||||||
objPath []Object // path of object dependencies during type inference (for cycle reporting)
|
objPath []Object // path of object dependencies during type-checking (for cycle reporting)
|
||||||
cleaners []cleaner // list of types that may need a final cleanup at the end of type-checking
|
objPathIdx map[Object]int // map of object to object path index during type-checking (for cycle reporting)
|
||||||
|
cleaners []cleaner // list of types that may need a final cleanup at the end of type-checking
|
||||||
|
|
||||||
// environment within which the current object is type-checked (valid only
|
// environment within which the current object is type-checked (valid only
|
||||||
// for the duration of type-checking a specific object)
|
// for the duration of type-checking a specific object)
|
||||||
|
|
@ -248,19 +249,22 @@ func (check *Checker) later(f func()) *action {
|
||||||
return &check.delayed[i]
|
return &check.delayed[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// push pushes obj onto the object path and returns its index in the path.
|
// push pushes obj onto the object path and records its index in the path index map.
|
||||||
func (check *Checker) push(obj Object) int {
|
func (check *Checker) push(obj Object) {
|
||||||
|
if check.objPathIdx == nil {
|
||||||
|
check.objPathIdx = make(map[Object]int)
|
||||||
|
}
|
||||||
|
check.objPathIdx[obj] = len(check.objPath)
|
||||||
check.objPath = append(check.objPath, obj)
|
check.objPath = append(check.objPath, obj)
|
||||||
return len(check.objPath) - 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pop pops and returns the topmost object from the object path.
|
// pop pops an object from the object path and removes it from the path index map.
|
||||||
func (check *Checker) pop() Object {
|
func (check *Checker) pop() {
|
||||||
i := len(check.objPath) - 1
|
i := len(check.objPath) - 1
|
||||||
obj := check.objPath[i]
|
obj := check.objPath[i]
|
||||||
check.objPath[i] = nil
|
check.objPath[i] = nil // help the garbage collector
|
||||||
check.objPath = check.objPath[:i]
|
check.objPath = check.objPath[:i]
|
||||||
return obj
|
delete(check.objPathIdx, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
type cleaner interface {
|
type cleaner interface {
|
||||||
|
|
@ -319,6 +323,7 @@ func (check *Checker) initFiles(files []*syntax.File) {
|
||||||
check.untyped = nil
|
check.untyped = nil
|
||||||
check.delayed = nil
|
check.delayed = nil
|
||||||
check.objPath = nil
|
check.objPath = nil
|
||||||
|
check.objPathIdx = nil
|
||||||
check.cleaners = nil
|
check.cleaners = nil
|
||||||
|
|
||||||
// We must initialize usedVars and usedPkgNames both here and in NewChecker,
|
// We must initialize usedVars and usedPkgNames both here and in NewChecker,
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,6 @@ func (check *Checker) directCycle(tname *TypeName, pathIdx map[*TypeName]int) {
|
||||||
// tname is marked grey - we have a cycle on the path beginning at start.
|
// tname is marked grey - we have a cycle on the path beginning at start.
|
||||||
// Mark tname as invalid.
|
// Mark tname as invalid.
|
||||||
tname.setType(Typ[Invalid])
|
tname.setType(Typ[Invalid])
|
||||||
tname.setColor(black)
|
|
||||||
|
|
||||||
// collect type names on cycle
|
// collect type names on cycle
|
||||||
var cycle []Object
|
var cycle []Object
|
||||||
|
|
|
||||||
|
|
@ -62,114 +62,77 @@ func (check *Checker) objDecl(obj Object, def *TypeName) {
|
||||||
if check.indent == 0 {
|
if check.indent == 0 {
|
||||||
fmt.Println() // empty line between top-level objects for readability
|
fmt.Println() // empty line between top-level objects for readability
|
||||||
}
|
}
|
||||||
check.trace(obj.Pos(), "-- checking %s (%s, objPath = %s)", obj, obj.color(), pathString(check.objPath))
|
check.trace(obj.Pos(), "-- checking %s (objPath = %s)", obj, pathString(check.objPath))
|
||||||
check.indent++
|
check.indent++
|
||||||
defer func() {
|
defer func() {
|
||||||
check.indent--
|
check.indent--
|
||||||
check.trace(obj.Pos(), "=> %s (%s)", obj, obj.color())
|
check.trace(obj.Pos(), "=> %s", obj)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checking the declaration of obj means inferring its type
|
// Checking the declaration of an object means determining its type
|
||||||
// (and possibly its value, for constants).
|
// (and also its value for constants). An object (and thus its type)
|
||||||
// An object's type (and thus the object) may be in one of
|
// may be in 1 of 3 states:
|
||||||
// three states which are expressed by colors:
|
|
||||||
//
|
//
|
||||||
// - an object whose type is not yet known is painted white (initial color)
|
// - not in Checker.objPathIdx and type == nil : type is not yet known (white)
|
||||||
// - an object whose type is in the process of being inferred is painted grey
|
// - in Checker.objPathIdx : type is pending (grey)
|
||||||
// - an object whose type is fully inferred is painted black
|
// - not in Checker.objPathIdx and type != nil : type is known (black)
|
||||||
//
|
//
|
||||||
// During type inference, an object's color changes from white to grey
|
// During type-checking, an object changes from white to grey to black.
|
||||||
// to black (pre-declared objects are painted black from the start).
|
// Predeclared objects start as black (their type is known without checking).
|
||||||
// A black object (i.e., its type) can only depend on (refer to) other black
|
|
||||||
// ones. White and grey objects may depend on white and black objects.
|
|
||||||
// A dependency on a grey object indicates a cycle which may or may not be
|
|
||||||
// valid.
|
|
||||||
//
|
//
|
||||||
// When objects turn grey, they are pushed on the object path (a stack);
|
// A black object may only depend on (refer to) to other black objects. White
|
||||||
// they are popped again when they turn black. Thus, if a grey object (a
|
// and grey objects may depend on white or black objects. A dependency on a
|
||||||
// cycle) is encountered, it is on the object path, and all the objects
|
// grey object indicates a (possibly invalid) cycle.
|
||||||
// it depends on are the remaining objects on that path. Color encoding
|
//
|
||||||
// is such that the color value of a grey object indicates the index of
|
// When an object is marked grey, it is pushed onto the object path (a stack)
|
||||||
// that object in the object path.
|
// and its index in the path is recorded in the path index map. It is popped
|
||||||
|
// and removed from the map when its type is determined (and marked black).
|
||||||
|
|
||||||
// During type-checking, white objects may be assigned a type without
|
// If this object is grey, we have a (possibly invalid) cycle. This is signaled
|
||||||
// traversing through objDecl; e.g., when initializing constants and
|
// by a non-nil type for the object, except for constants and variables whose
|
||||||
// variables. Update the colors of those objects here (rather than
|
// type may be non-nil (known), or nil if it depends on a not-yet known
|
||||||
// everywhere where we set the type) to satisfy the color invariants.
|
// initialization value.
|
||||||
if obj.color() == white && obj.Type() != nil {
|
//
|
||||||
obj.setColor(black)
|
// In the former case, set the type to Typ[Invalid] because we have an
|
||||||
return
|
// initialization cycle. The cycle error will be reported later, when
|
||||||
}
|
// determining initialization order.
|
||||||
|
//
|
||||||
switch obj.color() {
|
// TODO(gri) Report cycle here and simplify initialization order code.
|
||||||
case white:
|
if _, ok := check.objPathIdx[obj]; ok {
|
||||||
assert(obj.Type() == nil)
|
|
||||||
// All color values other than white and black are considered grey.
|
|
||||||
// Because black and white are < grey, all values >= grey are grey.
|
|
||||||
// Use those values to encode the object's index into the object path.
|
|
||||||
obj.setColor(grey + color(check.push(obj)))
|
|
||||||
defer func() {
|
|
||||||
check.pop().setColor(black)
|
|
||||||
}()
|
|
||||||
|
|
||||||
case black:
|
|
||||||
assert(obj.Type() != nil)
|
|
||||||
return
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Color values other than white or black are considered grey.
|
|
||||||
fallthrough
|
|
||||||
|
|
||||||
case grey:
|
|
||||||
// We have a (possibly invalid) cycle.
|
|
||||||
// In the existing code, this is marked by a non-nil type
|
|
||||||
// for the object except for constants and variables whose
|
|
||||||
// type may be non-nil (known), or nil if it depends on the
|
|
||||||
// not-yet known initialization value.
|
|
||||||
// In the former case, set the type to Typ[Invalid] because
|
|
||||||
// we have an initialization cycle. The cycle error will be
|
|
||||||
// reported later, when determining initialization order.
|
|
||||||
// TODO(gri) Report cycle here and simplify initialization
|
|
||||||
// order code.
|
|
||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
case *Const:
|
case *Const, *Var:
|
||||||
if !check.validCycle(obj) || obj.typ == nil {
|
if !check.validCycle(obj) || obj.Type() == nil {
|
||||||
obj.typ = Typ[Invalid]
|
obj.setType(Typ[Invalid])
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Var:
|
|
||||||
if !check.validCycle(obj) || obj.typ == nil {
|
|
||||||
obj.typ = Typ[Invalid]
|
|
||||||
}
|
|
||||||
|
|
||||||
case *TypeName:
|
case *TypeName:
|
||||||
if !check.validCycle(obj) {
|
if !check.validCycle(obj) {
|
||||||
// break cycle
|
obj.setType(Typ[Invalid])
|
||||||
// (without this, calling underlying()
|
|
||||||
// below may lead to an endless loop
|
|
||||||
// if we have a cycle for a defined
|
|
||||||
// (*Named) type)
|
|
||||||
obj.typ = Typ[Invalid]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Func:
|
case *Func:
|
||||||
if !check.validCycle(obj) {
|
if !check.validCycle(obj) {
|
||||||
// Don't set obj.typ to Typ[Invalid] here
|
// Don't set type to Typ[Invalid]; plenty of code asserts that
|
||||||
// because plenty of code type-asserts that
|
// functions have a *Signature type. Instead, leave the type
|
||||||
// functions have a *Signature type. Grey
|
// as an empty signature, which makes it impossible to
|
||||||
// functions have their type set to an empty
|
|
||||||
// signature which makes it impossible to
|
|
||||||
// initialize a variable with the function.
|
// initialize a variable with the function.
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(obj.Type() != nil)
|
assert(obj.Type() != nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if obj.Type() != nil { // black, meaning it's already type-checked
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// white, meaning it must be type-checked
|
||||||
|
|
||||||
|
check.push(obj)
|
||||||
|
defer check.pop()
|
||||||
|
|
||||||
d := check.objMap[obj]
|
d := check.objMap[obj]
|
||||||
if d == nil {
|
if d == nil {
|
||||||
check.dump("%v: %s should have been declared", obj.Pos(), obj)
|
check.dump("%v: %s should have been declared", obj.Pos(), obj)
|
||||||
|
|
@ -221,8 +184,8 @@ func (check *Checker) validCycle(obj Object) (valid bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count cycle objects.
|
// Count cycle objects.
|
||||||
assert(obj.color() >= grey)
|
start, found := check.objPathIdx[obj]
|
||||||
start := obj.color() - grey // index of obj in objPath
|
assert(found)
|
||||||
cycle := check.objPath[start:]
|
cycle := check.objPath[start:]
|
||||||
tparCycle := false // if set, the cycle is through a type parameter list
|
tparCycle := false // if set, the cycle is through a type parameter list
|
||||||
nval := 0 // number of (constant or variable) values in the cycle
|
nval := 0 // number of (constant or variable) values in the cycle
|
||||||
|
|
@ -764,17 +727,8 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
|
||||||
sig := new(Signature)
|
sig := new(Signature)
|
||||||
obj.typ = sig // guard against cycles
|
obj.typ = sig // guard against cycles
|
||||||
|
|
||||||
// Avoid cycle error when referring to method while type-checking the signature.
|
|
||||||
// This avoids a nuisance in the best case (non-parameterized receiver type) and
|
|
||||||
// since the method is not a type, we get an error. If we have a parameterized
|
|
||||||
// receiver type, instantiating the receiver type leads to the instantiation of
|
|
||||||
// its methods, and we don't want a cycle error in that case.
|
|
||||||
// TODO(gri) review if this is correct and/or whether we still need this?
|
|
||||||
saved := obj.color_
|
|
||||||
obj.color_ = black
|
|
||||||
fdecl := decl.fdecl
|
fdecl := decl.fdecl
|
||||||
check.funcType(sig, fdecl.Recv, fdecl.TParamList, fdecl.Type)
|
check.funcType(sig, fdecl.Recv, fdecl.TParamList, fdecl.Type)
|
||||||
obj.color_ = saved
|
|
||||||
|
|
||||||
// Set the scope's extent to the complete "func (...) { ... }"
|
// Set the scope's extent to the complete "func (...) { ... }"
|
||||||
// so that Scope.Innermost works correctly.
|
// so that Scope.Innermost works correctly.
|
||||||
|
|
@ -921,10 +875,9 @@ func (check *Checker) declStmt(list []syntax.Decl) {
|
||||||
// the innermost containing block."
|
// the innermost containing block."
|
||||||
scopePos := s.Name.Pos()
|
scopePos := s.Name.Pos()
|
||||||
check.declare(check.scope, s.Name, obj, scopePos)
|
check.declare(check.scope, s.Name, obj, scopePos)
|
||||||
// mark and unmark type before calling typeDecl; its type is still nil (see Checker.objDecl)
|
check.push(obj) // mark as grey
|
||||||
obj.setColor(grey + color(check.push(obj)))
|
defer check.pop()
|
||||||
check.typeDecl(obj, s, nil)
|
check.typeDecl(obj, s, nil)
|
||||||
check.pop().setColor(black)
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
check.errorf(s, InvalidSyntaxTree, "unknown syntax.Decl node %T", s)
|
check.errorf(s, InvalidSyntaxTree, "unknown syntax.Decl node %T", s)
|
||||||
|
|
|
||||||
|
|
@ -42,18 +42,12 @@ type Object interface {
|
||||||
// 0 for all other objects (including objects in file scopes).
|
// 0 for all other objects (including objects in file scopes).
|
||||||
order() uint32
|
order() uint32
|
||||||
|
|
||||||
// color returns the object's color.
|
|
||||||
color() color
|
|
||||||
|
|
||||||
// setType sets the type of the object.
|
// setType sets the type of the object.
|
||||||
setType(Type)
|
setType(Type)
|
||||||
|
|
||||||
// setOrder sets the order number of the object. It must be > 0.
|
// setOrder sets the order number of the object. It must be > 0.
|
||||||
setOrder(uint32)
|
setOrder(uint32)
|
||||||
|
|
||||||
// setColor sets the object's color. It must not be white.
|
|
||||||
setColor(color color)
|
|
||||||
|
|
||||||
// setParent sets the parent scope of the object.
|
// setParent sets the parent scope of the object.
|
||||||
setParent(*Scope)
|
setParent(*Scope)
|
||||||
|
|
||||||
|
|
@ -102,41 +96,9 @@ type object struct {
|
||||||
name string
|
name string
|
||||||
typ Type
|
typ Type
|
||||||
order_ uint32
|
order_ uint32
|
||||||
color_ color
|
|
||||||
scopePos_ syntax.Pos
|
scopePos_ syntax.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
// color encodes the color of an object (see Checker.objDecl for details).
|
|
||||||
type color uint32
|
|
||||||
|
|
||||||
// An object may be painted in one of three colors.
|
|
||||||
// Color values other than white or black are considered grey.
|
|
||||||
const (
|
|
||||||
white color = iota
|
|
||||||
black
|
|
||||||
grey // must be > white and black
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c color) String() string {
|
|
||||||
switch c {
|
|
||||||
case white:
|
|
||||||
return "white"
|
|
||||||
case black:
|
|
||||||
return "black"
|
|
||||||
default:
|
|
||||||
return "grey"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// colorFor returns the (initial) color for an object depending on
|
|
||||||
// whether its type t is known or not.
|
|
||||||
func colorFor(t Type) color {
|
|
||||||
if t != nil {
|
|
||||||
return black
|
|
||||||
}
|
|
||||||
return white
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parent returns the scope in which the object is declared.
|
// Parent returns the scope in which the object is declared.
|
||||||
// The result is nil for methods and struct fields.
|
// The result is nil for methods and struct fields.
|
||||||
func (obj *object) Parent() *Scope { return obj.parent }
|
func (obj *object) Parent() *Scope { return obj.parent }
|
||||||
|
|
@ -164,13 +126,11 @@ func (obj *object) Id() string { return Id(obj.pkg, obj.name) }
|
||||||
|
|
||||||
func (obj *object) String() string { panic("abstract") }
|
func (obj *object) String() string { panic("abstract") }
|
||||||
func (obj *object) order() uint32 { return obj.order_ }
|
func (obj *object) order() uint32 { return obj.order_ }
|
||||||
func (obj *object) color() color { return obj.color_ }
|
|
||||||
func (obj *object) scopePos() syntax.Pos { return obj.scopePos_ }
|
func (obj *object) scopePos() syntax.Pos { return obj.scopePos_ }
|
||||||
|
|
||||||
func (obj *object) setParent(parent *Scope) { obj.parent = parent }
|
func (obj *object) setParent(parent *Scope) { obj.parent = parent }
|
||||||
func (obj *object) setType(typ Type) { obj.typ = typ }
|
func (obj *object) setType(typ Type) { obj.typ = typ }
|
||||||
func (obj *object) setOrder(order uint32) { assert(order > 0); obj.order_ = order }
|
func (obj *object) setOrder(order uint32) { assert(order > 0); obj.order_ = order }
|
||||||
func (obj *object) setColor(color color) { assert(color != white); obj.color_ = color }
|
|
||||||
func (obj *object) setScopePos(pos syntax.Pos) { obj.scopePos_ = pos }
|
func (obj *object) setScopePos(pos syntax.Pos) { obj.scopePos_ = pos }
|
||||||
|
|
||||||
func (obj *object) sameId(pkg *Package, name string, foldCase bool) bool {
|
func (obj *object) sameId(pkg *Package, name string, foldCase bool) bool {
|
||||||
|
|
@ -247,7 +207,7 @@ type PkgName struct {
|
||||||
// NewPkgName returns a new PkgName object representing an imported package.
|
// NewPkgName returns a new PkgName object representing an imported package.
|
||||||
// The remaining arguments set the attributes found with all Objects.
|
// The remaining arguments set the attributes found with all Objects.
|
||||||
func NewPkgName(pos syntax.Pos, pkg *Package, name string, imported *Package) *PkgName {
|
func NewPkgName(pos syntax.Pos, pkg *Package, name string, imported *Package) *PkgName {
|
||||||
return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, black, nopos}, imported}
|
return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, nopos}, imported}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Imported returns the package that was imported.
|
// Imported returns the package that was imported.
|
||||||
|
|
@ -263,7 +223,7 @@ type Const struct {
|
||||||
// NewConst returns a new constant with value val.
|
// NewConst returns a new constant with value val.
|
||||||
// The remaining arguments set the attributes found with all Objects.
|
// The remaining arguments set the attributes found with all Objects.
|
||||||
func NewConst(pos syntax.Pos, pkg *Package, name string, typ Type, val constant.Value) *Const {
|
func NewConst(pos syntax.Pos, pkg *Package, name string, typ Type, val constant.Value) *Const {
|
||||||
return &Const{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, val}
|
return &Const{object{nil, pos, pkg, name, typ, 0, nopos}, val}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Val returns the constant's value.
|
// Val returns the constant's value.
|
||||||
|
|
@ -288,7 +248,7 @@ type TypeName struct {
|
||||||
// argument for NewNamed, which will set the TypeName's type as a side-
|
// argument for NewNamed, which will set the TypeName's type as a side-
|
||||||
// effect.
|
// effect.
|
||||||
func NewTypeName(pos syntax.Pos, pkg *Package, name string, typ Type) *TypeName {
|
func NewTypeName(pos syntax.Pos, pkg *Package, name string, typ Type) *TypeName {
|
||||||
return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}}
|
return &TypeName{object{nil, pos, pkg, name, typ, 0, nopos}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTypeNameLazy returns a new defined type like NewTypeName, but it
|
// NewTypeNameLazy returns a new defined type like NewTypeName, but it
|
||||||
|
|
@ -402,7 +362,7 @@ func NewField(pos syntax.Pos, pkg *Package, name string, typ Type, embedded bool
|
||||||
// newVar returns a new variable.
|
// newVar returns a new variable.
|
||||||
// The arguments set the attributes found with all Objects.
|
// The arguments set the attributes found with all Objects.
|
||||||
func newVar(kind VarKind, pos syntax.Pos, pkg *Package, name string, typ Type) *Var {
|
func newVar(kind VarKind, pos syntax.Pos, pkg *Package, name string, typ Type) *Var {
|
||||||
return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, kind: kind}
|
return &Var{object: object{nil, pos, pkg, name, typ, 0, nopos}, kind: kind}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anonymous reports whether the variable is an embedded field.
|
// Anonymous reports whether the variable is an embedded field.
|
||||||
|
|
@ -452,7 +412,7 @@ func NewFunc(pos syntax.Pos, pkg *Package, name string, sig *Signature) *Func {
|
||||||
// as this would violate object.{Type,color} invariants.
|
// as this would violate object.{Type,color} invariants.
|
||||||
// TODO(adonovan): propose to disallow NewFunc with nil *Signature.
|
// TODO(adonovan): propose to disallow NewFunc with nil *Signature.
|
||||||
}
|
}
|
||||||
return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, false, nil}
|
return &Func{object{nil, pos, pkg, name, typ, 0, nopos}, false, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signature returns the signature (type) of the function or method.
|
// Signature returns the signature (type) of the function or method.
|
||||||
|
|
@ -534,7 +494,7 @@ type Label struct {
|
||||||
|
|
||||||
// NewLabel returns a new label.
|
// NewLabel returns a new label.
|
||||||
func NewLabel(pos syntax.Pos, pkg *Package, name string) *Label {
|
func NewLabel(pos syntax.Pos, pkg *Package, name string) *Label {
|
||||||
return &Label{object{pos: pos, pkg: pkg, name: name, typ: Typ[Invalid], color_: black}, false}
|
return &Label{object{pos: pos, pkg: pkg, name: name, typ: Typ[Invalid]}, false}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Builtin represents a built-in function.
|
// A Builtin represents a built-in function.
|
||||||
|
|
@ -545,7 +505,7 @@ type Builtin struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBuiltin(id builtinId) *Builtin {
|
func newBuiltin(id builtinId) *Builtin {
|
||||||
return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid], color_: black}, id}
|
return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid]}, id}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nil represents the predeclared value nil.
|
// Nil represents the predeclared value nil.
|
||||||
|
|
|
||||||
|
|
@ -217,10 +217,8 @@ func (*lazyObject) Exported() bool { panic("unreachable") }
|
||||||
func (*lazyObject) Id() string { panic("unreachable") }
|
func (*lazyObject) Id() string { panic("unreachable") }
|
||||||
func (*lazyObject) String() string { panic("unreachable") }
|
func (*lazyObject) String() string { panic("unreachable") }
|
||||||
func (*lazyObject) order() uint32 { panic("unreachable") }
|
func (*lazyObject) order() uint32 { panic("unreachable") }
|
||||||
func (*lazyObject) color() color { panic("unreachable") }
|
|
||||||
func (*lazyObject) setType(Type) { panic("unreachable") }
|
func (*lazyObject) setType(Type) { panic("unreachable") }
|
||||||
func (*lazyObject) setOrder(uint32) { panic("unreachable") }
|
func (*lazyObject) setOrder(uint32) { panic("unreachable") }
|
||||||
func (*lazyObject) setColor(color color) { panic("unreachable") }
|
|
||||||
func (*lazyObject) setParent(*Scope) { panic("unreachable") }
|
func (*lazyObject) setParent(*Scope) { panic("unreachable") }
|
||||||
func (*lazyObject) sameId(*Package, string, bool) bool { panic("unreachable") }
|
func (*lazyObject) sameId(*Package, string, bool) bool { panic("unreachable") }
|
||||||
func (*lazyObject) scopePos() syntax.Pos { panic("unreachable") }
|
func (*lazyObject) scopePos() syntax.Pos { panic("unreachable") }
|
||||||
|
|
|
||||||
|
|
@ -36,14 +36,14 @@ func TestSizeof(t *testing.T) {
|
||||||
{term{}, 12, 24},
|
{term{}, 12, 24},
|
||||||
|
|
||||||
// Objects
|
// Objects
|
||||||
{PkgName{}, 60, 96},
|
{PkgName{}, 56, 96},
|
||||||
{Const{}, 64, 104},
|
{Const{}, 60, 104},
|
||||||
{TypeName{}, 56, 88},
|
{TypeName{}, 52, 88},
|
||||||
{Var{}, 64, 104},
|
{Var{}, 60, 104},
|
||||||
{Func{}, 64, 104},
|
{Func{}, 60, 104},
|
||||||
{Label{}, 60, 96},
|
{Label{}, 56, 96},
|
||||||
{Builtin{}, 60, 96},
|
{Builtin{}, 56, 96},
|
||||||
{Nil{}, 56, 88},
|
{Nil{}, 52, 88},
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
{Scope{}, 60, 104},
|
{Scope{}, 60, 104},
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,6 @@ func defPredeclaredTypes() {
|
||||||
// interface.
|
// interface.
|
||||||
{
|
{
|
||||||
universeAnyNoAlias = NewTypeName(nopos, nil, "any", &Interface{complete: true, tset: &topTypeSet})
|
universeAnyNoAlias = NewTypeName(nopos, nil, "any", &Interface{complete: true, tset: &topTypeSet})
|
||||||
universeAnyNoAlias.setColor(black)
|
|
||||||
// ensure that the any TypeName reports a consistent Parent, after
|
// ensure that the any TypeName reports a consistent Parent, after
|
||||||
// hijacking Universe.Lookup with gotypesalias=0.
|
// hijacking Universe.Lookup with gotypesalias=0.
|
||||||
universeAnyNoAlias.setParent(Universe)
|
universeAnyNoAlias.setParent(Universe)
|
||||||
|
|
@ -107,7 +106,6 @@ func defPredeclaredTypes() {
|
||||||
// into the Universe, but we lean toward the future and insert the Alias
|
// into the Universe, but we lean toward the future and insert the Alias
|
||||||
// representation.
|
// representation.
|
||||||
universeAnyAlias = NewTypeName(nopos, nil, "any", nil)
|
universeAnyAlias = NewTypeName(nopos, nil, "any", nil)
|
||||||
universeAnyAlias.setColor(black)
|
|
||||||
_ = NewAlias(universeAnyAlias, universeAnyNoAlias.Type().Underlying()) // Link TypeName and Alias
|
_ = NewAlias(universeAnyAlias, universeAnyNoAlias.Type().Underlying()) // Link TypeName and Alias
|
||||||
def(universeAnyAlias)
|
def(universeAnyAlias)
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +113,6 @@ func defPredeclaredTypes() {
|
||||||
// type error interface{ Error() string }
|
// type error interface{ Error() string }
|
||||||
{
|
{
|
||||||
obj := NewTypeName(nopos, nil, "error", nil)
|
obj := NewTypeName(nopos, nil, "error", nil)
|
||||||
obj.setColor(black)
|
|
||||||
typ := (*Checker)(nil).newNamed(obj, nil, nil)
|
typ := (*Checker)(nil).newNamed(obj, nil, nil)
|
||||||
|
|
||||||
// error.Error() string
|
// error.Error() string
|
||||||
|
|
@ -136,7 +133,6 @@ func defPredeclaredTypes() {
|
||||||
// type comparable interface{} // marked as comparable
|
// type comparable interface{} // marked as comparable
|
||||||
{
|
{
|
||||||
obj := NewTypeName(nopos, nil, "comparable", nil)
|
obj := NewTypeName(nopos, nil, "comparable", nil)
|
||||||
obj.setColor(black)
|
|
||||||
typ := (*Checker)(nil).newNamed(obj, nil, nil)
|
typ := (*Checker)(nil).newNamed(obj, nil, nil)
|
||||||
|
|
||||||
// interface{} // marked as comparable
|
// interface{} // marked as comparable
|
||||||
|
|
@ -165,7 +161,7 @@ func defPredeclaredConsts() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func defPredeclaredNil() {
|
func defPredeclaredNil() {
|
||||||
def(&Nil{object{name: "nil", typ: Typ[UntypedNil], color_: black}})
|
def(&Nil{object{name: "nil", typ: Typ[UntypedNil]}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// A builtinId is the id of a builtin function.
|
// A builtinId is the id of a builtin function.
|
||||||
|
|
@ -289,7 +285,7 @@ func init() {
|
||||||
// a scope. Objects with exported names are inserted in the unsafe package
|
// a scope. Objects with exported names are inserted in the unsafe package
|
||||||
// scope; other objects are inserted in the universe scope.
|
// scope; other objects are inserted in the universe scope.
|
||||||
func def(obj Object) {
|
func def(obj Object) {
|
||||||
assert(obj.color() == black)
|
assert(obj.Type() != nil)
|
||||||
name := obj.Name()
|
name := obj.Name()
|
||||||
if strings.Contains(name, " ") {
|
if strings.Contains(name, " ") {
|
||||||
return // nothing to do
|
return // nothing to do
|
||||||
|
|
|
||||||
|
|
@ -191,12 +191,13 @@ type Checker struct {
|
||||||
usedPkgNames map[*PkgName]bool // set of used package names
|
usedPkgNames map[*PkgName]bool // set of used package names
|
||||||
mono monoGraph // graph for detecting non-monomorphizable instantiation loops
|
mono monoGraph // graph for detecting non-monomorphizable instantiation loops
|
||||||
|
|
||||||
firstErr error // first error encountered
|
firstErr error // first error encountered
|
||||||
methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
|
methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
|
||||||
untyped map[ast.Expr]exprInfo // map of expressions without final type
|
untyped map[ast.Expr]exprInfo // map of expressions without final type
|
||||||
delayed []action // stack of delayed action segments; segments are processed in FIFO order
|
delayed []action // stack of delayed action segments; segments are processed in FIFO order
|
||||||
objPath []Object // path of object dependencies during type inference (for cycle reporting)
|
objPath []Object // path of object dependencies during type-checking (for cycle reporting)
|
||||||
cleaners []cleaner // list of types that may need a final cleanup at the end of type-checking
|
objPathIdx map[Object]int // map of object to object path index during type-checking (for cycle reporting)
|
||||||
|
cleaners []cleaner // list of types that may need a final cleanup at the end of type-checking
|
||||||
|
|
||||||
// environment within which the current object is type-checked (valid only
|
// environment within which the current object is type-checked (valid only
|
||||||
// for the duration of type-checking a specific object)
|
// for the duration of type-checking a specific object)
|
||||||
|
|
@ -268,19 +269,22 @@ func (check *Checker) later(f func()) *action {
|
||||||
return &check.delayed[i]
|
return &check.delayed[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// push pushes obj onto the object path and returns its index in the path.
|
// push pushes obj onto the object path and records its index in the path index map.
|
||||||
func (check *Checker) push(obj Object) int {
|
func (check *Checker) push(obj Object) {
|
||||||
|
if check.objPathIdx == nil {
|
||||||
|
check.objPathIdx = make(map[Object]int)
|
||||||
|
}
|
||||||
|
check.objPathIdx[obj] = len(check.objPath)
|
||||||
check.objPath = append(check.objPath, obj)
|
check.objPath = append(check.objPath, obj)
|
||||||
return len(check.objPath) - 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pop pops and returns the topmost object from the object path.
|
// pop pops an object from the object path and removes it from the path index map.
|
||||||
func (check *Checker) pop() Object {
|
func (check *Checker) pop() {
|
||||||
i := len(check.objPath) - 1
|
i := len(check.objPath) - 1
|
||||||
obj := check.objPath[i]
|
obj := check.objPath[i]
|
||||||
check.objPath[i] = nil
|
check.objPath[i] = nil // help the garbage collector
|
||||||
check.objPath = check.objPath[:i]
|
check.objPath = check.objPath[:i]
|
||||||
return obj
|
delete(check.objPathIdx, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
type cleaner interface {
|
type cleaner interface {
|
||||||
|
|
@ -343,6 +347,7 @@ func (check *Checker) initFiles(files []*ast.File) {
|
||||||
check.untyped = nil
|
check.untyped = nil
|
||||||
check.delayed = nil
|
check.delayed = nil
|
||||||
check.objPath = nil
|
check.objPath = nil
|
||||||
|
check.objPathIdx = nil
|
||||||
check.cleaners = nil
|
check.cleaners = nil
|
||||||
|
|
||||||
// We must initialize usedVars and usedPkgNames both here and in NewChecker,
|
// We must initialize usedVars and usedPkgNames both here and in NewChecker,
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,6 @@ func (check *Checker) directCycle(tname *TypeName, pathIdx map[*TypeName]int) {
|
||||||
// tname is marked grey - we have a cycle on the path beginning at start.
|
// tname is marked grey - we have a cycle on the path beginning at start.
|
||||||
// Mark tname as invalid.
|
// Mark tname as invalid.
|
||||||
tname.setType(Typ[Invalid])
|
tname.setType(Typ[Invalid])
|
||||||
tname.setColor(black)
|
|
||||||
|
|
||||||
// collect type names on cycle
|
// collect type names on cycle
|
||||||
var cycle []Object
|
var cycle []Object
|
||||||
|
|
|
||||||
|
|
@ -63,114 +63,77 @@ func (check *Checker) objDecl(obj Object, def *TypeName) {
|
||||||
if check.indent == 0 {
|
if check.indent == 0 {
|
||||||
fmt.Println() // empty line between top-level objects for readability
|
fmt.Println() // empty line between top-level objects for readability
|
||||||
}
|
}
|
||||||
check.trace(obj.Pos(), "-- checking %s (%s, objPath = %s)", obj, obj.color(), pathString(check.objPath))
|
check.trace(obj.Pos(), "-- checking %s (objPath = %s)", obj, pathString(check.objPath))
|
||||||
check.indent++
|
check.indent++
|
||||||
defer func() {
|
defer func() {
|
||||||
check.indent--
|
check.indent--
|
||||||
check.trace(obj.Pos(), "=> %s (%s)", obj, obj.color())
|
check.trace(obj.Pos(), "=> %s", obj)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checking the declaration of obj means inferring its type
|
// Checking the declaration of an object means determining its type
|
||||||
// (and possibly its value, for constants).
|
// (and also its value for constants). An object (and thus its type)
|
||||||
// An object's type (and thus the object) may be in one of
|
// may be in 1 of 3 states:
|
||||||
// three states which are expressed by colors:
|
|
||||||
//
|
//
|
||||||
// - an object whose type is not yet known is painted white (initial color)
|
// - not in Checker.objPathIdx and type == nil : type is not yet known (white)
|
||||||
// - an object whose type is in the process of being inferred is painted grey
|
// - in Checker.objPathIdx : type is pending (grey)
|
||||||
// - an object whose type is fully inferred is painted black
|
// - not in Checker.objPathIdx and type != nil : type is known (black)
|
||||||
//
|
//
|
||||||
// During type inference, an object's color changes from white to grey
|
// During type-checking, an object changes from white to grey to black.
|
||||||
// to black (pre-declared objects are painted black from the start).
|
// Predeclared objects start as black (their type is known without checking).
|
||||||
// A black object (i.e., its type) can only depend on (refer to) other black
|
|
||||||
// ones. White and grey objects may depend on white and black objects.
|
|
||||||
// A dependency on a grey object indicates a cycle which may or may not be
|
|
||||||
// valid.
|
|
||||||
//
|
//
|
||||||
// When objects turn grey, they are pushed on the object path (a stack);
|
// A black object may only depend on (refer to) to other black objects. White
|
||||||
// they are popped again when they turn black. Thus, if a grey object (a
|
// and grey objects may depend on white or black objects. A dependency on a
|
||||||
// cycle) is encountered, it is on the object path, and all the objects
|
// grey object indicates a (possibly invalid) cycle.
|
||||||
// it depends on are the remaining objects on that path. Color encoding
|
//
|
||||||
// is such that the color value of a grey object indicates the index of
|
// When an object is marked grey, it is pushed onto the object path (a stack)
|
||||||
// that object in the object path.
|
// and its index in the path is recorded in the path index map. It is popped
|
||||||
|
// and removed from the map when its type is determined (and marked black).
|
||||||
|
|
||||||
// During type-checking, white objects may be assigned a type without
|
// If this object is grey, we have a (possibly invalid) cycle. This is signaled
|
||||||
// traversing through objDecl; e.g., when initializing constants and
|
// by a non-nil type for the object, except for constants and variables whose
|
||||||
// variables. Update the colors of those objects here (rather than
|
// type may be non-nil (known), or nil if it depends on a not-yet known
|
||||||
// everywhere where we set the type) to satisfy the color invariants.
|
// initialization value.
|
||||||
if obj.color() == white && obj.Type() != nil {
|
//
|
||||||
obj.setColor(black)
|
// In the former case, set the type to Typ[Invalid] because we have an
|
||||||
return
|
// initialization cycle. The cycle error will be reported later, when
|
||||||
}
|
// determining initialization order.
|
||||||
|
//
|
||||||
switch obj.color() {
|
// TODO(gri) Report cycle here and simplify initialization order code.
|
||||||
case white:
|
if _, ok := check.objPathIdx[obj]; ok {
|
||||||
assert(obj.Type() == nil)
|
|
||||||
// All color values other than white and black are considered grey.
|
|
||||||
// Because black and white are < grey, all values >= grey are grey.
|
|
||||||
// Use those values to encode the object's index into the object path.
|
|
||||||
obj.setColor(grey + color(check.push(obj)))
|
|
||||||
defer func() {
|
|
||||||
check.pop().setColor(black)
|
|
||||||
}()
|
|
||||||
|
|
||||||
case black:
|
|
||||||
assert(obj.Type() != nil)
|
|
||||||
return
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Color values other than white or black are considered grey.
|
|
||||||
fallthrough
|
|
||||||
|
|
||||||
case grey:
|
|
||||||
// We have a (possibly invalid) cycle.
|
|
||||||
// In the existing code, this is marked by a non-nil type
|
|
||||||
// for the object except for constants and variables whose
|
|
||||||
// type may be non-nil (known), or nil if it depends on the
|
|
||||||
// not-yet known initialization value.
|
|
||||||
// In the former case, set the type to Typ[Invalid] because
|
|
||||||
// we have an initialization cycle. The cycle error will be
|
|
||||||
// reported later, when determining initialization order.
|
|
||||||
// TODO(gri) Report cycle here and simplify initialization
|
|
||||||
// order code.
|
|
||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
case *Const:
|
case *Const, *Var:
|
||||||
if !check.validCycle(obj) || obj.typ == nil {
|
if !check.validCycle(obj) || obj.Type() == nil {
|
||||||
obj.typ = Typ[Invalid]
|
obj.setType(Typ[Invalid])
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Var:
|
|
||||||
if !check.validCycle(obj) || obj.typ == nil {
|
|
||||||
obj.typ = Typ[Invalid]
|
|
||||||
}
|
|
||||||
|
|
||||||
case *TypeName:
|
case *TypeName:
|
||||||
if !check.validCycle(obj) {
|
if !check.validCycle(obj) {
|
||||||
// break cycle
|
obj.setType(Typ[Invalid])
|
||||||
// (without this, calling underlying()
|
|
||||||
// below may lead to an endless loop
|
|
||||||
// if we have a cycle for a defined
|
|
||||||
// (*Named) type)
|
|
||||||
obj.typ = Typ[Invalid]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Func:
|
case *Func:
|
||||||
if !check.validCycle(obj) {
|
if !check.validCycle(obj) {
|
||||||
// Don't set obj.typ to Typ[Invalid] here
|
// Don't set type to Typ[Invalid]; plenty of code asserts that
|
||||||
// because plenty of code type-asserts that
|
// functions have a *Signature type. Instead, leave the type
|
||||||
// functions have a *Signature type. Grey
|
// as an empty signature, which makes it impossible to
|
||||||
// functions have their type set to an empty
|
|
||||||
// signature which makes it impossible to
|
|
||||||
// initialize a variable with the function.
|
// initialize a variable with the function.
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(obj.Type() != nil)
|
assert(obj.Type() != nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if obj.Type() != nil { // black, meaning it's already type-checked
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// white, meaning it must be type-checked
|
||||||
|
|
||||||
|
check.push(obj) // mark as grey
|
||||||
|
defer check.pop()
|
||||||
|
|
||||||
d := check.objMap[obj]
|
d := check.objMap[obj]
|
||||||
if d == nil {
|
if d == nil {
|
||||||
check.dump("%v: %s should have been declared", obj.Pos(), obj)
|
check.dump("%v: %s should have been declared", obj.Pos(), obj)
|
||||||
|
|
@ -222,8 +185,8 @@ func (check *Checker) validCycle(obj Object) (valid bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count cycle objects.
|
// Count cycle objects.
|
||||||
assert(obj.color() >= grey)
|
start, found := check.objPathIdx[obj]
|
||||||
start := obj.color() - grey // index of obj in objPath
|
assert(found)
|
||||||
cycle := check.objPath[start:]
|
cycle := check.objPath[start:]
|
||||||
tparCycle := false // if set, the cycle is through a type parameter list
|
tparCycle := false // if set, the cycle is through a type parameter list
|
||||||
nval := 0 // number of (constant or variable) values in the cycle
|
nval := 0 // number of (constant or variable) values in the cycle
|
||||||
|
|
@ -857,17 +820,8 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
|
||||||
sig := new(Signature)
|
sig := new(Signature)
|
||||||
obj.typ = sig // guard against cycles
|
obj.typ = sig // guard against cycles
|
||||||
|
|
||||||
// Avoid cycle error when referring to method while type-checking the signature.
|
|
||||||
// This avoids a nuisance in the best case (non-parameterized receiver type) and
|
|
||||||
// since the method is not a type, we get an error. If we have a parameterized
|
|
||||||
// receiver type, instantiating the receiver type leads to the instantiation of
|
|
||||||
// its methods, and we don't want a cycle error in that case.
|
|
||||||
// TODO(gri) review if this is correct and/or whether we still need this?
|
|
||||||
saved := obj.color_
|
|
||||||
obj.color_ = black
|
|
||||||
fdecl := decl.fdecl
|
fdecl := decl.fdecl
|
||||||
check.funcType(sig, fdecl.Recv, fdecl.Type)
|
check.funcType(sig, fdecl.Recv, fdecl.Type)
|
||||||
obj.color_ = saved
|
|
||||||
|
|
||||||
// Set the scope's extent to the complete "func (...) { ... }"
|
// Set the scope's extent to the complete "func (...) { ... }"
|
||||||
// so that Scope.Innermost works correctly.
|
// so that Scope.Innermost works correctly.
|
||||||
|
|
@ -980,10 +934,9 @@ func (check *Checker) declStmt(d ast.Decl) {
|
||||||
// the innermost containing block."
|
// the innermost containing block."
|
||||||
scopePos := d.spec.Name.Pos()
|
scopePos := d.spec.Name.Pos()
|
||||||
check.declare(check.scope, d.spec.Name, obj, scopePos)
|
check.declare(check.scope, d.spec.Name, obj, scopePos)
|
||||||
// mark and unmark type before calling typeDecl; its type is still nil (see Checker.objDecl)
|
check.push(obj) // mark as grey
|
||||||
obj.setColor(grey + color(check.push(obj)))
|
defer check.pop()
|
||||||
check.typeDecl(obj, d.spec, nil)
|
check.typeDecl(obj, d.spec, nil)
|
||||||
check.pop().setColor(black)
|
|
||||||
default:
|
default:
|
||||||
check.errorf(d.node(), InvalidSyntaxTree, "unknown ast.Decl node %T", d.node())
|
check.errorf(d.node(), InvalidSyntaxTree, "unknown ast.Decl node %T", d.node())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,18 +45,12 @@ type Object interface {
|
||||||
// 0 for all other objects (including objects in file scopes).
|
// 0 for all other objects (including objects in file scopes).
|
||||||
order() uint32
|
order() uint32
|
||||||
|
|
||||||
// color returns the object's color.
|
|
||||||
color() color
|
|
||||||
|
|
||||||
// setType sets the type of the object.
|
// setType sets the type of the object.
|
||||||
setType(Type)
|
setType(Type)
|
||||||
|
|
||||||
// setOrder sets the order number of the object. It must be > 0.
|
// setOrder sets the order number of the object. It must be > 0.
|
||||||
setOrder(uint32)
|
setOrder(uint32)
|
||||||
|
|
||||||
// setColor sets the object's color. It must not be white.
|
|
||||||
setColor(color color)
|
|
||||||
|
|
||||||
// setParent sets the parent scope of the object.
|
// setParent sets the parent scope of the object.
|
||||||
setParent(*Scope)
|
setParent(*Scope)
|
||||||
|
|
||||||
|
|
@ -105,41 +99,9 @@ type object struct {
|
||||||
name string
|
name string
|
||||||
typ Type
|
typ Type
|
||||||
order_ uint32
|
order_ uint32
|
||||||
color_ color
|
|
||||||
scopePos_ token.Pos
|
scopePos_ token.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
// color encodes the color of an object (see Checker.objDecl for details).
|
|
||||||
type color uint32
|
|
||||||
|
|
||||||
// An object may be painted in one of three colors.
|
|
||||||
// Color values other than white or black are considered grey.
|
|
||||||
const (
|
|
||||||
white color = iota
|
|
||||||
black
|
|
||||||
grey // must be > white and black
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c color) String() string {
|
|
||||||
switch c {
|
|
||||||
case white:
|
|
||||||
return "white"
|
|
||||||
case black:
|
|
||||||
return "black"
|
|
||||||
default:
|
|
||||||
return "grey"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// colorFor returns the (initial) color for an object depending on
|
|
||||||
// whether its type t is known or not.
|
|
||||||
func colorFor(t Type) color {
|
|
||||||
if t != nil {
|
|
||||||
return black
|
|
||||||
}
|
|
||||||
return white
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parent returns the scope in which the object is declared.
|
// Parent returns the scope in which the object is declared.
|
||||||
// The result is nil for methods and struct fields.
|
// The result is nil for methods and struct fields.
|
||||||
func (obj *object) Parent() *Scope { return obj.parent }
|
func (obj *object) Parent() *Scope { return obj.parent }
|
||||||
|
|
@ -167,13 +129,11 @@ func (obj *object) Id() string { return Id(obj.pkg, obj.name) }
|
||||||
|
|
||||||
func (obj *object) String() string { panic("abstract") }
|
func (obj *object) String() string { panic("abstract") }
|
||||||
func (obj *object) order() uint32 { return obj.order_ }
|
func (obj *object) order() uint32 { return obj.order_ }
|
||||||
func (obj *object) color() color { return obj.color_ }
|
|
||||||
func (obj *object) scopePos() token.Pos { return obj.scopePos_ }
|
func (obj *object) scopePos() token.Pos { return obj.scopePos_ }
|
||||||
|
|
||||||
func (obj *object) setParent(parent *Scope) { obj.parent = parent }
|
func (obj *object) setParent(parent *Scope) { obj.parent = parent }
|
||||||
func (obj *object) setType(typ Type) { obj.typ = typ }
|
func (obj *object) setType(typ Type) { obj.typ = typ }
|
||||||
func (obj *object) setOrder(order uint32) { assert(order > 0); obj.order_ = order }
|
func (obj *object) setOrder(order uint32) { assert(order > 0); obj.order_ = order }
|
||||||
func (obj *object) setColor(color color) { assert(color != white); obj.color_ = color }
|
|
||||||
func (obj *object) setScopePos(pos token.Pos) { obj.scopePos_ = pos }
|
func (obj *object) setScopePos(pos token.Pos) { obj.scopePos_ = pos }
|
||||||
|
|
||||||
func (obj *object) sameId(pkg *Package, name string, foldCase bool) bool {
|
func (obj *object) sameId(pkg *Package, name string, foldCase bool) bool {
|
||||||
|
|
@ -250,7 +210,7 @@ type PkgName struct {
|
||||||
// NewPkgName returns a new PkgName object representing an imported package.
|
// NewPkgName returns a new PkgName object representing an imported package.
|
||||||
// The remaining arguments set the attributes found with all Objects.
|
// The remaining arguments set the attributes found with all Objects.
|
||||||
func NewPkgName(pos token.Pos, pkg *Package, name string, imported *Package) *PkgName {
|
func NewPkgName(pos token.Pos, pkg *Package, name string, imported *Package) *PkgName {
|
||||||
return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, black, nopos}, imported}
|
return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, nopos}, imported}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Imported returns the package that was imported.
|
// Imported returns the package that was imported.
|
||||||
|
|
@ -266,7 +226,7 @@ type Const struct {
|
||||||
// NewConst returns a new constant with value val.
|
// NewConst returns a new constant with value val.
|
||||||
// The remaining arguments set the attributes found with all Objects.
|
// The remaining arguments set the attributes found with all Objects.
|
||||||
func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val constant.Value) *Const {
|
func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val constant.Value) *Const {
|
||||||
return &Const{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, val}
|
return &Const{object{nil, pos, pkg, name, typ, 0, nopos}, val}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Val returns the constant's value.
|
// Val returns the constant's value.
|
||||||
|
|
@ -291,7 +251,7 @@ type TypeName struct {
|
||||||
// argument for NewNamed, which will set the TypeName's type as a side-
|
// argument for NewNamed, which will set the TypeName's type as a side-
|
||||||
// effect.
|
// effect.
|
||||||
func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
|
func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
|
||||||
return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}}
|
return &TypeName{object{nil, pos, pkg, name, typ, 0, nopos}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTypeNameLazy returns a new defined type like NewTypeName, but it
|
// NewTypeNameLazy returns a new defined type like NewTypeName, but it
|
||||||
|
|
@ -405,7 +365,7 @@ func NewField(pos token.Pos, pkg *Package, name string, typ Type, embedded bool)
|
||||||
// newVar returns a new variable.
|
// newVar returns a new variable.
|
||||||
// The arguments set the attributes found with all Objects.
|
// The arguments set the attributes found with all Objects.
|
||||||
func newVar(kind VarKind, pos token.Pos, pkg *Package, name string, typ Type) *Var {
|
func newVar(kind VarKind, pos token.Pos, pkg *Package, name string, typ Type) *Var {
|
||||||
return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, kind: kind}
|
return &Var{object: object{nil, pos, pkg, name, typ, 0, nopos}, kind: kind}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anonymous reports whether the variable is an embedded field.
|
// Anonymous reports whether the variable is an embedded field.
|
||||||
|
|
@ -455,7 +415,7 @@ func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func {
|
||||||
// as this would violate object.{Type,color} invariants.
|
// as this would violate object.{Type,color} invariants.
|
||||||
// TODO(adonovan): propose to disallow NewFunc with nil *Signature.
|
// TODO(adonovan): propose to disallow NewFunc with nil *Signature.
|
||||||
}
|
}
|
||||||
return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, false, nil}
|
return &Func{object{nil, pos, pkg, name, typ, 0, nopos}, false, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signature returns the signature (type) of the function or method.
|
// Signature returns the signature (type) of the function or method.
|
||||||
|
|
@ -537,7 +497,7 @@ type Label struct {
|
||||||
|
|
||||||
// NewLabel returns a new label.
|
// NewLabel returns a new label.
|
||||||
func NewLabel(pos token.Pos, pkg *Package, name string) *Label {
|
func NewLabel(pos token.Pos, pkg *Package, name string) *Label {
|
||||||
return &Label{object{pos: pos, pkg: pkg, name: name, typ: Typ[Invalid], color_: black}, false}
|
return &Label{object{pos: pos, pkg: pkg, name: name, typ: Typ[Invalid]}, false}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Builtin represents a built-in function.
|
// A Builtin represents a built-in function.
|
||||||
|
|
@ -548,7 +508,7 @@ type Builtin struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBuiltin(id builtinId) *Builtin {
|
func newBuiltin(id builtinId) *Builtin {
|
||||||
return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid], color_: black}, id}
|
return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid]}, id}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nil represents the predeclared value nil.
|
// Nil represents the predeclared value nil.
|
||||||
|
|
|
||||||
|
|
@ -220,10 +220,8 @@ func (*lazyObject) Exported() bool { panic("unreachable") }
|
||||||
func (*lazyObject) Id() string { panic("unreachable") }
|
func (*lazyObject) Id() string { panic("unreachable") }
|
||||||
func (*lazyObject) String() string { panic("unreachable") }
|
func (*lazyObject) String() string { panic("unreachable") }
|
||||||
func (*lazyObject) order() uint32 { panic("unreachable") }
|
func (*lazyObject) order() uint32 { panic("unreachable") }
|
||||||
func (*lazyObject) color() color { panic("unreachable") }
|
|
||||||
func (*lazyObject) setType(Type) { panic("unreachable") }
|
func (*lazyObject) setType(Type) { panic("unreachable") }
|
||||||
func (*lazyObject) setOrder(uint32) { panic("unreachable") }
|
func (*lazyObject) setOrder(uint32) { panic("unreachable") }
|
||||||
func (*lazyObject) setColor(color color) { panic("unreachable") }
|
|
||||||
func (*lazyObject) setParent(*Scope) { panic("unreachable") }
|
func (*lazyObject) setParent(*Scope) { panic("unreachable") }
|
||||||
func (*lazyObject) sameId(*Package, string, bool) bool { panic("unreachable") }
|
func (*lazyObject) sameId(*Package, string, bool) bool { panic("unreachable") }
|
||||||
func (*lazyObject) scopePos() token.Pos { panic("unreachable") }
|
func (*lazyObject) scopePos() token.Pos { panic("unreachable") }
|
||||||
|
|
|
||||||
|
|
@ -35,14 +35,14 @@ func TestSizeof(t *testing.T) {
|
||||||
{term{}, 12, 24},
|
{term{}, 12, 24},
|
||||||
|
|
||||||
// Objects
|
// Objects
|
||||||
{PkgName{}, 44, 80},
|
{PkgName{}, 40, 80},
|
||||||
{Const{}, 48, 88},
|
{Const{}, 44, 88},
|
||||||
{TypeName{}, 40, 72},
|
{TypeName{}, 36, 72},
|
||||||
{Var{}, 48, 88},
|
{Var{}, 44, 88},
|
||||||
{Func{}, 48, 88},
|
{Func{}, 44, 88},
|
||||||
{Label{}, 44, 80},
|
{Label{}, 40, 80},
|
||||||
{Builtin{}, 44, 80},
|
{Builtin{}, 40, 80},
|
||||||
{Nil{}, 40, 72},
|
{Nil{}, 36, 72},
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
{Scope{}, 44, 88},
|
{Scope{}, 44, 88},
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,6 @@ func defPredeclaredTypes() {
|
||||||
// interface.
|
// interface.
|
||||||
{
|
{
|
||||||
universeAnyNoAlias = NewTypeName(nopos, nil, "any", &Interface{complete: true, tset: &topTypeSet})
|
universeAnyNoAlias = NewTypeName(nopos, nil, "any", &Interface{complete: true, tset: &topTypeSet})
|
||||||
universeAnyNoAlias.setColor(black)
|
|
||||||
// ensure that the any TypeName reports a consistent Parent, after
|
// ensure that the any TypeName reports a consistent Parent, after
|
||||||
// hijacking Universe.Lookup with gotypesalias=0.
|
// hijacking Universe.Lookup with gotypesalias=0.
|
||||||
universeAnyNoAlias.setParent(Universe)
|
universeAnyNoAlias.setParent(Universe)
|
||||||
|
|
@ -110,7 +109,6 @@ func defPredeclaredTypes() {
|
||||||
// into the Universe, but we lean toward the future and insert the Alias
|
// into the Universe, but we lean toward the future and insert the Alias
|
||||||
// representation.
|
// representation.
|
||||||
universeAnyAlias = NewTypeName(nopos, nil, "any", nil)
|
universeAnyAlias = NewTypeName(nopos, nil, "any", nil)
|
||||||
universeAnyAlias.setColor(black)
|
|
||||||
_ = NewAlias(universeAnyAlias, universeAnyNoAlias.Type().Underlying()) // Link TypeName and Alias
|
_ = NewAlias(universeAnyAlias, universeAnyNoAlias.Type().Underlying()) // Link TypeName and Alias
|
||||||
def(universeAnyAlias)
|
def(universeAnyAlias)
|
||||||
}
|
}
|
||||||
|
|
@ -118,7 +116,6 @@ func defPredeclaredTypes() {
|
||||||
// type error interface{ Error() string }
|
// type error interface{ Error() string }
|
||||||
{
|
{
|
||||||
obj := NewTypeName(nopos, nil, "error", nil)
|
obj := NewTypeName(nopos, nil, "error", nil)
|
||||||
obj.setColor(black)
|
|
||||||
typ := (*Checker)(nil).newNamed(obj, nil, nil)
|
typ := (*Checker)(nil).newNamed(obj, nil, nil)
|
||||||
|
|
||||||
// error.Error() string
|
// error.Error() string
|
||||||
|
|
@ -139,7 +136,6 @@ func defPredeclaredTypes() {
|
||||||
// type comparable interface{} // marked as comparable
|
// type comparable interface{} // marked as comparable
|
||||||
{
|
{
|
||||||
obj := NewTypeName(nopos, nil, "comparable", nil)
|
obj := NewTypeName(nopos, nil, "comparable", nil)
|
||||||
obj.setColor(black)
|
|
||||||
typ := (*Checker)(nil).newNamed(obj, nil, nil)
|
typ := (*Checker)(nil).newNamed(obj, nil, nil)
|
||||||
|
|
||||||
// interface{} // marked as comparable
|
// interface{} // marked as comparable
|
||||||
|
|
@ -168,7 +164,7 @@ func defPredeclaredConsts() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func defPredeclaredNil() {
|
func defPredeclaredNil() {
|
||||||
def(&Nil{object{name: "nil", typ: Typ[UntypedNil], color_: black}})
|
def(&Nil{object{name: "nil", typ: Typ[UntypedNil]}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// A builtinId is the id of a builtin function.
|
// A builtinId is the id of a builtin function.
|
||||||
|
|
@ -292,7 +288,7 @@ func init() {
|
||||||
// a scope. Objects with exported names are inserted in the unsafe package
|
// a scope. Objects with exported names are inserted in the unsafe package
|
||||||
// scope; other objects are inserted in the universe scope.
|
// scope; other objects are inserted in the universe scope.
|
||||||
func def(obj Object) {
|
func def(obj Object) {
|
||||||
assert(obj.color() == black)
|
assert(obj.Type() != nil)
|
||||||
name := obj.Name()
|
name := obj.Name()
|
||||||
if strings.Contains(name, " ") {
|
if strings.Contains(name, " ") {
|
||||||
return // nothing to do
|
return // nothing to do
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue