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
|
||||
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)
|
||||
|
|
@ -248,19 +249,22 @@ func (check *Checker) later(f func()) *action {
|
|||
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 {
|
||||
|
|
@ -319,6 +323,7 @@ func (check *Checker) initFiles(files []*syntax.File) {
|
|||
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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
// Mark tname as invalid.
|
||||
tname.setType(Typ[Invalid])
|
||||
tname.setColor(black)
|
||||
|
||||
// collect type names on cycle
|
||||
var cycle []Object
|
||||
|
|
|
|||
|
|
@ -62,114 +62,77 @@ func (check *Checker) objDecl(obj Object, def *TypeName) {
|
|||
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.
|
||||
// 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).
|
||||
|
||||
// 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.
|
||||
// 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 *Const, *Var:
|
||||
if !check.validCycle(obj) || obj.Type() == nil {
|
||||
obj.setType(Typ[Invalid])
|
||||
}
|
||||
|
||||
case *Var:
|
||||
if !check.validCycle(obj) || obj.typ == nil {
|
||||
obj.typ = 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)
|
||||
|
|
@ -221,8 +184,8 @@ func (check *Checker) validCycle(obj Object) (valid bool) {
|
|||
}
|
||||
|
||||
// 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
|
||||
|
|
@ -764,17 +727,8 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
|
|||
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.
|
||||
|
|
@ -921,10 +875,9 @@ func (check *Checker) declStmt(list []syntax.Decl) {
|
|||
// 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)
|
||||
|
|
|
|||
|
|
@ -42,18 +42,12 @@ type Object interface {
|
|||
// 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)
|
||||
|
||||
|
|
@ -102,41 +96,9 @@ type object struct {
|
|||
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 }
|
||||
|
|
@ -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) 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 {
|
||||
|
|
@ -247,7 +207,7 @@ type PkgName struct {
|
|||
// 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.
|
||||
|
|
@ -263,7 +223,7 @@ type Const struct {
|
|||
// 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.
|
||||
|
|
@ -288,7 +248,7 @@ type TypeName struct {
|
|||
// 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
|
||||
|
|
@ -402,7 +362,7 @@ func NewField(pos syntax.Pos, pkg *Package, name string, typ Type, embedded bool
|
|||
// 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.
|
||||
|
|
@ -452,7 +412,7 @@ func NewFunc(pos syntax.Pos, pkg *Package, name string, sig *Signature) *Func {
|
|||
// 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.
|
||||
|
|
@ -534,7 +494,7 @@ type Label struct {
|
|||
|
||||
// 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.
|
||||
|
|
@ -545,7 +505,7 @@ type Builtin struct {
|
|||
}
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -217,10 +217,8 @@ func (*lazyObject) Exported() bool { panic("unreachable") }
|
|||
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") }
|
||||
|
|
|
|||
|
|
@ -36,14 +36,14 @@ func TestSizeof(t *testing.T) {
|
|||
{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},
|
||||
|
|
|
|||
|
|
@ -98,7 +98,6 @@ func defPredeclaredTypes() {
|
|||
// 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)
|
||||
|
|
@ -107,7 +106,6 @@ func defPredeclaredTypes() {
|
|||
// 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)
|
||||
}
|
||||
|
|
@ -115,7 +113,6 @@ func defPredeclaredTypes() {
|
|||
// type error interface{ Error() string }
|
||||
{
|
||||
obj := NewTypeName(nopos, nil, "error", nil)
|
||||
obj.setColor(black)
|
||||
typ := (*Checker)(nil).newNamed(obj, nil, nil)
|
||||
|
||||
// error.Error() string
|
||||
|
|
@ -136,7 +133,6 @@ func defPredeclaredTypes() {
|
|||
// 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
|
||||
|
|
@ -165,7 +161,7 @@ func defPredeclaredConsts() {
|
|||
}
|
||||
|
||||
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.
|
||||
|
|
@ -289,7 +285,7 @@ func init() {
|
|||
// 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
|
||||
|
|
|
|||
|
|
@ -191,12 +191,13 @@ type Checker struct {
|
|||
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)
|
||||
|
|
@ -268,19 +269,22 @@ func (check *Checker) later(f func()) *action {
|
|||
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 {
|
||||
|
|
@ -343,6 +347,7 @@ func (check *Checker) initFiles(files []*ast.File) {
|
|||
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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
// Mark tname as invalid.
|
||||
tname.setType(Typ[Invalid])
|
||||
tname.setColor(black)
|
||||
|
||||
// collect type names on cycle
|
||||
var cycle []Object
|
||||
|
|
|
|||
|
|
@ -63,114 +63,77 @@ func (check *Checker) objDecl(obj Object, def *TypeName) {
|
|||
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.
|
||||
// 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).
|
||||
|
||||
// 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.
|
||||
// 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 *Const, *Var:
|
||||
if !check.validCycle(obj) || obj.Type() == nil {
|
||||
obj.setType(Typ[Invalid])
|
||||
}
|
||||
|
||||
case *Var:
|
||||
if !check.validCycle(obj) || obj.typ == nil {
|
||||
obj.typ = 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)
|
||||
|
|
@ -222,8 +185,8 @@ func (check *Checker) validCycle(obj Object) (valid bool) {
|
|||
}
|
||||
|
||||
// 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
|
||||
|
|
@ -857,17 +820,8 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
|
|||
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.
|
||||
|
|
@ -980,10 +934,9 @@ func (check *Checker) declStmt(d ast.Decl) {
|
|||
// 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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,18 +45,12 @@ type Object interface {
|
|||
// 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)
|
||||
|
||||
|
|
@ -105,41 +99,9 @@ type object struct {
|
|||
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 }
|
||||
|
|
@ -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) 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 {
|
||||
|
|
@ -250,7 +210,7 @@ type PkgName struct {
|
|||
// 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.
|
||||
|
|
@ -266,7 +226,7 @@ type Const struct {
|
|||
// 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.
|
||||
|
|
@ -291,7 +251,7 @@ type TypeName struct {
|
|||
// argument for NewNamed, which will set the TypeName's type as a side-
|
||||
// effect.
|
||||
func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
|
||||
return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}}
|
||||
return &TypeName{object{nil, pos, pkg, name, typ, 0, nopos}}
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
|
|
@ -455,7 +415,7 @@ func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func {
|
|||
// 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.
|
||||
|
|
@ -537,7 +497,7 @@ type Label struct {
|
|||
|
||||
// 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.
|
||||
|
|
@ -548,7 +508,7 @@ type Builtin struct {
|
|||
}
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -220,10 +220,8 @@ func (*lazyObject) Exported() bool { panic("unreachable") }
|
|||
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") }
|
||||
|
|
|
|||
|
|
@ -35,14 +35,14 @@ func TestSizeof(t *testing.T) {
|
|||
{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},
|
||||
|
|
|
|||
|
|
@ -101,7 +101,6 @@ func defPredeclaredTypes() {
|
|||
// 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)
|
||||
|
|
@ -110,7 +109,6 @@ func defPredeclaredTypes() {
|
|||
// 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)
|
||||
}
|
||||
|
|
@ -118,7 +116,6 @@ func defPredeclaredTypes() {
|
|||
// type error interface{ Error() string }
|
||||
{
|
||||
obj := NewTypeName(nopos, nil, "error", nil)
|
||||
obj.setColor(black)
|
||||
typ := (*Checker)(nil).newNamed(obj, nil, nil)
|
||||
|
||||
// error.Error() string
|
||||
|
|
@ -139,7 +136,6 @@ func defPredeclaredTypes() {
|
|||
// 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
|
||||
|
|
@ -168,7 +164,7 @@ func defPredeclaredConsts() {
|
|||
}
|
||||
|
||||
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.
|
||||
|
|
@ -292,7 +288,7 @@ func init() {
|
|||
// 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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue