From a05da76c7b5c297d32abf7aa836a9efabbadabe9 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 23 Sep 2013 15:14:17 -0700 Subject: [PATCH] go.tools/go/types: represent built-ins as Objects (rather than types) This change affects the API: Func objects now always have a *Signature as type (never a *Builtin). Instead, built-ins now appear as *Builtin objects. Only the built-in name is exposed, other fields are now private to go/types. Several bugs are fixed: - correctly checking for built-ins permitted in statement context - expression statements that are calls are not type-checked twice anymore - go/defer statements report call types and provide good error messages now This CL will briefly break the build until CL 13848043 is submitted. R=adonovan CC=golang-dev https://golang.org/cl/13813043 --- go/types/api.go | 2 - go/types/builtins.go | 11 +-- go/types/call.go | 56 +++++++---- go/types/check_test.go | 4 +- go/types/expr.go | 49 +++++++--- go/types/objects.go | 30 ++++-- go/types/operand.go | 5 +- go/types/return.go | 6 +- go/types/stdlib_test.go | 1 - go/types/stmt.go | 57 +++++------ go/types/testdata/methodsets.src | 6 +- go/types/testdata/stmt0.src | 6 +- go/types/types.go | 44 --------- go/types/typexpr.go | 7 +- go/types/universe.go | 161 +++++++++++++++++++++---------- 15 files changed, 253 insertions(+), 192 deletions(-) diff --git a/go/types/api.go b/go/types/api.go index effc38a3..563dafc7 100644 --- a/go/types/api.go +++ b/go/types/api.go @@ -185,7 +185,5 @@ func IsAssignableTo(V, T Type) bool { // BUG(gri): Some built-ins don't check parameters fully, yet (e.g. append). // BUG(gri): Use of labels is only partially checked. -// BUG(gri): Unused variables and imports are not reported. // BUG(gri): Interface vs non-interface comparisons are not correctly implemented. // BUG(gri): Switch statements don't check duplicate cases for all types for which it is required. -// BUG(gri): Some built-ins may not be callable if in statement-context. diff --git a/go/types/builtins.go b/go/types/builtins.go index 560b8b26..3be050c9 100644 --- a/go/types/builtins.go +++ b/go/types/builtins.go @@ -16,13 +16,11 @@ import ( // TODO(gri): Several built-ins are missing assignment checks. As a result, // non-constant shift arguments may not be properly type-checked. -// builtin typechecks a built-in call. The built-in type is bin, and the result -// of the call is returned via x. If the call has type errors, the returned x is -// marked as invalid (x.mode == invalid). +// builtin typechecks a call to a built-in and returns the result via x. +// If the call has type errors, the returned x is marked as invalid. // -func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin) { +func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) { args := call.Args - id := bin.id // declare before goto's var arg0 ast.Expr // first argument, if present @@ -30,9 +28,10 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin) { // check argument count n := len(args) msg := "" + bin := predeclaredFuncs[id] if n < bin.nargs { msg = "not enough" - } else if !bin.isVariadic && n > bin.nargs { + } else if !bin.variadic && n > bin.nargs { msg = "too many" } if msg != "" { diff --git a/go/types/call.go b/go/types/call.go index d81e2cf5..005ded75 100644 --- a/go/types/call.go +++ b/go/types/call.go @@ -9,11 +9,26 @@ package types import ( "go/ast" "go/token" + + "code.google.com/p/go.tools/go/exact" ) -func (check *checker) call(x *operand, e *ast.CallExpr) { +// exprKind describes the kind of an expression; +// the kind determines if an expression is valid +// in 'statement context'. +type exprKind int + +const ( + conversion exprKind = iota + expression + statement +) + +func (check *checker) call(x *operand, e *ast.CallExpr) exprKind { check.exprOrType(x, e.Fun) - if x.mode == invalid { + + switch x.mode { + case invalid: // We don't have a valid call or conversion but we have a list of arguments. // Typecheck them independently for better partial type information in // the presence of type errors. @@ -22,10 +37,9 @@ func (check *checker) call(x *operand, e *ast.CallExpr) { } x.mode = invalid x.expr = e - return - } + return statement - if x.mode == typexpr { + case typexpr: // conversion T := x.typ x.mode = invalid @@ -44,11 +58,23 @@ func (check *checker) call(x *operand, e *ast.CallExpr) { check.errorf(e.Args[n-1].Pos(), "too many arguments in conversion to %s", T) } x.expr = e - return - } + return conversion - if sig, ok := x.typ.Underlying().(*Signature); ok { + case builtin: + id, _ := exact.Int64Val(x.val) + check.builtin(x, e, builtinId(id)) + return predeclaredFuncs[id].kind + + default: // function/method call + sig, _ := x.typ.Underlying().(*Signature) + if sig == nil { + check.invalidOp(x.pos(), "cannot call non-function %s", x) + x.mode = invalid + x.expr = e + return statement + } + passSlice := false if e.Ellipsis.IsValid() { // last argument is of the form x... @@ -116,17 +142,9 @@ func (check *checker) call(x *operand, e *ast.CallExpr) { x.typ = sig.results } x.expr = e - return - } - if bin, ok := x.typ.(*Builtin); ok { - check.builtin(x, e, bin) - return + return statement } - - check.invalidOp(x.pos(), "cannot call non-function %s", x) - x.mode = invalid - x.expr = e } // argument checks passing of argument x to the i'th parameter of the given signature. @@ -218,6 +236,10 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) { case *Func: x.mode = value x.typ = exp.typ + case *Builtin: + x.mode = builtin + x.typ = exp.typ + x.val = exact.MakeInt64(int64(exp.id)) default: unreachable() } diff --git a/go/types/check_test.go b/go/types/check_test.go index 0a58589e..a4dd555c 100644 --- a/go/types/check_test.go +++ b/go/types/check_test.go @@ -248,9 +248,7 @@ func TestCheck(t *testing.T) { // the construction of the Universe var. if !testBuiltinsDeclared { testBuiltinsDeclared = true - // Pkg == nil for Universe objects - def(NewFunc(token.NoPos, nil, "assert", &Builtin{_Assert, "assert", 1, false, true})) - def(NewFunc(token.NoPos, nil, "trace", &Builtin{_Trace, "trace", 0, true, true})) + defPredeclaredTestFuncs() } // If explicit test files are specified, only check those. diff --git a/go/types/expr.go b/go/types/expr.go index 8a8151fc..d4ca5eba 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -844,13 +844,13 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64 // value or type. If an error occurred, x.mode is set to invalid. // If hint != nil, it is the type of a composite literal element. // -func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) { +func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind { if trace { check.trace(e.Pos(), "%s", e) check.indent++ } - check.expr0(x, e, hint) + kind := check.expr0(x, e, hint) // convert x into a user-friendly set of values record := true @@ -885,12 +885,14 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) { check.indent-- check.trace(e.Pos(), "=> %s", x) } + + return kind } // expr0 contains the core of type checking of expressions. // Must only be called by rawExpr. // -func (check *checker) expr0(x *operand, e ast.Expr, hint Type) { +func (check *checker) expr0(x *operand, e ast.Expr, hint Type) exprKind { // make sure x has a valid state in case of bailout // (was issue 5770) x.mode = invalid @@ -1070,7 +1072,9 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) { x.typ = typ case *ast.ParenExpr: - check.rawExpr(x, e.X, nil) + kind := check.rawExpr(x, e.X, nil) + x.expr = e + return kind case *ast.SelectorExpr: check.selector(x, e) @@ -1130,7 +1134,7 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) { x.mode = valueok x.typ = typ.elt x.expr = e - return + return expression } if !valid { @@ -1246,7 +1250,7 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) { x.typ = T case *ast.CallExpr: - check.call(x, e) + return check.call(x, e) case *ast.StarExpr: check.exprOrType(x, e.X) @@ -1274,6 +1278,10 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) { if x.mode == invalid { goto Error } + if e.Op == token.ARROW { + x.expr = e + return statement // receive operators may appear in statement context + } case *ast.BinaryExpr: check.binary(x, e.X, e.Y, e.Op) @@ -1305,11 +1313,12 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) { // everything went well x.expr = e - return + return expression Error: x.mode = invalid x.expr = e + return statement // avoid follow-up errors } // typeAssertion checks that x.(T) is legal; xtyp must be the type of x. @@ -1332,14 +1341,19 @@ func (check *checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface, // func (check *checker) expr(x *operand, e ast.Expr) { check.rawExpr(x, e, nil) + var msg string switch x.mode { + default: + return case novalue: - check.errorf(x.pos(), "%s used as value", x) - x.mode = invalid + msg = "%s used as value" + case builtin: + msg = "%s must be called" case typexpr: - check.errorf(x.pos(), "%s is not an expression", x) - x.mode = invalid + msg = "%s is not an expression" } + check.errorf(x.pos(), msg, x) + x.mode = invalid } // exprWithHint typechecks expression e and initializes x with the expression value. @@ -1349,14 +1363,19 @@ func (check *checker) expr(x *operand, e ast.Expr) { func (check *checker) exprWithHint(x *operand, e ast.Expr, hint Type) { assert(hint != nil) check.rawExpr(x, e, hint) + var msg string switch x.mode { + default: + return case novalue: - check.errorf(x.pos(), "%s used as value", x) - x.mode = invalid + msg = "%s used as value" + case builtin: + msg = "%s must be called" case typexpr: - check.errorf(x.pos(), "%s is not an expression", x) - x.mode = invalid + msg = "%s is not an expression" } + check.errorf(x.pos(), msg, x) + x.mode = invalid } // exprOrType typechecks expression or type e and initializes x with the expression value or type. diff --git a/go/types/objects.go b/go/types/objects.go index 7879fbac..020a3e16 100644 --- a/go/types/objects.go +++ b/go/types/objects.go @@ -187,13 +187,18 @@ func (obj *Var) Anonymous() bool { return obj.anonymous } func (obj *Var) String() string { return obj.toString("var", obj.typ) } // A Func represents a declared function, concrete method, or abstract -// (interface) method. Its Type() is a *Signature. +// (interface) method. Its Type() is always a *Signature. // An abstract method may belong to many interfaces due to embedding. type Func struct { object } -func NewFunc(pos token.Pos, pkg *Package, name string, typ Type) *Func { +func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func { + // don't store a nil signature + var typ Type + if sig != nil { + typ = sig + } return &Func{object{nil, pos, pkg, name, typ, false}} } @@ -206,7 +211,8 @@ func (obj *Func) FullName() string { } func (obj *Func) fullname(buf *bytes.Buffer) { - if sig, _ := obj.typ.(*Signature); sig != nil { // may be a *Builtin + if obj.typ != nil { + sig := obj.typ.(*Signature) if recv := sig.Recv(); recv != nil { buf.WriteByte('(') if _, ok := recv.Type().(*Interface); ok { @@ -232,8 +238,8 @@ func (obj *Func) String() string { var buf bytes.Buffer buf.WriteString("func ") obj.fullname(&buf) - if sig, _ := obj.typ.(*Signature); sig != nil { - writeSignature(&buf, sig) + if obj.typ != nil { + writeSignature(&buf, obj.typ.(*Signature)) } return buf.String() } @@ -244,7 +250,19 @@ type Label struct { } func NewLabel(pos token.Pos, name string) *Label { - return &Label{object{nil, pos, nil, name, nil, false}} + return &Label{object{pos: pos, name: name}} } func (obj *Label) String() string { return fmt.Sprintf("label %s", obj.Name()) } + +// A Builtin represents a built-in function. +// Builtins don't have a valid type. +type Builtin struct { + object + + id builtinId +} + +func newBuiltin(id builtinId) *Builtin { + return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid]}, id} +} diff --git a/go/types/operand.go b/go/types/operand.go index 7c49abf5..b53a3807 100644 --- a/go/types/operand.go +++ b/go/types/operand.go @@ -21,6 +21,7 @@ type operandMode int const ( invalid operandMode = iota // operand is invalid novalue // operand represents no value (result of a function call w/o result) + builtin // operand is a built-in function typexpr // operand is a type constant // operand is a constant; the operand's typ is a Basic type variable // operand is an addressable variable @@ -31,6 +32,7 @@ const ( var operandModeString = [...]string{ invalid: "invalid", novalue: "no value", + builtin: "builtin", typexpr: "type", constant: "constant", variable: "variable", @@ -40,7 +42,8 @@ var operandModeString = [...]string{ // An operand represents an intermediate value during type checking. // Operands have an (addressing) mode, the expression evaluating to -// the operand, the operand's type, and a value if mode == constant. +// the operand, the operand's type, and a value if mode == constant +// or builtin. The built-in id is encoded as an exact Int64 in val. // The zero value of operand is a ready to use invalid operand. // type operand struct { diff --git a/go/types/return.go b/go/types/return.go index 68f3f6ea..ad5b22d0 100644 --- a/go/types/return.go +++ b/go/types/return.go @@ -32,11 +32,7 @@ func (check *checker) isTerminating(s ast.Stmt, label string) bool { if call, _ := s.X.(*ast.CallExpr); call != nil { if id, _ := call.Fun.(*ast.Ident); id != nil { if obj := check.topScope.LookupParent(id.Name); obj != nil { - // TODO(gri) Predeclared functions should be modelled as objects - // rather then ordinary functions that have a predeclared - // function type. This would simplify code here and else- - // where. - if f, _ := obj.(*Func); f != nil && f.typ == predeclaredFunctions[_Panic] { + if b, _ := obj.(*Builtin); b != nil && b.id == _Panic { return true } } diff --git a/go/types/stdlib_test.go b/go/types/stdlib_test.go index 9bf50296..95f06ded 100644 --- a/go/types/stdlib_test.go +++ b/go/types/stdlib_test.go @@ -134,7 +134,6 @@ func TestStdfixed(t *testing.T) { "bug223.go", "bug413.go", "bug459.go", // TODO(gri) complete initialization checks "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore "bug250.go", // TODO(gri) fix recursive interfaces - "bug376.go", // TODO(gri) built-ins must be called (no built-in function expressions) "issue3924.go", // TODO(gri) && and || produce bool result (not untyped bool) "issue4847.go", // TODO(gri) initialization cycle error not found ) diff --git a/go/types/stmt.go b/go/types/stmt.go index efeede87..dfd6338a 100644 --- a/go/types/stmt.go +++ b/go/types/stmt.go @@ -71,6 +71,22 @@ func assignOp(op token.Token) token.Token { return token.ILLEGAL } +func (check *checker) suspendedCall(keyword string, call *ast.CallExpr) { + var x operand + var msg string + switch check.rawExpr(&x, call, nil) { + case conversion: + msg = "requires function call, not conversion" + case expression: + msg = "discards result of" + case statement: + return + default: + panic("unreachable") + } + check.errorf(x.pos(), "%s %s %s", keyword, msg, &x) +} + // stmt typechecks statement s. func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) { // statements cannot use iota in general @@ -99,35 +115,14 @@ func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) { check.stmt(s.Stmt, fallthroughOk) case *ast.ExprStmt: + // spec: "With the exception of specific built-in functions, + // function and method calls and receive operations can appear + // in statement context. Such statements may be parenthesized." var x operand - used := false - switch e := unparen(s.X).(type) { - case *ast.CallExpr: - // function calls are permitted - used = true - // but some builtins are excluded - // (Caution: This evaluates e.Fun twice, once here and once - // below as part of s.X. Perhaps this can be avoided.) - check.expr(&x, e.Fun) - if x.mode != invalid { - if b, ok := x.typ.(*Builtin); ok && !b.isStatement { - used = false - } - } - case *ast.UnaryExpr: - // receive operations are permitted - if e.Op == token.ARROW { - used = true - } - } - if !used { - check.errorf(s.Pos(), "%s not used", s.X) - // ok to continue - } - check.rawExpr(&x, s.X, nil) - if x.mode == typexpr { - check.errorf(x.pos(), "%s is not an expression", &x) + if check.rawExpr(&x, s.X, nil) != statement { + check.errorf(s.X.Pos(), "%s is not used", s.X) } + return case *ast.SendStmt: var ch, x operand @@ -195,14 +190,10 @@ func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) { } case *ast.GoStmt: - var x operand - check.call(&x, s.Call) - // TODO(gri) If a builtin is called, the builtin must be valid in this context. + check.suspendedCall("go", s.Call) case *ast.DeferStmt: - var x operand - check.call(&x, s.Call) - // TODO(gri) If a builtin is called, the builtin must be valid in this context. + check.suspendedCall("defer", s.Call) case *ast.ReturnStmt: sig := check.funcSig diff --git a/go/types/testdata/methodsets.src b/go/types/testdata/methodsets.src index 60988bbc..89211468 100644 --- a/go/types/testdata/methodsets.src +++ b/go/types/testdata/methodsets.src @@ -161,11 +161,9 @@ func _() { } // Method calls with value receivers -// TODO(gri) For internal reasons, these expressions are evaluated twice, -// hence the double (equal) error messages for now. Fix this. func _() { T0{}.v0() - T0 /* ERROR "not in method set" */ /* ERROR "not in method set" */ {}.p0() + T0 /* ERROR "not in method set" */ {}.p0() (&T0{}).v0() (&T0{}).p0() @@ -175,7 +173,7 @@ func _() { // no values for T2 T3{}.v0() - T3 /* ERROR "not in method set" */ /* ERROR "not in method set" */ {}.p0() + T3 /* ERROR "not in method set" */ {}.p0() T3{}.v1() T3{}.p1() T3{}.v2() diff --git a/go/types/testdata/stmt0.src b/go/types/testdata/stmt0.src index 4856eebc..953c3df8 100644 --- a/go/types/testdata/stmt0.src +++ b/go/types/testdata/stmt0.src @@ -149,18 +149,20 @@ func selects() { func gos() { go 1 /* ERROR "expected function/method call" */ + go int /* ERROR "go requires function call, not conversion" */ (0) go gos() var c chan int go close(c) - go len(c) // TODO(gri) this should not be legal + go len /* ERROR "go discards result" */ (c) } func defers() { defer 1 /* ERROR "expected function/method call" */ + defer int /* ERROR "defer requires function call, not conversion" */ (0) defer defers() var c chan int defer close(c) - defer len(c) // TODO(gri) this should not be legal + defer len /* ERROR "defer discards result" */ (c) } func switches0() { diff --git a/go/types/types.go b/go/types/types.go index 9144295d..262272cd 100644 --- a/go/types/types.go +++ b/go/types/types.go @@ -236,50 +236,6 @@ func (s *Signature) Results() *Tuple { return s.results } // IsVariadic reports whether the signature s is variadic. func (s *Signature) IsVariadic() bool { return s.isVariadic } -// builtinId is an id of a builtin function. -type builtinId int - -// Predeclared builtin functions. -const ( - // Universe scope - _Append builtinId = iota - _Cap - _Close - _Complex - _Copy - _Delete - _Imag - _Len - _Make - _New - _Panic - _Print - _Println - _Real - _Recover - - // Unsafe package - _Alignof - _Offsetof - _Sizeof - - // Testing support - _Assert - _Trace -) - -// A Builtin represents the type of a built-in function. -type Builtin struct { - id builtinId - name string - nargs int // number of arguments (minimum if variadic) - isVariadic bool - isStatement bool // true if the built-in is valid as an expression statement -} - -// Name returns the name of the built-in function b. -func (b *Builtin) Name() string { return b.name } - // An Interface represents an interface type. type Interface struct { methods []*Func // methods declared with or embedded in this interface diff --git a/go/types/typexpr.go b/go/types/typexpr.go index 8d83b4f2..201897a9 100644 --- a/go/types/typexpr.go +++ b/go/types/typexpr.go @@ -89,9 +89,14 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool) x.mode = variable case *Func: - obj.used = true // use dot-imported function + obj.used = true x.mode = value + case *Builtin: + obj.used = true // for built-ins defined by package unsafe + x.mode = builtin + x.val = exact.MakeInt64(int64(obj.id)) + default: unreachable() } diff --git a/go/types/universe.go b/go/types/universe.go index 86533df5..70876c69 100644 --- a/go/types/universe.go +++ b/go/types/universe.go @@ -19,7 +19,6 @@ var ( universeIota *Const ) -// Predeclared types, indexed by BasicKind. var Typ = [...]*Basic{ Invalid: {Invalid, 0, 0, "invalid type"}, @@ -56,41 +55,7 @@ var aliases = [...]*Basic{ {Rune, IsInteger, 4, "rune"}, } -var predeclaredConstants = [...]*Const{ - NewConst(token.NoPos, nil, "true", Typ[UntypedBool], exact.MakeBool(true)), - NewConst(token.NoPos, nil, "false", Typ[UntypedBool], exact.MakeBool(false)), - NewConst(token.NoPos, nil, "iota", Typ[UntypedInt], exact.MakeInt64(0)), - NewConst(token.NoPos, nil, "nil", Typ[UntypedNil], exact.MakeNil()), -} - -var predeclaredFunctions = [...]*Builtin{ - {_Append, "append", 1, true, false}, - {_Cap, "cap", 1, false, false}, - {_Close, "close", 1, false, true}, - {_Complex, "complex", 2, false, false}, - {_Copy, "copy", 2, false, true}, - {_Delete, "delete", 2, false, true}, - {_Imag, "imag", 1, false, false}, - {_Len, "len", 1, false, false}, - {_Make, "make", 1, true, false}, - {_New, "new", 1, false, false}, - {_Panic, "panic", 1, false, true}, - {_Print, "print", 0, true, true}, - {_Println, "println", 0, true, true}, - {_Real, "real", 1, false, false}, - {_Recover, "recover", 0, false, true}, - - {_Alignof, "Alignof", 1, false, false}, - {_Offsetof, "Offsetof", 1, false, false}, - {_Sizeof, "Sizeof", 1, false, false}, -} - -func init() { - Universe = NewScope(nil) - Unsafe = NewPackage("unsafe", "unsafe", NewScope(Universe)) - Unsafe.complete = true - - // predeclared types +func defPredeclaredTypes() { for _, t := range Typ { def(NewTypeName(token.NoPos, nil, t.name, t)) } @@ -98,24 +63,116 @@ func init() { def(NewTypeName(token.NoPos, nil, t.name, t)) } - // error type - { - // Error has a nil package in its qualified name since it is in no package - res := NewVar(token.NoPos, nil, "", Typ[String]) - sig := &Signature{results: NewTuple(res)} - err := NewFunc(token.NoPos, nil, "Error", sig) - typ := &Named{underlying: &Interface{methods: []*Func{err}}, complete: true} - sig.recv = NewVar(token.NoPos, nil, "", typ) - def(NewTypeName(token.NoPos, nil, "error", typ)) - } + // Error has a nil package in its qualified name since it is in no package + res := NewVar(token.NoPos, nil, "", Typ[String]) + sig := &Signature{results: NewTuple(res)} + err := NewFunc(token.NoPos, nil, "Error", sig) + typ := &Named{underlying: &Interface{methods: []*Func{err}}, complete: true} + sig.recv = NewVar(token.NoPos, nil, "", typ) + def(NewTypeName(token.NoPos, nil, "error", typ)) +} - for _, c := range predeclaredConstants { - def(c) - } +var predeclaredConsts = [...]struct { + name string + kind BasicKind + val exact.Value +}{ + {"true", UntypedBool, exact.MakeBool(true)}, + {"false", UntypedBool, exact.MakeBool(false)}, + {"iota", UntypedInt, exact.MakeInt64(0)}, + {"nil", UntypedNil, exact.MakeNil()}, +} - for _, f := range predeclaredFunctions { - def(NewFunc(token.NoPos, nil, f.name, f)) +func defPredeclaredConsts() { + for _, c := range predeclaredConsts { + def(NewConst(token.NoPos, nil, c.name, Typ[c.kind], c.val)) } +} + +// A builtinId is the id of a builtin function. +type builtinId int + +const ( + // universe scope + _Append builtinId = iota + _Cap + _Close + _Complex + _Copy + _Delete + _Imag + _Len + _Make + _New + _Panic + _Print + _Println + _Real + _Recover + + // package unsafe + _Alignof + _Offsetof + _Sizeof + + // testing support + _Assert + _Trace +) + +var predeclaredFuncs = [...]struct { + name string + nargs int + variadic bool + kind exprKind +}{ + _Append: {"append", 1, true, expression}, + _Cap: {"cap", 1, false, expression}, + _Close: {"close", 1, false, statement}, + _Complex: {"complex", 2, false, expression}, + _Copy: {"copy", 2, false, statement}, + _Delete: {"delete", 2, false, statement}, + _Imag: {"imag", 1, false, expression}, + _Len: {"len", 1, false, expression}, + _Make: {"make", 1, true, expression}, + _New: {"new", 1, false, expression}, + _Panic: {"panic", 1, false, statement}, + _Print: {"print", 0, true, statement}, + _Println: {"println", 0, true, statement}, + _Real: {"real", 1, false, expression}, + _Recover: {"recover", 0, false, statement}, + + _Alignof: {"Alignof", 1, false, expression}, + _Offsetof: {"Offsetof", 1, false, expression}, + _Sizeof: {"Sizeof", 1, false, expression}, + + _Assert: {"assert", 1, false, statement}, + _Trace: {"trace", 0, true, statement}, +} + +func defPredeclaredFuncs() { + for i := range predeclaredFuncs { + id := builtinId(i) + if id == _Assert || id == _Trace { + continue // only define these in testing environment + } + def(newBuiltin(id)) + } +} + +func defPredeclaredTestFuncs() { + def(newBuiltin(_Assert)) + def(newBuiltin(_Trace)) +} + +func init() { + Universe = NewScope(nil) + Unsafe = NewPackage("unsafe", "unsafe", NewScope(Universe)) + Unsafe.complete = true + + defPredeclaredTypes() + defPredeclaredConsts() + defPredeclaredFuncs() universeIota = Universe.Lookup("iota").(*Const) } @@ -141,7 +198,7 @@ func def(obj Object) { switch obj := obj.(type) { case *TypeName: obj.pkg = Unsafe - case *Func: + case *Builtin: obj.pkg = Unsafe default: unreachable()