From: Robert Griesemer Date: Thu, 17 Sep 2015 21:13:56 +0000 (-0700) Subject: go/types: unified handling of assignment errors X-Git-Tag: go1.6beta1~1037 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=3d91624fad8be21e2e612ec28cc3a4a752c45561;p=gostls13.git go/types: unified handling of assignment errors - simpler code - closer to gc error messages - more context information in some cases Change-Id: Iad155a887b838a4fc1edf719eed18269670b5ede Reviewed-on: https://go-review.googlesource.com/14720 Reviewed-by: Alan Donovan --- diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index eeda0d847c..76c34b4c4f 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -797,7 +797,7 @@ func main() { makePkg("main", mainSrc) for e, sel := range selections { - sel.String() // assertion: must not panic + _ = sel.String() // assertion: must not panic start := fset.Position(e.Pos()).Offset end := fset.Position(e.End()).Offset diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go index a906252fdb..10ab17b9cf 100644 --- a/src/go/types/assignments.go +++ b/src/go/types/assignments.go @@ -13,17 +13,15 @@ import ( // assignment reports whether x can be assigned to a variable of type T, // if necessary by attempting to convert untyped values to the appropriate -// type. If x.mode == invalid upon return, then assignment has already -// issued an error message and the caller doesn't have to report another. +// type. context describes the context in which the assignment takes place. // Use T == nil to indicate assignment to an untyped blank identifier. -// If the result is false and a non-nil reason is provided, it may be set -// to a more detailed explanation of the failure (result != ""). -func (check *Checker) assignment(x *operand, T Type, reason *string) bool { +// x.mode is set to invalid if the assignment failed. +func (check *Checker) assignment(x *operand, T Type, context string) { check.singleValue(x) switch x.mode { case invalid: - return true // error reported before + return // error reported before case constant_, variable, mapindex, value, commaok: // ok default: @@ -39,15 +37,15 @@ func (check *Checker) assignment(x *operand, T Type, reason *string) bool { // or string constant." if T == nil || IsInterface(T) { if T == nil && x.typ == Typ[UntypedNil] { - check.errorf(x.pos(), "use of untyped nil") + check.errorf(x.pos(), "use of untyped nil in %s", context) x.mode = invalid - return false + return } target = defaultType(x.typ) } check.convertUntyped(x, target) if x.mode == invalid { - return false + return } } // x.typ is typed @@ -55,7 +53,18 @@ func (check *Checker) assignment(x *operand, T Type, reason *string) bool { // spec: "If a left-hand side is the blank identifier, any typed or // non-constant value except for the predeclared identifier nil may // be assigned to it." - return T == nil || x.assignableTo(check.conf, T, reason) + if T == nil { + return + } + + if reason := ""; !x.assignableTo(check.conf, T, &reason) { + if reason != "" { + check.errorf(x.pos(), "cannot use %s as %s value in %s: %s", x, T, context, reason) + } else { + check.errorf(x.pos(), "cannot use %s as %s value in %s", x, T, context) + } + x.mode = invalid + } } func (check *Checker) initConst(lhs *Const, x *operand) { @@ -81,18 +90,15 @@ func (check *Checker) initConst(lhs *Const, x *operand) { lhs.typ = x.typ } - if reason := ""; !check.assignment(x, lhs.typ, &reason) { - if x.mode != invalid { - check.xerrorf(x.pos(), reason, "cannot define constant %s (type %s) as %s", lhs.Name(), lhs.typ, x) - } + check.assignment(x, lhs.typ, "constant declaration") + if x.mode == invalid { return } lhs.val = x.val } -// If result is set, lhs is a function result parameter and x is a return result. -func (check *Checker) initVar(lhs *Var, x *operand, result bool) Type { +func (check *Checker) initVar(lhs *Var, x *operand, context string) Type { if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] { if lhs.typ == nil { lhs.typ = Typ[Invalid] @@ -106,7 +112,7 @@ func (check *Checker) initVar(lhs *Var, x *operand, result bool) Type { if isUntyped(typ) { // convert untyped types to default types if typ == Typ[UntypedNil] { - check.errorf(x.pos(), "use of untyped nil") + check.errorf(x.pos(), "use of untyped nil in %s", context) lhs.typ = Typ[Invalid] return nil } @@ -115,15 +121,8 @@ func (check *Checker) initVar(lhs *Var, x *operand, result bool) Type { lhs.typ = typ } - if reason := ""; !check.assignment(x, lhs.typ, &reason) { - if x.mode != invalid { - if result { - // don't refer to lhs.name because it may be an anonymous result parameter - check.xerrorf(x.pos(), reason, "cannot return %s as value of type %s", x, lhs.typ) - } else { - check.xerrorf(x.pos(), reason, "cannot initialize %s with %s", lhs, x) - } - } + check.assignment(x, lhs.typ, context) + if x.mode == invalid { return nil } @@ -141,9 +140,9 @@ func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type { // Don't evaluate lhs if it is the blank identifier. if ident != nil && ident.Name == "_" { check.recordDef(ident, nil) - if !check.assignment(x, nil, nil) { - assert(x.mode == invalid) - x.typ = nil + check.assignment(x, nil, "assignment to _ identifier") + if x.mode == invalid { + return nil } return x.typ } @@ -184,10 +183,8 @@ func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type { return nil } - if reason := ""; !check.assignment(x, z.typ, &reason) { - if x.mode != invalid { - check.xerrorf(x.pos(), reason, "cannot assign %s to %s", x, &z) - } + check.assignment(x, z.typ, "assignment") + if x.mode == invalid { return nil } @@ -218,12 +215,17 @@ func (check *Checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) return } + context := "assignment" + if returnPos.IsValid() { + context = "return statement" + } + var x operand if commaOk { var a [2]Type for i := range a { get(&x, i) - a[i] = check.initVar(lhs[i], &x, returnPos.IsValid()) + a[i] = check.initVar(lhs[i], &x, context) } check.recordCommaOkTypes(rhs[0], a) return @@ -231,7 +233,7 @@ func (check *Checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) for i, lhs := range lhs { get(&x, i) - check.initVar(lhs, &x, returnPos.IsValid()) + check.initVar(lhs, &x, context) } } diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index be6c92982d..c288024c54 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -471,8 +471,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b case _Panic: // panic(x) T := new(Interface) - if !check.assignment(x, T, nil) { - assert(x.mode == invalid) + check.assignment(x, T, "argument to panic") + if x.mode == invalid { return } @@ -491,8 +491,9 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b if i > 0 { arg(x, i) // first argument already evaluated } - if !check.assignment(x, nil, nil) { - assert(x.mode == invalid) + check.assignment(x, nil, "argument to "+predeclaredFuncs[id].name) + if x.mode == invalid { + // TODO(gri) "use" all arguments? return } params[i] = x.typ @@ -514,8 +515,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b case _Alignof: // unsafe.Alignof(x T) uintptr - if !check.assignment(x, nil, nil) { - assert(x.mode == invalid) + check.assignment(x, nil, "argument to unsafe.Alignof") + if x.mode == invalid { return } @@ -571,8 +572,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b case _Sizeof: // unsafe.Sizeof(x T) uintptr - if !check.assignment(x, nil, nil) { - assert(x.mode == invalid) + check.assignment(x, nil, "argument to unsafe.Sizeof") + if x.mode == invalid { return } diff --git a/src/go/types/call.go b/src/go/types/call.go index 4ce0a6bd62..8aeb862993 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -202,7 +202,7 @@ func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, if i == n-1 && call.Ellipsis.IsValid() { ellipsis = call.Ellipsis } - check.argument(sig, i, x, ellipsis) + check.argument(call.Fun, sig, i, x, ellipsis) } } @@ -220,7 +220,7 @@ func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, // argument checks passing of argument x to the i'th parameter of the given signature. // If ellipsis is valid, the argument is followed by ... at that position in the call. -func (check *Checker) argument(sig *Signature, i int, x *operand, ellipsis token.Pos) { +func (check *Checker) argument(fun ast.Expr, sig *Signature, i int, x *operand, ellipsis token.Pos) { check.singleValue(x) if x.mode == invalid { return @@ -260,9 +260,7 @@ func (check *Checker) argument(sig *Signature, i int, x *operand, ellipsis token typ = typ.(*Slice).elem } - if reason := ""; !check.assignment(x, typ, &reason) && x.mode != invalid { - check.xerrorf(x.pos(), reason, "cannot pass argument %s to parameter of type %s", x, typ) - } + check.assignment(x, typ, check.sprintf("argument to %s", fun)) } func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 8e9e5f36de..f064f6856f 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -156,7 +156,7 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) { assert(lhs == nil || lhs[0] == obj) var x operand check.expr(&x, init) - check.initVar(obj, &x, false) + check.initVar(obj, &x, "variable declaration") return } diff --git a/src/go/types/errors.go b/src/go/types/errors.go index 7c81b129a4..0c0049b1f3 100644 --- a/src/go/types/errors.go +++ b/src/go/types/errors.go @@ -86,14 +86,6 @@ func (check *Checker) errorf(pos token.Pos, format string, args ...interface{}) check.err(pos, check.sprintf(format, args...), false) } -func (check *Checker) xerrorf(pos token.Pos, reason, format string, args ...interface{}) { - if reason != "" { - format += ": %s" - args = append(args, reason) - } - check.err(pos, check.sprintf(format, args...), true) -} - func (check *Checker) softErrorf(pos token.Pos, format string, args ...interface{}) { check.err(pos, check.sprintf(format, args...), true) } diff --git a/src/go/types/expr.go b/src/go/types/expr.go index 0f5712b1a5..ce9ea83b97 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -898,9 +898,7 @@ func (check *Checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64 // check element against composite literal element type var x operand check.exprWithHint(&x, eval, typ) - if reason := ""; !check.assignment(&x, typ, &reason) && x.mode != invalid { - check.xerrorf(x.pos(), reason, "cannot use %s as %s value in array or slice literal", &x, typ) - } + check.assignment(&x, typ, "array or slice literal") } return max } @@ -1062,12 +1060,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { visited[i] = true check.expr(x, kv.Value) etyp := fld.typ - if reason := ""; !check.assignment(x, etyp, &reason) { - if x.mode != invalid { - check.xerrorf(x.pos(), reason, "cannot use %s as %s value in struct literal", x, etyp) - } - continue - } + check.assignment(x, etyp, "struct literal") } } else { // no element must have a key @@ -1088,12 +1081,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { continue } etyp := fld.typ - if reason := ""; !check.assignment(x, etyp, &reason) { - if x.mode != invalid { - check.xerrorf(x.pos(), reason, "cannot use %s as %s value in struct literal", x, etyp) - } - continue - } + check.assignment(x, etyp, "struct literal") } if len(e.Elts) < len(fields) { check.error(e.Rbrace, "too few values in struct literal") @@ -1120,10 +1108,8 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { continue } check.exprWithHint(x, kv.Key, utyp.key) - if reason := ""; !check.assignment(x, utyp.key, &reason) { - if x.mode != invalid { - check.xerrorf(x.pos(), reason, "cannot use %s as %s key in map literal", x, utyp.key) - } + check.assignment(x, utyp.key, "map literal") + if x.mode == invalid { continue } if x.mode == constant_ { @@ -1147,12 +1133,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { } } check.exprWithHint(x, kv.Value, utyp.elem) - if reason := ""; !check.assignment(x, utyp.elem, &reason) { - if x.mode != invalid { - check.xerrorf(x.pos(), reason, "cannot use %s as %s value in map literal", x, utyp.elem) - } - continue - } + check.assignment(x, utyp.elem, "map literal") } default: @@ -1220,10 +1201,8 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { case *Map: var key operand check.expr(&key, e.Index) - if reason := ""; !check.assignment(&key, typ.key, &reason) { - if key.mode != invalid { - check.xerrorf(key.pos(), reason, "cannot use %s as map index of type %s", &key, typ.key) - } + check.assignment(&key, typ.key, "map index") + if x.mode == invalid { goto Error } x.mode = mapindex diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index 50efc1fc99..973af423c1 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -321,13 +321,20 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { if ch.mode == invalid || x.mode == invalid { return } - reason := "" - if tch, ok := ch.typ.Underlying().(*Chan); !ok || tch.dir == RecvOnly || !check.assignment(&x, tch.elem, &reason) { - if x.mode != invalid { - check.xerrorf(x.pos(), reason, "cannot send %s to channel %s", &x, &ch) - } + + tch, ok := ch.typ.Underlying().(*Chan) + if !ok { + check.invalidOp(s.Arrow, "cannot send to non-chan type %s", ch.typ) + return } + if tch.dir == RecvOnly { + check.invalidOp(s.Arrow, "cannot send to receive-only type %s", tch) + return + } + + check.assignment(&x, tch.elem, "send") + case *ast.IncDecStmt: var op token.Token switch s.Tok { @@ -465,7 +472,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { check.expr(&x, s.Tag) // By checking assignment of x to an invisible temporary // (as a compiler would), we get all the relevant checks. - check.assignment(&x, nil, nil) + check.assignment(&x, nil, "switch expression") } else { // spec: "A missing switch expression is // equivalent to the boolean value true." @@ -767,7 +774,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { x.mode = value x.expr = lhs // we don't have a better rhs expression to use here x.typ = typ - check.initVar(obj, &x, false) + check.initVar(obj, &x, "range clause") } else { obj.typ = Typ[Invalid] obj.used = true // don't complain about unused variable diff --git a/src/go/types/testdata/builtins.src b/src/go/types/testdata/builtins.src index 1931d56848..7fb7b58a48 100644 --- a/src/go/types/testdata/builtins.src +++ b/src/go/types/testdata/builtins.src @@ -22,12 +22,12 @@ func append1() { append /* ERROR not used */ (s) _ = append(s, b) - _ = append(s, x /* ERROR cannot pass argument x */ ) - _ = append(s, s /* ERROR cannot pass argument s */ ) + _ = append(s, x /* ERROR cannot use x */ ) + _ = append(s, s /* ERROR cannot use s */ ) _ = append(s... /* ERROR can only use ... with matching parameter */ ) _ = append(s, b, s... /* ERROR can only use ... with matching parameter */ ) _ = append(s, 1, 2, 3) - _ = append(s, 1, 2, 3, x /* ERROR cannot pass argument x */ , 5, 6, 6) + _ = append(s, 1, 2, 3, x /* ERROR cannot use x */ , 5, 6, 6) _ = append(s, 1, 2, s... /* ERROR can only use ... with matching parameter */ ) _ = append([]interface{}(nil), 1, 2, "foo", x, 3.1425, false) @@ -38,13 +38,13 @@ func append1() { _ = append(s, "foo"...) _ = append(S(s), "foo" /* ERROR cannot convert */ ) _ = append(S(s), "foo"...) - _ = append(s, t /* ERROR cannot pass argument t */ ) + _ = append(s, t /* ERROR cannot use t */ ) _ = append(s, t...) _ = append(s, T("foo")...) - _ = append(S(s), t /* ERROR cannot pass argument t */ ) + _ = append(S(s), t /* ERROR cannot use t */ ) _ = append(S(s), t...) _ = append(S(s), T("foo")...) - _ = append([]string{}, t /* ERROR cannot pass argument t */ , "foo") + _ = append([]string{}, t /* ERROR cannot use t */ , "foo") _ = append([]T{}, t, "foo") } @@ -192,9 +192,9 @@ func complex1() { complex /* ERROR not used */ (1, 2) var _ complex64 = complex(f32, f32) - var _ complex64 = complex /* ERROR cannot initialize */ (f64, f64) + var _ complex64 = complex /* ERROR cannot use .* in variable declaration */ (f64, f64) - var _ complex128 = complex /* ERROR cannot initialize */ (f32, f32) + var _ complex128 = complex /* ERROR cannot use .* in variable declaration */ (f32, f32) var _ complex128 = complex(f64, f64) // untyped constants @@ -213,7 +213,7 @@ func complex1() { var s uint _ = complex(1 /* ERROR integer */ <