Skip to content

transform: keep main types in imported generics reached via comptime fields - #29184

Merged
medvednikov merged 4 commits into
vlang:masterfrom
tomaszbk:fix-comptime-field-main-type
Oct 1, 2026
Merged

medvednikov merged 4 commits into
vlang:masterfrom
tomaszbk:fix-comptime-field-main-type

Conversation

@tomaszbk

Copy link
Copy Markdown
Contributor

Fixes generic arguments inferred from comptime field selectors (value.$(field.name)) inside an imported module that declares a type with the same short name as the caller's main type.

Reproduction

// module lib
module lib

struct Cell { column int } // private, unrelated

pub fn fields_of[T](_ T) []string {
	mut names := []string{}
	$for field in T.fields {
		$if field.is_pub { names << field.name }
	}
	return names
}

pub fn element_fields[E](_ []E) []string {
	$if E is $struct { return fields_of[E](E{}) } $else { return []string{} }
}

pub fn model_fields[T](value T) []string {
	mut out := []string{}
	$for field in T.fields {
		$if field.typ is $array { out << element_fields(value.$(field.name)) }
	}
	return out
}
// main.v
import lib

pub struct Cell { pub: id int }
pub struct Demo { pub: cells []Cell }

fn main() { println(lib.model_fields(Demo{})) }

Expected ['id'], got []: inside element_fields, E was lib.Cell. Calling lib.element_fields([]Cell{}) directly from main worked. With one more generic level (the pattern used by ui2's VML schema/value conversion), the build fails instead with cannot use []Cell as []lib.Cell, or a C type mismatch (main__Cell vs lib__Cell). Found in vlang/ui2 (see vlang/ui2#48), where a private ui2.GridCell broke examples/grid.

Cause

The transformer passes types as text and main types are spelled bare. Two paths re-parsed that text in the callee module, where Cell resolves to the module's own type:

  • lookup_struct_field_type_uncached (type_propagation.v): the field type of a main struct ([]Cell).
  • call_param_type_names (fn.v): parameter types of an already specialized call, rendered with .name().

Fix

Both paths now lock colliding main types to main.X with the existing lock_colliding_main_generic_type_text when the current module is not main/builtin (and, for fields, when the owner struct is a main struct). canonical_generic_specialization_arg still strips main., so specialization names are unchanged. Both parts are needed; either alone still fails.

Tests

New vlib/v/tests/comptime_field_generic_arg_main_type/: array and struct fields through value.$(field.name), nested specializations (E{} / $zero(E), iteration), and matching direct calls. It fails before the fix and passes after.

Run on macOS arm64 with V_MACOS_V3_NO_FALLBACK=1 (so failures are not retried with the fallback compiler):

  • v test vlib/v/tests/comptime vlib/v/tests/generics vlib/v/tests/generic_calls vlib/v/tests/module_type_named_like_main_type vlib/v/tests/comptime_field_generic_arg_main_type vlib/v/transform: 544/544 passed.
  • v test vlib/v/tests/*_test.v vlib/v/tests/structs vlib/v/tests/same_name_types_str_methods vlib/v/tests/selective_import_generic_global_28881 vlib/v/tests/generic_fn_ptr_arg_28884 vlib/v/tests/generic_type_alias_method_27465 vlib/v/tests/serialisation: 467 passed, 12 failed, 11 skipped. The same 12 fail without this change (orm_*_test.v, sql_statement_inside_fn_call_test.v, string_escape_opaque_backslash_test.v, runtime exit 11).
  • vlang/ui2 v test . with the original GridCell name: no new failures; examples/grid/grid_test.v passes.
  • I did not run the full v test-all.

🤖 Generated with Claude Code

…fields

A main struct field read with `value.$(field.name)` inside an imported
generic body was resolved by its bare name in the callee module. When
that module had its own type with the same name (e.g. a private `Cell`),
the generic argument rebound to it: comptime field lists came back empty,
or the build failed with `cannot use []Cell as []mod.Cell`.

Lock colliding main types to `main.X` when struct field types and
specialized call parameter types are spelled for a non-main module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…generics

A main alias like `type Cells = []Cell` read from a comptime field inside an
imported generic was still spelled bare, so its target was resolved in the
callee module and rebound to that module's own `Cell`. Lock such aliases to
`main.` when their target or name would collide.

Also trim the test fixture to what the cases need and cover map, option,
fixed array and alias fields.
@medvednikov
medvednikov merged commit efae23e into vlang:master Oct 1, 2026
MARCROCK22 added a commit to MARCROCK22/v that referenced this pull request Oct 1, 2026
vlang#29184 (main types kept in imported generics reached through compile-time
fields) merges without conflicts with the monomorphization of the
experiment.
@tomaszbk
tomaszbk deleted the fix-comptime-field-main-type branch October 1, 2026 01:02
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.

2 participants