diff --git a/src/pkg/exp/types/staging/builtins.go b/src/pkg/exp/types/staging/builtins.go new file mode 100644 index 0000000000..ef9ae80e2b --- /dev/null +++ b/src/pkg/exp/types/staging/builtins.go @@ -0,0 +1,349 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements typechecking of builtin function calls. + +package types + +import ( + "go/ast" + "go/token" +) + +// builtin typechecks a built-in call. The built-in type is bin, and iota is the current +// value of iota or -1 if iota doesn't have a value in the current context. 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). +// +func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota int) { + args := call.Args + id := bin.id + + // declare before goto's + var arg0 ast.Expr + var typ0 Type + + // check argument count + n := len(args) + msg := "" + if n < bin.nargs { + msg = "not enough" + } else if !bin.isVariadic && n > bin.nargs { + msg = "too many" + } + if msg != "" { + check.invalidOp(call.Pos(), msg+"arguments for %s (expected %d, found %d)", call, bin.nargs, n) + goto Error + } + + // common case: evaluate first argument if present; + // if it is an expression, x has the expression value + if n > 0 { + arg0 = args[0] + switch id { + case _Make, _New: + // argument must be a type + typ0 = underlying(check.typ(arg0, false)) + if typ0 == Typ[Invalid] { + goto Error + } + case _Trace: + // _Trace implementation does the work + default: + // argument must be an expression + check.expr(x, arg0, nil, iota) + if x.mode == invalid { + goto Error + } + typ0 = underlying(x.typ) + } + } + + switch id { + case _Append: + s, ok := typ0.(*Slice) + if !ok { + check.invalidArg(x.pos(), "%s is not a typed slice", x) + goto Error + } + for _, arg := range args[1:] { + check.expr(x, arg, nil, iota) + if x.mode == invalid { + goto Error + } + // TODO(gri) check assignability + } + x.mode = value + x.typ = s + + case _Cap, _Len: + mode := invalid + var val interface{} + switch typ := implicitDeref(typ0).(type) { + case *Basic: + if isString(typ) && id == _Len { + if x.mode == constant { + mode = constant + val = int64(len(x.val.(string))) + } else { + mode = value + } + } + + case *Array: + mode = value + if !containsCallsOrReceives(arg0) { + mode = constant + val = typ.Len + } + + case *Slice, *Chan: + mode = value + + case *Map: + if id == _Len { + mode = value + } + } + + if mode == invalid { + check.invalidArg(x.pos(), "%s for %s", x, bin.name) + goto Error + } + x.mode = mode + x.typ = Typ[Int] + x.val = val + + case _Close: + ch, ok := typ0.(*Chan) + if !ok { + check.invalidArg(x.pos(), "%s is not a channel", x) + goto Error + } + if ch.Dir&ast.SEND == 0 { + check.invalidArg(x.pos(), "%s must not be a receive-only channel", x) + goto Error + } + x.mode = novalue + + case _Complex: + var y operand + check.expr(&y, args[1], nil, iota) + if y.mode == invalid { + goto Error + } + // TODO(gri) handle complex(a, b) like (a + toImag(b)) + unimplemented() + + case _Copy: + // TODO(gri) implements checks + unimplemented() + x.mode = value + x.typ = Typ[Int] + + case _Delete: + m, ok := typ0.(*Map) + if !ok { + check.invalidArg(x.pos(), "%s is not a map", x) + goto Error + } + check.expr(x, args[1], nil, iota) + if x.mode == invalid { + goto Error + } + if !x.isAssignable(m.Key) { + check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.Key) + goto Error + } + x.mode = novalue + + case _Imag, _Real: + if !isComplex(typ0) { + check.invalidArg(x.pos(), "%s must be a complex number", x) + goto Error + } + if x.mode == constant { + // nothing to do for x.val == 0 + if !isZeroConst(x.val) { + c := x.val.(complex) + if id == _Real { + x.val = c.re + } else { + x.val = c.im + } + } + } else { + x.mode = value + } + k := Invalid + switch typ0.(*Basic).Kind { + case Complex64: + k = Float32 + case Complex128: + k = Float64 + case UntypedComplex: + k = UntypedFloat + default: + unreachable() + } + x.typ = Typ[k] + + case _Make: + var min int // minimum number of arguments + switch typ0.(type) { + case *Slice: + min = 2 + case *Map, *Chan: + min = 1 + default: + check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0) + goto Error + } + if n := len(args); n < min || min+1 < n { + check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, n) + goto Error + } + for _, arg := range args[1:] { + check.expr(x, arg, nil, iota) + if !x.isInteger() { + check.invalidArg(x.pos(), "%s must be an integer", x) + // safe to continue + } + } + x.mode = variable + x.typ = typ0 + + case _New: + x.mode = variable + x.typ = &Pointer{Base: typ0} + + case _Panic, _Print, _Println: + x.mode = novalue + + case _Recover: + x.mode = value + x.typ = emptyInterface + + case _Alignof: + x.mode = constant + x.typ = Typ[Uintptr] + // For now we return 1 always as it satisfies the spec's alignment guarantees. + // TODO(gri) Extend typechecker API so that platform-specific values can be + // provided. + x.val = int64(1) + + case _Offsetof: + if _, ok := unparen(x.expr).(*ast.SelectorExpr); !ok { + check.invalidArg(x.pos(), "%s is not a selector", x) + goto Error + } + x.mode = constant + x.typ = Typ[Uintptr] + // because of the size guarantees for basic types (> 0 for some), + // returning 0 is only correct if two distinct non-zero size + // structs can have the same address (the spec permits that) + x.val = int64(0) + + case _Sizeof: + // basic types with specified sizes have size guarantees; for all others we use 0 + var size int64 + if typ, ok := typ0.(*Basic); ok { + size = typ.Size + } + x.mode = constant + x.typ = Typ[Uintptr] + x.val = size + + case _Assert: + // assert(pred) causes a typechecker error if pred is false. + // The result of assert is the value of pred if there is no error. + // Note: assert is only available in self-test mode. + if x.mode != constant || !isBoolean(typ0) { + check.invalidArg(x.pos(), "%s is not a boolean constant", x) + goto Error + } + pred, ok := x.val.(bool) + if !ok { + check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x) + goto Error + } + if !pred { + check.errorf(call.Pos(), "%s failed", call) + // compile-time assertion failure - safe to continue + } + + case _Trace: + // trace(x, y, z, ...) dumps the positions, expressions, and + // values of its arguments. The result of trace is the value + // of the first argument. + // Note: trace is only available in self-test mode. + if len(args) == 0 { + check.dump("%s: trace() without arguments", call.Pos()) + x.mode = novalue + x.expr = call + return + } + var t operand + x1 := x + for _, arg := range args { + check.exprOrType(x1, arg, nil, iota, true) // permit trace for types, e.g.: new(trace(T)) + check.dump("%s: %s", x1.pos(), x1) + x1 = &t // use incoming x only for first argument + } + + default: + check.invalidAST(call.Pos(), "unknown builtin id %d", id) + goto Error + } + + x.expr = call + return + +Error: + x.mode = invalid + x.expr = call +} + +// implicitDeref returns A if typ is of the form *A and A is an array; +// otherwise it returns typ. +// +func implicitDeref(typ Type) Type { + if p, ok := typ.(*Pointer); ok { + if a, ok := underlying(p.Base).(*Array); ok { + return a + } + } + return typ +} + +// containsCallsOrReceives returns true if the expression x contains +// function calls or channel receives; it returns false otherwise. +// +func containsCallsOrReceives(x ast.Expr) bool { + res := false + ast.Inspect(x, func(x ast.Node) bool { + switch x := x.(type) { + case *ast.CallExpr: + res = true + return false + case *ast.UnaryExpr: + if x.Op == token.ARROW { + res = true + return false + } + } + return true + }) + return res +} + +// unparen removes any parentheses surrounding an expression and returns +// the naked expression. +// +func unparen(x ast.Expr) ast.Expr { + if p, ok := x.(*ast.ParenExpr); ok { + return unparen(p.X) + } + return x +} diff --git a/src/pkg/exp/types/staging/testdata/builtins.src b/src/pkg/exp/types/staging/testdata/builtins.src new file mode 100644 index 0000000000..c641537e93 --- /dev/null +++ b/src/pkg/exp/types/staging/testdata/builtins.src @@ -0,0 +1,258 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// builtin calls + +package builtins + +import "unsafe" + +func _append() { + var x int + var s []byte + _0 := append /* ERROR "argument" */ () + _1 := append("foo" /* ERROR "not a typed slice" */) + _2 := append(nil /* ERROR "not a typed slice" */, s) + _3 := append(x /* ERROR "not a typed slice" */, s) + _4 := append(s) + append /* ERROR "not used" */ (s) +} + +func _cap() { + var a [10]bool + var p *[20]int + var s []int + var c chan string + _0 := cap /* ERROR "argument" */ () + _1 := cap /* ERROR "argument" */ (1, 2) + _2 := cap(42 /* ERROR "invalid" */) + const _3 = cap(a) + assert(_3 == 10) + const _4 = cap(p) + assert(_4 == 20) + _5 := cap(c) + cap /* ERROR "not used" */ (c) +} + +func _close() { + var c chan int + var r <-chan int + close /* ERROR "argument" */ () + close /* ERROR "argument" */ (1, 2) + close(42 /* ERROR "not a channel" */) + close(r /* ERROR "receive-only channel" */) + close(c) +} + +func _complex() { + _0 := complex /* ERROR "argument" */ () + _1 := complex /* ERROR "argument" */ (1) + _2 := complex(1, 2) + // TODO(gri) add tests checking types + complex /* ERROR "not used" */ (1, 2) +} + +func _delete() { + var m map[string]int + var s string + delete /* ERROR "argument" */ () + delete /* ERROR "argument" */ (1) + delete /* ERROR "argument" */ (1, 2, 3) + delete(m, 0 /* ERROR "not assignable" */) + delete(m, s) +} + +func _imag() { + var f32 float32 + var f64 float64 + var c64 complex64 + var c128 complex128 + _0 := imag /* ERROR "argument" */ () + _1 := imag /* ERROR "argument" */ (1, 2) + _2 := imag(10 /* ERROR "must be a complex number" */) + _3 := imag(2.7182818 /* ERROR "must be a complex number" */) + _4 := imag("foo" /* ERROR "must be a complex number" */) + const _5 = imag(1 + 2i) + assert(_5 == 2) + f32 = _5 + f64 = _5 + const _6 = imag(0i) + assert(_6 == 0) + f32 = imag(c64) + f64 = imag(c128) + f32 = imag /* ERROR "cannot assign" */ (c128) + f64 = imag /* ERROR "cannot assign" */ (c64) + imag /* ERROR "not used" */ (c64) +} + +func _len() { + const c = "foobar" + var a [10]bool + var p *[20]int + var s []int + var m map[string]complex128 + _0 := len /* ERROR "argument" */ () + _1 := len /* ERROR "argument" */ (1, 2) + _2 := len(42 /* ERROR "invalid" */) + const _3 = len(c) + assert(_3 == 6) + const _4 = len(a) + assert(_4 == 10) + const _5 = len(p) + assert(_5 == 20) + _6 := len(m) + len /* ERROR "not used" */ (c) + + // esoteric case + var t string + var hash map[interface{}][]*[10]int + const n = len /* ERROR "not constant" */ (hash[recover()][len(t)]) + assert /* ERROR "failed" */ (n == 10) + var ch <-chan int + const nn = len /* ERROR "not constant" */ (hash[<-ch][len(t)]) + _7 := nn // TODO(gri) remove this once unused constants get type-checked +} + +func _make() { + n := 0 + + _0 := make /* ERROR "argument" */ () + _1 := make(1 /* ERROR "not a type" */) + _2 := make(int /* ERROR "cannot make" */) + + // slices + _3 := make/* ERROR "arguments" */ ([]int) + _4 := make/* ERROR "arguments" */ ([]int, 2, 3, 4) + _5 := make([]int, int /* ERROR "not an expression" */) + _6 := make([]int, 10, float32 /* ERROR "not an expression" */) + _7 := make([]int, "foo" /* ERROR "must be an integer" */) + _8 := make([]int, 10, 2.3 /* ERROR "must be an integer" */) + _9 := make([]int, 5, 10.0) + _10 := make([]int, 0i) + _11 := make([]int, -1, 1<<100) // out-of-range constants lead to run-time errors + + // maps + _12 := make /* ERROR "arguments" */ (map[int]string, 10, 20) + _13 := make(map[int]float32, int /* ERROR "not an expression" */) + _14 := make(map[int]float32, "foo" /* ERROR "must be an integer" */) + _15 := make(map[int]float32, 10) + _16 := make(map[int]float32, n) + _17 := make(map[int]float32, int64(n)) + + // channels + _22 := make /* ERROR "arguments" */ (chan int, 10, 20) + _23 := make(chan int, int /* ERROR "not an expression" */) + _24 := make(chan<- int, "foo" /* ERROR "must be an integer" */) + _25 := make(<-chan float64, 10) + _26 := make(chan chan int, n) + _27 := make(chan string, int64(n)) + + make /* ERROR "not used" */ ([]int, 10) +} + +func _new() { + _0 := new /* ERROR "argument" */ () + _1 := new /* ERROR "argument" */ (1, 2) + _3 := new("foo" /* ERROR "not a type" */) + _4 := new(float64) + _5 := new(struct{ x, y int }) + _6 := new(*float64) + _7 := *_4 == **_6 + new /* ERROR "not used" */ (int) +} + +func _real() { + var f32 float32 + var f64 float64 + var c64 complex64 + var c128 complex128 + _0 := real /* ERROR "argument" */ () + _1 := real /* ERROR "argument" */ (1, 2) + _2 := real(10 /* ERROR "must be a complex number" */) + _3 := real(2.7182818 /* ERROR "must be a complex number" */) + _4 := real("foo" /* ERROR "must be a complex number" */) + const _5 = real(1 + 2i) + assert(_5 == 1) + f32 = _5 + f64 = _5 + const _6 = real(0i) + assert(_6 == 0) + f32 = real(c64) + f64 = real(c128) + f32 = real /* ERROR "cannot assign" */ (c128) + f64 = real /* ERROR "cannot assign" */ (c64) + real /* ERROR "not used" */ (c64) +} + +func _recover() { + _0 := recover() + _1 := recover /* ERROR "argument" */ (10) + recover() +} + +func _Alignof() { + var x int + _0 := unsafe /* ERROR "argument" */ .Alignof() + _1 := unsafe /* ERROR "argument" */ .Alignof(1, 2) + _3 := unsafe.Alignof(int /* ERROR "not an expression" */) + _4 := unsafe.Alignof(42) + _5 := unsafe.Alignof(new(struct{})) + unsafe /* ERROR "not used" */ .Alignof(x) +} + +func _Offsetof() { + var x struct{ f int } + _0 := unsafe /* ERROR "argument" */ .Offsetof() + _1 := unsafe /* ERROR "argument" */ .Offsetof(1, 2) + _2 := unsafe.Offsetof(int /* ERROR "not an expression" */) + _3 := unsafe.Offsetof(x /* ERROR "not a selector" */) + _4 := unsafe.Offsetof(x.f) + _5 := unsafe.Offsetof((x.f)) + _6 := unsafe.Offsetof((((((((x))).f))))) + unsafe /* ERROR "not used" */ .Offsetof(x.f) +} + +func _Sizeof() { + var x int + _0 := unsafe /* ERROR "argument" */ .Sizeof() + _1 := unsafe /* ERROR "argument" */ .Sizeof(1, 2) + _2 := unsafe.Sizeof(int /* ERROR "not an expression" */) + _3 := unsafe.Sizeof(42) + _4 := unsafe.Sizeof(new(complex128)) + unsafe /* ERROR "not used" */ .Sizeof(x) + + // basic types have size guarantees + assert(unsafe.Sizeof(byte(0)) == 1) + assert(unsafe.Sizeof(uint8(0)) == 1) + assert(unsafe.Sizeof(int8(0)) == 1) + assert(unsafe.Sizeof(uint16(0)) == 2) + assert(unsafe.Sizeof(int16(0)) == 2) + assert(unsafe.Sizeof(uint32(0)) == 4) + assert(unsafe.Sizeof(int32(0)) == 4) + assert(unsafe.Sizeof(float32(0)) == 4) + assert(unsafe.Sizeof(uint64(0)) == 8) + assert(unsafe.Sizeof(int64(0)) == 8) + assert(unsafe.Sizeof(float64(0)) == 8) + assert(unsafe.Sizeof(complex64(0)) == 8) + assert(unsafe.Sizeof(complex128(0)) == 16) +} + +// self-testing only +func _assert() { + var x int + assert /* ERROR "argument" */ () + assert /* ERROR "argument" */ (1, 2) + assert("foo" /* ERROR "boolean constant" */ ) + assert(x /* ERROR "boolean constant" */) + assert(true) + assert /* ERROR "failed" */ (false) +} + +// self-testing only +func _trace() { + // Uncomment the code below to test trace - will produce console output + // _0 := trace /* ERROR "no value" */ () + // _1 := trace(1) + // _2 := trace(true, 1.2, '\'', "foo", 42i, "foo" <= "bar") +}