Skip to content

Commit 4ca3645

Browse files
committed
filter lifetime params from generic struct bindings
1 parent e7eb673 commit 4ca3645

4 files changed

Lines changed: 114 additions & 13 deletions

File tree

vlib/v2/gen/cleanc/cleanc_test.v

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
module cleanc
33

44
import v2.ast
5+
import v2.types
56

67
fn test_c_string_literal_content_to_c_single_line() {
78
out := c_string_literal_content_to_c('hello')
@@ -57,6 +58,61 @@ fn test_struct_generic_params_need_bindings_returns_true_for_runtime_generic_par
5758
assert struct_generic_params_need_bindings(params)
5859
}
5960

61+
fn test_runtime_generic_params_filter_lifetime_params() {
62+
params := [
63+
ast.Expr(ast.LifetimeExpr{
64+
name: 'a'
65+
}),
66+
ast.Expr(ast.Ident{
67+
name: 'T'
68+
}),
69+
]
70+
args := [
71+
ast.Expr(ast.LifetimeExpr{
72+
name: 'a'
73+
}),
74+
ast.Expr(ast.Ident{
75+
name: 'Value'
76+
}),
77+
]
78+
filtered_args := runtime_generic_args(args)
79+
assert runtime_generic_param_names(['^a', 'T']) == ['T']
80+
assert generic_param_names(params) == ['T']
81+
assert filtered_args.len == 1
82+
assert filtered_args[0] is ast.Ident
83+
assert (filtered_args[0] as ast.Ident).name == 'Value'
84+
}
85+
86+
fn test_record_generic_struct_bindings_filters_lifetime_params() {
87+
mut env := types.Environment.new()
88+
mut scope := types.new_scope(unsafe { nil })
89+
scope.insert('Ref', types.Object(types.Type(types.Struct{
90+
name: 'Ref'
91+
generic_params: ['^a', 'T']
92+
})))
93+
scope.insert('Value', types.Object(types.Type(types.Struct{
94+
name: 'Value'
95+
})))
96+
lock env.scopes {
97+
env.scopes['main'] = scope
98+
}
99+
mut g := Gen.new_with_env([], env)
100+
g.record_generic_struct_bindings('Ref', 'Ref', [
101+
ast.Expr(ast.LifetimeExpr{
102+
name: 'a'
103+
}),
104+
ast.Expr(ast.Ident{
105+
name: 'Value'
106+
}),
107+
])
108+
bindings := (g.generic_struct_bindings['Ref'] or { panic('missing Ref binding') }).clone()
109+
value_type := bindings['T'] or { panic('missing T binding') }
110+
assert value_type.name() == 'Value'
111+
instances := g.generic_struct_instances['Ref']
112+
assert instances.len == 1
113+
assert instances[0].params_key == 'Value'
114+
}
115+
60116
fn test_fixed_array_elem_type_ready_accepts_primitive_alias() {
61117
mut g := Gen.new([])
62118
g.primitive_type_aliases['sha3__Lane'] = true

vlib/v2/gen/cleanc/flag_enum_codegen_test.v

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,22 @@ fn test_generate_c_uses_concrete_map_method_name_in_generic_comptime_body() {
9292
assert csrc.contains('Map_string_int__query_item(')
9393
assert !csrc.contains('map__query_item(')
9494
}
95+
96+
fn test_generate_c_filters_lifetime_params_from_generic_struct_binding() {
97+
csrc := generate_c_for_test('
98+
struct Value {
99+
n int
100+
}
101+
102+
struct Ref[^a, T] {
103+
value T
104+
}
105+
106+
struct Holder[^a] {
107+
item Ref[^a, Value]
108+
}
109+
')
110+
assert csrc.contains('struct Ref {')
111+
assert csrc.contains('Value value;')
112+
assert !csrc.contains('T value;')
113+
}

vlib/v2/gen/cleanc/struct.v

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,28 @@ fn struct_generic_params_need_bindings(params []ast.Expr) bool {
1616
return false
1717
}
1818

19+
fn runtime_generic_param_names(param_names []string) []string {
20+
mut names := []string{cap: param_names.len}
21+
for name in param_names {
22+
if name.starts_with('^') {
23+
continue
24+
}
25+
names << name
26+
}
27+
return names
28+
}
29+
30+
fn runtime_generic_args(params []ast.Expr) []ast.Expr {
31+
mut args := []ast.Expr{cap: params.len}
32+
for param in params {
33+
if param is ast.LifetimeExpr {
34+
continue
35+
}
36+
args << param
37+
}
38+
return args
39+
}
40+
1941
// collect_generic_struct_bindings scans all struct fields for GenericType
2042
// instantiations (e.g. LinkedList[ValueInfo]) and records the concrete type
2143
// bindings so that methods on generic structs can resolve their generic params.

vlib/v2/gen/cleanc/types.v

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2706,15 +2706,16 @@ fn (g &Gen) is_c_type_name(name string) bool {
27062706
fn (mut g Gen) record_generic_struct_bindings(struct_base_name string, struct_c_name string, concrete_params []ast.Expr) {
27072707
// Find the struct decl to get generic param names.
27082708
env_struct := g.lookup_struct_type(struct_base_name)
2709-
generic_param_names := env_struct.generic_params
2710-
if generic_param_names.len == 0 || generic_param_names.len != concrete_params.len {
2709+
generic_param_names := runtime_generic_param_names(env_struct.generic_params)
2710+
concrete_runtime_params := runtime_generic_args(concrete_params)
2711+
if generic_param_names.len == 0 || generic_param_names.len != concrete_runtime_params.len {
27112712
return
27122713
}
27132714
// Check that all concrete params are non-placeholder types.
27142715
mut bindings := map[string]types.Type{}
2715-
mut param_c_names := []string{cap: concrete_params.len}
2716+
mut param_c_names := []string{cap: concrete_runtime_params.len}
27162717
for i, param_name in generic_param_names {
2717-
concrete_expr := concrete_params[i]
2718+
concrete_expr := concrete_runtime_params[i]
27182719
if is_generic_placeholder_type_name(concrete_expr.name()) {
27192720
return
27202721
}
@@ -2762,14 +2763,15 @@ fn (mut g Gen) record_generic_struct_bindings(struct_base_name string, struct_c_
27622763
// by resolving placeholder params through the parent struct's known bindings.
27632764
fn (mut g Gen) record_generic_struct_bindings_with_parent(struct_base_name string, struct_c_name string, concrete_params []ast.Expr, parent_bindings map[string]types.Type) {
27642765
env_struct := g.lookup_struct_type(struct_base_name)
2765-
generic_param_names := env_struct.generic_params
2766-
if generic_param_names.len == 0 || generic_param_names.len != concrete_params.len {
2766+
generic_param_names := runtime_generic_param_names(env_struct.generic_params)
2767+
concrete_runtime_params := runtime_generic_args(concrete_params)
2768+
if generic_param_names.len == 0 || generic_param_names.len != concrete_runtime_params.len {
27672769
return
27682770
}
27692771
mut bindings := map[string]types.Type{}
2770-
mut param_c_names := []string{cap: concrete_params.len}
2772+
mut param_c_names := []string{cap: concrete_runtime_params.len}
27712773
for i, param_name in generic_param_names {
2772-
concrete_expr := concrete_params[i]
2774+
concrete_expr := concrete_runtime_params[i]
27732775
expr_name := concrete_expr.name()
27742776
if is_generic_placeholder_type_name(expr_name) {
27752777
if parent_type := parent_bindings[expr_name] {
@@ -2825,8 +2827,9 @@ fn (mut g Gen) resolve_generic_struct_c_name(base_name string, concrete_params [
28252827
if instances.len <= 1 {
28262828
return base_name
28272829
}
2828-
mut param_c_names := []string{cap: concrete_params.len}
2829-
for p in concrete_params {
2830+
concrete_runtime_params := runtime_generic_args(concrete_params)
2831+
mut param_c_names := []string{cap: concrete_runtime_params.len}
2832+
for p in concrete_runtime_params {
28302833
param_c_names << g.expr_type_to_c(p)
28312834
}
28322835
params_key := param_c_names.join('_')
@@ -3928,9 +3931,10 @@ fn (mut g Gen) resolve_generic_struct_field_name(expr ast.Expr) string {
39283931
// Build the params_key from active_generic_types
39293932
// Find the struct's generic params to know the order
39303933
env_struct := g.lookup_struct_type(c_name.all_after_last('__'))
3931-
if env_struct.generic_params.len > 0 {
3932-
mut param_c_names := []string{cap: env_struct.generic_params.len}
3933-
for param_name in env_struct.generic_params {
3934+
generic_param_names := runtime_generic_param_names(env_struct.generic_params)
3935+
if generic_param_names.len > 0 {
3936+
mut param_c_names := []string{cap: generic_param_names.len}
3937+
for param_name in generic_param_names {
39343938
if concrete := g.active_generic_types[param_name] {
39353939
param_c_names << g.types_type_to_c(concrete)
39363940
} else {

0 commit comments

Comments
 (0)