aboutsummaryrefslogtreecommitdiff
path: root/src/go
diff options
context:
space:
mode:
authorRob Findley <rfindley@google.com>2020-12-08 09:44:48 -0500
committerRobert Findley <rfindley@google.com>2020-12-08 19:45:23 +0000
commit01b76d5fbc4d4acdd28b08a061b072b73b22f44e (patch)
tree01c49c26cbd469a94049eb33818914593b100a04 /src/go
parent48d6275952184f1e858c2796d36c6b205d5d7e83 (diff)
downloadgo-01b76d5fbc4d4acdd28b08a061b072b73b22f44e.tar.gz
go-01b76d5fbc4d4acdd28b08a061b072b73b22f44e.zip
go/types: correct error position for inherited const init expressions
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>
Diffstat (limited to 'src/go')
-rw-r--r--src/go/types/check.go1
-rw-r--r--src/go/types/decl.go35
-rw-r--r--src/go/types/errors.go20
-rw-r--r--src/go/types/resolver.go15
-rw-r--r--src/go/types/testdata/constdecl.src31
5 files changed, 82 insertions, 20 deletions
diff --git a/src/go/types/check.go b/src/go/types/check.go
index 5e7bd92076..280792e838 100644
--- a/src/go/types/check.go
+++ b/src/go/types/check.go
@@ -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
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
index 17b66ca387..1f0bc358a2 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -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
diff --git a/src/go/types/errors.go b/src/go/types/errors.go
index c9c475e469..a2195011f0 100644
--- a/src/go/types/errors.go
+++ b/src/go/types/errors.go
@@ -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,
}
}
diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go
index 4092d55b4e..b637f8b8ca 100644
--- a/src/go/types/resolver.go
+++ b/src/go/types/resolver.go
@@ -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)
}
diff --git a/src/go/types/testdata/constdecl.src b/src/go/types/testdata/constdecl.src
index c2f40ed6e6..680c85aff3 100644
--- a/src/go/types/testdata/constdecl.src
+++ b/src/go/types/testdata/constdecl.src
@@ -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