aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2020-03-19 13:59:37 +0100
committerRon Evans <[email protected]>2020-03-20 22:22:24 +0100
commit9222bda9c665daf64e16ac0e724946582ffc6860 (patch)
treeed56c005ccb1c534d48cc385467c2e2cfe9c8ee5
parent213a6240a17870dea2603e7fd01ff163468e50c9 (diff)
downloadtinygo-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.go15
-rw-r--r--interp/interp_test.go1
-rw-r--r--interp/testdata/interface.ll28
-rw-r--r--interp/testdata/interface.out.ll9
-rw-r--r--testdata/interface.go6
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':