v2 / vlib / v / gen / c / index.v
949 lines · 931 sloc · 28.96 KB · 4df08e9c8f8548c0107f3834927287074079fd88
Raw
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.
4module c
5
6import v.ast
7import v.util
8
9struct IndexOperatorMethodInfo {
10 method ast.Fn
11 name string
12 receiver_type ast.Type
13}
14
15enum CWideIndexKind {
16 plain
17 signed_64
18 unsigned_64
19}
20
21fn (mut g Gen) c_wide_index_kind(index_type ast.Type) CWideIndexKind {
22 if g.pref.backend != .c || index_type == 0 {
23 return .plain
24 }
25 mut internal_index_type := g.table.unaliased_type(index_type.clear_flag(.variadic))
26 internal_index_sym := g.table.final_sym(internal_index_type)
27 if internal_index_sym.kind == .enum {
28 internal_index_type = internal_index_sym.enum_info().typ
29 }
30 if internal_index_type == ast.int_literal_type || !internal_index_type.is_int() {
31 return .plain
32 }
33 int_size, _ := g.table.type_size(ast.int_type_idx)
34 internal_index_size, _ := g.table.type_size(internal_index_type.idx_type())
35 if internal_index_type.is_signed() {
36 return if internal_index_size <= int_size { .plain } else { .signed_64 }
37 }
38 return if internal_index_size < int_size { .plain } else { .unsigned_64 }
39}
40
41fn (mut g Gen) write_c_range_bound(expr ast.Expr) {
42 bound_type := g.resolved_expr_type(expr, 0)
43 match g.c_wide_index_kind(bound_type) {
44 .signed_64 {
45 g.write('builtin__v_slice_index_i64(')
46 g.expr(expr)
47 g.write(')')
48 }
49 .unsigned_64 {
50 g.write('builtin__v_slice_index_u64(')
51 g.expr(expr)
52 g.write(')')
53 }
54 else {
55 g.expr(expr)
56 }
57 }
58}
59
60fn (mut g Gen) resolved_index_operator_receiver_type(receiver ast.Expr, receiver_type ast.Type) ast.Type {
61 mut resolved_type := g.recheck_concrete_type(g.resolved_expr_type(receiver, receiver_type))
62 if resolved_type == 0 {
63 resolved_type = receiver_type
64 }
65 if resolved_type == 0 || resolved_type.has_flag(.generic)
66 || g.type_has_unresolved_generic_parts(resolved_type) {
67 resolved_type = g.resolved_expr_type(receiver, receiver_type)
68 }
69 if resolved_type == 0 {
70 resolved_type = receiver_type
71 }
72 return g.unwrap_generic(g.recheck_concrete_type(resolved_type))
73}
74
75fn (mut g Gen) index_operator_method_info(receiver ast.Expr, receiver_type ast.Type, op string) ?IndexOperatorMethodInfo {
76 resolved_receiver_type := g.resolved_index_operator_receiver_type(receiver, receiver_type)
77 recv :=
78 g.unwrap(if resolved_receiver_type != 0 { resolved_receiver_type } else { receiver_type })
79 mut method := ast.Fn{}
80 mut method_name := ''
81 if recv.sym.has_method(op) || recv.sym.has_method_with_generic_parent(op) {
82 method = recv.sym.find_method_with_generic_parent(op) or {
83 recv.sym.find_method(op) or { return none }
84 }
85 method_name = recv.sym.cname + '_' + util.replace_op(op)
86 if recv.sym.is_builtin() {
87 method_name = 'builtin__${method_name}'
88 }
89 } else if recv.unaliased_sym.has_method_with_generic_parent(op) {
90 method = recv.unaliased_sym.find_method_with_generic_parent(op) or { return none }
91 method_name = recv.unaliased_sym.cname + '_' + util.replace_op(op)
92 if recv.unaliased_sym.is_builtin() {
93 method_name = 'builtin__${method_name}'
94 }
95 } else {
96 return none
97 }
98 method_name = g.specialized_method_name_from_receiver(method, recv.typ, method_name)
99 return IndexOperatorMethodInfo{
100 method: method
101 name: method_name
102 receiver_type: recv.typ
103 }
104}
105
106fn (mut g Gen) index_operator_call(receiver ast.Expr, receiver_type ast.Type, index ast.Expr, index_type ast.Type, op string, value ast.Expr, value_type ast.Type) {
107 info := g.index_operator_method_info(receiver, receiver_type, op) or {
108 g.error('missing `${op}` overload for `${g.table.type_to_str(receiver_type)}`',
109 receiver.pos())
110 return
111 }
112 g.write(info.name)
113 g.write('(')
114 g.op_arg(receiver, info.method.params[0].typ, info.receiver_type)
115 g.write(', ')
116 g.op_arg(index, info.method.params[1].typ, index_type)
117 if op == '[]=' {
118 g.write(', ')
119 g.op_arg(value, info.method.params[2].typ, value_type)
120 }
121 g.write(')')
122}
123
124fn (mut g Gen) index_expr(node ast.IndexExpr) {
125 if node.index is ast.RangeExpr {
126 g.index_range_expr(node, node.index)
127 } else {
128 if node.is_index_operator {
129 g.index_operator_call(node.left, node.left_type, node.index, node.index_type, '[]',
130 ast.empty_expr, ast.void_type)
131 return
132 }
133 mut left_type := ast.Type(0)
134 if node.left is ast.Ident {
135 resolved_current_type := g.resolve_current_fn_generic_param_type(node.left.name)
136 if resolved_current_type != 0 {
137 left_type = g.unwrap_generic(g.recheck_concrete_type(resolved_current_type))
138 }
139 }
140 if left_type == 0 {
141 left_type = g.unwrap_generic(g.recheck_concrete_type(node.left_type))
142 }
143 if left_type == 0 || left_type.has_flag(.generic)
144 || g.type_has_unresolved_generic_parts(left_type) {
145 left_type = g.unwrap_generic(g.recheck_concrete_type(g.resolved_expr_type(node.left,
146 node.left_type)))
147 }
148 if left_type == 0 {
149 left_type = g.unwrap_generic(g.type_resolver.get_type_or_default(node.left,
150 node.left_type))
151 }
152 sym := g.table.final_sym(left_type)
153 if sym.kind == .array {
154 g.index_of_array(node, sym)
155 } else if sym.kind == .array_fixed {
156 g.index_of_fixed_array(node, sym)
157 } else if sym.kind == .map {
158 g.index_of_map(node, sym)
159 } else if sym.kind == .string && !node.left_type.is_ptr() {
160 gen_or := node.or_expr.kind != .absent || node.is_option
161 wide_index_kind := g.c_wide_index_kind(node.index_type)
162 string_at_fn := if node.is_gated {
163 'builtin__string_at_ni'
164 } else {
165 match wide_index_kind {
166 .signed_64 { 'builtin__string_at_i64' }
167 .unsigned_64 { 'builtin__string_at_u64' }
168 else { 'builtin__string_at' }
169 }
170 }
171 string_at_with_check_fn := if node.is_gated {
172 'builtin__string_at_with_check_ni'
173 } else {
174 match wide_index_kind {
175 .signed_64 { 'builtin__string_at_with_check_i64' }
176 .unsigned_64 { 'builtin__string_at_with_check_u64' }
177 else { 'builtin__string_at_with_check' }
178 }
179 }
180 if gen_or {
181 tmp_opt := g.new_tmp_var()
182 cur_line := g.go_before_last_stmt()
183 g.out.write_string(util.tabs(g.indent))
184 opt_elem_type := g.styp(ast.u8_type.set_flag(.option))
185 g.write('${opt_elem_type} ${tmp_opt} = ${string_at_with_check_fn}(')
186 g.expr(ast.Expr(node.left))
187 g.write(', ')
188 g.expr(node.index)
189 g.writeln(');')
190 if !node.is_option {
191 g.or_block(tmp_opt, node.or_expr, ast.u8_type)
192 }
193 g.write('\n${cur_line}*(byte*)&${tmp_opt}.data')
194 } else {
195 is_direct_array_access := !node.is_gated && wide_index_kind == .plain
196 && (g.is_direct_array_access || node.is_direct)
197 if is_direct_array_access {
198 g.expr(ast.Expr(node.left))
199 g.write('.str[ ')
200 g.expr(node.index)
201 g.write(']')
202 } else {
203 g.write('${string_at_fn}(')
204 g.expr(ast.Expr(node.left))
205 g.write(', ')
206 g.expr(node.index)
207 g.write(')')
208 }
209 }
210 } else if sym.info is ast.Aggregate
211 && sym.info.types.all(g.table.type_kind(it) in [.array, .array_fixed, .string, .map]) {
212 // treating sumtype of array types
213 unwrapped_got_type := sym.info.types[g.aggregate_type_idx]
214 g.index_expr(ast.IndexExpr{ ...node, left_type: unwrapped_got_type })
215 } else {
216 g.expr(ast.Expr(node.left))
217 g.write('[')
218 g.expr(node.index)
219 g.write(']')
220 }
221 }
222}
223
224fn (mut g Gen) index_range_expr(node ast.IndexExpr, range ast.RangeExpr) {
225 mut resolved_left_type := ast.Type(0)
226 if node.left is ast.Ident {
227 resolved_current_type := g.resolve_current_fn_generic_param_type(node.left.name)
228 if resolved_current_type != 0 {
229 resolved_left_type = resolved_current_type
230 }
231 }
232 if resolved_left_type == 0 {
233 resolved_left_type = g.recheck_concrete_type(node.left_type)
234 }
235 if resolved_left_type == 0 || resolved_left_type.has_flag(.generic)
236 || g.type_has_unresolved_generic_parts(resolved_left_type) {
237 resolved_left_type = g.resolved_expr_type(node.left, node.left_type)
238 }
239 if resolved_left_type == 0 {
240 resolved_left_type = g.type_resolver.get_type_or_default(node.left, node.left_type)
241 }
242 unwrapped_left_type := g.unwrap_generic(g.recheck_concrete_type(resolved_left_type))
243 sym := g.table.final_sym(unwrapped_left_type)
244 mut tmp_opt := ''
245 mut cur_line := ''
246 mut gen_or := node.or_expr.kind != .absent || node.is_option
247 left_is_shared := resolved_left_type.has_flag(.shared_f)
248 if sym.kind == .string {
249 if node.is_gated {
250 g.write('builtin__string_substr_ni(')
251 } else {
252 if gen_or {
253 tmp_opt = g.new_tmp_var()
254 cur_line = g.go_before_last_stmt()
255 g.out.write_string(util.tabs(g.indent))
256 opt_elem_type := g.styp(ast.string_type.set_flag(.result))
257 g.write('${opt_elem_type} ${tmp_opt} = builtin__string_substr_with_check(')
258 } else {
259 g.write('builtin__string_substr(')
260 }
261 }
262 if resolved_left_type.is_ptr() {
263 g.write('*')
264 }
265 g.expr(ast.Expr(node.left))
266 } else if sym.kind == .array {
267 if node.is_gated {
268 g.write('builtin__array_slice_ni(')
269 } else {
270 g.write('builtin__array_slice(')
271 }
272 if left_is_shared {
273 g.write('(')
274 }
275 if resolved_left_type.is_ptr() {
276 g.write('*')
277 }
278 g.expr(ast.Expr(node.left))
279 if left_is_shared {
280 g.write(').val')
281 }
282 } else if sym.info is ast.ArrayFixed {
283 // Convert a fixed array to V array when doing `fixed_arr[start..end]`
284 noscan := g.check_noscan(sym.info.elem_type)
285 if node.is_gated {
286 g.write('builtin__array_slice_ni(')
287 } else {
288 g.write('builtin__array_slice(')
289 }
290 g.write('builtin__new_array_from_c_array${noscan}(')
291 ctype := g.styp(sym.info.elem_type)
292 g.write('${sym.info.size}, ${sym.info.size}, sizeof(${ctype}), ')
293 if left_is_shared {
294 g.write('(')
295 }
296 if resolved_left_type.is_ptr() {
297 g.write('*')
298 }
299 if node.left is ast.ArrayInit {
300 var := g.new_tmp_var()
301 line := g.go_before_last_stmt().trim_space()
302 styp := g.styp(node.left_type)
303 g.empty_line = true
304 g.write('${styp} ${var} = ')
305 g.expr(ast.Expr(node.left))
306 g.writeln(';')
307 g.write2(line, ' ${var}')
308 } else {
309 g.expr(ast.Expr(node.left))
310 }
311 if left_is_shared {
312 g.write(').val')
313 }
314 g.write(')')
315 } else {
316 g.expr(ast.Expr(node.left))
317 }
318 g.write(', ')
319 if range.has_low {
320 g.write_c_range_bound(range.low)
321 } else {
322 g.write('0')
323 }
324 g.write(', ')
325 if range.has_high {
326 g.write_c_range_bound(range.high)
327 } else if sym.info is ast.ArrayFixed {
328 g.write('${sym.info.size}')
329 } else {
330 g.write('${max_int}')
331 }
332 g.write(')')
333
334 if gen_or {
335 if !node.is_option {
336 g.or_block(tmp_opt, node.or_expr, ast.string_type.set_flag(.result))
337 }
338
339 g.write('\n${cur_line}*(string*)&${tmp_opt}.data')
340 }
341}
342
343fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
344 gen_or := node.or_expr.kind != .absent || node.is_option
345 resolved_left_type := g.recheck_concrete_type(g.resolved_expr_type(node.left, node.left_type))
346 left_type := if resolved_left_type != 0 { resolved_left_type } else { node.left_type }
347 left_sym := if left_type != 0 {
348 g.table.final_sym(g.unwrap_generic(left_type))
349 } else {
350 g.table.final_sym(g.unwrap_generic(node.left_type))
351 }
352 array_left_type := if left_sym.kind == .array { left_type } else { node.left_type }
353 info := if left_sym.kind == .array {
354 left_sym.info as ast.Array
355 } else {
356 sym.info as ast.Array
357 }
358 resolved_elem_type_ := g.recheck_concrete_type(g.resolved_expr_type(node, node.typ))
359 resolved_elem_type := if resolved_elem_type_ == ast.int_literal_type {
360 ast.int_type
361 } else if resolved_elem_type_ == ast.float_literal_type {
362 ast.f64_type
363 } else {
364 resolved_elem_type_
365 }
366 elem_type := if resolved_elem_type != 0 && resolved_elem_type != ast.void_type {
367 resolved_elem_type
368 } else if info.elem_type == ast.int_literal_type {
369 ast.int_type
370 } else if info.elem_type == ast.float_literal_type {
371 ast.f64_type
372 } else {
373 info.elem_type
374 }
375 elem_sym := g.table.final_sym(elem_type)
376 mut left_is_ptr := array_left_type.is_ptr() || node.left.is_auto_deref_var()
377 // Auto-heap idents are pointers in C. `g.expr` writes `(*(name))` to deref them,
378 // but suppresses that deref when the ident is on an assign LHS / inside a selector
379 // LHS / inside an assign-fn-var context. In those cases the C result of `g.expr`
380 // is a pointer, so this IndexExpr must emit its own `*` before `array_get`.
381 if !left_is_ptr && node.left is ast.Ident && g.resolved_ident_is_auto_heap(node.left)
382 && (g.is_assign_lhs || g.inside_selector_lhs || g.inside_assign_fn_var) {
383 left_is_ptr = true
384 }
385 result_type := match true {
386 gen_or && elem_type.has_flag(.option) {
387 node.typ.clear_flag(.option)
388 }
389 gen_or {
390 node.typ
391 }
392 else {
393 elem_type
394 }
395 }
396
397 result_sym := g.table.final_sym(result_type)
398 elem_type_str := if elem_sym.kind == .function { 'voidptr' } else { g.styp(elem_type) }
399 result_type_str := if result_sym.kind == .function { 'voidptr' } else { g.styp(result_type) }
400 left_is_shared := array_left_type.has_flag(.shared_f)
401 wide_index_kind := g.c_wide_index_kind(node.index_type)
402 array_get_fn := if node.is_gated {
403 'builtin__array_get_ni'
404 } else {
405 match wide_index_kind {
406 .signed_64 { 'builtin__array_get_i64' }
407 .unsigned_64 { 'builtin__array_get_u64' }
408 else { 'builtin__array_get' }
409 }
410 }
411 array_get_with_check_fn := if node.is_gated {
412 'builtin__array_get_with_check_ni'
413 } else {
414 match wide_index_kind {
415 .signed_64 { 'builtin__array_get_with_check_i64' }
416 .unsigned_64 { 'builtin__array_get_with_check_u64' }
417 else { 'builtin__array_get_with_check' }
418 }
419 }
420 array_set_fn := if node.is_gated {
421 'builtin__array_set_ni'
422 } else {
423 match wide_index_kind {
424 .signed_64 { 'builtin__array_set_i64' }
425 .unsigned_64 { 'builtin__array_set_u64' }
426 else { 'builtin__array_set' }
427 }
428 }
429 // `vals[i].field = x` is an exception and requires `array_get`:
430 // `(*(Val*)array_get(vals, i)).field = x;`
431 if g.is_assign_lhs && node.is_setter {
432 is_direct_array_access := !node.is_gated && wide_index_kind == .plain
433 && (g.is_direct_array_access || node.is_direct)
434 is_op_assign := g.assign_op != .assign && info.elem_type != ast.string_type
435 if is_direct_array_access {
436 g.write('((${elem_type_str}*)')
437 } else if is_op_assign {
438 g.write('(*(${elem_type_str}*)${array_get_fn}(')
439 if left_is_ptr && !left_is_shared {
440 g.write('*')
441 }
442 } else {
443 g.cur_indexexpr << node.pos.pos
444 g.is_arraymap_set = true // special handling of assign_op and closing with '})'
445 g.write('${array_set_fn}(')
446 if !left_is_ptr || left_is_shared {
447 g.write('&')
448 }
449 }
450 if node.left is ast.IndexExpr {
451 g.inside_array_index = true
452 g.expr(ast.Expr(node.left))
453 g.inside_array_index = false
454 } else {
455 g.expr(ast.Expr(node.left))
456 }
457
458 if left_is_shared {
459 if node.index !is ast.RangeExpr && left_is_ptr {
460 g.write('->val')
461 } else {
462 g.write('.val')
463 }
464 }
465 if is_direct_array_access {
466 if left_is_ptr && !left_is_shared {
467 g.write('->')
468 } else {
469 g.write('.')
470 }
471 g.write('data)[')
472 g.expr(node.index)
473 g.write(']')
474 } else {
475 g.write(', ')
476 g.expr(node.index)
477 if !is_op_assign {
478 mut need_wrapper := true
479 /*
480 match node.right {
481 ast.EnumVal, ast.Ident {
482 // `&x` is enough for variables and enums
483 // `&(Foo[]){ ... }` is only needed for function calls and literals
484 need_wrapper = false
485 }
486 else {}
487 }
488 */
489 if elem_sym.kind != .array_fixed {
490 if need_wrapper {
491 g.write(', &(${elem_type_str}[]) { ')
492 } else {
493 g.write(', &')
494 }
495 }
496 } else {
497 // `x[0] *= y`
498 g.write('))')
499 }
500 }
501 } else {
502 is_direct_array_access := !node.is_gated && wide_index_kind == .plain
503 && (g.is_direct_array_access || node.is_direct)
504 is_fn_index_call := g.is_fn_index_call && elem_sym.info is ast.FnType
505 // do not clone inside `opt_ok(opt_ok(&(string[]) {..})` before returns
506 needs_clone := info.elem_type == ast.string_type_idx && g.is_autofree && !(g.inside_return
507 && g.fn_decl != unsafe { nil } && g.fn_decl.return_type.has_flag(.option))
508 && !g.is_assign_lhs
509 is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result
510 cur_line := if is_gen_or_and_assign_rhs {
511 line := g.go_before_last_stmt()
512 g.out.write_string(util.tabs(g.indent))
513 line
514 } else {
515 ''
516 }
517 tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
518 tmp_opt_ptr := if gen_or { g.new_tmp_var() } else { '' }
519 if gen_or {
520 g.write('${elem_type_str}* ${tmp_opt_ptr} = (${elem_type_str}*)(${array_get_with_check_fn}(')
521 if left_is_ptr && !left_is_shared {
522 g.write('*')
523 }
524 } else {
525 if needs_clone {
526 g.write('builtin__string_clone(')
527 }
528 if is_fn_index_call {
529 if elem_sym.info is ast.FnType {
530 g.write('((')
531 g.write_fn_ptr_decl(&elem_sym.info, '')
532 if is_direct_array_access {
533 g.write(')((${elem_type_str}*)')
534 } else {
535 g.write(')(*(${elem_type_str}*)${array_get_fn}(')
536 }
537 }
538 if left_is_ptr && !left_is_shared && !is_direct_array_access {
539 g.write('*')
540 }
541 } else if is_direct_array_access {
542 g.write('((${elem_type_str}*)')
543 } else {
544 g.write('(*(${elem_type_str}*)${array_get_fn}(')
545 if left_is_ptr && !left_is_shared {
546 g.write('*')
547 }
548 }
549 }
550 g.expr(ast.Expr(node.left))
551 // TODO: test direct_array_access when 'shared' is implemented
552 if left_is_shared {
553 if left_is_ptr {
554 g.write('->val')
555 } else {
556 g.write('.val')
557 }
558 }
559 if is_direct_array_access && !gen_or {
560 if left_is_ptr && !left_is_shared {
561 g.write('->')
562 } else {
563 g.write('.')
564 }
565 g.write('data)[')
566 g.expr(node.index)
567 g.write(']')
568 if is_fn_index_call {
569 g.write(')')
570 }
571 } else {
572 g.write(', ')
573 g.expr(node.index)
574 if is_fn_index_call {
575 g.write(')))')
576 } else {
577 g.write('))')
578 }
579 }
580 if !gen_or && needs_clone {
581 g.write(')')
582 }
583 if gen_or {
584 g.writeln(';')
585 opt_elem_type := g.styp(elem_type.set_flag(.option))
586 g.writeln('${opt_elem_type} ${tmp_opt} = {0};')
587 g.writeln('if (${tmp_opt_ptr}) {')
588 if elem_type.has_flag(.option) && !g.inside_opt_or_res {
589 g.writeln('\tif (${tmp_opt_ptr}->state == 0) {')
590 g.writeln('\t\t*((${result_type_str}*)&${tmp_opt}.data) = *((${result_type_str}*)${tmp_opt_ptr}->data);')
591 g.writeln('\t} else {')
592 g.writeln('\t\t${tmp_opt}.state = ${tmp_opt_ptr}->state;')
593 g.writeln('\t\t${tmp_opt}.err = ${tmp_opt_ptr}->err;')
594 g.writeln('\t}')
595 } else {
596 g.writeln('\t*((${elem_type_str}*)&${tmp_opt}.data) = *((${elem_type_str}*)${tmp_opt_ptr});')
597 }
598 g.writeln('} else {')
599 g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = builtin___v_error(_S("array index out of range"));')
600 g.writeln('}')
601 if !node.is_option {
602 g.or_block(tmp_opt, node.or_expr, elem_type)
603 }
604 if is_gen_or_and_assign_rhs {
605 g.set_current_pos_as_last_stmt_pos()
606 }
607 if !g.is_amp {
608 if g.inside_opt_or_res && elem_type.has_flag(.option) && g.inside_assign {
609 g.write('\n${cur_line}(*(${elem_type_str}*)&${tmp_opt})')
610 } else if elem_type.has_flag(.option) && !g.inside_opt_or_res {
611 g.write('\n${cur_line}(*(${result_type_str}*)${tmp_opt}.data)')
612 } else {
613 g.write('\n${cur_line}(*(${elem_type_str}*)${tmp_opt}.data)')
614 }
615 } else {
616 g.write('\n${cur_line}*${tmp_opt_ptr}')
617 }
618 }
619 }
620}
621
622fn (mut g Gen) index_of_fixed_array(node ast.IndexExpr, sym ast.TypeSymbol) {
623 info := sym.info as ast.ArrayFixed
624 elem_type := info.elem_type
625 elem_sym := g.table.sym(elem_type)
626 is_fn_index_call := g.is_fn_index_call && elem_sym.info is ast.FnType
627 wide_index_kind := g.c_wide_index_kind(node.index_type)
628
629 if node.left is ast.ArrayInit {
630 past := g.past_tmp_var_new()
631 styp := g.styp(node.left_type)
632 g.write('${styp} ${past.tmp_var} = ')
633 g.expr(ast.Expr(node.left))
634 g.writeln(';')
635 g.past_tmp_var_done(past)
636 } else if node.left is ast.IndexExpr && node.left.is_setter {
637 line := g.go_before_last_stmt().trim_space()
638 g.empty_line = true
639 tmp_var := g.new_tmp_var()
640 styp := g.styp(node.left_type)
641 g.write('${styp}* ${tmp_var} = &')
642 g.expr(ast.Expr(node.left))
643 g.writeln(';')
644 g.write(line)
645 g.write('(*')
646 g.write(tmp_var)
647 g.write(')')
648 } else {
649 if is_fn_index_call {
650 g.write('(*')
651 }
652 if node.left_type.is_ptr() || node.left.is_auto_deref_var() {
653 g.write('(*')
654 g.expr(ast.Expr(node.left))
655 g.write(')')
656 } else {
657 g.expr(ast.Expr(node.left))
658 }
659 if node.left_type.has_flag(.shared_f) {
660 g.write('.val')
661 }
662 }
663 g.write('[')
664 if node.is_gated {
665 g.write('builtin__v_fixed_index_ni(')
666 g.expr(node.index)
667 g.write(', ${info.size})')
668 } else if wide_index_kind == .signed_64 {
669 g.write('builtin__v_fixed_index_i64(')
670 g.expr(node.index)
671 g.write(', ${info.size})')
672 } else if wide_index_kind == .unsigned_64 {
673 g.write('builtin__v_fixed_index_u64(')
674 g.expr(node.index)
675 g.write(', ${info.size})')
676 } else if g.is_direct_array_access || g.pref.translated || node.index is ast.IntegerLiteral {
677 g.expr(node.index)
678 } else {
679 // bounds check
680 g.write('builtin__v_fixed_index(')
681 g.expr(node.index)
682 g.write(', ${info.size})')
683 }
684 g.write(']')
685 if is_fn_index_call {
686 g.write(')')
687 }
688}
689
690fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
691 gen_or := node.or_expr.kind != .absent || node.is_option
692 mut map_left_type := g.recheck_concrete_type(node.left_type)
693 resolved_left_type := g.recheck_concrete_type(g.resolved_expr_type(node.left, node.left_type))
694 if resolved_left_type != 0
695 && (g.cur_concrete_types.len > 0 || map_left_type == 0 || map_left_type.has_flag(.generic)
696 || g.type_has_unresolved_generic_parts(map_left_type)
697 || g.unwrap_generic(resolved_left_type) != g.unwrap_generic(map_left_type)) {
698 map_left_type = resolved_left_type
699 }
700 mut left_is_ptr := map_left_type.is_ptr()
701 if !left_is_ptr && g.is_assign_lhs && node.left is ast.Ident
702 && g.resolved_ident_is_auto_heap(node.left) {
703 left_is_ptr = true
704 }
705 left_sym := if map_left_type != 0 {
706 *g.table.final_sym(g.unwrap_generic(map_left_type))
707 } else {
708 sym
709 }
710 info := if left_sym.kind == .map {
711 left_sym.info as ast.Map
712 } else {
713 sym.info as ast.Map
714 }
715 mut key_type := g.unwrap_generic(g.recheck_concrete_type(info.key_type))
716 mut val_type := g.unwrap_generic(g.recheck_concrete_type(info.value_type))
717 if key_type == 0 {
718 key_type = info.key_type
719 }
720 if val_type == 0 {
721 val_type = info.value_type
722 }
723 if node.left is ast.Ident {
724 ident_key_type := g.resolved_ident_map_key_type(node.left)
725 if ident_key_type != 0 {
726 key_type = ident_key_type
727 }
728 ident_val_type := g.resolved_ident_map_value_type(node.left)
729 if ident_val_type != 0 {
730 val_type = ident_val_type
731 }
732 }
733 if key_type == ast.usize_type || val_type == ast.usize_type {
734 name_key_type, name_val_type := g.resolved_map_types_from_name(left_sym.name)
735 if key_type == ast.usize_type && name_key_type != 0 {
736 key_type = name_key_type
737 }
738 if val_type == ast.usize_type && name_val_type != 0 {
739 val_type = name_val_type
740 }
741 }
742 if val_type == ast.usize_type && node.typ != 0 {
743 candidate_val_type := g.unwrap_generic(g.recheck_concrete_type(node.typ))
744 if candidate_val_type != 0 && candidate_val_type != ast.void_type
745 && candidate_val_type !in [ast.int_literal_type, ast.float_literal_type] {
746 val_type = candidate_val_type
747 }
748 }
749 val_sym := g.table.final_sym(val_type)
750 left_is_shared := map_left_type.has_flag(.shared_f)
751 val_type_str := if val_sym.kind == .function {
752 'voidptr'
753 } else {
754 if g.inside_return {
755 g.styp(val_type)
756 } else {
757 g.styp(val_type.clear_flag(.result))
758 }
759 }
760 get_and_set_types := val_sym.kind in [.struct, .map, .array, .array_fixed]
761 use_get_and_set := g.inside_left_shift || (node.is_setter && !g.inside_map_infix)
762 if g.is_assign_lhs && !g.is_arraymap_set && !get_and_set_types {
763 if g.assign_op == .assign || val_type == ast.string_type {
764 g.cur_indexexpr << node.pos.pos
765 g.is_arraymap_set = true
766 g.write('builtin__map_set(')
767 } else {
768 if use_get_and_set {
769 g.write('(*((${val_type_str}*)builtin__map_get_and_set((map*)')
770 } else {
771 g.write('(*((${val_type_str}*)builtin__map_get((map*)')
772 }
773 }
774 if !left_is_ptr || left_is_shared {
775 g.write('&')
776 }
777 if node.left is ast.IndexExpr {
778 g.inside_map_index = true
779 g.expr(ast.Expr(node.left))
780 g.inside_map_index = false
781 } else {
782 g.expr(node.left)
783 }
784 if left_is_shared {
785 g.write('->val')
786 }
787 g.write(', ')
788 old_is_arraymap_set := g.is_arraymap_set
789 old_is_assign_lhs := g.is_assign_lhs
790 g.is_arraymap_set = false
791 g.is_assign_lhs = false
792 g.write_map_key_arg(node.index, key_type)
793 g.is_arraymap_set = old_is_arraymap_set
794 g.is_assign_lhs = old_is_assign_lhs
795 g.arraymap_set_pos = g.out.len
796 g.write(', &(${val_type_str}[]) { ')
797 if g.assign_op != .assign && val_type != ast.string_type {
798 zero := g.type_default(val_type)
799 g.write('${zero} })))')
800 }
801 } else if !gen_or && (g.inside_map_postfix || g.inside_map_infix
802 || g.inside_map_index || g.inside_array_index || g.inside_left_shift
803 || (g.is_assign_lhs && !g.is_arraymap_set && get_and_set_types)) {
804 zero := g.type_default(val_type)
805 if use_get_and_set {
806 g.write('(*(${val_type_str}*)builtin__map_get_and_set((map*)')
807 } else {
808 g.write('(*(${val_type_str}*)builtin__map_get((map*)')
809 }
810 if !left_is_ptr || left_is_shared {
811 g.write('&')
812 }
813 g.expr(node.left)
814 if left_is_shared {
815 g.write('->val')
816 }
817 g.write(', ')
818 old_is_assign_lhs := g.is_assign_lhs
819 g.is_assign_lhs = false
820 g.write_map_key_arg(node.index, key_type)
821 g.is_assign_lhs = old_is_assign_lhs
822 g.write(', &(${val_type_str}[]){ ${zero} }))')
823 } else {
824 zero := g.type_default(val_type)
825 is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result
826 cur_line := if is_gen_or_and_assign_rhs {
827 line := g.go_before_last_stmt()
828 g.out.write_string(util.tabs(g.indent))
829 line
830 } else {
831 ''
832 }
833 tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
834 tmp_opt_ptr := if gen_or { g.new_tmp_var() } else { '' }
835 mut is_fn_last_index_call := false
836 if gen_or {
837 g.write('${val_type_str}* ${tmp_opt_ptr} = (${val_type_str}*)(builtin__map_get_check(')
838 } else {
839 if g.is_fn_index_call {
840 if val_sym.info is ast.FnType {
841 g.write('((')
842 g.write_fn_ptr_decl(&val_sym.info, '')
843 g.write(')(*(voidptr*)builtin__map_get(')
844 is_fn_last_index_call = true
845 g.is_fn_index_call = false
846 }
847 } else {
848 g.write('(*(${val_type_str}*)builtin__map_get(')
849 }
850 }
851 if !left_is_ptr || left_is_shared {
852 g.write('ADDR(map, ')
853 g.expr(node.left)
854 } else {
855 g.write('(')
856 g.expr(node.left)
857 }
858 if left_is_shared {
859 if left_is_ptr {
860 g.write('->val')
861 } else {
862 g.write('.val')
863 }
864 }
865 g.write('), ')
866 g.write_map_key_arg(node.index, key_type)
867 if gen_or {
868 g.write('))')
869 } else if is_fn_last_index_call {
870 g.write(', &(voidptr[]){ ${zero} })))')
871 } else {
872 g.write(', &(${val_type_str}[]){ ${zero} }))')
873 }
874 if gen_or {
875 g.writeln(';')
876 // The surrounding expression may already carry a pointer prefix placeholder.
877 direct_ptr_cur_line := if cur_line.ends_with('&') || cur_line.ends_with('H') {
878 cur_line[..cur_line.len - 1]
879 } else {
880 cur_line
881 }
882 if g.unsafe_level > 0 && !node.is_option && !node.is_setter
883 && node.or_expr.kind == .block && node.or_expr.stmts.len == 1 {
884 last_stmt := node.or_expr.stmts[0]
885 if last_stmt is ast.ExprStmt && last_stmt.typ.is_any_kind_of_pointer() {
886 // handle the case of `p := unsafe{ &m[key] or { nil } }` directly, without an intermediate option + copies etc:
887 g.write('if (!${tmp_opt_ptr}) { ${tmp_opt_ptr} = ')
888 g.expr(last_stmt.expr)
889 g.write('; }')
890 if cur_line.ends_with('&') || cur_line.ends_with('H') {
891 g.write('\n${direct_ptr_cur_line}${tmp_opt_ptr}')
892 } else {
893 g.write('\n${cur_line}(*${tmp_opt_ptr})')
894 }
895 return
896 }
897 }
898 if val_type.has_flag(.option) {
899 or_value_type := g.resolved_or_block_value_type(node.or_expr)
900 keep_option_result := node.is_option || (node.or_expr.kind == .block
901 && (or_value_type in [ast.none_type, ast.none_type_idx]
902 || or_value_type.has_flag(.option)))
903 plain_val_type := val_type.clear_option_and_result()
904 plain_val_sym := g.table.final_sym(plain_val_type)
905 plain_val_type_str := if plain_val_sym.kind == .function {
906 'voidptr'
907 } else {
908 g.styp(plain_val_type)
909 }
910 g.writeln('${val_type_str} ${tmp_opt} = {0};')
911 g.writeln('if (${tmp_opt_ptr}) {')
912 g.writeln('\t${tmp_opt} = *${tmp_opt_ptr};')
913 g.writeln('} else {')
914 g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = builtin___v_error(_S("map key does not exist"));')
915 g.writeln('}')
916 if !node.is_option {
917 g.or_block_on_value(tmp_opt, node.or_expr, val_type)
918 }
919 if is_gen_or_and_assign_rhs {
920 g.set_current_pos_as_last_stmt_pos()
921 }
922 if keep_option_result {
923 g.write('\n${cur_line}${tmp_opt}')
924 } else {
925 g.write('\n${cur_line}(*(${plain_val_type_str}*)${tmp_opt}.data)')
926 }
927 } else {
928 opt_val_type := g.styp(val_type.set_flag(.option))
929 g.writeln('${opt_val_type} ${tmp_opt} = {0};')
930 g.writeln('if (${tmp_opt_ptr}) {')
931 if val_sym.kind == .array_fixed {
932 g.writeln('\tmemcpy((${val_type_str}*)${tmp_opt}.data, (${val_type_str}*)${tmp_opt_ptr}, sizeof(${val_type_str}));')
933 } else {
934 g.writeln('\t*((${val_type_str}*)&${tmp_opt}.data) = *((${val_type_str}*)${tmp_opt_ptr});')
935 }
936 g.writeln('} else {')
937 g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = builtin___v_error(_S("map key does not exist"));')
938 g.writeln('}')
939 if !node.is_option {
940 g.or_block(tmp_opt, node.or_expr, val_type)
941 }
942 if is_gen_or_and_assign_rhs {
943 g.set_current_pos_as_last_stmt_pos()
944 }
945 g.write('\n${cur_line}(*(${val_type_str}*)${tmp_opt}.data)')
946 }
947 }
948 }
949}
950