1
0
mirror of https://github.com/golang/go synced 2024-11-20 05:04:43 -07:00

exp/types: completed typechecking of parameter passing

Details:
- fixed variadic parameter passing and calls of the form f(g())
- fixed implementation of ^x for unsigned constants x
- fixed assignability of untyped booleans
- resolved a few TODOs, various minor fixes
- enabled many more tests (only 6 std packages don't typecheck)

R=rsc
CC=golang-dev
https://golang.org/cl/6930053
This commit is contained in:
Robert Griesemer 2012-12-17 11:35:59 -08:00
parent b7603cfc2c
commit 50d8787822
14 changed files with 335 additions and 129 deletions

View File

@ -50,7 +50,7 @@ var tests = []string{
// directories // directories
// Note: packages that don't typecheck yet are commented out // Note: packages that don't typecheck yet are commented out
// "archive/tar", // investigate "archive/tar",
"archive/zip", "archive/zip",
"bufio", "bufio",
@ -77,13 +77,13 @@ var tests = []string{
"crypto/md5", "crypto/md5",
"crypto/rand", "crypto/rand",
"crypto/rc4", "crypto/rc4",
// "crypto/rsa", // investigate (GOARCH=386) // "crypto/rsa", // src/pkg/crypto/rsa/pkcs1v15.go:21:27: undeclared name: io
"crypto/sha1", "crypto/sha1",
"crypto/sha256", "crypto/sha256",
"crypto/sha512", "crypto/sha512",
"crypto/subtle", "crypto/subtle",
"crypto/tls", "crypto/tls",
// "crypto/x509", // investigate // "crypto/x509", // src/pkg/crypto/x509/root.go:15:10: undeclared name: initSystemRoots
"crypto/x509/pkix", "crypto/x509/pkix",
"database/sql", "database/sql",
@ -117,7 +117,7 @@ var tests = []string{
"go/ast", "go/ast",
"go/build", "go/build",
// "go/doc", // variadic parameters don't work yet fully "go/doc",
"go/format", "go/format",
"go/parser", "go/parser",
"go/printer", "go/printer",
@ -125,7 +125,7 @@ var tests = []string{
"go/token", "go/token",
"hash/adler32", "hash/adler32",
// "hash/crc32", // investigate "hash/crc32",
"hash/crc64", "hash/crc64",
"hash/fnv", "hash/fnv",
@ -139,54 +139,54 @@ var tests = []string{
"index/suffixarray", "index/suffixarray",
"io", "io",
// "io/ioutil", // investigate "io/ioutil",
"log", "log",
"log/syslog", "log/syslog",
"math", "math",
// "math/big", // investigate "math/big",
"math/cmplx", "math/cmplx",
"math/rand", "math/rand",
"mime", "mime",
"mime/multipart", "mime/multipart",
// "net", // depends on C files // "net", // src/pkg/net/lookup_unix.go:56:20: undeclared name: cgoLookupHost
"net/http", "net/http",
"net/http/cgi", "net/http/cgi",
// "net/http/fcgi", // investigate "net/http/fcgi",
"net/http/httptest", "net/http/httptest",
"net/http/httputil", "net/http/httputil",
// "net/http/pprof", // investigate "net/http/pprof",
"net/mail", "net/mail",
// "net/rpc", // investigate "net/rpc",
"net/rpc/jsonrpc", "net/rpc/jsonrpc",
"net/smtp", "net/smtp",
"net/textproto", "net/textproto",
"net/url", "net/url",
// "path", // variadic parameters don't work yet fully "path",
// "path/filepath", // investigate "path/filepath",
// "reflect", // investigate // "reflect", // unsafe.Sizeof must return size > 0 for pointer types
"regexp", "regexp",
"regexp/syntax", "regexp/syntax",
"runtime", "runtime",
// "runtime/cgo", // import "C" "runtime/cgo",
"runtime/debug", "runtime/debug",
"runtime/pprof", "runtime/pprof",
"sort", "sort",
// "strconv", // investigate // "strconv", // bug in switch case duplicate detection
"strings", "strings",
// "sync", // platform-specific files "sync",
// "sync/atomic", // platform-specific files "sync/atomic",
// "syscall", // platform-specific files "syscall",
"testing", "testing",
"testing/iotest", "testing/iotest",
@ -194,10 +194,10 @@ var tests = []string{
"text/scanner", "text/scanner",
"text/tabwriter", "text/tabwriter",
// "text/template", // variadic parameters don't work yet fully "text/template",
// "text/template/parse", // variadic parameters don't work yet fully "text/template/parse",
// "time", // platform-specific files // "time", // local const decls without initialization expressions
"unicode", "unicode",
"unicode/utf16", "unicode/utf16",
"unicode/utf8", "unicode/utf8",

View File

@ -155,13 +155,14 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
goto Error goto Error
} }
typ := underlying(x.typ).(*Basic)
if x.mode == constant && y.mode == constant { 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 { } else {
x.mode = value x.mode = value
} }
switch underlying(x.typ).(*Basic).Kind { switch typ.Kind {
case Float32: case Float32:
x.typ = Typ[Complex64] x.typ = Typ[Complex64]
case Float64: case Float64:

View File

@ -49,12 +49,17 @@ func (nilType) String() string {
return "nil" return "nil"
} }
// Frequently used constants. // Implementation-specific constants.
// TODO(gri) These need to go elsewhere.
const (
intBits = 32
ptrBits = 64
)
// Frequently used values.
var ( var (
zeroConst = int64(0) nilConst = nilType{}
oneConst = int64(1) zeroConst = int64(0)
minusOneConst = int64(-1)
nilConst = nilType{}
) )
// int64 bounds // int64 bounds
@ -74,7 +79,7 @@ func normalizeIntConst(x *big.Int) interface{} {
} }
// normalizeRatConst returns the smallest constant representation // 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. // or *big.Rat value.
// //
func normalizeRatConst(x *big.Rat) interface{} { func normalizeRatConst(x *big.Rat) interface{} {
@ -84,15 +89,15 @@ func normalizeRatConst(x *big.Rat) interface{} {
return x return x
} }
// normalizeComplexConst returns the smallest constant representation // newComplex returns the smallest constant representation
// for the specific value of x; either an int64, *big.Int value, *big.Rat, // for the specific value re + im*i; either an int64, *big.Int,
// or complex value. // *big.Rat, or complex value.
// //
func normalizeComplexConst(x complex) interface{} { func newComplex(re, im *big.Rat) interface{} {
if x.im.Sign() == 0 { if im.Sign() == 0 {
return normalizeRatConst(x.re) return normalizeRatConst(re)
} }
return x return complex{re, im}
} }
// makeRuneConst returns the int64 code point for the rune literal // makeRuneConst returns the int64 code point for the rune literal
@ -140,7 +145,7 @@ func makeComplexConst(lit string) interface{} {
n := len(lit) n := len(lit)
if n > 0 && lit[n-1] == 'i' { if n > 0 && lit[n-1] == 'i' {
if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok { 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 return nil
@ -202,9 +207,6 @@ func isNegConst(x interface{}) bool {
// of precision. // of precision.
// //
func isRepresentableConst(x interface{}, as BasicKind) bool { 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) { switch x := x.(type) {
case bool: case bool:
return as == Bool || as == UntypedBool return as == Bool || as == UntypedBool
@ -386,13 +388,71 @@ func is63bit(x int64) bool {
return -1<<62 <= x && x <= 1<<62-1 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; // binaryOpConst returns the result of the constant evaluation x op y;
// both operands must be of the same "kind" (boolean, numeric, or string). // both operands must be of the same constant "kind" (boolean, numeric, or string).
// If intDiv is true, division (op == token.QUO) is using integer division // 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 // (and the result is guaranteed to be integer) rather than floating-point
// division. Division by zero leads to a run-time panic. // 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) x, y = matchConst(x, y)
switch x := x.(type) { switch x := x.(type) {
@ -403,8 +463,6 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
return x && y return x && y
case token.LOR: case token.LOR:
return x || y return x || y
default:
unreachable()
} }
case int64: case int64:
@ -431,7 +489,7 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
case token.REM: case token.REM:
return x % y return x % y
case token.QUO: case token.QUO:
if intDiv { if typ.Info&IsInteger != 0 {
return x / y return x / y
} }
return normalizeRatConst(new(big.Rat).SetFrac(big.NewInt(x), big.NewInt(y))) return normalizeRatConst(new(big.Rat).SetFrac(big.NewInt(x), big.NewInt(y)))
@ -443,8 +501,6 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
return x ^ y return x ^ y
case token.AND_NOT: case token.AND_NOT:
return x &^ y return x &^ y
default:
unreachable()
} }
case *big.Int: case *big.Int:
@ -460,7 +516,7 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
case token.REM: case token.REM:
z.Rem(x, y) z.Rem(x, y)
case token.QUO: case token.QUO:
if intDiv { if typ.Info&IsInteger != 0 {
z.Quo(x, y) z.Quo(x, y)
} else { } else {
return normalizeRatConst(new(big.Rat).SetFrac(x, y)) return normalizeRatConst(new(big.Rat).SetFrac(x, y))
@ -533,7 +589,7 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
default: default:
unreachable() unreachable()
} }
return normalizeComplexConst(complex{&re, &im}) return newComplex(&re, &im)
case string: case string:
if op == token.ADD { if op == token.ADD {

View File

@ -266,15 +266,8 @@ func writeType(buf *bytes.Buffer, typ Type) {
buf.WriteByte('*') buf.WriteByte('*')
writeType(buf, t.Base) writeType(buf, t.Base)
case *tuple: case *Result:
buf.WriteByte('(') writeParams(buf, t.Values, false)
for i, typ := range t.list {
if i > 0 {
buf.WriteString("; ")
}
writeType(buf, typ)
}
buf.WriteByte(')')
case *Signature: case *Signature:
buf.WriteString("func") buf.WriteString("func")

View File

@ -19,7 +19,6 @@ import (
// - at the moment, iota is passed around almost everywhere - in many places we know it cannot be used // - at the moment, iota is passed around almost everywhere - in many places we know it cannot be used
// TODO(gri) API issues // TODO(gri) API issues
// - clients need access to result type information (tuples)
// - clients need access to constant values // - clients need access to constant values
// - clients need access to built-in type information // - clients need access to built-in type information
@ -212,21 +211,11 @@ func (check *checker) unary(x *operand, op token.Token) {
} }
if x.mode == constant { if x.mode == constant {
switch op { typ := underlying(x.typ).(*Basic)
case token.ADD: x.val = unaryOpConst(x.val, op, typ)
// 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
}
// Typed constants must be representable in // Typed constants must be representable in
// their type after each constant operation. // their type after each constant operation.
check.isRepresentable(x, underlying(x.typ).(*Basic)) check.isRepresentable(x, typ)
return return
} }
@ -304,6 +293,8 @@ func (check *checker) convertUntyped(x *operand, target Type) {
if !x.isNil() { if !x.isNil() {
goto Error goto Error
} }
default:
unreachable()
} }
x.typ = target x.typ = target
@ -332,7 +323,7 @@ func (check *checker) comparison(x, y *operand, op token.Token) {
} }
if !valid { 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 x.mode = invalid
return return
} }
@ -465,10 +456,11 @@ func (check *checker) binary(x, y *operand, op token.Token, hint Type) {
} }
if x.mode == constant && y.mode == constant { 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 // Typed constants must be representable in
// their type after each constant operation. // their type after each constant operation.
check.isRepresentable(x, underlying(x.typ).(*Basic)) check.isRepresentable(x, typ)
return return
} }
@ -554,9 +546,15 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota
return max 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 var par *ast.Object
if n := len(sig.Params); i < n { n := len(sig.Params)
if i < n {
par = sig.Params[i] par = sig.Params[i]
} else if sig.IsVariadic { } else if sig.IsVariadic {
par = sig.Params[n-1] par = sig.Params[n-1]
@ -565,16 +563,32 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr) {
return return
} }
// TODO(gri) deal with ... last argument // determine argument
var z, x operand var z operand
z.mode = variable z.mode = variable
z.expr = nil // TODO(gri) can we do better here? z.expr = nil // TODO(gri) can we do better here? (for good error messages)
z.typ = par.Type.(Type) // TODO(gri) should become something like checkObj(&z, ...) eventually z.typ = par.Type.(Type)
check.expr(&x, arg, z.typ, -1)
if arg != nil {
check.expr(x, arg, z.typ, -1)
}
if x.mode == invalid { if x.mode == invalid {
return // ignore this argument 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) { func (check *checker) recordType(x *operand) {
@ -1052,25 +1066,79 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
check.conversion(x, e, x.typ, iota) check.conversion(x, e, x.typ, iota)
} else if sig, ok := underlying(x.typ).(*Signature); ok { } else if sig, ok := underlying(x.typ).(*Signature); ok {
// check parameters // check parameters
// TODO(gri)
// - deal with single multi-valued function arguments: f(g()) // If we have a trailing ... at the end of the parameter
// - variadic functions only partially addressed // list, the last argument must match the parameter type
for i, arg := range e.Args { // []T of a variadic function parameter x ...T.
check.argument(sig, i, arg) 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
}
}
// 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 {
// 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)
}
}
// 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 // determine result
x.mode = value switch len(sig.Results) {
if len(sig.Results) == 1 { case 0:
x.mode = novalue
case 1:
x.mode = value
x.typ = sig.Results[0].Type.(Type) x.typ = sig.Results[0].Type.(Type)
} else { default:
// TODO(gri) change Signature representation to use tuples, x.mode = value
// then this conversion is not required x.typ = &Result{Values: sig.Results}
list := make([]Type, len(sig.Results))
for i, obj := range sig.Results {
list[i] = obj.Type.(Type)
}
x.typ = &tuple{list: list}
} }
} else if bin, ok := x.typ.(*builtin); ok { } else if bin, ok := x.typ.(*builtin); ok {

View File

@ -182,7 +182,14 @@ func (x *operand) isAssignable(T Type) bool {
if isUntyped(Vu) { if isUntyped(Vu) {
switch t := Tu.(type) { switch t := Tu.(type) {
case *Basic: 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: case *Interface:
return x.isNil() || len(t.Methods) == 0 return x.isNil() || len(t.Methods) == 0
case *Pointer, *Signature, *Slice, *Map, *Chan: case *Pointer, *Signature, *Slice, *Map, *Chan:

View File

@ -225,25 +225,28 @@ func deref(typ Type) Type {
} }
// defaultType returns the default "typed" type for an "untyped" type; // 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 { func defaultType(typ Type) Type {
if t, ok := typ.(*Basic); ok { if t, ok := typ.(*Basic); ok {
var k BasicKind k := Invalid
switch t.Kind { 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: case UntypedBool:
k = Bool k = Bool
case UntypedRune:
k = Rune
case UntypedInt: case UntypedInt:
k = Int k = Int
case UntypedRune:
k = Rune
case UntypedFloat: case UntypedFloat:
k = Float64 k = Float64
case UntypedComplex: case UntypedComplex:
k = Complex128 k = Complex128
case UntypedString: case UntypedString:
k = String k = String
default:
unreachable()
} }
typ = Typ[k] typ = Typ[k]
} }

View File

@ -12,9 +12,9 @@ import (
) )
func (check *checker) assignOperand(z, x *operand) { 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) // 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 x.mode = invalid
return return
} }
@ -95,7 +95,12 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
if x.mode != invalid { if x.mode != invalid {
typ = x.typ typ = x.typ
if obj.Kind == ast.Var && isUntyped(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 obj.Type = typ
@ -177,12 +182,12 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
return 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 // function result
x.mode = value 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.expr = nil // TODO(gri) should do better here
x.typ = typ x.typ = obj.Type.(Type)
check.assign1to1(lhs[i], nil, &x, decl, iota) check.assign1to1(lhs[i], nil, &x, decl, iota)
} }
return return
@ -429,7 +434,7 @@ func (check *checker) stmt(s ast.Stmt) {
var x operand var x operand
tag := s.Tag tag := s.Tag
if tag == nil { 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")} tag = &ast.Ident{NamePos: s.Body.Lbrace, Name: "true", Obj: Universe.Lookup("true")}
} }
check.expr(&x, tag, nil, -1) check.expr(&x, tag, nil, -1)
@ -451,15 +456,15 @@ func (check *checker) stmt(s ast.Stmt) {
} }
// If we have a constant case value, it must appear only // If we have a constant case value, it must appear only
// once in the switch statement. Determine if there is a // once in the switch statement. Determine if there is a
// duplicate entry, but only report an error there are no // duplicate entry, but only report an error if there are
// other errors. // no other errors.
var dupl token.Pos var dupl token.Pos
if y.mode == constant { if y.mode == constant {
// TODO(gri) This code doesn't work correctly for // TODO(gri) This code doesn't work correctly for
// large integer, floating point, or // large integer, floating point, or
// complex values - the respective struct // complex values - the respective struct
// comparison is shallow. Need to use a // comparisons are shallow. Need to use a
// has function to index the seen map. // hash function to index the map.
dupl = seen[y.val] dupl = seen[y.val]
seen[y.val] = y.pos() seen[y.val] = y.pos()
} }
@ -475,7 +480,7 @@ func (check *checker) stmt(s ast.Stmt) {
} }
check.comparison(&y, &x, token.EQL) check.comparison(&y, &x, token.EQL)
if y.mode != invalid && dupl.IsValid() { 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)) &y, check.fset.Position(dupl))
} }
} }

View File

@ -46,7 +46,7 @@ var (
s14 = i << j /* ERROR "must be unsigned" */ s14 = i << j /* ERROR "must be unsigned" */
s18 = math.Pi * 10.0 s18 = math.Pi * 10.0
s19 = s1 /* ERROR "cannot call" */ () s19 = s1 /* ERROR "cannot call" */ ()
s20 = f0 /* ERROR "used as single value" */ () s20 = f0 /* ERROR "no value" */ ()
s21 = f6(1, s1, i) s21 = f6(1, s1, i)
s22 = f6(1, s1, uu /* ERROR "cannot assign" */ ) s22 = f6(1, s1, uu /* ERROR "cannot assign" */ )
@ -68,7 +68,7 @@ var (
t17 math /* ERROR "not a type" */ .Pi t17 math /* ERROR "not a type" */ .Pi
t18 float64 = math.Pi * 10.0 t18 float64 = math.Pi * 10.0
t19 int = t1 /* ERROR "cannot call" */ () t19 int = t1 /* ERROR "cannot call" */ ()
t20 int = f0 /* ERROR "used as single value" */ () t20 int = f0 /* ERROR "no value" */ ()
) )
// Various more complex expressions // Various more complex expressions
@ -94,6 +94,7 @@ var (
v10 byte = 1024 /* ERROR "overflows" */ v10 byte = 1024 /* ERROR "overflows" */
v11 = xx/yy*yy - xx v11 = xx/yy*yy - xx
v12 = true && false v12 = true && false
v13 = nil /* ERROR "use of untyped nil" */
) )
// Multiple assignment expressions // Multiple assignment expressions

View File

@ -63,6 +63,7 @@ var (
u16 = &u0 u16 = &u0
u17 = *u16 u17 = *u16
u18 = <-u16 /* ERROR "cannot receive" */ u18 = <-u16 /* ERROR "cannot receive" */
u19 = ^uint(0)
// float64 // float64
f0 = float64(1) f0 = float64(1)
@ -131,5 +132,4 @@ var (
ch7 = <-ch ch7 = <-ch
ch8 = <-rc ch8 = <-rc
ch9 = <-sc /* ERROR "cannot receive" */ ch9 = <-sc /* ERROR "cannot receive" */
)
)

View File

@ -6,6 +6,17 @@
package expr2 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 // corner cases
var ( var (
v0 = nil /* ERROR "cannot compare" */ == nil v0 = nil /* ERROR "cannot compare" */ == nil

View File

@ -286,3 +286,64 @@ func type_asserts() {
_ = t.(T2 /* ERROR "wrong type for method m" */ ) _ = t.(T2 /* ERROR "wrong type for method m" */ )
_ = t.(I2 /* 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" */ ())
}

View File

@ -141,15 +141,15 @@ type Pointer struct {
Base Type Base Type
} }
// A tuple represents a multi-value function return. // A Result represents a (multi-value) function call result.
// TODO(gri) use better name to avoid confusion (Go doesn't have tuples). // TODO(gri) consider using an empty Result (Values == nil)
type tuple struct { // as representation for the novalue operand mode.
type Result struct {
implementsType implementsType
list []Type Values ObjList // Signature.Results of the function called
} }
// A Signature represents a user-defined function type func(...) (...). // 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 { type Signature struct {
implementsType implementsType
Recv *ast.Object // nil if not a method Recv *ast.Object // nil if not a method

View File

@ -155,7 +155,7 @@ var testExprs = []testEntry{
dup("-f(10, 20)"), dup("-f(10, 20)"),
dup("f(x + y, +3.1415)"), dup("f(x + y, +3.1415)"),
{"func(a, b int) {}", "(func literal)"}, {"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}", "(composite literal)"},
{"[]int{1, 2, 3}[x:]", "(composite literal)[x:]"}, {"[]int{1, 2, 3}[x:]", "(composite literal)[x:]"},
{"i.([]string)", "i.(...)"}, {"i.([]string)", "i.(...)"},