diff options
author | Ayke van Laethem <[email protected]> | 2021-08-15 02:29:27 +0200 |
---|---|---|
committer | Ron Evans <[email protected]> | 2021-08-30 09:18:58 +0200 |
commit | 255f35671d3eef11d6cfe0020aeda5c17985657d (patch) | |
tree | 6e24e118a4f658f044ed2fe8b20f24fcf161975f | |
parent | 8e88e560a1eb76558364ee3400b3bfe0e4c054c6 (diff) | |
download | tinygo-255f35671d3eef11d6cfe0020aeda5c17985657d.tar.gz tinygo-255f35671d3eef11d6cfe0020aeda5c17985657d.zip |
compiler: add support for new language features of Go 1.17
-rw-r--r-- | compiler/asserts.go | 49 | ||||
-rw-r--r-- | compiler/compiler.go | 43 | ||||
-rw-r--r-- | compiler/compiler_test.go | 19 | ||||
-rw-r--r-- | compiler/testdata/go1.17.go | 41 | ||||
-rw-r--r-- | compiler/testdata/go1.17.ll | 136 | ||||
-rw-r--r-- | go.mod | 4 | ||||
-rw-r--r-- | go.sum | 23 | ||||
-rw-r--r-- | main_test.go | 9 | ||||
-rw-r--r-- | src/runtime/panic.go | 12 | ||||
-rw-r--r-- | testdata/go1.17.go | 34 | ||||
-rw-r--r-- | testdata/go1.17.txt | 3 |
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 +} @@ -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 ) @@ -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 |