Skip to content

v: Structured generic receiver patterns#27079

Open
GGRei wants to merge 6 commits into
vlang:masterfrom
GGRei:structured-generic-receiver-patterns
Open

v: Structured generic receiver patterns#27079
GGRei wants to merge 6 commits into
vlang:masterfrom
GGRei:structured-generic-receiver-patterns

Conversation

@GGRei
Copy link
Copy Markdown
Contributor

@GGRei GGRei commented May 3, 2026

Summary

This PR implements structured generic receiver patterns for generic methods.

The original request was about allowing methods like Expect[[]T], so a method can apply only when the wrapped generic type is an array. This PR generalizes that idea slightly: method receivers can now use structured type patterns such as arrays and maps, and the checker can infer the inner generic parameters from the concrete receiver type.

Dependency Note

This PR depends on:

The branch is intentionally stacked on top of those fixes. It should not be merged as a standalone change before those PRs are in main, because this feature builds on the generic/cgen correctness work from that stack.

After those prerequisite fixes are merged, I think this PR would be worth reviewing fairly early before more generic-receiver work diverges around it. It touches deep parser/type-table/checker/method-resolution behavior, so having this baseline reviewed and settled should make later work easier and safer.

What This Enables

This allows generic methods to bind type parameters from structured receiver types.

For example:

struct Expect[T] {
    value T
}

fn (e Expect[[]T]) first_or(value T) T {
    if e.value.len == 0 {
        return value
    }
    return e.value[0]
}

With this, Expect[[]int] binds T = int, while Expect[[]string] binds T = string.

Nested array elements are also handled structurally:

Expect[[][]int] // T = []int
Expect[[]map[string]int] // T = map[string]int

Maps are supported too:

fn (e Expect[map[K]V]) get_or(key K, value V) V {
    if key in e.value {
        return e.value[key]
    }
    return value
}

So:

Expect[map[string]int] // K = string, V = int
Expect[map[int]string] // K = int, V = string
Expect[map[string]map[string]int] // K = string, V = map[string]int

Behavior Rules

This PR keeps the feature deliberately scoped:

  • Expect[[]T] matches dynamic arrays.
  • Expect[map[K]V] matches maps.
  • Nested patterns are matched recursively.
  • Existing generic receivers like Expect[T] continue to work.
  • Exact concrete receiver methods keep precedence over structured receiver patterns.
  • Repeated placeholders must bind consistently, so invalid cases such as map[K]K with map[string]int are rejected.
  • Raw voidptr is not allowed to bind into these receiver patterns; users should cast to an explicit V type first.

Explicit Non-Goals

This PR does not implement the original []T{} syntax in type position. That remains invalid syntax.

It also does not implement fixed-size array receiver patterns such as:

fn (e Expect[[N]T]) contains_fixed(value T)

That needs a separate design decision because N is a compile-time integer binding, not a normal type binding.

Implementation Notes

The patch adds structured receiver pattern discovery and matching in the parser/type-table/checker path, then uses that information during method lookup and generic binding.

The goal is to keep the feature focused on method receiver type matching, without turning it into a broader generic overloading redesign.

Tests

Added coverage for:

  • existing generic receiver behavior;
  • concrete receiver method precedence;
  • dynamic array receiver patterns;
  • nested dynamic array receiver patterns;
  • map receiver patterns;
  • nested map value patterns;
  • repeated placeholder mismatch diagnostics;
  • voidptr rejection diagnostics;
  • invalid []T{} receiver syntax remaining invalid.

Validation run locally:

  • vlib/v/tests/generics
  • targeted compiler_errors filters for generic/method/structured receiver cases
  • vlib/v/parser
  • vlib/v/checker
  • vlib/v/gen/c
  • fmt -verify
  • git diff --check

Fix #22979

Copy link
Copy Markdown

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

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: 4817e91eb5

ℹ️ 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/ast/types.v Outdated
for method in methods {
receiver_generic_names :=
table.structured_receiver_generic_pattern_names(method.receiver_type)
if receiver_generic_names.len == 0 || receiver_generic_names.len != method.generic_names.len {
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 Match structured receivers even with extra method generics

Structured receiver methods are skipped whenever the number of receiver-bound placeholders differs from method.generic_names.len, but parser logic appends method-level generics to method.generic_names (for example fn (b Box[[]T]) map[U](...)). In that valid case, receiver_generic_names.len is 1 while method.generic_names.len is 2, so this branch always continues and the method is never discovered, producing an unknown method error for calls that should compile.

Useful? React with 👍 / 👎.

@GGRei GGRei marked this pull request as draft May 3, 2026 14:40
@GGRei GGRei marked this pull request as ready for review May 3, 2026 16:29
Copy link
Copy Markdown

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

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: 8ca5074b5f

ℹ️ 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/ast/types.v
Comment on lines +2044 to +2050
match sym.info {
Struct {
if sym.info.concrete_types.len > 0 {
parent_idx := if sym.info.parent_type != 0 {
sym.info.parent_type.clear_flag(.generic).idx()
} else if sym.parent_idx != 0 {
sym.parent_idx
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 Handle aliases in structured receiver matching

Structured receiver lookup now relies on receiver_generic_types, but this helper only matches Struct, Interface, SumType, and GenericInst. For alias receivers (e.g. type IntListExpect = Expect[[]int]), it returns none, so find_structured_receiver_method_with_types never sees the concrete generic arguments and calls like IntListExpect{...}.first_or(...) fall through to unknown method even though aliases normally inherit parent methods. This makes the new structured-receiver feature unavailable through type aliases.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This has been addressed in the latest patch. Direct aliases such as type IntListExpect = Expect[[]int] are now handled by structured receiver lookup, and regression tests were added for array aliases, map aliases, mixed receiver patterns, and exact-method precedence through aliases.

@GGRei GGRei marked this pull request as draft May 3, 2026 16:38
@GGRei GGRei marked this pull request as ready for review May 3, 2026 17:07
Copy link
Copy Markdown

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

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: 6d601c89dc

ℹ️ 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/ast/types.v
Comment on lines +2146 to +2147
if sym.info is Alias {
return t.receiver_generic_types(sym.info.parent_type.derive(typ))
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 alias chains for structured receiver lookup

actual_receiver_generic_types only unwraps a single alias layer (Alias -> parent_type) before giving up, so structured receiver methods stop resolving for alias-of-alias types (for example type B = A where A = Expect[[]int]). In that case method calls on B can incorrectly fail with unknown method even though aliases should inherit the same receiver methods as the underlying type. This is introduced by the new alias fallback path and can be fixed by recursively unwrapping aliases (or looping until a non-alias symbol) before matching receiver generics.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think this one is not currently reachable: V rejects alias-to-alias declarations before method lookup (type B = A where A is already an alias). I tried adding a regression test for that shape, but it fails earlier with the existing alias rule, so I kept the patch limited to direct aliases only.

@GGRei GGRei marked this pull request as draft May 3, 2026 17:23
@GGRei GGRei marked this pull request as ready for review May 3, 2026 19:49
@GGRei
Copy link
Copy Markdown
Contributor Author

GGRei commented May 3, 2026

The branch should now be ready for final review. ( gitbot is happy! )

The first P2 was fixed by supporting structured receiver methods with extra method-level generics.

For the second P2, direct aliases are now handled and covered by regression tests, including the Cgen exact-method precedence cases.
Alias-to-alias chains are different: V currently rejects declarations like type B = A when A is already an alias before method lookup is reached, so that part seems outside the scope of this PR unless the language rule itself is changed first.

@GGRei GGRei force-pushed the structured-generic-receiver-patterns branch from 2d90972 to 826aae7 Compare May 3, 2026 20:04
@medvednikov
Copy link
Copy Markdown
Member

V currently rejects declarations like type B = A when A is already an alias

Multi level aliases will never be allowed

@GGRei
Copy link
Copy Markdown
Contributor Author

GGRei commented May 3, 2026

V currently rejects declarations like type B = A when A is already an alias

Multi level aliases will never be allowed

@medvednikov : Understood, thanks for confirming. That is consistent with the current PR scope: I did not try to support multi-level aliases. I was only clarifying the P2 raised by the GitHub review bot, since it mentioned alias chains. The added handling is only for direct aliases that V already accepts today, such as type IntListExpect = Expect[[]int].

@iperov
Copy link
Copy Markdown

iperov commented May 4, 2026

What is the purpose of this?
An attempt to shorten the code at the expense of explicitness, requiring the developer to study Expect inner workings ?

@GGRei
Copy link
Copy Markdown
Contributor Author

GGRei commented May 5, 2026

The purpose is not to shorten code at the expense of explicitness.

It allows a generic wrapper to expose methods only when its inner type has a specific structure. The constraint is written directly in the receiver type, so it is explicit and checked by the compiler.

So the developer does not have to guess the inner workings of Expect; the accepted shape is part of the method signature itself.

This does not change existing V behavior and it does not force anyone to use generics. If a project prefers only fully concrete receiver methods, it can simply keep doing that.

@iperov
Copy link
Copy Markdown

iperov commented May 5, 2026

it does not force anyone to use generics

It's the same with C++: you aren't forced to use that 'multi-layered academic masturbation' with templates. 👎

The language should be simplified and redundant features removed, but you guys are doing the opposite by adding even more.

@GGRei
Copy link
Copy Markdown
Contributor Author

GGRei commented May 5, 2026

I think this is now more a general language philosophy discussion than a review of this specific PR.

For me, this is not the right place for that discussion; there are dedicated channels for language design debates.

@iperov
Copy link
Copy Markdown

iperov commented May 5, 2026

No, this is precisely the place to discuss what you are doing to the language, similar to how C++ began to bloat and turned into a dead language.

And if this pull request is accepted, I don't see the point of staying with this language.

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.

Generic array

3 participants