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
This commit is contained in:
Robert Griesemer 2013-09-23 15:14:17 -07:00
parent 3371b79a96
commit a05da76c7b
15 changed files with 253 additions and 192 deletions

View File

@ -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): Some built-ins don't check parameters fully, yet (e.g. append).
// BUG(gri): Use of labels is only partially checked. // 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): 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): 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.

View File

@ -16,13 +16,11 @@ import (
// TODO(gri): Several built-ins are missing assignment checks. As a result, // TODO(gri): Several built-ins are missing assignment checks. As a result,
// non-constant shift arguments may not be properly type-checked. // 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 // builtin typechecks a call to a built-in and returns the result via x.
// of the call is returned via x. If the call has type errors, the returned x is // If the call has type errors, the returned x is marked as invalid.
// marked as invalid (x.mode == 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 args := call.Args
id := bin.id
// declare before goto's // declare before goto's
var arg0 ast.Expr // first argument, if present 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 // check argument count
n := len(args) n := len(args)
msg := "" msg := ""
bin := predeclaredFuncs[id]
if n < bin.nargs { if n < bin.nargs {
msg = "not enough" msg = "not enough"
} else if !bin.isVariadic && n > bin.nargs { } else if !bin.variadic && n > bin.nargs {
msg = "too many" msg = "too many"
} }
if msg != "" { if msg != "" {

View File

@ -9,11 +9,26 @@ package types
import ( import (
"go/ast" "go/ast"
"go/token" "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) 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. // 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 // Typecheck them independently for better partial type information in
// the presence of type errors. // the presence of type errors.
@ -22,10 +37,9 @@ func (check *checker) call(x *operand, e *ast.CallExpr) {
} }
x.mode = invalid x.mode = invalid
x.expr = e x.expr = e
return return statement
}
if x.mode == typexpr { case typexpr:
// conversion // conversion
T := x.typ T := x.typ
x.mode = invalid 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) check.errorf(e.Args[n-1].Pos(), "too many arguments in conversion to %s", T)
} }
x.expr = e 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 // 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 passSlice := false
if e.Ellipsis.IsValid() { if e.Ellipsis.IsValid() {
// last argument is of the form x... // 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.typ = sig.results
} }
x.expr = e x.expr = e
return
}
if bin, ok := x.typ.(*Builtin); ok { return statement
check.builtin(x, e, bin)
return
} }
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. // 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: case *Func:
x.mode = value x.mode = value
x.typ = exp.typ x.typ = exp.typ
case *Builtin:
x.mode = builtin
x.typ = exp.typ
x.val = exact.MakeInt64(int64(exp.id))
default: default:
unreachable() unreachable()
} }

View File

@ -248,9 +248,7 @@ func TestCheck(t *testing.T) {
// the construction of the Universe var. // the construction of the Universe var.
if !testBuiltinsDeclared { if !testBuiltinsDeclared {
testBuiltinsDeclared = true testBuiltinsDeclared = true
// Pkg == nil for Universe objects defPredeclaredTestFuncs()
def(NewFunc(token.NoPos, nil, "assert", &Builtin{_Assert, "assert", 1, false, true}))
def(NewFunc(token.NoPos, nil, "trace", &Builtin{_Trace, "trace", 0, true, true}))
} }
// If explicit test files are specified, only check those. // If explicit test files are specified, only check those.

View File

@ -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. // 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. // 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 { if trace {
check.trace(e.Pos(), "%s", e) check.trace(e.Pos(), "%s", e)
check.indent++ check.indent++
} }
check.expr0(x, e, hint) kind := check.expr0(x, e, hint)
// convert x into a user-friendly set of values // convert x into a user-friendly set of values
record := true record := true
@ -885,12 +885,14 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) {
check.indent-- check.indent--
check.trace(e.Pos(), "=> %s", x) check.trace(e.Pos(), "=> %s", x)
} }
return kind
} }
// expr0 contains the core of type checking of expressions. // expr0 contains the core of type checking of expressions.
// Must only be called by rawExpr. // 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 // make sure x has a valid state in case of bailout
// (was issue 5770) // (was issue 5770)
x.mode = invalid x.mode = invalid
@ -1070,7 +1072,9 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) {
x.typ = typ x.typ = typ
case *ast.ParenExpr: case *ast.ParenExpr:
check.rawExpr(x, e.X, nil) kind := check.rawExpr(x, e.X, nil)
x.expr = e
return kind
case *ast.SelectorExpr: case *ast.SelectorExpr:
check.selector(x, e) check.selector(x, e)
@ -1130,7 +1134,7 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) {
x.mode = valueok x.mode = valueok
x.typ = typ.elt x.typ = typ.elt
x.expr = e x.expr = e
return return expression
} }
if !valid { if !valid {
@ -1246,7 +1250,7 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) {
x.typ = T x.typ = T
case *ast.CallExpr: case *ast.CallExpr:
check.call(x, e) return check.call(x, e)
case *ast.StarExpr: case *ast.StarExpr:
check.exprOrType(x, e.X) check.exprOrType(x, e.X)
@ -1274,6 +1278,10 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) {
if x.mode == invalid { if x.mode == invalid {
goto Error goto Error
} }
if e.Op == token.ARROW {
x.expr = e
return statement // receive operators may appear in statement context
}
case *ast.BinaryExpr: case *ast.BinaryExpr:
check.binary(x, e.X, e.Y, e.Op) 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 // everything went well
x.expr = e x.expr = e
return return expression
Error: Error:
x.mode = invalid x.mode = invalid
x.expr = e x.expr = e
return statement // avoid follow-up errors
} }
// typeAssertion checks that x.(T) is legal; xtyp must be the type of x. // 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) { func (check *checker) expr(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil) check.rawExpr(x, e, nil)
var msg string
switch x.mode { switch x.mode {
default:
return
case novalue: case novalue:
check.errorf(x.pos(), "%s used as value", x) msg = "%s used as value"
x.mode = invalid case builtin:
msg = "%s must be called"
case typexpr: case typexpr:
check.errorf(x.pos(), "%s is not an expression", x) msg = "%s is not an expression"
x.mode = invalid
} }
check.errorf(x.pos(), msg, x)
x.mode = invalid
} }
// exprWithHint typechecks expression e and initializes x with the expression value. // 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) { func (check *checker) exprWithHint(x *operand, e ast.Expr, hint Type) {
assert(hint != nil) assert(hint != nil)
check.rawExpr(x, e, hint) check.rawExpr(x, e, hint)
var msg string
switch x.mode { switch x.mode {
default:
return
case novalue: case novalue:
check.errorf(x.pos(), "%s used as value", x) msg = "%s used as value"
x.mode = invalid case builtin:
msg = "%s must be called"
case typexpr: case typexpr:
check.errorf(x.pos(), "%s is not an expression", x) msg = "%s is not an expression"
x.mode = invalid
} }
check.errorf(x.pos(), msg, x)
x.mode = invalid
} }
// exprOrType typechecks expression or type e and initializes x with the expression value or type. // exprOrType typechecks expression or type e and initializes x with the expression value or type.

View File

@ -187,13 +187,18 @@ func (obj *Var) Anonymous() bool { return obj.anonymous }
func (obj *Var) String() string { return obj.toString("var", obj.typ) } func (obj *Var) String() string { return obj.toString("var", obj.typ) }
// A Func represents a declared function, concrete method, or abstract // 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. // An abstract method may belong to many interfaces due to embedding.
type Func struct { type Func struct {
object 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}} 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) { 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 { if recv := sig.Recv(); recv != nil {
buf.WriteByte('(') buf.WriteByte('(')
if _, ok := recv.Type().(*Interface); ok { if _, ok := recv.Type().(*Interface); ok {
@ -232,8 +238,8 @@ func (obj *Func) String() string {
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString("func ") buf.WriteString("func ")
obj.fullname(&buf) obj.fullname(&buf)
if sig, _ := obj.typ.(*Signature); sig != nil { if obj.typ != nil {
writeSignature(&buf, sig) writeSignature(&buf, obj.typ.(*Signature))
} }
return buf.String() return buf.String()
} }
@ -244,7 +250,19 @@ type Label struct {
} }
func NewLabel(pos token.Pos, name string) *Label { 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()) } 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}
}

View File

@ -21,6 +21,7 @@ type operandMode int
const ( const (
invalid operandMode = iota // operand is invalid invalid operandMode = iota // operand is invalid
novalue // operand represents no value (result of a function call w/o result) 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 typexpr // operand is a type
constant // operand is a constant; the operand's typ is a Basic type constant // operand is a constant; the operand's typ is a Basic type
variable // operand is an addressable variable variable // operand is an addressable variable
@ -31,6 +32,7 @@ const (
var operandModeString = [...]string{ var operandModeString = [...]string{
invalid: "invalid", invalid: "invalid",
novalue: "no value", novalue: "no value",
builtin: "builtin",
typexpr: "type", typexpr: "type",
constant: "constant", constant: "constant",
variable: "variable", variable: "variable",
@ -40,7 +42,8 @@ var operandModeString = [...]string{
// An operand represents an intermediate value during type checking. // An operand represents an intermediate value during type checking.
// Operands have an (addressing) mode, the expression evaluating to // 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. // The zero value of operand is a ready to use invalid operand.
// //
type operand struct { type operand struct {

View File

@ -32,11 +32,7 @@ func (check *checker) isTerminating(s ast.Stmt, label string) bool {
if call, _ := s.X.(*ast.CallExpr); call != nil { if call, _ := s.X.(*ast.CallExpr); call != nil {
if id, _ := call.Fun.(*ast.Ident); id != nil { if id, _ := call.Fun.(*ast.Ident); id != nil {
if obj := check.topScope.LookupParent(id.Name); obj != nil { if obj := check.topScope.LookupParent(id.Name); obj != nil {
// TODO(gri) Predeclared functions should be modelled as objects if b, _ := obj.(*Builtin); b != nil && b.id == _Panic {
// 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] {
return true return true
} }
} }

View File

@ -134,7 +134,6 @@ func TestStdfixed(t *testing.T) {
"bug223.go", "bug413.go", "bug459.go", // TODO(gri) complete initialization checks "bug223.go", "bug413.go", "bug459.go", // TODO(gri) complete initialization checks
"bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
"bug250.go", // TODO(gri) fix recursive interfaces "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) "issue3924.go", // TODO(gri) && and || produce bool result (not untyped bool)
"issue4847.go", // TODO(gri) initialization cycle error not found "issue4847.go", // TODO(gri) initialization cycle error not found
) )

View File

@ -71,6 +71,22 @@ func assignOp(op token.Token) token.Token {
return token.ILLEGAL 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. // stmt typechecks statement s.
func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) { func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) {
// statements cannot use iota in general // statements cannot use iota in general
@ -99,35 +115,14 @@ func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) {
check.stmt(s.Stmt, fallthroughOk) check.stmt(s.Stmt, fallthroughOk)
case *ast.ExprStmt: 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 var x operand
used := false if check.rawExpr(&x, s.X, nil) != statement {
switch e := unparen(s.X).(type) { check.errorf(s.X.Pos(), "%s is not used", s.X)
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)
} }
return
case *ast.SendStmt: case *ast.SendStmt:
var ch, x operand var ch, x operand
@ -195,14 +190,10 @@ func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) {
} }
case *ast.GoStmt: case *ast.GoStmt:
var x operand check.suspendedCall("go", s.Call)
check.call(&x, s.Call)
// TODO(gri) If a builtin is called, the builtin must be valid in this context.
case *ast.DeferStmt: case *ast.DeferStmt:
var x operand check.suspendedCall("defer", s.Call)
check.call(&x, s.Call)
// TODO(gri) If a builtin is called, the builtin must be valid in this context.
case *ast.ReturnStmt: case *ast.ReturnStmt:
sig := check.funcSig sig := check.funcSig

View File

@ -161,11 +161,9 @@ func _() {
} }
// Method calls with value receivers // 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 _() { func _() {
T0{}.v0() T0{}.v0()
T0 /* ERROR "not in method set" */ /* ERROR "not in method set" */ {}.p0() T0 /* ERROR "not in method set" */ {}.p0()
(&T0{}).v0() (&T0{}).v0()
(&T0{}).p0() (&T0{}).p0()
@ -175,7 +173,7 @@ func _() {
// no values for T2 // no values for T2
T3{}.v0() T3{}.v0()
T3 /* ERROR "not in method set" */ /* ERROR "not in method set" */ {}.p0() T3 /* ERROR "not in method set" */ {}.p0()
T3{}.v1() T3{}.v1()
T3{}.p1() T3{}.p1()
T3{}.v2() T3{}.v2()

View File

@ -149,18 +149,20 @@ func selects() {
func gos() { func gos() {
go 1 /* ERROR "expected function/method call" */ go 1 /* ERROR "expected function/method call" */
go int /* ERROR "go requires function call, not conversion" */ (0)
go gos() go gos()
var c chan int var c chan int
go close(c) go close(c)
go len(c) // TODO(gri) this should not be legal go len /* ERROR "go discards result" */ (c)
} }
func defers() { func defers() {
defer 1 /* ERROR "expected function/method call" */ defer 1 /* ERROR "expected function/method call" */
defer int /* ERROR "defer requires function call, not conversion" */ (0)
defer defers() defer defers()
var c chan int var c chan int
defer close(c) defer close(c)
defer len(c) // TODO(gri) this should not be legal defer len /* ERROR "defer discards result" */ (c)
} }
func switches0() { func switches0() {

View File

@ -236,50 +236,6 @@ func (s *Signature) Results() *Tuple { return s.results }
// IsVariadic reports whether the signature s is variadic. // IsVariadic reports whether the signature s is variadic.
func (s *Signature) IsVariadic() bool { return s.isVariadic } 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. // An Interface represents an interface type.
type Interface struct { type Interface struct {
methods []*Func // methods declared with or embedded in this interface methods []*Func // methods declared with or embedded in this interface

View File

@ -89,9 +89,14 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool)
x.mode = variable x.mode = variable
case *Func: case *Func:
obj.used = true // use dot-imported function obj.used = true
x.mode = value 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: default:
unreachable() unreachable()
} }

View File

@ -19,7 +19,6 @@ var (
universeIota *Const universeIota *Const
) )
// Predeclared types, indexed by BasicKind.
var Typ = [...]*Basic{ var Typ = [...]*Basic{
Invalid: {Invalid, 0, 0, "invalid type"}, Invalid: {Invalid, 0, 0, "invalid type"},
@ -56,41 +55,7 @@ var aliases = [...]*Basic{
{Rune, IsInteger, 4, "rune"}, {Rune, IsInteger, 4, "rune"},
} }
var predeclaredConstants = [...]*Const{ func defPredeclaredTypes() {
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
for _, t := range Typ { for _, t := range Typ {
def(NewTypeName(token.NoPos, nil, t.name, t)) def(NewTypeName(token.NoPos, nil, t.name, t))
} }
@ -98,24 +63,116 @@ func init() {
def(NewTypeName(token.NoPos, nil, t.name, t)) 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])
// Error has a nil package in its qualified name since it is in no package sig := &Signature{results: NewTuple(res)}
res := NewVar(token.NoPos, nil, "", Typ[String]) err := NewFunc(token.NoPos, nil, "Error", sig)
sig := &Signature{results: NewTuple(res)} typ := &Named{underlying: &Interface{methods: []*Func{err}}, complete: true}
err := NewFunc(token.NoPos, nil, "Error", sig) sig.recv = NewVar(token.NoPos, nil, "", typ)
typ := &Named{underlying: &Interface{methods: []*Func{err}}, complete: true} def(NewTypeName(token.NoPos, nil, "error", typ))
sig.recv = NewVar(token.NoPos, nil, "", typ) }
def(NewTypeName(token.NoPos, nil, "error", typ))
}
for _, c := range predeclaredConstants { var predeclaredConsts = [...]struct {
def(c) 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 { func defPredeclaredConsts() {
def(NewFunc(token.NoPos, nil, f.name, f)) 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) universeIota = Universe.Lookup("iota").(*Const)
} }
@ -141,7 +198,7 @@ func def(obj Object) {
switch obj := obj.(type) { switch obj := obj.(type) {
case *TypeName: case *TypeName:
obj.pkg = Unsafe obj.pkg = Unsafe
case *Func: case *Builtin:
obj.pkg = Unsafe obj.pkg = Unsafe
default: default:
unreachable() unreachable()