]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types: correct error position for inherited const init expressions
authorRob Findley <rfindley@google.com>
Tue, 8 Dec 2020 14:44:48 +0000 (09:44 -0500)
committerRobert Findley <rfindley@google.com>
Tue, 8 Dec 2020 19:45:23 +0000 (19:45 +0000)
This is a port of CL 275517 from the dev.typeparams branch, to fix the
positioning of error messages for invalid const init expressions that
are inherited.

Differences from CL 275517:
 + The inherited flag is added to the constDecl intermediate
   representation.
 + The errpos override is made a positioner, the internal interface
   used by go/types to capture error position and span. For const decls
   errpos is just set to a singular point, but using positioner is
   correct and causes span start and end positions to also be
   overridden.
 + Test cases are updated to assert on just 'overflows', as the go/types
   error message is, for example, "cannot use 255 + iota (untyped int
   constant 256) as byte value in constant declaration (overflows)".
   This is more verbose than the compiler's "constant 256 overflows
   byte", but changing that is out of scope.

Fixes #42991

Change-Id: I0a71d2290f7fff5513f2a6e49b83e6f0f4da30e5
Reviewed-on: https://go-review.googlesource.com/c/go/+/276172
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/go/types/check.go
src/go/types/decl.go
src/go/types/errors.go
src/go/types/resolver.go
src/go/types/testdata/constdecl.src

index 5e7bd92076114ae7807250242d5d1c5830ed64fc..280792e838ef24bb62849c1b93904dc436770aba 100644 (file)
@@ -46,6 +46,7 @@ type context struct {
        scope         *Scope                 // top-most scope for lookups
        pos           token.Pos              // if valid, identifiers are looked up as if at position pos (used by Eval)
        iota          constant.Value         // value of iota in a constant declaration; nil otherwise
+       errpos        positioner             // if set, identifier position of a constant with inherited initializer
        sig           *Signature             // function signature if inside a function; nil otherwise
        isPanic       map[*ast.CallExpr]bool // set of panic call expressions (used for termination check)
        hasLabel      bool                   // set if a function makes use of labels (only ~1% of functions); unused outside functions
index 17b66ca387a73bdea38b85a0069b5a7f8e254319..1f0bc358a26e9ace5e0c3dcf396eaaf6079e2646 100644 (file)
@@ -183,7 +183,7 @@ func (check *Checker) objDecl(obj Object, def *Named) {
        switch obj := obj.(type) {
        case *Const:
                check.decl = d // new package-level const decl
-               check.constDecl(obj, d.typ, d.init)
+               check.constDecl(obj, d.typ, d.init, d.inherited)
        case *Var:
                check.decl = d // new package-level var decl
                check.varDecl(obj, d.lhs, d.typ, d.init)
@@ -388,10 +388,11 @@ type (
 
        importDecl struct{ spec *ast.ImportSpec }
        constDecl  struct {
-               spec *ast.ValueSpec
-               iota int
-               typ  ast.Expr
-               init []ast.Expr
+               spec      *ast.ValueSpec
+               iota      int
+               typ       ast.Expr
+               init      []ast.Expr
+               inherited bool
        }
        varDecl  struct{ spec *ast.ValueSpec }
        typeDecl struct{ spec *ast.TypeSpec }
@@ -424,14 +425,17 @@ func (check *Checker) walkDecl(d ast.Decl, f func(decl)) {
                                switch d.Tok {
                                case token.CONST:
                                        // determine which initialization expressions to use
+                                       inherited := true
                                        switch {
                                        case s.Type != nil || len(s.Values) > 0:
                                                last = s
+                                               inherited = false
                                        case last == nil:
                                                last = new(ast.ValueSpec) // make sure last exists
+                                               inherited = false
                                        }
                                        check.arityMatch(s, last)
-                                       f(constDecl{spec: s, iota: iota, init: last.Values, typ: last.Type})
+                                       f(constDecl{spec: s, iota: iota, typ: last.Type, init: last.Values, inherited: inherited})
                                case token.VAR:
                                        check.arityMatch(s, nil)
                                        f(varDecl{s})
@@ -451,12 +455,16 @@ func (check *Checker) walkDecl(d ast.Decl, f func(decl)) {
        }
 }
 
-func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) {
+func (check *Checker) constDecl(obj *Const, typ, init ast.Expr, inherited bool) {
        assert(obj.typ == nil)
 
        // use the correct value of iota
-       defer func(iota constant.Value) { check.iota = iota }(check.iota)
+       defer func(iota constant.Value, errpos positioner) {
+               check.iota = iota
+               check.errpos = errpos
+       }(check.iota, check.errpos)
        check.iota = obj.val
+       check.errpos = nil
 
        // provide valid constant value under all circumstances
        obj.val = constant.MakeUnknown()
@@ -479,6 +487,15 @@ func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) {
        // check initialization
        var x operand
        if init != nil {
+               if inherited {
+                       // The initialization expression is inherited from a previous
+                       // constant declaration, and (error) positions refer to that
+                       // expression and not the current constant declaration. Use
+                       // the constant identifier position for any errors during
+                       // init expression evaluation since that is all we have
+                       // (see issues #42991, #42992).
+                       check.errpos = atPos(obj.pos)
+               }
                check.expr(&x, init)
        }
        check.initConst(obj, &x)
@@ -753,7 +770,7 @@ func (check *Checker) declStmt(d ast.Decl) {
                                        init = d.init[i]
                                }
 
-                               check.constDecl(obj, d.typ, init)
+                               check.constDecl(obj, d.typ, init, d.inherited)
                        }
 
                        // process function literals in init expressions before scope changes
index c9c475e469bea0338fc482a27386f8a0a51ac62c..a2195011f0ee63a2f8c8459ce1617ff6cae212f0 100644 (file)
@@ -89,6 +89,18 @@ func (check *Checker) err(err error) {
                return
        }
 
+       if check.errpos != nil && isInternal {
+               // If we have an internal error and the errpos override is set, use it to
+               // augment our error positioning.
+               // TODO(rFindley) we may also want to augment the error message and refer
+               // to the position (pos) in the original expression.
+               span := spanOf(check.errpos)
+               e.Pos = span.pos
+               e.go116start = span.start
+               e.go116end = span.end
+               err = e
+       }
+
        if check.firstErr == nil {
                check.firstErr = err
        }
@@ -111,15 +123,15 @@ func (check *Checker) err(err error) {
 }
 
 func (check *Checker) newError(at positioner, code errorCode, soft bool, msg string) error {
-       ext := spanOf(at)
+       span := spanOf(at)
        return Error{
                Fset:       check.fset,
-               Pos:        ext.pos,
+               Pos:        span.pos,
                Msg:        msg,
                Soft:       soft,
                go116code:  code,
-               go116start: ext.start,
-               go116end:   ext.end,
+               go116start: span.start,
+               go116end:   span.end,
        }
 }
 
index 4092d55b4e5b457849cc81c6efddbe04be83277f..b637f8b8cacf75793f564660081bc0c741daab1f 100644 (file)
@@ -17,12 +17,13 @@ import (
 
 // A declInfo describes a package-level const, type, var, or func declaration.
 type declInfo struct {
-       file  *Scope        // scope of file containing this declaration
-       lhs   []*Var        // lhs of n:1 variable declarations, or nil
-       typ   ast.Expr      // type, or nil
-       init  ast.Expr      // init/orig expression, or nil
-       fdecl *ast.FuncDecl // func declaration, or nil
-       alias bool          // type alias declaration
+       file      *Scope        // scope of file containing this declaration
+       lhs       []*Var        // lhs of n:1 variable declarations, or nil
+       typ       ast.Expr      // type, or nil
+       init      ast.Expr      // init/orig expression, or nil
+       inherited bool          // if set, the init expression is inherited from a previous constant declaration
+       fdecl     *ast.FuncDecl // func declaration, or nil
+       alias     bool          // type alias declaration
 
        // The deps field tracks initialization expression dependencies.
        deps map[Object]bool // lazily initialized
@@ -323,7 +324,7 @@ func (check *Checker) collectObjects() {
                                                init = d.init[i]
                                        }
 
-                                       d := &declInfo{file: fileScope, typ: d.typ, init: init}
+                                       d := &declInfo{file: fileScope, typ: d.typ, init: init, inherited: d.inherited}
                                        check.declarePkgObj(name, obj, d)
                                }
 
index c2f40ed6e6711a26474339e2e96de9e861c65c27..680c85aff37f3f21cfc937cdf8f4515f38a9bf65 100644 (file)
@@ -107,4 +107,35 @@ func _() {
        const x, y, z = 0, 1, unsafe.Sizeof(func() { _ = x /* ERROR "undeclared name" */ + y /* ERROR "undeclared name" */ + z /* ERROR "undeclared name" */ })
 }
 
+// Test cases for errors in inherited constant initialization expressions.
+// Errors related to inherited initialization expressions must appear at
+// the constant identifier being declared, not at the original expression
+// (issues #42991, #42992).
+const (
+       _ byte = 255 + iota
+       /* some gap */
+       _ // ERROR overflows
+       /* some gap */
+       /* some gap */ _ /* ERROR overflows */; _ /* ERROR overflows */
+       /* some gap */
+       _ = 255 + iota
+       _ = byte /* ERROR overflows */ (255) + iota
+       _ /* ERROR overflows */
+)
+
+// Test cases from issue.
+const (
+       ok = byte(iota + 253)
+       bad
+       barn
+       bard // ERROR cannot convert
+)
+
+const (
+       c = len([1 - iota]int{})
+       d
+       e // ERROR invalid array length
+       f // ERROR invalid array length
+)
+
 // TODO(gri) move extra tests from testdata/const0.src into here