Skip to content

cgen: add deref for aliased reference types using mut captures#27377

Merged
medvednikov merged 3 commits into
vlang:masterfrom
dy-tea:mut-capture-deref
Jun 7, 2026
Merged

cgen: add deref for aliased reference types using mut captures#27377
medvednikov merged 3 commits into
vlang:masterfrom
dy-tea:mut-capture-deref

Conversation

@dy-tea

@dy-tea dy-tea commented Jun 7, 2026

Copy link
Copy Markdown
Member

Fixes #27355

@medvednikov

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@medvednikov

Copy link
Copy Markdown
Member

Verdict: Yes, it's a good PR — correct, well-scoped, and idiomatic. ✅

What I verified

  • Bug reproduces on master: the reproducer fails with error: struct or union expected / member reference base type 'main__AppRef'... is not a structure or union (one-deref-short access on the captured pointer).
  • Fix works: with the PR applied and v rebuilt, the reproducer compiles and prints ok.
  • No regressions: full vlib/v/tests/aliases/ suite passes (66 passed, 1 skipped, incl. the new test), plus targeted closure tests (closure_test.v, closure_of_method_defined_on_alias_test.v, mut_closure_fixed_array_thread_test.v, type_alias_of_fn_with_mut_args_test.v, alias_to_ptr_arg_test.v).
  • Edge cases I added all behave correctly: pointer-alias to a non-struct (type IntRef = &int), read-only alias capture, plain mut struct capture, and passing an alias-typed variable (the else branch). Importantly, mutation propagates through the pointer (score = 2 after two calls), so the alias-as-pointer semantics are preserved, not silently copied.
  • Formatting: v fmt -verify is clean on both files.

Why the approach is sound

The fix is three coordinated changes, and each one mirrors an existing pattern right next to it:

  1. gen_anon_fn (struct init) and gen_anon_fn_decl (struct field type) — symmetrically extend the existing is_auto_deref && !var.is_mut deref so it also fires for mut captures when the type is an .alias. This makes the context-struct field a single pointer (AppRef = App*), so the body's ->score matches.
  2. ref_or_deref_arg_ex — adds a branch parallel to the existing struct expected_type == arg_typ.ref() branch, emitting the extra & indirection for the mut alias arg. The new branch is tightly guarded (exp_sym.kind == .alias && unaliased_type(expected) == arg_typ) and is disjoint from the struct branch below it, so no shadowing.

Minor nitpicks (non-blocking)

  • The test is weak: test_main() only checks that it compiles and runs — there's no assert a.score == 1. I confirmed the mutation does propagate, so adding an assertion would pass and would lock in the semantics, not just "doesn't crash." Worth requesting.
  • Small asymmetry: the new PrefixExpr path does g.expr(...) without setting g.arg_no_auto_deref = true, whereas the analogous struct branch immediately below does. It's harmless for the &a case, but a reviewer might flag the inconsistency.
  • Some structural duplication with the struct branch — acceptable for readability.

@medvednikov medvednikov merged commit d1b4f65 into vlang:master Jun 7, 2026
83 of 93 checks passed
@medvednikov

Copy link
Copy Markdown
Member

Thanks!

@dy-tea dy-tea deleted the mut-capture-deref branch June 7, 2026 19:25
dy-tea added a commit to wenxuanjun/v that referenced this pull request Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cgen, closures: mut capture of a pointer-alias type emits a one-deref-short member access

2 participants