From 5d334922d78ed85e1e83341d2d6262755e043b19 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 24 Mar 2021 00:34:29 +0100 Subject: [PATCH] compiler: add interface IR test This is important as golden test output and to verify that the output is correct. Later improvements and bug fixes are clearly visible in the IR, and unintentional changes will also be immediately spotted. --- compiler/compiler_test.go | 1 + compiler/testdata/interface.go | 54 +++++++++++++++++++ compiler/testdata/interface.ll | 98 ++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 compiler/testdata/interface.go create mode 100644 compiler/testdata/interface.ll diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index 99e14e3e..5f2d18f4 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -61,6 +61,7 @@ func TestCompiler(t *testing.T) { "slice.go", "string.go", "float.go", + "interface.go", } for _, testCase := range tests { diff --git a/compiler/testdata/interface.go b/compiler/testdata/interface.go new file mode 100644 index 00000000..049869e2 --- /dev/null +++ b/compiler/testdata/interface.go @@ -0,0 +1,54 @@ +// This file tests interface types and interface builtins. + +package main + +// Test interface construction. + +func simpleType() interface{} { + return 0 +} + +func pointerType() interface{} { + // Pointers have an element type, in this case int. + var v *int + return v +} + +func interfaceType() interface{} { + // Interfaces can exist in interfaces, but only indirectly (through + // pointers). + var v *error + return v +} + +func anonymousInterfaceType() interface{} { + var v *interface { + String() string + } + return v +} + +// Test interface builtins. + +func isInt(itf interface{}) bool { + _, ok := itf.(int) + return ok +} + +func isError(itf interface{}) bool { + // Interface assert on (builtin) named interface type. + _, ok := itf.(error) + return ok +} + +func isStringer(itf interface{}) bool { + // Interface assert on anonymous interface type. + _, ok := itf.(interface { + String() string + }) + return ok +} + +func callErrorMethod(itf error) string { + return itf.Error() +} diff --git a/compiler/testdata/interface.ll b/compiler/testdata/interface.ll new file mode 100644 index 00000000..b958aa5f --- /dev/null +++ b/compiler/testdata/interface.ll @@ -0,0 +1,98 @@ +; ModuleID = 'interface.go' +source_filename = "interface.go" +target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128" +target triple = "i686--linux" + +%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo* } +%runtime.interfaceMethodInfo = type { i8*, i32 } +%runtime._interface = type { i32, i8* } +%runtime._string = type { i8*, i32 } + +@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID zeroinitializer +@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null } +@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null } +@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null } +@"reflect/types.type:interface:{func:{}{basic:string}}" = external constant %runtime.typecodeID +@"reflect/types.type:pointer:interface:{func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null } +@"reflect/types.type:basic:int$id" = external constant i8 +@"func Error() string" = external constant i8 +@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"] +@"func String() string" = external constant i8 +@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func String() string"] + +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 %runtime._interface @main.simpleType(i8* %context, i8* %parentHandle) unnamed_addr { +entry: + ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:int" to i32), i8* null } +} + +define hidden %runtime._interface @main.pointerType(i8* %context, i8* %parentHandle) unnamed_addr { +entry: + ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:basic:int" to i32), i8* null } +} + +define hidden %runtime._interface @main.interfaceType(i8* %context, i8* %parentHandle) unnamed_addr { +entry: + ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:named:error" to i32), i8* null } +} + +define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context, i8* %parentHandle) unnamed_addr { +entry: + ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:interface:{func:{}{basic:string}}" to i32), i8* null } +} + +define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr { +entry: + %typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.type:basic:int$id", i8* undef, i8* null) + br i1 %typecode, label %typeassert.ok, label %typeassert.next + +typeassert.ok: ; preds = %entry + br label %typeassert.next + +typeassert.next: ; preds = %typeassert.ok, %entry + ret i1 %typecode +} + +declare i1 @runtime.typeAssert(i32, i8* dereferenceable_or_null(1), i8*, i8*) + +define hidden i1 @main.isError(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr { +entry: + %0 = call i1 @runtime.interfaceImplements(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"error$interface", i32 0, i32 0), i8* undef, i8* null) + br i1 %0, label %typeassert.ok, label %typeassert.next + +typeassert.ok: ; preds = %entry + br label %typeassert.next + +typeassert.next: ; preds = %typeassert.ok, %entry + ret i1 %0 +} + +declare i1 @runtime.interfaceImplements(i32, i8** dereferenceable_or_null(4), i8*, i8*) + +define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr { +entry: + %0 = call i1 @runtime.interfaceImplements(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"reflect/types.interface:interface{String() string}$interface", i32 0, i32 0), i8* undef, i8* null) + br i1 %0, label %typeassert.ok, label %typeassert.next + +typeassert.ok: ; preds = %entry + br label %typeassert.next + +typeassert.next: ; preds = %typeassert.ok, %entry + ret i1 %0 +} + +define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr { +entry: + %invoke.func = call i32 @runtime.interfaceMethod(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"error$interface", i32 0, i32 0), i8* nonnull @"func Error() string", i8* undef, i8* null) + %invoke.func.cast = inttoptr i32 %invoke.func to %runtime._string (i8*, i8*, i8*)* + %0 = call %runtime._string %invoke.func.cast(i8* %itf.value, i8* undef, i8* undef) + ret %runtime._string %0 +} + +declare i32 @runtime.interfaceMethod(i32, i8** dereferenceable_or_null(4), i8* dereferenceable_or_null(1), i8*, i8*)