aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDamian Gryski <[email protected]>2023-06-01 10:41:36 -0700
committerRon Evans <[email protected]>2023-06-09 17:30:02 +0200
commitf5f4751088bd40a8680899eefeb9854c8dbf4b33 (patch)
treeb7a20ff88c6f31d368fcc783a6a92fa81021ab33
parent62fb386d57a0080881c756000e42a2c873372eb8 (diff)
downloadtinygo-f5f4751088bd40a8680899eefeb9854c8dbf4b33.tar.gz
tinygo-f5f4751088bd40a8680899eefeb9854c8dbf4b33.zip
compiler,transform: fix for pointer-to-pointer type switches from @aykevl
-rw-r--r--compiler/interface.go7
-rw-r--r--testdata/interface.go22
-rw-r--r--testdata/interface.txt3
-rw-r--r--transform/interface-lowering.go18
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