Skip to content

[Bug] STLOC_TYPE_MISMATCH_ERROR caused by reference mode leaks through Block/IfElse #19345

@zzjas

Description

@zzjas

🐛 Bug

//# publish
module 0x42::test {
    struct Inner has copy, drop { x: u64 }
    struct Outer has copy, drop { inner: Inner }

    fun if_else_wrapping_select(cond: bool, o1: Outer, o2: Outer): u64 {
        (if (cond) { o1.inner } else { o2.inner }).x
    }
}

Compiling this gives:

bug: bytecode verification failed with unexpected status code `STLOC_TYPE_MISMATCH_ERROR`:
Error message: none
  ┌─ .../test/sources/test3.move:7:22
  │
7 │         (if (cond) { o1.inner } else { o2.inner }).x
  │                      ^^^^^^^^
  │

Analysis from Claude Code

bytecode_generator.rs:

When gen_select processes EXPR.field, it calls gen_auto_ref_arg(EXPR, Immutable) which enters reference mode and delegates to gen_arg. If EXPR is a Block or IfElse (not a direct Select, Temporary, or LocalVar), gen_arg falls to the _ => case (line ~1375), allocates a value-typed temporary via self.get_node_type(id), and calls self.generate(vec![temp], exp) — still in reference mode.

generate for Block (line ~450) and IfElse (lines ~494-498) propagates to inner expressions without resetting reference mode. When an inner Select (e.g. o.inner) is reached, gen_select runs with:

  • target_type = Inner (value, not reference)
  • self.reference_mode() = true (leaked)

This makes need_read_ref = !(false || true) = false, so BorrowField writes a &Inner reference directly into the value-typed Inner temporary — a STLOC_TYPE_MISMATCH_ERROR at bytecode verification.

Triggering expressions: { o.inner }.x and (if (cond) { o1.inner } else { o2.inner }).x.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status

✅ Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions