Skip to content

cgen: fix generic multi-return interface cast with stale checker types - #27610

Merged
medvednikov merged 8 commits into
vlang:masterfrom
Jengro777:generic_interface
Jul 5, 2026
Merged

medvednikov merged 8 commits into
vlang:masterfrom
Jengro777:generic_interface

Conversation

@Jengro777

@Jengro777 Jengro777 commented Jul 1, 2026 •

Copy link
Copy Markdown
Contributor

fixed: #27637

Description

When a generic method returns a multi-return value where one value needs to be auto-cast to an interface type, the generated C code used the wrong concrete type for the interface cast function when multiple generic instantiations existed in the same compilation unit.

For example, a generic method get() on Container[T] returning (Interface, int) would generate I_Impl2_to_Interface(HEAP(Impl2, ...)) for BOTH Container[Impl1] and Container[Impl2] instantiations — the first instantiation would incorrectly use the second concrete type's cast function.

Root Cause

Two issues combined:

  1. Stale type resolution caches (fn.v): unwrap_generic_cache and resolved_scope_var_type_cache persisted across generic specializations in post_process_generic_fns_for_files. Since the cache keys are based on source position (identical across instantiations), the second specialization would reuse cached results from the first.

  2. Checker-mutated AST types in return_stmt (cgen.v): The V checker processes each generic function instantiation and mutates AST node types in-place, leaving the last processed concrete type's types in the AST. return_stmt used these mutated ret_expr_types directly when generating multi-return struct fields, causing all specializations to use the same (wrong) concrete type.

Changes

vlib/v/gen/c/fn.v

  • Added g.clear_type_resolution_caches() after setting g.cur_concrete_types for each specialization in post_process_generic_fns_for_files, ensuring each instantiation resolves types with its own concrete types.

vlib/v/gen/c/cgen.v

  • In return_stmt, when generating multi-return struct literals in a generic context, resolve the expression type correctly:
    • For Ident expressions: use resolved_scope_var_type_uncached (preserves original generic type via generic_typ field)
    • For other expressions (SelectorExpr, etc.): use resolved_expr_type (resolves through receiver/field type chain)

vlib/v/tests/generic_multi_return_interface_cast_test.v (new)

  • Tests generic multi-return with two concrete types that have different method implementations (x+100 vs x+200). If the wrong interface cast function is used, the return value differs from the expected value, causing an assertion failure.

Testing

  • vlib/v/tests/generic_multi_return_interface_cast_test.v — new test, passes
  • vlib/v/tests/interfaces/interface_generic_pool_cast_multi_type_test.v — existing test, still passes
  • vlib/v/parser/ (9 tests) — all pass
  • vlib/v/checker/ — passes
  • vlib/v/slow_tests/inout/ (91 compiler error tests) — 0 errors
  • vlib/v/tests/comptime/ (169 tests) — all pass

Two issues caused wrong concrete type to be used in interface cast
when multiple generic instantiations existed in one compilation unit:

1. Type resolution caches (unwrap_generic_cache, resolved_scope_var_type_cache)
   persisted across generic specializations in post_process_generic_fns_for_files,
   causing the second specialization to use cached results from the first.

2. return_stmt used checker-mutated ret_expr_types directly for multi-return
   struct field generation. In a generic context, the checker mutates AST
   types to the last processed concrete type, so all specializations would
   use the same (wrong) concrete type.

Fix:
- fn.v: clear_type_resolution_caches() between specializations
- cgen.v return_stmt: resolve Ident via resolved_scope_var_type_uncached
  and non-Ident via resolved_expr_type in generic contexts

Add test: generic_multi_return_interface_cast_test.v
@Jengro777 Jengro777 mentioned this pull request Jul 1, 2026
@Jengro777

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 58c1d70e18

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread vlib/v/gen/c/cgen.v
When a generic multi-return returns a mut generic parameter as an
interface (e.g. fn get[T](mut x T) (Getter, int) { return x, 0 }),
resolved_scope_var_type_uncached resolved x to the lowered pointer
type (&Concrete). return_stmt had already emitted * for auto-deref
vars, so expr_with_cast used the pointer-to-interface helper while
the argument was already dereferenced, producing invalid C.
Fix by dereferencing the resolved type when is_auto_deref_var().
Additionally, the * auto-deref wrapped the entire interface cast call
instead of being inside it; use a synthetic PrefixExpr{.op: .mul} so
* is emitted inside the cast expression.

tests: add multi-return mut param interface cast regression
@Jengro777

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d51a26a55b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread vlib/v/gen/c/cgen.v
…ace cast

When a returned auto-deref var is a mut generic parameter whose concrete
type T is itself a pointer (e.g. fn f[T](mut x T) (I, int) { return x, 0 }
with T = &Impl), the synthetic PrefixExpr{op: .mul} from the previous
fix hits the special handling for mut generic pointer params in expr(),
which emits an additional dereference. The interface caster then receives
**x even though resolved_ret_type was only dereferenced to &Impl,
producing invalid C.

Fix by adding an is_auto_deref_synthetic flag to Gen. Set it before
creating the synthetic PrefixExpr so the extra * logic in the generic
prefix handler is suppressed.

tests: add pointer generic param multi-return interface cast regression
@Jengro777

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 77280840d4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Jengro777 added 4 commits July 3, 2026 14:35
Add test_generic_as_cast_then_multi_return_interface to cover the
pattern: raw := interface_value as T; return raw, ... (multi-return
with interface cast). Both generic instantiations in the same file
trigger the codegen path that was broken before the fix.

Co-authored-by: Jengro <avey777@outlook.com>
@Jengro777

Jengro777 commented Jul 3, 2026 •

Copy link
Copy Markdown
Contributor Author

@GGRei
Sorry to disturb you
Do you have time to review this PR?

Since the last codex review, only testing code and merging upstream code have been added

@GGRei
GGRei self-requested a review July 3, 2026 08:15

@GGRei GGRei left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good for me. Good work!

@medvednikov
medvednikov merged commit 5337164 into vlang:master Jul 5, 2026
68 of 93 checks passed
@Jengro777
Jengro777 deleted the generic_interface branch July 6, 2026 01:31
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.

C codegen: type confusion between DatabasePool[pg.DB] and DatabasePool[mysql.DB] in same file

3 participants