diem/diem · error
Unbound local {:?}
Error message
Unbound local {:?} What it means
`copy_local` reads the abstract value of a local from the analyzer's `locals` map; if the temp index has no bound value it panics with 'Unbound local'. The analysis tracks abstract addresses per TempIndex, and every read local must have been bound by a prior `assign_local`, `borrow_loc`, or parameter initialization. This panic means the instruction stream referenced a local before (or without) binding it in the abstract state.
Source
Thrown at language/move-prover/bytecode/src/read_write_set_analysis.rs:203
self.locals.bind_local_node(*ret, node, caller_fun_env)
}
}
// (5) join caller and callee accesses
// TODO: can we do a strong update here in some cases?
self.accesses.join(&new_callee_accesses);
}
/// Copy the contents of `rhs_index` into `lhs_index`. Fails if `rhs_index` is not bound
pub fn copy_local(
&mut self,
lhs_index: TempIndex,
rhs_index: TempIndex,
fun_env: &FunctionEnv,
) {
let rhs_value = self
.locals
.get_local(rhs_index, fun_env)
.unwrap_or_else(|| panic!("Unbound local {:?}", rhs_index))
.clone();
self.locals.bind_local(lhs_index, rhs_value, fun_env)
}
pub fn assign_local(
&mut self,
lhs_index: TempIndex,
rhs_index: TempIndex,
func_env: &FunctionEnv,
) {
if let Some(rhs_data) = self.locals.get_local(rhs_index, func_env).cloned() {
self.locals.bind_local(lhs_index, rhs_data, func_env);
self.record_access(rhs_index, Access::Read, func_env)
} else if let Some(rhs_node) = self.locals.get_local_node(rhs_index, func_env).cloned() {
self.locals.bind_local_node(lhs_index, rhs_node, func_env);
}
}
View on GitHub (pinned to fc4714a8ea)
Solutions
- Verify the offending bytecode instruction's temp index against the function's local/parameter list in fun_env
- Ensure all locals are bound (via assign_local or parameter initialization) before `copy_local` reads them
- Re-run bytecode verification/stackless transformation with a matched toolchain version so temp indices are consistent
- If you added a bytecode pass, bind an abstract value for the local in the state before copying
Example fix
// before
let rhs_value = self.locals.get_local(rhs_index, fun_env)
.unwrap_or_else(|| panic!("Unbound local {:?}", rhs_index)).clone();
// after
let rhs_value = self.locals.get_local(rhs_index, fun_env)
.cloned()
.unwrap_or_else(|| AbsAddr::default()); // or skip/bail on unbound locals Defensive patterns
Strategy: validation
Validate before calling
// Before copy_local, check the source local is bound:
fn local_bound(state: &TransferFunctionState, idx: TempIndex, fun_env: &FunctionEnv) -> bool {
idx < fun_env.get_local_count() && state.locals.get_local(idx, fun_env).is_some()
} Type guard
fn is_bound(state: &TransferFunctionState, idx: TempIndex, env: &FunctionEnv) -> bool {
state.locals.get_local(idx, env).is_some()
} Prevention
- Bind all parameters/locals at function entry before interpretation
- Avoid consuming (MoveLoc) locals that are later read
- Run bytecode well-formedness verification before analysis
When it happens
Trigger: `copy_local(lhs_index, rhs_index, fun_env)` called with `rhs_index` never bound in the current abstract state — e.g. bytecode `CopyLoc` of a local whose abstract value was dropped/not initialized, or an out-of-sync temp index after bytecode transformation.
Common situations: Running the RWSet analysis over bytecode generated or rewritten by an external tool; stackless-bytecode passes that remove initialization of dead locals but leave later reads; mismatches between function signature temps and the analyzed body.
Related errors
- Untracked local {:?} of address type
- Unbound local
- Unbound address local
- Bad offset type {:?} for address base
- unsupported oper {:?}
AI-assisted analysis of diem/diem@fc4714a8ea (2026-09-04).
Data as JSON: /api/errors/87474c4b01af3505.
Report an issue: GitHub.