diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index 6e98bee645..8adf389df2 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -399,7 +399,7 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { && right.or_expr.kind == .absent { right_obj_var := right.obj as ast.Var if right_obj_var.ct_type_var != .no_comptime { - ctyp := c.comptime.get_comptime_var_type(right) + ctyp := c.comptime.get_type(right) if ctyp != ast.void_type { left.obj.ct_type_var = right_obj_var.ct_type_var left.obj.typ = ctyp @@ -421,11 +421,12 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { fn_ret_type := c.resolve_return_type(right) if fn_ret_type != ast.void_type && c.table.final_sym(fn_ret_type).kind != .multi_return { - c.comptime.type_map['g.${left.name}.${left.obj.pos.pos}'] = if right.or_block.kind == .absent { + var_type := if right.or_block.kind == .absent { fn_ret_type } else { fn_ret_type.clear_option_and_result() } + c.comptime.type_map['g.${left.name}.${left.obj.pos.pos}'] = var_type } } } @@ -505,7 +506,7 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { } if right is ast.Ident && c.comptime.is_comptime_var(right) { - right_type = c.comptime.get_comptime_var_type(right) + right_type = c.comptime.get_type(right) } } if mut left is ast.InfixExpr { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index b37408d5b3..6897687747 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2928,7 +2928,7 @@ pub fn (mut c Checker) expr(mut node ast.Expr) ast.Type { if c.comptime.inside_comptime_for && node.expr is ast.Ident { if c.comptime.is_comptime_var(node.expr) { - node.expr_type = c.comptime.get_comptime_var_type(node.expr as ast.Ident) + node.expr_type = c.comptime.get_type(node.expr as ast.Ident) } else if (node.expr as ast.Ident).name in c.comptime.type_map { node.expr_type = c.comptime.type_map[(node.expr as ast.Ident).name] } @@ -3805,7 +3805,7 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type { if node.kind in [.constant, .global, .variable] { info := node.info as ast.IdentVar typ := if c.comptime.is_comptime_var(node) { - ctype := c.comptime.get_comptime_var_type(node) + ctype := c.comptime.get_type(node) if ctype != ast.void_type { ctype } else { diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 986d54d22d..94e31ff4cf 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -134,7 +134,7 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { node.args[i].typ = c.expr(mut arg.expr) } c.stmts_ending_with_expression(mut node.or_block.stmts, c.expected_or_type) - return c.comptime.get_comptime_var_type(node) + return c.comptime.get_type(node) } if node.method_name == 'res' { if !c.inside_defer { @@ -790,7 +790,7 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr, pos token.Pos) ComptimeBr if mut cond.left is ast.SelectorExpr && cond.right is ast.ComptimeType { comptime_type := cond.right as ast.ComptimeType if c.comptime.is_comptime_selector_type(cond.left) { - checked_type := c.comptime.get_comptime_var_type(cond.left) + checked_type := c.comptime.get_type(cond.left) return if c.comptime.is_comptime_type(checked_type, comptime_type) { .eval } else { diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index ac058e1e8b..605dac77d8 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1807,7 +1807,7 @@ fn (mut c Checker) resolve_comptime_args(func &ast.Fn, node_ ast.CallExpr, concr 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_var, .generic_param, .no_comptime] { - mut ctyp := c.comptime.get_comptime_var_type(call_arg.expr) + mut ctyp := c.comptime.get_type(call_arg.expr) if ctyp != ast.void_type { arg_sym := c.table.sym(ctyp) param_sym := c.table.final_sym(param_typ) @@ -1830,7 +1830,7 @@ fn (mut c Checker) resolve_comptime_args(func &ast.Fn, node_ ast.CallExpr, concr comptime_args[k] = ctyp } } else if call_arg.expr.obj.ct_type_var == .generic_param { - mut ctyp := c.comptime.get_comptime_var_type(call_arg.expr) + mut ctyp := c.comptime.get_type(call_arg.expr) if ctyp != ast.void_type { arg_sym := c.table.final_sym(call_arg.typ) param_typ_sym := c.table.sym(param_typ) @@ -1893,7 +1893,10 @@ fn (mut c Checker) resolve_comptime_args(func &ast.Fn, node_ ast.CallExpr, concr } } } else if call_arg.expr.obj.ct_type_var == .generic_var { - mut ctyp := c.comptime.get_comptime_var_type(call_arg.expr) + mut ctyp := c.comptime.get_type(call_arg.expr) + if node_.args[i].expr.is_auto_deref_var() { + ctyp = ctyp.deref() + } if ctyp.nr_muls() > 0 && param_typ.nr_muls() > 0 { ctyp = ctyp.set_nr_muls(0) } @@ -1902,14 +1905,14 @@ fn (mut c Checker) resolve_comptime_args(func &ast.Fn, node_ ast.CallExpr, concr } } else if call_arg.expr is ast.PrefixExpr { if call_arg.expr.right is ast.ComptimeSelector { - comptime_args[k] = c.comptime.get_comptime_var_type(call_arg.expr.right) + comptime_args[k] = c.comptime.get_type(call_arg.expr.right) comptime_args[k] = comptime_args[k].deref() if comptime_args[k].nr_muls() > 0 && param_typ.nr_muls() > 0 { comptime_args[k] = comptime_args[k].set_nr_muls(0) } } } else if call_arg.expr is ast.ComptimeSelector { - ct_value := c.comptime.get_comptime_var_type(call_arg.expr) + ct_value := c.comptime.get_type(call_arg.expr) param_typ_sym := c.table.sym(param_typ) if ct_value != ast.void_type { arg_sym := c.table.final_sym(call_arg.typ) @@ -1934,7 +1937,7 @@ fn (mut c Checker) resolve_comptime_args(func &ast.Fn, node_ ast.CallExpr, concr } } } else if call_arg.expr is ast.ComptimeCall { - comptime_args[k] = c.comptime.get_comptime_var_type(call_arg.expr) + comptime_args[k] = c.comptime.get_type(call_arg.expr) } } } diff --git a/vlib/v/checker/for.v b/vlib/v/checker/for.v index 76563fb02e..779d0f731d 100644 --- a/vlib/v/checker/for.v +++ b/vlib/v/checker/for.v @@ -80,7 +80,7 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) { mut is_comptime := false if (node.cond is ast.Ident && c.comptime.is_comptime_var(node.cond)) || node.cond is ast.ComptimeSelector { - ctyp := c.comptime.get_comptime_var_type(node.cond) + ctyp := c.comptime.get_type(node.cond) if ctyp != ast.void_type { is_comptime = true typ = ctyp diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v index e267d67288..5effb8a693 100644 --- a/vlib/v/checker/if.v +++ b/vlib/v/checker/if.v @@ -557,7 +557,7 @@ fn (mut c Checker) smartcast_if_conds(mut node ast.Expr, mut scope ast.Scope, co c.smartcast_if_conds(mut node.right, mut scope, control_expr) } else if node.left is ast.Ident && node.op == .ne && node.right is ast.None { if node.left is ast.Ident && c.comptime.get_ct_type_var(node.left) == .smartcast { - node.left_type = c.comptime.get_comptime_var_type(node.left) + node.left_type = c.comptime.get_type(node.left) c.smartcast(mut node.left, node.left_type, node.left_type.clear_flag(.option), mut scope, true) } else { @@ -566,7 +566,7 @@ fn (mut c Checker) smartcast_if_conds(mut node ast.Expr, mut scope ast.Scope, co } } else if node.op == .key_is { if node.left is ast.Ident && c.comptime.is_comptime_var(node.left) { - node.left_type = c.comptime.get_comptime_var_type(node.left) + node.left_type = c.comptime.get_type(node.left) } else { node.left_type = c.expr(mut node.left) } @@ -650,7 +650,7 @@ fn (mut c Checker) smartcast_if_conds(mut node ast.Expr, mut scope ast.Scope, co if first_cond.left is ast.Ident && first_cond.op == .eq && first_cond.right is ast.None { if first_cond.left is ast.Ident && c.comptime.get_ct_type_var(first_cond.left) == .smartcast { - first_cond.left_type = c.comptime.get_comptime_var_type(first_cond.left) + first_cond.left_type = c.comptime.get_type(first_cond.left) c.smartcast(mut first_cond.left, first_cond.left_type, first_cond.left_type.clear_flag(.option), mut scope, true) } else { diff --git a/vlib/v/checker/postfix.v b/vlib/v/checker/postfix.v index 8ce727f250..878bb805f5 100644 --- a/vlib/v/checker/postfix.v +++ b/vlib/v/checker/postfix.v @@ -26,7 +26,7 @@ fn (mut c Checker) postfix_expr(mut node ast.PostfixExpr) ast.Type { if !(typ_sym.is_number() || ((c.inside_unsafe || c.pref.translated) && is_non_void_pointer)) { if c.comptime.comptime_for_field_var != '' { if c.comptime.is_comptime_var(node.expr) || node.expr is ast.ComptimeSelector { - node.typ = c.unwrap_generic(c.comptime.get_comptime_var_type(node.expr)) + node.typ = c.unwrap_generic(c.comptime.get_type(node.expr)) if node.op == .question { node.typ = node.typ.clear_flag(.option) } diff --git a/vlib/v/checker/str.v b/vlib/v/checker/str.v index aaa2b5615f..5ef49a396b 100644 --- a/vlib/v/checker/str.v +++ b/vlib/v/checker/str.v @@ -47,7 +47,7 @@ fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) ast.Type { mut ftyp := c.expr(mut expr) ftyp = c.check_expr_option_or_result_call(expr, ftyp) if c.comptime.is_comptime_var(expr) { - ctyp := c.comptime.get_comptime_var_type(expr) + ctyp := c.comptime.get_type(expr) if ctyp != ast.void_type { ftyp = ctyp } @@ -79,8 +79,7 @@ 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.comptime.is_comptime_var(expr) - && c.comptime.get_comptime_var_type(expr) != ast.void_type { + } else if c.comptime.is_comptime_var(expr) && c.comptime.get_type(expr) != ast.void_type { // still `_` placeholder for comptime variable without specifier node.need_fmts[i] = false } else { diff --git a/vlib/v/comptime/comptimeinfo.v b/vlib/v/comptime/comptimeinfo.v index c623365cbd..2eef68dbbe 100644 --- a/vlib/v/comptime/comptimeinfo.v +++ b/vlib/v/comptime/comptimeinfo.v @@ -28,6 +28,14 @@ pub fn (mut ct ComptimeInfo) get_comptime_selector_key_type(val ast.ComptimeSele return '' } +// is_comptime_expr checks if the node is related to a comptime expr +@[inline] +pub fn (mut ct ComptimeInfo) is_comptime_expr(node ast.Expr) bool { + return (node is ast.Ident && ct.get_ct_type_var(node) != .no_comptime) + || (node is ast.IndexExpr && ct.is_comptime_expr(node.left)) + || node is ast.ComptimeSelector +} + // is_comptime_var checks if the node is related to a comptime variable @[inline] pub fn (mut ct ComptimeInfo) is_comptime_var(node ast.Expr) bool { @@ -45,6 +53,8 @@ pub fn (mut ct ComptimeInfo) is_comptime_variant_var(node ast.Ident) bool { pub fn (mut ct ComptimeInfo) get_ct_type_var(node ast.Expr) ast.ComptimeVarKind { return if node is ast.Ident && node.obj is ast.Var { (node.obj as ast.Var).ct_type_var + } else if node is ast.IndexExpr { + return ct.get_ct_type_var(node.left) } else { .no_comptime } @@ -56,9 +66,23 @@ pub fn (mut ct ComptimeInfo) is_generic_param_var(node ast.Expr) bool { && (node.obj as ast.Var).ct_type_var == .generic_param } -// get_comptime_var_type retrieves the actual type from a comptime related ast node +// get_expr_type computes the ast node type regarding its or_expr +pub fn (mut ct ComptimeInfo) get_expr_type(node ast.Expr) ast.Type { + ctyp := ct.get_type(node) + match node { + ast.Ident { + if ctyp.has_flag(.option) && node.or_expr.kind != .absent { + return ctyp.clear_flag(.option) + } + } + else {} + } + return ctyp +} + +// get_type retrieves the actual type from a comptime related ast node @[inline] -pub fn (mut ct ComptimeInfo) get_comptime_var_type(node ast.Expr) ast.Type { +pub fn (mut ct ComptimeInfo) get_type(node ast.Expr) ast.Type { if node is ast.Ident { if node.obj is ast.Var { return match node.obj.ct_type_var { @@ -109,6 +133,8 @@ pub fn (mut ct ComptimeInfo) get_comptime_var_type(node ast.Expr) ast.Type { return ast.void_type } return f.return_type + } else if node is ast.IndexExpr && ct.is_comptime_var(node.left) { + return ct.table.value_type(ct.resolver.unwrap_generic(ct.get_type(node.left))) } return ast.void_type } @@ -304,16 +330,16 @@ pub fn (mut ct ComptimeInfo) unwrap_generic_expr(expr ast.Expr, default_typ ast. } ast.InfixExpr { if ct.is_comptime_var(expr.left) { - return ct.resolver.unwrap_generic(ct.get_comptime_var_type(expr.left)) + return ct.resolver.unwrap_generic(ct.get_type(expr.left)) } if ct.is_comptime_var(expr.right) { - return ct.resolver.unwrap_generic(ct.get_comptime_var_type(expr.right)) + return ct.resolver.unwrap_generic(ct.get_type(expr.right)) } return default_typ } ast.Ident { return if ct.is_comptime_var(expr) { - ct.resolver.unwrap_generic(ct.get_comptime_var_type(expr)) + ct.resolver.unwrap_generic(ct.get_type(expr)) } else { default_typ } diff --git a/vlib/v/gen/c/array.v b/vlib/v/gen/c/array.v index 2d3289bc66..6d3f92d3f1 100644 --- a/vlib/v/gen/c/array.v +++ b/vlib/v/gen/c/array.v @@ -564,7 +564,7 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) { // value.map(Type(it)) when `value` is a comptime var if expr.expr is ast.Ident && node.left is ast.Ident && g.comptime.is_comptime_var(node.left) { - ctyp := g.comptime.get_comptime_var_type(node.left) + ctyp := g.comptime.get_type(node.left) if ctyp != ast.void_type { expr.expr_type = g.table.value_type(ctyp) } diff --git a/vlib/v/gen/c/assert.v b/vlib/v/gen/c/assert.v index 3d19cd1711..80d30ee71b 100644 --- a/vlib/v/gen/c/assert.v +++ b/vlib/v/gen/c/assert.v @@ -151,12 +151,22 @@ fn (mut g Gen) gen_assert_metainfo(node ast.AssertStmt, kind AssertMetainfoKind) g.writeln('\t${metaname}.op = ${expr_op_str};') g.writeln('\t${metaname}.llabel = ${expr_left_str};') g.writeln('\t${metaname}.rlabel = ${expr_right_str};') + left_type := if g.comptime.is_comptime_expr(node.expr.left) { + g.comptime.get_type(node.expr.left) + } else { + node.expr.left_type + } + right_type := if g.comptime.is_comptime_expr(node.expr.right) { + g.comptime.get_type(node.expr.right) + } else { + node.expr.right_type + } if kind != .pass { g.write('\t${metaname}.lvalue = ') - g.gen_assert_single_expr(node.expr.left, node.expr.left_type) + g.gen_assert_single_expr(node.expr.left, left_type) g.writeln(';') g.write('\t${metaname}.rvalue = ') - g.gen_assert_single_expr(node.expr.right, node.expr.right_type) + g.gen_assert_single_expr(node.expr.right, right_type) g.writeln(';') } } @@ -207,6 +217,8 @@ fn (mut g Gen) gen_assert_single_expr(expr ast.Expr, typ ast.Type) { // vlib/builtin/map_test.v (a map of &int, set to &int(0)) fails // without special casing ast.CastExpr here g.write(ctoslit(expr_str)) + } else if expr.right is ast.Ident { + g.write(ctoslit(expr_str)) } else { g.gen_expr_to_string(expr, typ) } diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index d2288c0cbc..349e7933ed 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -259,7 +259,7 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { } if mut left.obj is ast.Var { if val is ast.Ident && g.comptime.is_comptime_var(val) { - ctyp := g.unwrap_generic(g.comptime.get_comptime_var_type(val)) + ctyp := g.unwrap_generic(g.comptime.get_type(val)) if ctyp != ast.void_type { var_type = ctyp val_type = var_type diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 2ca5596394..52ba0fb97d 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -191,8 +191,9 @@ mut: array_index_types []ast.Type auto_fn_definitions []string // auto generated functions definition list sumtype_casting_fns []SumtypeCastingFn - anon_fn_definitions []string // anon generated functions definition list - sumtype_definitions map[int]bool // `_TypeA_to_sumtype_TypeB()` fns that have been generated + anon_fn_definitions []string // anon generated functions definition list + anon_fns shared []string // remove duplicate anon generated functions + sumtype_definitions map[int]bool // `_TypeA_to_sumtype_TypeB()` fns that have been generated trace_fn_definitions []string json_types []ast.Type // to avoid json gen duplicates pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name @@ -713,6 +714,7 @@ fn cgen_process_one_file_cb(mut p pool.PoolProcessor, idx int, wid int) &Gen { waiter_fns: global_g.waiter_fns threaded_fns: global_g.threaded_fns str_fn_names: global_g.str_fn_names + anon_fns: global_g.anon_fns options_forward: global_g.options_forward results_forward: global_g.results_forward done_options: global_g.done_options @@ -3895,6 +3897,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { if node.expr_type == 0 { g.checker_bug('unexpected SelectorExpr.expr_type = 0', node.pos) } + sym := g.table.sym(g.unwrap_generic(node.expr_type)) field_name := if sym.language == .v { c_name(node.field_name) } else { node.field_name } is_as_cast := node.expr is ast.AsCast @@ -4033,53 +4036,13 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } receiver := m.params[0] expr_styp := g.styp(g.unwrap_generic(node.expr_type).idx_type()) - data_styp := g.styp(receiver.typ.idx_type()) - mut sb := strings.new_builder(256) name := '_V_closure_${expr_styp}_${m.name}_${node.pos.pos}' - sb.write_string('${g.styp(m.return_type)} ${name}(') - for i in 1 .. m.params.len { - param := m.params[i] - if i != 1 { - sb.write_string(', ') - } - sb.write_string('${g.styp(param.typ)} a${i}') - } - sb.writeln(') {') - sb.writeln('\t${data_styp}* a0 = __CLOSURE_GET_DATA();') - if m.return_type != ast.void_type { - sb.write_string('\treturn ') - } else { - sb.write_string('\t') - } - mut method_name := m.name - rec_sym := g.table.sym(receiver.typ) - if rec_sym.info is ast.Struct { - if rec_sym.info.concrete_types.len > 0 { - method_name = g.generic_fn_name(rec_sym.info.concrete_types, m.name) + lock g.anon_fns { + if name !in g.anon_fns { + g.anon_fns << name + g.gen_closure_fn(expr_styp, m, name) } } - if rec_sym.info is ast.Interface { - left_cc_type := g.cc_type(g.table.unaliased_type(receiver.typ), false) - left_type_name := util.no_dots(left_cc_type) - sb.write_string('${c_name(left_type_name)}_name_table[a0->_typ]._method_${method_name}(') - } else { - sb.write_string('${expr_styp}_${method_name}(') - if !receiver.typ.is_ptr() { - sb.write_string('*') - } - } - for i in 0 .. m.params.len { - if i != 0 { - sb.write_string(', ') - } - sb.write_string('a${i}') - } - sb.writeln(');') - sb.writeln('}') - - g.anon_fn_definitions << sb.str() - g.nr_closures++ - g.write('__closure_create(${name}, ') if !receiver.typ.is_ptr() { g.write('memdup_uncollectable(') @@ -4191,6 +4154,56 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } } +fn (mut g Gen) gen_closure_fn(expr_styp string, m ast.Fn, name string) { + receiver := m.params[0] + data_styp := g.styp(receiver.typ.idx_type()) + + mut sb := strings.new_builder(256) + sb.write_string('${g.styp(m.return_type)} ${name}(') + for i in 1 .. m.params.len { + param := m.params[i] + if i != 1 { + sb.write_string(', ') + } + sb.write_string('${g.styp(param.typ)} a${i}') + } + sb.writeln(') {') + sb.writeln('\t${data_styp}* a0 = __CLOSURE_GET_DATA();') + if m.return_type != ast.void_type { + sb.write_string('\treturn ') + } else { + sb.write_string('\t') + } + mut method_name := m.name + rec_sym := g.table.sym(receiver.typ) + if rec_sym.info is ast.Struct { + if rec_sym.info.concrete_types.len > 0 { + method_name = g.generic_fn_name(rec_sym.info.concrete_types, m.name) + } + } + if rec_sym.info is ast.Interface { + left_cc_type := g.cc_type(g.table.unaliased_type(receiver.typ), false) + left_type_name := util.no_dots(left_cc_type) + sb.write_string('${c_name(left_type_name)}_name_table[a0->_typ]._method_${method_name}(') + } else { + sb.write_string('${expr_styp}_${method_name}(') + if !receiver.typ.is_ptr() { + sb.write_string('*') + } + } + for i in 0 .. m.params.len { + if i != 0 { + sb.write_string(', ') + } + sb.write_string('a${i}') + } + sb.writeln(');') + sb.writeln('}') + + g.anon_fn_definitions << sb.str() + g.nr_closures++ +} + fn (mut g Gen) write_selector_expr_embed_name(node ast.SelectorExpr, embed_types []ast.Type) { for i, embed in embed_types { embed_sym := g.table.sym(embed) @@ -4261,7 +4274,7 @@ fn (mut g Gen) debugger_stmt(node ast.DebuggerStmt) { if obj is ast.Var && g.check_var_scope(obj, node.pos.pos) { keys.write_string('_SLIT("${obj.name}")') var_typ := if obj.ct_type_var != .no_comptime { - g.comptime.get_comptime_var_type(ast.Ident{ obj: obj }) + g.comptime.get_type(ast.Ident{ obj: obj }) } else if obj.smartcasts.len > 0 { obj.smartcasts.last() } else { @@ -4858,7 +4871,7 @@ fn (mut g Gen) ident(node ast.Ident) { if node.obj is ast.Var { if !g.is_assign_lhs && node.obj.ct_type_var !in [.smartcast, .generic_param, .no_comptime] { - comptime_type := g.comptime.get_comptime_var_type(node) + comptime_type := g.comptime.get_type(node) if comptime_type.has_flag(.option) { if (g.inside_opt_or_res || g.left_is_opt) && node.or_expr.kind == .absent { if !g.is_assign_lhs && is_auto_heap { @@ -4876,7 +4889,11 @@ fn (mut g Gen) ident(node ast.Ident) { } } } else { - g.write(name) + if is_auto_heap { + g.write2('*', name) + } else { + g.write(name) + } } if node.or_expr.kind != .absent && !(g.inside_opt_or_res && g.inside_assign && !g.is_assign_lhs) { @@ -4944,7 +4961,7 @@ fn (mut g Gen) ident(node ast.Ident) { if node.obj.smartcasts.len > 0 { obj_sym := g.table.sym(g.unwrap_generic(node.obj.typ)) if node.obj.ct_type_var == .smartcast { - ctyp := g.unwrap_generic(g.comptime.get_comptime_var_type(node)) + ctyp := g.unwrap_generic(g.comptime.get_type(node)) cur_variant_sym := g.table.sym(ctyp) variant_name := g.get_sumtype_variant_name(ctyp, cur_variant_sym) g.write('._${variant_name}') @@ -4980,7 +4997,7 @@ fn (mut g Gen) ident(node ast.Ident) { is_option_unwrap := is_option && typ == node.obj.typ.clear_flag(.option) g.write('(') if i == 0 && node.obj.is_unwrapped { - ctyp := g.unwrap_generic(g.comptime.get_comptime_var_type(node)) + ctyp := g.unwrap_generic(g.comptime.get_type(node)) g.write('*(${g.base_type(ctyp)}*)(') } if obj_sym.kind == .sum_type && !is_auto_heap { @@ -5036,7 +5053,7 @@ fn (mut g Gen) ident(node ast.Ident) { } } if node.obj.ct_type_var == .smartcast { - mut ctyp := g.unwrap_generic(g.comptime.get_comptime_var_type(node)) + mut ctyp := g.unwrap_generic(g.comptime.get_type(node)) cur_variant_sym := g.table.sym(ctyp) if node.obj.is_unwrapped { ctyp = ctyp.set_flag(.option) @@ -5111,9 +5128,8 @@ fn (mut g Gen) cast_expr(node ast.CastExpr) { node_typ := g.unwrap_generic(node.typ) mut expr_type := node.expr_type sym := g.table.sym(node_typ) - if (node.expr is ast.Ident && g.comptime.is_comptime_var(node.expr)) - || node.expr is ast.ComptimeSelector { - expr_type = g.unwrap_generic(g.comptime.get_comptime_var_type(node.expr)) + if g.comptime.is_comptime_expr(node.expr) { + expr_type = g.unwrap_generic(g.comptime.get_type(node.expr)) } if sym.kind in [.sum_type, .interface] { if node.typ.has_flag(.option) && node.expr is ast.None { diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index b22057bd57..6259922d44 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -460,7 +460,7 @@ fn (mut g Gen) get_expr_type(cond ast.Expr) ast.Type { match cond { ast.Ident { return if g.comptime.is_comptime_var(cond) { - g.unwrap_generic(g.comptime.get_comptime_var_type(cond)) + g.unwrap_generic(g.comptime.get_type(cond)) } else { g.unwrap_generic(cond.obj.typ) } @@ -800,8 +800,8 @@ fn (mut g Gen) pop_comptime_info() { } fn (mut g Gen) resolve_comptime_type(node ast.Expr, default_type ast.Type) ast.Type { - if (node is ast.Ident && g.comptime.is_comptime_var(node)) || node is ast.ComptimeSelector { - return g.comptime.get_comptime_var_type(node) + if g.comptime.is_comptime_expr(node) { + return g.comptime.get_type(node) } else if node is ast.SelectorExpr && node.expr_type != 0 { if node.expr is ast.Ident && g.comptime.is_comptime_selector_type(node) { return g.comptime.get_type_from_comptime_var(node.expr) @@ -1062,6 +1062,100 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) { g.writeln('}// \$for') } +// comptime_selector_type computes the selector type from an comptime var +fn (mut g Gen) comptime_selector_type(node ast.SelectorExpr) ast.Type { + if !(node.expr is ast.Ident && g.comptime.is_comptime_var(node.expr)) { + return node.expr_type + } + prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once + g.prevent_sum_type_unwrapping_once = false + + mut typ := g.comptime.get_type(node.expr) + if node.expr.is_auto_deref_var() { + if node.expr is ast.Ident { + if node.expr.obj is ast.Var { + typ = node.expr.obj.typ + } + } + } + if g.comptime.inside_comptime_for && typ == g.enum_data_type && node.field_name == 'value' { + // for comp-time enum.values + return g.comptime.type_map['${g.comptime.comptime_for_enum_var}.typ'] + } + field_name := node.field_name + sym := g.table.sym(typ) + final_sym := g.table.final_sym(typ) + if (typ.has_flag(.variadic) || final_sym.kind == .array_fixed) && field_name == 'len' { + return ast.int_type + } + if sym.kind == .chan { + if field_name == 'closed' { + return ast.bool_type + } else if field_name in ['len', 'cap'] { + return ast.u32_type + } + } + mut has_field := false + mut field := ast.StructField{} + if field_name.len > 0 && field_name[0].is_capital() && sym.info is ast.Struct + && sym.language == .v { + // x.Foo.y => access the embedded struct + for embed in sym.info.embeds { + embed_sym := g.table.sym(embed) + if embed_sym.embed_name() == field_name { + return embed + } + } + } else { + if f := g.table.find_field(sym, field_name) { + has_field = true + field = f + } else { + // look for embedded field + has_field = true + g.table.find_field_from_embeds(sym, field_name) or { has_field = false } + } + if typ.has_flag(.generic) && !has_field { + gs := g.table.sym(g.unwrap_generic(typ)) + if f := g.table.find_field(gs, field_name) { + has_field = true + field = f + } else { + // look for embedded field + has_field = true + g.table.find_field_from_embeds(gs, field_name) or { has_field = false } + } + } + } + + if has_field { + field_sym := g.table.sym(field.typ) + if field_sym.kind in [.sum_type, .interface] { + if !prevent_sum_type_unwrapping_once { + if scope_field := node.scope.find_struct_field(node.expr.str(), typ, field_name) { + return scope_field.smartcasts.last() + } + } + } + return field.typ + } + if mut method := g.table.sym(g.unwrap_generic(typ)).find_method_with_generic_parent(field_name) { + method.params = method.params[1..] + method.name = '' + fn_type := ast.new_type(g.table.find_or_register_fn_type(method, false, true)) + return fn_type + } + if sym.kind !in [.struct, .aggregate, .interface, .sum_type] { + if sym.kind != .placeholder { + unwrapped_sym := g.table.sym(g.unwrap_generic(typ)) + if unwrapped_sym.kind == .array_fixed && node.field_name == 'len' { + return ast.int_type + } + } + } + return node.expr_type +} + fn (mut g Gen) comptime_if_to_ifdef(name string, is_comptime_option bool) !string { match name { // platforms/os-es: diff --git a/vlib/v/gen/c/dumpexpr.v b/vlib/v/gen/c/dumpexpr.v index ce6506cbdf..0d708a0fbe 100644 --- a/vlib/v/gen/c/dumpexpr.v +++ b/vlib/v/gen/c/dumpexpr.v @@ -44,9 +44,13 @@ fn (mut g Gen) dump_expr(node ast.DumpExpr) { } } } - } else if node.expr is ast.Ident && g.comptime.inside_comptime_for - && g.comptime.is_comptime_var(node.expr) { - expr_type = g.comptime.get_comptime_var_type(node.expr) + } else if node.expr is ast.Ident && g.comptime.is_comptime_var(node.expr) { + expr_type = g.comptime.get_type(node.expr) + name = g.styp(g.unwrap_generic(expr_type.clear_flags(.shared_f, .result))).replace('*', + '') + } else if node.expr is ast.SelectorExpr && node.expr.expr is ast.Ident + && g.comptime.is_comptime_var(node.expr.expr) { + expr_type = g.comptime_selector_type(node.expr) name = g.styp(g.unwrap_generic(expr_type.clear_flags(.shared_f, .result))).replace('*', '') } diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 747f4d864c..ca5f9e4e61 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1294,7 +1294,7 @@ fn (mut g Gen) gen_to_str_method_call(node ast.CallExpr) bool { } else if left_node is ast.Ident { if left_node.obj is ast.Var { if left_node.obj.ct_type_var != .no_comptime { - rec_type = g.comptime.get_comptime_var_type(left_node) + rec_type = g.comptime.get_type(left_node) g.gen_expr_to_string(left_node, rec_type) return true } else if left_node.obj.smartcasts.len > 0 { @@ -1456,7 +1456,7 @@ fn (mut g Gen) resolve_comptime_args(func &ast.Fn, mut node_ ast.CallExpr, concr if mut call_arg.expr.obj is ast.Var { node_.args[i].typ = call_arg.expr.obj.typ if call_arg.expr.obj.ct_type_var !in [.generic_var, .generic_param, .no_comptime] { - mut ctyp := g.comptime.get_comptime_var_type(call_arg.expr) + mut ctyp := g.comptime.get_type(call_arg.expr) if ctyp != ast.void_type { arg_sym := g.table.sym(ctyp) param_sym := g.table.final_sym(param_typ) @@ -1490,7 +1490,7 @@ fn (mut g Gen) resolve_comptime_args(func &ast.Fn, mut node_ ast.CallExpr, concr comptime_args[k] = ctyp } } else if call_arg.expr.obj.ct_type_var == .generic_param { - mut ctyp := g.comptime.get_comptime_var_type(call_arg.expr) + mut ctyp := g.comptime.get_type(call_arg.expr) if ctyp != ast.void_type { arg_sym := g.table.final_sym(call_arg.typ) param_typ_sym := g.table.sym(param_typ) @@ -1561,7 +1561,7 @@ fn (mut g Gen) resolve_comptime_args(func &ast.Fn, mut node_ ast.CallExpr, concr } } else if mut call_arg.expr.right is ast.Ident { if g.comptime.get_ct_type_var(call_arg.expr.right) != .generic_var { - mut ctyp := g.comptime.get_comptime_var_type(call_arg.expr.right) + mut ctyp := g.comptime.get_type(call_arg.expr.right) if ctyp != ast.void_type { comptime_args[k] = ctyp if param_typ.nr_muls() > 0 && comptime_args[k].nr_muls() > 0 { @@ -1658,7 +1658,7 @@ fn (mut g Gen) unwrap_receiver_type(node ast.CallExpr) (ast.Type, &ast.TypeSymbo if node.left.obj is ast.Var { if node.left.obj.smartcasts.len > 0 { if node.left.obj.ct_type_var == .smartcast { - unwrapped_rec_type = g.unwrap_generic(g.comptime.get_comptime_var_type(node.left)) + unwrapped_rec_type = g.unwrap_generic(g.comptime.get_type(node.left)) } else { unwrapped_rec_type = g.unwrap_generic(node.left.obj.smartcasts.last()) cast_sym := g.table.sym(unwrapped_rec_type) @@ -2171,7 +2171,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { } mut typ := node.args[0].typ if g.comptime.is_comptime_var(node.args[0].expr) { - ctyp := g.comptime.get_comptime_var_type(node.args[0].expr) + ctyp := g.comptime.get_type(node.args[0].expr) if ctyp != ast.void_type { typ = ctyp } @@ -2223,7 +2223,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { if cast_sym.info is ast.Aggregate { typ = cast_sym.info.types[g.aggregate_type_idx] } else if expr.obj.ct_type_var == .smartcast { - typ = g.unwrap_generic(g.comptime.get_comptime_var_type(expr)) + typ = g.unwrap_generic(g.comptime.get_type(expr)) } } // handling println( var or { ... }) @@ -2575,7 +2575,7 @@ fn (mut g Gen) call_args(node ast.CallExpr) { if i < node.expected_arg_types.len && node.expected_arg_types[i].has_flag(.generic) && arg.expr.obj.ct_type_var !in [.generic_param, .no_comptime] { exp_option := node.expected_arg_types[i].has_flag(.option) - expected_types[i] = g.unwrap_generic(g.comptime.get_comptime_var_type(arg.expr)) + expected_types[i] = g.unwrap_generic(g.comptime.get_type(arg.expr)) if !exp_option { expected_types[i] = expected_types[i].clear_flag(.option) } @@ -2609,7 +2609,7 @@ fn (mut g Gen) call_args(node ast.CallExpr) { } else if arg.expr is ast.ComptimeSelector && i < node.expected_arg_types.len && node.expected_arg_types[i].has_flag(.generic) { exp_option := node.expected_arg_types[i].has_flag(.option) - expected_types[i] = g.unwrap_generic(g.comptime.get_comptime_var_type(arg.expr)) + expected_types[i] = g.unwrap_generic(g.comptime.get_type(arg.expr)) if !exp_option { expected_types[i] = expected_types[i].clear_flag(.option) } @@ -2776,7 +2776,7 @@ fn (mut g Gen) keep_alive_call_postgen(node ast.CallExpr, tmp_cnt_save int) { @[inline] fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type ast.Type, lang ast.Language, is_smartcast bool) { arg_typ := if arg.expr is ast.ComptimeSelector { - g.unwrap_generic(g.comptime.get_comptime_var_type(arg.expr)) + g.unwrap_generic(g.comptime.get_type(arg.expr)) } else { g.unwrap_generic(arg.typ) } diff --git a/vlib/v/gen/c/for.v b/vlib/v/gen/c/for.v index b6398b7cc8..622a4cf766 100644 --- a/vlib/v/gen/c/for.v +++ b/vlib/v/gen/c/for.v @@ -144,7 +144,7 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) { if (node.cond is ast.Ident && g.comptime.is_comptime_var(node.cond)) || node.cond is ast.ComptimeSelector { mut unwrapped_typ := g.unwrap_generic(node.cond_type) - ctyp := g.comptime.get_comptime_var_type(node.cond) + ctyp := g.comptime.get_type(node.cond) if ctyp != ast.void_type { unwrapped_typ = g.unwrap_generic(ctyp) is_comptime = true @@ -223,7 +223,7 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) { if is_comptime && g.comptime.is_comptime_var(node.cond) { mut unwrapped_typ := g.unwrap_generic(node.cond_type) - ctyp := g.unwrap_generic(g.comptime.get_comptime_var_type(node.cond)) + ctyp := g.unwrap_generic(g.comptime.get_type(node.cond)) if ctyp != ast.void_type { unwrapped_typ = ctyp } diff --git a/vlib/v/gen/c/infix.v b/vlib/v/gen/c/infix.v index ea42633d0a..031b823723 100644 --- a/vlib/v/gen/c/infix.v +++ b/vlib/v/gen/c/infix.v @@ -96,13 +96,13 @@ fn (mut g Gen) infix_expr_arrow_op(node ast.InfixExpr) { // infix_expr_eq_op generates code for `==` and `!=` fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) { - left_type := if node.left is ast.ComptimeSelector { - g.comptime.get_comptime_var_type(node.left) + left_type := if g.comptime.is_comptime_expr(node.left) { + g.comptime.get_expr_type(node.left) } else { node.left_type } - right_type := if node.right is ast.ComptimeSelector { - g.comptime.get_comptime_var_type(node.right) + right_type := if g.comptime.is_comptime_expr(node.right) { + g.comptime.get_expr_type(node.right) } else { node.right_type } @@ -796,7 +796,7 @@ fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, left_type ast.Type, rig // infix_expr_is_op generates code for `is` and `!is` fn (mut g Gen) infix_expr_is_op(node ast.InfixExpr) { mut left_sym := if g.comptime.is_comptime_var(node.left) { - g.table.sym(g.unwrap_generic(g.comptime.get_comptime_var_type(node.left))) + g.table.sym(g.unwrap_generic(g.comptime.get_type(node.left))) } else { g.table.sym(node.left_type) } diff --git a/vlib/v/gen/c/str_intp.v b/vlib/v/gen/c/str_intp.v index b8807e2ad4..ad28c80d64 100644 --- a/vlib/v/gen/c/str_intp.v +++ b/vlib/v/gen/c/str_intp.v @@ -191,7 +191,7 @@ fn (mut g Gen) str_val(node ast.StringInterLiteral, i int, fmts []u8) { mut exp_typ := typ if expr is ast.Ident { if g.comptime.get_ct_type_var(expr) == .smartcast { - exp_typ = g.comptime.get_comptime_var_type(expr) + exp_typ = g.comptime.get_type(expr) } else if expr.obj is ast.Var { if expr.obj.smartcasts.len > 0 { exp_typ = g.unwrap_generic(expr.obj.smartcasts.last()) @@ -245,7 +245,7 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { mut fmts := node_.fmts.clone() for i, mut expr in node_.exprs { if g.comptime.is_comptime_var(expr) { - ctyp := g.comptime.get_comptime_var_type(expr) + ctyp := g.comptime.get_type(expr) if ctyp != ast.void_type { node_.expr_types[i] = ctyp if node_.fmts[i] == `_` { diff --git a/vlib/v/gen/c/struct.v b/vlib/v/gen/c/struct.v index f662fe3796..415f1d60ac 100644 --- a/vlib/v/gen/c/struct.v +++ b/vlib/v/gen/c/struct.v @@ -53,6 +53,7 @@ fn (mut g Gen) struct_init(node ast.StructInit) { if mut sym.info is ast.Struct { is_anon = sym.info.is_anon } + is_generic_default := sym.kind !in [.struct, .array_fixed] && node.typ.has_flag(.generic) // T{} is_array := sym.kind in [.array_fixed, .array] if sym.kind == .array_fixed { arr_info := sym.array_fixed_info() @@ -63,7 +64,7 @@ fn (mut g Gen) struct_init(node ast.StructInit) { // detect if we need type casting on msvc initialization const_msvc_init := g.is_cc_msvc && g.inside_const && !g.inside_cast && g.inside_array_item - if !g.inside_cinit && !is_anon && !is_array && !const_msvc_init { + if !g.inside_cinit && !is_anon && !is_generic_default && !is_array && !const_msvc_init { g.write('(') defer { g.write(')') @@ -128,6 +129,8 @@ fn (mut g Gen) struct_init(node ast.StructInit) { } } else if is_multiline { g.writeln('(${styp}){') + } else if is_generic_default { + g.write(g.type_default(node.typ)) } else { g.write('(${styp}){') } @@ -356,7 +359,7 @@ fn (mut g Gen) struct_init(node ast.StructInit) { g.indent-- } - if !initialized { + if !initialized && !is_generic_default { if nr_fields > 0 { g.write('0') } else { @@ -364,7 +367,7 @@ fn (mut g Gen) struct_init(node ast.StructInit) { } } - if !is_array_fixed_struct_init { + if !is_array_fixed_struct_init && !is_generic_default { g.write('}') } if g.is_shared && !g.inside_opt_data && !g.is_arraymap_set { diff --git a/vlib/v/tests/comptime/comptime_default_value_indexexpr_test.v b/vlib/v/tests/comptime/comptime_default_value_indexexpr_test.v new file mode 100644 index 0000000000..b35c53345b --- /dev/null +++ b/vlib/v/tests/comptime/comptime_default_value_indexexpr_test.v @@ -0,0 +1,56 @@ +module main + +struct Decoder { + json string +} + +pub fn decode[T](val string) !T { + mut decoder := Decoder{ + json: val + } + + mut result := T{} + decoder.decode_value(mut &result)! + return result +} + +fn (mut decoder Decoder) decode_value[T](mut val T) ! { + $if T.unaliased_typ is $array { + mut array_element := create_array_element(val) + + decoder.decode_value(mut array_element)! + + val << array_element + + assert val.len == 1 + assert val[0] == array_element + } $else $if T.unaliased_typ is $map { + mut map_value := create_map_value(val) + + decoder.decode_value(mut map_value)! + + val['key'] = map_value + + assert val.len == 1 + assert val['key'] == map_value + } +} + +fn create_array_element[T](array []T) T { + return T{} +} + +fn create_map_value[K, V](map_ map[K]V) V { + return V{} +} + +fn test_main() { + assert decode[[]int]('[1, 2, 3]')! == [0] + assert decode[[]string]('["1", "2", "3"]')! == [''] + assert decode[map[string]int]('{"a": 1}')! == { + 'key': 0 + } + assert decode[map[string]string]('{"val": "2"}')! == { + 'key': '' + } +} diff --git a/vlib/v/tests/comptime/comptime_default_value_test.v b/vlib/v/tests/comptime/comptime_default_value_test.v new file mode 100644 index 0000000000..83bc2bea0c --- /dev/null +++ b/vlib/v/tests/comptime/comptime_default_value_test.v @@ -0,0 +1,64 @@ +module main + +struct Decoder { + json string +} + +pub fn decode[T](val string) !T { + mut decoder := Decoder{ + json: val + } + + mut result := T{} + decoder.decode_value(mut &result)! + return result +} + +fn (mut decoder Decoder) decode_value[T](mut val T) ! { + $if T is $array { + mut array_element := create_array_element(val) + + decoder.decode_value(mut array_element)! + println(array_element) + dump(array_element) + assert &array_element != unsafe { nil } + + val << array_element + } $else $if T is $map { + mut map_value := create_map_value(val) + + decoder.decode_value(mut map_value)! + println(map_value) + dump(map_value) + assert &map_value != unsafe { nil } + + val['key'] = map_value + } +} + +fn create_array_element[T](array []T) T { + a := T{} + dump(a) + dump(a.str) + assert a.str != unsafe { nil } + return a +} + +fn create_map_value[K, V](map_ map[K]V) V { + a := V{} + dump(a) + dump(a.str) + assert a.str != unsafe { nil } + return a +} + +fn test_main() { + assert decode[[]string]('["1", "2", "3"]')! == [''] + assert decode[[]int]('[1, 2, 3]')! == [0] + assert decode[map[string]string]('{"val": "2"}')! == { + 'key': '' + } + assert decode[map[string]int]('{"a": 1}')! == { + 'key': 0 + } +}