| 1 | // Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license that can be found in the LICENSE file. |
| 3 | module c |
| 4 | |
| 5 | import v.ast |
| 6 | import v.util |
| 7 | |
| 8 | fn (mut g Gen) string_literal(node ast.StringLiteral) { |
| 9 | escaped_val := cescape_nonascii(util.smart_quote(node.val, node.is_raw)) |
| 10 | if node.language == .c { |
| 11 | g.write(cescaped_string_literal(escaped_val)) |
| 12 | } else { |
| 13 | g.write('_S(${cescaped_string_literal(escaped_val)})') |
| 14 | } |
| 15 | } |
| 16 | |
| 17 | // optimize string interpolation in string builders: |
| 18 | // `sb.writeln('a=${a}')` => |
| 19 | // `sb.writeln('a='); sb.writeln(a.str())` |
| 20 | fn (mut g Gen) string_inter_literal_sb_optimized(call_expr ast.CallExpr) { |
| 21 | node := call_expr.args[0].expr as ast.StringInterLiteral |
| 22 | g.writeln('// sb inter opt') |
| 23 | is_nl := call_expr.name == 'writeln' |
| 24 | for i, val in node.vals { |
| 25 | escaped_val := cescape_nonascii(util.smart_quote(val, false)) |
| 26 | g.write('strings__Builder_write_string(&') |
| 27 | g.expr(call_expr.left) |
| 28 | g.write2(', _S("', escaped_val) |
| 29 | g.writeln('"));') |
| 30 | if i >= node.exprs.len { |
| 31 | break |
| 32 | } |
| 33 | if is_nl && i == node.exprs.len - 1 { |
| 34 | g.write('strings__Builder_writeln(&') |
| 35 | } else { |
| 36 | g.write('strings__Builder_write_string(&') |
| 37 | } |
| 38 | g.expr(call_expr.left) |
| 39 | g.write(', ') |
| 40 | typ := node.expr_types[i] |
| 41 | g.write2(g.styp(typ), '_str(') |
| 42 | sym := g.table.sym(typ) |
| 43 | if sym.kind != .function { |
| 44 | g.expr(node.exprs[i]) |
| 45 | } |
| 46 | g.writeln('));') |
| 47 | } |
| 48 | g.writeln('') |
| 49 | return |
| 50 | } |
| 51 | |
| 52 | fn (g &Gen) option_mut_param_surface_type(expr ast.Expr) ast.Type { |
| 53 | ident := match expr { |
| 54 | ast.Ident { expr } |
| 55 | else { return 0 } |
| 56 | } |
| 57 | |
| 58 | mut typ := ast.Type(0) |
| 59 | if ident.obj is ast.Var { |
| 60 | typ = g.option_mut_param_surface_type_from_var(ident.name, ident.obj) |
| 61 | } |
| 62 | if scope_var := ident.scope.find_var(ident.name) { |
| 63 | scope_typ := g.option_mut_param_surface_type_from_var(ident.name, scope_var) |
| 64 | if scope_typ != 0 { |
| 65 | typ = scope_typ |
| 66 | } |
| 67 | } |
| 68 | if typ == 0 && ident.obj is ast.Var { |
| 69 | if ident.obj.is_arg && ident.obj.orig_type.has_flag(.option) { |
| 70 | typ = ident.obj.orig_type |
| 71 | } |
| 72 | } |
| 73 | if typ == 0 || !typ.has_flag(.option) { |
| 74 | return 0 |
| 75 | } |
| 76 | return typ |
| 77 | } |
| 78 | |
| 79 | fn (g &Gen) option_mut_param_surface_type_from_var(name string, var ast.Var) ast.Type { |
| 80 | if !var.is_arg || var.is_unwrapped || !var.typ.has_flag(.option_mut_param_t) { |
| 81 | return 0 |
| 82 | } |
| 83 | mut typ := var.typ.clear_flag(.option_mut_param_t) |
| 84 | if g.mut_option_param_assigned_directly(name) { |
| 85 | inner := typ.clear_option_and_result() |
| 86 | if inner.is_ptr() { |
| 87 | typ = inner.deref().set_flag(.option) |
| 88 | } |
| 89 | } |
| 90 | return typ |
| 91 | } |
| 92 | |
| 93 | fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) { |
| 94 | old_inside_opt_or_res := g.inside_opt_or_res |
| 95 | g.inside_opt_or_res = true |
| 96 | g.expected_fixed_arr = true |
| 97 | defer { |
| 98 | g.inside_opt_or_res = old_inside_opt_or_res |
| 99 | g.expected_fixed_arr = false |
| 100 | } |
| 101 | mut expr_type := etype |
| 102 | if expr is ast.Ident && g.resolved_ident_is_by_value_auto_deref_capture(expr) { |
| 103 | resolved_scope_type := g.resolved_scope_var_type(expr) |
| 104 | if resolved_scope_type != 0 { |
| 105 | expr_type = resolved_scope_type |
| 106 | } |
| 107 | } |
| 108 | is_shared := expr_type.has_flag(.shared_f) |
| 109 | mut typ := expr_type |
| 110 | if is_shared { |
| 111 | typ = typ.clear_flag(.shared_f).set_nr_muls(0) |
| 112 | } |
| 113 | if expr is ast.Ident && g.cur_fn != unsafe { nil } |
| 114 | && g.mut_option_param_assigned_directly(expr.name) { |
| 115 | for param in g.cur_fn.params { |
| 116 | if param.name == expr.name && param.typ.has_flag(.option_mut_param_t) { |
| 117 | mut opt_typ := param.typ.clear_flag(.option_mut_param_t) |
| 118 | if opt_typ.is_ptr() { |
| 119 | opt_typ = opt_typ.deref() |
| 120 | } |
| 121 | g.write('${g.get_str_fn(opt_typ)}(*') |
| 122 | g.expr(expr) |
| 123 | g.write(')') |
| 124 | return |
| 125 | } |
| 126 | } |
| 127 | } |
| 128 | mut_arg_option_type := g.option_mut_param_surface_type(expr) |
| 129 | if mut_arg_option_type != 0 { |
| 130 | typ = mut_arg_option_type |
| 131 | } |
| 132 | if mut_arg_option_type != 0 && expr is ast.Ident |
| 133 | && g.mut_option_param_assigned_directly(expr.name) { |
| 134 | g.write('${g.get_str_fn(mut_arg_option_type)}(*') |
| 135 | g.expr(expr) |
| 136 | g.write(')') |
| 137 | return |
| 138 | } |
| 139 | if mut_arg_option_type == 0 && expr is ast.Ident && g.expr_is_auto_deref_var(expr) |
| 140 | && typ.has_flag(.option) { |
| 141 | g.write('${g.get_str_fn(typ)}(*') |
| 142 | g.expr(expr) |
| 143 | g.write(')') |
| 144 | return |
| 145 | } |
| 146 | if expr is ast.Ident && expr.obj is ast.Var && expr.obj.is_inherited { |
| 147 | inherited_typ := g.resolved_scope_var_type(expr) |
| 148 | if inherited_typ != 0 { |
| 149 | typ = inherited_typ |
| 150 | } |
| 151 | } |
| 152 | // `mut ?T` params are passed by pointer in C, but should still stringify as |
| 153 | // option values rather than as raw `&...` pointers. |
| 154 | is_ptr := typ.is_ptr() || (typ.has_flag(.option_mut_param_t) && !typ.has_flag(.option)) |
| 155 | mut sym := g.table.sym(typ) |
| 156 | // when type is non-option alias and doesn't has `str()`, print the aliased value |
| 157 | if mut sym.info is ast.Alias && !sym.has_method('str') && !expr_type.has_flag(.option) { |
| 158 | parent_sym := g.table.sym(sym.info.parent_type) |
| 159 | if parent_sym.has_method('str') { |
| 160 | typ = sym.info.parent_type |
| 161 | sym = unsafe { parent_sym } |
| 162 | } |
| 163 | } |
| 164 | sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() |
| 165 | // When interface smartcast expr produces a pointer in C but type was already dereffed, |
| 166 | // we need to dereference the generated expression. |
| 167 | is_interface_smartcast_to_nonptr := !is_ptr && expr is ast.Ident && expr.obj is ast.Var |
| 168 | && (expr.obj as ast.Var).smartcasts.len > 0 |
| 169 | && (expr.obj as ast.Var).smartcasts.last().is_ptr() |
| 170 | && g.table.final_sym(g.unwrap_generic((expr.obj as ast.Var).orig_type)).kind == .interface |
| 171 | && g.table.final_sym(g.unwrap_generic((expr.obj as ast.Var).smartcasts.last())).kind != .interface |
| 172 | use_raw_interface_smartcast_expr := is_ptr && expr is ast.Ident && expr.obj is ast.Var |
| 173 | && (expr.obj as ast.Var).smartcasts.len > 0 |
| 174 | && (expr.obj as ast.Var).smartcasts.last().is_ptr() |
| 175 | && (g.table.final_sym(g.unwrap_generic((expr.obj as ast.Var).typ)).kind == .interface |
| 176 | || ((expr.obj as ast.Var).orig_type != 0 |
| 177 | && g.table.final_sym(g.unwrap_generic((expr.obj as ast.Var).orig_type)).kind == .interface)) |
| 178 | if typ.has_flag(.variadic) { |
| 179 | str_fn_name := g.get_str_fn(typ) |
| 180 | g.write('${str_fn_name}(') |
| 181 | g.expr(expr) |
| 182 | g.write(')') |
| 183 | } else if typ == ast.string_type { |
| 184 | if expr_type.is_ptr() { |
| 185 | g.write('*') |
| 186 | } |
| 187 | g.expr(expr) |
| 188 | } else if typ == ast.bool_type { |
| 189 | g.write('(') |
| 190 | g.expr(expr) |
| 191 | g.write(' ? _S("true") : _S("false"))') |
| 192 | } else if sym.kind == .none || typ == ast.void_type.set_flag(.option) { |
| 193 | if expr is ast.CallExpr { |
| 194 | stmt_str := g.go_before_last_stmt() |
| 195 | g.expr(expr) |
| 196 | g.writeln(';') |
| 197 | g.write(stmt_str) |
| 198 | } |
| 199 | g.write('_S("<none>")') |
| 200 | } else if sym.kind == .enum { |
| 201 | if expr !is ast.EnumVal || sym.has_method('str') { |
| 202 | str_fn_name := g.get_str_fn(typ) |
| 203 | g.write('${str_fn_name}(') |
| 204 | if typ.nr_muls() > 0 { |
| 205 | g.write('*'.repeat(typ.nr_muls())) |
| 206 | } |
| 207 | if expr is ast.EnumVal { |
| 208 | g.write2(sym.cname, '__') |
| 209 | } |
| 210 | g.enum_expr(expr) |
| 211 | g.write(')') |
| 212 | } else { |
| 213 | g.write('_S("') |
| 214 | g.enum_expr(expr) |
| 215 | g.write('")') |
| 216 | } |
| 217 | } else if sym_has_str_method |
| 218 | || sym.kind in [.array, .array_fixed, .map, .struct, .multi_return, .sum_type, .interface] { |
| 219 | unwrap_opt_or_res := match expr { |
| 220 | ast.CallExpr, ast.ComptimeCall, ast.ComptimeSelector, ast.InfixExpr, ast.PrefixExpr, |
| 221 | ast.SelectorExpr { |
| 222 | expr.or_block.kind != .absent |
| 223 | } |
| 224 | ast.Ident, ast.IndexExpr { |
| 225 | expr.or_expr.kind != .absent |
| 226 | } |
| 227 | else { |
| 228 | false |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | exp_typ := if unwrap_opt_or_res { typ.clear_option_and_result() } else { typ } |
| 233 | if unwrap_opt_or_res { |
| 234 | typ = exp_typ |
| 235 | } |
| 236 | is_dump_expr := expr is ast.DumpExpr |
| 237 | is_var_mut := g.expr_is_auto_deref_var(expr) && !typ.has_flag(.option) |
| 238 | str_fn_name := if mut_arg_option_type != 0 { |
| 239 | g.get_str_fn(mut_arg_option_type) |
| 240 | } else { |
| 241 | g.get_str_fn(exp_typ) |
| 242 | } |
| 243 | temp_var_needed := expr is ast.CallExpr |
| 244 | && (expr.return_type.is_ptr() || g.table.sym(expr.return_type).is_c_struct()) |
| 245 | mut tmp_var := '' |
| 246 | if temp_var_needed { |
| 247 | tmp_var = g.new_tmp_var() |
| 248 | ret_typ := g.styp(exp_typ) |
| 249 | line := g.go_before_last_stmt().trim_space() |
| 250 | g.empty_line = true |
| 251 | g.write('${ret_typ} ${tmp_var} = ') |
| 252 | g.expr(expr) |
| 253 | g.writeln(';') |
| 254 | g.write(line) |
| 255 | } |
| 256 | if is_ptr && !is_var_mut { |
| 257 | ref_str := '&'.repeat(typ.nr_muls()) |
| 258 | g.write('builtin__str_intp(1, _MOV((StrIntpData[]){{_S("${ref_str}"), ${si_s_code}, {.d_s = builtin__isnil(') |
| 259 | if typ.has_flag(.option) || mut_arg_option_type != 0 { |
| 260 | if mut_arg_option_type != 0 { |
| 261 | if temp_var_needed { |
| 262 | g.write(tmp_var) |
| 263 | } else { |
| 264 | g.expr(expr) |
| 265 | } |
| 266 | g.write(') ? _S("nil") : ') |
| 267 | } else { |
| 268 | g.write('*(${g.base_type(exp_typ)}*)&') |
| 269 | if temp_var_needed { |
| 270 | g.write(tmp_var) |
| 271 | } else { |
| 272 | g.expr(expr) |
| 273 | } |
| 274 | g.write('.data) ? _S("Option(&nil)") : ') |
| 275 | } |
| 276 | } else { |
| 277 | inside_interface_deref_old := g.inside_interface_deref |
| 278 | g.inside_interface_deref = false |
| 279 | defer(fn) { |
| 280 | g.inside_interface_deref = inside_interface_deref_old |
| 281 | } |
| 282 | if temp_var_needed { |
| 283 | g.write(tmp_var) |
| 284 | } else if use_raw_interface_smartcast_expr { |
| 285 | old_inside_selector_lhs := g.inside_selector_lhs |
| 286 | g.inside_selector_lhs = true |
| 287 | g.expr(expr) |
| 288 | g.inside_selector_lhs = old_inside_selector_lhs |
| 289 | } else { |
| 290 | g.expr(expr) |
| 291 | } |
| 292 | g.write(') ? _S("nil") : ') |
| 293 | } |
| 294 | } |
| 295 | g.write2(str_fn_name, '(') |
| 296 | if str_method_expects_ptr && !is_ptr { |
| 297 | if is_dump_expr || (g.pref.ccompiler_type != .tinyc && expr is ast.CallExpr) { |
| 298 | g.write('ADDR(${g.styp(typ)}, ') |
| 299 | defer(fn) { |
| 300 | g.write(')') |
| 301 | } |
| 302 | } else { |
| 303 | g.write('&') |
| 304 | } |
| 305 | } else if mut_arg_option_type != 0 { |
| 306 | g.write('*') |
| 307 | } else if is_ptr && typ.has_flag(.option) { |
| 308 | if typ.has_flag(.option_mut_param_t) { |
| 309 | g.write('*') |
| 310 | } else { |
| 311 | g.write('*(${g.styp(typ)}*)&') |
| 312 | } |
| 313 | } else if !str_method_expects_ptr && !is_shared && (is_ptr || is_var_mut) { |
| 314 | if sym.is_c_struct() { |
| 315 | g.write(c_struct_ptr(sym, typ, str_method_expects_ptr)) |
| 316 | } else { |
| 317 | g.write('*'.repeat(expr_type.nr_muls())) |
| 318 | } |
| 319 | } else if !str_method_expects_ptr && is_interface_smartcast_to_nonptr { |
| 320 | g.write('*') |
| 321 | } else if sym.is_c_struct() { |
| 322 | g.write(c_struct_ptr(sym, typ, str_method_expects_ptr)) |
| 323 | } |
| 324 | if expr is ast.ArrayInit { |
| 325 | if expr.is_fixed { |
| 326 | s := g.styp(expr.typ) |
| 327 | if !expr.has_index { |
| 328 | g.write('(${s})') |
| 329 | } |
| 330 | } |
| 331 | } |
| 332 | if unwrap_opt_or_res { |
| 333 | g.expr(expr) |
| 334 | } else { |
| 335 | if temp_var_needed { |
| 336 | g.write(tmp_var) |
| 337 | } else if use_raw_interface_smartcast_expr { |
| 338 | old_inside_selector_lhs := g.inside_selector_lhs |
| 339 | g.inside_selector_lhs = true |
| 340 | g.expr_with_cast(expr, typ, typ) |
| 341 | g.inside_selector_lhs = old_inside_selector_lhs |
| 342 | } else { |
| 343 | g.expr_with_cast(expr, typ, typ) |
| 344 | } |
| 345 | } |
| 346 | |
| 347 | if is_shared { |
| 348 | g.write('->val') |
| 349 | } |
| 350 | g.write(')') |
| 351 | if is_ptr && !is_var_mut { |
| 352 | g.write('}, 0, 0, 0}}))') |
| 353 | } |
| 354 | } else { |
| 355 | is_var_mut := g.expr_is_auto_deref_var(expr) && !typ.has_flag(.option) |
| 356 | str_fn_name := g.get_str_fn(typ) |
| 357 | g.write('${str_fn_name}(') |
| 358 | if sym.kind != .function { |
| 359 | unwrap_option := expr is ast.Ident && expr.or_expr.kind == .propagate_option |
| 360 | exp_typ := if unwrap_option { typ.clear_flag(.option) } else { typ } |
| 361 | temp_var_needed := expr is ast.CallExpr |
| 362 | && (expr.return_type.is_ptr() || g.table.sym(expr.return_type).is_c_struct()) |
| 363 | mut tmp_var := '' |
| 364 | if temp_var_needed { |
| 365 | tmp_var = g.new_tmp_var() |
| 366 | ret_typ := g.styp(exp_typ) |
| 367 | line := g.go_before_last_stmt().trim_space() |
| 368 | g.empty_line = true |
| 369 | g.write('${ret_typ} ${tmp_var} = ') |
| 370 | g.expr(expr) |
| 371 | g.writeln(';') |
| 372 | g.write(line) |
| 373 | } |
| 374 | if str_method_expects_ptr && !is_ptr && !typ.has_flag(.option) { |
| 375 | g.write('&') |
| 376 | } else if typ.has_flag(.option_mut_param_t) { |
| 377 | g.write('*') |
| 378 | } else if (!str_method_expects_ptr && is_ptr && !is_shared) || is_var_mut { |
| 379 | g.write('*'.repeat(typ.nr_muls())) |
| 380 | } else { |
| 381 | if sym.is_c_struct() { |
| 382 | g.write(c_struct_ptr(sym, typ, str_method_expects_ptr)) |
| 383 | } |
| 384 | } |
| 385 | if temp_var_needed { |
| 386 | g.write(tmp_var) |
| 387 | } else { |
| 388 | if expr is ast.StructInit && g.table.final_sym(expr.typ).is_primitive_fixed_array() { |
| 389 | s := g.styp(expr.typ) |
| 390 | g.write('(${s})') |
| 391 | } |
| 392 | g.expr_with_cast(expr, typ, typ) |
| 393 | } |
| 394 | } else if typ.has_flag(.option) { |
| 395 | // only Option fn receive argument |
| 396 | g.expr_with_cast(expr, typ, typ) |
| 397 | } |
| 398 | g.write(')') |
| 399 | } |
| 400 | } |
| 401 | |