Skip to content

Commit e62adb1

Browse files
committed
cmd/compile: fix devirtualization of promoted interface methods
A method selector expression can pick out a method or promoted method (represented by ODOTMETH), but it can also pick out an interface method from an embedded interface-typed field (represented by ODOTINTER). In the case that we're picking out an interface method, we're not able to fully devirtualize the method call. However, we're still able to improve escape analysis somewhat. E.g., the included test case demonstrates that we can optimize "i.M()" to "i.(T).I.M()", which means the T literal can be stack allocated instead of heap allocated. Fixes #42279. Change-Id: Ifa21d19011e2f008d84f9624b7055b4676b6d188 Reviewed-on: https://go-review.googlesource.com/c/go/+/266300 Trust: Matthew Dempsky <[email protected]> Run-TryBot: Matthew Dempsky <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: David Chase <[email protected]>
1 parent faa4426 commit e62adb1

File tree

2 files changed

+25
-9
lines changed

2 files changed

+25
-9
lines changed

src/cmd/compile/internal/gc/inl.go

+17-9
Original file line numberDiff line numberDiff line change
@@ -1446,19 +1446,27 @@ func devirtualizeCall(call *Node) {
14461446
x.Type = typ
14471447
x = nodlSym(call.Left.Pos, OXDOT, x, call.Left.Sym)
14481448
x = typecheck(x, ctxExpr|ctxCallee)
1449-
if x.Op != ODOTMETH {
1450-
// TODO(mdempsky): Figure out how to avoid this and
1451-
// turn back into a Fatalf.
1449+
switch x.Op {
1450+
case ODOTMETH:
14521451
if Debug.m != 0 {
1453-
Warnl(call.Pos, "failed to devirtualize %v", x)
1452+
Warnl(call.Pos, "devirtualizing %v to %v", call.Left, typ)
1453+
}
1454+
call.Op = OCALLMETH
1455+
call.Left = x
1456+
case ODOTINTER:
1457+
// Promoted method from embedded interface-typed field (#42279).
1458+
if Debug.m != 0 {
1459+
Warnl(call.Pos, "partially devirtualizing %v to %v", call.Left, typ)
1460+
}
1461+
call.Op = OCALLINTER
1462+
call.Left = x
1463+
default:
1464+
// TODO(mdempsky): Turn back into Fatalf after more testing.
1465+
if Debug.m != 0 {
1466+
Warnl(call.Pos, "failed to devirtualize %v (%v)", x, x.Op)
14541467
}
14551468
return
14561469
}
1457-
if Debug.m != 0 {
1458-
Warnl(call.Pos, "devirtualizing %v to %v", call.Left, typ)
1459-
}
1460-
call.Op = OCALLMETH
1461-
call.Left = x
14621470

14631471
// Duplicated logic from typecheck for function call return
14641472
// value types.

test/escape_iface.go

+8
Original file line numberDiff line numberDiff line change
@@ -255,3 +255,11 @@ func dotTypeEscape2() { // #13805, #15796
255255
sink, *(&ok) = y.(*int)
256256
}
257257
}
258+
259+
func issue42279() {
260+
type I interface{ M() }
261+
type T struct{ I }
262+
263+
var i I = T{} // ERROR "T\{\} does not escape"
264+
i.M() // ERROR "partially devirtualizing i.M to T"
265+
}

0 commit comments

Comments
 (0)