aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDamian Gryski <[email protected]>2023-11-04 10:14:08 -0700
committerRon Evans <[email protected]>2023-11-08 19:41:25 +0100
commit777048cfa9d37c5604b02a974574a78981ab9374 (patch)
tree94a97cea20055754d88d912158cf0810f8bea92e
parent2b215955cac75b1308d02269b8ea5b73448708a9 (diff)
downloadtinygo-777048cfa9d37c5604b02a974574a78981ab9374.tar.gz
tinygo-777048cfa9d37c5604b02a974574a78981ab9374.zip
compiler: fix crash on type assert on interfaces with no methods
-rw-r--r--compiler/interface.go30
-rw-r--r--testdata/interface.go9
-rw-r--r--testdata/interface.txt1
3 files changed, 28 insertions, 12 deletions
diff --git a/compiler/interface.go b/compiler/interface.go
index 6a45dd787..564e3a414 100644
--- a/compiler/interface.go
+++ b/compiler/interface.go
@@ -684,19 +684,25 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
actualTypeNum := b.CreateExtractValue(itf, 0, "interface.type")
commaOk := llvm.Value{}
- if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
- // Type assert on interface type.
- // This is a call to an interface type assert function.
- // The interface lowering pass will define this function by filling it
- // with a type switch over all concrete types that implement this
- // interface, and returning whether it's one of the matched types.
- // This is very different from how interface asserts are implemented in
- // the main Go compiler, where the runtime checks whether the type
- // implements each method of the interface. See:
- // https://research.swtch.com/interfaces
- fn := b.getInterfaceImplementsFunc(expr.AssertedType)
- commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
+ if intf, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
+ if intf.Empty() {
+ // intf is the empty interface => no methods
+ // This type assertion always succeeds, so we can just set commaOk to true.
+ commaOk = llvm.ConstInt(b.ctx.Int1Type(), 1, true)
+ } else {
+ // Type assert on interface type with methods.
+ // This is a call to an interface type assert function.
+ // The interface lowering pass will define this function by filling it
+ // with a type switch over all concrete types that implement this
+ // interface, and returning whether it's one of the matched types.
+ // This is very different from how interface asserts are implemented in
+ // the main Go compiler, where the runtime checks whether the type
+ // implements each method of the interface. See:
+ // https://research.swtch.com/interfaces
+ fn := b.getInterfaceImplementsFunc(expr.AssertedType)
+ commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
+ }
} else {
name, _ := getTypeCodeName(expr.AssertedType)
globalName := "reflect/types.typeid:" + name
diff --git a/testdata/interface.go b/testdata/interface.go
index 72cc76bce..1a3a83828 100644
--- a/testdata/interface.go
+++ b/testdata/interface.go
@@ -116,6 +116,9 @@ func main() {
// check that pointer-to-pointer type switches work
ptrptrswitch()
+
+ // check that type asserts to interfaces with no methods work
+ emptyintfcrash()
}
func printItf(val interface{}) {
@@ -334,3 +337,9 @@ func identify(itf any) {
println("other type??")
}
}
+
+func emptyintfcrash() {
+ if x, ok := any(5).(any); ok {
+ println("x is", x.(int))
+ }
+}
diff --git a/testdata/interface.txt b/testdata/interface.txt
index fec46637b..55f7ae1de 100644
--- a/testdata/interface.txt
+++ b/testdata/interface.txt
@@ -27,3 +27,4 @@ slept 1ms
type is int
type is *int
type is **int
+x is 5