Skip to content

Commit b67d49e

Browse files
committed
move StaticType to devirtualize pkg
Change-Id: Iadcbf84a666a3e43ce2b460e064b111efa0f2022
1 parent 291add4 commit b67d49e

File tree

3 files changed

+137
-37
lines changed

3 files changed

+137
-37
lines changed

src/cmd/compile/internal/devirtualize/devirtualize.go

+88-1
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,16 @@ func StaticCall(call *ir.CallExpr) {
4040
}
4141

4242
sel := call.Fun.(*ir.SelectorExpr)
43-
typ := ir.StaticType(sel.X)
43+
typ := staticType(sel.X)
4444
if typ == nil {
4545
return
4646
}
4747

48+
// Don't try to devirtualize calls that we statically know that would have failed at runtime.
49+
// This can happen in such case: any(0).(interface {A()}).A(), this typechecks without
50+
// any errors, but will cause a runtime panic. We statically know that int(0) does not
51+
// implement that interface, thus we skip the devirtualization, as it is not possible
52+
// to make a type assertion from interface{A()} to int (int does not implement A()).
4853
if !typecheck.Implements(typ, sel.X.Type()) {
4954
return
5055
}
@@ -136,3 +141,85 @@ func StaticCall(call *ir.CallExpr) {
136141
// Desugar OCALLMETH, if we created one (#57309).
137142
typecheck.FixMethodCall(call)
138143
}
144+
145+
func staticType(n ir.Node) *types.Type {
146+
for {
147+
switch n1 := n.(type) {
148+
case *ir.ConvExpr:
149+
if n1.Op() == ir.OCONVNOP || n1.Op() == ir.OCONVIFACE {
150+
n = n1.X
151+
continue
152+
}
153+
case *ir.InlinedCallExpr:
154+
if n1.Op() == ir.OINLCALL {
155+
n = n1.SingleResult()
156+
continue
157+
}
158+
case *ir.ParenExpr:
159+
n = n1.X
160+
continue
161+
case *ir.TypeAssertExpr:
162+
n = n1.X
163+
continue
164+
}
165+
166+
n1 := staticValue(n)
167+
if n1 == nil {
168+
if n.Type().IsInterface() {
169+
return nil
170+
}
171+
return n.Type()
172+
}
173+
n = n1
174+
}
175+
}
176+
177+
func staticValue(nn ir.Node) ir.Node {
178+
if nn.Op() != ir.ONAME {
179+
return nil
180+
}
181+
182+
n := nn.(*ir.Name).Canonical()
183+
if n.Class != ir.PAUTO {
184+
return nil
185+
}
186+
187+
defn := n.Defn
188+
if defn == nil {
189+
return nil
190+
}
191+
192+
var rhs ir.Node
193+
FindRHS:
194+
switch defn.Op() {
195+
case ir.OAS:
196+
defn := defn.(*ir.AssignStmt)
197+
rhs = defn.Y
198+
case ir.OAS2:
199+
defn := defn.(*ir.AssignListStmt)
200+
for i, lhs := range defn.Lhs {
201+
if lhs == n {
202+
rhs = defn.Rhs[i]
203+
break FindRHS
204+
}
205+
}
206+
base.Fatalf("%v missing from LHS of %v", n, defn)
207+
case ir.OAS2DOTTYPE:
208+
defn := defn.(*ir.AssignListStmt)
209+
if defn.Lhs[0] == n {
210+
rhs = defn.Rhs[0]
211+
}
212+
default:
213+
return nil
214+
}
215+
216+
if rhs == nil {
217+
base.Fatalf("RHS is nil: %v", defn)
218+
}
219+
220+
if ir.Reassigned(n) {
221+
return nil
222+
}
223+
224+
return rhs
225+
}

src/cmd/compile/internal/ir/expr.go

+2-36
Original file line numberDiff line numberDiff line change
@@ -840,18 +840,6 @@ func IsAddressable(n Node) bool {
840840
return false
841841
}
842842

843-
// StaticType is like StaticValue, but also follows ODOTTYPE and OCONVIFACE.
844-
func StaticType(n Node) *types.Type {
845-
out := staticValue(n, true)
846-
847-
typ := out.Type()
848-
if typ.IsInterface() {
849-
return nil
850-
}
851-
852-
return typ
853-
}
854-
855843
// StaticValue analyzes n to find the earliest expression that always
856844
// evaluates to the same value as n, which might be from an enclosing
857845
// function.
@@ -867,22 +855,13 @@ func StaticType(n Node) *types.Type {
867855
// calling StaticValue on the "int(y)" expression returns the outer
868856
// "g()" expression.
869857
func StaticValue(n Node) Node {
870-
return staticValue(n, false)
871-
872-
}
873-
874-
func staticValue(n Node, forDevirt bool) Node {
875858
for {
876859
switch n1 := n.(type) {
877860
case *ConvExpr:
878861
if n1.Op() == OCONVNOP {
879862
n = n1.X
880863
continue
881864
}
882-
if forDevirt && n1.Op() == OCONVIFACE {
883-
n = n1.X
884-
continue
885-
}
886865
case *InlinedCallExpr:
887866
if n1.Op() == OINLCALL {
888867
n = n1.SingleResult()
@@ -891,22 +870,17 @@ func staticValue(n Node, forDevirt bool) Node {
891870
case *ParenExpr:
892871
n = n1.X
893872
continue
894-
case *TypeAssertExpr:
895-
if forDevirt {
896-
n = n1.X
897-
continue
898-
}
899873
}
900874

901-
n1 := staticValue1(n, forDevirt)
875+
n1 := staticValue1(n)
902876
if n1 == nil {
903877
return n
904878
}
905879
n = n1
906880
}
907881
}
908882

909-
func staticValue1(nn Node, forDevirt bool) Node {
883+
func staticValue1(nn Node) Node {
910884
if nn.Op() != ONAME {
911885
return nil
912886
}
@@ -935,14 +909,6 @@ FindRHS:
935909
}
936910
}
937911
base.Fatalf("%v missing from LHS of %v", n, defn)
938-
case OAS2DOTTYPE:
939-
if !forDevirt {
940-
return nil
941-
}
942-
defn := defn.(*AssignListStmt)
943-
if defn.Lhs[0] == n {
944-
rhs = defn.Rhs[0]
945-
}
946912
default:
947913
return nil
948914
}

test/escape_iface_with_devirt_type_assertions.go

+47
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,55 @@ func callIfA(m M) { // ERROR "can inline" "leaking param"
143143
}
144144
}
145145

146+
//go:noinline
147+
func newImplNoInline() *Impl {
148+
return &Impl{} // ERROR "escapes"
149+
}
150+
151+
func t3() {
152+
{
153+
var a A = newImplNoInline()
154+
if v, ok := a.(M); ok {
155+
v.M() // ERROR "devirtualizing" "inlining call"
156+
}
157+
}
158+
{
159+
m := make(map[*Impl]struct{}) // ERROR "does not escape"
160+
for v := range m {
161+
var v A = v
162+
if v, ok := v.(M); ok {
163+
v.M() // ERROR "devirtualizing" "inlining call"
164+
}
165+
}
166+
}
167+
{
168+
m := make(map[int]*Impl) // ERROR "does not escape"
169+
for _, v := range m {
170+
var v A = v
171+
if v, ok := v.(M); ok {
172+
v.M() // ERROR "devirtualizing" "inlining call"
173+
}
174+
}
175+
}
176+
{
177+
m := make(map[int]*Impl) // ERROR "does not escape"
178+
var v A = m[0]
179+
if v, ok := v.(M); ok {
180+
v.M() // ERROR "devirtualizing" "inlining call"
181+
}
182+
}
183+
{
184+
m := make(chan *Impl)
185+
var v A = <-m
186+
if v, ok := v.(M); ok {
187+
v.M() // ERROR "devirtualizing" "inlining call"
188+
}
189+
}
190+
}
191+
146192
//go:noinline
147193
func testInvalidAsserts() {
194+
any(0).(interface{ A() }).A() // ERROR "escapes"
148195
{
149196
var a M = &Impl{} // ERROR "escapes"
150197
a.(C).C() // this will panic

0 commit comments

Comments
 (0)