usedPkgNames map[*PkgName]bool // set of used package names
mono monoGraph // graph for detecting non-monomorphizable instantiation loops
- firstErr error // first error encountered
- 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
- 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)
- cleaners []cleaner // list of types that may need a final cleanup at the end of type-checking
+ firstErr error // first error encountered
+ 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
+ delayed []action // stack of delayed action segments; segments are processed in FIFO order
+ objPath []Object // path of object dependencies during type-checking (for cycle reporting)
+ 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
// for the duration of type-checking a specific object)
return &check.delayed[i]
}
-// push pushes obj onto the object path and returns its index in the path.
-func (check *Checker) push(obj Object) int {
+// push pushes obj onto the object path and records its index in the path index map.
+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)
- return len(check.objPath) - 1
}
-// pop pops and returns the topmost object from the object path.
-func (check *Checker) pop() Object {
+// pop pops an object from the object path and removes it from the path index map.
+func (check *Checker) pop() {
i := len(check.objPath) - 1
obj := check.objPath[i]
- check.objPath[i] = nil
+ check.objPath[i] = nil // help the garbage collector
check.objPath = check.objPath[:i]
- return obj
+ delete(check.objPathIdx, obj)
}
type cleaner interface {
check.untyped = nil
check.delayed = nil
check.objPath = nil
+ check.objPathIdx = nil
check.cleaners = nil
// We must initialize usedVars and usedPkgNames both here and in NewChecker,
// tname is marked grey - we have a cycle on the path beginning at start.
// Mark tname as invalid.
tname.setType(Typ[Invalid])
- tname.setColor(black)
// collect type names on cycle
var cycle []Object
if check.indent == 0 {
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++
defer func() {
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
- // (and possibly its value, for constants).
- // An object's type (and thus the object) may be in one of
- // three states which are expressed by colors:
+ // Checking the declaration of an object means determining its type
+ // (and also its value for constants). An object (and thus its type)
+ // may be in 1 of 3 states:
//
- // - an object whose type is not yet known is painted white (initial color)
- // - an object whose type is in the process of being inferred is painted grey
- // - an object whose type is fully inferred is painted black
+ // - not in Checker.objPathIdx and type == nil : type is not yet known (white)
+ // - in Checker.objPathIdx : type is pending (grey)
+ // - not in Checker.objPathIdx and type != nil : type is known (black)
//
- // During type inference, an object's color changes from white to grey
- // to black (pre-declared objects are painted black from the start).
- // 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.
+ // During type-checking, an object changes from white to grey to black.
+ // Predeclared objects start as black (their type is known without checking).
//
- // When objects turn grey, they are pushed on the object path (a stack);
- // they are popped again when they turn black. Thus, if a grey object (a
- // cycle) is encountered, it is on the object path, and all the objects
- // 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
- // that object in the object path.
-
- // During type-checking, white objects may be assigned a type without
- // traversing through objDecl; e.g., when initializing constants and
- // variables. Update the colors of those objects here (rather than
- // everywhere where we set the type) to satisfy the color invariants.
- if obj.color() == white && obj.Type() != nil {
- obj.setColor(black)
- return
- }
-
- switch obj.color() {
- case white:
- 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.
+ // A black object may only depend on (refer to) to other black objects. White
+ // and grey objects may depend on white or black objects. A dependency on a
+ // grey object indicates a (possibly invalid) cycle.
+ //
+ // When an object is marked grey, it is pushed onto the object path (a stack)
+ // 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).
+
+ // If this object is grey, we have a (possibly invalid) cycle. This is signaled
+ // 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 a 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.
+ if _, ok := check.objPathIdx[obj]; ok {
switch obj := obj.(type) {
- case *Const:
- if !check.validCycle(obj) || obj.typ == nil {
- obj.typ = Typ[Invalid]
- }
-
- case *Var:
- if !check.validCycle(obj) || obj.typ == nil {
- obj.typ = Typ[Invalid]
+ case *Const, *Var:
+ if !check.validCycle(obj) || obj.Type() == nil {
+ obj.setType(Typ[Invalid])
}
-
case *TypeName:
if !check.validCycle(obj) {
- // break cycle
- // (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]
+ obj.setType(Typ[Invalid])
}
-
case *Func:
if !check.validCycle(obj) {
- // Don't set obj.typ to Typ[Invalid] here
- // because plenty of code type-asserts that
- // functions have a *Signature type. Grey
- // functions have their type set to an empty
- // signature which makes it impossible to
+ // Don't set type to Typ[Invalid]; plenty of code asserts that
+ // functions have a *Signature type. Instead, leave the type
+ // as an empty signature, which makes it impossible to
// initialize a variable with the function.
}
-
default:
panic("unreachable")
}
+
assert(obj.Type() != nil)
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]
if d == nil {
check.dump("%v: %s should have been declared", obj.Pos(), obj)
}
// Count cycle objects.
- assert(obj.color() >= grey)
- start := obj.color() - grey // index of obj in objPath
+ start, found := check.objPathIdx[obj]
+ assert(found)
cycle := check.objPath[start:]
tparCycle := false // if set, the cycle is through a type parameter list
nval := 0 // number of (constant or variable) values in the cycle
sig := new(Signature)
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
check.funcType(sig, fdecl.Recv, fdecl.TParamList, fdecl.Type)
- obj.color_ = saved
// Set the scope's extent to the complete "func (...) { ... }"
// so that Scope.Innermost works correctly.
// the innermost containing block."
scopePos := s.Name.Pos()
check.declare(check.scope, s.Name, obj, scopePos)
- // mark and unmark type before calling typeDecl; its type is still nil (see Checker.objDecl)
- obj.setColor(grey + color(check.push(obj)))
+ check.push(obj) // mark as grey
+ defer check.pop()
check.typeDecl(obj, s, nil)
- check.pop().setColor(black)
default:
check.errorf(s, InvalidSyntaxTree, "unknown syntax.Decl node %T", s)
// 0 for all other objects (including objects in file scopes).
order() uint32
- // color returns the object's color.
- color() color
-
// setType sets the type of the object.
setType(Type)
// setOrder sets the order number of the object. It must be > 0.
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(*Scope)
name string
typ Type
order_ uint32
- color_ color
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.
// The result is nil for methods and struct fields.
func (obj *object) Parent() *Scope { return obj.parent }
func (obj *object) String() string { panic("abstract") }
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) setParent(parent *Scope) { obj.parent = parent }
func (obj *object) setType(typ Type) { obj.typ = typ }
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) sameId(pkg *Package, name string, foldCase bool) bool {
// NewPkgName returns a new PkgName object representing an imported package.
// The remaining arguments set the attributes found with all Objects.
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.
// NewConst returns a new constant with value val.
// 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 {
- 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.
// argument for NewNamed, which will set the TypeName's type as a side-
// effect.
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
// newVar returns a new variable.
// The arguments set the attributes found with all Objects.
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.
// as this would violate object.{Type,color} invariants.
// 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.
// NewLabel returns a new 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.
}
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.
func (*lazyObject) Id() string { panic("unreachable") }
func (*lazyObject) String() string { panic("unreachable") }
func (*lazyObject) order() uint32 { panic("unreachable") }
-func (*lazyObject) color() color { panic("unreachable") }
func (*lazyObject) setType(Type) { panic("unreachable") }
func (*lazyObject) setOrder(uint32) { panic("unreachable") }
-func (*lazyObject) setColor(color color) { panic("unreachable") }
func (*lazyObject) setParent(*Scope) { panic("unreachable") }
func (*lazyObject) sameId(*Package, string, bool) bool { panic("unreachable") }
func (*lazyObject) scopePos() syntax.Pos { panic("unreachable") }
{term{}, 12, 24},
// Objects
- {PkgName{}, 60, 96},
- {Const{}, 64, 104},
- {TypeName{}, 56, 88},
- {Var{}, 64, 104},
- {Func{}, 64, 104},
- {Label{}, 60, 96},
- {Builtin{}, 60, 96},
- {Nil{}, 56, 88},
+ {PkgName{}, 56, 96},
+ {Const{}, 60, 104},
+ {TypeName{}, 52, 88},
+ {Var{}, 60, 104},
+ {Func{}, 60, 104},
+ {Label{}, 56, 96},
+ {Builtin{}, 56, 96},
+ {Nil{}, 52, 88},
// Misc
{Scope{}, 60, 104},
// interface.
{
universeAnyNoAlias = NewTypeName(nopos, nil, "any", &Interface{complete: true, tset: &topTypeSet})
- universeAnyNoAlias.setColor(black)
// ensure that the any TypeName reports a consistent Parent, after
// hijacking Universe.Lookup with gotypesalias=0.
universeAnyNoAlias.setParent(Universe)
// into the Universe, but we lean toward the future and insert the Alias
// representation.
universeAnyAlias = NewTypeName(nopos, nil, "any", nil)
- universeAnyAlias.setColor(black)
_ = NewAlias(universeAnyAlias, universeAnyNoAlias.Type().Underlying()) // Link TypeName and Alias
def(universeAnyAlias)
}
// type error interface{ Error() string }
{
obj := NewTypeName(nopos, nil, "error", nil)
- obj.setColor(black)
typ := (*Checker)(nil).newNamed(obj, nil, nil)
// error.Error() string
// type comparable interface{} // marked as comparable
{
obj := NewTypeName(nopos, nil, "comparable", nil)
- obj.setColor(black)
typ := (*Checker)(nil).newNamed(obj, nil, nil)
// interface{} // marked as comparable
}
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 scope. Objects with exported names are inserted in the unsafe package
// scope; other objects are inserted in the universe scope.
func def(obj Object) {
- assert(obj.color() == black)
+ assert(obj.Type() != nil)
name := obj.Name()
if strings.Contains(name, " ") {
return // nothing to do
usedPkgNames map[*PkgName]bool // set of used package names
mono monoGraph // graph for detecting non-monomorphizable instantiation loops
- firstErr error // first error encountered
- 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
- 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)
- cleaners []cleaner // list of types that may need a final cleanup at the end of type-checking
+ firstErr error // first error encountered
+ 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
+ delayed []action // stack of delayed action segments; segments are processed in FIFO order
+ objPath []Object // path of object dependencies during type-checking (for cycle reporting)
+ 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
// for the duration of type-checking a specific object)
return &check.delayed[i]
}
-// push pushes obj onto the object path and returns its index in the path.
-func (check *Checker) push(obj Object) int {
+// push pushes obj onto the object path and records its index in the path index map.
+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)
- return len(check.objPath) - 1
}
-// pop pops and returns the topmost object from the object path.
-func (check *Checker) pop() Object {
+// pop pops an object from the object path and removes it from the path index map.
+func (check *Checker) pop() {
i := len(check.objPath) - 1
obj := check.objPath[i]
- check.objPath[i] = nil
+ check.objPath[i] = nil // help the garbage collector
check.objPath = check.objPath[:i]
- return obj
+ delete(check.objPathIdx, obj)
}
type cleaner interface {
check.untyped = nil
check.delayed = nil
check.objPath = nil
+ check.objPathIdx = nil
check.cleaners = nil
// We must initialize usedVars and usedPkgNames both here and in NewChecker,
// tname is marked grey - we have a cycle on the path beginning at start.
// Mark tname as invalid.
tname.setType(Typ[Invalid])
- tname.setColor(black)
// collect type names on cycle
var cycle []Object
if check.indent == 0 {
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++
defer func() {
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
- // (and possibly its value, for constants).
- // An object's type (and thus the object) may be in one of
- // three states which are expressed by colors:
+ // Checking the declaration of an object means determining its type
+ // (and also its value for constants). An object (and thus its type)
+ // may be in 1 of 3 states:
//
- // - an object whose type is not yet known is painted white (initial color)
- // - an object whose type is in the process of being inferred is painted grey
- // - an object whose type is fully inferred is painted black
+ // - not in Checker.objPathIdx and type == nil : type is not yet known (white)
+ // - in Checker.objPathIdx : type is pending (grey)
+ // - not in Checker.objPathIdx and type != nil : type is known (black)
//
- // During type inference, an object's color changes from white to grey
- // to black (pre-declared objects are painted black from the start).
- // 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.
+ // During type-checking, an object changes from white to grey to black.
+ // Predeclared objects start as black (their type is known without checking).
//
- // When objects turn grey, they are pushed on the object path (a stack);
- // they are popped again when they turn black. Thus, if a grey object (a
- // cycle) is encountered, it is on the object path, and all the objects
- // 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
- // that object in the object path.
-
- // During type-checking, white objects may be assigned a type without
- // traversing through objDecl; e.g., when initializing constants and
- // variables. Update the colors of those objects here (rather than
- // everywhere where we set the type) to satisfy the color invariants.
- if obj.color() == white && obj.Type() != nil {
- obj.setColor(black)
- return
- }
-
- switch obj.color() {
- case white:
- 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.
+ // A black object may only depend on (refer to) to other black objects. White
+ // and grey objects may depend on white or black objects. A dependency on a
+ // grey object indicates a (possibly invalid) cycle.
+ //
+ // When an object is marked grey, it is pushed onto the object path (a stack)
+ // 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).
+
+ // If this object is grey, we have a (possibly invalid) cycle. This is signaled
+ // 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 a 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.
+ if _, ok := check.objPathIdx[obj]; ok {
switch obj := obj.(type) {
- case *Const:
- if !check.validCycle(obj) || obj.typ == nil {
- obj.typ = Typ[Invalid]
- }
-
- case *Var:
- if !check.validCycle(obj) || obj.typ == nil {
- obj.typ = Typ[Invalid]
+ case *Const, *Var:
+ if !check.validCycle(obj) || obj.Type() == nil {
+ obj.setType(Typ[Invalid])
}
-
case *TypeName:
if !check.validCycle(obj) {
- // break cycle
- // (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]
+ obj.setType(Typ[Invalid])
}
-
case *Func:
if !check.validCycle(obj) {
- // Don't set obj.typ to Typ[Invalid] here
- // because plenty of code type-asserts that
- // functions have a *Signature type. Grey
- // functions have their type set to an empty
- // signature which makes it impossible to
+ // Don't set type to Typ[Invalid]; plenty of code asserts that
+ // functions have a *Signature type. Instead, leave the type
+ // as an empty signature, which makes it impossible to
// initialize a variable with the function.
}
-
default:
panic("unreachable")
}
+
assert(obj.Type() != nil)
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]
if d == nil {
check.dump("%v: %s should have been declared", obj.Pos(), obj)
}
// Count cycle objects.
- assert(obj.color() >= grey)
- start := obj.color() - grey // index of obj in objPath
+ start, found := check.objPathIdx[obj]
+ assert(found)
cycle := check.objPath[start:]
tparCycle := false // if set, the cycle is through a type parameter list
nval := 0 // number of (constant or variable) values in the cycle
sig := new(Signature)
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
check.funcType(sig, fdecl.Recv, fdecl.Type)
- obj.color_ = saved
// Set the scope's extent to the complete "func (...) { ... }"
// so that Scope.Innermost works correctly.
// the innermost containing block."
scopePos := d.spec.Name.Pos()
check.declare(check.scope, d.spec.Name, obj, scopePos)
- // mark and unmark type before calling typeDecl; its type is still nil (see Checker.objDecl)
- obj.setColor(grey + color(check.push(obj)))
+ check.push(obj) // mark as grey
+ defer check.pop()
check.typeDecl(obj, d.spec, nil)
- check.pop().setColor(black)
default:
check.errorf(d.node(), InvalidSyntaxTree, "unknown ast.Decl node %T", d.node())
}
// 0 for all other objects (including objects in file scopes).
order() uint32
- // color returns the object's color.
- color() color
-
// setType sets the type of the object.
setType(Type)
// setOrder sets the order number of the object. It must be > 0.
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(*Scope)
name string
typ Type
order_ uint32
- color_ color
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.
// The result is nil for methods and struct fields.
func (obj *object) Parent() *Scope { return obj.parent }
func (obj *object) String() string { panic("abstract") }
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) setParent(parent *Scope) { obj.parent = parent }
func (obj *object) setType(typ Type) { obj.typ = typ }
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) sameId(pkg *Package, name string, foldCase bool) bool {
// NewPkgName returns a new PkgName object representing an imported package.
// The remaining arguments set the attributes found with all Objects.
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.
// NewConst returns a new constant with value val.
// 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 {
- 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.
// argument for NewNamed, which will set the TypeName's type as a side-
// effect.
func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
- return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}}
+ return &TypeName{object{nil, pos, pkg, name, typ, 0, nopos}}
}
// NewTypeNameLazy returns a new defined type like NewTypeName, but it
// newVar returns a new variable.
// The arguments set the attributes found with all Objects.
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.
// as this would violate object.{Type,color} invariants.
// 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.
// NewLabel returns a new 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.
}
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.
func (*lazyObject) Id() string { panic("unreachable") }
func (*lazyObject) String() string { panic("unreachable") }
func (*lazyObject) order() uint32 { panic("unreachable") }
-func (*lazyObject) color() color { panic("unreachable") }
func (*lazyObject) setType(Type) { panic("unreachable") }
func (*lazyObject) setOrder(uint32) { panic("unreachable") }
-func (*lazyObject) setColor(color color) { panic("unreachable") }
func (*lazyObject) setParent(*Scope) { panic("unreachable") }
func (*lazyObject) sameId(*Package, string, bool) bool { panic("unreachable") }
func (*lazyObject) scopePos() token.Pos { panic("unreachable") }
{term{}, 12, 24},
// Objects
- {PkgName{}, 44, 80},
- {Const{}, 48, 88},
- {TypeName{}, 40, 72},
- {Var{}, 48, 88},
- {Func{}, 48, 88},
- {Label{}, 44, 80},
- {Builtin{}, 44, 80},
- {Nil{}, 40, 72},
+ {PkgName{}, 40, 80},
+ {Const{}, 44, 88},
+ {TypeName{}, 36, 72},
+ {Var{}, 44, 88},
+ {Func{}, 44, 88},
+ {Label{}, 40, 80},
+ {Builtin{}, 40, 80},
+ {Nil{}, 36, 72},
// Misc
{Scope{}, 44, 88},
// interface.
{
universeAnyNoAlias = NewTypeName(nopos, nil, "any", &Interface{complete: true, tset: &topTypeSet})
- universeAnyNoAlias.setColor(black)
// ensure that the any TypeName reports a consistent Parent, after
// hijacking Universe.Lookup with gotypesalias=0.
universeAnyNoAlias.setParent(Universe)
// into the Universe, but we lean toward the future and insert the Alias
// representation.
universeAnyAlias = NewTypeName(nopos, nil, "any", nil)
- universeAnyAlias.setColor(black)
_ = NewAlias(universeAnyAlias, universeAnyNoAlias.Type().Underlying()) // Link TypeName and Alias
def(universeAnyAlias)
}
// type error interface{ Error() string }
{
obj := NewTypeName(nopos, nil, "error", nil)
- obj.setColor(black)
typ := (*Checker)(nil).newNamed(obj, nil, nil)
// error.Error() string
// type comparable interface{} // marked as comparable
{
obj := NewTypeName(nopos, nil, "comparable", nil)
- obj.setColor(black)
typ := (*Checker)(nil).newNamed(obj, nil, nil)
// interface{} // marked as comparable
}
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 scope. Objects with exported names are inserted in the unsafe package
// scope; other objects are inserted in the universe scope.
func def(obj Object) {
- assert(obj.color() == black)
+ assert(obj.Type() != nil)
name := obj.Name()
if strings.Contains(name, " ") {
return // nothing to do