]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: update operand types early
authorRobert Griesemer <gri@golang.org>
Mon, 11 Mar 2013 20:38:45 +0000 (13:38 -0700)
committerRobert Griesemer <gri@golang.org>
Mon, 11 Mar 2013 20:38:45 +0000 (13:38 -0700)
For expressions where the result type is independent
of the argument types (comparisons, conversions, rhs
of shifts), set the final expression types for those
subtrees early.

This fixes several bugs where incorrect lhs shift
operands where used (say in a comparison), but were
not reported.

Together with the changes listed below this CL fixes
many type-checker bugs.

Also:
- better documented updateExprType
- added larger comment to expr.go explaining
  the basic expression checking algorithm
- use latest definition for indices and make
  arguments; use the same code to check both
- use the same mechanism for cycle detection
  in constant expressions as for variables
  (new field Constant.visited)
- more tests for complex and make builtins
- many more and systematic tests for shifts;
  moved them into separate testfile
- in the testing code, don't compare the
  expected error pattern against itself
  (the actual message was always ignored...)
- fix affected error patterns in the test files
- various cleanups along the way

R=adonovan
CC=golang-dev
https://golang.org/cl/7432051

13 files changed:
src/pkg/go/types/builtins.go
src/pkg/go/types/check.go
src/pkg/go/types/check_test.go
src/pkg/go/types/const.go
src/pkg/go/types/conversions.go
src/pkg/go/types/expr.go
src/pkg/go/types/operand.go
src/pkg/go/types/stmt.go
src/pkg/go/types/testdata/builtins.src
src/pkg/go/types/testdata/decls1.src
src/pkg/go/types/testdata/expr3.src
src/pkg/go/types/testdata/shifts.src [new file with mode: 0644]
src/pkg/go/types/testdata/stmt0.src

index ad9259118e02a61307b218c03badfbf0b7da8a47..220be08b5deb28b4661dfa7d814f6575e8afa450 100644 (file)
@@ -11,6 +11,9 @@ import (
        "go/token"
 )
 
+// TODO(gri): Several built-ins are missing assignment checks. As a result,
+//            non-constant shift arguments may not be properly type-checked.
+
 // 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
@@ -170,6 +173,10 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
                        goto Error
                }
 
+               // arguments have final type
+               check.updateExprType(args[0], typ, true)
+               check.updateExprType(args[1], typ, true)
+
        case _Copy:
                var y operand
                check.expr(&y, args[1], nil, iota)
@@ -269,24 +276,13 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
                        check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, n)
                        goto Error
                }
-               var sizes []interface{} // constant integer arguments, if any
+               var sizes []int64 // constant integer arguments, if any
                for _, arg := range args[1:] {
-                       check.expr(x, arg, nil, iota)
-                       if x.isInteger(check.ctxt) {
-                               if x.mode == constant {
-                                       if isNegConst(x.val) {
-                                               check.invalidArg(x.pos(), "%s must not be negative", x)
-                                               // safe to continue
-                                       } else {
-                                               sizes = append(sizes, x.val) // x.val >= 0
-                                       }
-                               }
-                       } else {
-                               check.invalidArg(x.pos(), "%s must be an integer", x)
-                               // safe to continue
+                       if s, ok := check.index(arg, -1, iota); ok && s >= 0 {
+                               sizes = append(sizes, s)
                        }
                }
-               if len(sizes) == 2 && compareConst(sizes[0], sizes[1], token.GTR) {
+               if len(sizes) == 2 && sizes[0] > sizes[1] {
                        check.invalidArg(args[1].Pos(), "length and capacity swapped")
                        // safe to continue
                }
index 8d45d2ea81bfc32b1433cb71bb8966ab9113e686..e8ee9bc3363bcdf187272f42f072785f79a6ef31 100644 (file)
@@ -18,6 +18,15 @@ const (
        trace = false // turn on for detailed type resolution traces
 )
 
+// exprInfo stores type and constant value for an untyped expression.
+type exprInfo struct {
+       isConst bool // expression has a, possibly unknown, constant value
+       isLhs   bool // expression is lhs operand of a shift with delayed type check
+       typ     *Basic
+       val     interface{} // constant value (may be nil if unknown); valid if isConst
+}
+
+// A checker is an instance of the type checker.
 type checker struct {
        ctxt  *Context
        fset  *token.FileSet
@@ -31,14 +40,7 @@ type checker struct {
        initspecs   map[*ast.ValueSpec]*ast.ValueSpec // "inherited" type and initialization expressions for constant declarations
        methods     map[*TypeName]*Scope              // maps type names to associated methods
        conversions map[*ast.CallExpr]bool            // set of type-checked conversions (to distinguish from calls)
-
-       // untyped expressions
-       // TODO(gri): Consider merging the untyped and constants map. Should measure
-       // the ratio between untyped non-constant and untyped constant expressions
-       // to make an informed decision.
-       untyped   map[ast.Expr]*Basic      // map of expressions of untyped type
-       constants map[ast.Expr]interface{} // map of untyped constant expressions; each key also appears in untyped
-       shiftOps  map[ast.Expr]bool        // map of lhs shift operands with delayed type-checking
+       untyped     map[ast.Expr]exprInfo             // map of expressions without final type
 
        // functions
        funclist []function  // list of functions/methods with correct signatures and non-empty bodies
@@ -234,18 +236,14 @@ func (check *checker) object(obj Object, cycleOk bool) {
                        obj.Type = Typ[Invalid]
                        return
                }
+               obj.visited = true
                switch d := obj.decl.(type) {
                case *ast.Field:
                        unreachable() // function parameters are always typed when collected
                case *ast.ValueSpec:
-                       obj.visited = true
                        check.valueSpec(d.Pos(), obj, d.Names, d, 0)
                case *ast.AssignStmt:
-                       // If we reach here, we have a short variable declaration
-                       // where the rhs didn't typecheck and thus the lhs has no
-                       // types.
-                       obj.visited = true
-                       obj.Type = Typ[Invalid]
+                       unreachable() // assign1to1 sets the type for failing short var decls
                default:
                        unreachable() // see also function newObj
                }
@@ -428,9 +426,7 @@ func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package,
                initspecs:   make(map[*ast.ValueSpec]*ast.ValueSpec),
                methods:     make(map[*TypeName]*Scope),
                conversions: make(map[*ast.CallExpr]bool),
-               untyped:     make(map[ast.Expr]*Basic),
-               constants:   make(map[ast.Expr]interface{}),
-               shiftOps:    make(map[ast.Expr]bool),
+               untyped:     make(map[ast.Expr]exprInfo),
        }
 
        // set results and handle panics
@@ -490,9 +486,9 @@ func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package,
 
        // remaining untyped expressions must indeed be untyped
        if debug {
-               for x, typ := range check.untyped {
-                       if !isUntyped(typ) {
-                               check.dump("%s: %s (type %s) is not untyped", x.Pos(), x, typ)
+               for x, info := range check.untyped {
+                       if !isUntyped(info.typ) {
+                               check.dump("%s: %s (type %s) is not untyped", x.Pos(), x, info.typ)
                                panic(0)
                        }
                }
@@ -503,8 +499,12 @@ func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package,
        // after function body checking for smaller
        // map size and more immediate feedback.
        if ctxt.Expr != nil {
-               for x, typ := range check.untyped {
-                       ctxt.Expr(x, typ, check.constants[x])
+               for x, info := range check.untyped {
+                       var val interface{}
+                       if info.isConst {
+                               val = info.val
+                       }
+                       ctxt.Expr(x, info.typ, val)
                }
        }
 
index 28308a579a4d756e07450918bc0e7b9fda565a9e..2ee7f6eef8a947aec769f21b7f6d7253b742a687 100644 (file)
@@ -54,6 +54,7 @@ var tests = []struct {
        {"expr1", []string{"testdata/expr1.src"}},
        {"expr2", []string{"testdata/expr2.src"}},
        {"expr3", []string{"testdata/expr3.src"}},
+       {"shifts", []string{"testdata/shifts.src"}},
        {"builtins", []string{"testdata/builtins.src"}},
        {"conversions", []string{"testdata/conversions.src"}},
        {"stmt0", []string{"testdata/stmt0.src"}},
@@ -62,17 +63,6 @@ var tests = []struct {
 
 var fset = token.NewFileSet()
 
-func getFile(filename string) (file *token.File) {
-       fset.Iterate(func(f *token.File) bool {
-               if f.Name() == filename {
-                       file = f
-                       return false // end iteration
-               }
-               return true
-       })
-       return file
-}
-
 // Positioned errors are of the form filename:line:column: message .
 var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(.*)`)
 
@@ -120,6 +110,7 @@ var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`)
 // in files and returns them as a map of error positions to error messages.
 //
 func errMap(t *testing.T, testname string, files []*ast.File) map[string][]string {
+       // map of position strings to lists of error message patterns
        errmap := make(map[string][]string)
 
        for _, file := range files {
@@ -130,10 +121,7 @@ func errMap(t *testing.T, testname string, files []*ast.File) map[string][]strin
                }
 
                var s scanner.Scanner
-               // file was parsed already - do not add it again to the file
-               // set otherwise the position information returned here will
-               // not match the position information collected by the parser
-               s.Init(getFile(filename), src, nil, scanner.ScanComments)
+               s.Init(fset.AddFile(filename, fset.Base(), len(src)), src, nil, scanner.ScanComments)
                var prev string // position string of last non-comment, non-semicolon token
 
        scanFile:
@@ -143,9 +131,8 @@ func errMap(t *testing.T, testname string, files []*ast.File) map[string][]strin
                        case token.EOF:
                                break scanFile
                        case token.COMMENT:
-                               s := errRx.FindStringSubmatch(lit)
-                               if len(s) == 2 {
-                                       errmap[prev] = append(errmap[prev], string(s[1]))
+                               if s := errRx.FindStringSubmatch(lit); len(s) == 2 {
+                                       errmap[prev] = append(errmap[prev], s[1])
                                }
                        case token.SEMICOLON:
                                // ignore automatically inserted semicolon
@@ -164,17 +151,17 @@ func errMap(t *testing.T, testname string, files []*ast.File) map[string][]strin
 
 func eliminate(t *testing.T, errmap map[string][]string, errlist []error) {
        for _, err := range errlist {
-               pos, msg := splitError(err)
+               pos, gotMsg := splitError(err)
                list := errmap[pos]
                index := -1 // list index of matching message, if any
                // we expect one of the messages in list to match the error at pos
-               for i, msg := range list {
-                       rx, err := regexp.Compile(msg)
+               for i, wantRx := range list {
+                       rx, err := regexp.Compile(wantRx)
                        if err != nil {
                                t.Errorf("%s: %v", pos, err)
                                continue
                        }
-                       if rx.MatchString(msg) {
+                       if rx.MatchString(gotMsg) {
                                index = i
                                break
                        }
@@ -190,9 +177,8 @@ func eliminate(t *testing.T, errmap map[string][]string, errlist []error) {
                                delete(errmap, pos)
                        }
                } else {
-                       t.Errorf("%s: no error expected: %q", pos, msg)
+                       t.Errorf("%s: no error expected: %q", pos, gotMsg)
                }
-
        }
 }
 
@@ -213,10 +199,8 @@ func checkFiles(t *testing.T, testname string, testfiles []string) {
                return
        }
 
-       // match and eliminate errors
+       // match and eliminate errors;
        // we are expecting the following errors
-       // (collect these after parsing the files so that
-       // they are found in the file set)
        errmap := errMap(t, testname, files)
        eliminate(t, errmap, errlist)
 
index e8e86e4fb8926a326d0d619ff2e3221dc7e166af..7df9038bbe626377ab649062e7073d31488b67ba 100644 (file)
@@ -182,24 +182,6 @@ func isZeroConst(x interface{}) bool {
        return ok && i == 0
 }
 
-// isNegConst reports whether the value of constant x is < 0.
-// x must be a non-complex numeric value.
-//
-func isNegConst(x interface{}) bool {
-       switch x := x.(type) {
-       case nil:
-               return false
-       case int64:
-               return x < 0
-       case *big.Int:
-               return x.Sign() < 0
-       case *big.Rat:
-               return x.Sign() < 0
-       }
-       unreachable()
-       return false
-}
-
 // isRepresentableConst reports whether the value of constant x can
 // be represented as a value of the basic type Typ[as] without loss
 // of precision.
index ea629b961c16dc5526744a5055336d74d40aa61f..f75568e27ccdb9fe50855ad80f3e74088c770de6 100644 (file)
@@ -36,7 +36,7 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota
                // common issue.
                if typ.Kind == String {
                        switch {
-                       case x.isInteger(check.ctxt):
+                       case x.isInteger():
                                codepoint, ok := x.val.(int64)
                                if !ok {
                                        // absolute value too large (or unknown) for conversion;
@@ -60,6 +60,9 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota
                x.mode = value
        }
 
+       // the conversion argument types are final
+       check.updateExprType(x.expr, x.typ, true)
+
        check.conversions[conv] = true // for cap/len checking
        x.expr = conv
        x.typ = typ
index 3b0625239f23795e7b012c443a00da8deda50aff..5ccd75f99b9c680c0e29cbc263ee5452846e95ca 100644 (file)
@@ -24,6 +24,51 @@ import (
 // - clients need access to builtins type information
 // - API tests are missing (e.g., identifiers should be handled as expressions in callbacks)
 
+/*
+Basic algorithm:
+
+Expressions are checked recursively, top down. Expression checker functions
+are generally of the form:
+
+  func f(x *operand, e *ast.Expr, ...)
+
+where e is the expression to be checked, and x is the result of the check.
+The check performed by f may fail in which case x.mode == invalid, and
+related error messages will have been issued by f.
+
+If a hint argument is present, it is the composite literal element type
+of an outer composite literal; it is used to type-check composite literal
+elements that have no explicit type specification in the source
+(e.g.: []T{{...}, {...}}, the hint is the type T in this case).
+
+If an iota argument >= 0 is present, it is the value of iota for the
+specific expression.
+
+All expressions are checked via rawExpr, which dispatches according
+to expression kind. Upon returning, rawExpr is recording the types and
+constant values for all expressions that have an untyped type (those types
+may change on the way up in the expression tree). Usually these are constants,
+but the results of comparisons or non-constant shifts of untyped constants
+may also be untyped, but not constant.
+
+Untyped expressions may eventually become fully typed (i.e., not untyped),
+typically when the value is assigned to a variable, or is used otherwise.
+The updateExprType method is used to record this final type and update
+the recorded types: the type-checked expression tree is again traversed down,
+and the new type is propagated as needed. Untyped constant expression values
+that become fully typed must now be representable by the full type (constant
+sub-expression trees are left alone except for their roots). This mechanism
+ensures that a client sees the actual (run-time) type an untyped value would
+have. It also permits type-checking of lhs shift operands "as if the shift
+were not present": when updateExprType visits an untyped lhs shift operand
+and assigns it it's final type, that type must be an integer type, and a
+constant lhs must be representable as an integer.
+
+When an expression gets its final type, either on the way out from rawExpr,
+on the way down in updateExprType, or at the end of the type checker run,
+if present the Context.Expr method is invoked to notify a go/types client.
+*/
+
 func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (params []*Var, isVariadic bool) {
        if list == nil {
                return
@@ -260,7 +305,7 @@ func (check *checker) isRepresentable(x *operand, typ *Basic) {
        if !isRepresentableConst(x.val, check.ctxt, typ.Kind) {
                var msg string
                if isNumeric(x.typ) && isNumeric(typ) {
-                       msg = "%s overflows %s"
+                       msg = "%s overflows (or cannot be accurately represented as) %s"
                } else {
                        msg = "cannot convert %s to %s"
                }
@@ -269,24 +314,30 @@ func (check *checker) isRepresentable(x *operand, typ *Basic) {
        }
 }
 
-// updateExprType updates the type of all untyped nodes in the
-// expression tree of x to typ. If shiftOp is set, x is the lhs
-// of a shift expression. In that case, and if x is in the set
-// of shift operands with delayed type checking, and typ is not
-// an untyped type, updateExprType will check if typ is an
-// integer type.
-// If Context.Expr != nil, it is called for all nodes that are
-// now assigned their final (not untyped) type.
-func (check *checker) updateExprType(x ast.Expr, typ Type, shiftOp bool) {
+// updateExprType updates the type of x to typ and invokes itself
+// recursively for the operands of x, depending on expression kind.
+// If typ is still an untyped and not the final type, updateExprType
+// only updates the recorded untyped type for x and possibly its
+// operands. Otherwise (i.e., typ is not an untyped type anymore,
+// or it is the final type for x), Context.Expr is invoked, if present.
+// Also, if x is a constant, it must be representable as a value of typ,
+// and if x is the (formerly untyped) lhs operand of a non-constant
+// shift, it must be an integer value.
+//
+func (check *checker) updateExprType(x ast.Expr, typ Type, final bool) {
+       old, found := check.untyped[x]
+       if !found {
+               return // nothing to do
+       }
+
+       // update operands of x if necessary
        switch x := x.(type) {
        case *ast.BadExpr,
                *ast.FuncLit,
                *ast.CompositeLit,
-               *ast.SelectorExpr,
                *ast.IndexExpr,
                *ast.SliceExpr,
                *ast.TypeAssertExpr,
-               *ast.CallExpr,
                *ast.StarExpr,
                *ast.KeyValueExpr,
                *ast.ArrayType,
@@ -295,58 +346,86 @@ func (check *checker) updateExprType(x ast.Expr, typ Type, shiftOp bool) {
                *ast.InterfaceType,
                *ast.MapType,
                *ast.ChanType:
-               // these expression are never untyped - nothing to do
+               // These expression are never untyped - nothing to do.
+               // The respective sub-expressions got their final types
+               // upon assignment or use.
+               if debug {
+                       check.dump("%s: found old type(%s): %s (new: %s)", x.Pos(), x, old.typ, typ)
+                       unreachable()
+               }
                return
 
-       case *ast.Ident, *ast.BasicLit:
-               // update type
+       case *ast.CallExpr:
+               // Resulting in an untyped constant (e.g., built-in complex).
+               // The respective calls take care of calling updateExprType
+               // for the arguments if necessary.
+
+       case *ast.Ident, *ast.BasicLit, *ast.SelectorExpr:
+               // An identifier denoting a constant, a constant literal,
+               // or a qualified identifier (imported untyped constant).
+               // No operands to take care of.
 
        case *ast.ParenExpr:
-               check.updateExprType(x.X, typ, false)
+               check.updateExprType(x.X, typ, final)
 
        case *ast.UnaryExpr:
-               check.updateExprType(x.X, typ, false)
+               // If x is a constant, the operands were constants.
+               // They don't need to be updated since they never
+               // get "materialized" into a typed value; and they
+               // will be processed at the end of the type check.
+               if old.isConst {
+                       break
+               }
+               check.updateExprType(x.X, typ, final)
 
        case *ast.BinaryExpr:
+               if old.isConst {
+                       break // see comment for unary expressions
+               }
                if isComparison(x.Op) {
-                       // result type is independent of operand types
+                       // The result type is independent of operand types
+                       // and the operand types must have final types.
                } else if isShift(x.Op) {
-                       // result type depends only on lhs operand
-                       check.updateExprType(x.X, typ, true)
+                       // The result type depends only on lhs operand.
+                       // The rhs type was updated when checking the shift.
+                       check.updateExprType(x.X, typ, final)
                } else {
-                       // operand types match result type
-                       check.updateExprType(x.X, typ, false)
-                       check.updateExprType(x.Y, typ, false)
+                       // The operand types match the result type.
+                       check.updateExprType(x.X, typ, final)
+                       check.updateExprType(x.Y, typ, final)
                }
 
-       case *ast.Ellipsis:
-               unreachable()
        default:
                unreachable()
        }
 
-       // TODO(gri) t should always exist, shouldn't it?
-       if t := check.untyped[x]; t != nil {
-               if isUntyped(typ) {
-                       check.untyped[x] = typ.(*Basic)
-               } else {
-                       // notify clients of final type for x
-                       if f := check.ctxt.Expr; f != nil {
-                               f(x, typ, check.constants[x])
-                       }
-                       delete(check.untyped, x)
-                       delete(check.constants, x)
-                       // check delayed shift
-                       // Note: Using shiftOp is an optimization: it prevents
-                       // map lookups when we know x is not a shiftOp in the
-                       // first place.
-                       if shiftOp && check.shiftOps[x] {
-                               if !isInteger(typ) {
-                                       check.invalidOp(x.Pos(), "shifted operand %s (type %s) must be integer", x, typ)
-                               }
-                               delete(check.shiftOps, x)
-                       }
+       // If the new type is not final and still untyped, just
+       // update the recorded type.
+       if !final && isUntyped(typ) {
+               old.typ = underlying(typ).(*Basic)
+               check.untyped[x] = old
+               return
+       }
+
+       // Otherwise we have the final (typed or untyped type).
+       // Remove it from the map.
+       delete(check.untyped, x)
+
+       // If x is the lhs of a shift, its final type must be integer.
+       // We already know from the shift check that it is representable
+       // as an integer if it is a constant.
+       if old.isLhs && !isInteger(typ) {
+               check.invalidOp(x.Pos(), "shifted operand %s (type %s) must be integer", x, typ)
+               return
+       }
+
+       // Everything's fine, notify client of final type for x.
+       if f := check.ctxt.Expr; f != nil {
+               var val interface{}
+               if old.isConst {
+                       val = old.val
                }
+               f(x, typ, val)
        }
 }
 
@@ -419,7 +498,7 @@ func (check *checker) convertUntyped(x *operand, target Type) {
        }
 
        x.typ = target
-       check.updateExprType(x.expr, target, false)
+       check.updateExprType(x.expr, target, true) // UntypedNils are final
        return
 
 Error:
@@ -456,18 +535,39 @@ func (check *checker) comparison(x, y *operand, op token.Token) {
                x.mode = value
        }
 
+       // The result type of a comparison is always boolean and
+       // independent of the argument types. They have now their
+       // final types (untyped or typed): update the respective
+       // expression trees.
+       check.updateExprType(x.expr, x.typ, true)
+       check.updateExprType(y.expr, y.typ, true)
+
        x.typ = Typ[UntypedBool]
 }
 
 func (check *checker) shift(x, y *operand, op token.Token) {
+       untypedx := isUntyped(x.typ)
+
+       // The lhs must be of integer type or be representable
+       // as an integer; otherwise the shift has no chance.
+       if !isInteger(x.typ) && (!untypedx || !isRepresentableConst(x.val, nil, UntypedInt)) {
+               check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
+               x.mode = invalid
+               return
+       }
+
        // spec: "The right operand in a shift expression must have unsigned
        // integer type or be an untyped constant that can be converted to
        // unsigned integer type."
        switch {
        case isInteger(y.typ) && isUnsigned(y.typ):
                // nothing to do
-       case y.mode == constant && isUntyped(y.typ):
-               check.convertUntyped(x, Typ[UntypedInt])
+       case isUntyped(y.typ):
+               check.convertUntyped(y, Typ[UntypedInt])
+               if y.mode == invalid {
+                       x.mode = invalid
+                       return
+               }
        default:
                check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y)
                x.mode = invalid
@@ -476,33 +576,28 @@ func (check *checker) shift(x, y *operand, op token.Token) {
 
        if x.mode == constant {
                if y.mode == constant {
-                       // constant shift - lhs must be (representable as) an integer
-                       if isUntyped(x.typ) {
-                               if !isRepresentableConst(x.val, check.ctxt, UntypedInt) {
-                                       check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
+                       if untypedx {
+                               x.typ = Typ[UntypedInt]
+                       }
+                       if x.val != nil && y.val != nil {
+                               // rhs must be within reasonable bounds
+                               const stupidShift = 1024
+                               s, ok := y.val.(int64)
+                               if !ok || s < 0 || s >= stupidShift {
+                                       check.invalidOp(y.pos(), "%s: stupid shift", y)
                                        x.mode = invalid
                                        return
                                }
-                               x.typ = Typ[UntypedInt]
-                       }
-                       assert(x.isInteger(check.ctxt))
-
-                       // rhs must be within reasonable bounds
-                       const stupidShift = 1024
-                       s, ok := y.val.(int64)
-                       if !ok || s < 0 || s >= stupidShift {
-                               check.invalidOp(y.pos(), "%s: stupid shift", y)
-                               x.mode = invalid
-                               return
+                               // everything's ok
+                               x.val = shiftConst(x.val, uint(s), op)
+                       } else {
+                               x.val = nil
                        }
-
-                       // everything's ok
-                       x.val = shiftConst(x.val, uint(s), op)
                        return
                }
 
                // non-constant shift with constant lhs
-               if isUntyped(x.typ) {
+               if untypedx {
                        // spec: "If the left operand of a non-constant shift expression is
                        // an untyped constant, the type of the constant is what it would be
                        // if the shift expression were replaced by its left operand alone;
@@ -510,8 +605,16 @@ func (check *checker) shift(x, y *operand, op token.Token) {
                        // instance, if the shift expression is an operand in a comparison
                        // against an untyped constant)".
 
-                       // delay operand checking until we know the type
-                       check.shiftOps[x.expr] = true
+                       // Delay operand checking until we know the final type:
+                       // The lhs expression must be in the untyped map, mark
+                       // the entry as lhs shift operand.
+                       if info, ok := check.untyped[x.expr]; ok {
+                               info.isLhs = true
+                               check.untyped[x.expr] = info
+                       } else {
+                               unreachable()
+                       }
+                       // keep x's type
                        x.mode = value
                        return
                }
@@ -613,36 +716,41 @@ func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token, iota
        // x.typ is unchanged
 }
 
-// index checks an index expression for validity. If length >= 0, it is the upper
-// bound for the index. The result is a valid index >= 0, or a negative value.
-//
-func (check *checker) index(index ast.Expr, length int64, iota int) int64 {
+// index checks an index/size expression arg for validity.
+// If length >= 0, it is the upper bound for arg.
+// TODO(gri): Do we need iota?
+func (check *checker) index(arg ast.Expr, length int64, iota int) (i int64, ok bool) {
        var x operand
+       check.expr(&x, arg, nil, iota)
 
-       check.expr(&x, index, nil, iota)
-       if !x.isInteger(check.ctxt) {
-               check.errorf(x.pos(), "index %s must be integer", &x)
-               return -1
-       }
-       if x.mode != constant {
-               return -1 // we cannot check more
-       }
-       // The spec doesn't require int64 indices, but perhaps it should.
-       i, ok := x.val.(int64)
-       if !ok {
-               check.errorf(x.pos(), "stupid index %s", &x)
-               return -1
+       // an untyped constant must be representable as Int
+       check.convertUntyped(&x, Typ[Int])
+       if x.mode == invalid {
+               return
        }
-       if i < 0 {
-               check.errorf(x.pos(), "index %s must not be negative", &x)
-               return -1
+
+       // the index/size must be of integer type
+       if !isInteger(x.typ) {
+               check.invalidArg(x.pos(), "%s must be integer", &x)
+               return
        }
-       if length >= 0 && i >= length {
-               check.errorf(x.pos(), "index %s is out of bounds (>= %d)", &x, length)
-               return -1
+
+       // a constant index/size i must be 0 <= i < length
+       if x.mode == constant && x.val != nil {
+               i = x.val.(int64)
+               if i < 0 {
+                       check.invalidArg(x.pos(), "%s must not be negative", &x)
+                       return
+               }
+               if length >= 0 && i >= length {
+                       check.errorf(x.pos(), "index %s is out of bounds (>= %d)", &x, length)
+                       return
+               }
+               // 0 <= i [ && i < length ]
+               return i, true
        }
 
-       return i
+       return -1, true
 }
 
 // compositeLitKey resolves unresolved composite literal keys.
@@ -673,8 +781,10 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota
                eval := e
                if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
                        check.compositeLitKey(kv.Key)
-                       if i := check.index(kv.Key, length, iota); i >= 0 {
-                               index = i
+                       if i, ok := check.index(kv.Key, length, iota); ok {
+                               if i >= 0 {
+                                       index = i
+                               }
                                validIndex = true
                        }
                        eval = kv.Value
@@ -756,6 +866,7 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand,
 var emptyResult Result
 
 func (check *checker) callExpr(x *operand) {
+       // convert x into a user-friendly set of values
        var typ Type
        var val interface{}
        switch x.mode {
@@ -774,10 +885,7 @@ func (check *checker) callExpr(x *operand) {
        // until it becomes typed or until the end of
        // type checking
        if isUntyped(typ) {
-               check.untyped[x.expr] = typ.(*Basic)
-               if val != nil {
-                       check.constants[x.expr] = val
-               }
+               check.untyped[x.expr] = exprInfo{x.mode == constant, false, typ.(*Basic), val}
                return
        }
 
@@ -806,6 +914,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                defer check.untrace("=> %s", x)
        }
 
+       // record final type of x if untyped, notify clients of type otherwise
        defer check.callExpr(x)
 
        switch e := e.(type) {
@@ -998,7 +1107,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                                        }
                                        continue
                                }
-                               if x.mode == constant {
+                               if x.mode == constant && x.val != nil {
                                        if visited[x.val] {
                                                check.errorf(x.pos(), "duplicate key %s in map literal", x.val)
                                                continue
@@ -1112,7 +1221,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                case *Basic:
                        if isString(typ) {
                                valid = true
-                               if x.mode == constant {
+                               if x.mode == constant && x.val != nil {
                                        length = int64(len(x.val.(string)))
                                }
                                // an indexed string always yields a byte value
@@ -1183,7 +1292,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                case *Basic:
                        if isString(typ) {
                                valid = true
-                               if x.mode == constant {
+                               if x.mode == constant && x.val != nil {
                                        length = int64(len(x.val.(string))) + 1 // +1 for slice
                                }
                                // a sliced string always yields a string value
@@ -1228,12 +1337,16 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
 
                lo := int64(0)
                if e.Low != nil {
-                       lo = check.index(e.Low, length, iota)
+                       if i, ok := check.index(e.Low, length, iota); ok && i >= 0 {
+                               lo = i
+                       }
                }
 
                hi := int64(-1)
                if e.High != nil {
-                       hi = check.index(e.High, length, iota)
+                       if i, ok := check.index(e.High, length, iota); ok && i >= 0 {
+                               hi = i
+                       }
                } else if length >= 0 {
                        hi = length
                }
index 0e4ee2506a9b7fe6cad80c5033bed5e5a83da804..6f6e058a850c60d0bb9801c0d6bbfe963d70c7b5 100644 (file)
@@ -205,11 +205,10 @@ func (x *operand) isAssignable(ctxt *Context, T Type) bool {
 }
 
 // isInteger reports whether x is a (typed or untyped) integer value.
-// TODO(gri) remove ctxt argument - it is not required for UntypedInt.
-func (x *operand) isInteger(ctxt *Context) bool {
+func (x *operand) isInteger() bool {
        return x.mode == invalid ||
                isInteger(x.typ) ||
-               x.mode == constant && isRepresentableConst(x.val, ctxt, UntypedInt)
+               x.mode == constant && isRepresentableConst(x.val, nil, UntypedInt) // no context required for UntypedInt
 }
 
 // lookupResult represents the result of a struct field/method lookup.
index ae0d4225273aa7f4cb0637ff72e1c7eff6e9ab0a..198cc439a6cbbd4ad4d84648f11a2f3f7282f412 100644 (file)
@@ -59,6 +59,8 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
        if !decl {
                // anything can be assigned to the blank identifier
                if ident != nil && ident.Name == "_" {
+                       // the rhs has its final type
+                       check.updateExprType(rhs, x.typ, true)
                        return
                }
 
index 6fe46550890ef95b01a1f968dce7eca94867459f..e8b5da149b82a24c40f0aff018f6f359f45a0507 100644 (file)
@@ -54,6 +54,7 @@ func _complex() {
        var f32 float32
        var f64 float64
        var c64 complex64
+       var c128 complex128
        _ = complex /* ERROR "argument" */ ()
        _ = complex /* ERROR "argument" */ (1)
        _ = complex(true /* ERROR "invalid argument" */ , 0)
@@ -78,6 +79,21 @@ func _complex() {
        _ = complex(1, 1.1)
        _ = complex(1, 'a')
        complex /* ERROR "not used" */ (1, 2)
+
+       var _ complex64 = complex(f32, f32)
+       var _ complex64 = complex /* ERROR "cannot initialize" */ (f64, f64)
+
+       var _ complex128 = complex /* ERROR "cannot initialize" */ (f32, f32)
+       var _ complex128 = complex(f64, f64)
+
+       // untyped constants
+       const _ int = complex(1, 0)
+       const _ float32 = complex(1, 0)
+       const _ complex64 = complex(1, 0)
+       const _ complex128 = complex(1, 0)
+
+       const _ int = complex /* ERROR "int" */ (1.1, 0)
+       const _ float32 = complex /* ERROR "float32" */ (1, 2)
 }
 
 func _copy() {
@@ -161,7 +177,9 @@ func _len() {
 }
 
 func _make() {
-       n := 0
+       var n int
+       var m float32
+       var s uint
 
        _ = make /* ERROR "argument" */ ()
        _ = make(1 /* ERROR "not a type" */)
@@ -172,32 +190,40 @@ func _make() {
        _ = make/* ERROR "arguments" */ ([]int, 2, 3, 4)
        _ = make([]int, int /* ERROR "not an expression" */)
        _ = make([]int, 10, float32 /* ERROR "not an expression" */)
-       _ = make([]int, "foo" /* ERROR "must be an integer" */)
-       _ = make([]int, 10, 2.3 /* ERROR "must be an integer" */)
+       _ = make([]int, "foo" /* ERROR "cannot convert" */)
+       _ = make([]int, 10, 2.3 /* ERROR "overflows" */)
        _ = make([]int, 5, 10.0)
        _ = make([]int, 0i)
+       _ = make([]int, 1.0)
+       _ = make([]int, 1.0<<s)
+       _ = make([]int, 1.1 /* ERROR "int" */ <<s)
        _ = make([]int, - /* ERROR "must not be negative" */ 1, 10)
        _ = make([]int, 0, - /* ERROR "must not be negative" */ 1)
        _ = make([]int, - /* ERROR "must not be negative" */ 1, - /* ERROR "must not be negative" */ 1)
-       _ = make([]int, 1<<100, 1<<100)  // run-time panic
-       _ = make([]int, 1 /* ERROR "length and capacity swapped" */ <<100 + 1, 1<<100)
-       _ = make([]int, 1 /* ERROR "length and capacity swapped" */ <<100, 12345)
+       _ = make([]int, 1 /* ERROR "overflows" */ <<100, 1 /* ERROR "overflows" */ <<100)
+       _ = make([]int, 10 /* ERROR "length and capacity swapped" */ , 9)
+       _ = make([]int, 1 /* ERROR "overflows" */ <<100, 12345)
+       _ = make([]int, m /* ERROR "must be integer" */ )
 
        // maps
        _ = make /* ERROR "arguments" */ (map[int]string, 10, 20)
        _ = make(map[int]float32, int /* ERROR "not an expression" */)
-       _ = make(map[int]float32, "foo" /* ERROR "must be an integer" */)
+       _ = make(map[int]float32, "foo" /* ERROR "cannot convert" */)
        _ = make(map[int]float32, 10)
        _ = make(map[int]float32, n)
        _ = make(map[int]float32, int64(n))
+       _ = make(map[string]bool, 10.0)
+       _ = make(map[string]bool, 10.0<<s)
 
        // channels
        _ = make /* ERROR "arguments" */ (chan int, 10, 20)
        _ = make(chan int, int /* ERROR "not an expression" */)
-       _ = make(chan<- int, "foo" /* ERROR "must be an integer" */)
+       _ = make(chan<- int, "foo" /* ERROR "cannot convert" */)
        _ = make(<-chan float64, 10)
        _ = make(chan chan int, n)
        _ = make(chan string, int64(n))
+       _ = make(chan bool, 10.0)
+       _ = make(chan bool, 10.0<<s)
 
        make /* ERROR "not used" */ ([]int, 10)
 }
@@ -309,8 +335,8 @@ func _Offsetof() {
        var x struct{ f int }
        _ = unsafe /* ERROR "argument" */ .Offsetof()
        _ = unsafe /* ERROR "argument" */ .Offsetof(1, 2)
-       _ = unsafe.Offsetof(int /* ERROR "not an expression" */)
-       _ = unsafe.Offsetof(x /* ERROR "not a selector" */)
+       _ = unsafe.Offsetof(int /* ERROR "not a selector expression" */)
+       _ = unsafe.Offsetof(x /* ERROR "not a selector expression" */)
        _ = unsafe.Offsetof(x.f)
        _ = unsafe.Offsetof((x.f))
        _ = unsafe.Offsetof((((((((x))).f)))))
@@ -338,7 +364,7 @@ func _Offsetof() {
 
        var y2 S2
        assert(unsafe.Offsetof(y2.S1) == 0)
-       _ = unsafe.Offsetof(y2 /* ERROR "embedded via pointer" */ .x)
+       _ = unsafe.Offsetof(y2 /* ERROR "embedded via pointer" */ .x)
 }
 
 func _Sizeof() {
index d74b7d9bedfd2beed10ef61d9bc6af5d918ed45d..79c9e65997d46fdf98b229ba55f2cbc8d44d4f07 100644 (file)
@@ -37,7 +37,7 @@ var (
        s4 = s + t
        s5 = s /* ERROR "invalid operation" */ / t
        s6 = array[t1]
-       s7 = array[x /* ERROR "index" */]
+       s7 = array[x /* ERROR "integer" */]
        s8 = &a
        s10 = &42 /* ERROR "cannot take address" */
        s11 = &v
@@ -48,16 +48,16 @@ var (
        s19 = s1 /* ERROR "cannot call" */ ()
        s20 = f0 /* ERROR "no value" */ ()
        s21 = f6(1, s1, i)
-       s22 = f6(1, s1, uu /* ERROR "cannot assign" */ )
+       s22 = f6(1, s1, uu /* ERROR "cannot pass argument" */ )
        
        t1 int = i + j
        t2 int = i /* ERROR "mismatched types" */ + x
-       t3 int = c /* ERROR "cannot assign" */ + d
+       t3 int = c /* ERROR "cannot initialize" */ + d
        t4 string = s + t
        t5 string = s /* ERROR "invalid operation" */ / t
        t6 byte = array[t1]
-       t7 byte = array[x /* ERROR "index" */]
-       t8 *int = & /* ERROR "cannot assign" */ a
+       t7 byte = array[x /* ERROR "must be integer" */]
+       t8 *int = & /* ERROR "cannot initialize" */ a
        t10 *int = &42 /* ERROR "cannot take address" */
        t11 *complex64 = &v
        t12 complex64 = -(u + *t11) / *&v
index f5963ca117ded2077c2b5d870d6dd620843a9a75..48f6bc770f0b3c6cb2fd2a9d7518cea9211ff4be 100644 (file)
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// various expressions
-
 package expr3
 
-func shifts1() {
-       var (
-               i0 int
-               u0 uint
-       )
-
-       var (
-               v0 = 1<<0
-               v1 = 1<<i0 /* ERROR "must be unsigned" */
-               v2 = 1<<u0
-               v3 = 1<<"foo" /* ERROR "must be unsigned" */
-               v4 = 1<<- /* ERROR "stupid shift" */ 1
-               v5 = 1<<1025 /* ERROR "stupid shift" */
-               v6 = 1 /* ERROR "overflows" */ <<100
-
-               v10 uint = 1 << 0
-               v11 uint = 1 << u0
-               v12 float32 = 1 /* ERROR "must be integer" */ << u0
-       )
-}
-
-func shifts2() {
-       // from the spec
-       var (
-               s uint = 33
-               i = 1<<s           // 1 has type int
-               j int32 = 1<<s     // 1 has type int32; j == 0
-               k = uint64(1<<s)   // 1 has type uint64; k == 1<<33
-               m int = 1.0<<s     // 1.0 has type int
-               n = 1.0<<s != 0    // 1.0 has type int; n == false if ints are 32bits in size
-               o = 1<<s == 2<<s   // 1 and 2 have type int; o == true if ints are 32bits in size
-               p = 1<<s == 1<<33  // illegal if ints are 32bits in size: 1 has type int, but 1<<33 overflows int
-               u = 1.0 /* ERROR "must be integer" */ <<s         // illegal: 1.0 has type float64, cannot shift
-               v float32 = 1 /* ERROR "must be integer" */ <<s   // illegal: 1 has type float32, cannot shift
-               w int64 = 1.0<<33  // 1.0<<33 is a constant shift expression
-       )
-}
-
-func shifts3(a int16, b float32) {
-       var (
-               s uint = 11
-               u = 1 /* ERROR "must be integer" */ <<s + 1.0
-               v complex128 = 1 /* ERROR "must be integer" */ << s + 1.0 /* ERROR "must be integer" */ << s + 1
-       )
-       x := 1.0 /* ERROR "must be integer" */ <<s + 1
-       shifts3(1.0 << s, 1 /* ERROR "must be integer" */ >> s)
-       // TODO(gri) add more tests (systematically)
-}
-
-func shifts4() {
-       // from src/pkg/compress/lzw/reader.go:90
-       {
-               var d struct {
-                       bits     uint32
-                       width    uint
-               }
-               _ = uint16(d.bits & (1<<d.width - 1))
-       }
-
-       // from src/pkg/debug/dwarf/buf.go:116
-       {
-               var ux uint64
-               var bits uint
-               x := int64(ux)
-               if x&(1<<(bits-1)) != 0 {}
-       }
-
-       // from src/pkg/encoding/asn1/asn1.go:160
-       {
-               var bytes []byte
-               if bytes[len(bytes)-1]&((1<<bytes[0])-1) != 0 {}
-       }
-
-       // from src/pkg/math/big/rat.go:140
-       {
-               var exp int
-               var mantissa uint64
-               shift := uint64(-1022 - (exp - 1)) // [1..53)
-               _ = mantissa & (1<<shift - 1)
-       }
-
-       // from src/pkg/net/interface.go:51
-       {
-               type Flags uint
-               var f Flags
-               var i int
-               if f&(1<<uint(i)) != 0 {}
-       }
-
-       // from src/pkg/runtime/softfloat64.go:234
-       {
-               var gm uint64
-               var shift uint
-               _ = gm & (1<<shift - 1)
-       }
-
-       // from src/pkg/strconv/atof.go:326
-       {
-               var mant uint64
-               var mantbits uint
-               if mant == 2<<mantbits {}
-       }
-
-       // from src/pkg/syscall/route_bsd.go:82
-       {
-               var Addrs int32
-               const rtaRtMask = 1
-               var i uint
-               if Addrs&rtaRtMask&(1<<i) == 0 {}
-       }
-
-       // from src/pkg/text/scanner/scanner.go:540
-       {
-               var s struct { Whitespace uint64 }
-               var ch rune
-               for s.Whitespace&(1<<uint(ch)) != 0 {}
-       }
-}
-
-// TODO(gri) The error messages below depend on adjusting the spec
-//           to reflect what gc is doing at the moment (the spec
-//           asks for run-time errors at the moment - see issue 4231).
-// TODO(gri) This has been fixed in the spec. Fix this.
-//
 func indexes() {
        _ = 1 /* ERROR "cannot index" */ [0]
        _ = indexes /* ERROR "cannot index" */ [0]
        _ = ( /* ERROR "cannot slice" */ 12 + 3)[1:2]
 
        var a [10]int
-       _ = a[true /* ERROR "must be integer" */ ]
-       _ = a["foo" /* ERROR "must be integer" */ ]
-       _ = a[1.1 /* ERROR "must be integer" */ ]
+       _ = a[true /* ERROR "cannot convert" */ ]
+       _ = a["foo" /* ERROR "cannot convert" */ ]
+       _ = a[1.1 /* ERROR "overflows" */ ]
        _ = a[1.0]
-       _ = a[- /* ERROR "index .* negative" */ 1]
-       _ = a[- /* ERROR "index .* negative" */ 1 :]
-       _ = a[: - /* ERROR "index .* negative" */ 1]
+       _ = a[- /* ERROR "negative" */ 1]
+       _ = a[- /* ERROR "negative" */ 1 :]
+       _ = a[: - /* ERROR "negative" */ 1]
        var a0 int
        a0 = a[0]
        var a1 int32
        a1 = a /* ERROR "cannot assign" */ [1] 
        _ = a[9]
        _ = a[10 /* ERROR "index .* out of bounds" */ ]
-       _ = a[1 /* ERROR "stupid index" */ <<100]
+       _ = a[1 /* ERROR "overflows" */ <<100]
        _ = a[10:]
        _ = a[:10]
        _ = a[10:10]
        _ = a[11 /* ERROR "index .* out of bounds" */ :]
        _ = a[: 11 /* ERROR "index .* out of bounds" */ ]
-       _ = a[: 1 /* ERROR "stupid index" */ <<100]
+       _ = a[: 1 /* ERROR "overflows" */ <<100]
 
        pa := &a
        _ = pa[9]
        _ = pa[10 /* ERROR "index .* out of bounds" */ ]
-       _ = pa[1 /* ERROR "stupid index" */ <<100]
+       _ = pa[1 /* ERROR "overflows" */ <<100]
        _ = pa[10:]
        _ = pa[:10]
        _ = pa[10:10]
        _ = pa[11 /* ERROR "index .* out of bounds" */ :]
        _ = pa[: 11 /* ERROR "index .* out of bounds" */ ]
-       _ = pa[: 1 /* ERROR "stupid index" */ <<100]
+       _ = pa[: 1 /* ERROR "overflows" */ <<100]
 
        var b [0]int
        _ = b[0 /* ERROR "index .* out of bounds" */ ]
@@ -176,21 +50,21 @@ func indexes() {
        _ = b[0:0]
 
        var s []int
-       _ = s[- /* ERROR "index .* negative" */ 1]
-       _ = s[- /* ERROR "index .* negative" */ 1 :]
-       _ = s[: - /* ERROR "index .* negative" */ 1]
+       _ = s[- /* ERROR "negative" */ 1]
+       _ = s[- /* ERROR "negative" */ 1 :]
+       _ = s[: - /* ERROR "negative" */ 1]
        _ = s[0]
        _ = s[1 : 2]
        _ = s[2 /* ERROR "inverted slice range" */ : 1]
        _ = s[2 :]
-       _ = s[: 1 /* ERROR "stupid index" */ <<100]
-       _ = s[1 /* ERROR "stupid index" */ <<100 :]
-       _ = s[1 /* ERROR "stupid index" */ <<100 : 1 /* ERROR "stupid index" */ <<100]
+       _ = s[: 1 /* ERROR "overflows" */ <<100]
+       _ = s[1 /* ERROR "overflows" */ <<100 :]
+       _ = s[1 /* ERROR "overflows" */ <<100 : 1 /* ERROR "overflows" */ <<100]
 
        var t string
-       _ = t[- /* ERROR "index .* negative" */ 1]
-       _ = t[- /* ERROR "index .* negative" */ 1 :]
-       _ = t[: - /* ERROR "index .* negative" */ 1]
+       _ = t[- /* ERROR "negative" */ 1]
+       _ = t[- /* ERROR "negative" */ 1 :]
+       _ = t[: - /* ERROR "negative" */ 1]
        var t0 byte
        t0 = t[0]
        var t1 rune
@@ -199,9 +73,9 @@ func indexes() {
        _ = ("foo" + "bar")[6 /* ERROR "index .* out of bounds" */ ]
 
        const c = "foo"
-       _ = c[- /* ERROR "index .* negative" */ 1]
-       _ = c[- /* ERROR "index .* negative" */ 1 :]
-       _ = c[: - /* ERROR "index .* negative" */ 1]
+       _ = c[- /* ERROR "negative" */ 1]
+       _ = c[- /* ERROR "negative" */ 1 :]
+       _ = c[: - /* ERROR "negative" */ 1]
        var c0 byte
        c0 = c[0]
        var c2 float32
@@ -233,7 +107,7 @@ func method_expressions() {
        _ = T /* ERROR "has no method" */ .x
        _ = T.m
        var f func(*T) = (*T).m
-       var g func(*T) = ( /* ERROR "cannot assign" */ T).m
+       var g func(*T) = ( /* ERROR "cannot initialize" */ T).m
 }
 
 func struct_literals() {
@@ -254,7 +128,7 @@ func struct_literals() {
        _ = T1{aa /* ERROR "unknown field" */ : 0}
        _ = T1{1 /* ERROR "invalid field name" */ : 0}
        _ = T1{a: 0, s: "foo", u: 0, a /* ERROR "duplicate field" */: 10}
-       _ = T1{a: "foo" /* ERROR "cannot use" */ }
+       _ = T1{a: "foo" /* ERROR "cannot convert" */ }
        _ = T1{c /* ERROR "unknown field" */ : 0}
        _ = T1{T0: { /* ERROR "missing type" */ }}
        _ = T1{T0: T0{}}
@@ -265,7 +139,7 @@ func struct_literals() {
        _ = T0{1, b /* ERROR "mixture" */ : 2, 3}
        _ = T0{1, 2} /* ERROR "too few values" */
        _ = T0{1, 2, 3, 4  /* ERROR "too many values" */ }
-       _ = T0{1, "foo" /* ERROR "cannot use" */, 3.4  /* ERROR "cannot use" */}
+       _ = T0{1, "foo" /* ERROR "cannot convert" */, 3.4  /* ERROR "overflows" */}
 }
 
 func array_literals() {
@@ -279,18 +153,18 @@ func array_literals() {
        _ = A1{0, 1, 2}
        _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
        _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /* ERROR "index .* out of bounds" */ }
-       _ = A1{- /* ERROR "index .* negative" */ 1: 0}
+       _ = A1{- /* ERROR "negative" */ 1: 0}
        _ = A1{8: 8, 9}
        _ = A1{8: 8, 9, 10 /* ERROR "index .* out of bounds" */ }
        _ = A1{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
        _ = A1{5: 5, 6, 7, 3: 3, 4}
        _ = A1{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ }
        _ = A1{10 /* ERROR "index .* out of bounds" */ : 10, 10 /* ERROR "index .* out of bounds" */ : 10}
-       _ = A1{5: 5, 6, 7, 3: 3, 1 /* ERROR "stupid index" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
-       _ = A1{5: 5, 6, 7, 4: 4, 1 /* ERROR "stupid index" */ <<100: 4}
+       _ = A1{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
+       _ = A1{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4}
        _ = A1{2.0}
-       _ = A1{2.1 /* ERROR "cannot use" */ }
-       _ = A1{"foo" /* ERROR "cannot use" */ }
+       _ = A1{2.1 /* ERROR "overflows" */ }
+       _ = A1{"foo" /* ERROR "cannot convert" */ }
 
        a0 := [...]int{}
        assert(len(a0) == 0)
@@ -302,7 +176,7 @@ func array_literals() {
        a13 = a1
        a14 = a1 /* ERROR "cannot assign" */
        
-       a2 := [...]int{- /* ERROR "index .* negative" */ 1: 0}
+       a2 := [...]int{- /* ERROR "negative" */ 1: 0}
 
        a3 := [...]int{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
        assert(len(a3) == 5) // somewhat arbitrary
@@ -326,18 +200,18 @@ func slice_literals() {
        _ = S0{0, 1, 2}
        _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
        _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
-       _ = S0{- /* ERROR "index .* negative" */ 1: 0}
+       _ = S0{- /* ERROR "negative" */ 1: 0}
        _ = S0{8: 8, 9}
        _ = S0{8: 8, 9, 10}
        _ = S0{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
        _ = S0{5: 5, 6, 7, 3: 3, 4}
        _ = S0{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ }
        _ = S0{10: 10, 10 /* ERROR "duplicate index" */ : 10}
-       _ = S0{5: 5, 6, 7, 3: 3, 1 /* ERROR "stupid index" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
-       _ = S0{5: 5, 6, 7, 4: 4, 1 /* ERROR "stupid index" */ <<100: 4}
+       _ = S0{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
+       _ = S0{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4}
        _ = S0{2.0}
-       _ = S0{2.1 /* ERROR "cannot use" */ }
-       _ = S0{"foo" /* ERROR "cannot use" */ }
+       _ = S0{2.1 /* ERROR "overflows" */ }
+       _ = S0{"foo" /* ERROR "cannot convert" */ }
 
        // indices must be resolved correctly
        // (for details, see comment in go/parser/parser.go, method parseElement)
@@ -356,8 +230,8 @@ func map_literals() {
 
        _ = M0{}
        _ = M0{1 /* ERROR "missing key" */ }
-       _ = M0{1 /* ERROR "cannot use .* as string key" */ : 2}
-       _ = M0{"foo": "bar" /* ERROR "cannot use .* as int value" */ }
+       _ = M0{1 /* ERROR "cannot convert" */ : 2}
+       _ = M0{"foo": "bar" /* ERROR "cannot convert" */ }
        _ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 }
 
        // map keys must be resolved correctly
@@ -428,7 +302,7 @@ func _calls() {
        f1(10.0)
        f1 /* ERROR "too few arguments" */ ()
        f1(x, y /* ERROR "too many arguments" */ )
-       f1(s /* ERROR "cannot assign" */ )
+       f1(s /* ERROR "cannot pass" */ )
        f1(x ... /* ERROR "cannot use ..." */ )
        f1(g0 /* ERROR "used as value" */ ())
        f1(g1())
@@ -437,24 +311,24 @@ func _calls() {
        f2 /* ERROR "too few arguments" */ ()
        f2 /* ERROR "too few arguments" */ (3.14)
        f2(3.14, "foo")
-       f2(x /* ERROR "cannot assign" */ , "foo")
+       f2(x /* ERROR "cannot pass" */ , "foo")
        f2(g0 /* ERROR "used as value" */ ())
-       f2 /* ERROR "too few arguments" */ (g1 /* ERROR "cannot assign" */ ())
+       f2 /* ERROR "too few arguments" */ (g1 /* ERROR "cannot pass" */ ())
        f2(g2())
 
        fs /* ERROR "too few arguments" */ ()
        fs(g0 /* ERROR "used as value" */ ())
-       fs(g1 /* ERROR "cannot assign" */ ())
+       fs(g1 /* ERROR "cannot pass" */ ())
        // fs(g2()) // TODO(gri) missing position in error message
        fs(gs())
 
        fv()
        fv(1, 2.0, x)
-       fv(s /* ERROR "cannot assign" */ )
+       fv(s /* ERROR "cannot pass" */ )
        fv(s...)
        fv(1, s /* ERROR "can only use ... with matching parameter" */ ...)
-       fv(gs /* ERROR "cannot assign" */ ())
-       fv(gs /* ERROR "cannot assign" */ ()...)
+       fv(gs /* ERROR "cannot pass" */ ())
+       fv(gs /* ERROR "cannot pass" */ ()...)
 
        fi()
        fi(1, 2.0, x, 3.14, "foo")
diff --git a/src/pkg/go/types/testdata/shifts.src b/src/pkg/go/types/testdata/shifts.src
new file mode 100644 (file)
index 0000000..49496e5
--- /dev/null
@@ -0,0 +1,266 @@
+// Copyright 2013 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.
+
+package shifts
+
+func shifts1() {
+       // basics
+       var (
+               i0 int
+               u0 uint
+
+               v0 = 1<<0
+               v1 = 1<<i0 /* ERROR "must be unsigned" */
+               v2 = 1<<u0
+               v3 = 1<<"foo" /* ERROR "cannot convert" */
+               v4 = 1<<- /* ERROR "stupid shift" */ 1
+               v5 = 1<<1025 /* ERROR "stupid shift" */
+               v6 = 1 /* ERROR "overflows" */ <<100
+
+               v10 uint = 1 << 0
+               v11 uint = 1 << u0
+               v12 float32 = 1 /* ERROR "must be integer" */ << u0
+       )
+}
+
+func shifts2() {
+       // from the spec
+       var (
+               s uint = 33
+               i = 1<<s           // 1 has type int
+               j int32 = 1<<s     // 1 has type int32; j == 0
+               k = uint64(1<<s)   // 1 has type uint64; k == 1<<33
+               m int = 1.0<<s     // 1.0 has type int
+               // Disabled test below. gc and gccgo disagree: gc permits it per spec special case,
+               // gccgo does not (issue 4881). The spec special case seems not justified (issue 4883),
+               // and go/types agrees with gccgo.
+               // n = 1.0<<s != 0    // 1.0 has type int; n == false if ints are 32bits in size
+               n = 1.0 /* ERROR "must be integer" */ <<s != 0
+               o = 1<<s == 2<<s   // 1 and 2 have type int; o == true if ints are 32bits in size
+               p = 1<<s == 1<<33  // illegal if ints are 32bits in size: 1 has type int, but 1<<33 overflows int
+               u = 1.0 /* ERROR "must be integer" */ <<s         // illegal: 1.0 has type float64, cannot shift
+               v float32 = 1 /* ERROR "must be integer" */ <<s   // illegal: 1 has type float32, cannot shift
+               w int64 = 1.0<<33  // 1.0<<33 is a constant shift expression
+       )
+}
+
+func shifts3(a int16, b float32) {
+       // random tests
+       var (
+               s uint = 11
+               u = 1 /* ERROR "must be integer" */ <<s + 1.0
+               v complex128 = 1 /* ERROR "must be integer" */ << s + 1.0 /* ERROR "must be integer" */ << s + 1
+       )
+       x := 1.0 /* ERROR "must be integer" */ <<s + 1
+       shifts3(1.0 << s, 1 /* ERROR "must be integer" */ >> s)
+}
+
+func shifts4() {
+       // shifts in comparisons w/ untyped operands
+       var s uint
+
+       _ = 1<<s == 1
+       _ = 1 /* ERROR "integer" */ <<s == 1.
+       _ = 1. /* ERROR "integer" */ <<s == 1
+       _ = 1. /* ERROR "integer" */ <<s == 1.
+
+       _ = 1<<s + 1 == 1
+       _ = 1 /* ERROR "integer" */ <<s + 1 == 1.
+       _ = 1 /* ERROR "integer" */ <<s + 1. == 1
+       _ = 1 /* ERROR "integer" */ <<s + 1. == 1.
+       _ = 1. /* ERROR "integer" */ <<s + 1 == 1
+       _ = 1. /* ERROR "integer" */ <<s + 1 == 1.
+       _ = 1. /* ERROR "integer" */ <<s + 1. == 1
+       _ = 1. /* ERROR "integer" */ <<s + 1. == 1.
+
+       _ = 1<<s == 1<<s
+       _ = 1 /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s
+       _ = 1. /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s
+       _ = 1. /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s
+
+       _ = 1<<s + 1<<s == 1
+       _ = 1 /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1.
+       _ = 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1
+       _ = 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1.
+       _ = 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1
+       _ = 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1.
+       _ = 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1
+       _ = 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1.
+
+       _ = 1<<s + 1<<s == 1<<s + 1<<s
+       _ = 1 /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
+       _ = 1 /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s
+       _ = 1 /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
+       _ = 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s
+       _ = 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
+       _ = 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s
+       _ = 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
+       _ = 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s
+       _ = 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
+       _ = 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s
+       _ = 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
+       _ = 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s
+       _ = 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1 /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
+       _ = 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1 /* ERROR "integer" */ <<s
+       _ = 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s == 1. /* ERROR "integer" */ <<s + 1. /* ERROR "integer" */ <<s
+}
+
+func shifts5() {
+       // shifts in comparisons w/ typed operands
+       var s uint
+       var x int
+
+       _ = 1<<s == x
+       _ = 1.<<s == x
+       _ = 1.1 /* ERROR "int" */ <<s == x
+
+       _ = 1<<s + x == 1
+       _ = 1<<s + x == 1.
+       _ = 1<<s + x == 1.1 /* ERROR "int" */
+       _ = 1.<<s + x == 1
+       _ = 1.<<s + x == 1.
+       _ = 1.<<s + x == 1.1 /* ERROR "int" */
+       _ = 1.1 /* ERROR "int" */ <<s + x == 1
+       _ = 1.1 /* ERROR "int" */ <<s + x == 1.
+       _ = 1.1 /* ERROR "int" */ <<s + x == 1.1
+
+       _ = 1<<s == x<<s
+       _ = 1.<<s == x<<s
+       _ = 1.1  /* ERROR "int" */ <<s == x<<s
+}
+
+func shifts6() {
+       // shifts as operands in non-arithmetic operations and as arguments
+       var a [10]int
+       var s uint
+
+       _ = a[1<<s]
+       _ = a[1.0]
+       _ = a[1.0<<s]
+
+       _ = make([]int, 1.0)
+       _ = make([]int, 1.0<<s)
+       _ = make([]int, 1.1 /* ERROR "integer" */ <<s)
+
+       _ = float32(1)
+       _ = float32(1<<s)
+       _ = float32(1.0)
+       _ = float32(1.0 /* ERROR "int" */ <<s)
+       _ = float32(1.1 /* ERROR "int" */ <<s)
+
+       var b []int
+       _ = append(b, 1<<s)
+       _ = append(b, 1.0<<s)
+       _ = append(b, 1.1 /* ERROR "must be integer" */ <<s)
+
+       var c []float32
+       _ = append(b, 1<<s)
+       _ = append(b, 1.0<<s) // should fail - see TODO in append code
+       _ = append(b, 1.1 /* ERROR "must be integer" */ <<s)
+
+       _ = complex(1.0 /* ERROR "must be integer" */ <<s, 0)
+       _ = complex(1.1 /* ERROR "must be integer" */ <<s, 0)
+       _ = complex(0, 1.0 /* ERROR "must be integer" */ <<s)
+       _ = complex(0, 1.1 /* ERROR "must be integer" */ <<s)
+
+       // TODO(gri) The delete below is not type-checked correctly yet.
+       // var m1 map[int]string
+       // delete(m1, 1<<s)
+}
+
+func shifts7() {
+       // shifts of shifts
+       var s uint
+       var x int
+       _ = 1<<(1<<s)
+       _ = 1<<(1.<<s)
+       _ = 1. /* ERROR "integer" */ <<(1<<s)
+       _ = 1. /* ERROR "integer" */ <<(1.<<s)
+
+       x = 1<<(1<<s)
+       x = 1<<(1.<<s)
+       x = 1.<<(1<<s)
+       x = 1.<<(1.<<s)
+
+       _ = (1<<s)<<(1<<s)
+       _ = (1<<s)<<(1.<<s)
+       _ = ( /* ERROR "integer" */ 1.<<s)<<(1<<s)
+       _ = ( /* ERROR "integer" */ 1.<<s)<<(1.<<s)
+
+       x = (1<<s)<<(1<<s)
+       x = (1<<s)<<(1.<<s)
+       x = ( /* ERROR "integer" */ 1.<<s)<<(1<<s)
+       x = ( /* ERROR "integer" */ 1.<<s)<<(1.<<s)
+}
+
+func shifts8() {
+       // various originally failing snippets of code from the std library
+       // from src/pkg/compress/lzw/reader.go:90
+       {
+               var d struct {
+                       bits     uint32
+                       width    uint
+               }
+               _ = uint16(d.bits & (1<<d.width - 1))
+       }
+
+       // from src/pkg/debug/dwarf/buf.go:116
+       {
+               var ux uint64
+               var bits uint
+               x := int64(ux)
+               if x&(1<<(bits-1)) != 0 {}
+       }
+
+       // from src/pkg/encoding/asn1/asn1.go:160
+       {
+               var bytes []byte
+               if bytes[len(bytes)-1]&((1<<bytes[0])-1) != 0 {}
+       }
+
+       // from src/pkg/math/big/rat.go:140
+       {
+               var exp int
+               var mantissa uint64
+               shift := uint64(-1022 - (exp - 1)) // [1..53)
+               _ = mantissa & (1<<shift - 1)
+       }
+
+       // from src/pkg/net/interface.go:51
+       {
+               type Flags uint
+               var f Flags
+               var i int
+               if f&(1<<uint(i)) != 0 {}
+       }
+
+       // from src/pkg/runtime/softfloat64.go:234
+       {
+               var gm uint64
+               var shift uint
+               _ = gm & (1<<shift - 1)
+       }
+
+       // from src/pkg/strconv/atof.go:326
+       {
+               var mant uint64
+               var mantbits uint
+               if mant == 2<<mantbits {}
+       }
+
+       // from src/pkg/syscall/route_bsd.go:82
+       {
+               var Addrs int32
+               const rtaRtMask = 1
+               var i uint
+               if Addrs&rtaRtMask&(1<<i) == 0 {}
+       }
+
+       // from src/pkg/text/scanner/scanner.go:540
+       {
+               var s struct { Whitespace uint64 }
+               var ch rune
+               for s.Whitespace&(1<<uint(ch)) != 0 {}
+       }
+}
index 9d85de3bbb00686541c128a452922ad3a97f8e0b..af0d8061f234cb321bffec868682ca4fe6d31098 100644 (file)
@@ -116,7 +116,7 @@ func switches() {
        }
 
        switch x {
-       case 1 /* ERROR "overflows int" */ << 100:
+       case 1 /* ERROR "overflows" */ << 100:
        }
 
        switch x {
@@ -171,13 +171,13 @@ func typeswitches() {
 
        switch t := x.(type) {
        case nil:
-               var v bool = t /* ERROR "cannot assign" */
+               var v bool = t /* ERROR "cannot initialize" */
        case int:
                var v int = t
        case float32, complex64:
-               var v float32 = t /* ERROR "cannot assign" */
+               var v float32 = t /* ERROR "cannot initialize" */
        default:
-               var v float32 = t /* ERROR "cannot assign" */
+               var v float32 = t /* ERROR "cannot initialize" */
        }
 
        var t I