go/types, types2: better error message for invalid array length

If an invalid array length is just an identifier, mention
"array length" so that it's clear this is an invalid array
declaration and not a (invalid) generic type declaration.

Fixes #51145.

Change-Id: I8878cbb6c7b1277fc0a9a014712ec8d55499c5c7
Reviewed-on: https://go-review.googlesource.com/c/go/+/385255
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Robert Griesemer 2022-02-11 13:16:52 -08:00
parent bcee121ae4
commit badba359da
8 changed files with 75 additions and 25 deletions

View file

@ -9,7 +9,7 @@ const L = 10
type (
_ [L]struct{}
_ [A /* ERROR undeclared name A for array length */ ]struct{}
_ [B /* ERROR not an expression */ ]struct{}
_ [B /* ERROR invalid array length B */ ]struct{}
_[A any] struct{}
B int

View file

@ -0,0 +1,18 @@
// Copyright 2022 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 p
import "fmt"
type (
_ [fmt /* ERROR invalid array length fmt */ ]int
_ [float64 /* ERROR invalid array length float64 */ ]int
_ [f /* ERROR invalid array length f */ ]int
_ [nil /* ERROR invalid array length nil */ ]int
)
func f()
var _ fmt.Stringer // use fmt

View file

@ -502,12 +502,20 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
// and returns the constant length >= 0, or a value < 0
// to indicate an error (and thus an unknown length).
func (check *Checker) arrayLength(e syntax.Expr) int64 {
// If e is an undeclared identifier, the array declaration might be an
// attempt at a parameterized type declaration with missing constraint.
// Provide a better error message than just "undeclared name: X".
if name, _ := e.(*syntax.Name); name != nil && check.lookup(name.Value) == nil {
check.errorf(name, "undeclared name %s for array length", name.Value)
return -1
// If e is an identifier, the array declaration might be an
// attempt at a parameterized type declaration with missing
// constraint. Provide an error message that mentions array
// length.
if name, _ := e.(*syntax.Name); name != nil {
obj := check.lookup(name.Value)
if obj == nil {
check.errorf(name, "undeclared name %s for array length", name.Value)
return -1
}
if _, ok := obj.(*Const); !ok {
check.errorf(name, "invalid array length %s", name.Value)
return -1
}
}
var x operand

View file

@ -98,13 +98,10 @@ const (
// _InvalidDeclCycle occurs when a declaration cycle is not valid.
//
// Example:
// import "unsafe"
//
// type T struct {
// a [n]int
// type S struct {
// S
// }
//
// var n = unsafe.Sizeof(T{})
_InvalidDeclCycle
// _InvalidTypeCycle occurs when a cycle in type definitions results in a

View file

@ -9,7 +9,7 @@ const L = 10
type (
_ [L]struct{}
_ [A /* ERROR undeclared name A for array length */ ]struct{}
_ [B /* ERROR not an expression */ ]struct{}
_ [B /* ERROR invalid array length B */ ]struct{}
_[A any] struct{}
B int

View file

@ -0,0 +1,18 @@
// Copyright 2022 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 p
import "fmt"
type (
_ [fmt /* ERROR invalid array length fmt */ ]int
_ [float64 /* ERROR invalid array length float64 */ ]int
_ [f /* ERROR invalid array length f */ ]int
_ [nil /* ERROR invalid array length nil */ ]int
)
func f()
var _ fmt.Stringer // use fmt

View file

@ -487,12 +487,20 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (re
// and returns the constant length >= 0, or a value < 0
// to indicate an error (and thus an unknown length).
func (check *Checker) arrayLength(e ast.Expr) int64 {
// If e is an undeclared identifier, the array declaration might be an
// attempt at a parameterized type declaration with missing constraint.
// Provide a better error message than just "undeclared name: X".
if name, _ := e.(*ast.Ident); name != nil && check.lookup(name.Name) == nil {
check.errorf(name, _InvalidArrayLen, "undeclared name %s for array length", name.Name)
return -1
// If e is an identifier, the array declaration might be an
// attempt at a parameterized type declaration with missing
// constraint. Provide an error message that mentions array
// length.
if name, _ := e.(*ast.Ident); name != nil {
obj := check.lookup(name.Name)
if obj == nil {
check.errorf(name, _InvalidArrayLen, "undeclared name %s for array length", name.Name)
return -1
}
if _, ok := obj.(*Const); !ok {
check.errorf(name, _InvalidArrayLen, "invalid array length %s", name.Name)
return -1
}
}
var x operand

View file

@ -6,12 +6,13 @@
package main
var a [10]int // ok
var b [1e1]int // ok
var c [1.5]int // ERROR "truncated|must be integer"
var d ["abc"]int // ERROR "invalid array bound|not numeric|must be integer"
var e [nil]int // ERROR "use of untyped nil|invalid array bound|not numeric|must be constant"
var f [e]int // ok: error already reported for e
var a [10]int // ok
var b [1e1]int // ok
var c [1.5]int // ERROR "truncated|must be integer"
var d ["abc"]int // ERROR "invalid array bound|not numeric|must be integer"
var e [nil]int // ERROR "use of untyped nil|invalid array (bound|length)|not numeric|must be constant"
// var f [e]int // ok with Go 1.17 because an error was reported for e; leads to an error for Go 1.18
var f [ee]int // ERROR "undefined|undeclared"
var g [1 << 65]int // ERROR "array bound is too large|overflows|must be integer"
var h [len(a)]int // ok