Skip to content

Commit d75a88d

Browse files
committed
internal/refactor/inline: falcon: only map keys can be variable
The falcon analysis for inlining composite literals allowed considering arrays and slices for the uniqueness of their keys. But the keys of array and slice literals must be constant, slices were not handled, and the logic for arrays looked wrong. Fix this by only considering the uniqueness of map keys. Fixes golang/go#74393 Change-Id: Ic7c34942b9b09558b12ef709be839299c347021c Reviewed-on: https://go-review.googlesource.com/c/tools/+/684295 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent 9b2e031 commit d75a88d

File tree

3 files changed

+45
-32
lines changed

3 files changed

+45
-32
lines changed

internal/refactor/inline/falcon.go

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -423,51 +423,38 @@ func (st *falconState) expr(e ast.Expr) (res any) { // = types.TypeAndValue | as
423423
_ = st.expr(e.Type)
424424
}
425425
t := types.Unalias(typeparams.Deref(tv.Type))
426-
var uniques []ast.Expr
426+
ct := typeparams.CoreType(t)
427+
var mapKeys []ast.Expr // map key expressions; must be distinct if constant
427428
for _, elt := range e.Elts {
428429
if kv, ok := elt.(*ast.KeyValueExpr); ok {
429-
if !is[*types.Struct](t) {
430+
if is[*types.Map](ct) {
430431
if k := st.expr(kv.Key); k != nil {
431-
uniques = append(uniques, st.toExpr(k))
432+
mapKeys = append(mapKeys, st.toExpr(k))
432433
}
433434
}
434435
_ = st.expr(kv.Value)
435436
} else {
436437
_ = st.expr(elt)
437438
}
438439
}
439-
if uniques != nil {
440-
// Inv: not a struct.
441-
442-
// The type T in constraint T{...} depends on the CompLit:
443-
// - for a basic-keyed map, use map[K]int;
444-
// - for an interface-keyed map, use map[any]int;
445-
// - for a slice, use []int;
446-
// - for an array or *array, use [n]int.
447-
// The last two entail progressively stronger index checks.
448-
var ct ast.Expr // type syntax for constraint
449-
switch t := typeparams.CoreType(t).(type) {
450-
case *types.Map:
451-
if types.IsInterface(t.Key()) {
452-
ct = &ast.MapType{
453-
Key: makeIdent(st.any),
454-
Value: makeIdent(st.int),
455-
}
456-
} else {
457-
ct = &ast.MapType{
458-
Key: makeIdent(st.typename(t.Key())),
459-
Value: makeIdent(st.int),
460-
}
440+
if len(mapKeys) > 0 {
441+
// Inlining a map literal may replace variable key expressions by constants.
442+
// All such constants must have distinct values.
443+
// (Array and slice literals do not permit non-constant keys.)
444+
t := ct.(*types.Map)
445+
var typ ast.Expr
446+
if types.IsInterface(t.Key()) {
447+
typ = &ast.MapType{
448+
Key: makeIdent(st.any),
449+
Value: makeIdent(st.int),
461450
}
462-
case *types.Array: // or *array
463-
ct = &ast.ArrayType{
464-
Len: makeIntLit(t.Len()),
465-
Elt: makeIdent(st.int),
451+
} else {
452+
typ = &ast.MapType{
453+
Key: makeIdent(st.typename(t.Key())),
454+
Value: makeIdent(st.int),
466455
}
467-
default:
468-
panic(fmt.Sprintf("%T: %v", t, t))
469456
}
470-
st.emitUnique(ct, uniques)
457+
st.emitUnique(typ, mapKeys)
471458
}
472459
// definitely non-constant
473460

internal/refactor/inline/falcon_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,30 @@ func TestFalconMapKeys(t *testing.T) {
108108
_ = map[int]bool{1: true, x: true}
109109
}`,
110110
},
111+
{
112+
"Generic map keys",
113+
// We have to use a named map type here, because the substituter tries to
114+
// wrap the type expr in parens, which are not allowed around the type in
115+
// composite literal expressions.
116+
`type Map map[int]bool; func f[M Map](x int) { _ = M{1: true, x: true} }`,
117+
`func _() { f[Map](1) }`,
118+
`func _() {
119+
var x int = 1
120+
_ = Map{1: true, x: true}
121+
}`,
122+
},
123+
{
124+
"Array keys", // not a map; shouldn't crash (golang/go$74393)
125+
`func f(x int) { _ = [2]int{1: 0} }`,
126+
`func _() { f(0) }`,
127+
`func _() { _ = [2]int{1: 0} }`,
128+
},
129+
{
130+
"Slice keys", // not a map; shouldn't crash (golang/go$74393)
131+
`func f(x int) { _ = []int{1: 0} }`,
132+
`func _() { f(0) }`,
133+
`func _() { _ = []int{1: 0} }`,
134+
},
111135
{
112136
"Unique map keys (varied built-in types)",
113137
`func f(x int16) { _ = map[any]bool{1: true, x: true} }`,

internal/refactor/inline/inline.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,6 +1429,8 @@ func (st *state) typeArguments(call *ast.CallExpr) []*argument {
14291429
// ident or qualified ident to prevent "if x == struct{}"
14301430
// parsing ambiguity, or "T(x)" where T = "*int" or "func()"
14311431
// from misparsing.
1432+
// TODO(adonovan): this fails in cases where parens are disallowed, such as
1433+
// in the composite literal expression T{k: v}.
14321434
if _, ok := arg.expr.(*ast.Ident); !ok {
14331435
arg.expr = &ast.ParenExpr{X: arg.expr}
14341436
}

0 commit comments

Comments
 (0)