Skip to content

bug - loop item field move after field borrow emits invalid Rust #616

@dannymeijer

Description

@dannymeijer

Area

  • Compiler (frontend/backend/codegen)

Summary

Expected: Incan should compile record-field access from loop items without emitting Rust that moves a non-Copy field out of a shared reference. If a field is first borrowed for a helper call and later needs to be stored in a list, the compiler should clone/own the field consistently or otherwise emit valid Rust.

Actual: a loop over list[Assignment] can borrow assignment.output_name for a helper call, then assigning or appending assignment.output_name emits Rust that tries to move the String field out of assignment, which is behind a shared reference. Rust compilation fails with E0507.

This is related to the closed ownership-inference umbrella issue #121, but it is a concrete remaining codegen failure on incan 0.3.0-rc1.

Reproduction steps

  1. Create /private/tmp/incan-loop-field-move-repro/repro.incn:
@derive(Clone)
model Assignment:
    output_name: str


def names(assignments: list[Assignment]) -> list[str]:
    """Collect names from assignment records."""
    mut output_names: list[str] = []
    for assignment in assignments:
        existing_idx = index_of_name(output_names, assignment.output_name)
        if existing_idx >= 0:
            output_names[existing_idx] = assignment.output_name
        else:
            output_names.append(assignment.output_name)
    return output_names


def index_of_name(names: list[str], name: str) -> int:
    """Return the first matching name index, or `-1` when absent."""
    for idx, current in enumerate(names):
        if current == name:
            return idx
    return -1


def main() -> None:
    """Exercise record-field access from a list loop item."""
    result = names([Assignment(output_name="amount")])
    assert result[0] == "amount"
  1. Run:
/Users/danny/Development/encero/incan/target/debug/incan build /private/tmp/incan-loop-field-move-repro/repro.incn /private/tmp/incan-loop-field-move-repro/out

Output / logs

Generated Rust project in: /private/tmp/incan-loop-field-move-repro/out
Building...
Build failed:
   Compiling repro v0.3.0-rc1 (/private/tmp/incan-loop-field-move-repro/out)
error[E0507]: cannot move out of `assignment.output_name` which is behind a shared reference
  --> src/main.rs:22:17
   |
22 |             ) = assignment.output_name;
   |                 ^^^^^^^^^^^^^^^^^^^^^^ move occurs because `assignment.output_name` has type `String`, which does not implement the `Copy` trait
   |
help: consider cloning the value if the performance cost is acceptable
   |
22 |             ) = assignment.output_name.clone();
   |                                       ++++++++

For more information about this error, try `rustc --explain E0507`.
error: could not compile `repro` (bin "repro") due to 1 previous error

Original InQL trigger is src/projection_builders.incn on InQL PR dannys-code-corner/InQL#29. Removing the explicit str(assignment.output_name) materialization from project_output_columns(...) produces the same Rust error during make build.

Environment

OS: macOS / zsh local development environment
Rust: rustc 1.96.0-nightly (362211dc2 2026-03-24)
Incan: incan 0.3.0-rc1
Incan commit: 939ff58e0013fb342c256205ad83383f21626d44
Command:

/Users/danny/Development/encero/incan/target/debug/incan build /private/tmp/incan-loop-field-move-repro/repro.incn /private/tmp/incan-loop-field-move-repro/out

Blocking / workaround status

Non-blocking for InQL PR #29 because the field can be explicitly materialized once before assignment/append:

output_name = str(assignment.output_name)
if existing_idx >= 0:
    output_columns[existing_idx] = output_name
else:
    output_columns.append(output_name)

That workaround preserves semantics, but it should remain tracked as a compiler/codegen bug rather than treated as ordinary InQL style.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingincan compilerSuggestions, features, or bugs related to the Compiler (frontend/backend/codegen)

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions