diff options
-rw-r--r-- | compiler/interface.go | 7 | ||||
-rw-r--r-- | testdata/interface.go | 22 | ||||
-rw-r--r-- | testdata/interface.txt | 3 | ||||
-rw-r--r-- | transform/interface-lowering.go | 18 |
4 files changed, 44 insertions, 6 deletions
diff --git a/compiler/interface.go b/compiler/interface.go index 7b49a81d4..3fdb30bb8 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -683,11 +683,8 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value { commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "") } else { - assertedTypeGlobal := b.getTypeCode(expr.AssertedType) - if !assertedTypeGlobal.IsAConstantExpr().IsNil() { - assertedTypeGlobal = assertedTypeGlobal.Operand(0) // resolve the GEP operation - } - globalName := "reflect/types.typeid:" + strings.TrimPrefix(assertedTypeGlobal.Name(), "reflect/types.type:") + name, _ := getTypeCodeName(expr.AssertedType) + globalName := "reflect/types.typeid:" + name assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName) if assertedTypeCodeGlobal.IsNil() { // Create a new typecode global. diff --git a/testdata/interface.go b/testdata/interface.go index 7820538a4..72cc76bce 100644 --- a/testdata/interface.go +++ b/testdata/interface.go @@ -113,6 +113,9 @@ func main() { println("slept 1ms") blockStatic(SleepBlocker(time.Millisecond)) println("slept 1ms") + + // check that pointer-to-pointer type switches work + ptrptrswitch() } func printItf(val interface{}) { @@ -312,3 +315,22 @@ func namedptr2() interface{} { type Test byte return (*Test)(nil) } + +func ptrptrswitch() { + identify(0) + identify(new(int)) + identify(new(*int)) +} + +func identify(itf any) { + switch itf.(type) { + case int: + println("type is int") + case *int: + println("type is *int") + case **int: + println("type is **int") + default: + println("other type??") + } +} diff --git a/testdata/interface.txt b/testdata/interface.txt index 04e3fb9ea..fec46637b 100644 --- a/testdata/interface.txt +++ b/testdata/interface.txt @@ -24,3 +24,6 @@ Byte(): 3 non-blocking call on sometimes-blocking interface slept 1ms slept 1ms +type is int +type is *int +type is **int diff --git a/transform/interface-lowering.go b/transform/interface-lowering.go index ebd47ff8f..11d81e4f6 100644 --- a/transform/interface-lowering.go +++ b/transform/interface-lowering.go @@ -285,11 +285,27 @@ func (p *lowerInterfacesPass) run() error { for _, use := range getUses(p.mod.NamedFunction("runtime.typeAssert")) { actualType := use.Operand(0) name := strings.TrimPrefix(use.Operand(1).Name(), "reflect/types.typeid:") + gepOffset := uint64(0) + for strings.HasPrefix(name, "pointer:pointer:") { + // This is a type like **int, which has the name pointer:pointer:int + // but is encoded using pointer tagging. + // Calculate the pointer tag, which is emitted as a GEP instruction. + name = name[len("pointer:"):] + gepOffset++ + } + if t, ok := p.types[name]; ok { // The type exists in the program, so lower to a regular pointer // comparison. p.builder.SetInsertPointBefore(use) - commaOk := p.builder.CreateICmp(llvm.IntEQ, t.typecodeGEP, actualType, "typeassert.ok") + typecodeGEP := t.typecodeGEP + if gepOffset != 0 { + // This is a tagged pointer. + typecodeGEP = llvm.ConstInBoundsGEP(p.ctx.Int8Type(), typecodeGEP, []llvm.Value{ + llvm.ConstInt(p.ctx.Int64Type(), gepOffset, false), + }) + } + commaOk := p.builder.CreateICmp(llvm.IntEQ, typecodeGEP, actualType, "typeassert.ok") use.ReplaceAllUsesWith(commaOk) } else { // The type does not exist in the program, so lower to a constant |