From 80cd9f820a60a4ab3f9316d299ae60a5b914566c Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Sat, 14 Jan 2023 10:04:13 -0300 Subject: [PATCH] checker,cgen: allow ?.str() on compile-time fields var (#16969) --- vlib/v/checker/checker.v | 13 ++++++++ vlib/v/checker/fn.v | 2 +- vlib/v/gen/c/fn.v | 33 ++++++++++---------- vlib/v/tests/call_to_str_on_option_test.v | 38 +++++++++++++++++++++++ 4 files changed, 69 insertions(+), 17 deletions(-) create mode 100644 vlib/v/tests/call_to_str_on_option_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index be7061e022..79409c039a 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3503,6 +3503,11 @@ fn (c &Checker) has_return(stmts []ast.Stmt) ?bool { return none } +pub fn (mut c Checker) is_comptime_var(node ast.Expr) bool { + return c.inside_comptime_for_field && node is ast.Ident + && (node as ast.Ident).info is ast.IdentVar && ((node as ast.Ident).obj as ast.Var).is_comptime_field +} + fn (mut c Checker) postfix_expr(mut node ast.PostfixExpr) ast.Type { typ := c.unwrap_generic(c.expr(node.expr)) typ_sym := c.table.sym(typ) @@ -3511,6 +3516,14 @@ fn (mut c Checker) postfix_expr(mut node ast.PostfixExpr) ast.Type { c.warn('pointer arithmetic is only allowed in `unsafe` blocks', node.pos) } if !(typ_sym.is_number() || ((c.inside_unsafe || c.pref.translated) && is_non_void_pointer)) { + if c.inside_comptime_for_field { + if c.is_comptime_var(node.expr) { + return c.comptime_fields_default_type + } else if node.expr is ast.ComptimeSelector { + return c.comptime_fields_default_type + } + } + typ_str := c.table.type_to_str(typ) c.error('invalid operation: ${node.op.str()} (non-numeric type `${typ_str}`)', node.pos) diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index d3a287651b..28127ede66 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1326,7 +1326,7 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } else { 'unknown method or field: `${left_sym.name}.${method_name}`' } - if left_type.has_flag(.option) { + if left_type.has_flag(.option) && method_name != 'str' { c.error('option type cannot be called directly', node.left.pos()) return ast.void_type } else if left_type.has_flag(.result) { diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 1eef5e180c..50badcb7a7 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -864,40 +864,41 @@ fn (mut g Gen) gen_to_str_method_call(node ast.CallExpr) bool { if rec_type.has_flag(.shared_f) { rec_type = rec_type.clear_flag(.shared_f).set_nr_muls(0) } - if node.left is ast.ComptimeSelector { - key_str := g.get_comptime_selector_key_type(node.left) + left_node := if node.left is ast.PostfixExpr { node.left.expr } else { node.left } + if left_node is ast.ComptimeSelector { + key_str := g.get_comptime_selector_key_type(left_node) if key_str != '' { rec_type = g.comptime_var_type_map[key_str] or { rec_type } - g.gen_expr_to_string(node.left, rec_type) + g.gen_expr_to_string(left_node, rec_type) return true } - } else if node.left is ast.ComptimeCall { - if node.left.method_name == 'method' { - sym := g.table.sym(g.unwrap_generic(node.left.left_type)) + } else if left_node is ast.ComptimeCall { + if left_node.method_name == 'method' { + sym := g.table.sym(g.unwrap_generic(left_node.left_type)) if m := sym.find_method(g.comptime_for_method) { rec_type = m.return_type - g.gen_expr_to_string(node.left, rec_type) + g.gen_expr_to_string(left_node, rec_type) return true } } - } else if node.left is ast.Ident { - if node.left.obj is ast.Var { + } else if left_node is ast.Ident { + if left_node.obj is ast.Var { if g.comptime_var_type_map.len > 0 { - rec_type = node.left.obj.typ - g.gen_expr_to_string(node.left, rec_type) + rec_type = left_node.obj.typ + g.gen_expr_to_string(left_node, rec_type) return true - } else if node.left.obj.smartcasts.len > 0 { - rec_type = g.unwrap_generic(node.left.obj.smartcasts.last()) + } else if left_node.obj.smartcasts.len > 0 { + rec_type = g.unwrap_generic(left_node.obj.smartcasts.last()) cast_sym := g.table.sym(rec_type) if cast_sym.info is ast.Aggregate { rec_type = cast_sym.info.types[g.aggregate_type_idx] } - g.gen_expr_to_string(node.left, rec_type) + g.gen_expr_to_string(left_node, rec_type) return true } } - } else if node.left is ast.None { - g.gen_expr_to_string(node.left, ast.none_type) + } else if left_node is ast.None { + g.gen_expr_to_string(left_node, ast.none_type) return true } g.get_str_fn(rec_type) diff --git a/vlib/v/tests/call_to_str_on_option_test.v b/vlib/v/tests/call_to_str_on_option_test.v new file mode 100644 index 0000000000..fecc427094 --- /dev/null +++ b/vlib/v/tests/call_to_str_on_option_test.v @@ -0,0 +1,38 @@ +struct FixedStruct1 { + a int + b string + c ?int + d ?string +} + +struct Encoder {} + +fn test_main() { + fixed := FixedStruct1{123, '456', 789, '321'} + // this work well + println(fixed.a.str()) + println(fixed.c?.str()) + + println(fixed.b.int()) + println(fixed.d?.int()) + + e := Encoder{} + // this not work + e.encode_struct(fixed) +} + +fn (e &Encoder) encode_struct[T](val T) { + mut count := 0 + $for field in T.fields { + mut value := val.$(field.name) + $if field.is_option { + if field.name in ['c', 'd'] { + assert true + } + println('>> ${value ?.str()}') + println(val.$(field.name) ?.str()) + count += 1 + } + } + assert count == 2 +}