]> Cypherpunks repositories - gostls13.git/commitdiff
exp/types: completed typechecking of parameter passing
authorRobert Griesemer <gri@golang.org>
Mon, 17 Dec 2012 19:35:59 +0000 (11:35 -0800)
committerRobert Griesemer <gri@golang.org>
Mon, 17 Dec 2012 19:35:59 +0000 (11:35 -0800)
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

14 files changed:
src/pkg/exp/gotype/gotype_test.go
src/pkg/exp/types/builtins.go
src/pkg/exp/types/const.go
src/pkg/exp/types/errors.go
src/pkg/exp/types/expr.go
src/pkg/exp/types/operand.go
src/pkg/exp/types/predicates.go
src/pkg/exp/types/stmt.go
src/pkg/exp/types/testdata/decls1.src
src/pkg/exp/types/testdata/expr0.src
src/pkg/exp/types/testdata/expr2.src
src/pkg/exp/types/testdata/expr3.src
src/pkg/exp/types/types.go
src/pkg/exp/types/types_test.go

index 8a90082206e83c02dbcdf027f2d0bec500c5d2ed..59e2a885636348652502e63ce65f1f2f42cd5642 100644 (file)
@@ -50,7 +50,7 @@ var tests = []string{
 
        // directories
        // Note: packages that don't typecheck yet are commented out
-       // "archive/tar", // investigate
+       "archive/tar",
        "archive/zip",
 
        "bufio",
@@ -77,13 +77,13 @@ var tests = []string{
        "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",
@@ -117,7 +117,7 @@ var tests = []string{
 
        "go/ast",
        "go/build",
-       // "go/doc", // variadic parameters don't work yet fully
+       "go/doc",
        "go/format",
        "go/parser",
        "go/printer",
@@ -125,7 +125,7 @@ var tests = []string{
        "go/token",
 
        "hash/adler32",
-       // "hash/crc32", // investigate
+       "hash/crc32",
        "hash/crc64",
        "hash/fnv",
 
@@ -139,54 +139,54 @@ var tests = []string{
        "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",
@@ -194,10 +194,10 @@ var tests = []string{
 
        "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",
index f86ae6ac381b275ad7167d09abb7463a4e0df5a6..ed636ee2a4f148a179347def2166d307a515164a 100644 (file)
@@ -155,13 +155,14 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
                        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:
index cab6bbcbd000b0e1678060717c3acc1b528fc03e..d44c8fb61d22d12d37ffe1fad9e950c84b2465b6 100644 (file)
@@ -49,12 +49,17 @@ func (nilType) String() string {
        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
@@ -74,7 +79,7 @@ func normalizeIntConst(x *big.Int) interface{} {
 }
 
 // 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{} {
@@ -84,15 +89,15 @@ 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
@@ -140,7 +145,7 @@ func makeComplexConst(lit string) interface{} {
        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
@@ -202,9 +207,6 @@ func isNegConst(x interface{}) bool {
 // 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
@@ -386,13 +388,71 @@ func is63bit(x int64) bool {
        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) {
@@ -403,8 +463,6 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
                        return x && y
                case token.LOR:
                        return x || y
-               default:
-                       unreachable()
                }
 
        case int64:
@@ -431,7 +489,7 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
                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)))
@@ -443,8 +501,6 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
                        return x ^ y
                case token.AND_NOT:
                        return x &^ y
-               default:
-                       unreachable()
                }
 
        case *big.Int:
@@ -460,7 +516,7 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
                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))
@@ -533,7 +589,7 @@ func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
                default:
                        unreachable()
                }
-               return normalizeComplexConst(complex{&re, &im})
+               return newComplex(&re, &im)
 
        case string:
                if op == token.ADD {
index 1a1659538a50ce3d1a50e2f8c4c52cb87185e273..b1b6436968aaa90b684ffd6b5f73f4c04c04acbc 100644 (file)
@@ -266,15 +266,8 @@ func writeType(buf *bytes.Buffer, typ Type) {
                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")
index e1f627b98f27baaeda54275266ac3fd4c42ecdbb..2f53bc085e39d7fde291da9d58bb96c187a94ef6 100644 (file)
@@ -19,7 +19,6 @@ import (
 // - 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
 
@@ -212,21 +211,11 @@ func (check *checker) unary(x *operand, op token.Token) {
        }
 
        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
        }
 
@@ -304,6 +293,8 @@ func (check *checker) convertUntyped(x *operand, target Type) {
                if !x.isNil() {
                        goto Error
                }
+       default:
+               unreachable()
        }
 
        x.typ = target
@@ -332,7 +323,7 @@ func (check *checker) comparison(x, y *operand, op token.Token) {
        }
 
        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
        }
@@ -465,10 +456,11 @@ func (check *checker) binary(x, y *operand, op token.Token, hint Type) {
        }
 
        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
        }
 
@@ -554,9 +546,15 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota
        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]
@@ -565,16 +563,32 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr) {
                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) {
@@ -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)
                } 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 {
index 1a5e5172a8b1073ccf1d1790347c0710e3e3e19f..f8ddd84b5426aceb68ae6902299f9d2a30d235ba 100644 (file)
@@ -182,7 +182,14 @@ func (x *operand) isAssignable(T Type) bool {
        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:
index 2c1a99192aa57c1433727de40d00228919404752..ff6825ba3b44ac99bbf6201bb582e39b89100995 100644 (file)
@@ -225,25 +225,28 @@ func deref(typ Type) 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 {
        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]
        }
index edad87f2e09f5542b0b18d4dc8f746e83b3912dd..7f9d45eb9874edfff6232af2c1a6364720095577 100644 (file)
@@ -12,9 +12,9 @@ import (
 )
 
 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
        }
@@ -95,7 +95,12 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
                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
@@ -177,12 +182,12 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
                        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
@@ -429,7 +434,7 @@ func (check *checker) stmt(s ast.Stmt) {
                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)
@@ -451,15 +456,15 @@ func (check *checker) stmt(s ast.Stmt) {
                                        }
                                        // 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()
                                        }
@@ -475,7 +480,7 @@ func (check *checker) stmt(s ast.Stmt) {
                                        }
                                        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))
                                        }
                                }
index be927091c1b0842b8600ad5cd05e997cbfe264d2..3baed67505cfed4d6e1f5cf4659b37f7a7949b98 100644 (file)
@@ -46,7 +46,7 @@ var (
        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" */ )
        
@@ -68,7 +68,7 @@ var (
        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
@@ -94,6 +94,7 @@ var (
        v10 byte = 1024 /* ERROR "overflows" */
        v11 = xx/yy*yy - xx
        v12 = true && false
+       v13 = nil /* ERROR "use of untyped nil" */
 )
 
 // Multiple assignment expressions
index 0ed314a95cf5968ac404e55488f72ecaf12e1c14..8b2eb04f292b95b49114909eb6485d50be340bcc 100644 (file)
@@ -63,6 +63,7 @@ var (
        u16 = &u0
        u17 = *u16
        u18 = <-u16 /* ERROR "cannot receive" */
+       u19 = ^uint(0)
 
        // float64
        f0 = float64(1)
@@ -131,5 +132,4 @@ var (
        ch7 = <-ch
        ch8 = <-rc
        ch9 = <-sc /* ERROR "cannot receive" */
-
-)
\ No newline at end of file
+)
index 4bc2769651b0c8207261cf179408b0d4ac61e301..674be4005dd5c0a34c394a003d33f0de17a77c9b 100644 (file)
@@ -6,6 +6,17 @@
 
 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
index a5ea4d2b82ee471a5cfb12fe85abecec2e80aadb..35905c4972022df284807c695c659ffdf3f4c3d3 100644 (file)
@@ -286,3 +286,64 @@ func type_asserts() {
        _ = 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
index 83a08266dd30fad71c3be8edfbdea83e2fa3eeb8..6e4a98783eed6323626af20bf3bc83d753b71637 100644 (file)
@@ -141,15 +141,15 @@ type Pointer struct {
        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
index 62ca19badca2ce93435626039ca36774a26983b3..361f63634e556c4a10885af2a770f531a679613e 100644 (file)
@@ -155,7 +155,7 @@ var testExprs = []testEntry{
        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.(...)"},