Skip to content

Commit 7fea0a5

Browse files
committed
review update
Change-Id: I25f4ecbd6b96496aad681684e75c95b3d8f2a930
1 parent 1d45dfa commit 7fea0a5

File tree

2 files changed

+95
-73
lines changed

2 files changed

+95
-73
lines changed

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

+83-73
Original file line numberDiff line numberDiff line change
@@ -182,30 +182,32 @@ 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]struct{}))
186-
if isNil && typ != nil {
187-
base.Fatalf("typ = %v; want = <nil>", typ)
188-
}
185+
typ = concreteType1(s, n, make(map[*ir.Name]struct{}))
189186
if typ != nil && typ.IsInterface() {
190187
base.Fatalf("typ.IsInterface() = true; want = false; typ = %v", typ)
191188
}
189+
if typ == &noType {
190+
return nil
191+
}
192192
return typ
193193
}
194194

195+
// noType is a sentinel value returned by [concreteType1].
196+
var noType types.Type
197+
195198
// concreteType1 analyzes the node n and returns its concrete type if it is statically known.
196199
// Otherwise, it returns a nil Type, indicating that a concrete type was not determined.
197-
// This can happen in cases where n is assigned an interface type and the concrete type of that
198-
// interface is not statically known (e.g. a non-inlined function call returning an interface type)
199-
// or when multiple distinct concrete types are assigned.
200-
//
201-
// If n is statically known to be nil, this function returns a nil Type with isNil == true.
202-
// 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, seen map[*ir.Name]struct{}) (t *types.Type, isNil bool) {
200+
// When n is known to be statically nil or a self-assignment is detected, in returns a sentinel [noType] type instead.
201+
func concreteType1(s *State, n ir.Node, seen map[*ir.Name]struct{}) (outT *types.Type) {
204202
nn := n // for debug messages
205203

206204
if concreteTypeDebug {
207205
defer func() {
208-
base.Warn("concreteType1(%v) -> (%v;%v)", nn, t, isNil)
206+
t := "&noType"
207+
if outT != &noType {
208+
t = outT.String()
209+
}
210+
base.Warn("concreteType1(%v) -> %v", nn, t)
209211
}()
210212
}
211213

@@ -215,7 +217,7 @@ func concreteType1(s *State, n ir.Node, seen map[*ir.Name]struct{}) (t *types.Ty
215217
}
216218

217219
if !n.Type().IsInterface() {
218-
return n.Type(), false
220+
return n.Type()
219221
}
220222

221223
switch n1 := n.(type) {
@@ -249,12 +251,12 @@ func concreteType1(s *State, n ir.Node, seen map[*ir.Name]struct{}) (t *types.Ty
249251
}
250252

251253
if n.Op() != ir.ONAME {
252-
return nil, false
254+
return nil
253255
}
254256

255257
name := n.(*ir.Name).Canonical()
256258
if name.Class != ir.PAUTO {
257-
return nil, false
259+
return nil
258260
}
259261

260262
if name.Op() != ir.ONAME {
@@ -267,14 +269,14 @@ func concreteType1(s *State, n ir.Node, seen map[*ir.Name]struct{}) (t *types.Ty
267269
}
268270

269271
if name.Addrtaken() {
270-
return nil, false // conservatively assume it's reassigned with a different type indirectly
272+
return nil // conservatively assume it's reassigned with a different type indirectly
271273
}
272274

273275
if _, ok := seen[name]; ok {
274276
// Self assignment, treat it the same as a nil assignment.
275277
// In case this is the only assignment then we are not going to devirtualize anything.
276278
// In case there are other assignment, we still preserve the correct type.
277-
return nil, true
279+
return &noType
278280
}
279281
seen[name] = struct{}{}
280282

@@ -284,19 +286,18 @@ func concreteType1(s *State, n ir.Node, seen map[*ir.Name]struct{}) (t *types.Ty
284286

285287
var typ *types.Type
286288
for _, v := range s.assignments(name) {
287-
t := v.typ
288-
if v.node != nil {
289-
var isNil bool
290-
t, isNil = concreteType1(s, v.node, seen)
291-
if isNil {
292-
if t != nil {
293-
base.Fatalf("t = %v; want = <nil>", t)
294-
}
289+
var t *types.Type
290+
switch v := v.(type) {
291+
case *types.Type:
292+
t = v
293+
case ir.Node:
294+
t = concreteType1(s, v, seen)
295+
if t == &noType {
295296
continue
296297
}
297298
}
298299
if t == nil || (typ != nil && !types.Identical(typ, t)) {
299-
return nil, false
300+
return nil
300301
}
301302
typ = t
302303
}
@@ -305,27 +306,30 @@ func concreteType1(s *State, n ir.Node, seen map[*ir.Name]struct{}) (t *types.Ty
305306

306307
if typ == nil {
307308
// Variable either declared with zero value, or only assigned with nil.
308-
return nil, true
309+
return &noType
309310
}
310311

311-
return typ, false
312+
return typ
312313
}
313314

314-
// valOrTyp stores either node or a type that is assigned to a variable.
315-
// Never both of these fields are populated.
316-
// If both are nil, then either an interface type was assigned (e.g. a non-inlined
317-
// function call returning an interface type, in such case we don't know the
318-
// concrete type) or a basic type (i.e. int), which we know that does not have any
319-
// methods, thus not possible to devirtualize.
320-
type valOrTyp struct {
321-
typ *types.Type
322-
node ir.Node
323-
}
315+
// assignment can be one of:
316+
// - nil - assignment to an interface type.
317+
// - *types.Type - assignment to a concrete type (non-interface).
318+
// - ir.Node - assignment to a ir.Node.
319+
//
320+
// In most cases assignment should be an [ir.Node], but in cases where we
321+
// do not follow the data-flow, we return either a concrete type (*types.Type) or a nil.
322+
// For example in range over a slice, if the slice elem is of an interface type, then we return
323+
// a nil, otherwise the elem's concrete type (We do so because we do not analyze assignment to the
324+
// slice being ranged-over).
325+
type assignment any
324326

325327
// State holds precomputed state for use in [StaticCall].
326328
type State struct {
327-
// ifaceAssignments stores all assignments to all interface variables.
328-
ifaceAssignments map[*ir.Name][]valOrTyp
329+
// ifaceAssignments maps interface variables to all their assignments
330+
// defined inside functions stored in the analyzedFuncs set.
331+
// Note: it does not include direct assignments to nil.
332+
ifaceAssignments map[*ir.Name][]assignment
329333

330334
// ifaceCallExprAssigns stores every [*ir.CallExpr], which has an interface
331335
// result, that is assigned to a variable.
@@ -362,8 +366,8 @@ func (s *State) InlinedCall(fun *ir.Func, origCall *ir.CallExpr, inlinedCall *ir
362366
// Update assignments to reference the new ReturnVars of the inlined call.
363367
for _, ref := range refs {
364368
vt := &s.ifaceAssignments[ref.name][ref.valOrTypeIndex]
365-
if vt.node != nil || vt.typ != nil {
366-
base.Fatalf("unexpected non-empty valOrTyp")
369+
if *vt != nil {
370+
base.Fatalf("unexpected non-nil assignment")
367371
}
368372
if concreteTypeDebug {
369373
base.Warn(
@@ -373,12 +377,12 @@ func (s *State) InlinedCall(fun *ir.Func, origCall *ir.CallExpr, inlinedCall *ir
373377
inlinedCall.ReturnVars[ref.returnIndex].Type(),
374378
)
375379
}
376-
*vt = valOrTyp{node: inlinedCall.ReturnVars[ref.returnIndex]}
380+
*vt = inlinedCall.ReturnVars[ref.returnIndex]
377381
}
378382
}
379383

380384
// assignments returns all assignments to n.
381-
func (s *State) assignments(n *ir.Name) []valOrTyp {
385+
func (s *State) assignments(n *ir.Name) []assignment {
382386
fun := n.Curfn
383387
if fun == nil {
384388
base.Fatalf("n.Curfn = <nil>")
@@ -394,7 +398,7 @@ func (s *State) assignments(n *ir.Name) []valOrTyp {
394398
base.Warn("concreteType(): analyzing assignments in %v func", fun)
395399
}
396400
if s.analyzedFuncs == nil {
397-
s.ifaceAssignments = make(map[*ir.Name][]valOrTyp)
401+
s.ifaceAssignments = make(map[*ir.Name][]assignment)
398402
s.ifaceCallExprAssigns = make(map[*ir.CallExpr][]ifaceAssignRef)
399403
s.analyzedFuncs = make(map[*ir.Func]struct{})
400404
}
@@ -408,7 +412,7 @@ func (s *State) assignments(n *ir.Name) []valOrTyp {
408412

409413
// analyze analyzes every assignment to interface variables in nodes, updating [State].
410414
func (s *State) analyze(nodes ir.Nodes) {
411-
assign := func(name ir.Node, value valOrTyp) (*ir.Name, int) {
415+
assign := func(name ir.Node, assignment assignment) (*ir.Name, int) {
412416
if name == nil || name.Op() != ir.ONAME || ir.IsBlank(name) {
413417
return nil, -1
414418
}
@@ -429,20 +433,26 @@ func (s *State) analyze(nodes ir.Nodes) {
429433
base.Fatalf("reassigned %v", n)
430434
}
431435

432-
// n is assigned with nil, we can safely ignore them, see [StaticCall].
433-
if ir.IsNil(value.node) {
434-
return nil, -1
435-
}
436-
437-
if value.typ != nil && value.typ.IsInterface() {
438-
value.typ = nil
436+
switch a := assignment.(type) {
437+
case nil:
438+
case *types.Type:
439+
if a != nil && a.IsInterface() {
440+
assignment = nil // non-concrete type
441+
}
442+
case ir.Node:
443+
// nil assignment, we can safely ignore them, see [StaticCall].
444+
if ir.IsNil(a) {
445+
return nil, -1
446+
}
447+
default:
448+
base.Fatalf("unexpected type: %v", assignment)
439449
}
440450

441451
if concreteTypeDebug {
442-
base.Warn("analyze(): assignment found %v = (%v;%v)", name, value.typ, value.node)
452+
base.Warn("analyze(): assignment found %v = %v", name, assignment)
443453
}
444454

445-
s.ifaceAssignments[n] = append(s.ifaceAssignments[n], value)
455+
s.ifaceAssignments[n] = append(s.ifaceAssignments[n], assignment)
446456
return n, len(s.ifaceAssignments[n]) - 1
447457
}
448458

@@ -462,35 +472,35 @@ func (s *State) analyze(nodes ir.Nodes) {
462472
}
463473
if call, ok := rhs.(*ir.CallExpr); ok && call.Fun != nil {
464474
retTyp := call.Fun.Type().Results()[0].Type
465-
n, idx := assign(n.X, valOrTyp{typ: retTyp})
475+
n, idx := assign(n.X, retTyp)
466476
if n != nil && retTyp.IsInterface() {
467477
s.ifaceCallExprAssigns[call] = append(s.ifaceCallExprAssigns[call], ifaceAssignRef{n, idx, 0})
468478
}
469479
} else {
470-
assign(n.X, valOrTyp{node: rhs})
480+
assign(n.X, rhs)
471481
}
472482
}
473483
case ir.OAS2:
474484
n := n.(*ir.AssignListStmt)
475485
for i, p := range n.Lhs {
476486
if n.Rhs[i] != nil {
477-
assign(p, valOrTyp{node: n.Rhs[i]})
487+
assign(p, n.Rhs[i])
478488
}
479489
}
480490
case ir.OAS2DOTTYPE:
481491
n := n.(*ir.AssignListStmt)
482492
if n.Rhs[0] == nil {
483493
base.Fatalf("n.Rhs[0] == nil; n = %v", n)
484494
}
485-
assign(n.Lhs[0], valOrTyp{node: n.Rhs[0]})
486-
assign(n.Lhs[1], valOrTyp{}) // boolean does not have methods to devirtualize
495+
assign(n.Lhs[0], n.Rhs[0])
496+
assign(n.Lhs[1], nil) // boolean does not have methods to devirtualize
487497
case ir.OAS2MAPR, ir.OAS2RECV, ir.OSELRECV2:
488498
n := n.(*ir.AssignListStmt)
489499
if n.Rhs[0] == nil {
490500
base.Fatalf("n.Rhs[0] == nil; n = %v", n)
491501
}
492-
assign(n.Lhs[0], valOrTyp{typ: n.Rhs[0].Type()})
493-
assign(n.Lhs[1], valOrTyp{}) // boolean does not have methods to devirtualize
502+
assign(n.Lhs[0], n.Rhs[0].Type())
503+
assign(n.Lhs[1], nil) // boolean does not have methods to devirtualize
494504
case ir.OAS2FUNC:
495505
n := n.(*ir.AssignListStmt)
496506
rhs := n.Rhs[0]
@@ -504,19 +514,19 @@ func (s *State) analyze(nodes ir.Nodes) {
504514
if call, ok := rhs.(*ir.CallExpr); ok {
505515
for i, p := range n.Lhs {
506516
retTyp := call.Fun.Type().Results()[i].Type
507-
n, idx := assign(p, valOrTyp{typ: retTyp})
517+
n, idx := assign(p, retTyp)
508518
if n != nil && retTyp.IsInterface() {
509519
s.ifaceCallExprAssigns[call] = append(s.ifaceCallExprAssigns[call], ifaceAssignRef{n, idx, i})
510520
}
511521
}
512522
} else if call, ok := rhs.(*ir.InlinedCallExpr); ok {
513523
for i, p := range n.Lhs {
514-
assign(p, valOrTyp{node: call.ReturnVars[i]})
524+
assign(p, call.ReturnVars[i])
515525
}
516526
} else {
517527
// TODO: can we reach here?
518528
for _, p := range n.Lhs {
519-
assign(p, valOrTyp{})
529+
assign(p, nil)
520530
}
521531
}
522532
case ir.ORANGE:
@@ -529,18 +539,18 @@ func (s *State) analyze(nodes ir.Nodes) {
529539
}
530540

531541
if xTyp.IsArray() || xTyp.IsSlice() {
532-
assign(n.Key, valOrTyp{}) // boolean
533-
assign(n.Value, valOrTyp{typ: xTyp.Elem()})
542+
assign(n.Key, nil) // inteager does not have methods to devirtualize
543+
assign(n.Value, xTyp.Elem())
534544
} else if xTyp.IsChan() {
535-
assign(n.Key, valOrTyp{typ: xTyp.Elem()})
545+
assign(n.Key, xTyp.Elem())
536546
base.Assertf(n.Value == nil, "n.Value != nil in range over chan")
537547
} else if xTyp.IsMap() {
538-
assign(n.Key, valOrTyp{typ: xTyp.Key()})
539-
assign(n.Value, valOrTyp{typ: xTyp.Elem()})
548+
assign(n.Key, xTyp.Key())
549+
assign(n.Value, xTyp.Elem())
540550
} else if xTyp.IsInteger() || xTyp.IsString() {
541551
// Range over int/string, results do not have methods, so nothing to devirtualize.
542-
assign(n.Key, valOrTyp{})
543-
assign(n.Value, valOrTyp{})
552+
assign(n.Key, nil)
553+
assign(n.Value, nil)
544554
} else {
545555
// We will not reach here in case of an range-over-func, as it is
546556
// rewrtten to function calls in the noder package.
@@ -554,7 +564,7 @@ func (s *State) analyze(nodes ir.Nodes) {
554564
base.Assert(guard.Tag == nil)
555565
continue
556566
}
557-
assign(v.Var, valOrTyp{node: guard.X})
567+
assign(v.Var, guard.X)
558568
}
559569
}
560570
case ir.OCLOSURE:

test/devirtualization.go

+12
Original file line numberDiff line numberDiff line change
@@ -1262,3 +1262,15 @@ func testInvalidAsserts() {
12621262
a.(any).(M).(*Impl).M() // ERROR "inlining call to \(\*Impl\).M"
12631263
}
12641264
}
1265+
1266+
type namedBool bool
1267+
1268+
func (namedBool) M() {} // ERROR "can inline namedBool.M$"
1269+
1270+
func namedBoolTest() {
1271+
m := map[int]int{} // ERROR "map\[int\]int{} does not escape"
1272+
var ok namedBool
1273+
_, ok = m[5]
1274+
var i M = ok // ERROR "ok does not escape"
1275+
i.M() // ERROR "devirtualizing i.M to namedBool$" "inlining call to namedBool.M"
1276+
}

0 commit comments

Comments
 (0)