diff options
author | Ayke van Laethem <[email protected]> | 2020-03-19 13:59:37 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2020-03-20 22:22:24 +0100 |
commit | 9222bda9c665daf64e16ac0e724946582ffc6860 (patch) | |
tree | ed56c005ccb1c534d48cc385467c2e2cfe9c8ee5 | |
parent | 213a6240a17870dea2603e7fd01ff163468e50c9 (diff) | |
download | tinygo-9222bda9c665daf64e16ac0e724946582ffc6860.tar.gz tinygo-9222bda9c665daf64e16ac0e724946582ffc6860.zip |
interp: add support for constant type asserts
Non-constant type asserts are not yet implemented, but should be
relatively easy to add at a later time. They should result in a clear
error message for now.
-rw-r--r-- | interp/frame.go | 15 | ||||
-rw-r--r-- | interp/interp_test.go | 1 | ||||
-rw-r--r-- | interp/testdata/interface.ll | 28 | ||||
-rw-r--r-- | interp/testdata/interface.out.ll | 9 | ||||
-rw-r--r-- | testdata/interface.go | 6 |
5 files changed, 59 insertions, 0 deletions
diff --git a/interp/frame.go b/interp/frame.go index 4cf87408f..97b438cef 100644 --- a/interp/frame.go +++ b/interp/frame.go @@ -440,6 +440,21 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re ret = llvm.ConstInsertValue(ret, retLen, []uint32{1}) // len ret = llvm.ConstInsertValue(ret, retLen, []uint32{2}) // cap fr.locals[inst] = &LocalValue{fr.Eval, ret} + case callee.Name() == "runtime.typeAssert": + actualTypeInt := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying + assertedType := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying + if actualTypeInt.IsAConstantExpr().IsNil() || actualTypeInt.Opcode() != llvm.PtrToInt { + return nil, nil, fr.errorAt(inst, "interp: expected typecode in runtime.typeAssert to be a ptrtoint") + } + actualType := actualTypeInt.Operand(0) + if actualType.IsAConstant().IsNil() || assertedType.IsAConstant().IsNil() { + return nil, nil, fr.errorAt(inst, "interp: unimplemented: type assert with non-constant interface value") + } + assertOk := uint64(0) + if llvm.ConstExtractValue(actualType.Initializer(), []uint32{0}) == assertedType { + assertOk = 1 + } + fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), assertOk, false)} case callee.Name() == "runtime.interfaceImplements": typecode := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying interfaceMethodSet := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying diff --git a/interp/interp_test.go b/interp/interp_test.go index 702392f17..6b0cc38f2 100644 --- a/interp/interp_test.go +++ b/interp/interp_test.go @@ -15,6 +15,7 @@ func TestInterp(t *testing.T) { "slice-copy", "consteval", "map", + "interface", } { name := name // make tc local to this closure t.Run(name, func(t *testing.T) { diff --git a/interp/testdata/interface.ll b/interp/testdata/interface.ll new file mode 100644 index 000000000..22378b690 --- /dev/null +++ b/interp/testdata/interface.ll @@ -0,0 +1,28 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64--linux" + +%runtime.typecodeID = type { %runtime.typecodeID*, i64 } +%runtime.interfaceMethodInfo = type { i8*, i64 } +%runtime.typeInInterface = type { %runtime.typecodeID*, %runtime.interfaceMethodInfo* } + [email protected] = global i1 0 +@"reflect/types.type:named:main.foo" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i64 0 } +@"reflect/types.type:basic:int" = external constant %runtime.typecodeID +@"typeInInterface:reflect/types.type:named:main.foo" = private constant %runtime.typeInInterface { %runtime.typecodeID* @"reflect/types.type:named:main.foo", %runtime.interfaceMethodInfo* null } + + +declare i1 @runtime.typeAssert(i64, %runtime.typecodeID*, i8*, i8*) + +define void @runtime.initAll() unnamed_addr { +entry: + call void @main.init() + ret void +} + +define internal void @main.init() unnamed_addr { +entry: + ; Test type asserts. + %typecode = call i1 @runtime.typeAssert(i64 ptrtoint (%runtime.typeInInterface* @"typeInInterface:reflect/types.type:named:main.foo" to i64), %runtime.typecodeID* @"reflect/types.type:named:main.foo", i8* undef, i8* null) + store i1 %typecode, i1* @main.v1 + ret void +} diff --git a/interp/testdata/interface.out.ll b/interp/testdata/interface.out.ll new file mode 100644 index 000000000..2d2467b67 --- /dev/null +++ b/interp/testdata/interface.out.ll @@ -0,0 +1,9 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64--linux" + [email protected] = local_unnamed_addr global i1 true + +define void @runtime.initAll() unnamed_addr { +entry: + ret void +} diff --git a/testdata/interface.go b/testdata/interface.go index c23af4fd1..fac4dfc81 100644 --- a/testdata/interface.go +++ b/testdata/interface.go @@ -137,6 +137,12 @@ func printItf(val interface{}) { } } +var ( + // Test for type assert support in the interp package. + globalThing interface{} = Foo(3) + _ = globalThing.(Foo) +) + func nestedSwitch(verb rune, arg interface{}) bool { switch verb { case 'v', 's': |