diem/diem · error

Target locals out contains new local

Error message

Target locals out contains new local

What it means

This unreachable!() fires inside generate_block in the test-generation bytecode generator. The generator tracks an 'abstract state' of locals; after emitting bytecode for a block it verifies that the target locals output introduced no locals that were not already known to the state. Hitting this panic means the local-tracking logic produced an inconsistent local assignment, which the generator assumes is impossible.

Source

Thrown at language/testing-infra/test-generation/src/bytecode_generator.rs:707

                    && *current_availability == BorrowState::Available
                {
                    state = self.apply_instruction(
                        fn_context,
                        state,
                        &mut bytecode,
                        Bytecode::MoveLoc(*i as u8),
                        true,
                    )?;
                    state = self.apply_instruction(
                        fn_context,
                        state,
                        &mut bytecode,
                        Bytecode::Pop,
                        true,
                    )?;
                }
            } else {
                unreachable!("Target locals out contains new local");
            }
        }
        // Update the module to be the module that we've been building in our abstract state
        Some((bytecode, state))
    }

    /// Generate the body of a function definition given a set of starting `locals` and a target
    /// return `signature`. The sequence should contain at least `target_min` and at most
    /// `target_max` instructions.
    pub fn generate(
        &mut self,
        fn_context: &mut FunctionGenerationContext,
        locals: &[SignatureToken],
        fh: &FunctionHandle,
        acquires_global_resources: &[StructDefinitionIndex],
        module: &mut CompiledModule,
        call_graph: &mut CallGraph,
    ) -> Option<Vec<Bytecode>> {

View on GitHub (pinned to fc4714a8ea)

Solutions

  1. Inspect the failing seed and the generated instruction sequence to find which bytecode writes a new local index
  2. Fix the AbstractState join/merge so target locals only contain locals already present in the state, or remap the new local to an existing slot
  3. If the instruction legitimately creates a local, update generate_block to handle it instead of relying on the unreachable arm
  4. Add a regression proptest case with the failing seed

Example fix

// before
} else {
    unreachable!("Target locals out contains new local");
}
// after
} else if let Some(existing) = state.local_index_for(&new_local) {
    state.remap_target_local(existing);
} else {
    unreachable!("Target locals out contains new local");
}
Defensive patterns

Strategy: validation

Validate before calling

assert!(target_out.iter().all(|l| state.locals().contains_key(l)), "target locals out introduces unknown local");

Type guard

fn locals_known(state: &AbstractState, out: &BTreeSet<LocalIndex>) -> bool {
    out.iter().all(|l| state.local_exists(*l))
}

Prevention

When it happens

Trigger: Running the Move test generator (proptest-driven bytecode generation) on a generated program whose generated instructions create a new local in the target locals out-set instead of only reusing/merging existing ones; typically a generator bug after adding a new bytecode variant that writes to a fresh local index.

Common situations: Developers extending the test generator with new instruction kinds, or changing local index allocation in AbstractState, run randomized generation and hit this panic on some seed.

Related errors


AI-assisted analysis of diem/diem@fc4714a8ea (2026-09-04). Data as JSON: /api/errors/f6fd2debe6d21fbd. Report an issue: GitHub.