diff options
author | Damian Gryski <[email protected]> | 2023-11-04 10:14:08 -0700 |
---|---|---|
committer | Ron Evans <[email protected]> | 2023-11-08 19:41:25 +0100 |
commit | 777048cfa9d37c5604b02a974574a78981ab9374 (patch) | |
tree | 94a97cea20055754d88d912158cf0810f8bea92e | |
parent | 2b215955cac75b1308d02269b8ea5b73448708a9 (diff) | |
download | tinygo-777048cfa9d37c5604b02a974574a78981ab9374.tar.gz tinygo-777048cfa9d37c5604b02a974574a78981ab9374.zip |
compiler: fix crash on type assert on interfaces with no methods
-rw-r--r-- | compiler/interface.go | 30 | ||||
-rw-r--r-- | testdata/interface.go | 9 | ||||
-rw-r--r-- | testdata/interface.txt | 1 |
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 |