Skip to content

Commit bcec099

Browse files
findleyrgopherbot
authored andcommitted
internal/refactor/inline: remove eta abstraction inlining assignments
Remove the unnecessary eta abstraction reported in golang/go#65217, by introducing a new strategy for rewriting assignments. This strategy involves analyzing both LHS and RHS of an assignment, and choosing between three substrategies: - spread the result expressions in cases where types are unambiguous - predeclare LHS variables in cases where the return is itself a spread call - convert RHS expressions if types involve implicit conversions Doing this involved some fixes to the logic for detecting trivial conversions, and tracking some additional information about untyped nils in return expressions. Since this strategy avoids literalization by modifying assignments in place, it must be able to avoid nested blocks, and so it explicitly records that braces may be elided. There is more work to be done here, both improving the writeType helper, and by variable declarations, but this CL lays a foundation for later expansion. For golang/go#65217 Change-Id: I9b3b595f7f678ab9b86ef7cf19936fd818b45426 Reviewed-on: https://go-review.googlesource.com/c/tools/+/580835 Reviewed-by: Alan Donovan <[email protected]> Auto-Submit: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent fcea13b commit bcec099

File tree

6 files changed

+781
-125
lines changed

6 files changed

+781
-125
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
This test reproduces condition of golang/go#65217, where the inliner created an
2+
unnecessary eta abstraction.
3+
4+
-- go.mod --
5+
module unused.mod
6+
7+
go 1.18
8+
9+
-- a/a.go --
10+
package a
11+
12+
type S struct{}
13+
14+
func (S) Int() int { return 0 }
15+
16+
func _() {
17+
var s S
18+
_ = f(s, s.Int())
19+
var j int
20+
j = f(s, s.Int())
21+
_ = j
22+
}
23+
24+
func _() {
25+
var s S
26+
i := f(s, s.Int())
27+
_ = i
28+
}
29+
30+
func f(unused S, i int) int { //@codeaction("unused", "unused", "refactor.rewrite", rewrite, "Refactor: remove unused parameter"), diag("unused", re`unused`)
31+
return i
32+
}
33+
34+
-- @rewrite/a/a.go --
35+
package a
36+
37+
type S struct{}
38+
39+
func (S) Int() int { return 0 }
40+
41+
func _() {
42+
var s S
43+
_ = f(s.Int())
44+
var j int
45+
j = f(s.Int())
46+
_ = j
47+
}
48+
49+
func _() {
50+
var s S
51+
var _ S = s
52+
i := f(s.Int())
53+
_ = i
54+
}
55+
56+
func f(i int) int { //@codeaction("unused", "unused", "refactor.rewrite", rewrite, "Refactor: remove unused parameter"), diag("unused", re`unused`)
57+
return i
58+
}

internal/refactor/inline/callee.go

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -32,25 +32,33 @@ type gobCallee struct {
3232
Content []byte // file content, compacted to a single func decl
3333

3434
// results of type analysis (does not reach go/types data structures)
35-
PkgPath string // package path of declaring package
36-
Name string // user-friendly name for error messages
37-
Unexported []string // names of free objects that are unexported
38-
FreeRefs []freeRef // locations of references to free objects
39-
FreeObjs []object // descriptions of free objects
40-
ValidForCallStmt bool // function body is "return expr" where expr is f() or <-ch
41-
NumResults int // number of results (according to type, not ast.FieldList)
42-
Params []*paramInfo // information about parameters (incl. receiver)
43-
Results []*paramInfo // information about result variables
44-
Effects []int // order in which parameters are evaluated (see calleefx)
45-
HasDefer bool // uses defer
46-
HasBareReturn bool // uses bare return in non-void function
47-
TotalReturns int // number of return statements
48-
TrivialReturns int // number of return statements with trivial result conversions
49-
Labels []string // names of all control labels
50-
Falcon falconResult // falcon constraint system
35+
PkgPath string // package path of declaring package
36+
Name string // user-friendly name for error messages
37+
Unexported []string // names of free objects that are unexported
38+
FreeRefs []freeRef // locations of references to free objects
39+
FreeObjs []object // descriptions of free objects
40+
ValidForCallStmt bool // function body is "return expr" where expr is f() or <-ch
41+
NumResults int // number of results (according to type, not ast.FieldList)
42+
Params []*paramInfo // information about parameters (incl. receiver)
43+
Results []*paramInfo // information about result variables
44+
Effects []int // order in which parameters are evaluated (see calleefx)
45+
HasDefer bool // uses defer
46+
HasBareReturn bool // uses bare return in non-void function
47+
Returns [][]returnOperandFlags // metadata about result expressions for each return
48+
Labels []string // names of all control labels
49+
Falcon falconResult // falcon constraint system
5150
}
5251

53-
// A freeRef records a reference to a free object. Gob-serializable.
52+
// returnOperandFlags records metadata about a single result expression in a return
53+
// statement.
54+
type returnOperandFlags int
55+
56+
const (
57+
nonTrivialResult returnOperandFlags = 1 << iota // return operand has non-trivial conversion to result type
58+
untypedNilResult // return operand is nil literal
59+
)
60+
61+
// A freeRef records a reference to a free object. Gob-serializable.
5462
// (This means free relative to the FuncDecl as a whole, i.e. excluding parameters.)
5563
type freeRef struct {
5664
Offset int // byte offset of the reference relative to the FuncDecl
@@ -264,11 +272,10 @@ func AnalyzeCallee(logf func(string, ...any), fset *token.FileSet, pkg *types.Pa
264272
// Record information about control flow in the callee
265273
// (but not any nested functions).
266274
var (
267-
hasDefer = false
268-
hasBareReturn = false
269-
totalReturns = 0
270-
trivialReturns = 0
271-
labels []string
275+
hasDefer = false
276+
hasBareReturn = false
277+
returnInfo [][]returnOperandFlags
278+
labels []string
272279
)
273280
ast.Inspect(decl.Body, func(n ast.Node) bool {
274281
switch n := n.(type) {
@@ -279,34 +286,37 @@ func AnalyzeCallee(logf func(string, ...any), fset *token.FileSet, pkg *types.Pa
279286
case *ast.LabeledStmt:
280287
labels = append(labels, n.Label.Name)
281288
case *ast.ReturnStmt:
282-
totalReturns++
283289

284290
// Are implicit assignment conversions
285291
// to result variables all trivial?
286-
trivial := true
292+
var resultInfo []returnOperandFlags
287293
if len(n.Results) > 0 {
288-
argType := func(i int) types.Type {
289-
return info.TypeOf(n.Results[i])
294+
argInfo := func(i int) (ast.Expr, types.Type) {
295+
expr := n.Results[i]
296+
return expr, info.TypeOf(expr)
290297
}
291298
if len(n.Results) == 1 && sig.Results().Len() > 1 {
292299
// Spread return: return f() where f.Results > 1.
293300
tuple := info.TypeOf(n.Results[0]).(*types.Tuple)
294-
argType = func(i int) types.Type {
295-
return tuple.At(i).Type()
301+
argInfo = func(i int) (ast.Expr, types.Type) {
302+
return nil, tuple.At(i).Type()
296303
}
297304
}
298305
for i := 0; i < sig.Results().Len(); i++ {
299-
if !trivialConversion(argType(i), sig.Results().At(i)) {
300-
trivial = false
301-
break
306+
expr, typ := argInfo(i)
307+
var flags returnOperandFlags
308+
if typ == types.Typ[types.UntypedNil] { // untyped nil is preserved by go/types
309+
flags |= untypedNilResult
302310
}
311+
if !trivialConversion(info.Types[expr].Value, typ, sig.Results().At(i).Type()) {
312+
flags |= nonTrivialResult
313+
}
314+
resultInfo = append(resultInfo, flags)
303315
}
304316
} else if sig.Results().Len() > 0 {
305317
hasBareReturn = true
306318
}
307-
if trivial {
308-
trivialReturns++
309-
}
319+
returnInfo = append(returnInfo, resultInfo)
310320
}
311321
return true
312322
})
@@ -353,8 +363,7 @@ func AnalyzeCallee(logf func(string, ...any), fset *token.FileSet, pkg *types.Pa
353363
Effects: effects,
354364
HasDefer: hasDefer,
355365
HasBareReturn: hasBareReturn,
356-
TotalReturns: totalReturns,
357-
TrivialReturns: trivialReturns,
366+
Returns: returnInfo,
358367
Labels: labels,
359368
Falcon: falcon,
360369
}}, nil

internal/refactor/inline/falcon_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ func TestFalconComplex(t *testing.T) {
317317
"Complex arithmetic (good).",
318318
`func f(re, im float64, z complex128) byte { return "x"[int(real(complex(re, im)*complex(re, -im)-z))] }`,
319319
`func _() { f(1, 2, 5+0i) }`,
320-
`func _() { _ = "x"[int(real(complex(float64(1), float64(2))*complex(float64(1), -float64(2))-(5+0i)))] }`,
320+
`func _() { _ = "x"[int(real(complex(1, 2)*complex(1, -2)-(5+0i)))] }`,
321321
},
322322
{
323323
"Complex arithmetic (bad).",

0 commit comments

Comments
 (0)