From 9222bda9c665daf64e16ac0e724946582ffc6860 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Thu, 19 Mar 2020 13:59:37 +0100 Subject: [PATCH] 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. --- interp/frame.go | 15 +++++++++++++++ interp/interp_test.go | 1 + interp/testdata/interface.ll | 28 ++++++++++++++++++++++++++++ interp/testdata/interface.out.ll | 9 +++++++++ testdata/interface.go | 6 ++++++ 5 files changed, 59 insertions(+) create mode 100644 interp/testdata/interface.ll create mode 100644 interp/testdata/interface.out.ll diff --git a/interp/frame.go b/interp/frame.go index 4cf87408..97b438ce 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 702392f1..6b0cc38f 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 00000000..22378b69 --- /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* } + +@main.v1 = 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 00000000..2d2467b6 --- /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" + +@main.v1 = 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 c23af4fd..fac4dfc8 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':