Files
go/test/devirtualization.go
Mateusz Poliwczak de9da0de30 cmd/compile/internal/devirtualize: improve concrete type analysis
This change improves the concrete type analysis in the devirtualizer,
it not longer relies on ir.Reassigned, it now statically tries to
determine the concrete type of an interface, even when assigned
multiple times, following type assertions and iface conversions.

Alternative to CL 649195

Updates #69521
Fixes #64824

Change-Id: Ib1656e19f3619ab2e1e6b2c78346cc320490b2af
GitHub-Last-Rev: e8fa0b12f0
GitHub-Pull-Request: golang/go#71935
Reviewed-on: https://go-review.googlesource.com/c/go/+/652036
Reviewed-by: Michael Pratt <mpratt@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Keith Randall <khr@golang.org>
2025-10-08 14:09:22 -07:00

1278 lines
34 KiB
Go

// errorcheck -0 -m
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package escape
type M interface{ M() }
type A interface{ A() }
type C interface{ C() }
type Impl struct{}
func (*Impl) M() {} // ERROR "can inline \(\*Impl\).M$"
func (*Impl) A() {} // ERROR "can inline \(\*Impl\).A$"
type Impl2 struct{}
func (*Impl2) M() {} // ERROR "can inline \(\*Impl2\).M$"
func (*Impl2) A() {} // ERROR "can inline \(\*Impl2\).A$"
type CImpl struct{}
func (CImpl) C() {} // ERROR "can inline CImpl.C$"
func typeAsserts() {
var a M = &Impl{} // ERROR "&Impl{} does not escape$"
a.(M).M() // ERROR "devirtualizing a.\(M\).M to \*Impl$" "inlining call to \(\*Impl\).M"
a.(A).A() // ERROR "devirtualizing a.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
a.(*Impl).M() // ERROR "inlining call to \(\*Impl\).M"
a.(*Impl).A() // ERROR "inlining call to \(\*Impl\).A"
v := a.(M)
v.M() // ERROR "devirtualizing v.M to \*Impl$" "inlining call to \(\*Impl\).M"
v.(A).A() // ERROR "devirtualizing v.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
v.(*Impl).A() // ERROR "inlining call to \(\*Impl\).A"
v.(*Impl).M() // ERROR "inlining call to \(\*Impl\).M"
v2 := a.(A)
v2.A() // ERROR "devirtualizing v2.A to \*Impl$" "inlining call to \(\*Impl\).A"
v2.(M).M() // ERROR "devirtualizing v2.\(M\).M to \*Impl$" "inlining call to \(\*Impl\).M"
v2.(*Impl).A() // ERROR "inlining call to \(\*Impl\).A"
v2.(*Impl).M() // ERROR "inlining call to \(\*Impl\).M"
a.(M).(A).A() // ERROR "devirtualizing a.\(M\).\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
a.(A).(M).M() // ERROR "devirtualizing a.\(A\).\(M\).M to \*Impl$" "inlining call to \(\*Impl\).M"
a.(M).(A).(*Impl).A() // ERROR "inlining call to \(\*Impl\).A"
a.(A).(M).(*Impl).M() // ERROR "inlining call to \(\*Impl\).M"
any(a).(M).M() // ERROR "devirtualizing any\(a\).\(M\).M to \*Impl$" "inlining call to \(\*Impl\).M"
any(a).(A).A() // ERROR "devirtualizing any\(a\).\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
any(a).(M).(any).(A).A() // ERROR "devirtualizing any\(a\).\(M\).\(any\).\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
c := any(a)
c.(A).A() // ERROR "devirtualizing c.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
c.(M).M() // ERROR "devirtualizing c.\(M\).M to \*Impl$" "inlining call to \(\*Impl\).M"
M(a).M() // ERROR "devirtualizing M\(a\).M to \*Impl$" "inlining call to \(\*Impl\).M"
M(M(a)).M() // ERROR "devirtualizing M\(M\(a\)\).M to \*Impl$" "inlining call to \(\*Impl\).M"
a2 := a.(A)
A(a2).A() // ERROR "devirtualizing A\(a2\).A to \*Impl$" "inlining call to \(\*Impl\).A"
A(A(a2)).A() // ERROR "devirtualizing A\(A\(a2\)\).A to \*Impl$" "inlining call to \(\*Impl\).A"
{
var a C = &CImpl{} // ERROR "&CImpl{} does not escape$"
a.(any).(C).C() // ERROR "devirtualizing a.\(any\).\(C\).C to \*CImpl$" "inlining call to CImpl.C"
a.(any).(*CImpl).C() // ERROR "inlining call to CImpl.C"
}
}
func typeAssertsWithOkReturn() {
{
var a M = &Impl{} // ERROR "&Impl{} does not escape$"
if v, ok := a.(M); ok {
v.M() // ERROR "devirtualizing v.M to \*Impl$" "inlining call to \(\*Impl\).M"
}
}
{
var a M = &Impl{} // ERROR "&Impl{} does not escape$"
if v, ok := a.(A); ok {
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
}
{
var a M = &Impl{} // ERROR "&Impl{} does not escape$"
v, ok := a.(M)
if ok {
v.M() // ERROR "devirtualizing v.M to \*Impl$" "inlining call to \(\*Impl\).M"
}
}
{
var a M = &Impl{} // ERROR "&Impl{} does not escape$"
v, ok := a.(A)
if ok {
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
}
{
var a M = &Impl{} // ERROR "&Impl{} does not escape$"
v, ok := a.(*Impl)
if ok {
v.A() // ERROR "inlining call to \(\*Impl\).A"
v.M() // ERROR "inlining call to \(\*Impl\).M"
}
}
{
var a M = &Impl{} // ERROR "&Impl{} does not escape$"
v, _ := a.(M)
v.M() // ERROR "devirtualizing v.M to \*Impl$" "inlining call to \(\*Impl\).M"
}
{
var a M = &Impl{} // ERROR "&Impl{} does not escape$"
v, _ := a.(A)
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var a M = &Impl{} // ERROR "&Impl{} does not escape$"
v, _ := a.(*Impl)
v.A() // ERROR "inlining call to \(\*Impl\).A"
v.M() // ERROR "inlining call to \(\*Impl\).M"
}
{
a := newM() // ERROR "&Impl{} does not escape$" "inlining call to newM"
callA(a) // ERROR "devirtualizing m.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A" "inlining call to callA"
callIfA(a) // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A" "inlining call to callIfA"
}
{
_, a := newM2ret() // ERROR "&Impl{} does not escape$" "inlining call to newM2ret"
callA(a) // ERROR "devirtualizing m.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A" "inlining call to callA"
callIfA(a) // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A" "inlining call to callIfA"
}
{
var a M = &Impl{} // ERROR "&Impl{} does not escape$"
// Note the !ok condition, devirtualizing here is fine.
if v, ok := a.(M); !ok {
v.M() // ERROR "devirtualizing v.M to \*Impl$" "inlining call to \(\*Impl\).M"
}
}
{
var a A = newImplNoInline()
if v, ok := a.(M); ok {
v.M() // ERROR "devirtualizing v.M to \*Impl$" "inlining call to \(\*Impl\).M"
}
}
{
var impl2InA A = &Impl2{} // ERROR "&Impl2{} does not escape$"
var a A
a, _ = impl2InA.(*Impl)
// a now contains the zero value of *Impl
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
a := newANoInline()
a.A()
}
{
_, a := newANoInlineRet2()
a.A()
}
}
func newM() M { // ERROR "can inline newM$"
return &Impl{} // ERROR "&Impl{} escapes to heap$"
}
func newM2ret() (int, M) { // ERROR "can inline newM2ret$"
return -1, &Impl{} // ERROR "&Impl{} escapes to heap$"
}
func callA(m M) { // ERROR "can inline callA$" "leaking param: m$"
m.(A).A()
}
func callIfA(m M) { // ERROR "can inline callIfA$" "leaking param: m$"
if v, ok := m.(A); ok {
v.A()
}
}
//go:noinline
func newImplNoInline() *Impl {
return &Impl{} // ERROR "&Impl{} escapes to heap$"
}
//go:noinline
func newImpl2ret2() (string, *Impl2) {
return "str", &Impl2{} // ERROR "&Impl2{} escapes to heap$"
}
//go:noinline
func newImpl2() *Impl2 {
return &Impl2{} // ERROR "&Impl2{} escapes to heap$"
}
//go:noinline
func newANoInline() A {
return &Impl{} // ERROR "&Impl{} escapes to heap$"
}
//go:noinline
func newANoInlineRet2() (string, A) {
return "", &Impl{} // ERROR "&Impl{} escapes to heap$"
}
func testTypeSwitch() {
{
var v A = &Impl{} // ERROR "&Impl{} does not escape$"
switch v := v.(type) {
case A:
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
case M:
v.M() // ERROR "devirtualizing v.M to \*Impl$" "inlining call to \(\*Impl\).M"
}
}
{
var v A = &Impl{} // ERROR "&Impl{} does not escape$"
switch v := v.(type) {
case A:
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
case M:
v.M() // ERROR "devirtualizing v.M to \*Impl$" "inlining call to \(\*Impl\).M"
v = &Impl{} // ERROR "&Impl{} does not escape$"
v.M() // ERROR "devirtualizing v.M to \*Impl$" "inlining call to \(\*Impl\).M"
}
v.(M).M() // ERROR "devirtualizing v.\(M\).M to \*Impl$" "inlining call to \(\*Impl\).M"
}
{
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
switch v1 := v.(type) {
case A:
v1.A()
case M:
v1.M()
v = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
}
}
{
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
switch v := v.(type) {
case A:
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
case M:
v.M() // ERROR "devirtualizing v.M to \*Impl$" "inlining call to \(\*Impl\).M"
case C:
v.C()
}
}
{
var v A = &Impl{} // ERROR "&Impl{} does not escape$"
switch v := v.(type) {
case M:
v.M() // ERROR "devirtualizing v.M to \*Impl$" "inlining call to \(\*Impl\).M"
default:
panic("does not implement M") // ERROR ".does not implement M. escapes to heap$"
}
}
}
func differentTypeAssign() {
{
var a A
a = &Impl{} // ERROR "&Impl{} escapes to heap$"
a = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
a.A()
}
{
a := A(&Impl{}) // ERROR "&Impl{} escapes to heap$"
a = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
a.A()
}
{
a := A(&Impl{}) // ERROR "&Impl{} escapes to heap$"
a.A()
a = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
}
{
a := A(&Impl{}) // ERROR "&Impl{} escapes to heap$"
a = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
var asAny any = a
asAny.(A).A()
}
{
a := A(&Impl{}) // ERROR "&Impl{} escapes to heap$"
var asAny any = a
asAny = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
asAny.(A).A()
}
{
a := A(&Impl{}) // ERROR "&Impl{} escapes to heap$"
var asAny any = a
asAny.(A).A()
asAny = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var a A
a = &Impl{} // ERROR "&Impl{} escapes to heap$"
a = newImpl2()
a.A()
}
{
var a A
a = &Impl{} // ERROR "&Impl{} escapes to heap$"
_, a = newImpl2ret2()
a.A()
}
}
func assignWithTypeAssert() {
{
var i1 A = &Impl{} // ERROR "&Impl{} does not escape$"
var i2 A = &Impl2{} // ERROR "&Impl2{} does not escape$"
i1 = i2.(*Impl) // this will panic
i1.A() // ERROR "devirtualizing i1.A to \*Impl$" "inlining call to \(\*Impl\).A"
i2.A() // ERROR "devirtualizing i2.A to \*Impl2$" "inlining call to \(\*Impl2\).A"
}
{
var i1 A = &Impl{} // ERROR "&Impl{} does not escape$"
var i2 A = &Impl2{} // ERROR "&Impl2{} does not escape$"
i1, _ = i2.(*Impl) // i1 is going to be nil
i1.A() // ERROR "devirtualizing i1.A to \*Impl$" "inlining call to \(\*Impl\).A"
i2.A() // ERROR "devirtualizing i2.A to \*Impl2$" "inlining call to \(\*Impl2\).A"
}
}
func nilIface() {
{
var v A = &Impl{} // ERROR "&Impl{} does not escape$"
v = nil
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var v A = &Impl{} // ERROR "&Impl{} does not escape$"
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
v = nil
}
{
var nilIface A
var v A = &Impl{} // ERROR "&Impl{} does not escape$"
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
v = nilIface
}
{
var nilIface A
var v A = &Impl{} // ERROR "&Impl{} does not escape$"
v = nilIface
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var v A
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
v = &Impl{} // ERROR "&Impl{} does not escape$"
}
{
var v A
var v2 A = v
v2.A() // ERROR "devirtualizing v2.A to \*Impl$" "inlining call to \(\*Impl\).A"
v2 = &Impl{} // ERROR "&Impl{} does not escape$"
}
{
var v A
v.A()
}
{
var v A
var v2 A = v
v2.A()
}
{
var v A
var v2 A
v2 = v
v2.A()
}
}
func longDevirtTest() {
var a interface {
M
A
} = &Impl{} // ERROR "&Impl{} does not escape$"
{
var b A = a
b.A() // ERROR "devirtualizing b.A to \*Impl$" "inlining call to \(\*Impl\).A"
b.(M).M() // ERROR "devirtualizing b.\(M\).M to \*Impl$" "inlining call to \(\*Impl\).M"
}
{
var b M = a
b.M() // ERROR "devirtualizing b.M to \*Impl$" "inlining call to \(\*Impl\).M"
b.(A).A() // ERROR "devirtualizing b.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var b A = a.(M).(A)
b.A() // ERROR "devirtualizing b.A to \*Impl$" "inlining call to \(\*Impl\).A"
b.(M).M() // ERROR "devirtualizing b.\(M\).M to \*Impl$" "inlining call to \(\*Impl\).M"
}
{
var b M = a.(A).(M)
b.M() // ERROR "devirtualizing b.M to \*Impl$" "inlining call to \(\*Impl\).M"
b.(A).A() // ERROR "devirtualizing b.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
}
if v, ok := a.(A); ok {
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
if v, ok := a.(M); ok {
v.M() // ERROR "devirtualizing v.M to \*Impl$" "inlining call to \(\*Impl\).M"
}
{
var c A = a
if v, ok := c.(A); ok {
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
c = &Impl{} // ERROR "&Impl{} does not escape$"
if v, ok := c.(M); ok {
v.M() // ERROR "devirtualizing v.M to \*Impl$" "inlining call to \(\*Impl\).M"
}
if v, ok := c.(interface {
A
M
}); ok {
v.M() // ERROR "devirtualizing v.M to \*Impl$" "inlining call to \(\*Impl\).M"
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
}
}
func deferDevirt() {
var a A
defer func() { // ERROR "can inline deferDevirt.func1$" "func literal does not escape$"
a = &Impl{} // ERROR "&Impl{} escapes to heap$"
}()
a = &Impl{} // ERROR "&Impl{} does not escape$"
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
func deferNoDevirt() {
var a A
defer func() { // ERROR "can inline deferNoDevirt.func1$" "func literal does not escape$"
a = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
}()
a = &Impl{} // ERROR "&Impl{} escapes to heap$"
a.A()
}
//go:noinline
func closureDevirt() {
var a A
func() { // ERROR "func literal does not escape$"
// defer so that it does not lnline.
defer func() {}() // ERROR "can inline closureDevirt.func1.1$" "func literal does not escape$"
a = &Impl{} // ERROR "&Impl{} escapes to heap$"
}()
a = &Impl{} // ERROR "&Impl{} does not escape$"
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
//go:noinline
func closureNoDevirt() {
var a A
func() { // ERROR "func literal does not escape$"
// defer so that it does not lnline.
defer func() {}() // ERROR "can inline closureNoDevirt.func1.1$" "func literal does not escape$"
a = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
}()
a = &Impl{} // ERROR "&Impl{} escapes to heap$"
a.A()
}
var global = "1"
func closureDevirt2() {
var a A
a = &Impl{} // ERROR "&Impl{} does not escape$"
c := func() { // ERROR "can inline closureDevirt2.func1$" "func literal does not escape$"
a = &Impl{} // ERROR "&Impl{} escapes to heap$"
}
if global == "1" {
c = func() { // ERROR "can inline closureDevirt2.func2$" "func literal does not escape$"
a = &Impl{} // ERROR "&Impl{} escapes to heap$"
}
}
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
c()
}
func closureNoDevirt2() {
var a A
a = &Impl{} // ERROR "&Impl{} escapes to heap$"
c := func() { // ERROR "can inline closureNoDevirt2.func1$" "func literal does not escape$"
a = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
}
if global == "1" {
c = func() { // ERROR "can inline closureNoDevirt2.func2$" "func literal does not escape$"
a = &Impl{} // ERROR "&Impl{} escapes to heap$"
}
}
a.A()
c()
}
//go:noinline
func closureDevirt3() {
var a A = &Impl{} // ERROR "&Impl{} does not escape$"
func() { // ERROR "func literal does not escape$"
// defer so that it does not lnline.
defer func() {}() // ERROR "can inline closureDevirt3.func1.1$" "func literal does not escape$"
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}()
func() { // ERROR "can inline closureDevirt3.func2$"
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}() // ERROR "inlining call to closureDevirt3.func2" "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
//go:noinline
func closureNoDevirt3() {
var a A = &Impl{} // ERROR "&Impl{} escapes to heap$"
func() { // ERROR "func literal does not escape$"
// defer so that it does not lnline.
defer func() {}() // ERROR "can inline closureNoDevirt3.func1.1$" "func literal does not escape$"
a.A()
}()
func() { // ERROR "can inline closureNoDevirt3.func2$"
a.A()
}() // ERROR "inlining call to closureNoDevirt3.func2"
a = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
}
//go:noinline
func varDeclaredInClosureReferencesOuter() {
var a A = &Impl{} // ERROR "&Impl{} does not escape$"
func() { // ERROR "func literal does not escape$"
// defer for noinline
defer func() {}() // ERROR "can inline varDeclaredInClosureReferencesOuter.func1.1$" "func literal does not escape$"
var v A = a
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}()
func() { // ERROR "func literal does not escape$"
// defer for noinline
defer func() {}() // ERROR "can inline varDeclaredInClosureReferencesOuter.func2.1$" "func literal does not escape$"
var v A = a
v = &Impl{} // ERROR "&Impl{} does not escape$"
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}()
var b A = &Impl{} // ERROR "&Impl{} escapes to heap$"
func() { // ERROR "func literal does not escape$"
// defer for noinline
defer func() {}() // ERROR "can inline varDeclaredInClosureReferencesOuter.func3.1$" "func literal does not escape$"
var v A = b
v = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
v.A()
}()
func() { // ERROR "func literal does not escape$"
// defer for noinline
defer func() {}() // ERROR "can inline varDeclaredInClosureReferencesOuter.func4.1$" "func literal does not escape$"
var v A = b
v.A()
v = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
}()
}
//go:noinline
func testNamedReturn0() (v A) {
v = &Impl{} // ERROR "&Impl{} escapes to heap$"
v.A()
return
}
//go:noinline
func testNamedReturn1() (v A) {
v = &Impl{} // ERROR "&Impl{} escapes to heap$"
v.A()
return &Impl{} // ERROR "&Impl{} escapes to heap$"
}
func testNamedReturns3() (v A) {
v = &Impl{} // ERROR "&Impl{} escapes to heap$"
defer func() { // ERROR "can inline testNamedReturns3.func1$" "func literal does not escape$"
v.A()
}()
v.A()
return &Impl2{} // ERROR "&Impl2{} escapes to heap$"
}
var (
globalImpl = &Impl{}
globalImpl2 = &Impl2{}
globalA A = &Impl{}
globalM M = &Impl{}
)
func globals() {
{
globalA.A()
globalA.(M).M()
globalM.M()
globalM.(A).A()
a := globalA
a.A()
a.(M).M()
m := globalM
m.M()
m.(A).A()
}
{
var a A = &Impl{} // ERROR "&Impl{} does not escape$"
a = globalImpl
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var a A = &Impl{} // ERROR "&Impl{} does not escape$"
a = A(globalImpl)
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var a A = &Impl{} // ERROR "&Impl{} does not escape$"
a = M(globalImpl).(A)
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var a A = &Impl{} // ERROR "&Impl{} does not escape$"
a = globalA.(*Impl)
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
a = globalM.(*Impl)
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var a A = &Impl{} // ERROR "&Impl{} escapes to heap$"
a = globalImpl2
a.A()
}
{
var a A = &Impl{} // ERROR "&Impl{} escapes to heap$"
a = globalA
a.A()
}
{
var a A = &Impl{} // ERROR "&Impl{} escapes to heap$"
a = globalM.(A)
a.A()
}
}
func mapsDevirt() {
{
m := make(map[int]*Impl) // ERROR "make\(map\[int\]\*Impl\) does not escape$"
var v A = m[0]
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
v.(M).M() // ERROR "devirtualizing v.\(M\).M to \*Impl$" "inlining call to \(\*Impl\).M"
}
{
m := make(map[int]*Impl) // ERROR "make\(map\[int\]\*Impl\) does not escape$"
var v A
var ok bool
if v, ok = m[0]; ok {
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
m := make(map[int]*Impl) // ERROR "make\(map\[int\]\*Impl\) does not escape$"
var v A
v, _ = m[0]
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
}
func mapsNoDevirt() {
{
m := make(map[int]*Impl) // ERROR "make\(map\[int\]\*Impl\) does not escape$"
var v A = m[0]
v.A()
v = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
v.(M).M()
}
{
m := make(map[int]*Impl) // ERROR "make\(map\[int\]\*Impl\) does not escape$"
var v A
var ok bool
if v, ok = m[0]; ok {
v.A()
}
v = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
v.A()
}
{
m := make(map[int]*Impl) // ERROR "make\(map\[int\]\*Impl\) does not escape$"
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
v, _ = m[0]
v = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
v.A()
}
{
m := make(map[int]A) // ERROR "make\(map\[int\]A\) does not escape$"
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
v = m[0]
v.A()
}
{
m := make(map[int]A) // ERROR "make\(map\[int\]A\) does not escape$"
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
var ok bool
if v, ok = m[0]; ok {
v.A()
}
v.A()
}
{
m := make(map[int]A) // ERROR "make\(map\[int\]A\) does not escape$"
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
v, _ = m[0]
v.A()
}
}
func chanDevirt() {
{
m := make(chan *Impl)
var v A = <-m
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
m := make(chan *Impl)
var v A
v = <-m
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
m := make(chan *Impl)
var v A
v, _ = <-m
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
m := make(chan *Impl)
var v A
var ok bool
if v, ok = <-m; ok {
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
m := make(chan *Impl)
var v A
var ok bool
if v, ok = <-m; ok {
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
select {
case <-m:
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
case v = <-m:
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
case v, ok = <-m:
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
}
}
func chanNoDevirt() {
{
m := make(chan *Impl)
var v A = <-m
v = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
v.A()
}
{
m := make(chan *Impl)
var v A
v = <-m
v = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
v.A()
}
{
m := make(chan *Impl)
var v A
v, _ = <-m
v = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
v.A()
}
{
m := make(chan *Impl)
var v A
var ok bool
if v, ok = <-m; ok {
v.A()
}
v = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
v.A()
}
{
m := make(chan *Impl)
var v A = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
var ok bool
if v, ok = <-m; ok {
v.A()
}
}
{
m := make(chan *Impl)
var v A = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
select {
case v = <-m:
v.A()
}
v.A()
}
{
m := make(chan *Impl)
var v A = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
select {
case v, _ = <-m:
v.A()
}
v.A()
}
{
m := make(chan A)
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
v = <-m
v.A()
}
{
m := make(chan A)
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
v, _ = <-m
v.A()
}
{
m := make(chan A)
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
var ok bool
if v, ok = <-m; ok {
v.A()
}
}
{
m := make(chan A)
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
select {
case v = <-m:
v.A()
}
v.A()
}
{
m := make(chan A)
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
select {
case v, _ = <-m:
v.A()
}
v.A()
}
}
func rangeDevirt() {
{
var v A
m := make(map[*Impl]struct{}) // ERROR "make\(map\[\*Impl\]struct {}\) does not escape$"
v = &Impl{} // ERROR "&Impl{} does not escape$"
for v = range m {
}
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var v A
m := make(map[*Impl]*Impl) // ERROR "make\(map\[\*Impl\]\*Impl\) does not escape$"
v = &Impl{} // ERROR "&Impl{} does not escape$"
for v = range m {
}
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var v A
m := make(map[*Impl]*Impl) // ERROR "make\(map\[\*Impl\]\*Impl\) does not escape$"
v = &Impl{} // ERROR "&Impl{} does not escape$"
for _, v = range m {
}
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var v A
m := make(chan *Impl)
v = &Impl{} // ERROR "&Impl{} does not escape$"
for v = range m {
}
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var v A
m := []*Impl{} // ERROR "\[\]\*Impl{} does not escape$"
v = &Impl{} // ERROR "&Impl{} does not escape$"
for _, v = range m {
}
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var v A
v = &Impl{} // ERROR "&Impl{} does not escape$"
impl := &Impl{} // ERROR "&Impl{} does not escape$"
i := 0
for v = impl; i < 10; i++ {
}
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var v A
v = &Impl{} // ERROR "&Impl{} does not escape$"
impl := &Impl{} // ERROR "&Impl{} does not escape$"
i := 0
for v = impl; i < 10; i++ {
}
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var v A
m := [1]*Impl{&Impl{}} // ERROR "&Impl{} does not escape$"
v = &Impl{} // ERROR "&Impl{} does not escape$"
for _, v = range m {
}
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var v A
m := [1]*Impl{&Impl{}} // ERROR "&Impl{} does not escape$"
v = &Impl{} // ERROR "&Impl{} does not escape$"
for _, v = range &m {
}
v.A() // ERROR "devirtualizing v.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
}
func rangeNoDevirt() {
{
var v A = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
m := make(map[*Impl]struct{}) // ERROR "make\(map\[\*Impl\]struct {}\) does not escape$"
for v = range m {
}
v.A()
}
{
var v A = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
m := make(map[*Impl]*Impl) // ERROR "make\(map\[\*Impl\]\*Impl\) does not escape$"
for v = range m {
}
v.A()
}
{
var v A = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
m := make(map[*Impl]*Impl) // ERROR "make\(map\[\*Impl\]\*Impl\) does not escape$"
for _, v = range m {
}
v.A()
}
{
var v A = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
m := make(chan *Impl)
for v = range m {
}
v.A()
}
{
var v A = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
m := []*Impl{} // ERROR "\[\]\*Impl{} does not escape$"
for _, v = range m {
}
v.A()
}
{
var v A
v = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
impl := &Impl{} // ERROR "&Impl{} escapes to heap$"
i := 0
for v = impl; i < 10; i++ {
}
v.A()
}
{
var v A
v = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
impl := &Impl{} // ERROR "&Impl{} escapes to heap$"
i := 0
for v = impl; i < 10; i++ {
}
v.A()
}
{
var v A
m := [1]*Impl{&Impl{}} // ERROR "&Impl{} escapes to heap$"
v = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
for _, v = range m {
}
v.A()
}
{
var v A
m := [1]*Impl{&Impl{}} // ERROR "&Impl{} escapes to heap$"
v = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
for _, v = range &m {
}
v.A()
}
{
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
m := make(map[A]struct{}) // ERROR "make\(map\[A\]struct {}\) does not escape$"
for v = range m {
}
v.A()
}
{
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
m := make(map[A]A) // ERROR "make\(map\[A\]A\) does not escape$"
for v = range m {
}
v.A()
}
{
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
m := make(map[A]A) // ERROR "make\(map\[A\]A\) does not escape$"
for _, v = range m {
}
v.A()
}
{
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
m := make(chan A)
for v = range m {
}
v.A()
}
{
var v A = &Impl{} // ERROR "&Impl{} escapes to heap$"
m := []A{} // ERROR "\[\]A{} does not escape$"
for _, v = range m {
}
v.A()
}
{
var v A
m := [1]A{&Impl{}} // ERROR "&Impl{} escapes to heap$"
v = &Impl{} // ERROR "&Impl{} escapes to heap$"
for _, v = range m {
}
v.A()
}
{
var v A
m := [1]A{&Impl{}} // ERROR "&Impl{} escapes to heap$"
v = &Impl{} // ERROR "&Impl{} escapes to heap$"
for _, v = range &m {
}
v.A()
}
}
var globalInt = 1
func testIfInit() {
{
var a A = &Impl{} // ERROR "&Impl{} does not escape$"
var i = &Impl{} // ERROR "&Impl{} does not escape$"
if a = i; globalInt == 1 {
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
a.(M).M() // ERROR "devirtualizing a.\(M\).M to \*Impl$" "inlining call to \(\*Impl\).M"
}
{
var a A = &Impl{} // ERROR "&Impl{} escapes to heap$"
var i2 = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
if a = i2; globalInt == 1 {
a.A()
}
a.A()
}
}
func testSwitchInit() {
{
var a A = &Impl{} // ERROR "&Impl{} does not escape$"
var i = &Impl{} // ERROR "&Impl{} does not escape$"
switch a = i; globalInt {
case 12:
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
a.(M).M() // ERROR "devirtualizing a.\(M\).M to \*Impl$" "inlining call to \(\*Impl\).M"
}
{
var a A = &Impl{} // ERROR "&Impl{} escapes to heap$"
var i2 = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
switch a = i2; globalInt {
case 12:
a.A()
}
a.A()
}
}
type implWrapper Impl
func (implWrapper) A() {} // ERROR "can inline implWrapper.A$"
//go:noinline
func devirtWrapperType() {
{
i := &Impl{} // ERROR "&Impl{} does not escape$"
// This is an OCONVNOP, so we have to be careful, not to devirtualize it to Impl.A.
var a A = (*implWrapper)(i)
a.A() // ERROR "devirtualizing a.A to \*implWrapper$" "inlining call to implWrapper.A"
}
{
i := Impl{}
// This is an OCONVNOP, so we have to be careful, not to devirtualize it to Impl.A.
var a A = (implWrapper)(i) // ERROR "implWrapper\(i\) does not escape$"
a.A() // ERROR "devirtualizing a.A to implWrapper$" "inlining call to implWrapper.A"
}
}
func selfAssigns() {
{
var a A = &Impl{} // ERROR "&Impl{} does not escape$"
a = a
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var a A = &Impl{} // ERROR "&Impl{} does not escape"
var asAny any = a
asAny = asAny
asAny.(A).A() // ERROR "devirtualizing asAny.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var a A = &Impl{} // ERROR "&Impl{} does not escape"
var asAny any = a
a = asAny.(A)
asAny.(A).A() // ERROR "devirtualizing asAny.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
a.(A).A() // ERROR "devirtualizing a.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
b := a
b.(A).A() // ERROR "devirtualizing b.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var a A = &Impl{} // ERROR "&Impl{} does not escape"
var asAny any = a
asAny = asAny
a = asAny.(A)
asAny = a
asAny.(A).A() // ERROR "devirtualizing asAny.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
asAny.(M).M() // ERROR "devirtualizing asAny.\(M\).M to \*Impl$" "inlining call to \(\*Impl\).M"
}
{
var a A = &Impl{} // ERROR "&Impl{} does not escape"
var asAny A = a
a = asAny.(A)
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
{
var a, b, c A
c = &Impl{} // ERROR "&Impl{} does not escape$"
a = c
c = b
b = c
a = b
b = a
c = a
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
}
}
func boolNoDevirt() {
{
m := make(map[int]*Impl) // ERROR "make\(map\[int\]\*Impl\) does not escape$"
var v any = &Impl{} // ERROR "&Impl{} escapes to heap$"
_, v = m[0] // ERROR ".autotmp_[0-9]+ escapes to heap$"
v.(A).A()
}
{
m := make(chan *Impl)
var v any = &Impl{} // ERROR "&Impl{} escapes to heap$"
select {
case _, v = <-m: // ERROR ".autotmp_[0-9]+ escapes to heap$"
}
v.(A).A()
}
{
m := make(chan *Impl)
var v any = &Impl{} // ERROR "&Impl{} escapes to heap$"
_, v = <-m // ERROR ".autotmp_[0-9]+ escapes to heap$"
v.(A).A()
}
{
var a any = 4 // ERROR "4 does not escape$"
var v any = &Impl{} // ERROR "&Impl{} escapes to heap$"
_, v = a.(int) // ERROR ".autotmp_[0-9]+ escapes to heap$"
v.(A).A()
}
}
func addrTaken() {
{
var a A = &Impl{} // ERROR "&Impl{} escapes to heap$"
var ptrA = &a
a.A()
_ = ptrA
}
{
var a A = &Impl{} // ERROR "&Impl{} escapes to heap$"
var ptrA = &a
*ptrA = &Impl{} // ERROR "&Impl{} escapes to heap$"
a.A()
}
{
var a A = &Impl{} // ERROR "&Impl{} escapes to heap$"
var ptrA = &a
*ptrA = &Impl2{} // ERROR "&Impl2{} escapes to heap$"
a.A()
}
}
func testInvalidAsserts() {
any(0).(interface{ A() }).A() // ERROR "any\(0\) escapes to heap$"
{
var a M = &Impl{} // ERROR "&Impl{} escapes to heap$"
a.(C).C() // this will panic
a.(any).(C).C() // this will panic
}
{
var a C = &CImpl{} // ERROR "&CImpl{} escapes to heap$"
a.(M).M() // this will panic
a.(any).(M).M() // this will panic
}
{
var a C = &CImpl{} // ERROR "&CImpl{} does not escape$"
// this will panic
a.(M).(*Impl).M() // ERROR "inlining call to \(\*Impl\).M"
// this will panic
a.(any).(M).(*Impl).M() // ERROR "inlining call to \(\*Impl\).M"
}
}
type namedBool bool
func (namedBool) M() {} // ERROR "can inline namedBool.M$"
//go:noinline
func namedBoolTest() {
m := map[int]int{} // ERROR "map\[int\]int{} does not escape"
var ok namedBool
_, ok = m[5]
var i M = ok // ERROR "ok does not escape"
i.M() // ERROR "devirtualizing i.M to namedBool$" "inlining call to namedBool.M"
}