Skip to content

Commit d6e3c35

Browse files
committed
self assignments
Change-Id: Ic8164073baddc803e8e102c3088aec4724c7535f
1 parent f82d9e4 commit d6e3c35

File tree

2 files changed

+42
-35
lines changed

2 files changed

+42
-35
lines changed

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

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ const concreteTypeDebug = false
182182
// Returns nil when the concrete type could not be determined, or when there are multiple
183183
// (different) types assigned to an interface.
184184
func concreteType(s *State, n ir.Node) (typ *types.Type) {
185-
typ, isNil := concreteType1(s, n, make(map[*ir.Name]*types.Type))
185+
typ, isNil := concreteType1(s, n, make(map[*ir.Name]struct{}))
186186
if isNil && typ != nil {
187187
base.Fatalf("typ = %v; want = <nil>", typ)
188188
}
@@ -200,7 +200,7 @@ func concreteType(s *State, n ir.Node) (typ *types.Type) {
200200
//
201201
// If n is statically known to be nil, this function returns a nil Type with isNil == true.
202202
// However, if any concrete type is found, it is returned instead, even if n was assigned with nil.
203-
func concreteType1(s *State, n ir.Node, analyzed map[*ir.Name]*types.Type) (t *types.Type, isNil bool) {
203+
func concreteType1(s *State, n ir.Node, seen map[*ir.Name]struct{}) (t *types.Type, isNil bool) {
204204
nn := n // for debug messages
205205

206206
if concreteTypeDebug {
@@ -270,15 +270,13 @@ func concreteType1(s *State, n ir.Node, analyzed map[*ir.Name]*types.Type) (t *t
270270
return nil, false // conservatively assume it's reassigned with a different type indirectly
271271
}
272272

273-
if typ, ok := analyzed[name]; ok {
274-
return typ, false
273+
if _, ok := seen[name]; ok {
274+
// Self assignment, treat is the same as a nil assignment.
275+
// In case this is the only assignment then we are not going to devirtualize anything.
276+
// In case there are other assignment, we still preserve the correct type.
277+
return nil, true
275278
}
276-
277-
// For now set the Type to nil, as we don't know it yet, we will update
278-
// it at the end of this function, if we find a concrete type.
279-
// This is not ideal, as in-process concreteType1 calls (that this function also
280-
// executes) will get a nil (from the map lookup above), where we could determine the type.
281-
analyzed[name] = nil
279+
seen[name] = struct{}{}
282280

283281
if concreteTypeDebug {
284282
base.Warn("concreteType1(%v): analyzing assignments to %v", nn, name)
@@ -289,7 +287,7 @@ func concreteType1(s *State, n ir.Node, analyzed map[*ir.Name]*types.Type) (t *t
289287
t := v.typ
290288
if v.node != nil {
291289
var isNil bool
292-
t, isNil = concreteType1(s, v.node, analyzed)
290+
t, isNil = concreteType1(s, v.node, seen)
293291
if isNil {
294292
if t != nil {
295293
base.Fatalf("t = %v; want = <nil>", t)
@@ -303,15 +301,13 @@ func concreteType1(s *State, n ir.Node, analyzed map[*ir.Name]*types.Type) (t *t
303301
typ = t
304302
}
305303

304+
delete(seen, name)
305+
306306
if typ == nil {
307307
// Variable either declared with zero value, or only assigned with nil.
308-
// For now don't bother storing the information that we could have
309-
// assigned nil in the analyzed map, if we access the same name again we will
310-
// get an result as if an unknown concrete type was assigned.
311308
return nil, true
312309
}
313310

314-
analyzed[name] = typ
315311
return typ, false
316312
}
317313

@@ -346,16 +342,16 @@ type ifaceAssignRef struct {
346342
}
347343

348344
// InlinedCall updates the [State] to take into account a newly inlined call.
349-
func (s *State) InlinedCall(fun *ir.Func, origCall *ir.CallExpr, newInlinedCall *ir.InlinedCallExpr) {
345+
func (s *State) InlinedCall(fun *ir.Func, origCall *ir.CallExpr, inlinedCall *ir.InlinedCallExpr) {
350346
if _, ok := s.analyzedFuncs[fun]; !ok {
351347
// Full analyze has not been yet executed for the provided function, so we can skip it for now.
352348
// When no devirtualization happens in a function, it is unnecessary to analyze it.
353349
return
354350
}
355351

356352
// Analyze assignments in the newly inlined function.
357-
s.analyze(newInlinedCall.Init())
358-
s.analyze(newInlinedCall.Body)
353+
s.analyze(inlinedCall.Init())
354+
s.analyze(inlinedCall.Body)
359355

360356
refs, ok := s.ifaceCallExprAssigns[origCall]
361357
if !ok {
@@ -372,12 +368,12 @@ func (s *State) InlinedCall(fun *ir.Func, origCall *ir.CallExpr, newInlinedCall
372368
if concreteTypeDebug {
373369
base.Warn(
374370
"InlinedCall(%v, %v): replacing interface node in (%v,%v) to %v (typ %v)",
375-
origCall, newInlinedCall, ref.name, ref.valOrTypeIndex,
376-
newInlinedCall.ReturnVars[ref.returnIndex],
377-
newInlinedCall.ReturnVars[ref.returnIndex].Type(),
371+
origCall, inlinedCall, ref.name, ref.valOrTypeIndex,
372+
inlinedCall.ReturnVars[ref.returnIndex],
373+
inlinedCall.ReturnVars[ref.returnIndex].Type(),
378374
)
379375
}
380-
*vt = valOrTyp{node: newInlinedCall.ReturnVars[ref.returnIndex]}
376+
*vt = valOrTyp{node: inlinedCall.ReturnVars[ref.returnIndex]}
381377
}
382378
}
383379

test/devirtualization.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,39 +1143,50 @@ func devirtWrapperType() {
11431143

11441144
func selfAssigns() {
11451145
{
1146-
var a A = &Impl{} // ERROR "&Impl{} escapes to heap$"
1146+
var a A = &Impl{} // ERROR "&Impl{} does not escape$"
11471147
a = a
1148-
a.A()
1148+
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
11491149
}
11501150
{
1151-
var a A = &Impl{} // ERROR "&Impl{} escapes to heap$"
1151+
var a A = &Impl{} // ERROR "&Impl{} does not escape"
11521152
var asAny any = a
11531153
asAny = asAny
1154-
asAny.(A).A()
1154+
asAny.(A).A() // ERROR "devirtualizing asAny.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
11551155
}
11561156
{
1157-
var a A = &Impl{} // ERROR "&Impl{} escapes to heap$"
1157+
var a A = &Impl{} // ERROR "&Impl{} does not escape"
11581158
var asAny any = a
11591159
a = asAny.(A)
1160-
asAny.(A).A()
1161-
a.(A).A()
1160+
asAny.(A).A() // ERROR "devirtualizing asAny.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
1161+
a.(A).A() // ERROR "devirtualizing a.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
11621162
b := a
1163-
b.(A).A()
1163+
b.(A).A() // ERROR "devirtualizing b.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
11641164
}
11651165
{
1166-
var a A = &Impl{} // ERROR "&Impl{} escapes to heap$"
1166+
var a A = &Impl{} // ERROR "&Impl{} does not escape"
11671167
var asAny any = a
11681168
asAny = asAny
11691169
a = asAny.(A)
11701170
asAny = a
1171-
asAny.(A).A()
1172-
asAny.(M).M()
1171+
asAny.(A).A() // ERROR "devirtualizing asAny.\(A\).A to \*Impl$" "inlining call to \(\*Impl\).A"
1172+
asAny.(M).M() // ERROR "devirtualizing asAny.\(M\).M to \*Impl$" "inlining call to \(\*Impl\).M"
11731173
}
11741174
{
1175-
var a A = &Impl{} // ERROR "&Impl{} escapes to heap$"
1175+
var a A = &Impl{} // ERROR "&Impl{} does not escape"
11761176
var asAny A = a
11771177
a = asAny.(A)
1178-
a.A()
1178+
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
1179+
}
1180+
{
1181+
var a, b, c A
1182+
c = &Impl{} // ERROR "&Impl{} does not escape$"
1183+
a = c
1184+
c = b
1185+
b = c
1186+
a = b
1187+
b = a
1188+
c = a
1189+
a.A() // ERROR "devirtualizing a.A to \*Impl$" "inlining call to \(\*Impl\).A"
11791190
}
11801191
}
11811192

0 commit comments

Comments
 (0)