| 1 | // Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | module c |
| 5 | |
| 6 | import os |
| 7 | import v.ast |
| 8 | import v.util |
| 9 | import v.type_resolver |
| 10 | |
| 11 | fn (g &Gen) veb_context_html_arg() string { |
| 12 | if g.fn_decl.params.len < 2 { |
| 13 | return 'ctx' |
| 14 | } |
| 15 | ctx_param := g.fn_decl.params[1] |
| 16 | ctx_sym := g.table.final_sym(ctx_param.typ) |
| 17 | if ctx_sym.name == 'veb.Context' { |
| 18 | return ctx_param.name |
| 19 | } |
| 20 | if ctx_sym.info is ast.Struct { |
| 21 | for embed in ctx_sym.info.embeds { |
| 22 | embed_sym := g.table.sym(embed) |
| 23 | if embed_sym.name == 'veb.Context' { |
| 24 | dot := if ctx_param.typ.is_ptr() { '->' } else { '.' } |
| 25 | return '&${ctx_param.name}${dot}${embed_sym.embed_name()}' |
| 26 | } |
| 27 | } |
| 28 | } |
| 29 | return ctx_param.name |
| 30 | } |
| 31 | |
| 32 | fn (mut g Gen) is_string_array_type(typ ast.Type) bool { |
| 33 | final_typ := g.table.unaliased_type(g.unwrap_generic(typ)) |
| 34 | sym := g.table.final_sym(final_typ) |
| 35 | if sym.info is ast.Array { |
| 36 | return g.table.unaliased_type(sym.info.elem_type) == ast.string_type |
| 37 | } |
| 38 | return false |
| 39 | } |
| 40 | |
| 41 | fn (mut g Gen) comptime_call_expands_string_args(m &ast.Fn, node ast.ComptimeCall) bool { |
| 42 | if node.args.len == 0 || node.args.last().expr !is ast.ArrayDecompose { |
| 43 | return false |
| 44 | } |
| 45 | array_decompose := node.args.last().expr as ast.ArrayDecompose |
| 46 | mut array_type := g.resolved_expr_type(array_decompose.expr, array_decompose.expr_type) |
| 47 | if array_type == ast.void_type { |
| 48 | array_type = array_decompose.expr_type |
| 49 | } |
| 50 | if !g.is_string_array_type(array_type) || m.params.len - 1 < node.args.len { |
| 51 | return false |
| 52 | } |
| 53 | return !g.is_string_array_type(m.params[node.args.len].typ) |
| 54 | } |
| 55 | |
| 56 | fn (mut g Gen) comptime_zero_value(typ ast.Type) string { |
| 57 | resolved_type := g.unwrap_generic(g.recheck_concrete_type(typ)) |
| 58 | styp := g.styp(resolved_type) |
| 59 | mut default_value := g.type_default(resolved_type) |
| 60 | if default_value.len > 0 && default_value[0] == `{` { |
| 61 | default_value = '(${styp})${default_value}' |
| 62 | } |
| 63 | return default_value |
| 64 | } |
| 65 | |
| 66 | fn (mut g Gen) comptime_type_expr_type(expr ast.Expr, fallback_type ast.Type) ast.Type { |
| 67 | match expr { |
| 68 | ast.ParExpr { |
| 69 | return g.comptime_type_expr_type(expr.expr, fallback_type) |
| 70 | } |
| 71 | ast.TypeNode { |
| 72 | return g.unwrap_generic(g.recheck_concrete_type(expr.typ)) |
| 73 | } |
| 74 | ast.TypeOf { |
| 75 | if expr.is_type { |
| 76 | return g.unwrap_generic(g.recheck_concrete_type(expr.typ)) |
| 77 | } |
| 78 | mut default_type := g.get_type(expr.typ) |
| 79 | default_type = g.resolve_typeof_expr_type(expr.expr, default_type) |
| 80 | if expr.expr is ast.Ident && expr.expr.obj is ast.Var { |
| 81 | resolved := g.comptime_typeof_generic_ident_type(expr.expr) |
| 82 | if resolved != 0 { |
| 83 | return resolved |
| 84 | } |
| 85 | } |
| 86 | if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 { |
| 87 | resolved := g.resolve_typeof_in_generic(expr) |
| 88 | if resolved != 0 { |
| 89 | default_type = resolved |
| 90 | } |
| 91 | } |
| 92 | return g.resolved_typeof_name_type(expr, default_type) |
| 93 | } |
| 94 | ast.ArrayInit { |
| 95 | if expr.elem_type_expr !is ast.EmptyExpr { |
| 96 | elem_type := g.comptime_type_expr_type(expr.elem_type_expr, expr.elem_type) |
| 97 | if elem_type != 0 && elem_type != ast.void_type && elem_type != ast.no_type { |
| 98 | if expr.is_fixed { |
| 99 | sym := g.table.final_sym(expr.typ) |
| 100 | if sym.info is ast.ArrayFixed { |
| 101 | return ast.new_type(g.table.find_or_register_array_fixed(elem_type, |
| 102 | sym.info.size, sym.info.size_expr, sym.info.is_fn_ret)) |
| 103 | } |
| 104 | } |
| 105 | return ast.new_type(g.table.find_or_register_array(elem_type)) |
| 106 | } |
| 107 | } |
| 108 | return g.unwrap_generic(g.recheck_concrete_type(expr.typ)) |
| 109 | } |
| 110 | ast.SelectorExpr { |
| 111 | return g.comptime_selector_type_expr_type(expr, fallback_type) |
| 112 | } |
| 113 | ast.Ident { |
| 114 | if g.comptime.inside_comptime_for && expr.obj is ast.Var |
| 115 | && expr.obj.ct_type_var != .no_comptime { |
| 116 | typ := g.type_resolver.get_type_from_comptime_var(expr) |
| 117 | if typ != 0 && typ != ast.void_type { |
| 118 | return g.unwrap_generic(g.recheck_concrete_type(typ)) |
| 119 | } |
| 120 | } |
| 121 | resolved_generic_type := g.comptime_generic_type_expr_ident_type(expr.name) |
| 122 | if resolved_generic_type != 0 { |
| 123 | return resolved_generic_type |
| 124 | } |
| 125 | if expr.name in g.type_resolver.type_map { |
| 126 | return g.unwrap_generic(g.recheck_concrete_type(g.type_resolver.get_ct_type_or_default(expr.name, |
| 127 | fallback_type))) |
| 128 | } |
| 129 | if util.is_generic_type_name(expr.name) && g.cur_fn != unsafe { nil } { |
| 130 | return g.unwrap_generic(g.recheck_concrete_type(g.table.find_type(expr.name).set_flag(.generic))) |
| 131 | } |
| 132 | return g.resolved_expr_type(expr, fallback_type) |
| 133 | } |
| 134 | else { |
| 135 | return g.resolved_expr_type(expr, fallback_type) |
| 136 | } |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | fn (mut g Gen) comptime_generic_type_expr_ident_type(name string) ast.Type { |
| 141 | generic_names, concrete_types := g.comptime_current_generic_context() |
| 142 | if generic_names.len > 0 && generic_names.len == concrete_types.len { |
| 143 | idx := generic_names.index(name) |
| 144 | if idx >= 0 && idx < concrete_types.len { |
| 145 | return g.unwrap_generic(g.recheck_concrete_type(concrete_types[idx])) |
| 146 | } |
| 147 | } |
| 148 | if g.has_active_call_generic_context() { |
| 149 | idx := g.active_call_generic_names.index(name) |
| 150 | if idx >= 0 && idx < g.active_call_concrete_types.len { |
| 151 | return g.unwrap_generic(g.recheck_concrete_type(g.active_call_concrete_types[idx])) |
| 152 | } |
| 153 | } |
| 154 | return 0 |
| 155 | } |
| 156 | |
| 157 | fn (mut g Gen) comptime_current_generic_context() ([]string, []ast.Type) { |
| 158 | if g.cur_fn == unsafe { nil } || g.cur_concrete_types.len == 0 { |
| 159 | return []string{}, []ast.Type{} |
| 160 | } |
| 161 | mut generic_names := g.current_fn_generic_names() |
| 162 | mut concrete_types := g.cur_concrete_types.clone() |
| 163 | if generic_names.len == 0 || generic_names.len != concrete_types.len { |
| 164 | recovered_generic_names, recovered_concrete_types := |
| 165 | g.recover_specialized_generic_context_for(g.cur_fn.name) |
| 166 | if recovered_generic_names.len > 0 |
| 167 | && recovered_generic_names.len == recovered_concrete_types.len { |
| 168 | generic_names = recovered_generic_names.clone() |
| 169 | concrete_types = recovered_concrete_types.clone() |
| 170 | } |
| 171 | } |
| 172 | if generic_names.len == 0 || generic_names.len != concrete_types.len { |
| 173 | return []string{}, []ast.Type{} |
| 174 | } |
| 175 | return generic_names, concrete_types |
| 176 | } |
| 177 | |
| 178 | fn (mut g Gen) comptime_typeof_generic_ident_type(ident ast.Ident) ast.Type { |
| 179 | if ident.obj !is ast.Var { |
| 180 | return 0 |
| 181 | } |
| 182 | var := ident.obj as ast.Var |
| 183 | if var.generic_typ == 0 { |
| 184 | return 0 |
| 185 | } |
| 186 | generic_names, concrete_types := g.comptime_current_generic_context() |
| 187 | if generic_names.len == 0 || generic_names.len != concrete_types.len { |
| 188 | return 0 |
| 189 | } |
| 190 | mut muttable := unsafe { &ast.Table(g.table) } |
| 191 | if resolved := muttable.convert_generic_type(var.generic_typ, generic_names, concrete_types) { |
| 192 | return g.unwrap_generic(g.recheck_concrete_type(resolved)) |
| 193 | } |
| 194 | unwrapped := |
| 195 | muttable.unwrap_generic_type_ex(var.generic_typ, generic_names, concrete_types, true) |
| 196 | if unwrapped != var.generic_typ { |
| 197 | return g.unwrap_generic(g.recheck_concrete_type(unwrapped)) |
| 198 | } |
| 199 | return 0 |
| 200 | } |
| 201 | |
| 202 | fn (mut g Gen) comptime_selector_type_expr_type(expr ast.SelectorExpr, fallback_type ast.Type) ast.Type { |
| 203 | if expr.expr is ast.Ident && g.comptime.inside_comptime_for |
| 204 | && expr.field_name in ['typ', 'unaliased_typ', 'indirections', 'pointee_type', 'payload_type', 'variant_types'] { |
| 205 | ident := expr.expr as ast.Ident |
| 206 | if ident.name == g.comptime.comptime_for_field_var |
| 207 | || ident.name == g.comptime.comptime_for_variant_var |
| 208 | || ident.name == g.comptime.comptime_for_method_param_var { |
| 209 | typ := g.type_resolver.get_type_from_comptime_var(ident) |
| 210 | if expr.field_name == 'unaliased_typ' { |
| 211 | return g.table.unaliased_type(typ) |
| 212 | } |
| 213 | if expr.field_name in ['pointee_type', 'payload_type', 'variant_types'] { |
| 214 | resolved := g.type_resolver.typeof_field_type(typ, expr.field_name) |
| 215 | if resolved != ast.no_type { |
| 216 | return g.unwrap_generic(g.recheck_concrete_type(resolved)) |
| 217 | } |
| 218 | } |
| 219 | return g.unwrap_generic(g.recheck_concrete_type(typ)) |
| 220 | } |
| 221 | } |
| 222 | if expr.field_name in ['typ', 'unaliased_typ', 'key_type', 'value_type', 'element_type', |
| 223 | 'pointee_type', 'payload_type', 'variant_types'] { |
| 224 | mut base_type := g.comptime_type_expr_type(expr.expr, expr.name_type) |
| 225 | if (base_type == 0 || base_type == ast.void_type || base_type == ast.no_type) |
| 226 | && expr.name_type != 0 { |
| 227 | base_type = expr.name_type |
| 228 | } |
| 229 | if expr.field_name == 'unaliased_typ' { |
| 230 | return g.table.unaliased_type(g.unwrap_generic(base_type)) |
| 231 | } |
| 232 | resolved := g.type_resolver.typeof_field_type(base_type, expr.field_name) |
| 233 | if resolved != ast.no_type { |
| 234 | return g.unwrap_generic(g.recheck_concrete_type(resolved)) |
| 235 | } |
| 236 | } |
| 237 | return g.resolved_expr_type(expr, fallback_type) |
| 238 | } |
| 239 | |
| 240 | fn (mut g Gen) comptime_zero_new_result_type(node ast.ComptimeCall, fallback_type ast.Type) ast.Type { |
| 241 | if node.kind !in [.zero, .new] || node.args.len == 0 { |
| 242 | return fallback_type |
| 243 | } |
| 244 | arg_fallback_type := if node.kind == .new && fallback_type.is_ptr() { |
| 245 | fallback_type.deref() |
| 246 | } else { |
| 247 | fallback_type |
| 248 | } |
| 249 | mut resolved_type := g.comptime_type_expr_type(node.args[0].expr, arg_fallback_type) |
| 250 | if resolved_type == 0 || resolved_type == ast.void_type || resolved_type == ast.no_type { |
| 251 | resolved_type = arg_fallback_type |
| 252 | } |
| 253 | resolved_type = g.unwrap_generic(g.recheck_concrete_type(resolved_type)) |
| 254 | return if node.kind == .new { resolved_type.ref() } else { resolved_type } |
| 255 | } |
| 256 | |
| 257 | fn (mut g Gen) comptime_selector(node ast.ComptimeSelector) { |
| 258 | left_type := g.resolved_expr_type(node.left, node.left_type) |
| 259 | if node.is_method && g.comptime.comptime_for_method != unsafe { nil } { |
| 260 | g.selector_expr(ast.SelectorExpr{ |
| 261 | pos: node.pos |
| 262 | expr: node.left |
| 263 | expr_type: left_type |
| 264 | typ: g.type_resolver.get_type(node) |
| 265 | field_name: g.comptime.comptime_for_method.name |
| 266 | has_hidden_receiver: true |
| 267 | }) |
| 268 | return |
| 269 | } |
| 270 | is_interface_field := g.table.sym(left_type).kind == .interface |
| 271 | if is_interface_field { |
| 272 | g.write('*(') |
| 273 | } |
| 274 | g.expr(node.left) |
| 275 | is_auto_heap_ident := node.left is ast.Ident && g.resolved_ident_is_auto_heap(node.left) |
| 276 | // When `g.expr` writes an auto-heap ident, it emits `(*(name))` by default, |
| 277 | // but skips the deref when it's an LHS / inside a selector LHS / assign-fn-var. |
| 278 | // In the deref-skipped case the C result is a pointer, so we need `->`. |
| 279 | auto_heap_no_deref := is_auto_heap_ident |
| 280 | && (g.is_assign_lhs || g.inside_selector_lhs || g.inside_assign_fn_var) |
| 281 | if g.unwrap_generic(left_type).is_ptr() || auto_heap_no_deref { |
| 282 | g.write('->') |
| 283 | } else { |
| 284 | g.write('.') |
| 285 | } |
| 286 | // check for field.name |
| 287 | if node.is_name && node.field_expr is ast.SelectorExpr { |
| 288 | if node.field_expr.expr is ast.Ident { |
| 289 | if node.field_expr.expr.name == g.comptime.comptime_for_field_var { |
| 290 | _, field_name := g.resolve_comptime_selector_field(node, left_type) |
| 291 | g.write(c_name(field_name)) |
| 292 | if is_interface_field { |
| 293 | g.write(')') |
| 294 | } |
| 295 | return |
| 296 | } |
| 297 | } |
| 298 | } |
| 299 | g.expr(node.field_expr) |
| 300 | if is_interface_field { |
| 301 | g.write(')') |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | fn (mut g Gen) gen_comptime_selector(expr ast.ComptimeSelector) string { |
| 306 | left_type := g.resolved_expr_type(expr.left, expr.left_type) |
| 307 | is_auto_heap_ident := expr.left is ast.Ident && g.resolved_ident_is_auto_heap(expr.left) |
| 308 | auto_heap_no_deref := is_auto_heap_ident |
| 309 | && (g.is_assign_lhs || g.inside_selector_lhs || g.inside_assign_fn_var) |
| 310 | arrow_or_dot := if left_type.is_ptr() || auto_heap_no_deref { '->' } else { '.' } |
| 311 | mut field_name := if expr.typ_key.contains('|') { |
| 312 | expr.typ_key.all_after('|') |
| 313 | } else { |
| 314 | g.comptime.comptime_for_field_value.name |
| 315 | } |
| 316 | if expr.field_expr is ast.SelectorExpr { |
| 317 | if expr.field_expr.expr is ast.Ident |
| 318 | && expr.field_expr.expr.name == g.comptime.comptime_for_field_var { |
| 319 | field_name = g.comptime.comptime_for_field_value.name |
| 320 | } |
| 321 | } |
| 322 | return '${expr.left.str()}${arrow_or_dot}${field_name}' |
| 323 | } |
| 324 | |
| 325 | fn (mut g Gen) resolve_comptime_selector_field(node ast.ComptimeSelector, left_type ast.Type) (ast.StructField, string) { |
| 326 | mut field_name := if node.typ_key.contains('|') { |
| 327 | node.typ_key.all_after('|') |
| 328 | } else { |
| 329 | g.comptime.comptime_for_field_value.name |
| 330 | } |
| 331 | if node.field_expr is ast.SelectorExpr && g.comptime.comptime_for_field_var != '' |
| 332 | && g.comptime.comptime_for_method_var == '' && node.field_expr.field_name == 'name' { |
| 333 | if node.field_expr.expr is ast.Ident |
| 334 | && node.field_expr.expr.name == g.comptime.comptime_for_field_var { |
| 335 | field_name = g.comptime.comptime_for_field_value.name |
| 336 | } |
| 337 | } |
| 338 | resolved_left_type := g.unwrap_generic(g.recheck_concrete_type(left_type)) |
| 339 | left_sym := g.table.sym(resolved_left_type) |
| 340 | field := g.table.find_field_with_embeds(left_sym, field_name) or { |
| 341 | g.error('`${node.left}` has no field named `${field_name}`', node.left.pos()) |
| 342 | } |
| 343 | return field, field_name |
| 344 | } |
| 345 | |
| 346 | fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) { |
| 347 | if node.kind == .compile_error || node.kind == .compile_warn { |
| 348 | // handled by checker, this branch was not taken |
| 349 | return |
| 350 | } |
| 351 | if node.kind == .embed_file { |
| 352 | // $embed_file('/path/to/file') |
| 353 | g.gen_embed_file_init(mut node) |
| 354 | return |
| 355 | } |
| 356 | if node.kind == .env { |
| 357 | // $env('ENV_VAR_NAME') |
| 358 | // TODO: deprecate after support for $d() is stable |
| 359 | val := util.cescaped_path(os.getenv(node.args_var)) |
| 360 | g.write('_S("${val}")') |
| 361 | return |
| 362 | } |
| 363 | if node.kind == .d { |
| 364 | // $d('some_string',<default value>), affected by `-d some_string=actual_value` |
| 365 | val := util.cescaped_path(node.compile_value) |
| 366 | if node.result_type == ast.string_type { |
| 367 | g.write('_S("${val}")') |
| 368 | } else if node.result_type == ast.char_type { |
| 369 | g.write("'${val}'") |
| 370 | } else { |
| 371 | g.write('${val}') |
| 372 | } |
| 373 | return |
| 374 | } |
| 375 | if node.kind in [.zero, .new] { |
| 376 | result_type := g.comptime_zero_new_result_type(node, node.result_type) |
| 377 | resolved_type := if node.kind == .new { result_type.deref() } else { result_type } |
| 378 | default_value := g.comptime_zero_value(resolved_type) |
| 379 | if node.kind == .new { |
| 380 | g.write('HEAP(${g.styp(resolved_type)}, (${default_value}))') |
| 381 | } else { |
| 382 | g.write(default_value) |
| 383 | } |
| 384 | return |
| 385 | } |
| 386 | if node.kind == .res { |
| 387 | if node.args_var != '' { |
| 388 | g.write('${g.defer_return_tmp_var}.arg${node.args_var}') |
| 389 | return |
| 390 | } |
| 391 | |
| 392 | g.write('${g.defer_return_tmp_var}') |
| 393 | return |
| 394 | } |
| 395 | if node.is_template { |
| 396 | is_html := node.kind == .html |
| 397 | mut cur_line := '' |
| 398 | |
| 399 | if !is_html { |
| 400 | cur_line = g.go_before_last_stmt() |
| 401 | } |
| 402 | |
| 403 | fn_name := g.fn_decl.name.replace('.', '__').to_lower() + node.pos.pos.str() |
| 404 | |
| 405 | for stmt in node.veb_tmpl.stmts { |
| 406 | if stmt is ast.FnDecl { |
| 407 | if stmt.name.starts_with('main.veb_tmpl') { |
| 408 | prev_inside_veb_tmpl := g.inside_veb_tmpl |
| 409 | prev_veb_filter_fn_name := g.veb_filter_fn_name |
| 410 | if is_html { |
| 411 | g.inside_veb_tmpl = true |
| 412 | g.veb_filter_fn_name = 'veb__filter' |
| 413 | } |
| 414 | // insert stmts from veb_tmpl fn |
| 415 | g.stmts(stmt.stmts.filter(it !is ast.Return)) |
| 416 | // |
| 417 | if is_html { |
| 418 | g.inside_veb_tmpl = prev_inside_veb_tmpl |
| 419 | g.veb_filter_fn_name = prev_veb_filter_fn_name |
| 420 | } |
| 421 | break |
| 422 | } |
| 423 | } |
| 424 | } |
| 425 | |
| 426 | if is_html { |
| 427 | g.writeln('veb__Context_html(${g.veb_context_html_arg()}, _tmpl_res_${fn_name});') |
| 428 | g.writeln('strings__Builder_free(&sb_${fn_name});') |
| 429 | g.writeln('builtin__string_free(&_tmpl_res_${fn_name});') |
| 430 | } else { |
| 431 | // return $tmpl string |
| 432 | if g.inside_return_tmpl { |
| 433 | g.writeln('return _tmpl_res_${fn_name};') |
| 434 | } else { |
| 435 | g.write(cur_line) |
| 436 | g.write('_tmpl_res_${fn_name}') |
| 437 | } |
| 438 | } |
| 439 | return |
| 440 | } |
| 441 | mut left_type := g.resolved_expr_type(node.left, node.left_type) |
| 442 | if left_type == 0 { |
| 443 | left_type = node.left_type |
| 444 | } |
| 445 | left_type = g.unwrap_generic(g.recheck_concrete_type(left_type)) |
| 446 | sym := g.table.sym(left_type) |
| 447 | g.trace_autofree('// \$method call. sym="${sym.name}"') |
| 448 | if node.method_name == 'method' { |
| 449 | // `app.$method()` |
| 450 | m := g.table.find_method(sym, g.comptime.comptime_for_method.name) or { return } |
| 451 | /* |
| 452 | vals := m.attrs[0].split('/') |
| 453 | args := vals.filter(it.starts_with(':')).map(it[1..]) |
| 454 | println(vals) |
| 455 | for val in vals { |
| 456 | } |
| 457 | */ |
| 458 | if g.inside_call && m.return_type == ast.void_type { |
| 459 | g.error('method `${m.name}()` (no value) used as value', node.pos) |
| 460 | } |
| 461 | expand_strs := g.comptime_call_expands_string_args(m, node) |
| 462 | mut has_decompose := !m.is_variadic && node.args.any(it.expr is ast.ArrayDecompose) |
| 463 | && !expand_strs |
| 464 | // check argument length and types |
| 465 | if m.params.len - 1 != node.args.len && !expand_strs { |
| 466 | if g.inside_call { |
| 467 | g.error('expected ${m.params.len - 1} arguments to method ${sym.name}.${m.name}, but got ${node.args.len}', |
| 468 | node.pos) |
| 469 | } else { |
| 470 | if !has_decompose { |
| 471 | // do not generate anything if the argument lengths don't match |
| 472 | g.writeln('/* skipping ${sym.name}.${m.name} due to mismatched arguments list: node.args=${node.args.len} m.params=${m.params.len} */') |
| 473 | // Adding a println(_S(...)) like this breaks options |
| 474 | return |
| 475 | } |
| 476 | } |
| 477 | } |
| 478 | |
| 479 | mut has_unwrap := false |
| 480 | if !g.inside_call && node.or_block.kind != .block && m.return_type.has_option_or_result() { |
| 481 | if !(g.assign_ct_type[node.pos.pos] != 0 |
| 482 | && g.assign_ct_type[node.pos.pos].has_option_or_result()) { |
| 483 | g.write('(*(${g.base_type(m.return_type)}*)') |
| 484 | has_unwrap = true |
| 485 | } |
| 486 | } |
| 487 | // TODO: check argument types |
| 488 | // Use the declaring receiver type for the symbol prefix, so alias |
| 489 | // comptime calls can dispatch to inherited methods. |
| 490 | method_receiver_type := g.unwrap_generic(m.params[0].typ) |
| 491 | g.write('${g.cc_type(method_receiver_type, false)}_${g.comptime.comptime_for_method.name}(') |
| 492 | |
| 493 | // try to see if we need to pass a pointer |
| 494 | if mut node.left is ast.Ident { |
| 495 | if mut node.left.obj is ast.Var { |
| 496 | if m.params[0].typ.is_ptr() && !node.left.obj.typ.is_ptr() |
| 497 | && !node.left.obj.is_auto_deref { |
| 498 | g.write('&') |
| 499 | } else if !m.params[0].typ.is_ptr() && node.left.obj.typ.is_ptr() { |
| 500 | g.write('*'.repeat(node.left.obj.typ.nr_muls())) |
| 501 | } else if !m.params[0].typ.is_ptr() && node.left.obj.is_auto_deref { |
| 502 | g.write('*') |
| 503 | } |
| 504 | } |
| 505 | } |
| 506 | g.expr(node.left) |
| 507 | if m.params.len > 1 { |
| 508 | g.write(', ') |
| 509 | } |
| 510 | for i in 1 .. m.params.len { |
| 511 | if mut node.left is ast.Ident { |
| 512 | if m.params[i].name == node.left.name { |
| 513 | continue |
| 514 | } |
| 515 | } |
| 516 | if i - 1 <= node.args.len - 1 && has_decompose |
| 517 | && node.args[i - 1].expr is ast.ArrayDecompose { |
| 518 | mut d_count := 0 |
| 519 | for d_i in i .. m.params.len { |
| 520 | g.write('*(${g.styp(m.params[d_i].typ)}*)builtin__array_get(') |
| 521 | g.expr(ast.Expr(node.args[i - 1].expr)) |
| 522 | g.write(', ${d_count})') |
| 523 | |
| 524 | if d_i < m.params.len - 1 { |
| 525 | g.write(', ') |
| 526 | } |
| 527 | d_count++ |
| 528 | } |
| 529 | break |
| 530 | } else if i - 1 < node.args.len - 1 { |
| 531 | g.expr(node.args[i - 1].expr) |
| 532 | if i < m.params.len - 1 { |
| 533 | g.write(', ') |
| 534 | } |
| 535 | } else if !expand_strs && i == node.args.len { |
| 536 | g.expr(node.args[i - 1].expr) |
| 537 | break |
| 538 | } else { |
| 539 | // last argument; try to expand if it's []string |
| 540 | idx := i - node.args.len |
| 541 | last_arg := g.expr_string(node.args.last().expr) |
| 542 | // t := g.table.sym(m.params[i].typ) |
| 543 | // g.write('/*nr_args=${node.args.len} m.params.len=${m.params.len} i=${i} t=${t.name} ${m.params}*/') |
| 544 | if m.params[i].typ.is_int() || m.params[i].typ.is_float() |
| 545 | || m.params[i].typ.idx() == ast.bool_type_idx { |
| 546 | // Gets the type name and cast the string to the type with the string_<type> function |
| 547 | type_name := g.table.type_symbols[int(m.params[i].typ)].str() |
| 548 | g.write('builtin__string_${type_name}(((string*)${last_arg}.data) [${idx}])') |
| 549 | } else { |
| 550 | g.write('((string*)${last_arg}.data) [${idx}] ') |
| 551 | } |
| 552 | if i < m.params.len - 1 { |
| 553 | g.write(', ') |
| 554 | } |
| 555 | } |
| 556 | } |
| 557 | g.write(')') |
| 558 | if has_unwrap { |
| 559 | g.write('.data)') |
| 560 | } |
| 561 | if node.or_block.kind != .absent && m.return_type.has_option_or_result() { |
| 562 | if !g.inside_assign { |
| 563 | cur_line := g.go_before_last_stmt() |
| 564 | tmp_var := g.new_tmp_var() |
| 565 | g.write2('${g.styp(m.return_type)} ${tmp_var} = ', cur_line) |
| 566 | g.or_block(tmp_var, node.or_block, m.return_type) |
| 567 | } |
| 568 | } |
| 569 | return |
| 570 | } |
| 571 | mut j := 0 |
| 572 | for method in sym.methods { |
| 573 | // if method.return_type != ast.void_type { |
| 574 | if method.return_type != node.result_type { |
| 575 | continue |
| 576 | } |
| 577 | if method.params.len != 1 { |
| 578 | continue |
| 579 | } |
| 580 | // receiver := method.args[0] |
| 581 | // if !p.expr_var.ptr { |
| 582 | // p.error('`${p.expr_var.name}` needs to be a reference') |
| 583 | // } |
| 584 | amp := '' // if receiver.is_mut && !p.expr_var.ptr { '&' } else { '' } |
| 585 | if node.is_template { |
| 586 | if j > 0 { |
| 587 | g.write(' else ') |
| 588 | } |
| 589 | g.write('if (builtin__string__eq(${node.method_name}, _S("${method.name}"))) ') |
| 590 | } |
| 591 | g.write('${g.cc_type(left_type, false)}_${method.name}(${amp} ') |
| 592 | g.expr(node.left) |
| 593 | g.writeln(');') |
| 594 | j++ |
| 595 | } |
| 596 | } |
| 597 | |
| 598 | fn cgen_attrs(attrs []ast.Attr) []string { |
| 599 | mut res := []string{cap: attrs.len} |
| 600 | for attr in attrs { |
| 601 | mut s := attr.name |
| 602 | if attr.has_arg { |
| 603 | mut arg := attr.arg |
| 604 | if attr.kind == .string { |
| 605 | quote := if attr.quote == `"` { '"' } else { "'" } |
| 606 | arg = '${quote}${arg}${quote}' |
| 607 | } |
| 608 | s += ': ${arg}' |
| 609 | } |
| 610 | res << '_S("${cescape_nonascii(util.smart_quote(s, false))}")' |
| 611 | } |
| 612 | return res |
| 613 | } |
| 614 | |
| 615 | fn cgen_vattrs(attrs []ast.Attr) []string { |
| 616 | mut res := []string{cap: attrs.len} |
| 617 | for attr in attrs { |
| 618 | name := cescape_nonascii(util.smart_quote(attr.name, false)) |
| 619 | arg := cescape_nonascii(util.smart_quote(attr.arg, false)) |
| 620 | res << '((VAttribute){.name=_S("${name}"),.has_arg=${attr.has_arg},.arg=_S("${arg}"),.kind=AttributeKind__${attr.kind}})' |
| 621 | } |
| 622 | return res |
| 623 | } |
| 624 | |
| 625 | fn (mut g Gen) comptime_at(node ast.AtExpr) { |
| 626 | if node.kind == .vmod_file { |
| 627 | val := cescape_nonascii(util.smart_quote(node.val, false)) |
| 628 | g.write('_S("${val}")') |
| 629 | } else { |
| 630 | val := node.val.replace('\\', '\\\\') |
| 631 | g.write('_S("${val}")') |
| 632 | } |
| 633 | } |
| 634 | |
| 635 | // gen_branch_context_string generate current branches context string. |
| 636 | // context include generic types, `$for`. |
| 637 | fn (mut g Gen) recover_specialized_generic_context_for(fn_name string) ([]string, []ast.Type) { |
| 638 | if fn_name == '' { |
| 639 | return []string{}, []ast.Type{} |
| 640 | } |
| 641 | if t_idx := fn_name.index('_T_') { |
| 642 | if t_idx <= 0 { |
| 643 | return []string{}, []ast.Type{} |
| 644 | } |
| 645 | } else { |
| 646 | return []string{}, []ast.Type{} |
| 647 | } |
| 648 | for generic_fn in g.file.generic_fns { |
| 649 | if generic_fn.generic_names.len == 0 { |
| 650 | // Methods on generic structs may have no explicit generic_names |
| 651 | // but still have receiver generics. |
| 652 | if !(generic_fn.is_method && generic_fn.receiver.typ.has_flag(.generic)) { |
| 653 | continue |
| 654 | } |
| 655 | } |
| 656 | for base_name in [generic_fn.name, generic_fn.fkey()] { |
| 657 | if !fn_name.starts_with(base_name + '_T_') { |
| 658 | continue |
| 659 | } |
| 660 | mut generic_names := generic_fn.generic_names.clone() |
| 661 | fkey := generic_fn.fkey() |
| 662 | for concrete_types in g.table.fn_generic_types[fkey] { |
| 663 | if concrete_types.any(it.has_flag(.generic)) { |
| 664 | continue |
| 665 | } |
| 666 | if g.generic_fn_name(concrete_types, base_name) != fn_name { |
| 667 | if !generic_fn.is_method { |
| 668 | continue |
| 669 | } |
| 670 | receiver_generic_names := g.table.generic_type_names(generic_fn.receiver.typ) |
| 671 | if receiver_generic_names.len == 0 |
| 672 | || concrete_types.len <= receiver_generic_names.len { |
| 673 | continue |
| 674 | } |
| 675 | method_only_concrete_types := concrete_types[receiver_generic_names.len..] |
| 676 | if g.generic_fn_name(method_only_concrete_types, base_name) != fn_name { |
| 677 | continue |
| 678 | } |
| 679 | } |
| 680 | if generic_fn.is_method && concrete_types.len > generic_names.len { |
| 681 | receiver_generic_names := g.table.generic_type_names(generic_fn.receiver.typ) |
| 682 | if receiver_generic_names.len > 0 { |
| 683 | mut effective_generic_names := []string{cap: receiver_generic_names.len + |
| 684 | generic_names.len} |
| 685 | for name in receiver_generic_names { |
| 686 | if name !in effective_generic_names { |
| 687 | effective_generic_names << name |
| 688 | } |
| 689 | } |
| 690 | for name in generic_names { |
| 691 | if name !in effective_generic_names { |
| 692 | effective_generic_names << name |
| 693 | } |
| 694 | } |
| 695 | if effective_generic_names.len == concrete_types.len { |
| 696 | generic_names = effective_generic_names.clone() |
| 697 | } |
| 698 | } |
| 699 | } |
| 700 | if generic_names.len == concrete_types.len { |
| 701 | return generic_names, concrete_types |
| 702 | } |
| 703 | return []string{}, concrete_types |
| 704 | } |
| 705 | } |
| 706 | } |
| 707 | return []string{}, []ast.Type{} |
| 708 | } |
| 709 | |
| 710 | fn (mut g Gen) gen_branch_context_string() string { |
| 711 | mut arr := []string{} |
| 712 | |
| 713 | // gen `T=int,X=string` |
| 714 | mut generic_names := if g.cur_fn != unsafe { nil } { |
| 715 | g.cur_fn.generic_names.clone() |
| 716 | } else { |
| 717 | []string{} |
| 718 | } |
| 719 | // For methods on generic structs with no explicit generic params, |
| 720 | // extract generic names from the receiver type (mirrors checker's effective_fn_generic_names). |
| 721 | if generic_names.len == 0 && g.cur_fn != unsafe { nil } && g.cur_fn.is_method |
| 722 | && g.cur_fn.receiver.typ.has_flag(.generic) { |
| 723 | generic_names = g.table.generic_type_names(g.cur_fn.receiver.typ) |
| 724 | } |
| 725 | mut concrete_types := g.cur_concrete_types.clone() |
| 726 | if generic_names.len == 0 || generic_names.len != concrete_types.len { |
| 727 | recovered_generic_names, recovered_concrete_types := g.recover_specialized_generic_context_for(if g.cur_fn != unsafe { nil } { |
| 728 | g.cur_fn.name |
| 729 | } else { |
| 730 | '' |
| 731 | }) |
| 732 | if recovered_generic_names.len > 0 |
| 733 | && recovered_generic_names.len == recovered_concrete_types.len { |
| 734 | generic_names = recovered_generic_names.clone() |
| 735 | concrete_types = recovered_concrete_types.clone() |
| 736 | } |
| 737 | } |
| 738 | if generic_names.len > 0 && generic_names.len == concrete_types.len { |
| 739 | for i in 0 .. generic_names.len { |
| 740 | arr << generic_names[i] + '=' + |
| 741 | util.strip_main_name(g.table.type_to_str(concrete_types[i])) |
| 742 | } |
| 743 | } |
| 744 | |
| 745 | // gen comptime `$for` |
| 746 | if g.comptime.inside_comptime_for { |
| 747 | // variants |
| 748 | if g.comptime.comptime_for_variant_var.len > 0 { |
| 749 | variant := g.table.type_to_str(g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_variant_var}.typ', |
| 750 | ast.no_type)) |
| 751 | arr << g.comptime.comptime_for_variant_var + '.typ=' + variant |
| 752 | } |
| 753 | // fields |
| 754 | if g.comptime.comptime_for_field_var.len > 0 { |
| 755 | arr << g.comptime.comptime_for_field_var + '.name=' + |
| 756 | g.comptime.comptime_for_field_value.name |
| 757 | } |
| 758 | // values |
| 759 | if g.comptime.comptime_for_enum_var.len > 0 { |
| 760 | enum_var := g.table.type_to_str(g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_enum_var}.typ', |
| 761 | ast.void_type)) |
| 762 | arr << g.comptime.comptime_for_enum_var + '.typ=' + enum_var |
| 763 | } |
| 764 | // attributes |
| 765 | if g.comptime.comptime_for_attr_var.len > 0 { |
| 766 | arr << g.comptime.comptime_for_attr_var + '.name=' + |
| 767 | g.comptime.comptime_for_attr_value.name |
| 768 | } |
| 769 | // methods |
| 770 | if g.comptime.comptime_for_method_var.len > 0 { |
| 771 | arr << g.comptime.comptime_for_method_var + '.name=' + |
| 772 | g.comptime.comptime_for_method.name |
| 773 | } |
| 774 | // args |
| 775 | if g.comptime.comptime_for_method_param_var.len > 0 { |
| 776 | arg_var := g.table.type_to_str(g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_method_param_var}.typ', |
| 777 | ast.void_type)) |
| 778 | arr << g.comptime.comptime_for_method_param_var + '.typ=' + arg_var |
| 779 | } |
| 780 | } |
| 781 | return arr.join(',') |
| 782 | } |
| 783 | |
| 784 | fn (g &Gen) can_preserve_comptime_if_condition_in_c(cond ast.Expr) bool { |
| 785 | match cond { |
| 786 | ast.BoolLiteral { |
| 787 | return true |
| 788 | } |
| 789 | ast.Ident { |
| 790 | // CPU architecture and compiler conditions are safe to preserve as C |
| 791 | // preprocessor checks because they are mutually exclusive categories |
| 792 | // derived from real compiler intrinsics (see cheaders.v). |
| 793 | // This is important for Android builds where one C file is compiled |
| 794 | // by the NDK for multiple architectures (arm64, armv7a, x86, x86_64). |
| 795 | return cond.name in ast.valid_comptime_if_platforms |
| 796 | || cond.name in ast.valid_comptime_if_compilers |
| 797 | } |
| 798 | ast.ParExpr { |
| 799 | return g.can_preserve_comptime_if_condition_in_c(cond.expr) |
| 800 | } |
| 801 | ast.PrefixExpr { |
| 802 | return cond.op == .not && g.can_preserve_comptime_if_condition_in_c(cond.right) |
| 803 | } |
| 804 | ast.InfixExpr { |
| 805 | return cond.op in [.and, .logical_or] |
| 806 | && g.can_preserve_comptime_if_condition_in_c(cond.left) |
| 807 | && g.can_preserve_comptime_if_condition_in_c(cond.right) |
| 808 | } |
| 809 | ast.PostfixExpr { |
| 810 | return cond.op == .question && cond.expr is ast.Ident |
| 811 | } |
| 812 | else { |
| 813 | return false |
| 814 | } |
| 815 | } |
| 816 | } |
| 817 | |
| 818 | fn (g &Gen) comptime_if_condition_for_c(cond ast.Expr, result ast.ComptTimeCondResult) string { |
| 819 | if g.pref.output_cross_c || g.can_preserve_comptime_if_condition_in_c(cond) { |
| 820 | return result.c_str |
| 821 | } |
| 822 | // For normal C generation, honor the branch result that V already resolved. |
| 823 | // Re-evaluating built-in comptime conditions in the C preprocessor can pick |
| 824 | // a different branch than the V checker, e.g. `termux` vs `linux`. |
| 825 | return if result.val { '1' } else { '0' } |
| 826 | } |
| 827 | |
| 828 | fn (mut g Gen) comptime_if(node ast.IfExpr) { |
| 829 | tmp_var := g.new_tmp_var() |
| 830 | mut inferred_typ := node.typ |
| 831 | if node.is_expr && node.typ == ast.void_type && node.branches.len > 0 { |
| 832 | for branch in node.branches { |
| 833 | if branch.stmts.len > 0 { |
| 834 | last_stmt := branch.stmts.last() |
| 835 | if last_stmt is ast.ExprStmt { |
| 836 | expr_typ := g.type_resolver.get_type_or_default(last_stmt.expr, last_stmt.typ) |
| 837 | if expr_typ != ast.void_type && !expr_typ.has_flag(.generic) { |
| 838 | inferred_typ = expr_typ |
| 839 | break |
| 840 | } |
| 841 | } |
| 842 | } |
| 843 | } |
| 844 | } |
| 845 | is_opt_or_result := inferred_typ.has_option_or_result() |
| 846 | is_array_fixed := g.table.final_sym(inferred_typ).kind == .array_fixed |
| 847 | line := if node.is_expr && inferred_typ != ast.void_type { |
| 848 | stmt_str := g.go_before_last_stmt() |
| 849 | g.write(util.tabs(g.indent)) |
| 850 | styp := g.styp(inferred_typ) |
| 851 | g.writeln('${styp} ${tmp_var};') |
| 852 | stmt_str |
| 853 | } else { |
| 854 | '' |
| 855 | } |
| 856 | |
| 857 | // save node for processing hash stmts |
| 858 | // we only save the first node, when there is embedded $if |
| 859 | old_curr_comptime_node := g.curr_comptime_node |
| 860 | if !g.comptime.inside_comptime_if { |
| 861 | g.curr_comptime_node = ast.Expr(node) |
| 862 | } |
| 863 | defer { |
| 864 | g.curr_comptime_node = old_curr_comptime_node |
| 865 | } |
| 866 | mut comptime_branch_context_str := g.gen_branch_context_string() |
| 867 | mut is_true := ast.ComptTimeCondResult{} |
| 868 | for i, branch in node.branches { |
| 869 | g.push_new_comptime_info() |
| 870 | g.comptime.inside_comptime_if = true |
| 871 | start_pos := g.out.len |
| 872 | // `idx_str` is composed of two parts: |
| 873 | // The first part represents the current context of the branch statement, `comptime_branch_context_str`, formatted like `T=int,X=string,method.name=json` |
| 874 | // The second part is the branch's id. |
| 875 | // This format must match what is in `checker`. |
| 876 | mut idx_str := comptime_branch_context_str + '|id=${branch.id}|' |
| 877 | if g.comptime.inside_comptime_for && g.comptime.comptime_for_field_var != '' { |
| 878 | idx_str += '|field_type=${g.comptime.comptime_for_field_type}|' |
| 879 | } |
| 880 | if comptime_is_true := g.table.comptime_is_true[idx_str] { |
| 881 | // `g.table.comptime_is_true` are the branch condition results set by `checker` |
| 882 | is_true = comptime_is_true |
| 883 | } else { |
| 884 | // No checker data found for this key. This can happen when: |
| 885 | // 1. The markused walker spuriously registers generic instantiations |
| 886 | // that the checker never evaluated (e.g. from dead comptime branches). |
| 887 | // 2. Type alias resolution causes key mismatches. |
| 888 | // Default all branches to false (#if 0) since the function body |
| 889 | // is either dead code or will be generated correctly by another |
| 890 | // instantiation with matching types. |
| 891 | is_true = ast.ComptTimeCondResult{} |
| 892 | } |
| 893 | if !node.has_else || i < node.branches.len - 1 { |
| 894 | if i == 0 { |
| 895 | g.write('#if ') |
| 896 | } else { |
| 897 | g.write('#elif ') |
| 898 | } |
| 899 | g.writeln(g.comptime_if_condition_for_c(branch.cond, is_true)) |
| 900 | $if debug_comptime_branch_context ? { |
| 901 | g.writeln('/* ${node.branches[i].cond} | generic=[${comptime_branch_context_str}] */') |
| 902 | } |
| 903 | } else { |
| 904 | g.writeln('#else') |
| 905 | } |
| 906 | expr_str := g.out.last_n(g.out.len - start_pos).trim_space() |
| 907 | if expr_str != '' { |
| 908 | if g.defer_ifdef != '' { |
| 909 | g.defer_ifdef += '\n' + '\t'.repeat(g.indent + 1) |
| 910 | } |
| 911 | g.defer_ifdef += expr_str |
| 912 | } |
| 913 | if node.is_expr { |
| 914 | if is_true.val { |
| 915 | g.bind_comptime_if_generic_types(branch.cond) |
| 916 | len := branch.stmts.len |
| 917 | if len > 0 { |
| 918 | last := branch.stmts.last() as ast.ExprStmt |
| 919 | if len > 1 { |
| 920 | g.indent++ |
| 921 | g.writeln('{') |
| 922 | g.stmts(branch.stmts[..len - 1]) |
| 923 | g.set_current_pos_as_last_stmt_pos() |
| 924 | prev_skip_stmt_pos := g.skip_stmt_pos |
| 925 | g.skip_stmt_pos = true |
| 926 | if is_opt_or_result { |
| 927 | tmp_var2 := g.new_tmp_var() |
| 928 | g.write('{ ${g.base_type(inferred_typ)} ${tmp_var2} = ') |
| 929 | g.stmt(last) |
| 930 | g.writeln('builtin___result_ok(&(${g.base_type(inferred_typ)}[]) { ${tmp_var2} }, (_result*)(&${tmp_var}), sizeof(${g.base_type(inferred_typ)}));') |
| 931 | g.writeln('}') |
| 932 | } else { |
| 933 | g.write('\t${tmp_var} = ') |
| 934 | g.stmt(last) |
| 935 | } |
| 936 | g.skip_stmt_pos = prev_skip_stmt_pos |
| 937 | g.writeln2(';', '}') |
| 938 | g.indent-- |
| 939 | g.write_defer_stmts(branch.scope, false, branch.pos) |
| 940 | } else { |
| 941 | g.indent++ |
| 942 | g.set_current_pos_as_last_stmt_pos() |
| 943 | prev_skip_stmt_pos := g.skip_stmt_pos |
| 944 | g.skip_stmt_pos = true |
| 945 | if is_opt_or_result { |
| 946 | tmp_var2 := g.new_tmp_var() |
| 947 | base_styp := g.base_type(inferred_typ) |
| 948 | g.write('{ ${base_styp} ${tmp_var2} = ') |
| 949 | g.stmt(last) |
| 950 | g.writeln('builtin___result_ok(&(${base_styp}[]) { ${tmp_var2} }, (_result*)(&${tmp_var}), sizeof(${base_styp}));') |
| 951 | g.writeln('}') |
| 952 | } else if is_array_fixed { |
| 953 | tmp_var2 := g.new_tmp_var() |
| 954 | base_styp := g.base_type(inferred_typ) |
| 955 | g.write('{ ${base_styp} ${tmp_var2} = ') |
| 956 | g.stmt(last) |
| 957 | if g.out.last_n(2).contains(';') { |
| 958 | g.go_back(2) |
| 959 | } |
| 960 | g.writeln(';') |
| 961 | g.writeln2('memcpy(&${tmp_var}, &${tmp_var2}, sizeof(${base_styp}));', |
| 962 | '}') |
| 963 | } else { |
| 964 | g.write('${tmp_var} = ') |
| 965 | g.stmt(last) |
| 966 | } |
| 967 | g.skip_stmt_pos = prev_skip_stmt_pos |
| 968 | g.writeln(';') |
| 969 | g.indent-- |
| 970 | g.write_defer_stmts(branch.scope, false, branch.pos) |
| 971 | } |
| 972 | } |
| 973 | } |
| 974 | } else { |
| 975 | // Only wrap the contents in {} if we're inside a function, not on the top level scope |
| 976 | should_create_scope := unsafe { g.fn_decl != 0 } |
| 977 | if should_create_scope { |
| 978 | g.writeln('{') |
| 979 | } |
| 980 | if is_true.val || g.pref.output_cross_c { |
| 981 | g.bind_comptime_if_generic_types(branch.cond) |
| 982 | g.stmts(branch.stmts) |
| 983 | } |
| 984 | if should_create_scope { |
| 985 | g.write_defer_stmts(branch.scope, false, branch.pos) |
| 986 | g.writeln('}') |
| 987 | } |
| 988 | } |
| 989 | g.pop_comptime_info() |
| 990 | } |
| 991 | g.defer_ifdef = '' |
| 992 | g.writeln('#endif') |
| 993 | if node.is_expr { |
| 994 | g.write('${line}${tmp_var}') |
| 995 | } |
| 996 | } |
| 997 | |
| 998 | fn (mut g Gen) bind_comptime_if_generic_types(cond ast.Expr) { |
| 999 | match cond { |
| 1000 | ast.ParExpr { |
| 1001 | g.bind_comptime_if_generic_types(cond.expr) |
| 1002 | } |
| 1003 | ast.PrefixExpr { |
| 1004 | g.bind_comptime_if_generic_types(cond.right) |
| 1005 | } |
| 1006 | ast.Likely { |
| 1007 | g.bind_comptime_if_generic_types(cond.expr) |
| 1008 | } |
| 1009 | ast.InfixExpr { |
| 1010 | match cond.op { |
| 1011 | .and, .logical_or { |
| 1012 | g.bind_comptime_if_generic_types(cond.left) |
| 1013 | g.bind_comptime_if_generic_types(cond.right) |
| 1014 | } |
| 1015 | .key_is { |
| 1016 | if cond.right is ast.TypeNode { |
| 1017 | g.type_resolver.bind_matching_generic_type(g.get_expr_type(cond.left), |
| 1018 | cond.right.typ) |
| 1019 | } |
| 1020 | } |
| 1021 | .key_in { |
| 1022 | if cond.right is ast.ArrayInit { |
| 1023 | left_type := g.get_expr_type(cond.left) |
| 1024 | for expr in cond.right.exprs { |
| 1025 | if expr is ast.TypeNode |
| 1026 | && g.type_resolver.bind_matching_generic_type(left_type, expr.typ) { |
| 1027 | break |
| 1028 | } |
| 1029 | } |
| 1030 | } |
| 1031 | } |
| 1032 | else {} |
| 1033 | } |
| 1034 | } |
| 1035 | else {} |
| 1036 | } |
| 1037 | } |
| 1038 | |
| 1039 | fn (mut g Gen) get_expr_type(cond ast.Expr) ast.Type { |
| 1040 | match cond { |
| 1041 | ast.Ident { |
| 1042 | if cond.name in g.type_resolver.type_map { |
| 1043 | return g.type_resolver.get_ct_type_or_default(cond.name, ast.void_type) |
| 1044 | } |
| 1045 | return g.unwrap_generic(g.type_resolver.get_type_or_default(cond, cond.obj.typ)) |
| 1046 | } |
| 1047 | ast.TypeNode { |
| 1048 | return g.unwrap_generic(cond.typ) |
| 1049 | } |
| 1050 | ast.SelectorExpr { |
| 1051 | if cond.name_type != 0 |
| 1052 | && cond.field_name in ['key_type', 'value_type', 'element_type', 'pointee_type', 'payload_type', 'variant_types'] { |
| 1053 | return g.type_resolver.typeof_field_type(cond.name_type, cond.field_name) |
| 1054 | } |
| 1055 | if cond.gkind_field == .typ { |
| 1056 | return g.unwrap_generic(cond.name_type) |
| 1057 | } else if cond.gkind_field == .unaliased_typ { |
| 1058 | return g.table.unaliased_type(g.unwrap_generic(cond.name_type)) |
| 1059 | } else if cond.gkind_field == .indirections { |
| 1060 | return ast.int_type |
| 1061 | } else { |
| 1062 | if cond.expr is ast.TypeOf { |
| 1063 | return g.type_resolver.typeof_field_type(g.type_resolver.typeof_type(cond.expr.expr, |
| 1064 | cond.name_type), cond.field_name) |
| 1065 | } |
| 1066 | name := '${cond.expr}.${cond.field_name}' |
| 1067 | if name in g.type_resolver.type_map { |
| 1068 | return g.type_resolver.get_ct_type_or_default(name, ast.void_type) |
| 1069 | } else { |
| 1070 | return g.unwrap_generic(cond.typ) |
| 1071 | } |
| 1072 | } |
| 1073 | } |
| 1074 | ast.IntegerLiteral { |
| 1075 | return ast.int_type |
| 1076 | } |
| 1077 | ast.BoolLiteral { |
| 1078 | return ast.bool_type |
| 1079 | } |
| 1080 | ast.StringLiteral { |
| 1081 | return ast.string_type |
| 1082 | } |
| 1083 | ast.CharLiteral { |
| 1084 | return ast.char_type |
| 1085 | } |
| 1086 | ast.FloatLiteral { |
| 1087 | return ast.f64_type |
| 1088 | } |
| 1089 | else { |
| 1090 | return ast.void_type |
| 1091 | } |
| 1092 | } |
| 1093 | } |
| 1094 | |
| 1095 | // push_new_comptime_info saves the current comptime information |
| 1096 | fn (mut g Gen) push_new_comptime_info() { |
| 1097 | g.clear_type_resolution_caches() |
| 1098 | g.type_resolver.info_stack << type_resolver.ResolverInfo{ |
| 1099 | saved_type_map: g.type_resolver.type_map.clone() |
| 1100 | inside_comptime_for: g.comptime.inside_comptime_for |
| 1101 | inside_comptime_if: g.comptime.inside_comptime_if |
| 1102 | comptime_for_variant_var: g.comptime.comptime_for_variant_var |
| 1103 | comptime_for_field_var: g.comptime.comptime_for_field_var |
| 1104 | comptime_for_field_type: g.comptime.comptime_for_field_type |
| 1105 | comptime_for_field_value: g.comptime.comptime_for_field_value |
| 1106 | comptime_for_enum_var: g.comptime.comptime_for_enum_var |
| 1107 | comptime_for_attr_var: g.comptime.comptime_for_attr_var |
| 1108 | comptime_for_attr_value: g.comptime.comptime_for_attr_value |
| 1109 | comptime_for_method_var: g.comptime.comptime_for_method_var |
| 1110 | comptime_for_method: g.comptime.comptime_for_method |
| 1111 | comptime_for_method_ret_type: g.comptime.comptime_for_method_ret_type |
| 1112 | comptime_loop_id: g.comptime.comptime_loop_id++ |
| 1113 | } |
| 1114 | } |
| 1115 | |
| 1116 | // pop_comptime_info pops the current comptime information frame |
| 1117 | fn (mut g Gen) pop_comptime_info() { |
| 1118 | old := g.type_resolver.info_stack.pop() |
| 1119 | g.type_resolver.type_map = old.saved_type_map.clone() |
| 1120 | g.comptime.inside_comptime_for = old.inside_comptime_for |
| 1121 | g.comptime.inside_comptime_if = old.inside_comptime_if |
| 1122 | g.comptime.comptime_for_variant_var = old.comptime_for_variant_var |
| 1123 | g.comptime.comptime_for_field_var = old.comptime_for_field_var |
| 1124 | g.comptime.comptime_for_field_type = old.comptime_for_field_type |
| 1125 | g.comptime.comptime_for_field_value = old.comptime_for_field_value |
| 1126 | g.comptime.comptime_for_enum_var = old.comptime_for_enum_var |
| 1127 | g.comptime.comptime_for_attr_var = old.comptime_for_attr_var |
| 1128 | g.comptime.comptime_for_attr_value = old.comptime_for_attr_value |
| 1129 | g.comptime.comptime_for_method_var = old.comptime_for_method_var |
| 1130 | g.comptime.comptime_for_method = old.comptime_for_method |
| 1131 | g.comptime.comptime_for_method_ret_type = old.comptime_for_method_ret_type |
| 1132 | g.clear_type_resolution_caches() |
| 1133 | } |
| 1134 | |
| 1135 | fn (mut g Gen) eval_comptime_for_if_cond(cond ast.Expr) ?bool { |
| 1136 | match cond { |
| 1137 | ast.ParExpr { |
| 1138 | return g.eval_comptime_for_if_cond(cond.expr) |
| 1139 | } |
| 1140 | ast.PrefixExpr { |
| 1141 | if cond.op == .not { |
| 1142 | return !(g.eval_comptime_for_if_cond(cond.right)?) |
| 1143 | } |
| 1144 | } |
| 1145 | ast.InfixExpr { |
| 1146 | match cond.op { |
| 1147 | .and, .logical_or { |
| 1148 | left := g.eval_comptime_for_if_cond(cond.left)? |
| 1149 | right := g.eval_comptime_for_if_cond(cond.right)? |
| 1150 | return if cond.op == .and { left && right } else { left || right } |
| 1151 | } |
| 1152 | .key_is, .not_is { |
| 1153 | if cond.left is ast.SelectorExpr && cond.right is ast.TypeNode { |
| 1154 | if cond.left.field_name == 'return_type' && cond.left.expr is ast.Ident |
| 1155 | && cond.left.expr.name == g.comptime.comptime_for_method_var { |
| 1156 | left_type := |
| 1157 | g.table.unaliased_type(g.unwrap_generic(g.comptime.comptime_for_method_ret_type)) |
| 1158 | right_type := g.table.unaliased_type(g.unwrap_generic(cond.right.typ)) |
| 1159 | is_true := left_type == right_type |
| 1160 | return if cond.op == .key_is { is_true } else { !is_true } |
| 1161 | } |
| 1162 | } |
| 1163 | } |
| 1164 | .eq, .ne { |
| 1165 | if cond.left is ast.SelectorExpr && cond.right is ast.StringLiteral { |
| 1166 | if cond.left.field_name == 'name' && cond.left.expr is ast.Ident |
| 1167 | && cond.left.expr.name == g.comptime.comptime_for_method_var { |
| 1168 | is_true := g.comptime.comptime_for_method.name == cond.right.val |
| 1169 | return if cond.op == .eq { is_true } else { !is_true } |
| 1170 | } |
| 1171 | } |
| 1172 | } |
| 1173 | else {} |
| 1174 | } |
| 1175 | } |
| 1176 | else {} |
| 1177 | } |
| 1178 | |
| 1179 | return none |
| 1180 | } |
| 1181 | |
| 1182 | fn (mut g Gen) comptime_if_has_live_branch(node ast.IfExpr) bool { |
| 1183 | if g.pref.output_cross_c || node.has_else { |
| 1184 | return true |
| 1185 | } |
| 1186 | comptime_branch_context_str := g.gen_branch_context_string() |
| 1187 | for branch in node.branches { |
| 1188 | if evaluated := g.eval_comptime_for_if_cond(branch.cond) { |
| 1189 | if evaluated { |
| 1190 | return true |
| 1191 | } |
| 1192 | continue |
| 1193 | } |
| 1194 | mut idx_str := comptime_branch_context_str + '|id=${branch.id}|' |
| 1195 | if g.comptime.inside_comptime_for && g.comptime.comptime_for_field_var != '' { |
| 1196 | idx_str += '|field_type=${g.comptime.comptime_for_field_type}|' |
| 1197 | } |
| 1198 | if is_true := g.table.comptime_is_true[idx_str] { |
| 1199 | if is_true.val { |
| 1200 | return true |
| 1201 | } |
| 1202 | } else { |
| 1203 | return true |
| 1204 | } |
| 1205 | } |
| 1206 | return false |
| 1207 | } |
| 1208 | |
| 1209 | fn (mut g Gen) comptime_for_iteration_has_live_stmts(stmts []ast.Stmt) bool { |
| 1210 | for stmt in stmts { |
| 1211 | match stmt { |
| 1212 | ast.EmptyStmt, ast.SemicolonStmt {} |
| 1213 | ast.ExprStmt { |
| 1214 | if stmt.expr is ast.IfExpr && stmt.expr.is_comptime { |
| 1215 | if g.comptime_if_has_live_branch(stmt.expr) { |
| 1216 | return true |
| 1217 | } |
| 1218 | } else { |
| 1219 | return true |
| 1220 | } |
| 1221 | } |
| 1222 | else { |
| 1223 | return true |
| 1224 | } |
| 1225 | } |
| 1226 | } |
| 1227 | return false |
| 1228 | } |
| 1229 | |
| 1230 | fn (mut g Gen) comptime_for(node ast.ComptimeFor) { |
| 1231 | resolved_typ := if node.expr !is ast.EmptyExpr { |
| 1232 | mut expr_typ := g.unwrap_generic(g.recheck_concrete_type(g.resolved_expr_type(node.expr, |
| 1233 | node.typ))) |
| 1234 | resolved_ct_typ := g.type_resolver.get_type(node.expr) |
| 1235 | if resolved_ct_typ != ast.void_type { |
| 1236 | expr_typ = g.unwrap_generic(g.recheck_concrete_type(resolved_ct_typ)) |
| 1237 | } |
| 1238 | expr_typ |
| 1239 | } else if node.typ != g.field_data_type { |
| 1240 | g.unwrap_generic(node.typ) |
| 1241 | } else { |
| 1242 | g.comptime.comptime_for_field_type |
| 1243 | } |
| 1244 | // When the resolved type is FieldData, the expression refers to a comptime |
| 1245 | // field variable (e.g. `$for f3 in f.fields` where `f` comes from an outer |
| 1246 | // `$for f in T.fields`). In that case use the actual field type from the |
| 1247 | // outer comptime loop instead of the FieldData descriptor type. |
| 1248 | for_typ := if resolved_typ == g.field_data_type { |
| 1249 | g.comptime.comptime_for_field_type |
| 1250 | } else { |
| 1251 | resolved_typ |
| 1252 | } |
| 1253 | sym := g.table.final_sym(for_typ) |
| 1254 | iter_sym_name := if node.kind == .methods { g.table.sym(for_typ).name } else { sym.name } |
| 1255 | g.writeln('/* \$for ${node.val_var} in ${iter_sym_name}.${node.kind.str()} */ {') |
| 1256 | g.indent++ |
| 1257 | mut i := 0 |
| 1258 | old_defer_stmts := g.defer_stmts |
| 1259 | if node.kind == .methods { |
| 1260 | methods := g.table.get_type_methods(for_typ) |
| 1261 | if methods.len > 0 { |
| 1262 | g.writeln('FunctionData ${node.val_var} = {0};') |
| 1263 | } |
| 1264 | typ_veb_result := g.table.find_type('veb.Result') |
| 1265 | for method in methods { |
| 1266 | g.defer_stmts = old_defer_stmts |
| 1267 | g.push_new_comptime_info() |
| 1268 | g.comptime.inside_comptime_for = true |
| 1269 | // filter veb route methods (non-generic method) |
| 1270 | if method.receiver_type != 0 && method.return_type == typ_veb_result { |
| 1271 | rec_sym := g.table.sym(method.receiver_type) |
| 1272 | if rec_sym.kind == .struct { |
| 1273 | if _ := g.table.find_field_with_embeds(rec_sym, 'Context') { |
| 1274 | if method.generic_names.len > 0 |
| 1275 | || (method.params.len > 1 && method.attrs.len == 0) { |
| 1276 | g.pop_comptime_info() |
| 1277 | continue |
| 1278 | } |
| 1279 | } |
| 1280 | } |
| 1281 | } |
| 1282 | g.comptime.comptime_for_method = unsafe { &method } |
| 1283 | g.comptime.comptime_for_method_var = node.val_var |
| 1284 | g.comptime.comptime_for_method_ret_type = method.return_type |
| 1285 | if !g.comptime_for_iteration_has_live_stmts(node.stmts) { |
| 1286 | g.pop_comptime_info() |
| 1287 | continue |
| 1288 | } |
| 1289 | g.writeln('/* method ${i} : ${method.name} */ {') |
| 1290 | g.writeln('\t${node.val_var}.name = _S("${method.name}");') |
| 1291 | mlocation := util.cescaped_path(util.path_styled_for_error_messages(method.file)) |
| 1292 | g.writeln('\t${node.val_var}.location = _S("${mlocation}:${method.name_pos.line_nr + 1}:${method.name_pos.col}");') |
| 1293 | if method.attrs.len == 0 { |
| 1294 | g.writeln('\t${node.val_var}.attrs = builtin____new_array_with_default(0, 0, sizeof(string), 0);') |
| 1295 | g.writeln('\t${node.val_var}.attributes = builtin____new_array_with_default(0, 0, sizeof(VAttribute), 0);') |
| 1296 | } else { |
| 1297 | attrs := cgen_attrs(method.attrs) |
| 1298 | vattrs := cgen_vattrs(method.attrs) |
| 1299 | g.writeln( |
| 1300 | '\t${node.val_var}.attrs = builtin__new_array_from_c_array(${attrs.len}, ${attrs.len}, sizeof(string), _MOV((string[${attrs.len}]){' + |
| 1301 | attrs.join(', ') + '}));\n') |
| 1302 | g.writeln( |
| 1303 | '\t${node.val_var}.attributes = builtin__new_array_from_c_array(${vattrs.len}, ${vattrs.len}, sizeof(VAttribute), _MOV((VAttribute[${vattrs.len}]){' + |
| 1304 | vattrs.join(', ') + '}));\n') |
| 1305 | } |
| 1306 | if method.params.len < 2 { |
| 1307 | // 0 or 1 (the receiver) args |
| 1308 | g.writeln('\t${node.val_var}.args = builtin____new_array_with_default(0, 0, sizeof(FunctionParam), 0);') |
| 1309 | } else { |
| 1310 | len := method.params.len - 1 |
| 1311 | g.write('\t${node.val_var}.args = builtin__new_array_from_c_array(${len}, ${len}, sizeof(FunctionParam), _MOV((FunctionParam[${len}]){') |
| 1312 | // Skip receiver arg |
| 1313 | for j, arg in method.params[1..] { |
| 1314 | typ := arg.typ.idx() |
| 1315 | g.write('{${typ.str()}, _S("${arg.name}")}') |
| 1316 | if j < len - 1 { |
| 1317 | g.write(', ') |
| 1318 | } |
| 1319 | g.type_resolver.update_ct_type('${node.val_var}.args[${j}].typ', typ) |
| 1320 | } |
| 1321 | g.writeln('}));\n') |
| 1322 | } |
| 1323 | mut sig := 'fn (' |
| 1324 | // skip the first (receiver) arg |
| 1325 | for j, arg in method.params[1..] { |
| 1326 | // TODO: ignore mut/pts in sig for now |
| 1327 | typ := arg.typ.set_nr_muls(0) |
| 1328 | sig += g.table.sym(typ).name |
| 1329 | if j < method.params.len - 2 { |
| 1330 | sig += ', ' |
| 1331 | } |
| 1332 | } |
| 1333 | sig += ')' |
| 1334 | ret_type := g.table.sym(method.return_type).name |
| 1335 | if ret_type != 'void' { |
| 1336 | sig += ' ${ret_type}' |
| 1337 | } |
| 1338 | typ := g.table.find_type(sig) |
| 1339 | |
| 1340 | // TODO: type aliases |
| 1341 | ret_typ := method.return_type |
| 1342 | g.writeln('\t${node.val_var}.typ = ${int(typ)};') |
| 1343 | g.writeln('\t${node.val_var}.return_type = ${int(ret_typ.idx())};') |
| 1344 | |
| 1345 | g.type_resolver.update_ct_type('${node.val_var}.return_type', ret_typ) |
| 1346 | g.type_resolver.update_ct_type('${node.val_var}.typ', typ) |
| 1347 | g.stmts(node.stmts) |
| 1348 | g.write_defer_stmts(node.scope, false, node.pos) |
| 1349 | i++ |
| 1350 | g.writeln('}') |
| 1351 | g.pop_comptime_info() |
| 1352 | } |
| 1353 | } else if node.kind == .fields { |
| 1354 | if sym.kind in [.struct, .interface] { |
| 1355 | fields := match sym.info { |
| 1356 | ast.Struct { |
| 1357 | sym.info.fields |
| 1358 | } |
| 1359 | ast.Interface { |
| 1360 | sym.info.fields |
| 1361 | } |
| 1362 | else { |
| 1363 | g.error('comptime field lookup is supported only for structs and interfaces, and ${sym.name} is neither', |
| 1364 | node.pos) |
| 1365 | []ast.StructField{} |
| 1366 | } |
| 1367 | } |
| 1368 | |
| 1369 | if fields.len > 0 { |
| 1370 | g.writeln('\tFieldData ${node.val_var} = {0};') |
| 1371 | } |
| 1372 | for field in fields { |
| 1373 | g.defer_stmts = old_defer_stmts |
| 1374 | g.push_new_comptime_info() |
| 1375 | g.comptime.inside_comptime_for = true |
| 1376 | g.comptime.comptime_for_field_var = node.val_var |
| 1377 | g.comptime.comptime_for_field_value = field |
| 1378 | resolved_field_typ := g.unwrap_generic(field.typ) |
| 1379 | g.comptime.comptime_for_field_type = resolved_field_typ |
| 1380 | g.writeln('/* field ${i} : ${field.name} */ {') |
| 1381 | g.writeln('\t${node.val_var}.name = _S("${field.name}");') |
| 1382 | if field.attrs.len == 0 { |
| 1383 | g.writeln('\t${node.val_var}.attrs = builtin____new_array_with_default(0, 0, sizeof(string), 0);') |
| 1384 | } else { |
| 1385 | attrs := cgen_attrs(field.attrs) |
| 1386 | g.writeln( |
| 1387 | '\t${node.val_var}.attrs = builtin__new_array_from_c_array(${attrs.len}, ${attrs.len}, sizeof(string), _MOV((string[${attrs.len}]){' + |
| 1388 | attrs.join(', ') + '}));\n') |
| 1389 | } |
| 1390 | field_sym := g.table.sym(resolved_field_typ) |
| 1391 | styp := resolved_field_typ |
| 1392 | unaliased_styp := g.table.unaliased_type(styp) |
| 1393 | |
| 1394 | g.writeln('\t${node.val_var}.typ = ${int(styp.idx())};\t// ${g.table.type_to_str(styp)}') |
| 1395 | g.writeln('\t${node.val_var}.unaliased_typ = ${int(unaliased_styp.idx())};\t// ${g.table.type_to_str(unaliased_styp)}') |
| 1396 | g.writeln('\t${node.val_var}.is_pub = ${field.is_pub};') |
| 1397 | g.writeln('\t${node.val_var}.is_mut = ${field.is_mut};') |
| 1398 | g.writeln('\t${node.val_var}.is_embed = ${field.is_embed};') |
| 1399 | |
| 1400 | g.writeln('\t${node.val_var}.is_shared = ${resolved_field_typ.has_flag(.shared_f)};') |
| 1401 | g.writeln('\t${node.val_var}.is_atomic = ${resolved_field_typ.has_flag(.atomic_f)};') |
| 1402 | g.writeln('\t${node.val_var}.is_option = ${resolved_field_typ.has_flag(.option)};') |
| 1403 | |
| 1404 | g.writeln('\t${node.val_var}.is_array = ${field_sym.kind in [.array, .array_fixed]};') |
| 1405 | g.writeln('\t${node.val_var}.is_map = ${field_sym.kind == .map};') |
| 1406 | g.writeln('\t${node.val_var}.is_chan = ${field_sym.kind == .chan};') |
| 1407 | g.writeln('\t${node.val_var}.is_struct = ${field_sym.kind == .struct};') |
| 1408 | g.writeln('\t${node.val_var}.is_alias = ${field_sym.kind == .alias};') |
| 1409 | g.writeln('\t${node.val_var}.is_enum = ${field_sym.kind == .enum};') |
| 1410 | |
| 1411 | g.writeln('\t${node.val_var}.indirections = ${resolved_field_typ.nr_muls()};') |
| 1412 | |
| 1413 | g.type_resolver.update_ct_type('${node.val_var}.typ', resolved_field_typ) |
| 1414 | g.type_resolver.update_ct_type('${node.val_var}.unaliased_typ', unaliased_styp) |
| 1415 | g.stmts(node.stmts) |
| 1416 | g.write_defer_stmts(node.scope, false, node.pos) |
| 1417 | i++ |
| 1418 | g.writeln('}') |
| 1419 | g.pop_comptime_info() |
| 1420 | } |
| 1421 | } |
| 1422 | } else if node.kind == .values { |
| 1423 | if sym.kind == .enum { |
| 1424 | if sym.info is ast.Enum { |
| 1425 | if sym.info.vals.len > 0 { |
| 1426 | g.writeln('\tEnumData ${node.val_var} = {0};') |
| 1427 | } |
| 1428 | for val in sym.info.vals { |
| 1429 | g.defer_stmts = old_defer_stmts |
| 1430 | g.push_new_comptime_info() |
| 1431 | g.comptime.inside_comptime_for = true |
| 1432 | g.comptime.comptime_for_enum_var = node.val_var |
| 1433 | g.type_resolver.update_ct_type('${node.val_var}.typ', node.typ) |
| 1434 | |
| 1435 | g.writeln('/* enum vals ${i} */ {') |
| 1436 | g.writeln('\t${node.val_var}.name = _S("${val}");') |
| 1437 | g.write('\t${node.val_var}.value = ') |
| 1438 | if g.pref.translated && node.typ.is_number() { |
| 1439 | g.writeln('_const_main__${val};') |
| 1440 | } else { |
| 1441 | node_sym := g.table.sym(g.unwrap_generic(node.typ)) |
| 1442 | if node_sym.info is ast.Alias { |
| 1443 | g.writeln('${g.styp(node_sym.info.parent_type)}__${val};') |
| 1444 | } else { |
| 1445 | g.writeln('${g.styp(node.typ)}__${val};') |
| 1446 | } |
| 1447 | } |
| 1448 | enum_attrs := sym.info.attrs[val] |
| 1449 | if enum_attrs.len == 0 { |
| 1450 | g.writeln('\t${node.val_var}.attrs = builtin____new_array_with_default(0, 0, sizeof(string), 0);') |
| 1451 | } else { |
| 1452 | attrs := cgen_attrs(enum_attrs) |
| 1453 | g.writeln( |
| 1454 | '\t${node.val_var}.attrs = builtin__new_array_from_c_array(${attrs.len}, ${attrs.len}, sizeof(string), _MOV((string[${attrs.len}]){' + |
| 1455 | attrs.join(', ') + '}));\n') |
| 1456 | } |
| 1457 | g.stmts(node.stmts) |
| 1458 | g.write_defer_stmts(node.scope, false, node.pos) |
| 1459 | g.writeln('}') |
| 1460 | i++ |
| 1461 | g.pop_comptime_info() |
| 1462 | } |
| 1463 | } |
| 1464 | } |
| 1465 | } else if node.kind == .attributes { |
| 1466 | attrs := g.table.get_attrs(sym) |
| 1467 | if attrs.len > 0 { |
| 1468 | g.writeln('\tVAttribute ${node.val_var} = {0};') |
| 1469 | |
| 1470 | for attr in attrs { |
| 1471 | g.defer_stmts = old_defer_stmts |
| 1472 | g.push_new_comptime_info() |
| 1473 | g.comptime.inside_comptime_for = true |
| 1474 | g.comptime.comptime_for_attr_var = node.val_var |
| 1475 | g.comptime.comptime_for_attr_value = attr |
| 1476 | g.writeln('/* attribute ${i} : ${attr.name} */ {') |
| 1477 | g.writeln('\t${node.val_var}.name = _S("${attr.name}");') |
| 1478 | g.writeln('\t${node.val_var}.has_arg = ${attr.has_arg};') |
| 1479 | g.writeln('\t${node.val_var}.arg = _S("${util.smart_quote(attr.arg, false)}");') |
| 1480 | g.writeln('\t${node.val_var}.kind = AttributeKind__${attr.kind};') |
| 1481 | g.stmts(node.stmts) |
| 1482 | g.write_defer_stmts(node.scope, false, node.pos) |
| 1483 | g.writeln('}') |
| 1484 | i++ |
| 1485 | g.pop_comptime_info() |
| 1486 | } |
| 1487 | } |
| 1488 | } else if node.kind == .variants { |
| 1489 | if sym.info is ast.SumType { |
| 1490 | if sym.info.variants.len > 0 { |
| 1491 | g.writeln('\tVariantData ${node.val_var} = {0};') |
| 1492 | } |
| 1493 | g.comptime.inside_comptime_for = true |
| 1494 | for variant in sym.info.variants { |
| 1495 | g.defer_stmts = old_defer_stmts |
| 1496 | g.push_new_comptime_info() |
| 1497 | g.comptime.inside_comptime_for = true |
| 1498 | g.comptime.comptime_for_variant_var = node.val_var |
| 1499 | g.type_resolver.update_ct_type('${node.val_var}.typ', variant) |
| 1500 | |
| 1501 | g.writeln('/* variant ${i} : ${g.table.type_to_str(variant)} */ {') |
| 1502 | g.writeln('\t${node.val_var}.typ = ${int(variant)};\t// ') |
| 1503 | g.stmts(node.stmts) |
| 1504 | g.write_defer_stmts(node.scope, false, node.pos) |
| 1505 | g.writeln('}') |
| 1506 | i++ |
| 1507 | g.pop_comptime_info() |
| 1508 | } |
| 1509 | } |
| 1510 | } else if node.kind == .params { |
| 1511 | func := if sym.info is ast.FnType { &sym.info.func } else { g.comptime.comptime_for_method } |
| 1512 | if func.params.len > 0 { |
| 1513 | g.writeln('\tFunctionParam ${node.val_var} = {0};') |
| 1514 | } |
| 1515 | params := if func.is_method { func.params[1..] } else { func.params } |
| 1516 | for param in params { |
| 1517 | g.defer_stmts = old_defer_stmts |
| 1518 | g.push_new_comptime_info() |
| 1519 | g.comptime.inside_comptime_for = true |
| 1520 | g.comptime.comptime_for_method_param_var = node.val_var |
| 1521 | g.type_resolver.update_ct_type('${node.val_var}.typ', param.typ) |
| 1522 | |
| 1523 | g.writeln('/* method param ${i} : ${param.name} */ {') |
| 1524 | g.writeln('\t${node.val_var}.typ = ${int(param.typ)};\t// ${g.table.type_to_str(param.typ)}') |
| 1525 | g.writeln('\t${node.val_var}.name = _S("${param.name}");') |
| 1526 | g.stmts(node.stmts) |
| 1527 | g.write_defer_stmts(node.scope, false, node.pos) |
| 1528 | g.writeln('}') |
| 1529 | i++ |
| 1530 | g.pop_comptime_info() |
| 1531 | } |
| 1532 | } |
| 1533 | g.defer_stmts = old_defer_stmts |
| 1534 | g.indent-- |
| 1535 | g.writeln('}// \$for') |
| 1536 | } |
| 1537 | |
| 1538 | // comptime_selector_type computes the selector type from an comptime var |
| 1539 | fn (mut g Gen) comptime_selector_type(node ast.SelectorExpr) ast.Type { |
| 1540 | if !(node.expr is ast.Ident && node.expr.ct_expr) { |
| 1541 | return node.expr_type |
| 1542 | } |
| 1543 | prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once |
| 1544 | g.prevent_sum_type_unwrapping_once = false |
| 1545 | |
| 1546 | mut typ := g.type_resolver.get_type(node.expr) |
| 1547 | if node.expr is ast.Ident && node.expr.obj is ast.Var { |
| 1548 | ct_var := node.expr.obj.ct_type_var |
| 1549 | if ct_var == .generic_param || ct_var == .generic_var { |
| 1550 | if scope_var := node.expr.scope.find_var(node.expr.name) { |
| 1551 | if scope_var.ct_type_var == ct_var && g.cur_fn != unsafe { nil } |
| 1552 | && g.cur_fn.generic_names.len > 0 { |
| 1553 | // For generic_param/generic_var, node.obj.typ is a stale |
| 1554 | // copy from checker time. Use the refreshed scope var. |
| 1555 | typ = scope_var.typ |
| 1556 | } |
| 1557 | } |
| 1558 | } |
| 1559 | } |
| 1560 | if node.expr.is_auto_deref_var() { |
| 1561 | if node.expr is ast.Ident { |
| 1562 | if node.expr.obj is ast.Var { |
| 1563 | typ = node.expr.obj.typ |
| 1564 | } |
| 1565 | } |
| 1566 | } |
| 1567 | if g.comptime.inside_comptime_for && typ == g.enum_data_type && node.field_name == 'value' { |
| 1568 | // for comp-time enum.values |
| 1569 | return g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_enum_var}.typ', |
| 1570 | ast.void_type) |
| 1571 | } |
| 1572 | field_name := node.field_name |
| 1573 | sym := g.table.sym(typ) |
| 1574 | final_sym := g.table.final_sym(typ) |
| 1575 | if (typ.has_flag(.variadic) || final_sym.kind == .array_fixed) && field_name == 'len' { |
| 1576 | return ast.int_type |
| 1577 | } |
| 1578 | if sym.kind == .chan { |
| 1579 | if field_name == 'closed' { |
| 1580 | return ast.bool_type |
| 1581 | } else if field_name in ['len', 'cap'] { |
| 1582 | return ast.u32_type |
| 1583 | } |
| 1584 | } |
| 1585 | mut has_field := false |
| 1586 | mut field := ast.StructField{} |
| 1587 | if field_name.len > 0 && field_name[0].is_capital() && sym.language == .v { |
| 1588 | if sym.info is ast.Struct { |
| 1589 | // x.Foo.y => access the embedded struct |
| 1590 | for embed in sym.info.embeds { |
| 1591 | embed_sym := g.table.sym(embed) |
| 1592 | if embed_sym.embed_name() == field_name { |
| 1593 | return embed |
| 1594 | } |
| 1595 | } |
| 1596 | } |
| 1597 | } else { |
| 1598 | if f := g.table.find_field(sym, field_name) { |
| 1599 | has_field = true |
| 1600 | field = f |
| 1601 | } else { |
| 1602 | // look for embedded field |
| 1603 | has_field = true |
| 1604 | g.table.find_field_from_embeds(sym, field_name) or { has_field = false } |
| 1605 | } |
| 1606 | if typ.has_flag(.generic) && !has_field { |
| 1607 | gs := g.table.sym(g.unwrap_generic(typ)) |
| 1608 | if f := g.table.find_field(gs, field_name) { |
| 1609 | has_field = true |
| 1610 | field = f |
| 1611 | } else { |
| 1612 | // look for embedded field |
| 1613 | has_field = true |
| 1614 | g.table.find_field_from_embeds(gs, field_name) or { has_field = false } |
| 1615 | } |
| 1616 | } |
| 1617 | } |
| 1618 | |
| 1619 | if has_field { |
| 1620 | field_sym := g.table.sym(field.typ) |
| 1621 | if field_sym.kind in [.sum_type, .interface] { |
| 1622 | if !prevent_sum_type_unwrapping_once { |
| 1623 | scope_field := node.scope.find_struct_field(node.expr.str(), typ, field_name) |
| 1624 | if scope_field != unsafe { nil } { |
| 1625 | return scope_field.smartcasts.last() |
| 1626 | } |
| 1627 | } |
| 1628 | } |
| 1629 | return field.typ |
| 1630 | } |
| 1631 | if mut method := g.table.sym(g.unwrap_generic(typ)).find_method_with_generic_parent(field_name) { |
| 1632 | method.params = method.params[1..] |
| 1633 | method.name = '' |
| 1634 | fn_type := ast.new_type(g.table.find_or_register_fn_type(method, false, true)) |
| 1635 | return fn_type |
| 1636 | } |
| 1637 | if sym.kind !in [.struct, .aggregate, .interface, .sum_type] { |
| 1638 | if sym.kind != .placeholder { |
| 1639 | unwrapped_sym := g.table.sym(g.unwrap_generic(typ)) |
| 1640 | if unwrapped_sym.kind == .array_fixed && node.field_name == 'len' { |
| 1641 | return ast.int_type |
| 1642 | } |
| 1643 | } |
| 1644 | } |
| 1645 | return node.expr_type |
| 1646 | } |
| 1647 | |
| 1648 | fn (mut g Gen) comptime_match(node ast.MatchExpr) { |
| 1649 | tmp_var := g.new_tmp_var() |
| 1650 | mut inferred_typ := node.return_type |
| 1651 | if node.is_expr && (node.return_type == ast.void_type || node.return_type.idx() == 0) |
| 1652 | && node.branches.len > 0 { |
| 1653 | for branch in node.branches { |
| 1654 | if branch.stmts.len > 0 { |
| 1655 | last_stmt := branch.stmts.last() |
| 1656 | if last_stmt is ast.ExprStmt { |
| 1657 | expr_typ := g.type_resolver.get_type_or_default(last_stmt.expr, last_stmt.typ) |
| 1658 | if expr_typ != ast.void_type && expr_typ.idx() != 0 |
| 1659 | && !expr_typ.has_flag(.generic) { |
| 1660 | inferred_typ = expr_typ |
| 1661 | break |
| 1662 | } |
| 1663 | } |
| 1664 | } |
| 1665 | } |
| 1666 | } |
| 1667 | is_opt_or_result := inferred_typ.has_option_or_result() |
| 1668 | line := if node.is_expr && inferred_typ != ast.void_type && inferred_typ.idx() != 0 { |
| 1669 | stmt_str := g.go_before_last_stmt() |
| 1670 | g.write(util.tabs(g.indent)) |
| 1671 | styp := g.styp(inferred_typ) |
| 1672 | g.writeln('${styp} ${tmp_var};') |
| 1673 | stmt_str |
| 1674 | } else { |
| 1675 | '' |
| 1676 | } |
| 1677 | |
| 1678 | mut comptime_branch_context_str := g.gen_branch_context_string() |
| 1679 | mut is_true := ast.ComptTimeCondResult{} |
| 1680 | for i, branch in node.branches { |
| 1681 | // `idx_str` is composed of two parts: |
| 1682 | // The first part represents the current context of the branch statement, `comptime_branch_context_str`, formatted like `T=int,X=string,method.name=json` |
| 1683 | // The second part is the branch's id. |
| 1684 | // This format must match what is in `checker`. |
| 1685 | mut idx_str := comptime_branch_context_str + '|id=${branch.id}|' |
| 1686 | if g.comptime.inside_comptime_for && g.comptime.comptime_for_field_var != '' { |
| 1687 | idx_str += '|field_type=${g.comptime.comptime_for_field_type}|' |
| 1688 | } |
| 1689 | if comptime_is_true := g.table.comptime_is_true[idx_str] { |
| 1690 | // `g.table.comptime_is_true` are the branch condition results set by `checker` |
| 1691 | is_true = comptime_is_true |
| 1692 | } else { |
| 1693 | // No checker data - spurious instantiation or alias mismatch. |
| 1694 | // Default all branches to false (#if 0). |
| 1695 | is_true = ast.ComptTimeCondResult{} |
| 1696 | } |
| 1697 | if !branch.is_else { |
| 1698 | if i == 0 { |
| 1699 | g.write('#if ') |
| 1700 | } else { |
| 1701 | g.write('#elif ') |
| 1702 | } |
| 1703 | // directly use `checker` evaluate results |
| 1704 | g.writeln('${is_true.val}') |
| 1705 | $if debug_comptime_branch_context ? { |
| 1706 | g.writeln('/* | generic=[${comptime_branch_context_str}] */') |
| 1707 | } |
| 1708 | } else { |
| 1709 | g.writeln('#else') |
| 1710 | } |
| 1711 | if node.is_expr && !branch.is_comptime_err { |
| 1712 | len := branch.stmts.len |
| 1713 | if len > 0 { |
| 1714 | last := branch.stmts.last() |
| 1715 | if last is ast.ExprStmt { |
| 1716 | if len > 1 { |
| 1717 | g.indent++ |
| 1718 | g.writeln('{') |
| 1719 | g.stmts(branch.stmts[..len - 1]) |
| 1720 | g.set_current_pos_as_last_stmt_pos() |
| 1721 | prev_skip_stmt_pos := g.skip_stmt_pos |
| 1722 | g.skip_stmt_pos = true |
| 1723 | if is_opt_or_result { |
| 1724 | tmp_var2 := g.new_tmp_var() |
| 1725 | g.write('{ ${g.base_type(inferred_typ)} ${tmp_var2} = ') |
| 1726 | g.stmt(last) |
| 1727 | g.writeln('builtin___result_ok(&(${g.base_type(inferred_typ)}[]) { ${tmp_var2} }, (_result*)(&${tmp_var}), sizeof(${g.base_type(inferred_typ)}));') |
| 1728 | g.writeln('}') |
| 1729 | } else { |
| 1730 | g.write('\t${tmp_var} = ') |
| 1731 | g.stmt(last) |
| 1732 | } |
| 1733 | g.skip_stmt_pos = prev_skip_stmt_pos |
| 1734 | g.writeln(';') |
| 1735 | g.write_defer_stmts(branch.scope, false, branch.pos) |
| 1736 | g.writeln('}') |
| 1737 | g.indent-- |
| 1738 | } else { |
| 1739 | g.indent++ |
| 1740 | g.set_current_pos_as_last_stmt_pos() |
| 1741 | prev_skip_stmt_pos := g.skip_stmt_pos |
| 1742 | g.skip_stmt_pos = true |
| 1743 | if is_opt_or_result { |
| 1744 | tmp_var2 := g.new_tmp_var() |
| 1745 | g.write('{ ${g.base_type(inferred_typ)} ${tmp_var2} = ') |
| 1746 | g.stmt(last) |
| 1747 | g.writeln('builtin___result_ok(&(${g.base_type(inferred_typ)}[]) { ${tmp_var2} }, (_result*)(&${tmp_var}), sizeof(${g.base_type(inferred_typ)}));') |
| 1748 | g.writeln('}') |
| 1749 | } else { |
| 1750 | g.write('${tmp_var} = ') |
| 1751 | g.stmt(last) |
| 1752 | } |
| 1753 | g.skip_stmt_pos = prev_skip_stmt_pos |
| 1754 | g.writeln(';') |
| 1755 | g.write_defer_stmts(branch.scope, false, branch.pos) |
| 1756 | g.indent-- |
| 1757 | } |
| 1758 | } else if last is ast.Return { |
| 1759 | if last.exprs.len > 0 { |
| 1760 | g.write('${tmp_var} = ') |
| 1761 | g.expr(last.exprs[0]) |
| 1762 | g.writeln(';') |
| 1763 | g.write_defer_stmts(branch.scope, false, branch.pos) |
| 1764 | } |
| 1765 | } |
| 1766 | } |
| 1767 | } else if is_true.val || g.pref.output_cross_c { |
| 1768 | g.stmts(branch.stmts) |
| 1769 | g.write_defer_stmts(branch.scope, false, branch.pos) |
| 1770 | } |
| 1771 | } |
| 1772 | g.writeln('#endif') |
| 1773 | if node.is_expr { |
| 1774 | g.write('${line}${tmp_var}') |
| 1775 | } |
| 1776 | } |
| 1777 | |