Commit Graph

88592 Commits

Author SHA1 Message Date
Owen Mansel-Chan
8eaa86ea01 Unify incdec-rhs into the compound-rhs node kind 2026-07-11 01:08:19 +01:00
Owen Mansel-Chan
6bb4aefa4e Simplify zero-init code after unifying result-zero-init 2026-07-11 01:08:15 +01:00
Owen Mansel-Chan
992a0b5c17 Unify result-zero-init and zero-init node kinds 2026-07-11 00:08:22 +01:00
Owen Mansel-Chan
c8f81bc1c4 Fold tuple-destructuring extract and write into one node 2026-07-10 17:09:21 +01:00
Owen Mansel-Chan
d30162b2ca Fold uninitialised var-decl zero-init and write into one node 2026-07-10 17:09:13 +01:00
Owen Mansel-Chan
1c60ffa3a7 Use default goto handling 2026-07-10 16:03:43 +01:00
Owen Mansel-Chan
193cd99851 Simplify postOrInOrder 2026-07-10 16:03:41 +01:00
Owen Mansel-Chan
01eabc8fcf Remove redundant inConditionalContext 2026-07-10 16:03:38 +01:00
Owen Mansel-Chan
f07ab4517e 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.
2026-07-10 16:03:23 +01:00
Owen Mansel-Chan
57951e2f60 Merge 'result-init' node into 'result-zero-init'
A named result variable previously created two additional nodes:
'result-zero-init:i' computing the zero value, and 'result-init:i'
writing that value into the result variable. The result-init node
existed only to write the value already computed by result-zero-init.

Make the result-zero-init instruction perform the write itself
(InitResultInstruction is now keyed on result-zero-init with its own
value as RHS) and drop the separate result-init node. All zero-value
constant semantics stay on the same node, so GVN and constant analysis
are unchanged; the only data-flow change is the SSA def relabeling
(result-init -> result-zero-init) with no flow loss.
2026-07-10 16:03:00 +01:00
Owen Mansel-Chan
0c381d9978 Merge parameter 'arg' node into 'param-init'
Each parameter previously created two additional nodes: an 'arg:i'
node (ReadArgumentInstruction) holding the incoming argument value, and
a 'param-init:i' node (InitParameterInstruction) writing it to the
parameter's SSA variable. The 'arg' node existed only to be the RHS of
the param-init write; the ParameterNode and inter-procedural flow are
already anchored on param-init.

Make InitParameterInstruction its own RHS and drop the 'arg:i' node,
halving the per-parameter node count. DeadStoreOfLocal now excludes
InitParameterInstruction (instead of ReadArgumentInstruction). The
data-flow changes are pure relabelings (arg -> param-init) with no flow
loss.
2026-07-10 16:02:55 +01:00
Owen Mansel-Chan
27e86d8065 drop implicit slice-bound nodes
A slice expression with omitted bounds (e.g. `s[:]`, `s[a:]`, `s[:b]`)
previously created up to three synthetic control-flow nodes: implicit-low
(constant 0), implicit-high (length) and implicit-max (capacity). These
carried no data-flow value beyond the implicit lower bound of 0.

Stop emitting these nodes: control flow now skips directly to the next
present bound (or the slice evaluation). SliceInstruction.getLow/getHigh/
getMax return only explicit bounds. The one consumer of the implicit
lower bound, StringOps' HasPrefix_Substring, now treats an absent lower
bound as 0. No data-flow or StringOps results change.
2026-07-10 16:02:49 +01:00
Owen Mansel-Chan
c85744e912 fold compound-assign write into the compound-rhs instruction
A compound assignment (`x += y`) previously produced two sequential
control-flow nodes: a 'compound-rhs' node computing the binary operation
`x + y`, followed by an 'assign:0' write node storing the result.

Make the compound-rhs instruction itself perform the write (it becomes a
WriteInstruction whose right-hand side is its own value), and stop
emitting the separate assign:0 node for compound assignments. This saves
one CFG node per compound assignment while preserving the BinaryOperationNode
model (so string-concatenation taint through `s += ...` still holds) and
SSA semantics (the def value is the binary operation).
2026-07-10 16:02:38 +01:00
Owen Mansel-Chan
2bde73cd2d fold implicit-one operand into incdec-rhs instruction
Increment/decrement statements previously produced three control-flow
nodes: an 'implicit-one' node for the constant 1, an 'incdec-rhs' node
for the 'operand + 1' value, and the write node. The implicit-one node
existed only to serve as the right operand of the increment's binary
operation model.

Remove the implicit-one node, modelling the implicit constant 1 directly
on the incdec-rhs instruction. This saves one CFG node per inc/dec. The
inc/dec disjunct of BinaryOperationNode is dropped as well; it only fed
string-concatenation taint (impossible for ++/--) and GVN of a synthetic
node, so no data-flow results are lost.
2026-07-10 16:02:25 +01:00
Owen Mansel-Chan
c08b03fa97 Accept test result changes
Category 1 — GVN named-result zero-init:
Named result variables now get an explicit result-zero-init:0 node (the migrated CFG models result vars as zero-initialized in the prologue). Test passes; intended modeling.

Category 2 — ParenExpr (...) missingInNodeForPostOrInOrder.
postOrInOrder starts with n instanceof Go::ReferenceExpr, and ReferenceExpr includes the this.(ParenExpr).getExpr() instanceof ReferenceExpr disjunct, so parenthesized reference expressions are correctly treated pre-order and excluded. Accepted.

Category 3 — select multipleSuccessors/deadEnd:
Same pattern already accepted in the committed ControlFlowGraph consistency file.

Category 4 — defer-invoke / after-block multipleSuccessors:
A return/after-block node flows both to Normal Exit and into the deferred-call chain. Already accepted in committed ControlFlowGraph and experimental/InconsistentCode.
2026-07-10 15:53:51 +01:00
Owen Mansel-Chan
8d1fba328b Accept changes 2026-07-10 15:53:49 +01:00
Owen Mansel-Chan
b6136a1d15 Accept CFG changes
- An empty select statement is correctly identified as a dead end.
- Select statements have multiple successors.
- Return statements have multiple successors when there are conditionally called defer statements.
2026-07-10 15:53:47 +01:00
Owen Mansel-Chan
d9b3f00000 Accept consistency query output 2026-07-10 15:53:45 +01:00
Owen Mansel-Chan
9236c3a518 Accept test changes 2026-07-10 15:53:43 +01:00
Owen Mansel-Chan
dfb90b36c0 Accept improved accuracy in redundant recover test
The test shouldn't have an alert because the panic happens before the
deferred call to recover.
2026-07-10 15:53:40 +01:00
Owen Mansel-Chan
28bd631d52 Accept test result for improved GVN
This occurs as a direct consequence of the CFG migration.
IR::EvalInstruction now deliberately creates a value-producing
instruction for a NotExpr (or LogicalBinaryExpr) from its after-node,
but only when the expression is not in a boolean conditional contex. !d
here sits in a const initializer, not an if/for/switch condition, so
isInBooleanCondContext is false and the after-node becomes an
EvalInstruction — surfacing as the After !... data-flow node. That node
reports getBoolValue() = true and isPlatformIndependentConstant(), so
GVN can match it to the true literal.

On main, a NotExpr in this position produced no such value node (the old
hand-written CFG split the ! evaluation across branch outcomes rather
than exposing a single combined value node), so there was no
DataFlow::Node for !d to be numbered, and hence no row. The new design
intentionally re-introduces a single combined value node in
non-conditional contexts, which is exactly what makes this (correct) GVN
equivalence visible.

So: the line is right, and it's expected fallout from the migration's
handling of !/&&/|| value nodes.
2026-07-10 15:53:38 +01:00
Owen Mansel-Chan
17deba82ca Test changes to be checked 2026-07-10 15:53:36 +01:00
Owen Mansel-Chan
dc8dbacc33 Give KeyValueExpr a type and fix notype test 2026-07-10 15:53:34 +01:00
Owen Mansel-Chan
45ef41361a Fix InitLiteralComponentInstruction.getRhs() for key-value pairs
Key-value pairs are now expressions, but only the value is really written.
2026-07-10 15:53:32 +01:00
Owen Mansel-Chan
a3b3381226 Only make implicit varargs slice node for reachable calls 2026-07-10 15:53:30 +01:00
Owen Mansel-Chan
832f8443bb Go: adopt ForeachStmt with destructuring 2026-07-10 15:53:28 +01:00
Owen Mansel-Chan
107717d473 Fix ParenExpr consistency test failures 2026-07-10 15:53:27 +01:00
Owen Mansel-Chan
80ee8402c1 Fix return edges 2026-07-10 15:53:25 +01:00
Owen Mansel-Chan
4aaf7353a4 Fix circular dependency 2026-07-10 15:53:23 +01:00
Owen Mansel-Chan
f730c889d9 Include ResultZeroInitInstruction in EvalImplicitInitInstruction 2026-07-10 15:53:21 +01:00
Owen Mansel-Chan
e444730da3 Fix defer logic for panics 2026-07-10 15:53:19 +01:00
Owen Mansel-Chan
cdbc19bc12 Fix data flow through tuple extraction for f(g()) 2026-07-10 15:53:17 +01:00
Owen Mansel-Chan
0cb574f37e Remove unused send instruction node
This is now represented by `SendStmt` `isIn` node
2026-07-10 15:53:15 +01:00
Owen Mansel-Chan
7dfe573bfb Fix break in select statements 2026-07-10 15:53:13 +01:00
Owen Mansel-Chan
0c0506ea03 Small refactors 2026-07-10 15:53:11 +01:00
copilot-swe-agent[bot]
6f15605d82 Migrate Go type switches to shared CFG switch model (Option B) 2026-07-10 15:53:09 +01:00
copilot-swe-agent[bot]
e99ade728c Update SSA expected files for new shared-CFG node locations 2026-07-10 15:53:07 +01:00
copilot-swe-agent[bot]
6167003301 Fix switch-case sanitizer edge for shared CFG and accept CFG expected 2026-07-10 15:51:50 +01:00
copilot-swe-agent[bot]
ba7527cde4 Migrate Go expression switch CFG to shared library; handle fallthrough via fallsThrough 2026-07-10 15:35:08 +01:00
Owen Mansel-Chan
75a833c544 Update query for unreachable statements 2026-07-10 15:35:06 +01:00
Owen Mansel-Chan
5d8a9e4951 Fix calls for defer statements 2026-07-10 15:35:04 +01:00
Owen Mansel-Chan
a57367de50 Go CFG: run deferred calls at function exit in LIFO order
Model `defer`ed calls so the call runs at function exit rather than inline
at the `defer` statement, reproducing the previous control-flow semantics:

- Add a per-defer "defer-invoke" node for the deferred call.
- deferExitStep wires normal-exit predecessors (return nodes and body
  fall-through) through the active deferred-call invocations in
  last-in-first-out order, then on to the normal exit target (the
  result-read epilogue for named results, or the normal exit node).
- The chain is reachability-gated using the defer-free successor relation
  (succIgnoringDeferExit / isInOrderNode), so only deferred calls that were
  actually registered on a path are run on that path.
- overridesCallableBodyExit / overridesCallableEndAbruptCompletion suppress
  the default body-exit and return routing for functions containing
  `defer`, so the epilogue is interposed instead.
2026-07-10 15:35:02 +01:00
Owen Mansel-Chan
0bca94ffd8 Shared CFG: add hooks for reachability-gated exit epilogue
Add opt-in InputSig2 predicates to support a function-exit epilogue whose
placement depends on reachability (such as Go's deferred calls, run at
exit in last-in-first-out order):

- deferExitStep: language-provided exit-epilogue edges, wired into
  explicitStep but excluded from the defer-free reachability.
- overridesCallableBodyExit: suppresses the default fall-through edge from
  a body's "after" node to the normal exit, so the epilogue can be
  interposed on fall-through paths.

To let a language compute the reachability gate for those edges without
observing them (and without a non-monotonic cycle through reachable):

- explicitStep is split into explicitStepCommon (defer-free) plus
  deferExitStep, and defaultCfg now negates explicitStepCommon so default
  evaluation does not depend on deferExitStep.
- succIgnoringDeferExit exposes the defer-free successor relation, typed
  over PreControlFlowNode so it does not depend on reachable.
- getASuccessorIgnoringDeferredExit exposes the same relation as a
  ControlFlowNode member for general use.
- isInOrderNode exposes a structural, reachability-free node-identity test
  for use inside the negations a language needs when computing its gate.
- EntryNodeImpl is no longer private, so a language can identify the entry
  node over PreControlFlowNode.

All InputSig2 additions default to none(), leaving other languages
unchanged.
2026-07-10 15:35:00 +01:00
Owen Mansel-Chan
5438846bab Go CFG: anchor result-read epilogue on Normal Exit via new hooks 2026-07-10 15:34:58 +01:00
Owen Mansel-Chan
bb5f601a94 Shared CFG: add callableExitStep hook for routing epilogue tails to exit nodes 2026-07-10 15:34:56 +01:00
Owen Mansel-Chan
9a2f822da0 Allow overriding endAbruptCompletion for callables 2026-07-10 15:34:54 +01:00
Owen Mansel-Chan
2544cca5d3 Include implicit type switch var in CFG 2026-07-10 15:34:52 +01:00
Owen Mansel-Chan
8154ed6984 Accept changes 2026-07-10 15:34:50 +01:00
Owen Mansel-Chan
819f07615f Fix CFG for expressionless switch statements 2026-07-10 15:22:02 +01:00
Owen Mansel-Chan
94ccd7cad1 Fix ConditionGuardNode 2026-07-10 15:22:00 +01:00