@@ -41,11 +41,13 @@ type Caller struct {
41
41
enclosingFunc * ast.FuncDecl // top-level function/method enclosing the call, if any
42
42
}
43
43
44
+ type logger = func (string , ... any )
45
+
44
46
// Options specifies parameters affecting the inliner algorithm.
45
47
// All fields are optional.
46
48
type Options struct {
47
- Logf func ( string , ... any ) // log output function, records decision-making process
48
- IgnoreEffects bool // ignore potential side effects of arguments (unsound)
49
+ Logf logger // log output function, records decision-making process
50
+ IgnoreEffects bool // ignore potential side effects of arguments (unsound)
49
51
}
50
52
51
53
// Result holds the result of code transformation.
@@ -737,19 +739,30 @@ func (st *state) inlineCall() (*inlineCallResult, error) {
737
739
return nil , err // "can't happen"
738
740
}
739
741
740
- // replaceCalleeID replaces an identifier in the callee.
741
- // The replacement tree must not belong to the caller; use cloneNode as needed .
742
- replaceCalleeID := func (offset int , repl ast.Expr ) {
743
- id := findIdent (calleeDecl , calleeDecl .Pos ()+ token .Pos (offset ))
742
+ // replaceCalleeID replaces an identifier in the callee. See [replacer] for
743
+ // more detailed semantics .
744
+ replaceCalleeID := func (offset int , repl ast.Expr , unpackVariadic bool ) {
745
+ path , id := findIdent (calleeDecl , calleeDecl .Pos ()+ token .Pos (offset ))
744
746
logf ("- replace id %q @ #%d to %q" , id .Name , offset , debugFormatNode (calleeFset , repl ))
747
+ // Replace f([]T{a, b, c}...) with f(a, b, c).
748
+ if lit , ok := repl .(* ast.CompositeLit ); ok && unpackVariadic && len (path ) > 0 {
749
+ if call , ok := last (path ).(* ast.CallExpr ); ok &&
750
+ call .Ellipsis .IsValid () &&
751
+ id == last (call .Args ) {
752
+
753
+ call .Args = append (call .Args [:len (call .Args )- 1 ], lit .Elts ... )
754
+ call .Ellipsis = token .NoPos
755
+ return
756
+ }
757
+ }
745
758
replaceNode (calleeDecl , id , repl )
746
759
}
747
760
748
761
// Generate replacements for each free identifier.
749
762
// (The same tree may be spliced in multiple times, resulting in a DAG.)
750
763
for _ , ref := range callee .FreeRefs {
751
764
if repl := objRenames [ref .Object ]; repl != nil {
752
- replaceCalleeID (ref .Offset , repl )
765
+ replaceCalleeID (ref .Offset , repl , false )
753
766
}
754
767
}
755
768
@@ -825,6 +838,10 @@ func (st *state) inlineCall() (*inlineCallResult, error) {
825
838
// nop
826
839
} else {
827
840
// ordinary call: f(a1, ... aN) -> f([]T{a1, ..., aN})
841
+ //
842
+ // Substitution of []T{...} in the callee body may lead to
843
+ // g([]T{a1, ..., aN}...), which we simplify to g(a1, ..., an)
844
+ // later; see replaceCalleeID.
828
845
n := len (params ) - 1
829
846
ordinary , extra := args [:n ], args [n :]
830
847
var elts []ast.Expr
@@ -849,6 +866,7 @@ func (st *state) inlineCall() (*inlineCallResult, error) {
849
866
effects : effects ,
850
867
duplicable : false ,
851
868
freevars : freevars ,
869
+ variadic : true ,
852
870
})
853
871
}
854
872
}
@@ -1297,6 +1315,7 @@ type argument struct {
1297
1315
duplicable bool // expr may be duplicated
1298
1316
freevars map [string ]bool // free names of expr
1299
1317
substitutable bool // is candidate for substitution
1318
+ variadic bool // is explicit []T{...} for eliminated variadic
1300
1319
}
1301
1320
1302
1321
// arguments returns the effective arguments of the call.
@@ -1452,6 +1471,12 @@ type parameter struct {
1452
1471
variadic bool // (final) parameter is unsimplified ...T
1453
1472
}
1454
1473
1474
+ // A replacer replaces an identifier at the given offset in the callee.
1475
+ // The replacement tree must not belong to the caller; use cloneNode as needed.
1476
+ // If unpackVariadic is set, the replacement is a composite resulting from
1477
+ // variadic elimination, and may be unpackeded into variadic calls.
1478
+ type replacer = func (offset int , repl ast.Expr , unpackVariadic bool )
1479
+
1455
1480
// substitute implements parameter elimination by substitution.
1456
1481
//
1457
1482
// It considers each parameter and its corresponding argument in turn
@@ -1471,7 +1496,7 @@ type parameter struct {
1471
1496
// parameter, and is provided with its relative offset and replacement
1472
1497
// expression (argument), and the corresponding elements of params and
1473
1498
// args are replaced by nil.
1474
- func substitute (logf func ( string , ... any ), caller * Caller , params []* parameter , args []* argument , effects []int , falcon falconResult , replaceCalleeID func ( offset int , repl ast. Expr ) ) {
1499
+ func substitute (logf logger , caller * Caller , params []* parameter , args []* argument , effects []int , falcon falconResult , replace replacer ) {
1475
1500
// Inv:
1476
1501
// in calls to variadic, len(args) >= len(params)-1
1477
1502
// in spread calls to non-variadic, len(args) < len(params)
@@ -1621,7 +1646,7 @@ next:
1621
1646
logf ("replacing parameter %q by argument %q" ,
1622
1647
param .info .Name , debugFormatNode (caller .Fset , arg .expr ))
1623
1648
for _ , ref := range param .info .Refs {
1624
- replaceCalleeID (ref , internalastutil .CloneNode (arg .expr ).(ast.Expr ))
1649
+ replace (ref , internalastutil .CloneNode (arg .expr ).(ast.Expr ), arg . variadic )
1625
1650
}
1626
1651
params [i ] = nil // substituted
1627
1652
args [i ] = nil // substituted
@@ -1666,7 +1691,7 @@ func isUsedOutsideCall(caller *Caller, v *types.Var) bool {
1666
1691
// TODO(adonovan): we could obtain a finer result rejecting only the
1667
1692
// freevars of each failed constraint, and processing constraints in
1668
1693
// order of increasing arity, but failures are quite rare.
1669
- func checkFalconConstraints (logf func ( string , ... any ) , params []* parameter , args []* argument , falcon falconResult ) {
1694
+ func checkFalconConstraints (logf logger , params []* parameter , args []* argument , falcon falconResult ) {
1670
1695
// Create a dummy package, as this is the only
1671
1696
// way to create an environment for CheckExpr.
1672
1697
pkg := types .NewPackage ("falcon" , "falcon" )
@@ -1764,7 +1789,7 @@ func checkFalconConstraints(logf func(string, ...any), params []*parameter, args
1764
1789
// current argument. Subsequent iterations cannot introduce hazards
1765
1790
// with that argument because they can result only in additional
1766
1791
// binding of lower-ordered arguments.
1767
- func resolveEffects (logf func ( string , ... any ) , args []* argument , effects []int ) {
1792
+ func resolveEffects (logf logger , args []* argument , effects []int ) {
1768
1793
effectStr := func (effects bool , idx int ) string {
1769
1794
i := fmt .Sprint (idx )
1770
1795
if idx == len (args ) {
@@ -1923,7 +1948,7 @@ type bindingDeclInfo struct {
1923
1948
//
1924
1949
// Strategies may impose additional checks on return
1925
1950
// conversions, labels, defer, etc.
1926
- func createBindingDecl (logf func ( string , ... any ) , caller * Caller , args []* argument , calleeDecl * ast.FuncDecl , results []* paramInfo ) * bindingDeclInfo {
1951
+ func createBindingDecl (logf logger , caller * Caller , args []* argument , calleeDecl * ast.FuncDecl , results []* paramInfo ) * bindingDeclInfo {
1927
1952
// Spread calls are tricky as they may not align with the
1928
1953
// parameters' field groupings nor types.
1929
1954
// For example, given
@@ -2745,26 +2770,38 @@ func clearPositions(root ast.Node) {
2745
2770
})
2746
2771
}
2747
2772
2748
- // findIdent returns the Ident beneath root that has the given pos.
2749
- func findIdent (root ast.Node , pos token.Pos ) * ast.Ident {
2773
+ // findIdent finds the Ident beneath root that has the given pos.
2774
+ // It returns the path to the ident (excluding the ident), and the ident
2775
+ // itself, where the path is the sequence of ast.Nodes encountered in a
2776
+ // depth-first search to find ident.
2777
+ func findIdent (root ast.Node , pos token.Pos ) ([]ast.Node , * ast.Ident ) {
2750
2778
// TODO(adonovan): opt: skip subtrees that don't contain pos.
2751
- var found * ast.Ident
2779
+ var (
2780
+ path []ast.Node
2781
+ found * ast.Ident
2782
+ )
2752
2783
ast .Inspect (root , func (n ast.Node ) bool {
2753
2784
if found != nil {
2754
2785
return false
2755
2786
}
2787
+ if n == nil {
2788
+ path = path [:len (path )- 1 ]
2789
+ return false
2790
+ }
2756
2791
if id , ok := n .(* ast.Ident ); ok {
2757
2792
if id .Pos () == pos {
2758
2793
found = id
2794
+ return true
2759
2795
}
2760
2796
}
2797
+ path = append (path , n )
2761
2798
return true
2762
2799
})
2763
2800
if found == nil {
2764
2801
panic (fmt .Sprintf ("findIdent %d not found in %s" ,
2765
2802
pos , debugFormatNode (token .NewFileSet (), root )))
2766
2803
}
2767
- return found
2804
+ return path , found
2768
2805
}
2769
2806
2770
2807
func prepend [T any ](elem T , slice ... T ) []T {
0 commit comments