aboutsummaryrefslogtreecommitdiffhomepage
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
parent8e88e560a1eb76558364ee3400b3bfe0e4c054c6 (diff)
downloadtinygo-255f35671d3eef11d6cfe0020aeda5c17985657d.tar.gz
tinygo-255f35671d3eef11d6cfe0020aeda5c17985657d.zip
compiler: add support for new language features of Go 1.17
-rw-r--r--compiler/asserts.go49
-rw-r--r--compiler/compiler.go43
-rw-r--r--compiler/compiler_test.go19
-rw-r--r--compiler/testdata/go1.17.go41
-rw-r--r--compiler/testdata/go1.17.ll136
-rw-r--r--go.mod4
-rw-r--r--go.sum23
-rw-r--r--main_test.go9
-rw-r--r--src/runtime/panic.go12
-rw-r--r--testdata/go1.17.go34
-rw-r--r--testdata/go1.17.txt3
11 files changed, 361 insertions, 12 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) {
diff --git a/compiler/compiler.go b/compiler/compiler.go
index d78115351..185859dec 100644
--- a/compiler/compiler.go
+++ b/compiler/compiler.go
@@ -1293,6 +1293,38 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
case "ssa:wrapnilchk":
// TODO: do an actual nil check?
return argValues[0], nil
+
+ // Builtins from the unsafe package.
+ case "Add": // unsafe.Add
+ // This is basically just a GEP operation.
+ // Note: the pointer is always of type *i8.
+ ptr := argValues[0]
+ len := argValues[1]
+ return b.CreateGEP(ptr, []llvm.Value{len}, ""), nil
+ case "Slice": // unsafe.Slice
+ // This creates a slice from a pointer and a length.
+ // Note that the exception mentioned in the documentation (if the
+ // pointer and length are nil, the slice is also nil) is trivially
+ // already the case.
+ ptr := argValues[0]
+ len := argValues[1]
+ slice := llvm.Undef(b.ctx.StructType([]llvm.Type{
+ ptr.Type(),
+ b.uintptrType,
+ b.uintptrType,
+ }, false))
+ b.createUnsafeSliceCheck(ptr, len, argTypes[1].Underlying().(*types.Basic))
+ if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
+ // Too small, zero-extend len.
+ len = b.CreateZExt(len, b.uintptrType, "")
+ } else if len.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() {
+ // Too big, truncate len.
+ len = b.CreateTrunc(len, b.uintptrType, "")
+ }
+ slice = b.CreateInsertValue(slice, ptr, 0, "")
+ slice = b.CreateInsertValue(slice, len, 1, "")
+ slice = b.CreateInsertValue(slice, len, 2, "")
+ return slice, nil
default:
return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
}
@@ -1928,6 +1960,17 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
default:
return llvm.Value{}, b.makeError(expr.Pos(), "unknown slice type: "+typ.String())
}
+ case *ssa.SliceToArrayPointer:
+ // Conversion from a slice to an array pointer, as the name clearly
+ // says. This requires a runtime check to make sure the slice is at
+ // least as big as the array.
+ slice := b.getValue(expr.X)
+ sliceLen := b.CreateExtractValue(slice, 1, "")
+ arrayLen := expr.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Len()
+ b.createSliceToArrayPointerCheck(sliceLen, arrayLen)
+ ptr := b.CreateExtractValue(slice, 0, "")
+ ptr = b.CreateBitCast(ptr, b.getLLVMType(expr.Type()), "")
+ return ptr, nil
case *ssa.TypeAssert:
return b.createTypeAssert(expr), nil
case *ssa.UnOp:
diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go
index 6db5ebb38..0fb9fa913 100644
--- a/compiler/compiler_test.go
+++ b/compiler/compiler_test.go
@@ -9,6 +9,7 @@ import (
"testing"
"github.com/tinygo-org/tinygo/compileopts"
+ "github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/loader"
"tinygo.org/x/go-llvm"
)
@@ -16,6 +17,11 @@ import (
// Pass -update to go test to update the output of the test files.
var flagUpdate = flag.Bool("update", false, "update tests based on test output")
+type testCase struct {
+ file string
+ target string
+}
+
// Basic tests for the compiler. Build some Go files and compare the output with
// the expected LLVM IR for regression testing.
func TestCompiler(t *testing.T) {
@@ -34,10 +40,7 @@ func TestCompiler(t *testing.T) {
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
}
- tests := []struct {
- file string
- target string
- }{
+ tests := []testCase{
{"basic.go", ""},
{"pointer.go", ""},
{"slice.go", ""},
@@ -52,6 +55,14 @@ func TestCompiler(t *testing.T) {
{"intrinsics.go", "wasm"},
}
+ _, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
+ if err != nil {
+ t.Fatal("could not read Go version:", err)
+ }
+ if minor >= 17 {
+ tests = append(tests, testCase{"go1.17.go", ""})
+ }
+
for _, tc := range tests {
name := tc.file
targetString := "wasm"
diff --git a/compiler/testdata/go1.17.go b/compiler/testdata/go1.17.go
new file mode 100644
index 000000000..076dded4c
--- /dev/null
+++ b/compiler/testdata/go1.17.go
@@ -0,0 +1,41 @@
+package main
+
+// Test changes to the language introduced in Go 1.17.
+// For details, see: https://tip.golang.org/doc/go1.17#language
+// These tests should be merged into the regular slice tests once Go 1.17 is the
+// minimun Go version for TinyGo.
+
+import "unsafe"
+
+func Add32(p unsafe.Pointer, len int) unsafe.Pointer {
+ return unsafe.Add(p, len)
+}
+
+func Add64(p unsafe.Pointer, len int64) unsafe.Pointer {
+ return unsafe.Add(p, len)
+}
+
+func SliceToArray(s []int) *[4]int {
+ return (*[4]int)(s)
+}
+
+func SliceToArrayConst() *[4]int {
+ s := make([]int, 6)
+ return (*[4]int)(s)
+}
+
+func SliceInt(ptr *int, len int) []int {
+ return unsafe.Slice(ptr, len)
+}
+
+func SliceUint16(ptr *byte, len uint16) []byte {
+ return unsafe.Slice(ptr, len)
+}
+
+func SliceUint64(ptr *int, len uint64) []int {
+ return unsafe.Slice(ptr, len)
+}
+
+func SliceInt64(ptr *int, len int64) []int {
+ return unsafe.Slice(ptr, len)
+}
diff --git a/compiler/testdata/go1.17.ll b/compiler/testdata/go1.17.ll
new file mode 100644
index 000000000..6fa47c8b1
--- /dev/null
+++ b/compiler/testdata/go1.17.ll
@@ -0,0 +1,136 @@
+; ModuleID = 'go1.17.go'
+source_filename = "go1.17.go"
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32--wasi"
+
+declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+
+define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
+entry:
+ ret void
+}
+
+define hidden i8* @main.Add32(i8* %p, i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
+entry:
+ %0 = getelementptr i8, i8* %p, i32 %len
+ ret i8* %0
+}
+
+define hidden i8* @main.Add64(i8* %p, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr {
+entry:
+ %0 = trunc i64 %len to i32
+ %1 = getelementptr i8, i8* %p, i32 %0
+ ret i8* %1
+}
+
+define hidden [4 x i32]* @main.SliceToArray(i32* %s.data, i32 %s.len, i32 %s.cap, i8* %context, i8* %parentHandle) unnamed_addr {
+entry:
+ %0 = icmp ult i32 %s.len, 4
+ br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
+
+slicetoarray.throw: ; preds = %entry
+ call void @runtime.sliceToArrayPointerPanic(i8* undef, i8* null)
+ unreachable
+
+slicetoarray.next: ; preds = %entry
+ %1 = bitcast i32* %s.data to [4 x i32]*
+ ret [4 x i32]* %1
+}
+
+declare void @runtime.sliceToArrayPointerPanic(i8*, i8*)
+
+define hidden [4 x i32]* @main.SliceToArrayConst(i8* %context, i8* %parentHandle) unnamed_addr {
+entry:
+ %makeslice = call i8* @runtime.alloc(i32 24, i8* undef, i8* null)
+ br i1 false, label %slicetoarray.throw, label %slicetoarray.next
+
+slicetoarray.throw: ; preds = %entry
+ unreachable
+
+slicetoarray.next: ; preds = %entry
+ %0 = bitcast i8* %makeslice to [4 x i32]*
+ ret [4 x i32]* %0
+}
+
+define hidden { i32*, i32, i32 } @main.SliceInt(i32* dereferenceable_or_null(4) %ptr, i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
+entry:
+ %0 = icmp ugt i32 %len, 1073741823
+ %1 = icmp eq i32* %ptr, null
+ %2 = icmp ne i32 %len, 0
+ %3 = and i1 %1, %2
+ %4 = or i1 %3, %0
+ br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
+
+unsafe.Slice.throw: ; preds = %entry
+ call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
+ unreachable
+
+unsafe.Slice.next: ; preds = %entry
+ %5 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
+ %6 = insertvalue { i32*, i32, i32 } %5, i32 %len, 1
+ %7 = insertvalue { i32*, i32, i32 } %6, i32 %len, 2
+ ret { i32*, i32, i32 } %7
+}
+
+declare void @runtime.unsafeSlicePanic(i8*, i8*)
+
+define hidden { i8*, i32, i32 } @main.SliceUint16(i8* dereferenceable_or_null(1) %ptr, i16 %len, i8* %context, i8* %parentHandle) unnamed_addr {
+entry:
+ %0 = icmp eq i8* %ptr, null
+ %1 = icmp ne i16 %len, 0
+ %2 = and i1 %0, %1
+ br i1 %2, label %unsafe.Slice.throw, label %unsafe.Slice.next
+
+unsafe.Slice.throw: ; preds = %entry
+ call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
+ unreachable
+
+unsafe.Slice.next: ; preds = %entry
+ %3 = zext i16 %len to i32
+ %4 = insertvalue { i8*, i32, i32 } undef, i8* %ptr, 0
+ %5 = insertvalue { i8*, i32, i32 } %4, i32 %3, 1
+ %6 = insertvalue { i8*, i32, i32 } %5, i32 %3, 2
+ ret { i8*, i32, i32 } %6
+}
+
+define hidden { i32*, i32, i32 } @main.SliceUint64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr {
+entry:
+ %0 = icmp ugt i64 %len, 1073741823
+ %1 = icmp eq i32* %ptr, null
+ %2 = icmp ne i64 %len, 0
+ %3 = and i1 %1, %2
+ %4 = or i1 %3, %0
+ br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
+
+unsafe.Slice.throw: ; preds = %entry
+ call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
+ unreachable
+
+unsafe.Slice.next: ; preds = %entry
+ %5 = trunc i64 %len to i32
+ %6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
+ %7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
+ %8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
+ ret { i32*, i32, i32 } %8
+}
+
+define hidden { i32*, i32, i32 } @main.SliceInt64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr {
+entry:
+ %0 = icmp ugt i64 %len, 1073741823
+ %1 = icmp eq i32* %ptr, null
+ %2 = icmp ne i64 %len, 0
+ %3 = and i1 %1, %2
+ %4 = or i1 %3, %0
+ br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
+
+unsafe.Slice.throw: ; preds = %entry
+ call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
+ unreachable
+
+unsafe.Slice.next: ; preds = %entry
+ %5 = trunc i64 %len to i32
+ %6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
+ %7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
+ %8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
+ ret { i32*, i32, i32 } %8
+}
diff --git a/go.mod b/go.mod
index 285c02a4a..60b7cbe00 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
github.com/mattn/go-colorable v0.1.8
go.bug.st/serial v1.1.2
- golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
- golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
+ golang.org/x/sys v0.0.0-20210510120138-977fb7262007
+ golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c
)
diff --git a/go.sum b/go.sum
index 1451afebf..03688c437 100644
--- a/go.sum
+++ b/go.sum
@@ -30,6 +30,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.bug.st/serial v1.0.0 h1:ogEPzrllCsnG00EqKRjeYvPRsO7NJW6DqykzkdD6E/k=
go.bug.st/serial v1.0.0/go.mod h1:rpXPISGjuNjPTRTcMlxi9lN6LoIPxd1ixVjBd8aSk/Q=
go.bug.st/serial v1.1.2 h1:6xDpbta8KJ+VLRTeM8ghhxXRMLE/Lr8h9iDKwydarAY=
@@ -38,23 +40,32 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
-golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 h1:0sfSpGSa544Fwnbot3Oxq/U6SXqjty6Jy/3wRhVS7ig=
-golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9 h1:nvvuMxmx1q0gfRki3T0hjG8EwAcVCs91oWAXvyt4zhI=
+golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
diff --git a/main_test.go b/main_test.go
index bbc1a062d..7ebf8eb03 100644
--- a/main_test.go
+++ b/main_test.go
@@ -20,6 +20,7 @@ import (
"github.com/tinygo-org/tinygo/builder"
"github.com/tinygo-org/tinygo/compileopts"
+ "github.com/tinygo-org/tinygo/goenv"
)
const TESTDATA = "testdata"
@@ -54,6 +55,14 @@ func TestCompiler(t *testing.T) {
"zeroalloc.go",
}
+ _, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
+ if err != nil {
+ t.Fatal("could not read version from GOROOT:", err)
+ }
+ if minor >= 17 {
+ tests = append(tests, "go1.17.go")
+ }
+
if *testTarget != "" {
// This makes it possible to run one specific test (instead of all),
// which is especially useful to quickly check whether some changes
diff --git a/src/runtime/panic.go b/src/runtime/panic.go
index cf7534173..37b7c259a 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -42,6 +42,18 @@ func slicePanic() {
runtimePanic("slice out of range")
}
+// Panic when trying to convert a slice to an array pointer (Go 1.17+) and the
+// slice is shorter than the array.
+func sliceToArrayPointerPanic() {
+ runtimePanic("slice smaller than array")
+}
+
+// Panic when calling unsafe.Slice() (Go 1.17+) with a len that's too large
+// (which includes if the ptr is nil and len is nonzero).
+func unsafeSlicePanic() {
+ runtimePanic("unsafe.Slice: len out of range")
+}
+
// Panic when trying to create a new channel that is too big.
func chanMakePanic() {
runtimePanic("new channel is too big")
diff --git a/testdata/go1.17.go b/testdata/go1.17.go
new file mode 100644
index 000000000..2a9fba75c
--- /dev/null
+++ b/testdata/go1.17.go
@@ -0,0 +1,34 @@
+package main
+
+// Test new language features introduced in Go 1.17:
+// https://tip.golang.org/doc/go1.17#language
+// Once this becomes the minimum Go version of TinyGo, these tests should be
+// merged with the regular slice tests.
+
+import "unsafe"
+
+func main() {
+ // Test conversion from array to slice.
+ slice1 := []int{1, 2, 3, 4}
+ arr1 := (*[4]int)(slice1)
+ arr1[1] = -2
+ arr1[2] = 20
+ println("slice to array pointer:", arr1[0], arr1[1], arr1[2], arr1[3])
+
+ // Test unsafe.Add.
+ arr2 := [...]int{1, 2, 3, 4}
+ *(*int)(unsafe.Add(unsafe.Pointer(&arr2[0]), unsafe.Sizeof(int(1))*1)) = 5
+ *addInt(&arr2[0], 2) = 8
+ println("unsafe.Add array:", arr2[0], arr2[1], arr2[2], arr2[3])
+
+ // Test unsafe.Slice.
+ arr3 := [...]int{1, 2, 3, 4}
+ slice3 := unsafe.Slice(&arr3[1], 3)
+ slice3[0] = 9
+ slice3[1] = 15
+ println("unsafe.Slice array:", len(slice3), cap(slice3), slice3[0], slice3[1], slice3[2])
+}
+
+func addInt(ptr *int, index uintptr) *int {
+ return (*int)(unsafe.Add(unsafe.Pointer(ptr), unsafe.Sizeof(int(1))*index))
+}
diff --git a/testdata/go1.17.txt b/testdata/go1.17.txt
new file mode 100644
index 000000000..eafc1b45a
--- /dev/null
+++ b/testdata/go1.17.txt
@@ -0,0 +1,3 @@
+slice to array pointer: 1 -2 20 4
+unsafe.Add array: 1 5 8 4
+unsafe.Slice array: 3 3 9 15 4