From c63902baf02376c845f89d817c560a8e4def3285 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Wed, 26 Apr 2023 16:49:50 -0300 Subject: [PATCH] checker, cgen: fix generic resolution for comptimeselector and indirection checking on generic funcs (#18043) --- vlib/v/checker/checker.v | 18 ++++++-- vlib/v/checker/comptime.v | 16 +------ vlib/v/checker/fn.v | 19 ++++++-- vlib/v/checker/if.v | 58 ++++++++++++++++++++++++ vlib/v/checker/str.v | 11 ++++- vlib/v/gen/c/fn.v | 8 ++++ vlib/v/gen/c/str_intp.v | 54 +++++++++++++++++++--- vlib/v/slow_tests/inout/comptime_ptr.out | 3 ++ vlib/v/slow_tests/inout/comptime_ptr.vv | 56 +++++++++++++++++++++++ 9 files changed, 214 insertions(+), 29 deletions(-) create mode 100644 vlib/v/slow_tests/inout/comptime_ptr.out create mode 100644 vlib/v/slow_tests/inout/comptime_ptr.vv diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index a97194994..475b12340 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3199,22 +3199,32 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type { // second use if node.kind in [.constant, .global, .variable] { info := node.info as ast.IdentVar + typ := if c.is_comptime_var(node) { + ctype := c.get_comptime_var_type(node) + if ctype != ast.void_type { + ctype + } else { + info.typ + } + } else { + info.typ + } // Got a var with type T, return current generic type if node.or_expr.kind != .absent { - if !info.typ.has_flag(.option) { + if !typ.has_flag(.option) { if node.or_expr.kind == .propagate_option { c.error('cannot use `?` on non-option variable', node.pos) } else if node.or_expr.kind == .block { c.error('cannot use `or {}` block on non-option variable', node.pos) } } - unwrapped_typ := info.typ.clear_flags(.option, .result) + unwrapped_typ := typ.clear_flags(.option, .result) c.expected_or_type = unwrapped_typ c.stmts_ending_with_expression(node.or_expr.stmts) - c.check_or_expr(node.or_expr, info.typ, c.expected_or_type, node) + c.check_or_expr(node.or_expr, typ, c.expected_or_type, node) return unwrapped_typ } - return info.typ + return typ } else if node.kind == .function { info := node.info as ast.IdentFn return info.typ diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index bfe5779ff..ca67a5e0f 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -655,21 +655,9 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) ComptimeBran .eq, .ne { if cond.left is ast.SelectorExpr && cond.right in [ast.IntegerLiteral, ast.StringLiteral] { - if cond.right is ast.IntegerLiteral - && c.is_comptime_selector_field_name(cond.left as ast.SelectorExpr, 'indirections') { - ret := match cond.op { - .eq { c.comptime_fields_default_type.nr_muls() == cond.right.val.i64() } - .ne { c.comptime_fields_default_type.nr_muls() != cond.right.val.i64() } - else { false } - } - return if ret { - ComptimeBranchSkipState.eval - } else { - ComptimeBranchSkipState.skip - } - } - return .unknown + // $if field.indirections == 1 // $if method.args.len == 1 + return .unknown } else if cond.left is ast.SelectorExpr && c.check_comptime_is_field_selector_bool(cond.left as ast.SelectorExpr) { // field.is_public (from T.fields) diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 50f0f3981..3cbc1edd1 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1345,13 +1345,13 @@ fn (mut c Checker) get_comptime_args(func ast.Fn, node_ ast.CallExpr, concrete_t if !param.typ.has_flag(.generic) { continue } + param_typ := param.typ if call_arg.expr is ast.Ident { if call_arg.expr.obj is ast.Var { if call_arg.expr.obj.ct_type_var !in [.generic_param, .no_comptime] { mut ctyp := c.get_comptime_var_type(call_arg.expr) if ctyp != ast.void_type { arg_sym := c.table.sym(ctyp) - param_typ := param.typ if arg_sym.kind == .array && param_typ.has_flag(.generic) && c.table.final_sym(param_typ).kind == .array { ctyp = (arg_sym.info as ast.Array).elem_type @@ -1361,7 +1361,6 @@ fn (mut c Checker) get_comptime_args(func ast.Fn, node_ ast.CallExpr, concrete_t } else if call_arg.expr.obj.ct_type_var == .generic_param { mut ctyp := c.get_comptime_var_type(call_arg.expr) if ctyp != ast.void_type { - param_typ := param.typ arg_sym := c.table.final_sym(call_arg.typ) param_typ_sym := c.table.sym(param_typ) @@ -1413,6 +1412,14 @@ fn (mut c Checker) get_comptime_args(func ast.Fn, node_ ast.CallExpr, concrete_t } } } + } else if call_arg.expr is ast.PrefixExpr { + if call_arg.expr.right is ast.ComptimeSelector { + comptime_args[i] = c.get_comptime_var_type(call_arg.expr.right) + comptime_args[i] = comptime_args[i].deref() + if comptime_args[i].nr_muls() > 0 && param_typ.nr_muls() > 0 { + comptime_args[i] = comptime_args[i].set_nr_muls(0) + } + } } else if call_arg.expr is ast.ComptimeSelector && c.is_comptime_var(call_arg.expr) { comptime_args[i] = c.get_comptime_var_type(call_arg.expr) } @@ -1438,7 +1445,6 @@ fn (mut c Checker) resolve_fn_generic_args(func ast.Fn, mut node ast.CallExpr) [ else {} } } - mut comptime_args := c.get_comptime_args(func, node, concrete_types) if comptime_args.len > 0 { for k, v in comptime_args { @@ -1806,6 +1812,12 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { c.fail_if_unreadable(arg.expr, got_arg_typ, 'argument') } } + if concrete_types.len > 0 && method.generic_names.len != rec_concrete_types.len { + concrete_types = c.resolve_fn_generic_args(method, mut node) + if !concrete_types[0].has_flag(.generic) { + c.table.register_fn_concrete_types(method.fkey(), concrete_types) + } + } if exp_arg_typ.has_flag(.generic) { method_concrete_types := if method.generic_names.len == rec_concrete_types.len { rec_concrete_types @@ -1819,7 +1831,6 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } else { continue } - if got_arg_typ.has_flag(.generic) { if c.table.cur_fn != unsafe { nil } && c.table.cur_concrete_types.len > 0 { got_arg_typ = c.unwrap_generic(got_arg_typ) diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v index 72c58e86d..e3f8cef98 100644 --- a/vlib/v/checker/if.v +++ b/vlib/v/checker/if.v @@ -163,6 +163,64 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { skip_state = c.check_compatible_types(left_type, right as ast.TypeNode) } } + } else if branch.cond.op in [.eq, .ne] { + left := branch.cond.left + right := branch.cond.right + if left is ast.SelectorExpr && right is ast.IntegerLiteral { + comptime_field_name = left.expr.str() + is_comptime_type_is_expr = true + if comptime_field_name == c.comptime_for_field_var { + if left.field_name == 'indirections' { + skip_state = match branch.cond.op { + .gt { + if c.comptime_fields_default_type.nr_muls() > right.val.i64() { + ComptimeBranchSkipState.eval + } else { + ComptimeBranchSkipState.skip + } + } + .lt { + if c.comptime_fields_default_type.nr_muls() < right.val.i64() { + ComptimeBranchSkipState.eval + } else { + ComptimeBranchSkipState.skip + } + } + .ge { + if c.comptime_fields_default_type.nr_muls() >= right.val.i64() { + ComptimeBranchSkipState.eval + } else { + ComptimeBranchSkipState.skip + } + } + .le { + if c.comptime_fields_default_type.nr_muls() <= right.val.i64() { + ComptimeBranchSkipState.eval + } else { + ComptimeBranchSkipState.skip + } + } + .ne { + if c.comptime_fields_default_type.nr_muls() != right.val.i64() { + ComptimeBranchSkipState.eval + } else { + ComptimeBranchSkipState.skip + } + } + .eq { + if c.comptime_fields_default_type.nr_muls() == right.val.i64() { + ComptimeBranchSkipState.eval + } else { + ComptimeBranchSkipState.skip + } + } + else { + ComptimeBranchSkipState.skip + } + } + } + } + } } } cur_skip_flags := c.skip_flags diff --git a/vlib/v/checker/str.v b/vlib/v/checker/str.v index e52160b57..892966494 100644 --- a/vlib/v/checker/str.v +++ b/vlib/v/checker/str.v @@ -44,7 +44,13 @@ fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) ast.Type { inside_println_arg_save := c.inside_println_arg c.inside_println_arg = true for i, expr in node.exprs { - ftyp := c.expr(expr) + mut ftyp := c.expr(expr) + if c.is_comptime_var(expr) { + ctyp := c.get_comptime_var_type(expr) + if ctyp != ast.void_type { + ftyp = ctyp + } + } if ftyp == ast.void_type { c.error('expression does not return a value', expr.pos()) } else if ftyp == ast.char_type && ftyp.nr_muls() == 0 { @@ -72,6 +78,9 @@ fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) ast.Type { c.error('no known default format for type `${c.table.get_type_name(ftyp)}`', node.fmt_poss[i]) } + } else if c.is_comptime_var(expr) && c.get_comptime_var_type(expr) != ast.void_type { + // still `_` placeholder for comptime variable without specifier + node.need_fmts[i] = false } else { node.fmts[i] = fmt node.need_fmts[i] = false diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 27a98ae55..5875e0796 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1094,6 +1094,14 @@ fn (mut g Gen) change_comptime_args(func ast.Fn, mut node_ ast.CallExpr, concret } } } + } else if mut call_arg.expr is ast.PrefixExpr { + if call_arg.expr.right is ast.ComptimeSelector { + comptime_args[i] = g.comptime_for_field_type + comptime_args[i] = comptime_args[i].deref() + if param_typ.nr_muls() > 0 && comptime_args[i].nr_muls() > 0 { + comptime_args[i] = comptime_args[i].set_nr_muls(0) + } + } } else if mut call_arg.expr is ast.ComptimeSelector { comptime_args[i] = g.comptime_for_field_type if call_arg.expr.left.is_auto_deref_var() { diff --git a/vlib/v/gen/c/str_intp.v b/vlib/v/gen/c/str_intp.v index 6f3ad9c00..87439c828 100644 --- a/vlib/v/gen/c/str_intp.v +++ b/vlib/v/gen/c/str_intp.v @@ -12,6 +12,40 @@ module c import v.ast import v.util +fn (mut g Gen) get_default_fmt(ftyp ast.Type, typ ast.Type) u8 { + if ftyp.has_flag(.option) || ftyp.has_flag(.result) { + return `s` + } else if typ.is_float() { + return `g` + } else if typ.is_signed() || typ.is_int_literal() { + return `d` + } else if typ.is_unsigned() { + return `u` + } else if typ.is_pointer() { + return `p` + } else { + mut sym := g.table.sym(g.unwrap_generic(ftyp)) + if sym.kind == .alias { + // string aliases should be printable + info := sym.info as ast.Alias + sym = g.table.sym(info.parent_type) + if info.parent_type == ast.string_type { + return `s` + } + } + if sym.kind == .function { + return `s` + } + if ftyp in [ast.string_type, ast.bool_type] + || sym.kind in [.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type, .interface_, .none_] + || ftyp.has_flag(.option) || ftyp.has_flag(.result) || sym.has_method('str') { + return `s` + } else { + return `_` + } + } +} + fn (mut g Gen) str_format(node ast.StringInterLiteral, i int) (u64, string) { mut base := 0 // numeric base mut upper_case := false // set upercase for the result string @@ -202,12 +236,20 @@ fn (mut g Gen) str_val(node ast.StringInterLiteral, i int) { fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { // fn (mut g Gen) str_int2(node ast.StringInterLiteral) { - if g.inside_comptime_for_field { - mut node_ := unsafe { node } - for i, mut expr in node_.exprs { - if mut expr is ast.Ident { - if mut expr.obj is ast.Var { - node_.expr_types[i] = expr.obj.typ + mut node_ := unsafe { node } + for i, mut expr in node_.exprs { + if g.is_comptime_var(expr) { + ctyp := g.get_comptime_var_type(expr) + if ctyp != ast.void_type { + node_.expr_types[i] = ctyp + if node_.fmts[i] == `_` { + ftyp_sym := g.table.sym(ctyp) + typ := if ftyp_sym.kind == .alias && !ftyp_sym.has_method('str') { + g.table.unalias_num_type(ctyp) + } else { + ctyp + } + node_.fmts[i] = g.get_default_fmt(ctyp, typ) } } } diff --git a/vlib/v/slow_tests/inout/comptime_ptr.out b/vlib/v/slow_tests/inout/comptime_ptr.out new file mode 100644 index 000000000..589e4517d --- /dev/null +++ b/vlib/v/slow_tests/inout/comptime_ptr.out @@ -0,0 +1,3 @@ +[vlib/v/slow_tests/inout/comptime_ptr.vv:21] val: ads +[vlib/v/slow_tests/inout/comptime_ptr.vv:21] val: false +[vlib/v/slow_tests/inout/comptime_ptr.vv:21] val: true diff --git a/vlib/v/slow_tests/inout/comptime_ptr.vv b/vlib/v/slow_tests/inout/comptime_ptr.vv new file mode 100644 index 000000000..61d7df064 --- /dev/null +++ b/vlib/v/slow_tests/inout/comptime_ptr.vv @@ -0,0 +1,56 @@ +struct Encoder {} + +struct Writer {} + +struct StructTypePointer[T] { +mut: + val &T +} + +struct StructTypePointerPointer[T] { +mut: + val &&T +} + +pub fn (e &Encoder) encode_value[T](val T, mut wr Writer) ! { + e.encode_struct[T](val, 1, mut wr)! +} + +pub fn (e &Encoder) encode_value_with_level[T](val T, mut wr Writer) ! { + e.encode_struct[T](val, 1, mut wr)! + dump(val) +} + +fn (e &Encoder) encode_struct[U](val U, level int, mut wr Writer) ! { + $for field in U.fields { + $if field.indirections > 0 { + $if field.indirections == 1 { + e.encode_value_with_level(*val.$(field.name), mut wr)! + } + $if field.indirections == 2 { + e.encode_value_with_level(**val.$(field.name), mut wr)! + } + $if field.indirections == 3 { + e.encode_value_with_level(***val.$(field.name), mut wr)! + } + } + } +} + +fn main() { + e := Encoder{} + mut sb := Writer{} + + mut string_initialized_with_reference := 'ads' + mut bool_initialized_with_reference := false + + e.encode_value(StructTypePointer[string]{ val: &string_initialized_with_reference }, mut + sb) or {} + e.encode_value(StructTypePointer[bool]{ val: &bool_initialized_with_reference }, mut + sb) or {} + + bool_val := true + ptr_bool := &bool_val + + e.encode_value(StructTypePointerPointer[bool]{ val: &ptr_bool }, mut sb) or {} +} -- 2.39.5