Skip to content

Commit 918e96a

Browse files
adonovangopherbot
authored andcommitted
internal/refactor/inline: use binding decl with literalization
Previously, literalization, the strategy of last resort, didn't make use of a binding decl even when one was available. But a binding decl can make literalization more readable as it puts the arguments before the body, their natural place, which is important especially when the body is longer. func(params) { body } (args) => func() { var params = args; body }() We don't yet attempt to do this if any named result is referenced, because the binding decl would duplicate it; teasing apart the params and results of the binding decl is left for future work. Plus tests. Change-Id: I51da3016157c1531c2d57962c4bddb0203ac0946 Reviewed-on: https://go-review.googlesource.com/c/tools/+/535456 Reviewed-by: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Alan Donovan <[email protected]>
1 parent 12b5dad commit 918e96a

File tree

5 files changed

+54
-20
lines changed

5 files changed

+54
-20
lines changed

internal/refactor/inline/doc.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -251,20 +251,13 @@ TODO(adonovan): future work:
251251
could be achieved by returning metadata alongside the result
252252
and having the client conditionally discard the change.
253253
254-
- Is it acceptable to skip effects that are limited to runtime
255-
panics? Can we avoid evaluating an argument x.f
256-
or a[i] when the corresponding parameter is unused?
257-
258254
- Support inlining of generic functions, replacing type parameters
259255
by their instantiations.
260256
261257
- Support inlining of calls to function literals ("closures").
262258
But note that the existing algorithm makes widespread assumptions
263259
that the callee is a package-level function or method.
264260
265-
- Eliminate parens and braces inserted conservatively when they
266-
are redundant.
267-
268261
- Eliminate explicit conversions of "untyped" literals inserted
269262
conservatively when they are redundant. For example, the
270263
conversion int32(1) is redundant when this value is used only as a

internal/refactor/inline/inline.go

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -989,15 +989,32 @@ func inline(logf func(string, ...any), caller *Caller, callee *gobCallee) (*resu
989989
}
990990

991991
// Infallible general case: literalization.
992+
//
993+
// func(params) { body }(args)
994+
//
992995
logf("strategy: literalization")
996+
funcLit := &ast.FuncLit{
997+
Type: calleeDecl.Type,
998+
Body: calleeDecl.Body,
999+
}
1000+
1001+
// Literalization can still make use of a binding
1002+
// decl as it gives a more natural reading order:
1003+
//
1004+
// func() { var params = args; body }()
1005+
//
1006+
// TODO(adonovan): relax the allResultsUnreferenced requirement
1007+
// by adding a parameter-only (no named results) binding decl.
1008+
if bindingDeclStmt != nil && allResultsUnreferenced {
1009+
funcLit.Type.Params.List = nil
1010+
remainingArgs = nil
1011+
funcLit.Body.List = prepend(bindingDeclStmt, funcLit.Body.List...)
1012+
}
9931013

9941014
// Emit a new call to a function literal in place of
9951015
// the callee name, with appropriate replacements.
9961016
newCall := &ast.CallExpr{
997-
Fun: &ast.FuncLit{
998-
Type: calleeDecl.Type,
999-
Body: calleeDecl.Body,
1000-
},
1017+
Fun: funcLit,
10011018
Ellipsis: token.NoPos, // f(slice...) is always simplified
10021019
Args: remainingArgs,
10031020
}
@@ -1536,11 +1553,12 @@ func updateCalleeParams(calleeDecl *ast.FuncDecl, params []*parameter) {
15361553

15371554
// createBindingDecl constructs a "binding decl" that implements
15381555
// parameter assignment and declares any named result variables
1539-
// referenced by the callee.
1556+
// referenced by the callee. It returns nil if there were no
1557+
// unsubstituted parameters.
15401558
//
15411559
// It may not always be possible to create the decl (e.g. due to
1542-
// shadowing), in which case it returns nil; but if it succeeds, the
1543-
// declaration may be used by reduction strategies to relax the
1560+
// shadowing), in which case it also returns nil; but if it succeeds,
1561+
// the declaration may be used by reduction strategies to relax the
15441562
// requirement that all parameters have been substituted.
15451563
//
15461564
// For example, a call:
@@ -1682,14 +1700,19 @@ func createBindingDecl(logf func(string, ...any), caller *Caller, args []*argume
16821700
}
16831701
}
16841702

1685-
decl := &ast.DeclStmt{
1703+
if len(specs) == 0 {
1704+
logf("binding decl not needed: all parameters substituted")
1705+
return nil
1706+
}
1707+
1708+
stmt := &ast.DeclStmt{
16861709
Decl: &ast.GenDecl{
16871710
Tok: token.VAR,
16881711
Specs: specs,
16891712
},
16901713
}
1691-
logf("binding decl: %s", debugFormatNode(caller.Fset, decl))
1692-
return decl
1714+
logf("binding decl: %s", debugFormatNode(caller.Fset, stmt))
1715+
return stmt
16931716
}
16941717

16951718
// lookup does a symbol lookup in the lexical environment of the caller.

internal/refactor/inline/inline_test.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,7 @@ func TestVariadic(t *testing.T) {
693693
"Variadic cancellation (basic).",
694694
`func f(args ...any) { defer f(&args); println(args) }`,
695695
`func _(slice []any) { f(slice...) }`,
696-
`func _(slice []any) { func(args []any) { defer f(&args); println(args) }(slice) }`,
696+
`func _(slice []any) { func() { var args []any = slice; defer f(&args); println(args) }() }`,
697697
},
698698
{
699699
"Variadic cancellation (literalization with parameter elimination).",
@@ -797,6 +797,24 @@ func TestParameterBindingDecl(t *testing.T) {
797797
`func _(x *T) { f(x, recover()) }`,
798798
`func _(x *T) { x.g(recover()) }`,
799799
},
800+
{
801+
"Literalization can make use of a binding decl (all params).",
802+
`func f(x, y int) int { defer println(); return y + x }; func g(int) int`,
803+
`func _() { println(f(g(1), g(2))) }`,
804+
`func _() { println(func() int { var x, y int = g(1), g(2); defer println(); return y + x }()) }`,
805+
},
806+
{
807+
"Literalization can make use of a binding decl (some params).",
808+
`func f(x, y int) int { z := y + x; defer println(); return z }; func g(int) int`,
809+
`func _() { println(f(g(1), g(2))) }`,
810+
`func _() { println(func() int { var x int = g(1); z := g(2) + x; defer println(); return z }()) }`,
811+
},
812+
{
813+
"Literalization can't yet use of a binding decl if named results.",
814+
`func f(x, y int) (z int) { z = y + x; defer println(); return }; func g(int) int`,
815+
`func _() { println(f(g(1), g(2))) }`,
816+
`func _() { println(func(x int) (z int) { z = g(2) + x; defer println(); return }(g(1))) }`,
817+
},
800818
})
801819
}
802820

internal/refactor/inline/testdata/basic-literal.txtar

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func add(x, y int) int { defer print(); return x + y }
2323
package a
2424

2525
func _() {
26-
func(x int) int { defer print(); return x + 2 }(recover().(int)) //@ inline(re"add", add1)
26+
func() int { var x int = recover().(int); defer print(); return x + 2 }() //@ inline(re"add", add1)
2727
}
2828

2929
func add(x, y int) int { defer print(); return x + y }

internal/refactor/inline/testdata/issue62667.txtar

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import (
3838
)
3939

4040
func A() {
41-
func(path string) { defer func() {}(); path0.Split(path) }(g()) //@ inline(re"Dir", result)
41+
func() { var path string = g(); defer func() {}(); path0.Split(path) }() //@ inline(re"Dir", result)
4242
}
4343

4444
func g() string

0 commit comments

Comments
 (0)