Fold implicit literal element index into the lit-init node

A positional element of an array or slice literal (e.g. the elements of
[]T{a, b}) previously created a 'lit-index' node holding its implicit
index constant (0, 1, ...), in addition to the 'lit-init' write node.

Array and slice content flow is index-insensitive (the store step
ignores the element index), map literals always use explicit keys, and
literal bases are not tracked by VariableWithFields, so the implicit
index value is never actually consumed. Drop the 'lit-index' node and
let the lit-init instruction act as its own opaque index. All 100
data-flow library tests pass unchanged, confirming no content-flow loss.
This commit is contained in:
Owen Mansel-Chan
2026-07-10 08:16:05 +01:00
parent 57951e2f60
commit f07ab4517e
3 changed files with 16 additions and 82 deletions

View File

@@ -587,14 +587,6 @@ module GoCfg {
n = any(Go::CompositeLit lit).getAnElement() and
tag = "lit-init"
or
// Implicit literal element index
exists(Go::CompositeLit lit |
not lit instanceof Go::StructLit and
n = lit.getAnElement() and
not n instanceof Go::KeyValueExpr and
tag = "lit-index"
)
or
// Implicit field selection for promoted fields
exists(int i, Go::Field implicitField |
implicitFieldSelection(n, i, implicitField) and
@@ -1480,17 +1472,12 @@ module GoCfg {
not exists(lit.getElement(_)) and n2.isAfter(lit)
)
or
// After element → optional lit-index → lit-init → next element or After
// After element → lit-init → next element or After.
// Positional array/slice elements have an implicit index that is
// modelled on the `lit-init` instruction itself (see
// `IR::InitLiteralElementInstruction`) rather than as a separate node.
exists(int i |
n1.isAfter(lit.getElement(i)) and
(
n2.isAdditional(lit.getElement(i), "lit-index")
or
not exists(PreControlFlowNode idx | idx.isAdditional(lit.getElement(i), "lit-index")) and
n2.isAdditional(lit.getElement(i), "lit-init")
)
or
n1.isAdditional(lit.getElement(i), "lit-index") and
n2.isAdditional(lit.getElement(i), "lit-init")
or
n1.isAdditional(lit.getElement(i), "lit-init") and

View File

@@ -159,8 +159,6 @@ module IR {
or
this instanceof InitLiteralComponentInstruction and result = "element init"
or
this instanceof ImplicitLiteralElementIndexInstruction and result = "element index"
or
this instanceof AssignInstruction and result = "assignment"
or
this instanceof EvalCompoundAssignRhsInstruction and
@@ -578,7 +576,11 @@ module IR {
Instruction getIndex() {
result = evalExprInstruction(elt.(KeyValueExpr).getKey())
or
result.(ImplicitLiteralElementIndexInstruction).isAdditional(elt, "lit-index")
// A positional array/slice element has an implicit index. Array/slice
// content flow is index-insensitive, so rather than materialise a
// separate index node the element-init instruction acts as its own
// (opaque) index.
not elt instanceof KeyValueExpr and result = this
}
}
@@ -627,54 +629,6 @@ module IR {
}
}
private predicate noExplicitKeys(CompositeLit lit) {
not lit.getAnElement() instanceof KeyValueExpr
}
private int getElementIndex(CompositeLit lit, int i) {
(
lit.getType().getUnderlyingType() instanceof ArrayType or
lit.getType().getUnderlyingType() instanceof SliceType
) and
exists(Expr elt | elt = lit.getElement(i) |
noExplicitKeys(lit) and result = i
or
result = elt.(KeyValueExpr).getKey().getIntValue()
or
not elt instanceof KeyValueExpr and
(
i = 0 and result = 0
or
result = getElementIndex(lit, i - 1) + 1
)
)
}
/**
* An IR instruction computing the implicit index of an element in an array or slice literal.
*/
class ImplicitLiteralElementIndexInstruction extends Instruction {
Expr elt;
ImplicitLiteralElementIndexInstruction() { this.isAdditional(elt, "lit-index") }
override Type getResultType() { result instanceof IntType }
override ControlFlow::Root getRoot() { result.isRootOf(elt) }
override int getIntValue() {
exists(CompositeLit lit, int i | elt = lit.getElement(i) | result = getElementIndex(lit, i))
}
override string getStringValue() { none() }
override string getExactValue() { result = this.getIntValue().toString() }
override predicate isPlatformIndependentConstant() { any() }
override predicate isConst() { any() }
}
/**
* An instruction assigning to a variable or field.
*/

View File

@@ -969,9 +969,8 @@
| exprs.go:16:17:16:23 | After struct3 | exprs.go:16:17:16:25 | selection of x |
| exprs.go:16:17:16:23 | Before struct3 | exprs.go:16:17:16:23 | struct3 |
| exprs.go:16:17:16:23 | struct3 | exprs.go:16:17:16:23 | After struct3 |
| exprs.go:16:17:16:25 | After selection of x | exprs.go:16:17:16:25 | lit-index selection of x |
| exprs.go:16:17:16:25 | After selection of x | exprs.go:16:17:16:25 | lit-init selection of x |
| exprs.go:16:17:16:25 | Before selection of x | exprs.go:16:17:16:23 | Before struct3 |
| exprs.go:16:17:16:25 | lit-index selection of x | exprs.go:16:17:16:25 | lit-init selection of x |
| exprs.go:16:17:16:25 | lit-init selection of x | exprs.go:16:10:16:26 | After array literal |
| exprs.go:16:17:16:25 | selection of x | exprs.go:16:17:16:25 | After selection of x |
| exprs.go:17:2:17:40 | ... := ... | exprs.go:17:10:17:40 | Before array literal |
@@ -983,9 +982,8 @@
| exprs.go:17:19:17:25 | After struct3 | exprs.go:17:19:17:27 | selection of x |
| exprs.go:17:19:17:25 | Before struct3 | exprs.go:17:19:17:25 | struct3 |
| exprs.go:17:19:17:25 | struct3 | exprs.go:17:19:17:25 | After struct3 |
| exprs.go:17:19:17:27 | After selection of x | exprs.go:17:19:17:27 | lit-index selection of x |
| exprs.go:17:19:17:27 | After selection of x | exprs.go:17:19:17:27 | lit-init selection of x |
| exprs.go:17:19:17:27 | Before selection of x | exprs.go:17:19:17:25 | Before struct3 |
| exprs.go:17:19:17:27 | lit-index selection of x | exprs.go:17:19:17:27 | lit-init selection of x |
| exprs.go:17:19:17:27 | lit-init selection of x | exprs.go:17:30:17:39 | Before key-value pair |
| exprs.go:17:19:17:27 | selection of x | exprs.go:17:19:17:27 | After selection of x |
| exprs.go:17:30:17:30 | 2 | exprs.go:17:30:17:30 | After 2 |
@@ -1011,14 +1009,12 @@
| exprs.go:18:9:18:22 | After slice literal | exprs.go:18:2:18:22 | assign:0 ... := ... |
| exprs.go:18:9:18:22 | Before slice literal | exprs.go:18:9:18:22 | slice literal |
| exprs.go:18:9:18:22 | slice literal | exprs.go:18:18:18:18 | Before s |
| exprs.go:18:18:18:18 | After s | exprs.go:18:18:18:18 | lit-index s |
| exprs.go:18:18:18:18 | After s | exprs.go:18:18:18:18 | lit-init s |
| exprs.go:18:18:18:18 | Before s | exprs.go:18:18:18:18 | s |
| exprs.go:18:18:18:18 | lit-index s | exprs.go:18:18:18:18 | lit-init s |
| exprs.go:18:18:18:18 | lit-init s | exprs.go:18:21:18:21 | Before s |
| exprs.go:18:18:18:18 | s | exprs.go:18:18:18:18 | After s |
| exprs.go:18:21:18:21 | After s | exprs.go:18:21:18:21 | lit-index s |
| exprs.go:18:21:18:21 | After s | exprs.go:18:21:18:21 | lit-init s |
| exprs.go:18:21:18:21 | Before s | exprs.go:18:21:18:21 | s |
| exprs.go:18:21:18:21 | lit-index s | exprs.go:18:21:18:21 | lit-init s |
| exprs.go:18:21:18:21 | lit-init s | exprs.go:18:9:18:22 | After slice literal |
| exprs.go:18:21:18:21 | s | exprs.go:18:21:18:21 | After s |
| exprs.go:19:2:19:38 | ... := ... | exprs.go:19:8:19:38 | Before map literal |
@@ -1373,19 +1369,16 @@
| exprs.go:61:9:61:22 | Before slice literal | exprs.go:61:9:61:22 | slice literal |
| exprs.go:61:9:61:22 | slice literal | exprs.go:61:15:61:15 | Before 1 |
| exprs.go:61:15:61:15 | 1 | exprs.go:61:15:61:15 | After 1 |
| exprs.go:61:15:61:15 | After 1 | exprs.go:61:15:61:15 | lit-index 1 |
| exprs.go:61:15:61:15 | After 1 | exprs.go:61:15:61:15 | lit-init 1 |
| exprs.go:61:15:61:15 | Before 1 | exprs.go:61:15:61:15 | 1 |
| exprs.go:61:15:61:15 | lit-index 1 | exprs.go:61:15:61:15 | lit-init 1 |
| exprs.go:61:15:61:15 | lit-init 1 | exprs.go:61:18:61:18 | Before 2 |
| exprs.go:61:18:61:18 | 2 | exprs.go:61:18:61:18 | After 2 |
| exprs.go:61:18:61:18 | After 2 | exprs.go:61:18:61:18 | lit-index 2 |
| exprs.go:61:18:61:18 | After 2 | exprs.go:61:18:61:18 | lit-init 2 |
| exprs.go:61:18:61:18 | Before 2 | exprs.go:61:18:61:18 | 2 |
| exprs.go:61:18:61:18 | lit-index 2 | exprs.go:61:18:61:18 | lit-init 2 |
| exprs.go:61:18:61:18 | lit-init 2 | exprs.go:61:21:61:21 | Before 3 |
| exprs.go:61:21:61:21 | 3 | exprs.go:61:21:61:21 | After 3 |
| exprs.go:61:21:61:21 | After 3 | exprs.go:61:21:61:21 | lit-index 3 |
| exprs.go:61:21:61:21 | After 3 | exprs.go:61:21:61:21 | lit-init 3 |
| exprs.go:61:21:61:21 | Before 3 | exprs.go:61:21:61:21 | 3 |
| exprs.go:61:21:61:21 | lit-index 3 | exprs.go:61:21:61:21 | lit-init 3 |
| exprs.go:61:21:61:21 | lit-init 3 | exprs.go:61:9:61:22 | After slice literal |
| exprs.go:64:1:64:19 | After variable declaration | exprs.go:65:1:65:24 | variable declaration |
| exprs.go:64:1:64:19 | variable declaration | exprs.go:64:5:64:19 | value declaration specifier |