Commit 1006f703 authored by Robert Griesemer's avatar Robert Griesemer

go/types: better handle arrays whose length expression is invalid

While doing that, establish a negative value as signal for unknown
array lengths and adjust various array-length processing code to
handle that case.

Fixes #23712.

Change-Id: Icf488faaf972638b42b22d4b4607d1c512c8fc2c
Reviewed-on: https://go-review.googlesource.com/93438Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent ecba3714
...@@ -158,7 +158,11 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b ...@@ -158,7 +158,11 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
// function calls; in this case s is not evaluated." // function calls; in this case s is not evaluated."
if !check.hasCallOrRecv { if !check.hasCallOrRecv {
mode = constant_ mode = constant_
val = constant.MakeInt64(t.len) if t.len >= 0 {
val = constant.MakeInt64(t.len)
} else {
val = constant.MakeUnknown()
}
} }
case *Slice, *Chan: case *Slice, *Chan:
......
...@@ -150,7 +150,9 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { ...@@ -150,7 +150,9 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
// Two array types are identical if they have identical element types // Two array types are identical if they have identical element types
// and the same array length. // and the same array length.
if y, ok := y.(*Array); ok { if y, ok := y.(*Array); ok {
return x.len == y.len && identical(x.elem, y.elem, cmpTags, p) // If one or both array lengths are unknown (< 0) due to some error,
// assume they are the same to avoid spurious follow-on errors.
return (x.len < 0 || y.len < 0 || x.len == y.len) && identical(x.elem, y.elem, cmpTags, p)
} }
case *Slice: case *Slice:
......
...@@ -132,9 +132,10 @@ func (s *StdSizes) Sizeof(T Type) int64 { ...@@ -132,9 +132,10 @@ func (s *StdSizes) Sizeof(T Type) int64 {
} }
case *Array: case *Array:
n := t.len n := t.len
if n == 0 { if n <= 0 {
return 0 return 0
} }
// n > 0
a := s.Alignof(t.elem) a := s.Alignof(t.elem)
z := s.Sizeof(t.elem) z := s.Sizeof(t.elem)
return align(z, a)*(n-1) + z return align(z, a)*(n-1) + z
......
...@@ -20,7 +20,7 @@ type T struct { ...@@ -20,7 +20,7 @@ type T struct {
Ordinal int Ordinal int
} }
func f(args []T) { func _(args []T) {
var s string var s string
for i, v := range args { for i, v := range args {
cname := C.CString(v.Name) cname := C.CString(v.Name)
...@@ -33,3 +33,22 @@ type CType C.Type ...@@ -33,3 +33,22 @@ type CType C.Type
const _ CType = C.X // no error due to invalid constant type const _ CType = C.X // no error due to invalid constant type
const _ = C.X const _ = C.X
// Test cases extracted from issue #23712.
func _() {
var a [C.ArrayLength]byte
_ = a[0] // no index out of bounds error here
}
// Additional tests to verify fix for #23712.
func _() {
var a [C.ArrayLength1]byte
_ = 1 / len(a) // no division by zero error here and below
_ = 1 / cap(a)
_ = uint(unsafe.Sizeof(a)) // must not be negative
var b [C.ArrayLength2]byte
a = b // should be valid
}
...@@ -97,9 +97,11 @@ type Array struct { ...@@ -97,9 +97,11 @@ type Array struct {
} }
// NewArray returns a new array type for the given element type and length. // NewArray returns a new array type for the given element type and length.
// A negative length indicates an unknown length.
func NewArray(elem Type, len int64) *Array { return &Array{len, elem} } func NewArray(elem Type, len int64) *Array { return &Array{len, elem} }
// Len returns the length of array a. // Len returns the length of array a.
// A negative result indicates an unknown length.
func (a *Array) Len() int64 { return a.len } func (a *Array) Len() int64 { return a.len }
// Elem returns element type of array a. // Elem returns element type of array a.
......
...@@ -380,6 +380,9 @@ func (check *Checker) typOrNil(e ast.Expr) Type { ...@@ -380,6 +380,9 @@ func (check *Checker) typOrNil(e ast.Expr) Type {
return Typ[Invalid] return Typ[Invalid]
} }
// arrayLength type-checks the array length expression e
// 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 { func (check *Checker) arrayLength(e ast.Expr) int64 {
var x operand var x operand
check.expr(&x, e) check.expr(&x, e)
...@@ -387,7 +390,7 @@ func (check *Checker) arrayLength(e ast.Expr) int64 { ...@@ -387,7 +390,7 @@ func (check *Checker) arrayLength(e ast.Expr) int64 {
if x.mode != invalid { if x.mode != invalid {
check.errorf(x.pos(), "array length %s must be constant", &x) check.errorf(x.pos(), "array length %s must be constant", &x)
} }
return 0 return -1
} }
if isUntyped(x.typ) || isInteger(x.typ) { if isUntyped(x.typ) || isInteger(x.typ) {
if val := constant.ToInt(x.val); val.Kind() == constant.Int { if val := constant.ToInt(x.val); val.Kind() == constant.Int {
...@@ -396,12 +399,12 @@ func (check *Checker) arrayLength(e ast.Expr) int64 { ...@@ -396,12 +399,12 @@ func (check *Checker) arrayLength(e ast.Expr) int64 {
return n return n
} }
check.errorf(x.pos(), "invalid array length %s", &x) check.errorf(x.pos(), "invalid array length %s", &x)
return 0 return -1
} }
} }
} }
check.errorf(x.pos(), "array length %s must be integer", &x) check.errorf(x.pos(), "array length %s must be integer", &x)
return 0 return -1
} }
func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) { func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment