FuelLabs/sway · error
`PushAll` and `PopAll` don't belong in control flow ops sinc
Error message
`PushAll` and `PopAll` don't belong in control flow ops since they're not about control flow
What it means
This unreachable! guards worst_case_instruction_size, which computes the worst-case concrete instruction count for each allocated abstract op. PushAll and PopAll are abstract stack-management ops, not control-flow ops, so they must never appear in the ControlFlowOp arm of this match. Reaching this panic means an abstract instruction set was built with PushAll/PopAll ops inside the control-flow op representation, i.e. an internal codegen misclassification bug.
Source
Thrown at sway-core/src/asm_generation/fuel/allocated_abstract_instruction_set.rs:423
// This is a concrete op, size is fixed.
Either::Left(_) => 1,
// Worst case for jump is 2 opcodes, and 3 for calls.
Either::Right(Jump { ref type_, .. }) => match type_ {
JumpType::Unconditional => 2,
JumpType::NotZero(_) => 2,
JumpType::Call => 3,
},
Either::Right(JumpToAddr(..)) => 1,
Either::Right(ReturnFromCall { .. }) => 1,
Either::Right(Comment) => 0,
Either::Right(DataSectionOffsetPlaceholder) => {
// If the placeholder is 32 bits, this is 1. If 64, this should be 2. We use LW
// to load the data, which loads a whole word, so for now this is 2.
2
}
Either::Right(ConfigurablesOffsetPlaceholder) => 2,
Either::Right(PushAll(_)) | Either::Right(PopAll(_)) => unreachable!(
"`PushAll` and `PopAll` don't belong in control flow ops \
since they're not about control flow"
),
}
}
// Actual size of an instruction.
//
// **Note that this return incorrect values for far jumps, they must be handled separately.**
// The return value is in concrete instructions, i.e. units of 4 bytes.
fn instruction_size_not_far_jump(op: &AllocatedAbstractOp, data_section: &DataSection) -> u64 {
use ControlFlowOp::*;
match op.opcode {
Either::Right(Label(_)) => 0,
// A special case for LoadDataId which may be 1 or 2 ops, depending on the source size.
Either::Left(AllocatedInstruction::LoadDataId(_, ref data_id)) => {
let has_copy_type = data_section.has_copy_type(data_id).expect(View on GitHub (pinned to dad95cc42b)
Solutions
- Find where the abstract op was classified as a ControlFlowOp and move PushAll/PopAll to the plain (concrete) instruction category
- Ensure lowering passes never wrap PushAll/PopAll in Either::Right (control flow) — they should be Either::Left concrete ops
- Add a debug-time classification check when AllocatedAbstractOp values are constructed so misclassification is caught at creation rather than during size computation
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at sway-core/src/asm_generation/fuel/allocated_abstract_instruction_set.rs:423 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of FuelLabs/sway@dad95cc42b (2026-09-04).
Data as JSON: /api/errors/a0097561584b8e6f.
Report an issue: GitHub.