From ebfa7d86cf9fbd172bf4f9652ef410b43b7b06d2 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Mon, 17 Mar 2025 11:52:59 -0300 Subject: [PATCH] checker: fix typeof evaluation for generic reference (fix #23951) (fix #23952) (#23958) --- vlib/v/checker/assign.v | 2 ++ vlib/v/checker/checker.v | 4 +-- vlib/v/gen/c/assign.v | 2 +- vlib/v/gen/c/cgen.v | 2 +- vlib/v/gen/c/comptime.v | 5 +++ vlib/v/tests/generics/generic_typeof_test.v | 37 +++++++++++++++++++++ vlib/v/tests/options/option_var_test.v | 4 +-- 7 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 vlib/v/tests/generics/generic_typeof_test.v diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index f6e27d1956..6a943a6291 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -965,6 +965,8 @@ fn (mut c Checker) change_flags_if_comptime_expr(mut left ast.Ident, right ast.E if right_ct_var != .no_comptime { left.obj.ct_type_var = right_ct_var } + } else if right is ast.StructInit && right.unresolved && right.typ.has_flag(.generic) { + left.obj.ct_type_var = .generic_param } else if right is ast.IndexExpr && c.comptime.is_comptime(right) { right_ct_var := c.comptime.get_ct_type_var(right.left) if right_ct_var != .no_comptime { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index e63e31c8f7..bf992c0e31 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -899,9 +899,7 @@ fn (mut c Checker) fail_if_immutable(mut expr ast.Expr) (string, token.Pos) { mut expr_left := expr.left if mut expr.left is ast.Ident { if mut expr.left.obj is ast.Var { - if expr.left.obj.ct_type_var != .generic_param { - c.fail_if_immutable(mut expr_left) - } + c.fail_if_immutable(mut expr_left) } } return '', expr.pos diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index d455ea24d7..f53b5ed93b 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -163,7 +163,7 @@ fn (mut g Gen) expr_with_opt(expr ast.Expr, expr_typ ast.Type, ret_typ ast.Type) } g.expr(expr) if expr is ast.ComptimeSelector { - return '${expr.left.str()}.${g.comptime.comptime_for_field_value.name}' + return g.gen_comptime_selector(expr) } else { return expr.str() } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 78c619d48a..11956b93a8 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -3728,7 +3728,7 @@ fn (mut g Gen) expr(node_ ast.Expr) { mut is_unwrapped := true if mut node.expr is ast.ComptimeSelector && node.expr.left is ast.Ident { // val.$(field.name)? - expr_str = '${node.expr.left.str()}.${g.comptime.comptime_for_field_value.name}' + expr_str = g.gen_comptime_selector(node.expr) } else if mut node.expr is ast.Ident && node.expr.ct_expr { // val? expr_str = node.expr.name diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index 524422ee4c..040b560e87 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -39,6 +39,11 @@ fn (mut g Gen) comptime_selector(node ast.ComptimeSelector) { } } +fn (mut g Gen) gen_comptime_selector(expr ast.ComptimeSelector) string { + arrow_or_dot := if expr.left_type.is_ptr() { '->' } else { '.' } + return '${expr.left.str()}${arrow_or_dot}${g.comptime.comptime_for_field_value.name}' +} + fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) { if node.is_embed { // $embed_file('/path/to/file') diff --git a/vlib/v/tests/generics/generic_typeof_test.v b/vlib/v/tests/generics/generic_typeof_test.v new file mode 100644 index 0000000000..0b0b8c4487 --- /dev/null +++ b/vlib/v/tests/generics/generic_typeof_test.v @@ -0,0 +1,37 @@ +module main + +fn decode_array[T](x []T) string { + return 'decode_array: x.typename = ${typeof(x).name}' +} + +fn decode[T]() string { + x := T{0} + mut s := 'decode: x.typename = ${typeof(x).name}. ' + s += decode_array(x) + return s +} + +fn second[K, V](x map[K]V) string { + return 'second: x.typename = ${typeof(x).name}' +} + +fn first[T]() string { + x := T{} + mut s := 'first: x.typename = ${typeof(x).name}. ' + s += second(x) + return s +} + +fn test_map() { + assert first[map[string]string]() == 'first: x.typename = map[string]string. second: x.typename = map[string]string' + assert first[map[string]int]() == 'first: x.typename = map[string]int. second: x.typename = map[string]int' + assert first[map[string]u8]() == 'first: x.typename = map[string]u8. second: x.typename = map[string]u8' + assert first[map[string]rune]() == 'first: x.typename = map[string]rune. second: x.typename = map[string]rune' +} + +fn test_array() { + assert decode[[]u8]() == 'decode: x.typename = []u8. decode_array: x.typename = []u8' + assert decode[[]u16]() == 'decode: x.typename = []u16. decode_array: x.typename = []u16' + assert decode[[]int]() == 'decode: x.typename = []int. decode_array: x.typename = []int' + assert decode[[]rune]() == 'decode: x.typename = []rune. decode_array: x.typename = []rune' +} diff --git a/vlib/v/tests/options/option_var_test.v b/vlib/v/tests/options/option_var_test.v index d62f467dd8..0dac4e85c0 100644 --- a/vlib/v/tests/options/option_var_test.v +++ b/vlib/v/tests/options/option_var_test.v @@ -22,7 +22,7 @@ mut: struct Decoder {} -fn (d &Decoder) decode[T](typ T) T { +fn (d &Decoder) decode[T](mut typ T) T { $for field in T.fields { $if field.is_option { if typ.$(field.name) != none { @@ -36,7 +36,7 @@ fn (d &Decoder) decode[T](typ T) T { fn test_comptime() { d := Decoder{} - result := d.decode(StructType{ + result := d.decode(mut StructType{ a: 'foo' b: 3 })