// directories
// Note: packages that don't typecheck yet are commented out
- // "archive/tar", // investigate
+ "archive/tar",
"archive/zip",
"bufio",
"crypto/md5",
"crypto/rand",
"crypto/rc4",
- // "crypto/rsa", // investigate (GOARCH=386)
+ // "crypto/rsa", // src/pkg/crypto/rsa/pkcs1v15.go:21:27: undeclared name: io
"crypto/sha1",
"crypto/sha256",
"crypto/sha512",
"crypto/subtle",
"crypto/tls",
- // "crypto/x509", // investigate
+ // "crypto/x509", // src/pkg/crypto/x509/root.go:15:10: undeclared name: initSystemRoots
"crypto/x509/pkix",
"database/sql",
"go/ast",
"go/build",
- // "go/doc", // variadic parameters don't work yet fully
+ "go/doc",
"go/format",
"go/parser",
"go/printer",
"go/token",
"hash/adler32",
- // "hash/crc32", // investigate
+ "hash/crc32",
"hash/crc64",
"hash/fnv",
"index/suffixarray",
"io",
- // "io/ioutil", // investigate
+ "io/ioutil",
"log",
"log/syslog",
"math",
- // "math/big", // investigate
+ "math/big",
"math/cmplx",
"math/rand",
"mime",
"mime/multipart",
- // "net", // depends on C files
+ // "net", // src/pkg/net/lookup_unix.go:56:20: undeclared name: cgoLookupHost
"net/http",
"net/http/cgi",
- // "net/http/fcgi", // investigate
+ "net/http/fcgi",
"net/http/httptest",
"net/http/httputil",
- // "net/http/pprof", // investigate
+ "net/http/pprof",
"net/mail",
- // "net/rpc", // investigate
+ "net/rpc",
"net/rpc/jsonrpc",
"net/smtp",
"net/textproto",
"net/url",
- // "path", // variadic parameters don't work yet fully
- // "path/filepath", // investigate
+ "path",
+ "path/filepath",
- // "reflect", // investigate
+ // "reflect", // unsafe.Sizeof must return size > 0 for pointer types
"regexp",
"regexp/syntax",
"runtime",
- // "runtime/cgo", // import "C"
+ "runtime/cgo",
"runtime/debug",
"runtime/pprof",
"sort",
- // "strconv", // investigate
+ // "strconv", // bug in switch case duplicate detection
"strings",
- // "sync", // platform-specific files
- // "sync/atomic", // platform-specific files
+ "sync",
+ "sync/atomic",
- // "syscall", // platform-specific files
+ "syscall",
"testing",
"testing/iotest",
"text/scanner",
"text/tabwriter",
- // "text/template", // variadic parameters don't work yet fully
- // "text/template/parse", // variadic parameters don't work yet fully
+ "text/template",
+ "text/template/parse",
- // "time", // platform-specific files
+ // "time", // local const decls without initialization expressions
"unicode",
"unicode/utf16",
"unicode/utf8",
goto Error
}
+ typ := underlying(x.typ).(*Basic)
if x.mode == constant && y.mode == constant {
- x.val = binaryOpConst(x.val, toImagConst(y.val), token.ADD, false)
+ x.val = binaryOpConst(x.val, toImagConst(y.val), token.ADD, typ)
} else {
x.mode = value
}
- switch underlying(x.typ).(*Basic).Kind {
+ switch typ.Kind {
case Float32:
x.typ = Typ[Complex64]
case Float64:
return "nil"
}
-// Frequently used constants.
+// Implementation-specific constants.
+// TODO(gri) These need to go elsewhere.
+const (
+ intBits = 32
+ ptrBits = 64
+)
+
+// Frequently used values.
var (
- zeroConst = int64(0)
- oneConst = int64(1)
- minusOneConst = int64(-1)
- nilConst = nilType{}
+ nilConst = nilType{}
+ zeroConst = int64(0)
)
// int64 bounds
}
// normalizeRatConst returns the smallest constant representation
-// for the specific value of x; either an int64, *big.Int value,
+// for the specific value of x; either an int64, *big.Int,
// or *big.Rat value.
//
func normalizeRatConst(x *big.Rat) interface{} {
return x
}
-// normalizeComplexConst returns the smallest constant representation
-// for the specific value of x; either an int64, *big.Int value, *big.Rat,
-// or complex value.
+// newComplex returns the smallest constant representation
+// for the specific value re + im*i; either an int64, *big.Int,
+// *big.Rat, or complex value.
//
-func normalizeComplexConst(x complex) interface{} {
- if x.im.Sign() == 0 {
- return normalizeRatConst(x.re)
+func newComplex(re, im *big.Rat) interface{} {
+ if im.Sign() == 0 {
+ return normalizeRatConst(re)
}
- return x
+ return complex{re, im}
}
// makeRuneConst returns the int64 code point for the rune literal
n := len(lit)
if n > 0 && lit[n-1] == 'i' {
if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok {
- return normalizeComplexConst(complex{big.NewRat(0, 1), im})
+ return newComplex(big.NewRat(0, 1), im)
}
}
return nil
// of precision.
//
func isRepresentableConst(x interface{}, as BasicKind) bool {
- const intBits = 32 // TODO(gri) implementation-specific constant
- const ptrBits = 64 // TODO(gri) implementation-specific constant
-
switch x := x.(type) {
case bool:
return as == Bool || as == UntypedBool
return -1<<62 <= x && x <= 1<<62-1
}
+// unaryOpConst returns the result of the constant evaluation op x where x is of the given type.
+func unaryOpConst(x interface{}, op token.Token, typ *Basic) interface{} {
+ switch op {
+ case token.ADD:
+ return x // nothing to do
+ case token.SUB:
+ switch x := x.(type) {
+ case int64:
+ if z := -x; z != x {
+ return z // no overflow
+ }
+ // overflow - need to convert to big.Int
+ return normalizeIntConst(new(big.Int).Neg(big.NewInt(x)))
+ case *big.Int:
+ return normalizeIntConst(new(big.Int).Neg(x))
+ case *big.Rat:
+ return normalizeRatConst(new(big.Rat).Neg(x))
+ case complex:
+ return newComplex(new(big.Rat).Neg(x.re), new(big.Rat).Neg(x.im))
+ }
+ case token.XOR:
+ var z big.Int
+ switch x := x.(type) {
+ case int64:
+ z.Not(big.NewInt(x))
+ case *big.Int:
+ z.Not(x)
+ default:
+ unreachable()
+ }
+ // For unsigned types, the result will be negative and
+ // thus "too large": We must limit the result size to
+ // the type's size.
+ if typ.Info&IsUnsigned != 0 {
+ s := uint(typ.Size) * 8
+ if s == 0 {
+ // platform-specific type
+ // TODO(gri) this needs to be factored out
+ switch typ.Kind {
+ case Uint:
+ s = intBits
+ case Uintptr:
+ s = ptrBits
+ default:
+ unreachable()
+ }
+ }
+ // z &^= (-1)<<s
+ z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s))
+ }
+ return normalizeIntConst(&z)
+ case token.NOT:
+ return !x.(bool)
+ }
+ unreachable()
+ return nil
+}
+
// binaryOpConst returns the result of the constant evaluation x op y;
-// both operands must be of the same "kind" (boolean, numeric, or string).
-// If intDiv is true, division (op == token.QUO) is using integer division
+// both operands must be of the same constant "kind" (boolean, numeric, or string).
+// If typ is an integer type, division (op == token.QUO) is using integer division
// (and the result is guaranteed to be integer) rather than floating-point
// division. Division by zero leads to a run-time panic.
//
-func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
+func binaryOpConst(x, y interface{}, op token.Token, typ *Basic) interface{} {
x, y = matchConst(x, y)
switch x := x.(type) {
return x && y
case token.LOR:
return x || y
- default:
- unreachable()
}
case int64:
case token.REM:
return x % y
case token.QUO:
- if intDiv {
+ if typ.Info&IsInteger != 0 {
return x / y
}
return normalizeRatConst(new(big.Rat).SetFrac(big.NewInt(x), big.NewInt(y)))
return x ^ y
case token.AND_NOT:
return x &^ y
- default:
- unreachable()
}
case *big.Int:
case token.REM:
z.Rem(x, y)
case token.QUO:
- if intDiv {
+ if typ.Info&IsInteger != 0 {
z.Quo(x, y)
} else {
return normalizeRatConst(new(big.Rat).SetFrac(x, y))
default:
unreachable()
}
- return normalizeComplexConst(complex{&re, &im})
+ return newComplex(&re, &im)
case string:
if op == token.ADD {
buf.WriteByte('*')
writeType(buf, t.Base)
- case *tuple:
- buf.WriteByte('(')
- for i, typ := range t.list {
- if i > 0 {
- buf.WriteString("; ")
- }
- writeType(buf, typ)
- }
- buf.WriteByte(')')
+ case *Result:
+ writeParams(buf, t.Values, false)
case *Signature:
buf.WriteString("func")
// - at the moment, iota is passed around almost everywhere - in many places we know it cannot be used
// TODO(gri) API issues
-// - clients need access to result type information (tuples)
// - clients need access to constant values
// - clients need access to built-in type information
}
if x.mode == constant {
- switch op {
- case token.ADD:
- // nothing to do
- case token.SUB:
- x.val = binaryOpConst(zeroConst, x.val, token.SUB, false)
- case token.XOR:
- x.val = binaryOpConst(minusOneConst, x.val, token.XOR, false)
- case token.NOT:
- x.val = !x.val.(bool)
- default:
- unreachable() // operators where checked by check.op
- }
+ typ := underlying(x.typ).(*Basic)
+ x.val = unaryOpConst(x.val, op, typ)
// Typed constants must be representable in
// their type after each constant operation.
- check.isRepresentable(x, underlying(x.typ).(*Basic))
+ check.isRepresentable(x, typ)
return
}
if !x.isNil() {
goto Error
}
+ default:
+ unreachable()
}
x.typ = target
}
if !valid {
- check.invalidOp(x.pos(), "cannot compare %s and %s", x, y)
+ check.invalidOp(x.pos(), "cannot compare %s %s %s", x, op, y)
x.mode = invalid
return
}
}
if x.mode == constant && y.mode == constant {
- x.val = binaryOpConst(x.val, y.val, op, isInteger(x.typ))
+ typ := underlying(x.typ).(*Basic)
+ x.val = binaryOpConst(x.val, y.val, op, typ)
// Typed constants must be representable in
// their type after each constant operation.
- check.isRepresentable(x, underlying(x.typ).(*Basic))
+ check.isRepresentable(x, typ)
return
}
return max
}
-func (check *checker) argument(sig *Signature, i int, arg ast.Expr) {
+// argument typechecks passing an argument arg (if arg != nil) or
+// x (if arg == nil) to the i'th parameter of the given signature.
+// If passSlice is set, the argument is followed by ... in the call.
+//
+func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, passSlice bool) {
+ // determine parameter
var par *ast.Object
- if n := len(sig.Params); i < n {
+ n := len(sig.Params)
+ if i < n {
par = sig.Params[i]
} else if sig.IsVariadic {
par = sig.Params[n-1]
return
}
- // TODO(gri) deal with ... last argument
- var z, x operand
+ // determine argument
+ var z operand
z.mode = variable
- z.expr = nil // TODO(gri) can we do better here?
- z.typ = par.Type.(Type) // TODO(gri) should become something like checkObj(&z, ...) eventually
- check.expr(&x, arg, z.typ, -1)
+ z.expr = nil // TODO(gri) can we do better here? (for good error messages)
+ z.typ = par.Type.(Type)
+
+ if arg != nil {
+ check.expr(x, arg, z.typ, -1)
+ }
if x.mode == invalid {
return // ignore this argument
}
- check.assignOperand(&z, &x)
+
+ // check last argument of the form x...
+ if passSlice {
+ if i+1 != n {
+ check.errorf(x.pos(), "can only use ... with matching parameter")
+ return // ignore this argument
+ }
+ // spec: "If the final argument is assignable to a slice type []T,
+ // it may be passed unchanged as the value for a ...T parameter if
+ // the argument is followed by ..."
+ z.typ = &Slice{Elt: z.typ} // change final parameter type to []T
+ }
+
+ check.assignOperand(&z, x)
}
func (check *checker) recordType(x *operand) {
check.conversion(x, e, x.typ, iota)
} else if sig, ok := underlying(x.typ).(*Signature); ok {
// check parameters
- // TODO(gri)
- // - deal with single multi-valued function arguments: f(g())
- // - variadic functions only partially addressed
- for i, arg := range e.Args {
- check.argument(sig, i, arg)
+
+ // If we have a trailing ... at the end of the parameter
+ // list, the last argument must match the parameter type
+ // []T of a variadic function parameter x ...T.
+ passSlice := false
+ if e.Ellipsis.IsValid() {
+ if sig.IsVariadic {
+ passSlice = true
+ } else {
+ check.errorf(e.Ellipsis, "cannot use ... in call to %s", e.Fun)
+ // ok to continue
+ }
}
- // determine result
- x.mode = value
- if len(sig.Results) == 1 {
- x.typ = sig.Results[0].Type.(Type)
+ // If we have a single argument that is a function call
+ // we need to handle it separately. Determine if this
+ // is the case without checking the argument.
+ var call *ast.CallExpr
+ if len(e.Args) == 1 {
+ call, _ = unparen(e.Args[0]).(*ast.CallExpr)
+ }
+
+ n := 0 // parameter count
+ if call != nil {
+ // We have a single argument that is a function call.
+ check.expr(x, call, nil, -1)
+ if x.mode == invalid {
+ goto Error // TODO(gri): we can do better
+ }
+ if t, _ := x.typ.(*Result); t != nil {
+ // multiple result values
+ n = len(t.Values)
+ for i, obj := range t.Values {
+ x.mode = value
+ x.expr = nil // TODO(gri) can we do better here? (for good error messages)
+ x.typ = obj.Type.(Type)
+ check.argument(sig, i, nil, x, passSlice && i+1 == n)
+ }
+ } else {
+ // single result value
+ n = 1
+ check.argument(sig, 0, nil, x, passSlice)
+ }
+
} else {
- // TODO(gri) change Signature representation to use tuples,
- // then this conversion is not required
- list := make([]Type, len(sig.Results))
- for i, obj := range sig.Results {
- list[i] = obj.Type.(Type)
+ // We don't have a single argument or it is not a function call.
+ n = len(e.Args)
+ for i, arg := range e.Args {
+ check.argument(sig, i, arg, x, passSlice && i+1 == n)
}
- x.typ = &tuple{list: list}
+ }
+
+ // determine if we have enough arguments
+ if sig.IsVariadic {
+ // a variadic function accepts an "empty"
+ // last argument: count one extra
+ n++
+ }
+ if n < len(sig.Params) {
+ check.errorf(e.Fun.Pos(), "too few arguments in call to %s", e.Fun)
+ // ok to continue
+ }
+
+ // determine result
+ switch len(sig.Results) {
+ case 0:
+ x.mode = novalue
+ case 1:
+ x.mode = value
+ x.typ = sig.Results[0].Type.(Type)
+ default:
+ x.mode = value
+ x.typ = &Result{Values: sig.Results}
}
} else if bin, ok := x.typ.(*builtin); ok {
if isUntyped(Vu) {
switch t := Tu.(type) {
case *Basic:
- return x.mode == constant && isRepresentableConst(x.val, t.Kind)
+ if x.mode == constant {
+ return isRepresentableConst(x.val, t.Kind)
+ }
+ // The result of a comparison is an untyped boolean,
+ // but may not be a constant.
+ if Vb, _ := Vu.(*Basic); Vb != nil {
+ return Vb.Kind == UntypedBool && isBoolean(Tu)
+ }
case *Interface:
return x.isNil() || len(t.Methods) == 0
case *Pointer, *Signature, *Slice, *Map, *Chan:
}
// defaultType returns the default "typed" type for an "untyped" type;
-// it returns the argument typ for all other types.
+// it returns the incoming type for all other types. If there is no
+// corresponding untyped type, the result is Typ[Invalid].
+//
func defaultType(typ Type) Type {
if t, ok := typ.(*Basic); ok {
- var k BasicKind
+ k := Invalid
switch t.Kind {
+ // case UntypedNil:
+ // There is no default type for nil. For a good error message,
+ // catch this case before calling this function.
case UntypedBool:
k = Bool
- case UntypedRune:
- k = Rune
case UntypedInt:
k = Int
+ case UntypedRune:
+ k = Rune
case UntypedFloat:
k = Float64
case UntypedComplex:
k = Complex128
case UntypedString:
k = String
- default:
- unreachable()
}
typ = Typ[k]
}
)
func (check *checker) assignOperand(z, x *operand) {
- if t, ok := x.typ.(*tuple); ok {
+ if t, ok := x.typ.(*Result); ok {
// TODO(gri) elsewhere we use "assignment count mismatch" (consolidate)
- check.errorf(x.pos(), "%d-valued expression %s used as single value", len(t.list), x)
+ check.errorf(x.pos(), "%d-valued expression %s used as single value", len(t.Values), x)
x.mode = invalid
return
}
if x.mode != invalid {
typ = x.typ
if obj.Kind == ast.Var && isUntyped(typ) {
- typ = defaultType(typ)
+ if x.isNil() {
+ check.errorf(x.pos(), "use of untyped nil")
+ x.mode = invalid
+ } else {
+ typ = defaultType(typ)
+ }
}
}
obj.Type = typ
return
}
- if t, ok := x.typ.(*tuple); ok && len(lhs) == len(t.list) {
+ if t, ok := x.typ.(*Result); ok && len(lhs) == len(t.Values) {
// function result
x.mode = value
- for i, typ := range t.list {
+ for i, obj := range t.Values {
x.expr = nil // TODO(gri) should do better here
- x.typ = typ
+ x.typ = obj.Type.(Type)
check.assign1to1(lhs[i], nil, &x, decl, iota)
}
return
var x operand
tag := s.Tag
if tag == nil {
- // create true tag value and position it at the opening { of the switch
+ // use fake true tag value and position it at the opening { of the switch
tag = &ast.Ident{NamePos: s.Body.Lbrace, Name: "true", Obj: Universe.Lookup("true")}
}
check.expr(&x, tag, nil, -1)
}
// If we have a constant case value, it must appear only
// once in the switch statement. Determine if there is a
- // duplicate entry, but only report an error there are no
- // other errors.
+ // duplicate entry, but only report an error if there are
+ // no other errors.
var dupl token.Pos
if y.mode == constant {
// TODO(gri) This code doesn't work correctly for
// large integer, floating point, or
// complex values - the respective struct
- // comparison is shallow. Need to use a
- // has function to index the seen map.
+ // comparisons are shallow. Need to use a
+ // hash function to index the map.
dupl = seen[y.val]
seen[y.val] = y.pos()
}
}
check.comparison(&y, &x, token.EQL)
if y.mode != invalid && dupl.IsValid() {
- check.errorf(y.pos(), "%s is duplicate case in switch\n\tprevious case at %s",
+ check.errorf(y.pos(), "%s is duplicate case (previous at %s)",
&y, check.fset.Position(dupl))
}
}
s14 = i << j /* ERROR "must be unsigned" */
s18 = math.Pi * 10.0
s19 = s1 /* ERROR "cannot call" */ ()
- s20 = f0 /* ERROR "used as single value" */ ()
+ s20 = f0 /* ERROR "no value" */ ()
s21 = f6(1, s1, i)
s22 = f6(1, s1, uu /* ERROR "cannot assign" */ )
t17 math /* ERROR "not a type" */ .Pi
t18 float64 = math.Pi * 10.0
t19 int = t1 /* ERROR "cannot call" */ ()
- t20 int = f0 /* ERROR "used as single value" */ ()
+ t20 int = f0 /* ERROR "no value" */ ()
)
// Various more complex expressions
v10 byte = 1024 /* ERROR "overflows" */
v11 = xx/yy*yy - xx
v12 = true && false
+ v13 = nil /* ERROR "use of untyped nil" */
)
// Multiple assignment expressions
u16 = &u0
u17 = *u16
u18 = <-u16 /* ERROR "cannot receive" */
+ u19 = ^uint(0)
// float64
f0 = float64(1)
ch7 = <-ch
ch8 = <-rc
ch9 = <-sc /* ERROR "cannot receive" */
-
-)
\ No newline at end of file
+)
package expr2
+func _bool() {
+ const t = true == true
+ const f = true == false
+ _ = t /* ERROR "cannot compare" */ < f
+ _ = 0 /* ERROR "cannot convert" */ == t
+ var b bool
+ var x, y float32
+ b = x < y
+ _ = struct{b bool}{x < y}
+}
+
// corner cases
var (
v0 = nil /* ERROR "cannot compare" */ == nil
_ = t.(T2 /* ERROR "wrong type for method m" */ )
_ = t.(I2 /* ERROR "wrong type for method m" */ )
}
+
+func f0() {}
+func f1(x int) {}
+func f2(u float32, s string) {}
+func fs(s []byte) {}
+func fv(x ...int) {}
+func fi(x ... interface{}) {}
+
+func g0() {}
+func g1() int { return 0}
+func g2() (u float32, s string) { return }
+func gs() []byte { return nil }
+
+func _calls() {
+ var x int
+ var y float32
+ var s []int
+
+ f0()
+ _ = f0 /* ERROR "used as value" */ ()
+ f0(g0 /* ERROR "too many arguments" */ )
+
+ f1(0)
+ f1(x)
+ f1(10.0)
+ f1 /* ERROR "too few arguments" */ ()
+ f1(x, y /* ERROR "too many arguments" */ )
+ f1(s /* ERROR "cannot assign" */ )
+ f1(x ... /* ERROR "cannot use ..." */ )
+ f1(g0 /* ERROR "used as value" */ ())
+ f1(g1())
+ // f1(g2()) // TODO(gri) missing position in error message
+
+ f2 /* ERROR "too few arguments" */ ()
+ f2 /* ERROR "too few arguments" */ (3.14)
+ f2(3.14, "foo")
+ f2(x /* ERROR "cannot assign" */ , "foo")
+ f2(g0 /* ERROR "used as value" */ ())
+ f2 /* ERROR "too few arguments" */ (g1 /* ERROR "cannot assign" */ ())
+ f2(g2())
+
+ fs /* ERROR "too few arguments" */ ()
+ fs(g0 /* ERROR "used as value" */ ())
+ fs(g1 /* ERROR "cannot assign" */ ())
+ // fs(g2()) // TODO(gri) missing position in error message
+ fs(gs())
+
+ fv()
+ fv(1, 2.0, x)
+ fv(s /* ERROR "cannot assign" */ )
+ fv(s...)
+ fv(1, s /* ERROR "can only use ... with matching parameter" */ ...)
+ fv(gs /* ERROR "cannot assign" */ ())
+ fv(gs /* ERROR "cannot assign" */ ()...)
+
+ fi()
+ fi(1, 2.0, x, 3.14, "foo")
+ fi(g2())
+ fi(0, g2)
+ fi(0, g2 /* ERROR "2-valued expression" */ ())
+}
\ No newline at end of file
Base Type
}
-// A tuple represents a multi-value function return.
-// TODO(gri) use better name to avoid confusion (Go doesn't have tuples).
-type tuple struct {
+// A Result represents a (multi-value) function call result.
+// TODO(gri) consider using an empty Result (Values == nil)
+// as representation for the novalue operand mode.
+type Result struct {
implementsType
- list []Type
+ Values ObjList // Signature.Results of the function called
}
// A Signature represents a user-defined function type func(...) (...).
-// TODO(gri) consider using "tuples" to represent parameters and results (see comment on tuples).
type Signature struct {
implementsType
Recv *ast.Object // nil if not a method
dup("-f(10, 20)"),
dup("f(x + y, +3.1415)"),
{"func(a, b int) {}", "(func literal)"},
- {"func(a, b int) []int {}()[x]", "(func literal)()[x]"},
+ {"func(a, b int) []int {}(1, 2)[x]", "(func literal)(1, 2)[x]"},
{"[]int{1, 2, 3}", "(composite literal)"},
{"[]int{1, 2, 3}[x:]", "(composite literal)[x:]"},
{"i.([]string)", "i.(...)"},