From 1006f703ffc191dbcce3135f1992f1a24a50cb52 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 12 Feb 2018 16:42:31 -0800 Subject: [PATCH] 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/93438 Reviewed-by: Alan Donovan --- src/go/types/builtins.go | 6 +++++- src/go/types/predicates.go | 4 +++- src/go/types/sizes.go | 3 ++- src/go/types/testdata/importC.src | 21 ++++++++++++++++++++- src/go/types/type.go | 2 ++ src/go/types/typexpr.go | 9 ++++++--- 6 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index f22851e240..785daec331 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -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." if !check.hasCallOrRecv { mode = constant_ - val = constant.MakeInt64(t.len) + if t.len >= 0 { + val = constant.MakeInt64(t.len) + } else { + val = constant.MakeUnknown() + } } case *Slice, *Chan: diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go index 3aa4878cce..1ca146f590 100644 --- a/src/go/types/predicates.go +++ b/src/go/types/predicates.go @@ -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 // and the same array length. 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: diff --git a/src/go/types/sizes.go b/src/go/types/sizes.go index 0821a61359..eb274799f4 100644 --- a/src/go/types/sizes.go +++ b/src/go/types/sizes.go @@ -132,9 +132,10 @@ func (s *StdSizes) Sizeof(T Type) int64 { } case *Array: n := t.len - if n == 0 { + if n <= 0 { return 0 } + // n > 0 a := s.Alignof(t.elem) z := s.Sizeof(t.elem) return align(z, a)*(n-1) + z diff --git a/src/go/types/testdata/importC.src b/src/go/types/testdata/importC.src index f50f7f33d3..f55be2d5c5 100644 --- a/src/go/types/testdata/importC.src +++ b/src/go/types/testdata/importC.src @@ -20,7 +20,7 @@ type T struct { Ordinal int } -func f(args []T) { +func _(args []T) { var s string for i, v := range args { cname := C.CString(v.Name) @@ -33,3 +33,22 @@ type CType C.Type const _ CType = C.X // no error due to invalid constant type 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 +} diff --git a/src/go/types/type.go b/src/go/types/type.go index 50e3c6e4d0..afdbb680f8 100644 --- a/src/go/types/type.go +++ b/src/go/types/type.go @@ -97,9 +97,11 @@ type Array struct { } // 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} } // Len returns the length of array a. +// A negative result indicates an unknown length. func (a *Array) Len() int64 { return a.len } // Elem returns element type of array a. diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index aedd71e918..e86834efdd 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -380,6 +380,9 @@ func (check *Checker) typOrNil(e ast.Expr) Type { 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 { var x operand check.expr(&x, e) @@ -387,7 +390,7 @@ func (check *Checker) arrayLength(e ast.Expr) int64 { if x.mode != invalid { check.errorf(x.pos(), "array length %s must be constant", &x) } - return 0 + return -1 } if isUntyped(x.typ) || isInteger(x.typ) { if val := constant.ToInt(x.val); val.Kind() == constant.Int { @@ -396,12 +399,12 @@ func (check *Checker) arrayLength(e ast.Expr) int64 { return n } check.errorf(x.pos(), "invalid array length %s", &x) - return 0 + return -1 } } } 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) { -- 2.50.0