diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/pkg/go/types/builtins.go | 26 | ||||
| -rw-r--r-- | src/pkg/go/types/check.go | 44 | ||||
| -rw-r--r-- | src/pkg/go/types/check_test.go | 38 | ||||
| -rw-r--r-- | src/pkg/go/types/const.go | 18 | ||||
| -rw-r--r-- | src/pkg/go/types/conversions.go | 5 | ||||
| -rw-r--r-- | src/pkg/go/types/expr.go | 323 | ||||
| -rw-r--r-- | src/pkg/go/types/operand.go | 5 | ||||
| -rw-r--r-- | src/pkg/go/types/stmt.go | 2 | ||||
| -rw-r--r-- | src/pkg/go/types/testdata/builtins.src | 48 | ||||
| -rw-r--r-- | src/pkg/go/types/testdata/decls1.src | 10 | ||||
| -rw-r--r-- | src/pkg/go/types/testdata/expr3.src | 216 | ||||
| -rw-r--r-- | src/pkg/go/types/testdata/shifts.src | 266 | ||||
| -rw-r--r-- | src/pkg/go/types/testdata/stmt0.src | 8 |
13 files changed, 627 insertions, 382 deletions
diff --git a/src/pkg/go/types/builtins.go b/src/pkg/go/types/builtins.go index ad9259118e..220be08b5d 100644 --- a/src/pkg/go/types/builtins.go +++ b/src/pkg/go/types/builtins.go @@ -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 } diff --git a/src/pkg/go/types/check.go b/src/pkg/go/types/check.go index 8d45d2ea81..e8ee9bc336 100644 --- a/src/pkg/go/types/check.go +++ b/src/pkg/go/types/check.go @@ -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) } } diff --git a/src/pkg/go/types/check_test.go b/src/pkg/go/types/check_test.go index 28308a579a..2ee7f6eef8 100644 --- a/src/pkg/go/types/check_test.go +++ b/src/pkg/go/types/check_test.go @@ -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) diff --git a/src/pkg/go/types/const.go b/src/pkg/go/types/const.go index e8e86e4fb8..7df9038bbe 100644 --- a/src/pkg/go/types/const.go +++ b/src/pkg/go/types/const.go @@ -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. diff --git a/src/pkg/go/types/conversions.go b/src/pkg/go/types/conversions.go index ea629b961c..f75568e27c 100644 --- a/src/pkg/go/types/conversions.go +++ b/src/pkg/go/types/conversions.go @@ -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 diff --git a/src/pkg/go/types/expr.go b/src/pkg/go/types/expr.go index 3b0625239f..5ccd75f99b 100644 --- a/src/pkg/go/types/expr.go +++ b/src/pkg/go/types/expr.go @@ -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 } diff --git a/src/pkg/go/types/operand.go b/src/pkg/go/types/operand.go index 0e4ee2506a..6f6e058a85 100644 --- a/src/pkg/go/types/operand.go +++ b/src/pkg/go/types/operand.go @@ -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. diff --git a/src/pkg/go/types/stmt.go b/src/pkg/go/types/stmt.go index ae0d422527..198cc439a6 100644 --- a/src/pkg/go/types/stmt.go +++ b/src/pkg/go/types/stmt.go @@ -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 } diff --git a/src/pkg/go/types/testdata/builtins.src b/src/pkg/go/types/testdata/builtins.src index 6fe4655089..e8b5da149b 100644 --- a/src/pkg/go/types/testdata/builtins.src +++ b/src/pkg/go/types/testdata/builtins.src @@ -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 a pointer" */ .x) } func _Sizeof() { diff --git a/src/pkg/go/types/testdata/decls1.src b/src/pkg/go/types/testdata/decls1.src index d74b7d9bed..79c9e65997 100644 --- a/src/pkg/go/types/testdata/decls1.src +++ b/src/pkg/go/types/testdata/decls1.src @@ -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 diff --git a/src/pkg/go/types/testdata/expr3.src b/src/pkg/go/types/testdata/expr3.src index f5963ca117..48f6bc770f 100644 --- a/src/pkg/go/types/testdata/expr3.src +++ b/src/pkg/go/types/testdata/expr3.src @@ -2,171 +2,45 @@ // 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 index 0000000000..49496e5b4b --- /dev/null +++ b/src/pkg/go/types/testdata/shifts.src @@ -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 {} + } +} diff --git a/src/pkg/go/types/testdata/stmt0.src b/src/pkg/go/types/testdata/stmt0.src index 9d85de3bbb..af0d8061f2 100644 --- a/src/pkg/go/types/testdata/stmt0.src +++ b/src/pkg/go/types/testdata/stmt0.src @@ -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 |
