compiler: check slice cap instead of len during slicing

When doing a slice operation on a slice, use the capacity value instead
of the length. Of course, for strings and arrays, the slice operation
checks the length because there is no capacity. But according to the
spec, this check should be based on cap for slice instead of len:

> For slices, the upper index bound is the slice capacity cap(a) rather
> than the length.

https://golang.org/ref/spec#Slice_expressions

Fixes: https://github.com/aykevl/tinygo/issues/65
Этот коммит содержится в:
Ayke van Laethem 2018-10-30 15:50:22 +01:00
родитель 317b12b8c0
коммит 0314a487ff
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
2 изменённых файлов: 8 добавлений и 8 удалений

Просмотреть файл

@ -2237,7 +2237,7 @@ func (c *Compiler) emitBoundsCheck(frame *Frame, arrayLen, index llvm.Value, ind
} }
} }
func (c *Compiler) emitSliceBoundsCheck(frame *Frame, length, low, high llvm.Value) { func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.Value) {
if frame.fn.IsNoBounds() { if frame.fn.IsNoBounds() {
// The //go:nobounds pragma was added to the function to avoid bounds // The //go:nobounds pragma was added to the function to avoid bounds
// checking. // checking.
@ -2251,9 +2251,9 @@ func (c *Compiler) emitSliceBoundsCheck(frame *Frame, length, low, high llvm.Val
if high.Type().IntTypeWidth() < 64 { if high.Type().IntTypeWidth() < 64 {
high = c.builder.CreateSExt(high, c.ctx.Int64Type(), "") high = c.builder.CreateSExt(high, c.ctx.Int64Type(), "")
} }
c.createRuntimeCall("sliceBoundsCheckLong", []llvm.Value{length, low, high}, "") c.createRuntimeCall("sliceBoundsCheckLong", []llvm.Value{capacity, low, high}, "")
} else { } else {
c.createRuntimeCall("sliceBoundsCheck", []llvm.Value{length, low, high}, "") c.createRuntimeCall("sliceBoundsCheck", []llvm.Value{capacity, low, high}, "")
} }
} }
@ -2684,7 +2684,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
high = oldLen high = oldLen
} }
c.emitSliceBoundsCheck(frame, oldLen, low, high) c.emitSliceBoundsCheck(frame, oldCap, low, high)
if c.targetData.TypeAllocSize(low.Type()) > c.targetData.TypeAllocSize(c.lenType) { if c.targetData.TypeAllocSize(low.Type()) > c.targetData.TypeAllocSize(c.lenType) {
low = c.builder.CreateTrunc(low, c.lenType, "") low = c.builder.CreateTrunc(low, c.lenType, "")

Просмотреть файл

@ -38,15 +38,15 @@ func lookupBoundsCheckLong(length lenType, index int64) {
} }
// Check for bounds in *ssa.Slice. // Check for bounds in *ssa.Slice.
func sliceBoundsCheck(length lenType, low, high uint) { func sliceBoundsCheck(capacity lenType, low, high uint) {
if !(0 <= low && low <= high && high <= uint(length)) { if !(0 <= low && low <= high && high <= uint(capacity)) {
runtimePanic("slice out of range") runtimePanic("slice out of range")
} }
} }
// Check for bounds in *ssa.Slice. Supports 64-bit indexes. // Check for bounds in *ssa.Slice. Supports 64-bit indexes.
func sliceBoundsCheckLong(length lenType, low, high uint64) { func sliceBoundsCheckLong(capacity lenType, low, high uint64) {
if !(0 <= low && low <= high && high <= uint64(length)) { if !(0 <= low && low <= high && high <= uint64(capacity)) {
runtimePanic("slice out of range") runtimePanic("slice out of range")
} }
} }