Skip to content

checker: accept inline fn literal for fn-type alias param in generic fn (#28181) - #28191

Open
waterWang wants to merge 54 commits into
vlang:masterfrom
waterWang:fix-28181-fn-literal-fn-type-alias-generic
Open

checker: accept inline fn literal for fn-type alias param in generic fn (#28181)#28191
waterWang wants to merge 54 commits into
vlang:masterfrom
waterWang:fix-28181-fn-literal-fn-type-alias-generic

Conversation

@waterWang

Copy link
Copy Markdown
Contributor

Fixes #28181

Background

An inline fn literal passed to a vlib method that takes a fn-type alias as the parameter is rejected by the V3 type checker with a spurious error when the call site is inside a generic function:

type Handler = fn (req &Request, mut wr ResponseWriter)

fn startup[T]() {
    mut r := Router{}
    r.register('x', fn (req &Request, mut wr ResponseWriter) {
    })
}

The same call outside a generic function compiles fine.

Root cause

Generic function bodies are validated a second time during monomorphization (receiver_call_validate_argsresolved_receiver_arg_compatible in vlib/v3/transform/fn.v). That path compares the argument type to the parameter type as strings:

  • the fn literal is stringified as fn (&Request, &ResponseWriter) (no mut mode on the parameter, fn ( with a space)
  • the fn-type alias expands to its declared spelling fn(&Request, mut ResponseWriter) (mut parameter mode, no space)

actual != expected and no fn-signature comparison existed, so the pair was rejected even though both spellings denote the identical function type.

Fix

Added fn_type_texts_signature_compatible (plus the normalize_fn_param_text helper) and wired it into both resolved_receiver_arg_compatible and fn_field_arg_compatible:

  • splits both fn texts into parameter types + return type via the existing fn_type_text_parts
  • canonicalizes each parameter: mut T&T (a mut parameter denotes reference passing, and a fn literal stringifies it as &T), shared stripped, interior whitespace removed
  • compares canonical parameter lists and return types

Test

vlib/v/tests/generics/generic_fn_literal_matches_fn_type_alias_issue_28181_test.v — regression test that reproduces #28181 exactly (inline fn literal as a fn-type alias argument inside fn startup[T]()). Verified failing without the fix (type checker found 1 error(s): cannot use \fn (&Request, &ResponseWriter)` as argument 2 to `r.register`; expected `Handler``) and passing with it. All existing generics/fn-type tests continue to pass.

@medvednikov

Copy link
Copy Markdown
Member

@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: e960ed8d01

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/fn.v Outdated
Comment on lines +11817 to +11818
} else if clean.starts_with('shared ') {
clean = clean[7..].trim_space()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve shared mode when comparing function parameters

When a specialized generic call compares callbacks whose parameter texts differ only by shared (for example, fn(shared State) versus fn(State)), this branch makes the signatures equal. A shared parameter has distinct calling semantics: callers pass shared storage explicitly and codegen uses the shared-parameter metadata to handle its synchronized wrapper. Accepting the non-shared callback here can therefore suppress the intended type error and leave codegen calling it with an incompatible representation; retain and compare the shared mode rather than dropping it.

Useful? React with 👍 / 👎.

@antono3

antono3 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Replayed this cleanly onto current master (c0e47bf255) and tested the V3 path on Ubuntu 24.04.

Focused results after adding a final newline to the new test file locally:

There is one mechanical blocker in the submitted commit: generic_fn_literal_matches_fn_type_alias_issue_28181_test.v has no terminating newline, so ./v fmt -verify reports it as not vfmt-ed. Please add the final newline. The original formatting failure is visible in https://github.com/antono3/v/actions/runs/34096872789; the focused green steps after that correction are in https://github.com/antono3/v/actions/runs/34098531873 (the later full-directory V3 sweep was manually stopped after exceeding the useful scope/time for this review).

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-09T02:34:12.249831Z e739bf1 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@medvednikov

Copy link
Copy Markdown
Member

@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: 23e60b5bf7

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/fn.v Outdated
if clean.starts_with('mut ') {
clean = '&' + clean[4..].trim_space()
}
return clean.replace(' ', '')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve token boundaries when normalizing signatures

When a specialized generic expects a callback parameter such as chan foo.Item, an actual callback taking the unrelated type chanfoo.Item is treated as compatible because removing every space maps both spellings to chanfoo.Item. This can bypass the monomorphization-time type check when the generic body could not be validated concretely earlier, allowing an incompatible callback representation into codegen; normalize formatting around function syntax without deleting meaningful spaces in compound types.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Fixed in 00fd0e6: signature normalization now removes whitespace only around type punctuation while retaining identifier-token boundaries such as chan foo.Item; unit coverage distinguishes it from chanfoo.Item.

@medvednikov

Copy link
Copy Markdown
Member

@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: 00fd0e6a5a

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/fn.v Outdated
Comment on lines +11815 to +11816
if clean.starts_with('mut ') {
clean = '&' + clean[4..].trim_space()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid adding a second pointer to mut pointer parameters

When a generic callback parameter mut T is specialized with T = &Item, this blindly normalizes the resulting mut &Item to &&Item. The canonical type logic in fn_compatible_param_type deliberately adds a pointer only when the parameter is not already a pointer (vlib/v3/types/checker_tail.v:15444-15451), so mut &Item has the effective parameter type &Item, not &&Item. As a result, deferred specialization validation can accept an actual fn(&&Item) callback for an expected fn(mut &Item), allowing an incompatible callback signature through; preserve an existing leading & when normalizing mut.

Useful? React with 👍 / 👎.

@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: c5c4f0711c

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/fn.v Outdated
Comment on lines +11817 to +11818
if !clean.starts_with('&') {
clean = '&' + clean

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve pointer-like builtins when normalizing mut

In a deferred generic specialization involving voidptr, byteptr, or charptr, this guard canonicalizes mut voidptr to &voidptr, causing fn_type_texts_signature_compatible('fn(&voidptr)', 'fn(mut voidptr)') to return true even though the former takes a double pointer. These builtins are already represented as types.Pointer, and fn_compatible_param_type intentionally avoids adding another pointer to them, so this can admit an ABI-incompatible callback. The fresh evidence beyond the earlier mut &Item issue is that the revised guard recognizes only a leading & and still overlooks named pointer builtins.

Useful? React with 👍 / 👎.

@medvednikov

Copy link
Copy Markdown
Member

Fixed the exact-head pointer-builtin finding in 821479d148:

  • mut voidptr, mut byteptr, and mut charptr now retain their already-pointer-like ABI spelling during callback-signature normalization.
  • Added positive and negative compatibility regressions for all three pointer builtins.
  • Validation: vlib/v3/transform/fn_test.v passed; vlib/v/compiler_errors_test.v passed (1619 passed, 1 skipped); formatting and git diff --check passed.

@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: 821479d148

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/fn.v
Comment on lines +11797 to +11799
for i in 0 .. actual_params.len {
if normalize_fn_param_text(actual_params[i]) != normalize_fn_param_text(expected_params[i]) {
return false

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve shared mode before comparing callback signatures

When an inline callback has a shared parameter, this comparison still cannot validate its mode. Fresh evidence in the current head is that lift_fn_literal reconstructs the callback signature from param_type.name() at fn.v:10122-10131, while parsing shared T strips the modifier at checker_tail_stmt.v:13719-13720; consequently fn (shared value State) reaches this check as fn(State). A non-shared alias can therefore accept the shared callback through the earlier text-equality path, while a matching fn(shared State) alias is rejected, so the literal's shared metadata must be retained or inspected separately during compatibility checking.

Useful? React with 👍 / 👎.

@medvednikov

Copy link
Copy Markdown
Member

Fixed the exact-head shared callback review finding by retaining shared mode while lifting function-literal parameters, preserving shared-bearing specialized signatures, and comparing canonical signatures without discarding parameter names or mut-pointer semantics. Added a generic-context regression proving a shared alias accepts the matching literal and a non-shared alias rejects it. Validation: targeted shared-mode regression passed; full type_checker_errors_test.v passed; fn_test.v passed; compiler_errors passed (1619 passed, 1 skipped); formatting and git diff --check passed.

@medvednikov

Copy link
Copy Markdown
Member

@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: c71300c92b

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/fn.v Outdated
is_mut = true
clean = clean[4..].trim_space()
}
clean = generic_fn_type_param_payload(clean)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve const_ callback parameter metadata

When a generic call passes an inline callback such as fn (event &C.native_event) to an alias declared as fn (const_event &C.native_event), this payload extraction removes const_event and makes both signatures normalize to &C.native_event. That prefix is not merely a parameter name: c_abi_fn_param_type in vlib/v3/types/checker_tail_stmt.v uses it to emit a const pointer, so accepting the non-const callback produces an incompatible C function-pointer assignment and can fail -cstrict builds. Preserve this C-ABI qualifier while discarding ordinary parameter names.

Useful? React with 👍 / 👎.

@medvednikov

Copy link
Copy Markdown
Member

Fixed the latest exact-head finding in 7e62b77. Function-signature normalization now preserves the const_ pointer-parameter qualifier while discarding ordinary parameter names. Generic receiver validation also compares the source fn literal C ABI signature against the expected alias before fn-literal lifting can erase that metadata.

Regression coverage verifies matching const callback literals are accepted, non-const literals are rejected in generic specialization, and normalized signature comparisons distinguish the qualifier.

Validation:

  • targeted const callback generic regression passed
  • vlib/v3/transform/fn_test.v passed
  • full vlib/v3/tests/type_checker_errors_test.v passed
  • compiler_errors: 1619 passed, 1 skipped
  • formatting and git diff --check passed

@medvednikov

Copy link
Copy Markdown
Member

@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: 7e62b7794c

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/fn.v Outdated
if node.kind != .fn_literal {
return true
}
expected_abi := t.tc.c_abi_fn_ptr_type_for_type_text(expected_type) or { return true }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Compare C ABI qualifiers in both directions

When the expected alias is an ordinary fn (event &C.native_event) but the inline literal uses fn (const_event &C.native_event), this lookup finds no expected C-ABI metadata and returns true without inspecting the literal. The fresh evidence in the current head is this one-sided early return: lift_fn_literal subsequently drops the literal's const_ parameter name, so the textual fallback also treats the signatures as identical and can emit an incompatible const-taking function-pointer assignment under -cstrict. Inspect both signatures even when only the actual callback carries ABI metadata.

Useful? React with 👍 / 👎.

Comment thread vlib/v3/transform/fn.v
is_c_abi_const = true
clean = tail
} else {
clean = generic_fn_type_param_payload(clean)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Normalize names in nested callback parameters

When a callback alias contains another function type with named parameters, such as fn (cb fn (value Item)), the inline literal is stringified as fn (fn (Item)), but this only removes the outer parameter name. Because generic_top_level_space_index deliberately ignores the space inside the nested fn, the expected side remains fn(value Item) and the otherwise identical signatures compare unequal, reintroducing a spurious cannot use error during generic specialization. Recursively normalize parameters of nested function types as well.

Useful? React with 👍 / 👎.

@medvednikov

Copy link
Copy Markdown
Member

Fixed both latest exact-head findings in 84b49f9:

  • C ABI callback qualifiers are compared symmetrically, so const-to-nonconst and nonconst-to-const fn literal assignments are both rejected
  • nested function-type parameters are normalized recursively, discarding ordinary nested parameter names without changing their types

Regression coverage includes the reverse const mismatch and a generic nested callback alias whose inner parameter is named.

Validation:

  • both focused generic regressions passed
  • vlib/v3/transform/fn_test.v passed
  • full vlib/v3/tests/type_checker_errors_test.v passed
  • compiler_errors: 1619 passed, 1 skipped
  • formatting and git diff --check passed

@medvednikov

Copy link
Copy Markdown
Member

@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: 84b49f92ca

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/monomorphize.v Outdated
Comment on lines +4319 to +4320
fn_params, _ := fn_type_text_parts(qualified) or { []string{}, '' }
fn_has_shared_param := fn_params.any(it.trim_space().starts_with('shared '))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve shared modes in nested callback signatures

When a specialized signature contains a nested callback such as fn (cb fn (shared value State)), the sole outer parameter starts with cb, so this check does not detect the nested shared mode. The subsequent parse/name round trip stores it as fn(fn(State)) because types.FnType has no shared-parameter metadata, which can make matching nested callbacks fail validation or incompatible callbacks use the wrong shared representation. Recursively inspect nested function parameters and return types before allowing the round trip.

Useful? React with 👍 / 👎.

Comment thread vlib/v3/transform/fn.v
Comment on lines +11786 to +11788
if (actual.starts_with('fn(') || actual.starts_with('fn (')) && (expected.starts_with('fn(')
|| expected.starts_with('fn (')) && fn_type_texts_signature_compatible(actual, expected) {
return true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor C-ABI rejection for function-field arguments

When validate_specialized_fn_field_call checks an inline callback passed through a function-valued field, it falls back to this helper after resolved_receiver_arg_compatible returns false for a C-ABI mismatch. For example, with type Event = C.native_event, an expected fn (event &Event) has a const C pointer while an actual fn (mut event Event) has a mutable pointer, yet this normalization maps both parameters to &Event and returns true. That overrides the explicit ABI rejection and can emit an incompatible function-pointer call in specialized generic code; this fallback must also honor the C-ABI comparison.

Useful? React with 👍 / 👎.

@medvednikov

Copy link
Copy Markdown
Member

Fixed both latest exact-head findings in bda24ad:

  • shared parameter modes are detected at any nesting depth before specialized signature types take a lossy parse/name round trip
  • function-field argument fallback now honors the fn literal C ABI comparison and cannot re-accept a const/mutable mismatch

Regression coverage includes matching and incompatible nested shared callbacks plus a generic function-field call with an incompatible mutable C-alias callback.

Validation:

  • both focused generic regressions passed
  • vlib/v3/transform/fn_test.v passed
  • full vlib/v3/tests/type_checker_errors_test.v passed
  • compiler_errors: 1619 passed, 1 skipped
  • formatting and git diff --check passed

@medvednikov

Copy link
Copy Markdown
Member

Fixed callback modes in struct literals in 25bcd56:

  • recursively validate regular struct field initializers against their declared source types
  • preserve field alias spelling while substituting generic owner parameters, then qualify in declaration-file scope
  • cover matching and rejected callback-valued Options{handler: fn (...) {}} arguments

Validation:

  • rebuilt ./vnew
  • full callback container/struct regression group
  • cross-module callback alias-scope regression
  • transform fn test
  • vlib/v/compiler_errors_test.v (1619 passed, 1 skipped)

@medvednikov

Copy link
Copy Markdown
Member

@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: 25bcd56243

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/fn.v Outdated
Comment on lines +12357 to +12358
if container_compatible := t.fn_literal_container_modes_compatible(arg_id, expected_type) {
return container_compatible

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve outer type checks for container literals

When specialization validates a container literal, this early return replaces the ordinary actual_type versus expected_type check with only the recursive mode scan. In a generic body such as fn invoke[T](s T) { s.take(Foo{}) }, specializing T to a receiver whose take method expects int now accepts the Foo{} argument because the struct branch returns true regardless of its expected outer type, allowing invalid code to reach C generation. Run the mode scan in addition to, rather than instead of, the aggregate's normal type compatibility check.

Useful? React with 👍 / 👎.

Comment thread vlib/v3/transform/fn.v Outdated
if field.kind != .field_init || field.children_count == 0 {
continue
}
field_type := t.lookup_struct_field_type(struct_type, field.value) or { continue }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate positional struct callback fields

When a callback field is initialized positionally, as in Options{fn (const_event &C.native_event) {}}, its field_init has an empty field.value; this lookup therefore fails and continue skips ABI-mode validation. A positional const-qualified literal can consequently initialize a plain callback field during generic specialization, despite producing an incompatible C function pointer. Resolve empty field names by their declaration index before validating the value.

AGENTS.md reference: AGENTS.md:L320-L322

Useful? React with 👍 / 👎.

Comment on lines +4338 to 4339
if has_source_only_mode {
return qualified

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Qualify local types before preserving callback modes

When an imported generic function declares a callback using a module-local type, such as module m declaring fn apply[T](cb fn (const_event &Event)), this early return preserves the source-only mode before the parse/name round trip that would qualify Event as m.Event. Because qualify_specialized_signature_type_text leaves ordinary module-local names bare, validation from a caller with its own Event resolves the expected ABI against the caller type and rejects a matching fn (const_event &m.Event). Qualify declaration-local names while retaining the source mode.

AGENTS.md reference: AGENTS.md:L320-L322

Useful? React with 👍 / 👎.

@medvednikov

Copy link
Copy Markdown
Member

Fixed the latest Codex findings in ad2192f214:

  • retain normal outer-type compatibility checks after recursive callback container validation
  • resolve positional struct literal fields by declaration order before checking callback ABI modes
  • qualify declaration-local callback parameter types in imported generic signatures
  • added matching/rejected regressions for aggregate types, positional fields, and module-local type collisions

Tests: focused callback container group; focused declaration-module alias group; vlib/v3/transform/fn_test.v; vlib/v/compiler_errors_test.v (1,619 passed, 1 skipped).

@medvednikov

Copy link
Copy Markdown
Member

@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: ad2192f214

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/fn.v
}
actual_value := actual_abi or { return false }
expected_value := expected_abi or { return false }
return actual_value == expected_value

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply optional promotion before comparing callback ABIs

When a specialized generic body passes a matching bare callback literal to an optional parameter such as handler ?ConstHandler, V's normal T-to-?T argument promotion should accept it. Here the literal produces fn_ptr:... while the expected type produces ?(fn_ptr:...), so this equality returns false before the optional compatibility branch at line 12490 can run, emitting a spurious cannot use error. Compare the literal against the option's base callback ABI before applying the wrapper.

AGENTS.md reference: AGENTS.md:L320-L322

Useful? React with 👍 / 👎.

Comment thread vlib/v3/transform/fn.v Outdated
}
node := t.a.nodes[int(arg_id)]
match node.kind {
.paren, .cast_expr, .expr_stmt {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor non-callback casts when collecting literal ABIs

When a specialized generic call passes an explicitly boxed callback such as accept(Any(fn (const_event &C.native_event) {})), descending through this .cast_expr exposes the inner ABI-qualified literal even though the argument's type is now the empty interface Any. The subsequent check finds an actual ABI but no expected ABI and rejects the call, despite the existing empty-interface behavior demonstrated in vlib/v/tests/casts/cast_to_empty_interface_test.v:35-44. Stop ABI comparison at casts whose target is not callback-shaped.

AGENTS.md reference: AGENTS.md:L320-L322

Useful? React with 👍 / 👎.

@medvednikov

Copy link
Copy Markdown
Member

Fixed the latest Codex wrapper findings in a69b01f918:

  • compare callback ABIs against the base type before optional promotion
  • preserve ordinary alias-normalized optional compatibility
  • stop literal ABI descent at non-callback casts while retaining checks for callback-shaped casts
  • added matching/rejected optional promotion, empty-interface boxing, and callback-cast regressions

Tests (each with isolated PR-specific TMPDIR/VTMP): focused wrapper group; vlib/v3/transform/fn_test.v; vlib/v/compiler_errors_test.v (1,619 passed, 1 skipped).

@medvednikov

Copy link
Copy Markdown
Member

@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: a69b01f918

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/fn.v Outdated
for clean.starts_with('?') || clean.starts_with('!') {
clean = clean[1..].trim_space()
}
if t.is_fn_pointer_type_name(t.normalize_type_alias(clean)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate callback containers inside alias casts

When a generic specialization passes a cast such as Handlers([fn (const_event &C.native_event) {}]), where type Handlers = []PlainHandler, normalizing the cast target produces an array type, so this direct-function-only predicate returns false. The collector then stops at the cast, the container validator never sees the array literal, and the equal outer alias types allow the const-qualified callback into a plain function-pointer array. Unwrap casts whose targets contain callbacks and validate their operands against the corresponding container element types.

AGENTS.md reference: AGENTS.md:L320-L322

Useful? React with 👍 / 👎.

@medvednikov

Copy link
Copy Markdown
Member

Fixed the callback-container cast finding in d6c8820e9a:

  • detect callbacks through recursively expanded alias targets
  • unwrap callback-bearing casts into the existing container element validator
  • retain opaque behavior for non-callback casts such as Any(fn {...})
  • added matching/rejected callback array-alias cast regressions

Tests (isolated PR-specific TMPDIR/VTMP): focused six-case wrapper/cast group; vlib/v3/transform/fn_test.v; vlib/v/compiler_errors_test.v (1,619 passed, 1 skipped).

@medvednikov

Copy link
Copy Markdown
Member

@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: d6c8820e9a

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/fn.v
Comment on lines +12551 to +12553
if node.kind == .postfix && node.children_count == 1 {
return t.fn_literal_container_modes_compatible(t.a.child(&node, 0), expected_type)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Unwrap parentheses before validating callback containers

When a specialized generic receiver call passes a parenthesized container such as r.apply(([fn (const_event &C.native_event) {}])) to []PlainHandler, the argument remains a .paren node here, so this helper returns none. The source collector unwraps the parentheses but then stops at the array literal, leaving no ABI signature to compare; the erased semantic array type is therefore accepted despite containing an incompatible C function pointer. Unwrap .paren/.expr_stmt before dispatching to the container branches.

AGENTS.md reference: AGENTS.md:L320-L322

Useful? React with 👍 / 👎.

Comment thread vlib/v3/transform/fn.v
Comment on lines +12562 to +12566
element_expected := if source_expected.starts_with('[]') {
source_expected[2..]
} else if t.is_fixed_array_type(source_expected) {
fixed_array_elem_type(source_expected)
} else if normalized_expected.starts_with('[]') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Strip option wrappers before validating callback arrays

When a specialized generic receiver method expects ?[]PlainHandler and receives a bare array literal containing fn (const_event &C.native_event) {}, both source_expected and normalized_expected still start with ?, so none of these array element-type branches match and the helper returns none. The outer optional-promotion check later accepts the erased []fn(&C.native_event) type as the option's base, allowing the incompatible callback through; unwrap ? (and analogous promotable wrappers) before deriving the container element type.

AGENTS.md reference: AGENTS.md:L320-L322

Useful? React with 👍 / 👎.

@medvednikov

Copy link
Copy Markdown
Member

Fixed both exact-head callback-container findings in 6ca1c64e67:

  • unwrap parenthesized/expression-statement containers before recursive callback-mode validation
  • remove promotable option/result wrappers before deriving array element expectations
  • add matching and rejecting regressions for parenthesized and option-promoted callback arrays

Validation passed: focused V3 type-checker regression file; vlib/v3/transform/fn_test.v; vlib/v3/transform/monomorphize_test.v; compiler errors (1619 passed, 1 skipped); vfmt and git diff --check.

@medvednikov

Copy link
Copy Markdown
Member

@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: 6ca1c64e67

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/fn.v Outdated
map_expected) {
return false
}
i++

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Advance past both map-spread child slots

For a map-update literal such as consume[int]({...base}), the flat map AST reserves a key/value pair for the spread, as confirmed by lower_map_init_to_runtime setting start_i = 2; advancing only once leaves the placeholder value to be processed as the next key, so the following incomplete-pair check returns false and rejects an otherwise valid generic call. Fresh evidence beyond the earlier spread report is that the new special case still disagrees with every existing map walker, which advances by two for a spread entry.

Useful? React with 👍 / 👎.

@medvednikov

Copy link
Copy Markdown
Member

Fixed the exact-head map-spread finding in 04c0522760: callback-container map validation now advances over both flat-AST key/value slots reserved for a spread entry. Added a callback-bearing map-spread checker regression.

Validation passed: exact V3 check fixture; vlib/v3/transform/fn_test.v; vlib/v3/transform/monomorphize_test.v; compiler errors (1619 passed, 1 skipped); vfmt and git diff --check.

@medvednikov

Copy link
Copy Markdown
Member

@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: 04c0522760

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/fn.v Outdated
Comment on lines +12682 to +12683
abi_expected_type := if expected_type.starts_with('?') {
expected_type[1..]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Unwrap result callback parameters before ABI comparison

When a specialized generic call passes a bare callback literal to a result-wrapped parameter such as handler !ConstHandler, normal callback-to-!fn promotion should accept it (and is already exercised by vlib/v3/tests/fn_value_decl_type_test.v), but this strips only ?. The literal therefore yields fn_ptr:... while the expected signature remains !(fn_ptr:...), causing rejection before the result-promotion branch in resolved_receiver_arg_compatible can run. Unwrap ! here just as the container validator does.

AGENTS.md reference: AGENTS.md:L320-L322

Useful? React with 👍 / 👎.

Comment thread vlib/v3/transform/fn.v
for normalized_expected.starts_with('?') || normalized_expected.starts_with('!') {
normalized_expected = normalized_expected[1..].trim_space()
}
if node.kind in [.array_literal, .array_init] {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Traverse conditional expressions that return callback containers

When a specialized generic call receives if flag { [fn (const_event &C.native_event) {}] } else { []PlainHandler{} } for a []PlainHandler parameter, the outer node is .if_expr, so this literal-only container dispatch returns none. The source collector traverses the branches but stops at each array literal, leaving no ABI signature to compare; the erased semantic array type is then accepted despite containing an incompatible function pointer. Recurse through callback-valued if/match branches before dispatching on the aggregate literal.

AGENTS.md reference: AGENTS.md:L320-L322

Useful? React with 👍 / 👎.

@medvednikov

Copy link
Copy Markdown
Member

Fixed both exact-head wrapped-callback findings in 02bcbd39a6:

  • unwrap both option and result promotion before callback ABI comparison, including alias-normalized result payload compatibility
  • recursively validate callback containers returned through if, match, or, block, match-branch, and lock expression wrappers
  • add matching and rejecting regressions for result-promoted callbacks and conditional callback arrays

Validation passed: four exact V3 good/bad fixtures; vlib/v3/transform/fn_test.v; vlib/v3/transform/monomorphize_test.v; compiler errors (1619 passed, 1 skipped); vfmt and git diff --check.

@medvednikov

Copy link
Copy Markdown
Member

@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: 02bcbd39a6

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/fn.v
Comment on lines +12836 to +12837
if node.kind != .fn_literal {
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Track callback ABI metadata through identifiers

In a specialized generic receiver call, passing a named callback rather than an inline literal bypasses the new ABI validation because this collector immediately returns for .ident nodes. For example, a local variable or named function initialized/declared as fn (const_event &C.native_event) can be passed to a receiver parameter expecting plain fn (event &C.native_event); the resolved FnType has erased const_, so the later type comparison accepts it and emits an incompatible non-const function pointer. Preserve the source signature on callback-valued identifiers or trace them to their declaration before returning.

AGENTS.md reference: AGENTS.md:L320-L322

Useful? React with 👍 / 👎.

@medvednikov

Copy link
Copy Markdown
Member

@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: e739bf11e9

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v3/transform/fn.v
Comment on lines +12854 to +12857
else {}
}
if node.kind != .fn_literal {
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve source modes for method-value selectors

When a specialized generic call passes a method value such as Handler{}.handle, where handle takes const_event &C.native_event, the argument is a .selector rather than an .ident. This collector therefore falls through and returns without recording its source signature, even though the checker already recognizes selector method values; the erased semantic function type can then match a plain callback parameter and emit an incompatible C function pointer. Handle method-value selectors by resolving their declaration and retaining its source parameter modes.

AGENTS.md reference: AGENTS.md:L320-L322

Useful? React with 👍 / 👎.

Comment thread vlib/v3/transform/fn.v
Comment on lines +12596 to +12598
} else if normalized_expected.starts_with('[]') {
array_expected = normalized_expected
normalized_expected[2..]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Retain callback modes when expanding container aliases

When the expected parameter is an alias such as type ConstHandlers = []fn (const_event &C.native_event), source_expected is not syntactically an array, so this branch derives its element from normalized_expected. That normalization uses the semantic FnType, which has discarded the const_ parameter name, causing a matching const-qualified literal to be compared against a plain callback and rejected (while a plain literal can be accepted). Expand the alias using its source ABI metadata before recursively validating the elements.

AGENTS.md reference: AGENTS.md:L320-L322

Useful? React with 👍 / 👎.

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.

v3: false positive: "cannot use ... expected <fn-type alias>" for inline fn literal in a generic function

3 participants