compiler, runtime: allow slice to array pointer conversion

Panic if the slice is too short.

For golang/go#395

Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/338630
This commit is contained in:
Ian Lance Taylor 2021-07-30 17:19:42 -07:00
parent 06d0437d4a
commit 7459bfa8a3
7 changed files with 159 additions and 3 deletions

View File

@ -1,4 +1,4 @@
ad667e7c70cea9fa5730660d72ad891b5753eb62 0a4d612e6b211780b294717503fc739bbd1f509c
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.

View File

@ -3866,11 +3866,12 @@ Type_conversion_expression::do_traverse(Traverse* traverse)
return TRAVERSE_CONTINUE; return TRAVERSE_CONTINUE;
} }
// Convert to a constant at lowering time. // Convert to a constant at lowering time. Also lower conversions
// from slice to pointer-to-array, as they can panic.
Expression* Expression*
Type_conversion_expression::do_lower(Gogo*, Named_object*, Type_conversion_expression::do_lower(Gogo*, Named_object*,
Statement_inserter*, int) Statement_inserter* inserter, int)
{ {
Type* type = this->type_; Type* type = this->type_;
Expression* val = this->expr_; Expression* val = this->expr_;
@ -3958,6 +3959,54 @@ Type_conversion_expression::do_lower(Gogo*, Named_object*,
} }
} }
if (type->points_to() != NULL
&& type->points_to()->array_type() != NULL
&& !type->points_to()->is_slice_type()
&& val->type()->is_slice_type())
{
Temporary_statement* val_temp = NULL;
if (!val->is_multi_eval_safe())
{
val_temp = Statement::make_temporary(val->type(), NULL, location);
inserter->insert(val_temp);
val = Expression::make_set_and_use_temporary(val_temp, val,
location);
}
Type* int_type = Type::lookup_integer_type("int");
Temporary_statement* vallen_temp =
Statement::make_temporary(int_type, NULL, location);
inserter->insert(vallen_temp);
Expression* arrlen = type->points_to()->array_type()->length();
Expression* vallen =
Expression::make_slice_info(val, Expression::SLICE_INFO_LENGTH,
location);
vallen = Expression::make_set_and_use_temporary(vallen_temp, vallen,
location);
Expression* cond = Expression::make_binary(OPERATOR_GT, arrlen, vallen,
location);
vallen = Expression::make_temporary_reference(vallen_temp, location);
Expression* panic = Runtime::make_call(Runtime::PANIC_SLICE_CONVERT,
location, 2, arrlen, vallen);
Expression* nil = Expression::make_nil(location);
Expression* check = Expression::make_conditional(cond, panic, nil,
location);
if (val_temp == NULL)
val = val->copy();
else
val = Expression::make_temporary_reference(val_temp, location);
Expression* ptr =
Expression::make_slice_info(val, Expression::SLICE_INFO_VALUE_POINTER,
location);
ptr = Expression::make_unsafe_cast(type, ptr, location);
return Expression::make_compound(check, ptr, location);
}
return this; return this;
} }

View File

@ -582,6 +582,11 @@ DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_C, "runtime.goPanicExtendSlice3C",
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_C_U, "runtime.goPanicExtendSlice3CU", DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_C_U, "runtime.goPanicExtendSlice3CU",
P2(UINT64, INT), R0()) P2(UINT64, INT), R0())
// Panic for conversion of slice to pointer-to-array if the slice is
// too short.
DEF_GO_RUNTIME(PANIC_SLICE_CONVERT, "runtime.goPanicSliceConvert",
P2(INT, INT), R0())
// Remove helper macros. // Remove helper macros.
#undef ABFT6 #undef ABFT6
#undef ABFT2 #undef ABFT2

View File

@ -842,6 +842,13 @@ Type::are_convertible(const Type* lhs, const Type* rhs, std::string* reason)
return true; return true;
} }
// A slice may be converted to a pointer-to-array.
if (rhs->is_slice_type()
&& lhs->points_to() != NULL
&& lhs->points_to()->array_type() != NULL
&& !lhs->points_to()->is_slice_type())
return true;
// An unsafe.Pointer type may be converted to any pointer type or to // An unsafe.Pointer type may be converted to any pointer type or to
// a type whose underlying type is uintptr, and vice-versa. // a type whose underlying type is uintptr, and vice-versa.
if (lhs->is_unsafe_pointer_type() if (lhs->is_unsafe_pointer_type()

View File

@ -0,0 +1,86 @@
// run
// Copyright 2020 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.
// Test conversion from slice to array pointer.
package main
func wantPanic(fn func(), s string) {
defer func() {
err := recover()
if err == nil {
panic("expected panic")
}
if got := err.(error).Error(); got != s {
panic("expected panic " + s + " got " + got)
}
}()
fn()
}
func main() {
s := make([]byte, 8, 10)
if p := (*[8]byte)(s); &p[0] != &s[0] {
panic("*[8]byte conversion failed")
}
wantPanic(
func() {
_ = (*[9]byte)(s)
},
"runtime error: cannot convert slice with length 8 to pointer to array with length 9",
)
var n []byte
if p := (*[0]byte)(n); p != nil {
panic("nil slice converted to *[0]byte should be nil")
}
z := make([]byte, 0)
if p := (*[0]byte)(z); p == nil {
panic("empty slice converted to *[0]byte should be non-nil")
}
// Test with named types
type Slice []int
type Int4 [4]int
type PInt4 *[4]int
ii := make(Slice, 4)
if p := (*Int4)(ii); &p[0] != &ii[0] {
panic("*Int4 conversion failed")
}
if p := PInt4(ii); &p[0] != &ii[0] {
panic("PInt4 conversion failed")
}
}
// test static variable conversion
var (
ss = make([]string, 10)
s5 = (*[5]string)(ss)
s10 = (*[10]string)(ss)
ns []string
ns0 = (*[0]string)(ns)
zs = make([]string, 0)
zs0 = (*[0]string)(zs)
)
func init() {
if &ss[0] != &s5[0] {
panic("s5 conversion failed")
}
if &ss[0] != &s10[0] {
panic("s5 conversion failed")
}
if ns0 != nil {
panic("ns0 should be nil")
}
if zs0 == nil {
panic("zs0 should not be nil")
}
}

View File

@ -175,6 +175,7 @@ const (
boundsSlice3B // s[?:x:y], 0 <= x <= y failed (but boundsSlice3A didn't happen) boundsSlice3B // s[?:x:y], 0 <= x <= y failed (but boundsSlice3A didn't happen)
boundsSlice3C // s[x:y:?], 0 <= x <= y failed (but boundsSlice3A/B didn't happen) boundsSlice3C // s[x:y:?], 0 <= x <= y failed (but boundsSlice3A/B didn't happen)
boundsConvert // (*[x]T)(s), 0 <= x <= len(s) failed
// Note: in the above, len(s) and cap(s) are stored in y // Note: in the above, len(s) and cap(s) are stored in y
) )
@ -190,6 +191,7 @@ var boundsErrorFmts = [...]string{
boundsSlice3Acap: "slice bounds out of range [::%x] with capacity %y", boundsSlice3Acap: "slice bounds out of range [::%x] with capacity %y",
boundsSlice3B: "slice bounds out of range [:%x:%y]", boundsSlice3B: "slice bounds out of range [:%x:%y]",
boundsSlice3C: "slice bounds out of range [%x:%y:]", boundsSlice3C: "slice bounds out of range [%x:%y:]",
boundsConvert: "cannot convert slice with length %y to pointer to array with length %x",
} }
// boundsNegErrorFmts are overriding formats if x is negative. In this case there's no need to report y. // boundsNegErrorFmts are overriding formats if x is negative. In this case there's no need to report y.

View File

@ -38,6 +38,7 @@ import (
//go:linkname goPanicSlice3BU //go:linkname goPanicSlice3BU
//go:linkname goPanicSlice3C //go:linkname goPanicSlice3C
//go:linkname goPanicSlice3CU //go:linkname goPanicSlice3CU
//go:linkname goPanicSliceConvert
//go:linkname panicshift //go:linkname panicshift
//go:linkname panicdivide //go:linkname panicdivide
//go:linkname panicmem //go:linkname panicmem
@ -175,6 +176,12 @@ func goPanicSlice3CU(x uint, y int) {
panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3C}) panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3C})
} }
// failures in the conversion (*[x]T)s, 0 <= x <= y, x == cap(s)
func goPanicSliceConvert(x int, y int) {
panicCheck1(getcallerpc(), "slice length too short to convert to pointer to array")
panic(boundsError{x: int64(x), signed: true, y: y, code: boundsConvert})
}
var shiftError = error(errorString("negative shift amount")) var shiftError = error(errorString("negative shift amount"))
func panicshift() { func panicshift() {