@@ -508,7 +508,7 @@ func inline(logf func(string, ...any), caller *Caller, callee *gobCallee) (*resu
508
508
updateCalleeParams (calleeDecl , params )
509
509
510
510
// Create a var (param = arg; ...) decl for use by some strategies.
511
- bindingDeclStmt := createBindingDecl (logf , caller , args , calleeDecl )
511
+ bindingDeclStmt := createBindingDecl (logf , caller , args , calleeDecl , callee . Results )
512
512
513
513
var remainingArgs []ast.Expr
514
514
for _ , arg := range args {
@@ -577,14 +577,12 @@ func inline(logf func(string, ...any), caller *Caller, callee *gobCallee) (*resu
577
577
return res , nil
578
578
}
579
579
580
- // Attempt to reduce parameterless calls
581
- // whose result variables do not escape.
582
- allParamsSubstituted := forall (params , func (i int , p * parameter ) bool {
583
- return p == nil
584
- })
585
- noResultEscapes := ! exists (callee .Results , func (i int , r * paramInfo ) bool {
586
- return r .Escapes
587
- })
580
+ // If all parameters have been substituted and no result
581
+ // variable is referenced, we don't need a binding decl.
582
+ // This may enable better reduction strategies.
583
+ allResultsUnreferenced := forall (callee .Results , func (i int , r * paramInfo ) bool { return len (r .Refs ) == 0 })
584
+ needBindingDecl := ! allResultsUnreferenced ||
585
+ exists (params , func (i int , p * parameter ) bool { return p != nil })
588
586
589
587
// Special case: call to { return exprs }.
590
588
//
@@ -595,9 +593,8 @@ func inline(logf func(string, ...any), caller *Caller, callee *gobCallee) (*resu
595
593
//
596
594
// If:
597
595
// - the body is just "return expr" with trivial implicit conversions,
598
- // - all parameters can be eliminated
599
- // (by substitution, or a binding decl),
600
- // - no result var escapes,
596
+ // - all parameters and result vars can be eliminated
597
+ // or replaced by a binding decl,
601
598
// then the call expression can be replaced by the
602
599
// callee's body expression, suitably substituted.
603
600
if len (calleeDecl .Body .List ) == 1 &&
@@ -610,14 +607,14 @@ func inline(logf func(string, ...any), caller *Caller, callee *gobCallee) (*resu
610
607
611
608
// statement context
612
609
if stmt , ok := context .(* ast.ExprStmt ); ok &&
613
- (allParamsSubstituted && noResultEscapes || bindingDeclStmt != nil ) {
610
+ (! needBindingDecl || bindingDeclStmt != nil ) {
614
611
logf ("strategy: reduce stmt-context call to { return exprs }" )
615
612
clearPositions (calleeDecl .Body )
616
613
617
614
if callee .ValidForCallStmt {
618
615
logf ("callee body is valid as statement" )
619
616
// Inv: len(results) == 1
620
- if allParamsSubstituted && noResultEscapes {
617
+ if ! needBindingDecl {
621
618
// Reduces to: expr
622
619
res .old = caller .Call
623
620
res .new = results [0 ]
@@ -643,7 +640,7 @@ func inline(logf func(string, ...any), caller *Caller, callee *gobCallee) (*resu
643
640
Rhs : results ,
644
641
}
645
642
res .old = stmt
646
- if allParamsSubstituted && noResultEscapes {
643
+ if ! needBindingDecl {
647
644
// Reduces to: _, _ = exprs
648
645
res .new = discard
649
646
} else {
@@ -660,7 +657,7 @@ func inline(logf func(string, ...any), caller *Caller, callee *gobCallee) (*resu
660
657
}
661
658
662
659
// expression context
663
- if allParamsSubstituted && noResultEscapes {
660
+ if ! needBindingDecl {
664
661
clearPositions (calleeDecl .Body )
665
662
666
663
if callee .NumResults == 1 {
@@ -725,12 +722,12 @@ func inline(logf func(string, ...any), caller *Caller, callee *gobCallee) (*resu
725
722
// { var (bindings); body }
726
723
// { body }
727
724
// so long as:
728
- // - all parameters can be eliminated
729
- // (by substitution, or a binding decl),
725
+ // - all parameters can be eliminated or replaced by a binding decl,
730
726
// - call is a tail-call;
731
727
// - all returns in body have trivial result conversions;
732
728
// - there is no label conflict;
733
- // - no result variable is referenced by name.
729
+ // - no result variable is referenced by name,
730
+ // or implicitly by a bare return.
734
731
//
735
732
// The body may use defer, arbitrary control flow, and
736
733
// multiple returns.
@@ -744,16 +741,14 @@ func inline(logf func(string, ...any), caller *Caller, callee *gobCallee) (*resu
744
741
if ret , ok := callContext (caller .path ).(* ast.ReturnStmt ); ok &&
745
742
len (ret .Results ) == 1 &&
746
743
callee .TrivialReturns == callee .TotalReturns &&
747
- (allParamsSubstituted && noResultEscapes || bindingDeclStmt != nil ) &&
744
+ ! callee .HasBareReturn &&
745
+ (! needBindingDecl || bindingDeclStmt != nil ) &&
748
746
! hasLabelConflict (caller .path , callee .Labels ) &&
749
- forall (callee .Results , func (i int , p * paramInfo ) bool {
750
- // all result vars are unreferenced
751
- return len (p .Refs ) == 0
752
- }) {
747
+ allResultsUnreferenced {
753
748
logf ("strategy: reduce tail-call" )
754
749
body := calleeDecl .Body
755
750
clearPositions (body )
756
- if ! ( allParamsSubstituted && noResultEscapes ) {
751
+ if needBindingDecl {
757
752
body .List = prepend (bindingDeclStmt , body .List ... )
758
753
}
759
754
res .old = ret
@@ -774,20 +769,20 @@ func inline(logf func(string, ...any), caller *Caller, callee *gobCallee) (*resu
774
769
// - callee is a void function (no returns)
775
770
// - callee does not use defer
776
771
// - there is no label conflict between caller and callee
777
- // - all parameters can be eliminated
778
- // (by substitution, or a binding decl) ,
772
+ // - all parameters and result vars can be eliminated
773
+ // or replaced by a binding decl,
779
774
//
780
775
// If there is only a single statement, the braces are omitted.
781
776
if stmt := callStmt (caller .path ); stmt != nil &&
782
- (allParamsSubstituted && noResultEscapes || bindingDeclStmt != nil ) &&
777
+ (! needBindingDecl || bindingDeclStmt != nil ) &&
783
778
! callee .HasDefer &&
784
779
! hasLabelConflict (caller .path , callee .Labels ) &&
785
780
callee .TotalReturns == 0 {
786
781
logf ("strategy: reduce stmt-context call to { stmts }" )
787
782
body := calleeDecl .Body
788
783
var repl ast.Stmt = body
789
784
clearPositions (repl )
790
- if ! ( allParamsSubstituted && noResultEscapes ) {
785
+ if needBindingDecl {
791
786
body .List = prepend (bindingDeclStmt , body .List ... )
792
787
}
793
788
if len (body .List ) == 1 {
@@ -1175,11 +1170,13 @@ func updateCalleeParams(calleeDecl *ast.FuncDecl, params []*parameter) {
1175
1170
}
1176
1171
1177
1172
// createBindingDecl constructs a "binding decl" that implements
1178
- // parameter assignment.
1173
+ // parameter assignment and declares any named result variables
1174
+ // referenced by the callee.
1179
1175
//
1180
- // If we succeed, the declaration may be used by reduction
1181
- // strategies to relax the requirement that all parameters
1182
- // have been substituted.
1176
+ // It may not always be possible to create the decl (e.g. due to
1177
+ // shadowing), in which case it returns nil; but if it succeeds, the
1178
+ // declaration may be used by reduction strategies to relax the
1179
+ // requirement that all parameters have been substituted.
1183
1180
//
1184
1181
// For example, a call:
1185
1182
//
@@ -1210,7 +1207,7 @@ func updateCalleeParams(calleeDecl *ast.FuncDecl, params []*parameter) {
1210
1207
//
1211
1208
// Strategies may impose additional checks on return
1212
1209
// conversions, labels, defer, etc.
1213
- func createBindingDecl (logf func (string , ... any ), caller * Caller , args []* argument , calleeDecl * ast.FuncDecl ) ast.Stmt {
1210
+ func createBindingDecl (logf func (string , ... any ), caller * Caller , args []* argument , calleeDecl * ast.FuncDecl , results [] * paramInfo ) ast.Stmt {
1214
1211
// Spread calls are tricky as they may not align with the
1215
1212
// parameters' field groupings nor types.
1216
1213
// For example, given
@@ -1228,27 +1225,15 @@ func createBindingDecl(logf func(string, ...any), caller *Caller, args []*argume
1228
1225
return nil
1229
1226
}
1230
1227
1231
- // Compute remaining argument expressions.
1232
- var values []ast.Expr
1233
- for _ , arg := range args {
1234
- if arg != nil {
1235
- values = append (values , arg .expr )
1236
- }
1237
- }
1238
-
1239
1228
var (
1240
1229
specs []ast.Spec
1241
1230
shadowed = make (map [string ]bool ) // names defined by previous specs
1242
1231
)
1243
- for _ , field := range calleeDecl .Type .Params .List {
1244
- // Each field (param group) becomes a ValueSpec.
1245
- spec := & ast.ValueSpec {
1246
- Names : field .Names ,
1247
- Type : field .Type ,
1248
- Values : values [:len (field .Names )],
1249
- }
1250
- values = values [len (field .Names ):]
1251
-
1232
+ // shadow reports whether any name referenced by spec is
1233
+ // shadowed by a name declared by a previous spec (since,
1234
+ // unlike parameters, each spec of a var decl is within the
1235
+ // scope of the previous specs).
1236
+ shadow := func (spec * ast.ValueSpec ) bool {
1252
1237
// Compute union of free names of type and values
1253
1238
// and detect shadowing. Values is the arguments
1254
1239
// (caller syntax), so we can use type info.
@@ -1260,22 +1245,78 @@ func createBindingDecl(logf func(string, ...any), caller *Caller, args []*argume
1260
1245
free [name ] = true
1261
1246
}
1262
1247
}
1263
- freeishNames (free , field .Type )
1248
+ freeishNames (free , spec .Type )
1264
1249
for name := range free {
1265
1250
if shadowed [name ] {
1266
1251
logf ("binding decl would shadow free name %q" , name )
1267
- return nil
1252
+ return true
1268
1253
}
1269
1254
}
1270
1255
for _ , id := range spec .Names {
1271
1256
if id .Name != "_" {
1272
1257
shadowed [id .Name ] = true
1273
1258
}
1274
1259
}
1260
+ return false
1261
+ }
1275
1262
1263
+ // parameters
1264
+ //
1265
+ // Bind parameters that were not eliminated through
1266
+ // substitution. (Non-nil arguments correspond to the
1267
+ // remaining parameters in calleeDecl.)
1268
+ var values []ast.Expr
1269
+ for _ , arg := range args {
1270
+ if arg != nil {
1271
+ values = append (values , arg .expr )
1272
+ }
1273
+ }
1274
+ for _ , field := range calleeDecl .Type .Params .List {
1275
+ // Each field (param group) becomes a ValueSpec.
1276
+ spec := & ast.ValueSpec {
1277
+ Names : field .Names ,
1278
+ Type : field .Type ,
1279
+ Values : values [:len (field .Names )],
1280
+ }
1281
+ values = values [len (field .Names ):]
1282
+ if shadow (spec ) {
1283
+ return nil
1284
+ }
1276
1285
specs = append (specs , spec )
1277
1286
}
1278
1287
assert (len (values ) == 0 , "args/params mismatch" )
1288
+
1289
+ // results
1290
+ //
1291
+ // Add specs to declare any named result
1292
+ // variables that are referenced by the body.
1293
+ if calleeDecl .Type .Results != nil {
1294
+ resultIdx := 0
1295
+ for _ , field := range calleeDecl .Type .Results .List {
1296
+ if field .Names == nil {
1297
+ resultIdx ++
1298
+ continue // unnamed field
1299
+ }
1300
+ var names []* ast.Ident
1301
+ for _ , id := range field .Names {
1302
+ if len (results [resultIdx ].Refs ) > 0 {
1303
+ names = append (names , id )
1304
+ }
1305
+ resultIdx ++
1306
+ }
1307
+ if len (names ) > 0 {
1308
+ spec := & ast.ValueSpec {
1309
+ Names : names ,
1310
+ Type : field .Type ,
1311
+ }
1312
+ if shadow (spec ) {
1313
+ return nil
1314
+ }
1315
+ specs = append (specs , spec )
1316
+ }
1317
+ }
1318
+ }
1319
+
1279
1320
decl := & ast.DeclStmt {
1280
1321
Decl : & ast.GenDecl {
1281
1322
Tok : token .VAR ,
0 commit comments