Skip to content

Commit f38ff07

Browse files
adonovangopherbot
authored andcommitted
internal/refactor/inline: T{} is duplicable for struct/array
This change makes empty composite literals of aggregate (struct/array) types duplicable. Nonempty literals remain nonduplicable on grounds of verbosity; and map and slice literals are nonduplicable because they allocate a new variable. Change-Id: I9e2d778e004fb4743fd242c4e81d00e55830a6bd Reviewed-on: https://go-review.googlesource.com/c/tools/+/534397 Auto-Submit: Alan Donovan <[email protected]> Reviewed-by: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent ecbfa88 commit f38ff07

File tree

2 files changed

+69
-1
lines changed

2 files changed

+69
-1
lines changed

internal/refactor/inline/inline.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1998,10 +1998,28 @@ func duplicable(info *types.Info, e ast.Expr) bool {
19981998
case *ast.UnaryExpr: // e.g. +1, -1
19991999
return (e.Op == token.ADD || e.Op == token.SUB) && duplicable(info, e.X)
20002000

2001+
case *ast.CompositeLit:
2002+
// Empty struct or array literals T{} are duplicable.
2003+
// (Non-empty literals are too verbose, and slice/map
2004+
// literals allocate indirect variables.)
2005+
if len(e.Elts) == 0 {
2006+
switch info.TypeOf(e).Underlying().(type) {
2007+
case *types.Struct, *types.Array:
2008+
return true
2009+
}
2010+
}
2011+
return false
2012+
20012013
case *ast.CallExpr:
20022014
// Don't treat a conversion T(x) as duplicable even
20032015
// if x is duplicable because it could duplicate
2004-
// allocations. There may be cases to tease apart here.
2016+
// allocations.
2017+
//
2018+
// TODO(adonovan): there are cases to tease apart here:
2019+
// duplicating string([]byte) conversions increases
2020+
// allocation but doesn't change behavior, but the
2021+
// reverse, []byte(string), allocates a distinct array,
2022+
// which is observable
20052023
return false
20062024

20072025
case *ast.SelectorExpr:

internal/refactor/inline/inline_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,56 @@ func TestBasics(t *testing.T) {
391391
})
392392
}
393393

394+
func TestDuplicable(t *testing.T) {
395+
runTests(t, []testcase{
396+
{
397+
"Empty strings are duplicable.",
398+
`func f(s string) { print(s, s) }`,
399+
`func _() { f("") }`,
400+
`func _() { print("", "") }`,
401+
},
402+
{
403+
"Non-empty string literals are not duplicable.",
404+
`func f(s string) { print(s, s) }`,
405+
`func _() { f("hi") }`,
406+
`func _() {
407+
var s string = "hi"
408+
print(s, s)
409+
}`,
410+
},
411+
{
412+
"Empty array literals are duplicable.",
413+
`func f(a [2]int) { print(a, a) }`,
414+
`func _() { f([2]int{}) }`,
415+
`func _() { print([2]int{}, [2]int{}) }`,
416+
},
417+
{
418+
"Non-empty array literals are not duplicable.",
419+
`func f(a [2]int) { print(a, a) }`,
420+
`func _() { f([2]int{1, 2}) }`,
421+
`func _() {
422+
var a [2]int = [2]int{1, 2}
423+
print(a, a)
424+
}`,
425+
},
426+
{
427+
"Empty struct literals are duplicable.",
428+
`func f(s S) { print(s, s) }; type S struct { x int }`,
429+
`func _() { f(S{}) }`,
430+
`func _() { print(S{}, S{}) }`,
431+
},
432+
{
433+
"Non-empty struct literals are not duplicable.",
434+
`func f(s S) { print(s, s) }; type S struct { x int }`,
435+
`func _() { f(S{x: 1}) }`,
436+
`func _() {
437+
var s S = S{x: 1}
438+
print(s, s)
439+
}`,
440+
},
441+
})
442+
}
443+
394444
func TestExprStmtReduction(t *testing.T) {
395445
runTests(t, []testcase{
396446
{

0 commit comments

Comments
 (0)