aboutsummaryrefslogtreecommitdiffhomepage
path: root/compiler/asserts.go
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2021-08-15 02:29:27 +0200
committerRon Evans <[email protected]>2021-08-30 09:18:58 +0200
commit255f35671d3eef11d6cfe0020aeda5c17985657d (patch)
tree6e24e118a4f658f044ed2fe8b20f24fcf161975f /compiler/asserts.go
parent8e88e560a1eb76558364ee3400b3bfe0e4c054c6 (diff)
downloadtinygo-255f35671d3eef11d6cfe0020aeda5c17985657d.tar.gz
tinygo-255f35671d3eef11d6cfe0020aeda5c17985657d.zip
compiler: add support for new language features of Go 1.17
Diffstat (limited to 'compiler/asserts.go')
-rw-r--r--compiler/asserts.go49
1 files changed, 49 insertions, 0 deletions
diff --git a/compiler/asserts.go b/compiler/asserts.go
index 01e7a4021..381900bb6 100644
--- a/compiler/asserts.go
+++ b/compiler/asserts.go
@@ -101,6 +101,55 @@ func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lo
b.createRuntimeAssert(outOfBounds, "slice", "slicePanic")
}
+// createSliceToArrayPointerCheck adds a check for slice-to-array pointer
+// conversions. This conversion was added in Go 1.17. For details, see:
+// https://tip.golang.org/ref/spec#Conversions_from_slice_to_array_pointer
+func (b *builder) createSliceToArrayPointerCheck(sliceLen llvm.Value, arrayLen int64) {
+ // From the spec:
+ // > If the length of the slice is less than the length of the array, a
+ // > run-time panic occurs.
+ arrayLenValue := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
+ isLess := b.CreateICmp(llvm.IntULT, sliceLen, arrayLenValue, "")
+ b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic")
+}
+
+// createUnsafeSliceCheck inserts a runtime check used for unsafe.Slice. This
+// function must panic if the ptr/len parameters are invalid.
+func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Basic) {
+ // From the documentation of unsafe.Slice:
+ // > At run time, if len is negative, or if ptr is nil and len is not
+ // > zero, a run-time panic occurs.
+ // However, in practice, it is also necessary to check that the length is
+ // not too big that a GEP wouldn't be possible without wrapping the pointer.
+ // These two checks (non-negative and not too big) can be merged into one
+ // using an unsiged greater than.
+
+ // Make sure the len value is at least as big as a uintptr.
+ if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
+ if lenType.Info()&types.IsUnsigned != 0 {
+ len = b.CreateZExt(len, b.uintptrType, "")
+ } else {
+ len = b.CreateSExt(len, b.uintptrType, "")
+ }
+ }
+
+ // Determine the maximum slice size, and therefore the maximum value of the
+ // len parameter.
+ maxSize := b.maxSliceSize(ptr.Type().ElementType())
+ maxSizeValue := llvm.ConstInt(len.Type(), maxSize, false)
+
+ // Do the check. By using unsigned greater than for the length check, signed
+ // negative values are also checked (which are very large numbers when
+ // interpreted as signed values).
+ zero := llvm.ConstInt(len.Type(), 0, false)
+ lenOutOfBounds := b.CreateICmp(llvm.IntUGT, len, maxSizeValue, "")
+ ptrIsNil := b.CreateICmp(llvm.IntEQ, ptr, llvm.ConstNull(ptr.Type()), "")
+ lenIsNotZero := b.CreateICmp(llvm.IntNE, len, zero, "")
+ assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "")
+ assert = b.CreateOr(assert, lenOutOfBounds, "")
+ b.createRuntimeAssert(assert, "unsafe.Slice", "unsafeSlicePanic")
+}
+
// createChanBoundsCheck creates a bounds check before creating a new channel to
// check that the value is not too big for runtime.chanMake.
func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, bufSizeType *types.Basic, pos token.Pos) {