From 8cad25e0947448ebcf5a1018e850c2a21eaa9574 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sun, 19 Apr 2026 15:48:33 +0300 Subject: [PATCH] all: fix more tests --- vlib/flag/flag_to.v | 294 ++++++++---------- vlib/v/ast/ast.v | 19 +- vlib/v/ast/scope.v | 5 - vlib/v/ast/types.v | 16 + vlib/v/checker/assign.v | 6 + vlib/v/checker/checker.v | 30 +- vlib/v/checker/comptime.v | 14 +- vlib/v/checker/if.v | 45 ++- vlib/v/checker/orm.v | 5 +- vlib/v/fmt/fmt.v | 18 +- vlib/v/gen/c/assign.v | 10 +- vlib/v/gen/c/cgen.v | 364 +++++++++++++++-------- vlib/v/gen/c/cheaders.v | 140 ++++++++- vlib/v/gen/c/comptime.v | 17 +- vlib/v/gen/c/dumpexpr.v | 3 +- vlib/v/gen/c/fn.v | 28 ++ vlib/v/gen/c/orm.v | 41 ++- vlib/v/gen/c/str_intp.v | 49 +-- vlib/v/generics/generics.v | 7 +- vlib/v/markused/walker.v | 13 + vlib/v/parser/orm.v | 1 + vlib/v/type_resolver/comptime_resolver.v | 8 +- 22 files changed, 709 insertions(+), 424 deletions(-) diff --git a/vlib/flag/flag_to.v b/vlib/flag/flag_to.v index f3ea3cf0a..de67b1066 100644 --- a/vlib/flag/flag_to.v +++ b/vlib/flag/flag_to.v @@ -1,5 +1,7 @@ module flag +import v.ast + struct FlagData { raw string @[required] field_name string @[required] @@ -64,6 +66,115 @@ struct StructField { doc string // documentation string sat via `@[xdoc: x y z]` } +fn assign_single_flag_value[T](mut target T, default_value T, f FlagData, struct_name string, field_name string) ! { + _ = target + a_or_r := f.arg or { '${f.repeats}' } + $if T is int { + if !a_or_r.is_int() { + return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field_name}`') + } + target = a_or_r.int() + } $else $if T is i64 { + if !a_or_r.is_int() { + return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field_name}`') + } + target = a_or_r.i64() + } $else $if T is u64 { + if !a_or_r.is_int() { + return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field_name}`') + } + target = a_or_r.u64() + } $else $if T is i32 { + if !a_or_r.is_int() { + return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field_name}`') + } + target = a_or_r.i32() + } $else $if T is u32 { + if !a_or_r.is_int() { + return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field_name}`') + } + target = a_or_r.u32() + } $else $if T is i16 { + if !a_or_r.is_int() { + return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field_name}`') + } + target = a_or_r.i16() + } $else $if T is u16 { + if !a_or_r.is_int() { + return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field_name}`') + } + target = a_or_r.u16() + } $else $if T is i8 { + if !a_or_r.is_int() { + return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field_name}`') + } + target = a_or_r.i8() + } $else $if T is u8 { + if !a_or_r.is_int() { + return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field_name}`') + } + target = a_or_r.u8() + } $else $if T is f32 { + target = f.arg or { return error('failed appending ${f.raw} to ${field_name}') } + .f32() + } $else $if T is f64 { + target = f.arg or { return error('failed appending ${f.raw} to ${field_name}') } + .f64() + } $else $if T is bool { + if arg := f.arg { + return error('can not assign `${arg}` to bool field `${field_name}`') + } + target = !default_value + } $else $if T is string { + target = f.arg or { return error('failed appending ${f.raw} to ${field_name}') } + .str() + } $else { + return error('field type: ${T.name} for ${field_name} is not supported') + } +} + +fn append_multi_flag_value[T](mut target T, f FlagData, field_name string) ! { + $if T is []string { + target << f.arg or { return error('failed appending ${f.raw} to ${field_name}') } + .str() + } $else $if T is []int { + target << f.arg or { return error('failed appending ${f.raw} to ${field_name}') } + .int() + } $else $if T is []i64 { + target << f.arg or { return error('failed appending ${f.raw} to ${field_name}') } + .i64() + } $else $if T is []u64 { + target << f.arg or { return error('failed appending ${f.raw} to ${field_name}') } + .u64() + } $else $if T is []i32 { + target << f.arg or { return error('failed appending ${f.raw} to ${field_name}') } + .i32() + } $else $if T is []u32 { + target << f.arg or { return error('failed appending ${f.raw} to ${field_name}') } + .u32() + } $else $if T is []i16 { + target << f.arg or { return error('failed appending ${f.raw} to ${field_name}') } + .i16() + } $else $if T is []u16 { + target << f.arg or { return error('failed appending ${f.raw} to ${field_name}') } + .u16() + } $else $if T is []i8 { + target << f.arg or { return error('failed appending ${f.raw} to ${field_name}') } + .i8() + } $else $if T is []u8 { + target << f.arg or { return error('failed appending ${f.raw} to ${field_name}') } + .u8() + } $else $if T is []f32 { + target << f.arg or { return error('failed appending ${f.raw} to ${field_name}') } + .f32() + } $else $if T is []f64 { + target << f.arg or { return error('failed appending ${f.raw} to ${field_name}') } + .f64() + } $else { + return error('field type: ${T.name} for multi value ${field_name} is not supported') + } +} + fn (sf StructField) shortest_match_name() ?string { mut name := sf.short // if `short` is sat it takes precedence over `match_name` if name == '' && sf.match_name.len == 1 { @@ -240,23 +351,17 @@ fn (fm FlagMapper) get_struct_info[T]() !StructInfo { trace_println('\tmatch name: "${match_name}"') used_names << match_name - $if field.typ is int { - hints.set(.is_int_type) - } $else $if field.typ is i64 { - hints.set(.is_int_type) - } $else $if field.typ is u64 { - hints.set(.is_int_type) - } $else $if field.typ is i32 { - hints.set(.is_int_type) - } $else $if field.typ is u32 { - hints.set(.is_int_type) - } $else $if field.typ is i16 { - hints.set(.is_int_type) - } $else $if field.typ is u16 { - hints.set(.is_int_type) - } $else $if field.typ is i8 { - hints.set(.is_int_type) - } $else $if field.typ is u8 { + if field.typ in [ + int(ast.int_type), + int(ast.i64_type), + int(ast.u64_type), + int(ast.i32_type), + int(ast.u32_type), + int(ast.i16_type), + int(ast.u16_type), + int(ast.i8_type), + int(ast.u8_type), + ] { hints.set(.is_int_type) } @@ -270,11 +375,11 @@ fn (fm FlagMapper) get_struct_info[T]() !StructInfo { hints.set(.is_ignore) } - $if field.typ is bool { + if field.typ == int(ast.bool_type) { trace_println('\tfield "${field.name}" is a bool') hints.set(.is_bool) } - $if field.is_array { + if field.is_array { trace_println('\tfield "${field.name}" is array') hints.set(.is_array) } @@ -897,156 +1002,11 @@ pub fn (fm FlagMapper) to_struct[T](defaults ?T) !T { struct_name := T.name.all_after_last('.') $for field in T.fields { if f := fm.field_map_flag[field.name] { - a_or_r := f.arg or { '${f.repeats}' } - $if field.typ is int { - // TODO: find a way to move this kind of duplicate code out - if !a_or_r.is_int() { - return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field.name}`') - } - trace_dbg_println('${@FN}: assigning (int) ${struct_name}.${field.name} = ${a_or_r}') - result.$(field.name) = a_or_r.int() - } $else $if field.typ is i64 { - trace_dbg_println('${@FN}: assigning (i64) ${struct_name}.${field.name} = ${a_or_r}') - if !a_or_r.is_int() { - return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field.name}`') - } - result.$(field.name) = a_or_r.i64() - } $else $if field.typ is u64 { - trace_dbg_println('${@FN}: assigning (u64) ${struct_name}.${field.name} = ${a_or_r}') - if !a_or_r.is_int() { - return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field.name}`') - } - result.$(field.name) = a_or_r.u64() - } $else $if field.typ is i32 { - trace_dbg_println('${@FN}: assigning (i32) ${struct_name}.${field.name} = ${a_or_r}') - if !a_or_r.is_int() { - return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field.name}`') - } - result.$(field.name) = a_or_r.i32() - } $else $if field.typ is u32 { - trace_dbg_println('${@FN}: assigning (u32) ${struct_name}.${field.name} = ${a_or_r}') - if !a_or_r.is_int() { - return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field.name}`') - } - result.$(field.name) = a_or_r.u32() - } $else $if field.typ is i16 { - trace_dbg_println('${@FN}: assigning (i16) ${struct_name}.${field.name} = ${a_or_r}') - if !a_or_r.is_int() { - return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field.name}`') - } - result.$(field.name) = a_or_r.i16() - } $else $if field.typ is u16 { - trace_dbg_println('${@FN}: assigning (u16) ${struct_name}.${field.name} = ${a_or_r}') - if !a_or_r.is_int() { - return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field.name}`') - } - result.$(field.name) = a_or_r.u16() - } $else $if field.typ is i8 { - trace_dbg_println('${@FN}: assigning (i8) ${struct_name}.${field.name} = ${a_or_r}') - if !a_or_r.is_int() { - return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field.name}`') - } - result.$(field.name) = a_or_r.i8() - } $else $if field.typ is u8 { - trace_dbg_println('${@FN}: assigning (u8) ${struct_name}.${field.name} = ${a_or_r}') - if !a_or_r.is_int() { - return error('can not assign non-integer value `${a_or_r}` from flag `${f.raw}` to `${struct_name}.${field.name}`') - } - result.$(field.name) = a_or_r.u8() - } $else $if field.typ is f32 { - result.$(field.name) = f.arg or { - return error('failed appending ${f.raw} to ${field.name}') - } - .f32() - } $else $if field.typ is f64 { - result.$(field.name) = f.arg or { - return error('failed appending ${f.raw} to ${field.name}') - } - .f64() - } $else $if field.typ is bool { - if arg := f.arg { - return error('can not assign `${arg}` to bool field `${field.name}`') - } - result.$(field.name) = !the_default.$(field.name) - } $else $if field.typ is string { - trace_dbg_println('${@FN}: assigning (string) ${struct_name}.${field.name} = ${f.arg or { - 'ERROR' - } - .str()}') - result.$(field.name) = f.arg or { - return error('failed appending ${f.raw} to ${field.name}') - } - .str() - } $else { - return error('field type: ${field.typ} for ${field.name} is not supported') - } + assign_single_flag_value(mut result.$(field.name), the_default.$(field.name), f, + struct_name, field.name)! } for f in fm.array_field_map_flag[field.name] { - // trace_println('${@FN}: appending ${field.name} << ${f.arg}') - $if field.typ is []string { - // TODO: find a way to move this kind of duplicate code out - result.$(field.name) << f.arg or { - return error('failed appending ${f.raw} to ${field.name}') - } - .str() - } $else $if field.typ is []int { - result.$(field.name) << f.arg or { - return error('failed appending ${f.raw} to ${field.name}') - } - .int() - } $else $if field.typ is []i64 { - result.$(field.name) << f.arg or { - return error('failed appending ${f.raw} to ${field.name}') - } - .i64() - } $else $if field.typ is []u64 { - result.$(field.name) << f.arg or { - return error('failed appending ${f.raw} to ${field.name}') - } - .u64() - } $else $if field.typ is []i32 { - result.$(field.name) << f.arg or { - return error('failed appending ${f.raw} to ${field.name}') - } - .i32() - } $else $if field.typ is []u32 { - result.$(field.name) << f.arg or { - return error('failed appending ${f.raw} to ${field.name}') - } - .u32() - } $else $if field.typ is []i16 { - result.$(field.name) << f.arg or { - return error('failed appending ${f.raw} to ${field.name}') - } - .i16() - } $else $if field.typ is []u16 { - result.$(field.name) << f.arg or { - return error('failed appending ${f.raw} to ${field.name}') - } - .u16() - } $else $if field.typ is []i8 { - result.$(field.name) << f.arg or { - return error('failed appending ${f.raw} to ${field.name}') - } - .i8() - } $else $if field.typ is []u8 { - result.$(field.name) << f.arg or { - return error('failed appending ${f.raw} to ${field.name}') - } - .u8() - } $else $if field.typ is []f32 { - result.$(field.name) << f.arg or { - return error('failed appending ${f.raw} to ${field.name}') - } - .f32() - } $else $if field.typ is []f64 { - result.$(field.name) << f.arg or { - return error('failed appending ${f.raw} to ${field.name}') - } - .f64() - } $else { - return error('field type: ${field.typ} for multi value ${field.name} is not supported') - } + append_multi_flag_value(mut result.$(field.name), f, field.name)! } } } $else { diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index ff80cffd8..ed18d5817 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -2355,7 +2355,7 @@ pub mut: updated_columns []string // for `update set x=y` table_expr TypeNode fields []StructField - sub_structs map[int]SqlStmtLine + sub_structs map[string]SqlStmtLine where_expr Expr update_exprs []Expr // for `update` update_data_expr Expr @@ -2410,6 +2410,7 @@ pub: pos token.Pos pub mut: typ Type + scope &Scope = unsafe { nil } db_expr Expr // `db` in `sql db {` where_expr Expr order_expr Expr @@ -2618,10 +2619,10 @@ pub fn (expr Expr) is_pure_literal() bool { pub fn (expr Expr) is_auto_deref_var() bool { return match expr { Ident { - if expr.obj is Var { - expr.obj.is_auto_deref - } else { - false + obj := expr.obj + match obj { + Var { obj.is_auto_deref } + else { false } } } PrefixExpr { @@ -2636,10 +2637,10 @@ pub fn (expr Expr) is_auto_deref_var() bool { pub fn (expr Expr) is_auto_deref_arg() bool { return match expr { Ident { - if expr.obj is Var { - expr.obj.is_auto_deref && expr.obj.is_arg - } else { - false + obj := expr.obj + match obj { + Var { obj.is_auto_deref && obj.is_arg } + else { false } } } else { diff --git a/vlib/v/ast/scope.v b/vlib/v/ast/scope.v index 186481e89..49c464a0d 100644 --- a/vlib/v/ast/scope.v +++ b/vlib/v/ast/scope.v @@ -197,11 +197,6 @@ pub fn (mut s Scope) reset_smartcasts(name string) { // selector_expr: name.field_name pub fn (mut s Scope) register_struct_field(name string, field ScopeStructField) { k := '${name}.${field.name}' - if f := s.struct_fields[k] { - if f.struct_type == field.struct_type { - return - } - } s.struct_fields[k] = field } diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index 44d4ecef6..517a9afc7 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -138,6 +138,22 @@ pub mut: align int = -1 } +pub fn (sym TypeSymbol) aggregate_variant_type(idx int) Type { + info := sym.info + return match info { + Aggregate { + if idx >= 0 && idx < info.types.len { + info.types[idx] + } else { + Type(0) + } + } + else { + Type(0) + } + } +} + // max of 8 pub enum TypeFlag as u32 { option = 1 << 24 diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index 0a9420c16..c933496bf 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -32,6 +32,9 @@ fn (mut c Checker) smartcasted_assign_lhs_type(expr ast.Expr, fallback_type ast. match expr { ast.Ident { if expr.obj is ast.Var && expr.obj.smartcasts.len > 0 { + if expr.obj.orig_type.has_flag(.option) { + return expr.obj.orig_type + } if expr.obj.is_mut && expr.obj.orig_type != 0 { orig_sym := c.table.final_sym(expr.obj.orig_type) if orig_sym.kind == .sum_type { @@ -47,6 +50,9 @@ fn (mut c Checker) smartcasted_assign_lhs_type(expr ast.Expr, fallback_type ast. scope_field := expr.scope.find_struct_field(smartcast_selector_expr_str(expr), expr.expr_type, expr.field_name) if scope_field != unsafe { nil } && scope_field.smartcasts.len > 0 { + if scope_field.orig_type.has_flag(.option) { + return scope_field.orig_type + } return c.exposed_smartcast_type(scope_field.orig_type, scope_field.smartcasts.last(), scope_field.is_mut) } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 34aa63897..960faef53 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1843,8 +1843,27 @@ fn (mut c Checker) fail_if_immutable(mut expr ast.Expr) (string, token.Pos) { } scope_field := expr.scope.find_struct_field(smartcast_selector_expr_str(expr), expr.expr_type, expr.field_name) + mut selector_smartcast_is_mut := false + if scope_field != unsafe { nil } { + selector_smartcast_is_mut = scope_field.is_mut + } + if !selector_smartcast_is_mut { + expr_sym := c.table.sym(expr.expr_type) + if field := c.table.find_field(expr_sym, expr.field_name) { + if field.is_mut { + if root_ident := expr.root_ident() { + selector_smartcast_is_mut = root_ident.is_mut() + if !selector_smartcast_is_mut { + if v := expr.scope.find_var(root_ident.name) { + selector_smartcast_is_mut = v.is_mut + } + } + } + } + } + } if scope_field != unsafe { nil } && scope_field.smartcasts.len > 0 - && !scope_field.is_mut && !c.pref.translated && !c.file.is_translated + && !selector_smartcast_is_mut && !c.pref.translated && !c.file.is_translated && !c.inside_unsafe { expr_str := expr.str() c.error('cannot mutate `${expr_str}` in a non-mut smartcast, use `if mut ${expr_str} ...`', @@ -6935,8 +6954,9 @@ fn (mut c Checker) smartcast(mut expr ast.Expr, cur_type ast.Type, to_type_ ast. if field := c.table.find_field(expr_sym, expr.field_name) { if field.is_mut { if root_ident := expr.root_ident() { + is_mut = root_ident.is_mut() if v := scope.find_var(root_ident.name) { - is_mut = v.is_mut + is_mut = is_mut || v.is_mut } } } @@ -7458,6 +7478,12 @@ fn (mut c Checker) mark_as_referenced(mut node ast.Expr, as_interface bool) { obj.is_auto_heap = true } } + + if as_interface { + for method in type_sym.methods { + c.mark_fn_decl_as_referenced(method.fkey()) + } + } } } } diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index c68086122..edace07ea 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -475,14 +475,15 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { } c.comptime.comptime_for_field_value = field c.comptime.comptime_for_field_var = node.val_var + resolved_field_typ := c.unwrap_generic(field.typ) c.type_resolver.update_ct_type(node.val_var, c.field_data_type) - c.type_resolver.update_ct_type('${node.val_var}.typ', node.typ) - c.comptime.comptime_for_field_type = field.typ + c.type_resolver.update_ct_type('${node.val_var}.typ', resolved_field_typ) + c.comptime.comptime_for_field_type = resolved_field_typ c.comptime.has_different_types = has_different_types c.stmts(mut node.stmts) - if field.typ != ast.no_type { - unwrapped_expr_type := c.unwrap_generic(field.typ) + if resolved_field_typ != ast.no_type { + unwrapped_expr_type := c.unwrap_generic(resolved_field_typ) tsym := c.table.sym(unwrapped_expr_type) c.markused_comptimefor(mut node, unwrapped_expr_type) if tsym.kind == .array_fixed { @@ -604,11 +605,6 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { c.comptime.comptime_for_variant_var = node.val_var c.type_resolver.update_ct_type(node.val_var, c.variant_data_type) c.type_resolver.update_ct_type('${node.val_var}.typ', variant) - $if trace_ci_fixes ? { - if c.file.path.contains('decode_sumtype.v') { - eprintln('comptime variants val_var=${node.val_var} variant=${c.table.type_to_str(variant)} sumtype=${c.table.type_to_str(typ)}') - } - } c.stmts(mut node.stmts) c.pop_comptime_info() } diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v index bbd1db34f..0c70adea6 100644 --- a/vlib/v/checker/if.v +++ b/vlib/v/checker/if.v @@ -655,15 +655,16 @@ fn (mut c Checker) smartcast_if_conds(mut node ast.Expr, mut scope ast.Scope, co } } } else { + allow_mut_selector_smartcast := node.left is ast.SelectorExpr is_smartcast_ident := node.left is ast.Ident && c.comptime.get_ct_type_var(node.left) == .smartcast if is_smartcast_ident { node.left_type = c.type_resolver.get_type(node.left) c.smartcast(mut node.left, node.left_type, node.left_type.clear_flag(.option), mut - scope, true, true, false, false) + scope, true, true, allow_mut_selector_smartcast, true) } else { c.smartcast(mut node.left, node.left_type, node.left_type.clear_flag(.option), mut - scope, false, true, false, false) + scope, false, true, allow_mut_selector_smartcast, true) } } } else if node.op == .key_is { @@ -698,26 +699,6 @@ fn (mut c Checker) smartcast_if_conds(mut node ast.Expr, mut scope ast.Scope, co } if right_type != ast.no_type { - $if trace_ci_fixes ? { - source_path := if node.pos.file_idx >= 0 - && node.pos.file_idx < c.table.filelist.len { - c.table.filelist[node.pos.file_idx] - } else { - c.file.path - } - if source_path.contains('decode_sumtype.v') - && node.pos.line_nr + 1 in [12, 111, 138, 215] { - right_kind := match right_expr { - ast.Ident { 'ident:${right_expr.name}' } - ast.TypeNode { 'typenode:${c.table.type_to_str(right_expr.typ)}' } - ast.None { 'none' } - else { typeof(right_expr).name } - } - - eprintln('if is left=${node.left} left_type=${c.table.type_to_str(node.left_type)} right_kind=${right_kind} right_type=${c.table.type_to_str(right_type)} variant_var=${c.comptime.comptime_for_variant_var} file=${c.file.path} source=${source_path} line=${ - node.pos.line_nr + 1}') - } - } right_sym := c.table.sym(right_type) mut expr_type := c.unwrap_generic(node.left_type) left_sym := c.table.sym(expr_type) @@ -762,8 +743,10 @@ fn (mut c Checker) smartcast_if_conds(mut node ast.Expr, mut scope ast.Scope, co && (node.left as ast.Ident).name == c.comptime.comptime_for_field_var if !skip_smartcast && (left_final_sym.kind in [.interface, .sum_type] || is_option_unwrap) { + allow_mut_selector_smartcast := node.left is ast.SelectorExpr c.smartcast(mut node.left, node.left_type, right_type, mut scope, - is_comptime, is_option_unwrap, false, false) + is_comptime, is_option_unwrap, allow_mut_selector_smartcast, + allow_mut_selector_smartcast) } } } @@ -785,8 +768,9 @@ fn (mut c Checker) smartcast_if_conds(mut node ast.Expr, mut scope ast.Scope, co is_option_unwrap := left_type.has_flag(.option) && !right_type.has_flag(.option) if left_sym.kind in [.interface, .sum_type] || is_option_unwrap { mut left_expr := first_cond.left + allow_mut_selector_smartcast := left_expr is ast.SelectorExpr c.smartcast(mut left_expr, left_type, right_type, mut scope, false, - is_option_unwrap, false, false) + is_option_unwrap, allow_mut_selector_smartcast, allow_mut_selector_smartcast) } } c.smartcast_none_guard_unwrap(control_expr.branches[0].cond, mut scope) @@ -804,13 +788,18 @@ fn (mut c Checker) smartcast_none_guard_unwrap(cond ast.Expr, mut scope ast.Scop return } to_type := cond_expr.left_type.clear_flag(.option) + allow_mut_selector_smartcast := match cond_expr.left { + ast.SelectorExpr { true } + else { false } + } + if c.comptime.get_ct_type_var(cond_expr.left) == .smartcast { cond_expr.left_type = c.type_resolver.get_type(cond_expr.left) c.smartcast(mut cond_expr.left, cond_expr.left_type, to_type, mut scope, true, true, - false, false) + allow_mut_selector_smartcast, true) } else { c.smartcast(mut cond_expr.left, cond_expr.left_type, to_type, mut scope, false, true, - false, false) + allow_mut_selector_smartcast, true) } } } @@ -834,10 +823,10 @@ fn (mut c Checker) smartcast_none_guard_fallthrough(cond ast.Expr, mut scope ast if c.comptime.get_ct_type_var(cond_expr.left) == .smartcast { cond_expr.left_type = c.type_resolver.get_type(cond_expr.left) c.smartcast(mut cond_expr.left, cond_expr.left_type, to_type, mut scope, true, - true, false, false) + true, false, true) } else { c.smartcast(mut cond_expr.left, cond_expr.left_type, to_type, mut scope, false, - true, false, false) + true, false, true) } } } diff --git a/vlib/v/checker/orm.v b/vlib/v/checker/orm.v index 1a1f79a91..cb4d4a5e9 100644 --- a/vlib/v/checker/orm.v +++ b/vlib/v/checker/orm.v @@ -335,6 +335,7 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type { has_where: true where_expr: ast.None{} typ: field.typ.clear_flag(.option).set_flag(.result) + scope: c.fn_scope db_expr: node.db_expr table_expr: ast.TypeNode{ pos: node.table_expr.pos @@ -583,7 +584,7 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type { } fields = insert_fields.clone() - mut sub_structs := map[int]ast.SqlStmtLine{} + mut sub_structs := map[string]ast.SqlStmtLine{} non_primitive_fields := c.get_orm_non_primitive_fields(fields) for field in non_primitive_fields { @@ -619,7 +620,7 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type { tmp_inside_sql := c.inside_sql c.sql_stmt_line(mut subquery_expr) c.inside_sql = tmp_inside_sql - sub_structs[field.typ] = subquery_expr + sub_structs[field.name] = subquery_expr } node.fields = fields diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 1c0dc7184..2d442cd2f 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -13,6 +13,18 @@ const break_points = [0, 35, 60, 85, 93, 100]! // when to break a line depending const max_len = break_points[break_points.len - 1] const bs = '\\' +fn call_arg_spread_str(arg ast.CallArg) string { + return match arg.expr { + ast.ArrayDecompose { + decompose := arg.expr as ast.ArrayDecompose + '...${decompose.expr.str()}' + } + else { + arg.str() + } + } +} + @[minify] pub struct Fmt { pub: @@ -2383,11 +2395,7 @@ pub fn (mut f Fmt) comptime_call(node ast.ComptimeCall) { inner_args := if node.args_var != '' { node.args_var } else { - node.args.map(if it.expr is ast.ArrayDecompose { - '...${it.expr.expr.str()}' - } else { - it.str() - }).join(', ') + node.args.map(call_arg_spread_str).join(', ') } method_expr := if node.has_parens { '(${node.method_name}(${inner_args}))' diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index 3b6fff4f8..6491273c0 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -156,16 +156,16 @@ fn assign_expr_unwraps_option_or_result(expr ast.Expr) bool { } fn (mut g Gen) decl_assign_struct_init_needs_tmp(expr ast.Expr) bool { - node := match expr { + mut node := ast.StructInit{} + match expr { ast.StructInit { - expr + node = expr } ast.ParExpr { - if expr.expr is ast.StructInit { - expr.expr - } else { + if expr.expr !is ast.StructInit { return false } + node = expr.expr as ast.StructInit } else { return false diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index a54c70287..a6534a519 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -4017,6 +4017,11 @@ fn (g &Gen) interface_cast_requires_field_aliasing(expected_type ast.Type, expr } } +fn (mut g Gen) fn_ptr_cast_typ(func ast.FnType) string { + ptr_name := '__v_fn_ptr' + return g.fn_ptr_decl_str(func, ptr_name).replace_once(ptr_name, '') +} + fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp ast.Type, got ast.Type, exp_styp string, got_is_ptr bool, got_is_fn bool, got_styp string) { mut rparen_n := 1 @@ -4025,6 +4030,16 @@ fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp ast.Ty is_not_ptr_and_fn := !got_is_ptr && !got_is_fn is_sumtype_cast := !got_is_fn && fname.contains('_to_sumtype_') is_interface_cast := !got_is_fn && fname.contains('_to_Interface_') + interface_cast_source_expr := if is_interface_cast && expr is ast.CastExpr { + expr.expr + } else { + expr + } + expr_is_lvalue := if is_interface_cast { + interface_cast_source_expr.is_lvalue() + } else { + expr.is_lvalue() + } is_comptime_variant := is_not_ptr_and_fn && expr is ast.Ident && g.comptime.is_comptime_variant_var(expr) mut is_stack_rooted_interface_expr := false @@ -4047,24 +4062,24 @@ fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp ast.Ty is_primitive_to_interface := is_interface_cast && expr is ast.Ident && g.table.sym(got).kind in [.i8, .i16, .i32, .int, .i64, .isize, .u8, .u16, .u32, .u64, .usize, .f32, .f64, .bool, .rune, .string] preserve_interface_field_aliasing := is_interface_cast - && g.interface_cast_requires_field_aliasing(exp, expr) + && g.interface_cast_requires_field_aliasing(exp, interface_cast_source_expr) // Interface casts must not store pointers into stack-rooted lvalues such as // local variables or fields on by-value fn args (`p.email`). is_stack_rooted_interface_expr = is_interface_cast && !preserve_interface_field_aliasing - && g.interface_expr_needs_heap(expr) + && g.interface_expr_needs_heap(interface_cast_source_expr) // Interface casts must not store pointers into expressions without a // stable address, including stack-rooted lvalues and slice results. needs_interface_cast_promotion := is_interface_cast && !preserve_interface_field_aliasing - && g.expr_needs_heap_promotion_for_interface_cast(expr) + && g.expr_needs_heap_promotion_for_interface_cast(interface_cast_source_expr) // Value-to-interface conversions should preserve value semantics by storing a detached copy. // The only exception is an explicit mutable borrow, which must keep aliasing the original. // Interfaces with mutable fields also need to alias the original lvalue so field reads/writes // stay connected to the concrete value instead of a detached clone. needs_interface_value_copy := is_interface_cast && !preserve_interface_field_aliasing - && !g.expected_arg_mut && !got_is_ptr && expr.is_lvalue() + && !g.expected_arg_mut && !got_is_ptr && expr_is_lvalue - if !is_cast_fixed_array_init && (is_comptime_variant || !expr.is_lvalue() + if !is_cast_fixed_array_init && (is_comptime_variant || !expr_is_lvalue || (expr is ast.Ident && (expr.obj.is_simple_define_const() || (expr.obj is ast.Var && expr.obj.is_index_var))) || is_primitive_to_interface || needs_interface_cast_promotion @@ -4374,13 +4389,6 @@ fn (mut g Gen) widen_small_int_infix_for_cast(expr ast.InfixExpr, got_type ast.T // use instead of expr() when you need to cast to a different type fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_type ast.Type) { got_type := ast.mktyp(got_type_raw) - $if trace_ci_fixes ? { - if g.file.path.contains('binary_search_tree.v') && expr is ast.SelectorExpr { - if expr.expr is ast.Ident && expr.expr.name == 'tree' { - eprintln('expr_with_cast expr=${expr.expr.name}.${expr.field_name} got=${g.table.type_to_str(got_type)} expected=${g.table.type_to_str(expected_type)} got_ptr=${got_type.is_ptr()}') - } - } - } exp_sym := g.table.final_sym(expected_type) got_sym := g.table.sym(got_type) expected_is_ptr := expected_type.is_ptr() @@ -4455,11 +4463,18 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ } } // Auto-heap variables are already pointers in C. Treat them as - // pointer-backed here to avoid wrapping the raw pointer in an - // extra HEAP(...) and to suppress the usual auto-deref in g.expr(). + // pointer-backed only when interface semantics require aliasing the + // original storage. Plain value-to-interface conversions still need + // a detached copy, even when the value itself lives in auto-heap storage. + // The exception is values that already came from option/result unwrapping: + // those are already detached heap objects and should be passed through + // directly instead of being wrapped in HEAP(...) a second time. mut effective_got_is_ptr := got_is_ptr mut suppress_auto_heap_deref := false - if !got_is_ptr && expr is ast.Ident && g.resolved_ident_is_auto_heap(expr) { + if !got_is_ptr && expr is ast.Ident && g.resolved_ident_is_auto_heap(expr) + && (g.expected_arg_mut + || g.interface_cast_requires_field_aliasing(expected_type, expr) + || g.ident_unwraps_option_or_result(expr)) { effective_got_is_ptr = true suppress_auto_heap_deref = true } @@ -4542,13 +4557,15 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ } } } - if is_already_sum_type && !g.inside_return { + if is_already_sum_type { // Don't create a new sum type wrapper if there is already one g.prevent_sum_type_unwrapping_once = true g.expr(expr) } else { - if mut sumtype_got_sym.info is ast.Aggregate { - sumtype_got_type = sumtype_got_sym.info.types[g.aggregate_type_idx] + sumtype_got_sym_val := *sumtype_got_sym + variant_typ := sumtype_got_sym_val.aggregate_variant_type(g.aggregate_type_idx) + if variant_typ != 0 { + sumtype_got_type = variant_typ sumtype_got_sym = g.table.sym(sumtype_got_type) sumtype_got_styp = g.styp(sumtype_got_type) sumtype_got_is_fn = sumtype_got_sym.kind == .function @@ -5903,9 +5920,10 @@ fn (mut g Gen) resolve_typeof_expr_type(expr ast.Expr, default_type ast.Type) as if expr.obj is ast.Var { if expr.obj.smartcasts.len > 0 { mut typ := g.unwrap_generic(expr.obj.smartcasts.last()) - cast_sym := g.table.sym(typ) - if cast_sym.info is ast.Aggregate { - typ = cast_sym.info.types[g.aggregate_type_idx] + cast_sym := *g.table.sym(typ) + variant_typ := cast_sym.aggregate_variant_type(g.aggregate_type_idx) + if variant_typ != 0 { + typ = variant_typ } else if expr.obj.ct_type_var == .smartcast { typ = g.unwrap_generic(g.type_resolver.get_type(expr)) } @@ -6209,24 +6227,17 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { selector_scope = g.file.scope.innermost(node.pos.pos) } mut selector_expr_expr := node.expr - mut is_sumtype_smartcast_expr_ptr := false - // Track if the ident's original type is a sumtype/interface and it has - // smartcasts; the ident codegen dereferences the variant pointer with `*`, - // so the result is a value even if the original type is a pointer. + // Sumtype smartcast idents dereference the variant pointer eagerly, so the + // selector receiver is already a value. Interface smartcasts are more + // context-sensitive and may still emit a pointer while inside selector codegen. mut smartcast_ident_already_dereferenced := false if mut selector_expr_expr is ast.Ident && selector_scope != unsafe { nil } { selector_expr_expr.scope = selector_scope if scope_var := selector_scope.find_var(selector_expr_expr.name) { if scope_var.smartcasts.len > 0 { selector_expr_expr.obj = *scope_var - is_sumtype_smartcast_expr_ptr = g.table.final_sym(g.unwrap_generic(scope_var.typ)).kind == .sum_type orig_sym := g.table.final_sym(g.unwrap_generic(scope_var.orig_type)) - // For sum types: ident codegen emits `*name->_variant` which - // dereferences the variant pointer, producing a value. - // For interfaces: ident codegen emits `*(type*)name._variant` - // which also dereferences the interface field pointer. - // In both cases the expression result is a value, not a ptr. - smartcast_ident_already_dereferenced = orig_sym.kind in [.sum_type, .interface] + smartcast_ident_already_dereferenced = orig_sym.kind == .sum_type } } } @@ -6444,7 +6455,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { if field_sym.kind == .sum_type && !is_option { g.write('*') } - cast_sym := g.table.sym(g.unwrap_generic(typ)) + cast_sym := *g.table.sym(g.unwrap_generic(typ)) if field_sym.kind == .interface && cast_sym.kind == .interface && !is_option_unwrap { ptr := '*'.repeat(field.typ.nr_muls()) @@ -6456,8 +6467,9 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { dot := if field.typ.is_ptr() { '->' } else { '.' } sum_type_deref_field += ')${dot}' } - if cast_sym.info is ast.Aggregate { - agg_sym := g.table.sym(cast_sym.info.types[g.aggregate_type_idx]) + variant_typ := cast_sym.aggregate_variant_type(g.aggregate_type_idx) + if variant_typ != 0 { + agg_sym := g.table.sym(variant_typ) sum_type_deref_field += '_${agg_sym.cname}' } else { if i == 0 && nested_unwrap { @@ -6544,9 +6556,11 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { g.inside_selector_lhs = true mut smartcast_expr_var := ast.Var{} mut is_interface_smartcast_expr := false - if node.expr is ast.Ident { - if node.expr.obj is ast.Var && g.table.is_interface_smartcast(node.expr.obj) { - smartcast_expr_var = node.expr.obj + if selector_expr_expr is ast.Ident { + if selector_expr_expr.obj is ast.Var && selector_expr_expr.obj.smartcasts.len > 0 + && selector_expr_expr.obj.orig_type != 0 + && g.table.final_sym(g.unwrap_generic(selector_expr_expr.obj.orig_type)).kind == .interface { + smartcast_expr_var = selector_expr_expr.obj is_interface_smartcast_expr = true } } @@ -6616,12 +6630,6 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } else { resolved_selector_expr_type.nr_muls() - 1 } - $if trace_ci_fixes ? { - if g.file.path.contains('binary_search_tree.v') && node.expr is ast.Ident - && node.expr.name == 'tree' { - eprintln('selector emit tree.${node.field_name} lhs=${g.table.type_to_str(lhs_expr_type)} resolved=${g.table.type_to_str(resolved_selector_expr_type)} n_ptr=${n_ptr} sum_ptr=${is_sumtype_smartcast_expr_ptr}') - } - } if expr_is_unwrapped_autoheap_option { expr_ident := node.expr as ast.Ident expr_name := if expr_ident.obj is ast.Var && expr_ident.obj.is_inherited { @@ -6722,8 +6730,9 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } else { ast.void_type } - interface_smartcast_expr_is_dereferenced := is_interface_smartcast_expr - && smartcast_expr_var.smartcasts.last().nr_muls() == exposed_interface_smartcast_type.nr_muls() + 1 + // Selector receivers for interface smartcasts still need pointer-style field + // access even when the exposed smartcast type is a value type like `string`. + interface_smartcast_expr_is_dereferenced := false // An interface smartcast to a non-interface type normally produces a pointer // (the interface stores fields as pointers). But the ident codegen may // already dereference the pointer, depending on the exposed smartcast type. @@ -7077,7 +7086,7 @@ fn (mut g Gen) debugger_stmt(node ast.DebuggerStmt) { } values.write_string('{.typ=_S("${g.table.type_to_str(g.unwrap_generic(var_typ))}"),.value=') obj_sym := g.table.sym(obj.typ) - cast_sym := g.table.sym(var_typ) + cast_sym := *g.table.sym(var_typ) mut param_var := strings.new_builder(50) is_option := obj.orig_type.has_flag(.option) @@ -7118,23 +7127,26 @@ fn (mut g Gen) debugger_stmt(node ast.DebuggerStmt) { cur_variant_sym := g.table.sym(g.unwrap_generic(g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_variant_var}.typ', ast.void_type))) param_var.write_string('${cobj_name}${dot}_${cur_variant_sym.cname}') - } else if cast_sym.info is ast.Aggregate { - sym := g.table.sym(cast_sym.info.types[g.aggregate_type_idx]) - func = g.get_str_fn(cast_sym.info.types[g.aggregate_type_idx]) - param_var.write_string('${cobj_name}${dot}_${sym.cname}') - } else if obj_sym.kind == .interface && cast_sym.kind == .interface { - ptr := '*'.repeat(obj.typ.nr_muls()) - param_var.write_string('I_${obj_sym.cname}_as_I_${cast_sym.cname}(${ptr}${cobj_name})') - } else if obj_sym.kind in [.sum_type, .interface] { - param_var.write_string('${cobj_name}') - if opt_cast { - param_var.write_string('.data)') - } - param_var.write_string('${dot}_${cast_sym.cname}') - } else if is_option && !var_typ_is_option { - param_var.write_string('${cobj_name}.data') } else { - param_var.write_string('${cobj_name}') + variant_typ := cast_sym.aggregate_variant_type(g.aggregate_type_idx) + if variant_typ != 0 { + sym := g.table.sym(variant_typ) + func = g.get_str_fn(variant_typ) + param_var.write_string('${cobj_name}${dot}_${sym.cname}') + } else if obj_sym.kind == .interface && cast_sym.kind == .interface { + ptr := '*'.repeat(obj.typ.nr_muls()) + param_var.write_string('I_${obj_sym.cname}_as_I_${cast_sym.cname}(${ptr}${cobj_name})') + } else if obj_sym.kind in [.sum_type, .interface] { + param_var.write_string('${cobj_name}') + if opt_cast { + param_var.write_string('.data)') + } + param_var.write_string('${dot}_${cast_sym.cname}') + } else if is_option && !var_typ_is_option { + param_var.write_string('${cobj_name}.data') + } else { + param_var.write_string('${cobj_name}') + } } param_var.write_string(')') @@ -7801,35 +7813,6 @@ fn (mut g Gen) ident(node ast.Ident) { } } if node.info is ast.IdentVar { - $if trace_ci_fixes ? { - if g.file.path.contains('binary_search_tree.v') && node.name == 'tree' - && g.cur_fn != unsafe { nil } { - safe_typ := if has_resolved_var && resolved_var.typ != 0 { - g.table.type_to_str(resolved_var.typ) - } else { - '' - } - safe_orig := if has_resolved_var && resolved_var.orig_type != 0 { - g.table.type_to_str(resolved_var.orig_type) - } else { - '' - } - safe_smart := if has_resolved_var { - resolved_var.smartcasts.filter(it != 0).map(g.table.type_to_str(it)).str() - } else { - '[]' - } - eprintln('cgen ident fn=${g.cur_fn.name} name=${node.name} has=${has_resolved_var} typ=${safe_typ} orig=${safe_orig} smart=${safe_smart} ct=${if has_resolved_var { - resolved_var.ct_type_var.str() - } else { - '' - }} inherited=${if has_resolved_var { - resolved_var.is_inherited.str() - } else { - 'false' - }}') - } - } node_info_is_option = node.info.is_option if node.or_expr.kind == .absent { resolved_scope_type := g.resolved_scope_var_type(node) @@ -7932,7 +7915,11 @@ fn (mut g Gen) ident(node ast.Ident) { // `x = 10` => `x.data = 10` (g.right_is_opt == false) // `x = new_opt()` => `x = new_opt()` (g.right_is_opt == true) // `println(x)` => `println(*(int*)x.data)` - if node_info_is_option && !(g.is_assign_lhs && g.right_is_opt) { + sumtype_option_variant_smartcast := node_info_is_option && has_resolved_var + && resolved_var.smartcasts.len > 0 && resolved_var.orig_type.has_flag(.option) + && g.table.final_sym(g.unwrap_generic(resolved_var.orig_type.clear_option_and_result())).kind == .sum_type + if node_info_is_option && !sumtype_option_variant_smartcast && !(g.is_assign_lhs + && g.right_is_opt) { selector_uses_unwrapped_option_smartcast := has_resolved_var && resolved_var.smartcasts.len > 0 && resolved_var.ct_type_var == .no_comptime && resolved_var.is_unwrapped && g.inside_selector_lhs @@ -7983,15 +7970,19 @@ fn (mut g Gen) ident(node ast.Ident) { stmt_str := g.go_before_last_stmt().trim_space() g.empty_line = true var_name := if !g.is_assign_lhs && is_auto_heap { '(*${name})' } else { name } - or_return_type := if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 { + mut or_return_type := if has_resolved_var + && resolved_var.orig_type.has_flag(.option) { + resolved_var.orig_type + } else if has_resolved_var && resolved_var.typ.has_flag(.option) { + resolved_var.typ + } else { + node.info.typ + } + if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 { or_value_type := g.resolved_or_block_value_type(node.or_expr) if or_value_type != 0 { - or_value_type.set_flag(.option) - } else { - node.info.typ + or_return_type = or_value_type.set_flag(.option) } - } else { - node.info.typ } g.or_block(var_name, node.or_expr, g.or_type_with_auto_deref(node, or_return_type)) g.write(stmt_str) @@ -8065,6 +8056,7 @@ fn (mut g Gen) ident(node ast.Ident) { } // Skip smartcasts for comptime_for variables to avoid using stale types skip_smartcasts := g.is_comptime_for_var(node) + || (g.is_assign_lhs && resolved_var.orig_type.has_flag(.option)) if has_resolved_var && resolved_var.smartcasts.len > 0 && !skip_smartcasts { mut smartcast_types := resolved_var.smartcasts.clone() if resolved_var.orig_type.has_flag(.option) { @@ -8135,8 +8127,20 @@ fn (mut g Gen) ident(node ast.Ident) { g.write('(') if i == 0 && resolved_var.is_unwrapped && resolved_var.ct_type_var == .smartcast { - ctyp := g.unwrap_generic(g.type_resolver.get_type(node)) - g.write('*(${g.base_type(ctyp)}*)(') + ctyp := if is_option && resolved_var.is_unwrapped { + resolved_var.typ + } else if is_option && typ.has_flag(.option) { + typ + } else { + g.unwrap_generic(g.type_resolver.get_type(node)) + } + ctyp_styp := if ctyp.has_flag(.option) { + opt_styp, _ := g.option_type_name(ctyp) + opt_styp + } else { + g.styp(ctyp) + } + g.write('*(${ctyp_styp}*)(') } if obj_sym.kind == .sum_type && !is_auto_heap { if is_option { @@ -8144,7 +8148,19 @@ fn (mut g Gen) ident(node ast.Ident) { if !is_option_unwrap { g.write('*(') } - styp := g.base_type(resolved_var.typ) + cast_typ := if is_option + && resolved_var.ct_type_var == .smartcast + && resolved_var.is_unwrapped && !typ.has_flag(.option) { + resolved_var.typ + } else { + typ + } + styp := if cast_typ.has_flag(.option) { + opt_styp, _ := g.option_type_name(cast_typ) + opt_styp + } else { + g.styp(cast_typ) + } g.write('*(${styp}*)') } } else if !g.arg_no_auto_deref && !sumtype_fn_value_smartcast { @@ -8165,7 +8181,7 @@ fn (mut g Gen) ident(node ast.Ident) { } for i, typ in smartcast_types { is_option_unwrap := is_option && typ == resolved_var.typ.clear_flag(.option) - cast_sym := g.table.sym(g.unwrap_generic(typ)) + cast_sym := *g.table.sym(g.unwrap_generic(typ)) final_cast_sym := g.table.final_sym(g.unwrap_generic(typ)) sumtype_fn_value_smartcast := obj_sym.kind == .sum_type && !is_auto_heap && final_cast_sym.info is ast.FnType && !is_option_unwrap @@ -8201,8 +8217,9 @@ fn (mut g Gen) ident(node ast.Ident) { } } dot := if is_ptr || is_auto_heap { '->' } else { '.' } - if cast_sym.info is ast.Aggregate { - sym := g.table.sym(cast_sym.info.types[g.aggregate_type_idx]) + variant_typ := cast_sym.aggregate_variant_type(g.aggregate_type_idx) + if variant_typ != 0 { + sym := g.table.sym(variant_typ) g.write('${dot}_${sym.cname}') } else { if is_option && !resolved_var.is_unwrapped { @@ -8678,7 +8695,15 @@ fn include_path_for_generated_c(node ast.HashStmt) string { return node.main } +fn is_manual_stdarg_include(raw_target string) bool { + target := raw_target.trim_space() + return target in ['', '"stdarg.h"'] +} + fn (mut g Gen) hash_stmt_guarded_include(node ast.HashStmt) string { + if is_manual_stdarg_include(node.main) { + return '// stdarg declarations are provided by c_headers' + } mut missing_message := 'Header file ${node.main}, needed for module `${node.mod}` was not found.' missing_message += ' ${missing_shader_header_message(node)}' mut guarded_include := get_guarded_include_text(include_path_for_generated_c(node), @@ -8998,6 +9023,18 @@ fn (mut g Gen) return_stmt(node ast.Return) { inferred_type := g.type_resolver.get_type_or_default(expr0, ast.void_type) if inferred_type != ast.void_type && inferred_type != 0 { type0 = g.unwrap_generic(g.recheck_concrete_type(inferred_type)) + } else { + match expr0 { + ast.CallExpr { + if expr0.name == 'error' { + type0 = ast.error_type + } + } + ast.None { + type0 = ast.none_type + } + else {} + } } } // When the expression uses `?` or `!` propagation, the evaluated type is @@ -10217,10 +10254,26 @@ fn (mut g Gen) gen_or_block_stmts(cvar_name string, cast_typ string, stmts []ast if i == stmts.len - 1 { expr_stmt := stmt as ast.ExprStmt g.set_current_pos_as_last_stmt_pos() - is_custom_error := g.is_error_type(expr_stmt.typ) - if g.inside_return && (expr_stmt.typ.idx() == ast.error_type_idx - || expr_stmt.typ in [ast.none_type, ast.error_type] - || is_custom_error) { + mut expr_typ := expr_stmt.typ + if expr_typ == ast.void_type || expr_typ == 0 { + expr_typ = g.type_resolver.get_type_or_default(expr_stmt.expr, + return_type.clear_option_and_result()) + if expr_typ == ast.void_type || expr_typ == 0 { + match expr_stmt.expr { + ast.CallExpr { + if expr_stmt.expr.return_type != 0 { + expr_typ = expr_stmt.expr.return_type + } else if func := g.table.find_fn(expr_stmt.expr.name) { + expr_typ = func.return_type + } + } + else {} + } + } + } + is_custom_error := g.is_error_type(expr_typ) + if g.inside_return && (expr_typ.idx() == ast.error_type_idx + || expr_typ in [ast.none_type, ast.error_type] || is_custom_error) { // `return foo() or { error('failed') }` or `return foo() or { CustomError{} }` if g.cur_fn != unsafe { nil } { g.write_defer_stmts(scope, true, pos) @@ -10232,7 +10285,7 @@ fn (mut g Gen) gen_or_block_stmts(cvar_name string, cast_typ string, stmts []ast expr: expr_stmt.expr typname: 'IError' typ: ast.error_type - expr_type: expr_stmt.typ + expr_type: expr_typ pos: pos } } @@ -10247,7 +10300,7 @@ fn (mut g Gen) gen_or_block_stmts(cvar_name string, cast_typ string, stmts []ast } } } else { - if expr_stmt.typ == ast.none_type_idx { + if expr_typ == ast.none_type_idx { g.write('${cvar_name} = ') g.gen_option_error(return_type, expr_stmt.expr) g.writeln(';') @@ -10282,15 +10335,14 @@ fn (mut g Gen) gen_or_block_stmts(cvar_name string, cast_typ string, stmts []ast // unwrap the option/result, so assign the // unwrapped value to the or-block's data slot. g.write('*(${cast_typ}*) ${cvar_name}${tmp_op}data = ') - } - } else if expr_stmt.expr is ast.CallExpr { - call_expr := expr_stmt.expr as ast.CallExpr - if call_expr.is_return_used { + } else { g.write('*(${cast_typ}*) ${cvar_name}${tmp_op}data = ') } + } else if return_type.clear_option_and_result() != ast.void_type { + g.write('*(${cast_typ}*) ${cvar_name}${tmp_op}data = ') } else if g.inside_opt_or_res && return_is_option && g.inside_assign { g.write('builtin___option_ok(&(${cast_typ}[]) { ') - g.expr_with_cast(expr_stmt.expr, expr_stmt.typ, + g.expr_with_cast(expr_stmt.expr, expr_typ, return_type.clear_option_and_result()) g.writeln(' }, (${option_name}*)&${cvar_name}, sizeof(${cast_typ}));') g.indent-- @@ -10322,16 +10374,16 @@ fn (mut g Gen) gen_or_block_stmts(cvar_name string, cast_typ string, stmts []ast // return expr or { fn_returns_option() } if is_option && g.inside_return && expr_stmt.expr is ast.CallExpr && return_is_option { - g.expr_with_cast(ast.Expr(expr_stmt.expr), expr_stmt.typ, return_type) + g.expr_with_cast(ast.Expr(expr_stmt.expr), expr_typ, return_type) } else if direct_option_value { old_inside_opt_data := g.inside_opt_data g.inside_opt_data = true - g.expr_with_opt(expr_stmt.expr, expr_stmt.typ, return_type) + g.expr_with_opt(expr_stmt.expr, expr_typ, return_type) g.inside_opt_data = old_inside_opt_data } else { old_inside_opt_data := g.inside_opt_data g.inside_opt_data = true - g.expr_with_cast(expr_stmt.expr, expr_stmt.typ, + g.expr_with_cast(expr_stmt.expr, expr_typ, return_type.clear_option_and_result()) g.inside_opt_data = old_inside_opt_data } @@ -10386,7 +10438,29 @@ fn (mut g Gen) or_block_on_value(var_name string, or_block ast.OrExpr, return_ty g.inside_or_block = false } stmts := or_block.stmts - if stmts.len > 0 && stmts.last() is ast.ExprStmt && stmts.last().typ != ast.void_type { + mut last_expr_produces_value := false + if stmts.len > 0 && stmts.last() is ast.ExprStmt { + last_expr_stmt := stmts.last() as ast.ExprStmt + mut last_expr_typ := if last_expr_stmt.typ != ast.void_type { + last_expr_stmt.typ + } else { + g.type_resolver.get_type_or_default(last_expr_stmt.expr, ast.void_type) + } + if last_expr_typ == ast.void_type || last_expr_typ == 0 { + match last_expr_stmt.expr { + ast.CallExpr { + if last_expr_stmt.expr.return_type != 0 { + last_expr_typ = last_expr_stmt.expr.return_type + } else if func := g.table.find_fn(last_expr_stmt.expr.name) { + last_expr_typ = func.return_type + } + } + else {} + } + } + last_expr_produces_value = last_expr_typ != ast.void_type + } + if last_expr_produces_value { g.gen_or_block_stmts(cvar_name, g.base_type(return_type), stmts, return_type, false, or_block.scope, or_block.pos) } else { @@ -10461,7 +10535,29 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Ty g.inside_or_block = false } stmts := or_block.stmts - if stmts.len > 0 && stmts.last() is ast.ExprStmt && stmts.last().typ != ast.void_type { + mut last_expr_produces_value := false + if stmts.len > 0 && stmts.last() is ast.ExprStmt { + last_expr_stmt := stmts.last() as ast.ExprStmt + mut last_expr_typ := if last_expr_stmt.typ != ast.void_type { + last_expr_stmt.typ + } else { + g.type_resolver.get_type_or_default(last_expr_stmt.expr, ast.void_type) + } + if last_expr_typ == ast.void_type || last_expr_typ == 0 { + match last_expr_stmt.expr { + ast.CallExpr { + if last_expr_stmt.expr.return_type != 0 { + last_expr_typ = last_expr_stmt.expr.return_type + } else if func := g.table.find_fn(last_expr_stmt.expr.name) { + last_expr_typ = func.return_type + } + } + else {} + } + } + last_expr_produces_value = last_expr_typ != ast.void_type + } + if last_expr_produces_value { g.gen_or_block_stmts(cvar_name, mr_styp, stmts, return_type, true, or_block.scope, or_block.pos) } else { @@ -11382,18 +11478,22 @@ return ${cast_struct_str}; cast_fn_name = g.generic_fn_name(inter_info.concrete_types, cast_fn_name) } mut clone_expr := '${cast_fn_name}((${cctype}*)builtin__memdup(x, sizeof(${cctype})))' - match st_sym.kind { - .array { - depth := g.get_array_depth((st_sym.info as ast.Array).elem_type) - clone_expr = '${cast_fn_name}(HEAP(${cctype}, builtin__array_clone_static_to_depth(*(${cctype}*)x, ${depth})))' - } - .map { - clone_expr = '${cast_fn_name}(HEAP(${cctype}, builtin__map_clone((${cctype}*)x)))' - } - .string { - clone_expr = '${cast_fn_name}(HEAP(${cctype}, builtin__string_clone(*(${cctype}*)x)))' + if is_fn_variant { + clone_expr = '${cast_fn_name}((${g.fn_ptr_cast_typ(st_sym_info.info as ast.FnType)})x)' + } else { + match st_sym.kind { + .array { + depth := g.get_array_depth((st_sym.info as ast.Array).elem_type) + clone_expr = '${cast_fn_name}(HEAP(${cctype}, builtin__array_clone_static_to_depth(*(${cctype}*)x, ${depth})))' + } + .map { + clone_expr = '${cast_fn_name}(HEAP(${cctype}, builtin__map_clone((${cctype}*)x)))' + } + .string { + clone_expr = '${cast_fn_name}(HEAP(${cctype}, builtin__string_clone(*(${cctype}*)x)))' + } + else {} } - else {} } clone_functions.writeln(' diff --git a/vlib/v/gen/c/cheaders.v b/vlib/v/gen/c/cheaders.v index 450c6f471..9b48ff2a6 100644 --- a/vlib/v/gen/c/cheaders.v +++ b/vlib/v/gen/c/cheaders.v @@ -347,10 +347,139 @@ const c_headers = c_helper_macros + c_common_macros + c_common_callconv_attr + r' // c_headers typedef int (*qsort_callback_func)(const void*, const void*); -#include // TODO: remove all these includes, define all function signatures and types manually -#include -#include -#include // for va_list +#if defined(_MSC_VER) +typedef struct _iobuf FILE; +typedef char* va_list; +#ifndef _ADDRESSOF + #define _ADDRESSOF(v) (&(v)) +#endif +#ifndef _INTSIZEOF + #define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - + 1) & ~(sizeof(int) - + 1)) +#endif +#ifndef va_start + #define va_start(ap, v) ((void)((ap) = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v))) +#endif +#ifndef va_arg + #define va_arg(ap, t) (*(t*)((ap += _INTSIZEOF(t)) - + _INTSIZEOF(t))) +#endif +#ifndef va_end + #define va_end(ap) ((void)((ap) = (va_list)0)) +#endif +#ifndef va_copy + #define va_copy(dest, src) ((dest) = (src)) +#endif +FILE* __cdecl __acrt_iob_func(unsigned index); +#define stdin (__acrt_iob_func(0)) +#define stdout (__acrt_iob_func(1)) +#define stderr (__acrt_iob_func(2)) +#else + #if defined(__APPLE__) +typedef struct __sFILE FILE; +extern FILE* __stdinp; +extern FILE* __stdoutp; +extern FILE* __stderrp; +#define stdin __stdinp +#define stdout __stdoutp +#define stderr __stderrp + #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) +typedef struct __sFILE FILE; +extern FILE* stdin; +extern FILE* stdout; +extern FILE* stderr; + #else +typedef struct _IO_FILE FILE; +extern FILE* stdin; +extern FILE* stdout; +extern FILE* stderr; + #endif +typedef __builtin_va_list va_list; +#ifndef va_start + #define va_start(ap, v) __builtin_va_start(ap, v) +#endif +#ifndef va_arg + #define va_arg(ap, t) __builtin_va_arg(ap, t) +#endif +#ifndef va_end + #define va_end(ap) __builtin_va_end(ap) +#endif +#ifndef va_copy + #define va_copy(dest, src) __builtin_va_copy(dest, src) +#endif +#endif +int vfprintf(FILE *stream, const char *format, va_list ap); +int vsnprintf(char *str, size_t size, const char *format, va_list ap); +int fprintf(FILE *stream, const char *format, ...); +int printf(const char *format, ...); +int snprintf(char *str, size_t size, const char *format, ...); +int sprintf(char *str, const char *format, ...); +int sscanf(const char *str, const char *format, ...); +int scanf(const char *format, ...); +int puts(const char *str); +int fputs(const char *str, FILE *stream); +int getchar(void); +int putchar(int ch); +int getc(FILE *stream); +int fgetc(FILE *stream); +int ungetc(int ch, FILE *stream); +int fflush(FILE *stream); +int feof(FILE *stream); +int ferror(FILE *stream); +void clearerr(FILE *stream); +int setvbuf(FILE *stream, char *buf, int mode, size_t size); +long ftell(FILE *stream); +void rewind(FILE *stream); +FILE *fopen(const char *filename, const char *mode); +FILE *fdopen(int fd, const char *mode); +FILE *freopen(const char *filename, const char *mode, FILE *stream); +int fileno(FILE *stream); +size_t fread(void *ptr, size_t size, size_t items, FILE *stream); +size_t fwrite(const void *ptr, size_t size, size_t items, FILE *stream); +char *fgets(char *str, int size, FILE *stream); +int fclose(FILE *stream); +FILE *popen(const char *command, const char *mode); +int pclose(FILE *stream); +void *malloc(size_t size); +void *calloc(size_t nitems, size_t size); +void *realloc(void *ptr, size_t size); +void *aligned_alloc(size_t alignment, size_t size); +void free(void *ptr); +int atexit(void (*cb)(void)); +void exit(int status); +int atoi(const char *str); +char *getenv(const char *name); +int setenv(const char *name, const char *value, int overwrite); +int unsetenv(const char *name); +int system(const char *command); +int remove(const char *path); +int rename(const char *old_path, const char *new_path); +char *realpath(const char *path, char *resolved_path); +void qsort(void *base, size_t items, size_t item_size, qsort_callback_func cb); +size_t strlen(const char *str); +char *strerror(int errnum); +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); +void *memset(void *dest, int ch, size_t n); +int memcmp(const void *left, const void *right, size_t n); +void *memchr(const void *str, int c, size_t n); +char *strchr(const char *str, int c); +char *strrchr(const char *str, int c); +int fseek(FILE *stream, long offset, int whence); +isize getline(char **lineptr, size_t *n, FILE *stream); +#ifndef _IOFBF + #define _IOFBF 0 +#endif +#ifndef _IOLBF + #define _IOLBF 1 +#endif +#ifndef _IONBF + #define _IONBF 2 +#endif +#ifndef EOF + #define EOF (-1) +#endif #if defined(__TINYC__) // https://lists.nongnu.org/archive/html/tinycc-devel/2025-10/msg00007.html // gnu headers use to #define __attribute__ to empty for non-gcc compilers @@ -358,9 +487,6 @@ typedef int (*qsort_callback_func)(const void*, const void*); #endif #if defined(_MSC_VER) // Ensure C99-like return semantics and NUL-termination for MSVC snprintf/vsnprintf. -#ifndef va_copy - #define va_copy(dest, src) ((dest) = (src)) -#endif static int v__vsnprintf(char *s, size_t n, const char *fmt, va_list ap) { va_list ap_copy; va_copy(ap_copy, ap); diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index 1c6a84031..1b585331c 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -1053,7 +1053,8 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) { g.comptime.inside_comptime_for = true g.comptime.comptime_for_field_var = node.val_var g.comptime.comptime_for_field_value = field - g.comptime.comptime_for_field_type = field.typ + resolved_field_typ := g.unwrap_generic(field.typ) + g.comptime.comptime_for_field_type = resolved_field_typ g.writeln('/* field ${i} : ${field.name} */ {') g.writeln('\t${node.val_var}.name = _S("${field.name}");') if field.attrs.len == 0 { @@ -1064,8 +1065,8 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) { '\t${node.val_var}.attrs = builtin__new_array_from_c_array(${attrs.len}, ${attrs.len}, sizeof(string), _MOV((string[${attrs.len}]){' + attrs.join(', ') + '}));\n') } - field_sym := g.table.sym(field.typ) - styp := field.typ + field_sym := g.table.sym(resolved_field_typ) + styp := resolved_field_typ unaliased_styp := g.table.unaliased_type(styp) g.writeln('\t${node.val_var}.typ = ${int(styp.idx())};\t// ${g.table.type_to_str(styp)}') @@ -1074,9 +1075,9 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) { g.writeln('\t${node.val_var}.is_mut = ${field.is_mut};') g.writeln('\t${node.val_var}.is_embed = ${field.is_embed};') - g.writeln('\t${node.val_var}.is_shared = ${field.typ.has_flag(.shared_f)};') - g.writeln('\t${node.val_var}.is_atomic = ${field.typ.has_flag(.atomic_f)};') - g.writeln('\t${node.val_var}.is_option = ${field.typ.has_flag(.option)};') + g.writeln('\t${node.val_var}.is_shared = ${resolved_field_typ.has_flag(.shared_f)};') + g.writeln('\t${node.val_var}.is_atomic = ${resolved_field_typ.has_flag(.atomic_f)};') + g.writeln('\t${node.val_var}.is_option = ${resolved_field_typ.has_flag(.option)};') g.writeln('\t${node.val_var}.is_array = ${field_sym.kind in [.array, .array_fixed]};') g.writeln('\t${node.val_var}.is_map = ${field_sym.kind == .map};') @@ -1085,9 +1086,9 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) { g.writeln('\t${node.val_var}.is_alias = ${field_sym.kind == .alias};') g.writeln('\t${node.val_var}.is_enum = ${field_sym.kind == .enum};') - g.writeln('\t${node.val_var}.indirections = ${field.typ.nr_muls()};') + g.writeln('\t${node.val_var}.indirections = ${resolved_field_typ.nr_muls()};') - g.type_resolver.update_ct_type('${node.val_var}.typ', field.typ) + g.type_resolver.update_ct_type('${node.val_var}.typ', resolved_field_typ) g.type_resolver.update_ct_type('${node.val_var}.unaliased_typ', unaliased_styp) g.stmts(node.stmts) g.write_defer_stmts(node.scope, false, node.pos) diff --git a/vlib/v/gen/c/dumpexpr.v b/vlib/v/gen/c/dumpexpr.v index 0c2a8db6f..fd9f20b4a 100644 --- a/vlib/v/gen/c/dumpexpr.v +++ b/vlib/v/gen/c/dumpexpr.v @@ -51,6 +51,7 @@ fn (mut g Gen) dump_expr(node ast.DumpExpr) { if node.expr is ast.Ident { // var if node.expr.info is ast.IdentVar { + ident_info := node.expr.var_info() current_fn_ident_type := g.resolve_current_fn_generic_param_type(node.expr.name) if current_fn_ident_type != 0 { is_auto_deref := node.expr.obj is ast.Var && node.expr.obj.is_auto_deref @@ -82,7 +83,7 @@ fn (mut g Gen) dump_expr(node ast.DumpExpr) { if re != 0 { expr_type = re } else { - expr_type = g.unwrap_generic(node.expr.info.typ) + expr_type = g.unwrap_generic(ident_info.typ) } } } diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 3db8bc77c..a24d84e43 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -165,6 +165,10 @@ fn free_method_matches_receiver_expr(expr ast.Expr, receiver_name string) bool { } } +fn (g &Gen) prefers_msvc_compatible_code() bool { + return g.is_cc_msvc || g.pref.os == .windows +} + fn (mut g Gen) node_decl_fkey(node ast.FnDecl) string { if node.is_method && !node.name.contains('_T_') { receiver_sym := g.table.sym(node.receiver.typ) @@ -653,6 +657,26 @@ fn (mut g Gen) is_used_by_main(node ast.FnDecl) bool { $if trace_skip_unused_fns ? { println('> is_used_by_main: ${is_used_by_main} | node.name: ${node.name} | fkey: ${fkey} | node.is_method: ${node.is_method}') } + if !is_used_by_main && node.is_method { + receiver_type := g.table.final_type(node.receiver.typ.set_nr_muls(0)) + for isym in g.table.type_symbols { + if isym.kind != .interface || isym.info !is ast.Interface { + continue + } + if isym.idx !in g.table.used_features.used_syms { + continue + } + inter_info := isym.info as ast.Interface + impl_types := inter_info.implementor_types(true) + if receiver_type !in impl_types { + continue + } + if isym.has_method(node.name) || isym.has_method_with_generic_parent(node.name) { + is_used_by_main = true + break + } + } + } if !is_used_by_main { $if trace_skip_unused_fns_in_c_code ? { g.writeln('// trace_skip_unused_fns_in_c_code, ${node.name}, fkey: ${fkey}') @@ -859,6 +883,10 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { if node.language == .c && !g.inside_c_extern { return } + c_sym_name := node.name.all_after_first('C__').all_after_first('C.') + if node.language == .c && c_sym_name in ['va_start', 'va_arg', 'va_end', 'va_copy'] { + return + } old_is_vlines_enabled := g.is_vlines_enabled g.is_vlines_enabled = true defer { diff --git a/vlib/v/gen/c/orm.v b/vlib/v/gen/c/orm.v index abeaf95d6..bfe41a4a2 100644 --- a/vlib/v/gen/c/orm.v +++ b/vlib/v/gen/c/orm.v @@ -22,6 +22,22 @@ fn orm_field_access_name(field_name string) string { return c_name(field_name) } +fn (g &Gen) orm_inserting_object_type(obj ast.ScopeObject) ast.Type { + return match obj { + ast.Var { + if obj.smartcasts.len > 0 && obj.orig_type != 0 + && g.table.final_sym(g.table.final_type(obj.orig_type)).kind == .sum_type { + obj.orig_type + } else { + obj.typ + } + } + else { + ast.void_type + } + } +} + fn (g &Gen) orm_primitive_field_name(typ ast.Type) string { final_typ := g.table.final_type(typ.clear_flag(.option)) sym := g.table.sym(final_typ) @@ -295,23 +311,16 @@ fn (mut g Gen) sql_insert_expr(node ast.SqlExpr) { } fn (mut g Gen) build_sql_stmt_line_from_sql_expr(node ast.SqlExpr) ast.SqlStmtLine { - mut sub_structs := map[int]ast.SqlStmtLine{} + mut sub_structs := map[string]ast.SqlStmtLine{} for key, sub in node.sub_structs { - // Find the field type for this field name to use as the int key - mut field_typ := ast.Type(0) - for field in node.fields { - if field.name == key { - field_typ = field.typ - break - } - } - sub_structs[int(field_typ)] = g.build_sql_stmt_line_from_sql_expr(sub) + sub_structs[key] = g.build_sql_stmt_line_from_sql_expr(sub) } return ast.SqlStmtLine{ object_var: node.inserted_var fields: node.fields table_expr: node.table_expr sub_structs: sub_structs + scope: node.scope } } @@ -556,7 +565,7 @@ fn (mut g Gen) write_orm_upsert(node &ast.SqlStmtLine, table_name string, connec if inserting_object.typ.is_ptr() { member_access_type = '->' } - inserting_object_type = inserting_object.typ + inserting_object_type = g.orm_inserting_object_type(inserting_object) } inserting_object_sym := g.table.sym(inserting_object_type) data_var_name := g.new_tmp_var() @@ -827,8 +836,8 @@ fn (mut g Gen) write_orm_insert_with_last_ids(node ast.SqlStmtLine, connection_v final_field_typ := g.table.final_type(field.typ) sym := g.table.sym(final_field_typ) if sym.kind == .struct && sym.name != 'time.Time' { - if final_field_typ in node.sub_structs { - subs << unsafe { node.sub_structs[int(final_field_typ)] } + if field.name in node.sub_structs { + subs << unsafe { node.sub_structs[field.name] } unwrapped_c_typ := g.styp(final_field_typ.clear_flag(.option)) subs_unwrapped_c_typ << if final_field_typ.has_flag(.option) { @@ -847,8 +856,8 @@ fn (mut g Gen) write_orm_insert_with_last_ids(node ast.SqlStmtLine, connection_v if final_field_typ.has_flag(.option) { opt_fields << arrs.len } - if final_field_typ in node.sub_structs { - arrs << unsafe { node.sub_structs[int(final_field_typ)] } + if field.name in node.sub_structs { + arrs << unsafe { node.sub_structs[field.name] } } field_names << field.name } @@ -871,7 +880,7 @@ fn (mut g Gen) write_orm_insert_with_last_ids(node ast.SqlStmtLine, connection_v if inserting_object.typ.is_ptr() { member_access_type = '->' } - inserting_object_type = inserting_object.typ + inserting_object_type = g.orm_inserting_object_type(inserting_object) } inserting_object_sym := g.table.sym(inserting_object_type) diff --git a/vlib/v/gen/c/str_intp.v b/vlib/v/gen/c/str_intp.v index 8c04b8415..ea50cbd6e 100644 --- a/vlib/v/gen/c/str_intp.v +++ b/vlib/v/gen/c/str_intp.v @@ -84,11 +84,15 @@ fn int_ref_interpolates_as_value(expr ast.Expr, typ ast.Type, fmt u8) bool { } return match expr { ast.Ident { - if expr.obj is ast.Var { - expr.obj.is_arg || expr.obj.expr is ast.AsCast - || (expr.obj.expr is ast.PrefixExpr && expr.obj.expr.op == .amp) - } else { - false + obj := expr.obj + match obj { + ast.Var { + obj.is_arg || obj.expr is ast.AsCast + || (obj.expr is ast.PrefixExpr && obj.expr.op == .amp) + } + else { + false + } } } ast.PrefixExpr { @@ -174,9 +178,10 @@ fn (mut g Gen) str_format(node ast.StringInterLiteral, i int, fmts []u8) (u64, s typ = g.unwrap_generic(node.expr_types[i]) } else { typ = g.unwrap_generic(expr.obj.smartcasts.last()) - cast_sym := g.table.sym(typ) - if cast_sym.info is ast.Aggregate { - typ = cast_sym.info.types[g.aggregate_type_idx] + cast_sym := *g.table.sym(typ) + smartcast_variant_typ := cast_sym.aggregate_variant_type(g.aggregate_type_idx) + if smartcast_variant_typ != 0 { + typ = smartcast_variant_typ } else if expr.obj.ct_type_var == .smartcast { typ = g.unwrap_generic(g.type_resolver.get_type(expr)) } @@ -357,9 +362,10 @@ fn (mut g Gen) str_val(node ast.StringInterLiteral, i int, fmts []u8) { } // Resolve aggregate types (from multi-branch match arms) to the // concrete variant type for the current iteration. - orig_typ_sym := g.table.sym(orig_typ) - if orig_typ_sym.info is ast.Aggregate { - orig_typ = orig_typ_sym.info.types[g.aggregate_type_idx] + orig_typ_sym := *g.table.sym(orig_typ) + orig_variant_typ := orig_typ_sym.aggregate_variant_type(g.aggregate_type_idx) + if orig_variant_typ != 0 { + orig_typ = orig_variant_typ } is_int_valptr := int_ref_interpolates_as_value(expr, orig_typ, fmt) typ := if is_int_valptr { orig_typ.deref() } else { orig_typ } @@ -426,9 +432,10 @@ fn (mut g Gen) str_val(node ast.StringInterLiteral, i int, fmts []u8) { } else if expr.obj is ast.Var { if expr.obj.smartcasts.len > 0 { exp_typ = g.unwrap_generic(expr.obj.smartcasts.last()) - cast_sym := g.table.sym(exp_typ) - if cast_sym.info is ast.Aggregate { - exp_typ = cast_sym.info.types[g.aggregate_type_idx] + cast_sym := *g.table.sym(exp_typ) + exp_variant_typ := cast_sym.aggregate_variant_type(g.aggregate_type_idx) + if exp_variant_typ != 0 { + exp_typ = exp_variant_typ } if exp_typ.has_flag(.option) && expr.obj.is_unwrapped { exp_typ = exp_typ.clear_flag(.option) @@ -525,9 +532,10 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { node_.expr_types[i] } else { mut typ := g.unwrap_generic(expr.obj.smartcasts.last()) - cast_sym := g.table.sym(typ) - if cast_sym.info is ast.Aggregate { - typ = cast_sym.info.types[g.aggregate_type_idx] + cast_sym := *g.table.sym(typ) + field_variant_typ := cast_sym.aggregate_variant_type(g.aggregate_type_idx) + if field_variant_typ != 0 { + typ = field_variant_typ } else if expr.obj.ct_type_var == .smartcast { typ = g.unwrap_generic(g.type_resolver.get_type(expr)) } @@ -589,9 +597,10 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { } // Resolve aggregate types (from multi-branch match arms) to the // concrete variant type for the current iteration. - field_sym := g.table.sym(field_typ) - if field_sym.info is ast.Aggregate { - field_typ = field_sym.info.types[g.aggregate_type_idx] + field_sym := *g.table.sym(field_typ) + interp_variant_typ := field_sym.aggregate_variant_type(g.aggregate_type_idx) + if interp_variant_typ != 0 { + field_typ = interp_variant_typ } // Clear option flag for variables unwrapped via `or {}` blocks if field_typ.has_flag(.option) && g.should_clear_option_flag(expr) { diff --git a/vlib/v/generics/generics.v b/vlib/v/generics/generics.v index 2b695a174..2e66e99be 100644 --- a/vlib/v/generics/generics.v +++ b/vlib/v/generics/generics.v @@ -829,12 +829,15 @@ pub fn (mut g Generics) expr(mut node ast.Expr) ast.Expr { name := if mut node.expr is ast.Ident { // var if node.expr.info is ast.IdentVar && node.expr.language == .v { - g.styp(g.unwrap_generic(node.expr.info.typ.clear_flags(.shared_f, .result))).replace('*', '') + ident_info := node.expr.var_info() + g.styp(g.unwrap_generic(ident_info.typ.clear_flags(.shared_f, .result))).replace('*', + '') } else { node.cname } } else if mut node.expr is ast.CallExpr { - g.styp(g.unwrap_generic(node.expr_type.clear_flags(.shared_f, .result))).replace('*', '').replace('.', '__') + g.styp(g.unwrap_generic(node.expr_type.clear_flags(.shared_f, .result))).replace('*', + '').replace('.', '__') } else { node.cname } diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index 439eb90c5..30ac4bc2b 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -2113,6 +2113,19 @@ pub fn (mut w Walker) mark_by_sym(isym ast.TypeSymbol) { w.uses_mem_align = w.uses_mem_align || decl.is_aligned for iface_typ in decl.implements_types { w.mark_by_type(iface_typ.typ) + iface_sym := w.table.sym(iface_typ.typ) + for method in iface_sym.methods { + if impl_method := isym.find_method_with_generic_parent(method.name) { + w.fn_by_name(impl_method.fkey()) + } else { + impl_method, _ := w.table.find_method_from_embeds(isym, method.name) or { + ast.Fn{}, []ast.Type{} + } + if impl_method.name != '' { + w.fn_by_name(impl_method.fkey()) + } + } + } } } } diff --git a/vlib/v/parser/orm.v b/vlib/v/parser/orm.v index 7a18f9781..10cddd3a1 100644 --- a/vlib/v/parser/orm.v +++ b/vlib/v/parser/orm.v @@ -212,6 +212,7 @@ fn (mut p Parser) sql_expr_after_prefix(prefix SqlPrefix) ast.Expr { aggregate_field: aggregate_field is_insert: is_insert typ: typ.set_flag(.result) + scope: p.scope or_expr: or_expr db_expr: prefix.db_expr where_expr: where_expr diff --git a/vlib/v/type_resolver/comptime_resolver.v b/vlib/v/type_resolver/comptime_resolver.v index 54e5f052e..6b658c4b2 100644 --- a/vlib/v/type_resolver/comptime_resolver.v +++ b/vlib/v/type_resolver/comptime_resolver.v @@ -52,14 +52,10 @@ pub fn (t &ResolverInfo) is_comptime(node ast.Expr) bool { node.ct_expr } ast.IndexExpr { - if node.left is ast.Ident { - node.left.ct_expr - } else { - false - } + t.has_comptime_expr(node.left) } ast.SelectorExpr { - return node.expr is ast.Ident && node.expr.ct_expr + t.has_comptime_expr(node.expr) } ast.InfixExpr { return node.left_ct_expr || node.right_ct_expr -- 2.39.5