@@ -41,11 +41,13 @@ type Caller struct {
4141 enclosingFunc * ast.FuncDecl // top-level function/method enclosing the call, if any
4242}
4343
44+ type logger = func (string , ... any )
45+
4446// Options specifies parameters affecting the inliner algorithm.
4547// All fields are optional.
4648type 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)
4951}
5052
5153// Result holds the result of code transformation.
@@ -737,19 +739,30 @@ func (st *state) inlineCall() (*inlineCallResult, error) {
737739 return nil , err // "can't happen"
738740 }
739741
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 ))
744746 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+ }
745758 replaceNode (calleeDecl , id , repl )
746759 }
747760
748761 // Generate replacements for each free identifier.
749762 // (The same tree may be spliced in multiple times, resulting in a DAG.)
750763 for _ , ref := range callee .FreeRefs {
751764 if repl := objRenames [ref .Object ]; repl != nil {
752- replaceCalleeID (ref .Offset , repl )
765+ replaceCalleeID (ref .Offset , repl , false )
753766 }
754767 }
755768
@@ -825,6 +838,10 @@ func (st *state) inlineCall() (*inlineCallResult, error) {
825838 // nop
826839 } else {
827840 // 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.
828845 n := len (params ) - 1
829846 ordinary , extra := args [:n ], args [n :]
830847 var elts []ast.Expr
@@ -849,6 +866,7 @@ func (st *state) inlineCall() (*inlineCallResult, error) {
849866 effects : effects ,
850867 duplicable : false ,
851868 freevars : freevars ,
869+ variadic : true ,
852870 })
853871 }
854872 }
@@ -1297,6 +1315,7 @@ type argument struct {
12971315 duplicable bool // expr may be duplicated
12981316 freevars map [string ]bool // free names of expr
12991317 substitutable bool // is candidate for substitution
1318+ variadic bool // is explicit []T{...} for eliminated variadic
13001319}
13011320
13021321// arguments returns the effective arguments of the call.
@@ -1452,6 +1471,12 @@ type parameter struct {
14521471 variadic bool // (final) parameter is unsimplified ...T
14531472}
14541473
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+
14551480// substitute implements parameter elimination by substitution.
14561481//
14571482// It considers each parameter and its corresponding argument in turn
@@ -1471,7 +1496,7 @@ type parameter struct {
14711496// parameter, and is provided with its relative offset and replacement
14721497// expression (argument), and the corresponding elements of params and
14731498// 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 ) {
14751500 // Inv:
14761501 // in calls to variadic, len(args) >= len(params)-1
14771502 // in spread calls to non-variadic, len(args) < len(params)
@@ -1621,7 +1646,7 @@ next:
16211646 logf ("replacing parameter %q by argument %q" ,
16221647 param .info .Name , debugFormatNode (caller .Fset , arg .expr ))
16231648 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 )
16251650 }
16261651 params [i ] = nil // substituted
16271652 args [i ] = nil // substituted
@@ -1666,7 +1691,7 @@ func isUsedOutsideCall(caller *Caller, v *types.Var) bool {
16661691// TODO(adonovan): we could obtain a finer result rejecting only the
16671692// freevars of each failed constraint, and processing constraints in
16681693// 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 ) {
16701695 // Create a dummy package, as this is the only
16711696 // way to create an environment for CheckExpr.
16721697 pkg := types .NewPackage ("falcon" , "falcon" )
@@ -1764,7 +1789,7 @@ func checkFalconConstraints(logf func(string, ...any), params []*parameter, args
17641789// current argument. Subsequent iterations cannot introduce hazards
17651790// with that argument because they can result only in additional
17661791// 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 ) {
17681793 effectStr := func (effects bool , idx int ) string {
17691794 i := fmt .Sprint (idx )
17701795 if idx == len (args ) {
@@ -1923,7 +1948,7 @@ type bindingDeclInfo struct {
19231948//
19241949// Strategies may impose additional checks on return
19251950// 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 {
19271952 // Spread calls are tricky as they may not align with the
19281953 // parameters' field groupings nor types.
19291954 // For example, given
@@ -2745,26 +2770,38 @@ func clearPositions(root ast.Node) {
27452770 })
27462771}
27472772
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 ) {
27502778 // 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+ )
27522783 ast .Inspect (root , func (n ast.Node ) bool {
27532784 if found != nil {
27542785 return false
27552786 }
2787+ if n == nil {
2788+ path = path [:len (path )- 1 ]
2789+ return false
2790+ }
27562791 if id , ok := n .(* ast.Ident ); ok {
27572792 if id .Pos () == pos {
27582793 found = id
2794+ return true
27592795 }
27602796 }
2797+ path = append (path , n )
27612798 return true
27622799 })
27632800 if found == nil {
27642801 panic (fmt .Sprintf ("findIdent %d not found in %s" ,
27652802 pos , debugFormatNode (token .NewFileSet (), root )))
27662803 }
2767- return found
2804+ return path , found
27682805}
27692806
27702807func prepend [T any ](elem T , slice ... T ) []T {
0 commit comments