Skip to content

Commit 01b76d5

Browse files
committed
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 <[email protected]> TryBot-Result: Go Bot <[email protected]> Trust: Robert Findley <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent 48d6275 commit 01b76d5

File tree

5 files changed

+82
-20
lines changed

5 files changed

+82
-20
lines changed

src/go/types/check.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ type context struct {
4646
scope *Scope // top-most scope for lookups
4747
pos token.Pos // if valid, identifiers are looked up as if at position pos (used by Eval)
4848
iota constant.Value // value of iota in a constant declaration; nil otherwise
49+
errpos positioner // if set, identifier position of a constant with inherited initializer
4950
sig *Signature // function signature if inside a function; nil otherwise
5051
isPanic map[*ast.CallExpr]bool // set of panic call expressions (used for termination check)
5152
hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions

src/go/types/decl.go

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ func (check *Checker) objDecl(obj Object, def *Named) {
183183
switch obj := obj.(type) {
184184
case *Const:
185185
check.decl = d // new package-level const decl
186-
check.constDecl(obj, d.typ, d.init)
186+
check.constDecl(obj, d.typ, d.init, d.inherited)
187187
case *Var:
188188
check.decl = d // new package-level var decl
189189
check.varDecl(obj, d.lhs, d.typ, d.init)
@@ -388,10 +388,11 @@ type (
388388

389389
importDecl struct{ spec *ast.ImportSpec }
390390
constDecl struct {
391-
spec *ast.ValueSpec
392-
iota int
393-
typ ast.Expr
394-
init []ast.Expr
391+
spec *ast.ValueSpec
392+
iota int
393+
typ ast.Expr
394+
init []ast.Expr
395+
inherited bool
395396
}
396397
varDecl struct{ spec *ast.ValueSpec }
397398
typeDecl struct{ spec *ast.TypeSpec }
@@ -424,14 +425,17 @@ func (check *Checker) walkDecl(d ast.Decl, f func(decl)) {
424425
switch d.Tok {
425426
case token.CONST:
426427
// determine which initialization expressions to use
428+
inherited := true
427429
switch {
428430
case s.Type != nil || len(s.Values) > 0:
429431
last = s
432+
inherited = false
430433
case last == nil:
431434
last = new(ast.ValueSpec) // make sure last exists
435+
inherited = false
432436
}
433437
check.arityMatch(s, last)
434-
f(constDecl{spec: s, iota: iota, init: last.Values, typ: last.Type})
438+
f(constDecl{spec: s, iota: iota, typ: last.Type, init: last.Values, inherited: inherited})
435439
case token.VAR:
436440
check.arityMatch(s, nil)
437441
f(varDecl{s})
@@ -451,12 +455,16 @@ func (check *Checker) walkDecl(d ast.Decl, f func(decl)) {
451455
}
452456
}
453457

454-
func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) {
458+
func (check *Checker) constDecl(obj *Const, typ, init ast.Expr, inherited bool) {
455459
assert(obj.typ == nil)
456460

457461
// use the correct value of iota
458-
defer func(iota constant.Value) { check.iota = iota }(check.iota)
462+
defer func(iota constant.Value, errpos positioner) {
463+
check.iota = iota
464+
check.errpos = errpos
465+
}(check.iota, check.errpos)
459466
check.iota = obj.val
467+
check.errpos = nil
460468

461469
// provide valid constant value under all circumstances
462470
obj.val = constant.MakeUnknown()
@@ -479,6 +487,15 @@ func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) {
479487
// check initialization
480488
var x operand
481489
if init != nil {
490+
if inherited {
491+
// The initialization expression is inherited from a previous
492+
// constant declaration, and (error) positions refer to that
493+
// expression and not the current constant declaration. Use
494+
// the constant identifier position for any errors during
495+
// init expression evaluation since that is all we have
496+
// (see issues #42991, #42992).
497+
check.errpos = atPos(obj.pos)
498+
}
482499
check.expr(&x, init)
483500
}
484501
check.initConst(obj, &x)
@@ -753,7 +770,7 @@ func (check *Checker) declStmt(d ast.Decl) {
753770
init = d.init[i]
754771
}
755772

756-
check.constDecl(obj, d.typ, init)
773+
check.constDecl(obj, d.typ, init, d.inherited)
757774
}
758775

759776
// process function literals in init expressions before scope changes

src/go/types/errors.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,18 @@ func (check *Checker) err(err error) {
8989
return
9090
}
9191

92+
if check.errpos != nil && isInternal {
93+
// If we have an internal error and the errpos override is set, use it to
94+
// augment our error positioning.
95+
// TODO(rFindley) we may also want to augment the error message and refer
96+
// to the position (pos) in the original expression.
97+
span := spanOf(check.errpos)
98+
e.Pos = span.pos
99+
e.go116start = span.start
100+
e.go116end = span.end
101+
err = e
102+
}
103+
92104
if check.firstErr == nil {
93105
check.firstErr = err
94106
}
@@ -111,15 +123,15 @@ func (check *Checker) err(err error) {
111123
}
112124

113125
func (check *Checker) newError(at positioner, code errorCode, soft bool, msg string) error {
114-
ext := spanOf(at)
126+
span := spanOf(at)
115127
return Error{
116128
Fset: check.fset,
117-
Pos: ext.pos,
129+
Pos: span.pos,
118130
Msg: msg,
119131
Soft: soft,
120132
go116code: code,
121-
go116start: ext.start,
122-
go116end: ext.end,
133+
go116start: span.start,
134+
go116end: span.end,
123135
}
124136
}
125137

src/go/types/resolver.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ import (
1717

1818
// A declInfo describes a package-level const, type, var, or func declaration.
1919
type declInfo struct {
20-
file *Scope // scope of file containing this declaration
21-
lhs []*Var // lhs of n:1 variable declarations, or nil
22-
typ ast.Expr // type, or nil
23-
init ast.Expr // init/orig expression, or nil
24-
fdecl *ast.FuncDecl // func declaration, or nil
25-
alias bool // type alias declaration
20+
file *Scope // scope of file containing this declaration
21+
lhs []*Var // lhs of n:1 variable declarations, or nil
22+
typ ast.Expr // type, or nil
23+
init ast.Expr // init/orig expression, or nil
24+
inherited bool // if set, the init expression is inherited from a previous constant declaration
25+
fdecl *ast.FuncDecl // func declaration, or nil
26+
alias bool // type alias declaration
2627

2728
// The deps field tracks initialization expression dependencies.
2829
deps map[Object]bool // lazily initialized
@@ -323,7 +324,7 @@ func (check *Checker) collectObjects() {
323324
init = d.init[i]
324325
}
325326

326-
d := &declInfo{file: fileScope, typ: d.typ, init: init}
327+
d := &declInfo{file: fileScope, typ: d.typ, init: init, inherited: d.inherited}
327328
check.declarePkgObj(name, obj, d)
328329
}
329330

src/go/types/testdata/constdecl.src

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,35 @@ func _() {
107107
const x, y, z = 0, 1, unsafe.Sizeof(func() { _ = x /* ERROR "undeclared name" */ + y /* ERROR "undeclared name" */ + z /* ERROR "undeclared name" */ })
108108
}
109109

110+
// Test cases for errors in inherited constant initialization expressions.
111+
// Errors related to inherited initialization expressions must appear at
112+
// the constant identifier being declared, not at the original expression
113+
// (issues #42991, #42992).
114+
const (
115+
_ byte = 255 + iota
116+
/* some gap */
117+
_ // ERROR overflows
118+
/* some gap */
119+
/* some gap */ _ /* ERROR overflows */; _ /* ERROR overflows */
120+
/* some gap */
121+
_ = 255 + iota
122+
_ = byte /* ERROR overflows */ (255) + iota
123+
_ /* ERROR overflows */
124+
)
125+
126+
// Test cases from issue.
127+
const (
128+
ok = byte(iota + 253)
129+
bad
130+
barn
131+
bard // ERROR cannot convert
132+
)
133+
134+
const (
135+
c = len([1 - iota]int{})
136+
d
137+
e // ERROR invalid array length
138+
f // ERROR invalid array length
139+
)
140+
110141
// TODO(gri) move extra tests from testdata/const0.src into here

0 commit comments

Comments
 (0)