v4 / vlib / v / gen / c / index.v
965 lines · 946 sloc · 29.46 KB · 00645aa246e8ebedd74a1534aa500c1d9776811f
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_or_expr_keeps_option(node ast.IndexExpr) bool {
225 if !node.typ.has_flag(.option) || node.or_expr.kind != .block {
226 return false
227 }
228 or_value_type := g.resolved_or_block_value_type(node.or_expr)
229 return or_value_type in [ast.none_type, ast.none_type_idx] || or_value_type.has_flag(.option)
230}
231
232fn (mut g Gen) index_range_expr(node ast.IndexExpr, range ast.RangeExpr) {
233 mut resolved_left_type := ast.Type(0)
234 if node.left is ast.Ident {
235 resolved_current_type := g.resolve_current_fn_generic_param_type(node.left.name)
236 if resolved_current_type != 0 {
237 resolved_left_type = resolved_current_type
238 }
239 }
240 if resolved_left_type == 0 {
241 resolved_left_type = g.recheck_concrete_type(node.left_type)
242 }
243 if resolved_left_type == 0 || resolved_left_type.has_flag(.generic)
244 || g.type_has_unresolved_generic_parts(resolved_left_type) {
245 resolved_left_type = g.resolved_expr_type(node.left, node.left_type)
246 }
247 if resolved_left_type == 0 {
248 resolved_left_type = g.type_resolver.get_type_or_default(node.left, node.left_type)
249 }
250 unwrapped_left_type := g.unwrap_generic(g.recheck_concrete_type(resolved_left_type))
251 sym := g.table.final_sym(unwrapped_left_type)
252 mut tmp_opt := ''
253 mut cur_line := ''
254 mut gen_or := node.or_expr.kind != .absent || node.is_option
255 left_is_shared := resolved_left_type.has_flag(.shared_f)
256 if sym.kind == .string {
257 if node.is_gated {
258 g.write('builtin__string_substr_ni(')
259 } else {
260 if gen_or {
261 tmp_opt = g.new_tmp_var()
262 cur_line = g.go_before_last_stmt()
263 g.out.write_string(util.tabs(g.indent))
264 opt_elem_type := g.styp(ast.string_type.set_flag(.result))
265 g.write('${opt_elem_type} ${tmp_opt} = builtin__string_substr_with_check(')
266 } else {
267 g.write('builtin__string_substr(')
268 }
269 }
270 if resolved_left_type.is_ptr() {
271 g.write('*')
272 }
273 g.expr(ast.Expr(node.left))
274 } else if sym.kind == .array {
275 if node.is_gated {
276 g.write('builtin__array_slice_ni(')
277 } else {
278 g.write('builtin__array_slice(')
279 }
280 if left_is_shared {
281 g.write('(')
282 }
283 if resolved_left_type.is_ptr() {
284 g.write('*')
285 }
286 g.expr(ast.Expr(node.left))
287 if left_is_shared {
288 g.write(').val')
289 }
290 } else if sym.info is ast.ArrayFixed {
291 // Convert a fixed array to V array when doing `fixed_arr[start..end]`
292 noscan := g.check_noscan(sym.info.elem_type)
293 if node.is_gated {
294 g.write('builtin__array_slice_ni(')
295 } else {
296 g.write('builtin__array_slice(')
297 }
298 g.write('builtin__new_array_from_c_array${noscan}(')
299 ctype := g.styp(sym.info.elem_type)
300 g.write('${sym.info.size}, ${sym.info.size}, sizeof(${ctype}), ')
301 if left_is_shared {
302 g.write('(')
303 }
304 if resolved_left_type.is_ptr() {
305 g.write('*')
306 }
307 if node.left is ast.ArrayInit {
308 var := g.new_tmp_var()
309 line := g.go_before_last_stmt().trim_space()
310 styp := g.styp(node.left_type)
311 g.empty_line = true
312 g.write('${styp} ${var} = ')
313 g.expr(ast.Expr(node.left))
314 g.writeln(';')
315 g.write2(line, ' ${var}')
316 } else {
317 g.expr(ast.Expr(node.left))
318 }
319 if left_is_shared {
320 g.write(').val')
321 }
322 g.write(')')
323 } else {
324 g.expr(ast.Expr(node.left))
325 }
326 g.write(', ')
327 if range.has_low {
328 g.write_c_range_bound(range.low)
329 } else {
330 g.write('0')
331 }
332 g.write(', ')
333 if range.has_high {
334 g.write_c_range_bound(range.high)
335 } else if sym.info is ast.ArrayFixed {
336 g.write('${sym.info.size}')
337 } else {
338 g.write('${max_int}')
339 }
340 g.write(')')
341
342 if gen_or {
343 if !node.is_option {
344 g.or_block(tmp_opt, node.or_expr, ast.string_type.set_flag(.result))
345 }
346
347 g.write('\n${cur_line}*(string*)&${tmp_opt}.data')
348 }
349}
350
351fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
352 gen_or := node.or_expr.kind != .absent || node.is_option
353 resolved_left_type := g.recheck_concrete_type(g.resolved_expr_type(node.left, node.left_type))
354 left_type := if resolved_left_type != 0 { resolved_left_type } else { node.left_type }
355 left_sym := if left_type != 0 {
356 g.table.final_sym(g.unwrap_generic(left_type))
357 } else {
358 g.table.final_sym(g.unwrap_generic(node.left_type))
359 }
360 array_left_type := if left_sym.kind == .array { left_type } else { node.left_type }
361 info := if left_sym.kind == .array {
362 left_sym.info as ast.Array
363 } else {
364 sym.info as ast.Array
365 }
366 resolved_elem_type_ := g.recheck_concrete_type(g.resolved_expr_type(node, node.typ))
367 resolved_elem_type := if resolved_elem_type_ == ast.int_literal_type {
368 ast.int_type
369 } else if resolved_elem_type_ == ast.float_literal_type {
370 ast.f64_type
371 } else {
372 resolved_elem_type_
373 }
374 mut elem_type := if resolved_elem_type != 0 && resolved_elem_type != ast.void_type {
375 resolved_elem_type
376 } else if info.elem_type == ast.int_literal_type {
377 ast.int_type
378 } else if info.elem_type == ast.float_literal_type {
379 ast.f64_type
380 } else {
381 info.elem_type
382 }
383 if gen_or && node.typ.has_flag(.option) && !info.elem_type.has_flag(.option) {
384 elem_type = elem_type.clear_option_and_result()
385 }
386 elem_sym := g.table.final_sym(elem_type)
387 mut left_is_ptr := array_left_type.is_ptr() || node.left.is_auto_deref_var()
388 // Auto-heap idents are pointers in C. `g.expr` writes `(*(name))` to deref them,
389 // but suppresses that deref when the ident is on an assign LHS / inside a selector
390 // LHS / inside an assign-fn-var context. In those cases the C result of `g.expr`
391 // is a pointer, so this IndexExpr must emit its own `*` before `array_get`.
392 if !left_is_ptr && node.left is ast.Ident && g.resolved_ident_is_auto_heap(node.left)
393 && (g.is_assign_lhs || g.inside_selector_lhs || g.inside_assign_fn_var) {
394 left_is_ptr = true
395 }
396 result_type := match true {
397 gen_or && elem_type.has_flag(.option) {
398 node.typ.clear_flag(.option)
399 }
400 gen_or {
401 node.typ
402 }
403 else {
404 elem_type
405 }
406 }
407
408 result_sym := g.table.final_sym(result_type)
409 elem_type_str := if elem_sym.kind == .function { 'voidptr' } else { g.styp(elem_type) }
410 result_type_str := if result_sym.kind == .function { 'voidptr' } else { g.styp(result_type) }
411 left_is_shared := array_left_type.has_flag(.shared_f)
412 wide_index_kind := g.c_wide_index_kind(node.index_type)
413 array_get_fn := if node.is_gated {
414 'builtin__array_get_ni'
415 } else {
416 match wide_index_kind {
417 .signed_64 { 'builtin__array_get_i64' }
418 .unsigned_64 { 'builtin__array_get_u64' }
419 else { 'builtin__array_get' }
420 }
421 }
422 array_get_with_check_fn := if node.is_gated {
423 'builtin__array_get_with_check_ni'
424 } else {
425 match wide_index_kind {
426 .signed_64 { 'builtin__array_get_with_check_i64' }
427 .unsigned_64 { 'builtin__array_get_with_check_u64' }
428 else { 'builtin__array_get_with_check' }
429 }
430 }
431 array_set_fn := if node.is_gated {
432 'builtin__array_set_ni'
433 } else {
434 match wide_index_kind {
435 .signed_64 { 'builtin__array_set_i64' }
436 .unsigned_64 { 'builtin__array_set_u64' }
437 else { 'builtin__array_set' }
438 }
439 }
440 // `vals[i].field = x` is an exception and requires `array_get`:
441 // `(*(Val*)array_get(vals, i)).field = x;`
442 if g.is_assign_lhs && node.is_setter {
443 is_direct_array_access := !node.is_gated && wide_index_kind == .plain
444 && (g.is_direct_array_access || node.is_direct)
445 is_op_assign := g.assign_op != .assign && info.elem_type != ast.string_type
446 if is_direct_array_access {
447 g.write('((${elem_type_str}*)')
448 } else if is_op_assign {
449 g.write('(*(${elem_type_str}*)${array_get_fn}(')
450 if left_is_ptr && !left_is_shared {
451 g.write('*')
452 }
453 } else {
454 g.cur_indexexpr << node.pos.pos
455 g.is_arraymap_set = true // special handling of assign_op and closing with '})'
456 g.write('${array_set_fn}(')
457 if !left_is_ptr || left_is_shared {
458 g.write('&')
459 }
460 }
461 if node.left is ast.IndexExpr {
462 g.inside_array_index = true
463 g.expr(ast.Expr(node.left))
464 g.inside_array_index = false
465 } else {
466 g.expr(ast.Expr(node.left))
467 }
468
469 if left_is_shared {
470 if node.index !is ast.RangeExpr && left_is_ptr {
471 g.write('->val')
472 } else {
473 g.write('.val')
474 }
475 }
476 if is_direct_array_access {
477 if left_is_ptr && !left_is_shared {
478 g.write('->')
479 } else {
480 g.write('.')
481 }
482 g.write('data)[')
483 g.expr(node.index)
484 g.write(']')
485 } else {
486 g.write(', ')
487 g.expr(node.index)
488 if !is_op_assign {
489 mut need_wrapper := true
490 /*
491 match node.right {
492 ast.EnumVal, ast.Ident {
493 // `&x` is enough for variables and enums
494 // `&(Foo[]){ ... }` is only needed for function calls and literals
495 need_wrapper = false
496 }
497 else {}
498 }
499 */
500 if elem_sym.kind != .array_fixed {
501 if need_wrapper {
502 g.write(', &(${elem_type_str}[]) { ')
503 } else {
504 g.write(', &')
505 }
506 }
507 } else {
508 // `x[0] *= y`
509 g.write('))')
510 }
511 }
512 } else {
513 is_direct_array_access := !node.is_gated && wide_index_kind == .plain
514 && (g.is_direct_array_access || node.is_direct)
515 is_fn_index_call := g.is_fn_index_call && elem_sym.info is ast.FnType
516 // do not clone inside `opt_ok(opt_ok(&(string[]) {..})` before returns
517 needs_clone := info.elem_type == ast.string_type_idx && g.is_autofree && !(g.inside_return
518 && g.fn_decl != unsafe { nil } && g.fn_decl.return_type.has_flag(.option))
519 && !g.is_assign_lhs
520 is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result
521 cur_line := if is_gen_or_and_assign_rhs {
522 line := g.go_before_last_stmt()
523 g.out.write_string(util.tabs(g.indent))
524 line
525 } else {
526 ''
527 }
528 tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
529 tmp_opt_ptr := if gen_or { g.new_tmp_var() } else { '' }
530 if gen_or {
531 g.write('${elem_type_str}* ${tmp_opt_ptr} = (${elem_type_str}*)(${array_get_with_check_fn}(')
532 if left_is_ptr && !left_is_shared {
533 g.write('*')
534 }
535 } else {
536 if needs_clone {
537 g.write('builtin__string_clone(')
538 }
539 if is_fn_index_call {
540 if elem_sym.info is ast.FnType {
541 g.write('((')
542 g.write_fn_ptr_decl(&elem_sym.info, '')
543 if is_direct_array_access {
544 g.write(')((${elem_type_str}*)')
545 } else {
546 g.write(')(*(${elem_type_str}*)${array_get_fn}(')
547 }
548 }
549 if left_is_ptr && !left_is_shared && !is_direct_array_access {
550 g.write('*')
551 }
552 } else if is_direct_array_access {
553 g.write('((${elem_type_str}*)')
554 } else {
555 g.write('(*(${elem_type_str}*)${array_get_fn}(')
556 if left_is_ptr && !left_is_shared {
557 g.write('*')
558 }
559 }
560 }
561 g.expr(ast.Expr(node.left))
562 // TODO: test direct_array_access when 'shared' is implemented
563 if left_is_shared {
564 if left_is_ptr {
565 g.write('->val')
566 } else {
567 g.write('.val')
568 }
569 }
570 if is_direct_array_access && !gen_or {
571 if left_is_ptr && !left_is_shared {
572 g.write('->')
573 } else {
574 g.write('.')
575 }
576 g.write('data)[')
577 g.expr(node.index)
578 g.write(']')
579 if is_fn_index_call {
580 g.write(')')
581 }
582 } else {
583 g.write(', ')
584 g.expr(node.index)
585 if is_fn_index_call {
586 g.write(')))')
587 } else {
588 g.write('))')
589 }
590 }
591 if !gen_or && needs_clone {
592 g.write(')')
593 }
594 if gen_or {
595 g.writeln(';')
596 keep_option_result := g.index_or_expr_keeps_option(node)
597 opt_elem_type := g.styp(elem_type.set_flag(.option))
598 g.writeln('${opt_elem_type} ${tmp_opt} = {0};')
599 g.writeln('if (${tmp_opt_ptr}) {')
600 if elem_type.has_flag(.option) && !g.inside_opt_or_res {
601 g.writeln('\tif (${tmp_opt_ptr}->state == 0) {')
602 g.writeln('\t\t*((${result_type_str}*)&${tmp_opt}.data) = *((${result_type_str}*)${tmp_opt_ptr}->data);')
603 g.writeln('\t} else {')
604 g.writeln('\t\t${tmp_opt}.state = ${tmp_opt_ptr}->state;')
605 g.writeln('\t\t${tmp_opt}.err = ${tmp_opt_ptr}->err;')
606 g.writeln('\t}')
607 } else {
608 g.writeln('\t*((${elem_type_str}*)&${tmp_opt}.data) = *((${elem_type_str}*)${tmp_opt_ptr});')
609 }
610 g.writeln('} else {')
611 g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = builtin___v_error(_S("array index out of range"));')
612 g.writeln('}')
613 if !node.is_option {
614 if keep_option_result {
615 g.or_block_on_value(tmp_opt, node.or_expr, elem_type.set_flag(.option))
616 } else {
617 g.or_block(tmp_opt, node.or_expr, elem_type)
618 }
619 }
620 if is_gen_or_and_assign_rhs {
621 g.set_current_pos_as_last_stmt_pos()
622 }
623 if !g.is_amp {
624 if keep_option_result {
625 g.write('\n${cur_line}${tmp_opt}')
626 } else if g.inside_opt_or_res && elem_type.has_flag(.option) && g.inside_assign {
627 g.write('\n${cur_line}(*(${elem_type_str}*)&${tmp_opt})')
628 } else if elem_type.has_flag(.option) && !g.inside_opt_or_res {
629 g.write('\n${cur_line}(*(${result_type_str}*)${tmp_opt}.data)')
630 } else {
631 g.write('\n${cur_line}(*(${elem_type_str}*)${tmp_opt}.data)')
632 }
633 } else {
634 g.write('\n${cur_line}*${tmp_opt_ptr}')
635 }
636 }
637 }
638}
639
640fn (mut g Gen) index_of_fixed_array(node ast.IndexExpr, sym ast.TypeSymbol) {
641 info := sym.info as ast.ArrayFixed
642 elem_type := info.elem_type
643 elem_sym := g.table.sym(elem_type)
644 is_fn_index_call := g.is_fn_index_call && elem_sym.info is ast.FnType
645 wide_index_kind := g.c_wide_index_kind(node.index_type)
646
647 if node.left is ast.ArrayInit {
648 past := g.past_tmp_var_new()
649 styp := g.styp(node.left_type)
650 g.write('${styp} ${past.tmp_var} = ')
651 g.expr(ast.Expr(node.left))
652 g.writeln(';')
653 g.past_tmp_var_done(past)
654 } else if node.left is ast.IndexExpr && node.left.is_setter {
655 line := g.go_before_last_stmt().trim_space()
656 g.empty_line = true
657 tmp_var := g.new_tmp_var()
658 styp := g.styp(node.left_type)
659 g.write('${styp}* ${tmp_var} = &')
660 g.expr(ast.Expr(node.left))
661 g.writeln(';')
662 g.write(line)
663 g.write('(*')
664 g.write(tmp_var)
665 g.write(')')
666 } else {
667 if is_fn_index_call {
668 g.write('(*')
669 }
670 if node.left_type.is_ptr() || node.left.is_auto_deref_var() {
671 g.write('(*')
672 g.expr(ast.Expr(node.left))
673 g.write(')')
674 } else {
675 g.expr(ast.Expr(node.left))
676 }
677 if node.left_type.has_flag(.shared_f) {
678 g.write('.val')
679 }
680 }
681 g.write('[')
682 if node.is_gated {
683 g.write('builtin__v_fixed_index_ni(')
684 g.expr(node.index)
685 g.write(', ${info.size})')
686 } else if wide_index_kind == .signed_64 {
687 g.write('builtin__v_fixed_index_i64(')
688 g.expr(node.index)
689 g.write(', ${info.size})')
690 } else if wide_index_kind == .unsigned_64 {
691 g.write('builtin__v_fixed_index_u64(')
692 g.expr(node.index)
693 g.write(', ${info.size})')
694 } else if g.is_direct_array_access || g.pref.translated || node.index is ast.IntegerLiteral {
695 g.expr(node.index)
696 } else {
697 // bounds check
698 g.write('builtin__v_fixed_index(')
699 g.expr(node.index)
700 g.write(', ${info.size})')
701 }
702 g.write(']')
703 if is_fn_index_call {
704 g.write(')')
705 }
706}
707
708fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
709 gen_or := node.or_expr.kind != .absent || node.is_option
710 map_left_type := g.resolved_map_type_from_expr(node.left, node.left_type)
711 mut left_is_ptr := map_left_type.is_ptr()
712 if !left_is_ptr && g.is_assign_lhs && node.left is ast.Ident
713 && g.resolved_ident_is_auto_heap(node.left) {
714 left_is_ptr = true
715 }
716 left_sym := if map_left_type != 0 {
717 *g.table.final_sym(g.unwrap_generic(map_left_type))
718 } else {
719 sym
720 }
721 info := if left_sym.kind == .map {
722 left_sym.info as ast.Map
723 } else {
724 sym.info as ast.Map
725 }
726 key_type_, val_type_ := g.resolved_map_key_value_types(map_left_type, info.key_type,
727 info.value_type)
728 mut key_type := key_type_
729 mut val_type := val_type_
730 if node.left is ast.Ident {
731 ident_key_type := g.resolved_ident_map_key_type(node.left)
732 if ident_key_type != 0 {
733 key_type = ident_key_type
734 }
735 ident_val_type := g.resolved_ident_map_value_type(node.left)
736 if ident_val_type != 0 {
737 val_type = ident_val_type
738 }
739 }
740 if key_type == ast.usize_type || val_type == ast.usize_type {
741 name_key_type, name_val_type := g.resolved_map_types_from_name(left_sym.name)
742 if key_type == ast.usize_type && name_key_type != 0 {
743 key_type = name_key_type
744 }
745 if val_type == ast.usize_type && name_val_type != 0 {
746 val_type = name_val_type
747 }
748 }
749 if val_type == ast.usize_type && node.typ != 0 {
750 candidate_val_type := g.unwrap_generic(g.recheck_concrete_type(node.typ))
751 if candidate_val_type != 0 && candidate_val_type != ast.void_type
752 && candidate_val_type !in [ast.int_literal_type, ast.float_literal_type] {
753 val_type = candidate_val_type
754 }
755 }
756 val_sym := g.table.final_sym(val_type)
757 left_is_shared := map_left_type.has_flag(.shared_f)
758 val_type_str := if val_sym.kind == .function {
759 'voidptr'
760 } else {
761 if g.inside_return {
762 g.styp(val_type)
763 } else {
764 g.styp(val_type.clear_flag(.result))
765 }
766 }
767 get_and_set_types := val_sym.kind in [.struct, .map, .array, .array_fixed]
768 use_get_and_set := g.inside_left_shift || (node.is_setter && !g.inside_map_infix)
769 if g.is_assign_lhs && !g.is_arraymap_set && !get_and_set_types {
770 if g.assign_op == .assign || val_type == ast.string_type {
771 g.cur_indexexpr << node.pos.pos
772 g.is_arraymap_set = true
773 g.write('builtin__map_set(')
774 } else {
775 if use_get_and_set {
776 g.write('(*((${val_type_str}*)builtin__map_get_and_set((map*)')
777 } else {
778 g.write('(*((${val_type_str}*)builtin__map_get((map*)')
779 }
780 }
781 if !left_is_ptr || left_is_shared {
782 g.write('&')
783 }
784 if node.left is ast.IndexExpr {
785 g.inside_map_index = true
786 g.expr(ast.Expr(node.left))
787 g.inside_map_index = false
788 } else {
789 g.expr(node.left)
790 }
791 if left_is_shared {
792 g.write('->val')
793 }
794 g.write(', ')
795 old_is_arraymap_set := g.is_arraymap_set
796 old_is_assign_lhs := g.is_assign_lhs
797 g.is_arraymap_set = false
798 g.is_assign_lhs = false
799 g.write_map_key_arg(node.index, key_type)
800 g.is_arraymap_set = old_is_arraymap_set
801 g.is_assign_lhs = old_is_assign_lhs
802 g.arraymap_set_pos = g.out.len
803 g.write(', &(${val_type_str}[]) { ')
804 if g.assign_op != .assign && val_type != ast.string_type {
805 zero := g.type_default(val_type)
806 g.write('${zero} })))')
807 }
808 } else if !gen_or && (g.inside_map_postfix || g.inside_map_infix
809 || g.inside_map_index || g.inside_array_index || g.inside_left_shift
810 || (g.is_assign_lhs && !g.is_arraymap_set && get_and_set_types)) {
811 zero := g.type_default(val_type)
812 if use_get_and_set {
813 g.write('(*(${val_type_str}*)builtin__map_get_and_set((map*)')
814 } else {
815 g.write('(*(${val_type_str}*)builtin__map_get((map*)')
816 }
817 if !left_is_ptr || left_is_shared {
818 g.write('&')
819 }
820 g.expr(node.left)
821 if left_is_shared {
822 g.write('->val')
823 }
824 g.write(', ')
825 old_is_assign_lhs := g.is_assign_lhs
826 g.is_assign_lhs = false
827 g.write_map_key_arg(node.index, key_type)
828 g.is_assign_lhs = old_is_assign_lhs
829 g.write(', &(${val_type_str}[]){ ${zero} }))')
830 } else {
831 zero := g.type_default(val_type)
832 is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result
833 cur_line := if is_gen_or_and_assign_rhs {
834 line := g.go_before_last_stmt()
835 g.out.write_string(util.tabs(g.indent))
836 line
837 } else {
838 ''
839 }
840 tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
841 tmp_opt_ptr := if gen_or { g.new_tmp_var() } else { '' }
842 mut is_fn_last_index_call := false
843 if gen_or {
844 g.write('${val_type_str}* ${tmp_opt_ptr} = (${val_type_str}*)(builtin__map_get_check(')
845 } else {
846 if g.is_fn_index_call {
847 if val_sym.info is ast.FnType {
848 g.write('((')
849 g.write_fn_ptr_decl(&val_sym.info, '')
850 g.write(')(*(voidptr*)builtin__map_get(')
851 is_fn_last_index_call = true
852 g.is_fn_index_call = false
853 }
854 } else {
855 g.write('(*(${val_type_str}*)builtin__map_get(')
856 }
857 }
858 if !left_is_ptr || left_is_shared {
859 g.write('ADDR(map, ')
860 g.expr(node.left)
861 } else {
862 g.write('(')
863 g.expr(node.left)
864 }
865 if left_is_shared {
866 if left_is_ptr {
867 g.write('->val')
868 } else {
869 g.write('.val')
870 }
871 }
872 g.write('), ')
873 g.write_map_key_arg(node.index, key_type)
874 if gen_or {
875 g.write('))')
876 } else if is_fn_last_index_call {
877 g.write(', &(voidptr[]){ ${zero} })))')
878 } else {
879 g.write(', &(${val_type_str}[]){ ${zero} }))')
880 }
881 if gen_or {
882 g.writeln(';')
883 // The surrounding expression may already carry a pointer prefix placeholder.
884 direct_ptr_cur_line := if cur_line.ends_with('&') || cur_line.ends_with('H') {
885 cur_line[..cur_line.len - 1]
886 } else {
887 cur_line
888 }
889 if g.unsafe_level > 0 && !node.is_option && !node.is_setter
890 && node.or_expr.kind == .block && node.or_expr.stmts.len == 1 {
891 last_stmt := node.or_expr.stmts[0]
892 if last_stmt is ast.ExprStmt && last_stmt.typ.is_any_kind_of_pointer() {
893 // handle the case of `p := unsafe{ &m[key] or { nil } }` directly, without an intermediate option + copies etc:
894 g.write('if (!${tmp_opt_ptr}) { ${tmp_opt_ptr} = ')
895 g.expr(last_stmt.expr)
896 g.write('; }')
897 if cur_line.ends_with('&') || cur_line.ends_with('H') {
898 g.write('\n${direct_ptr_cur_line}${tmp_opt_ptr}')
899 } else {
900 g.write('\n${cur_line}(*${tmp_opt_ptr})')
901 }
902 return
903 }
904 }
905 if val_type.has_flag(.option) {
906 or_value_type := g.resolved_or_block_value_type(node.or_expr)
907 keep_option_result := node.is_option || (node.or_expr.kind == .block
908 && (or_value_type in [ast.none_type, ast.none_type_idx]
909 || or_value_type.has_flag(.option)))
910 plain_val_type := val_type.clear_option_and_result()
911 plain_val_sym := g.table.final_sym(plain_val_type)
912 plain_val_type_str := if plain_val_sym.kind == .function {
913 'voidptr'
914 } else {
915 g.styp(plain_val_type)
916 }
917 g.writeln('${val_type_str} ${tmp_opt} = {0};')
918 g.writeln('if (${tmp_opt_ptr}) {')
919 g.writeln('\t${tmp_opt} = *${tmp_opt_ptr};')
920 g.writeln('} else {')
921 g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = builtin___v_error(_S("map key does not exist"));')
922 g.writeln('}')
923 if !node.is_option {
924 g.or_block_on_value(tmp_opt, node.or_expr, val_type)
925 }
926 if is_gen_or_and_assign_rhs {
927 g.set_current_pos_as_last_stmt_pos()
928 }
929 if keep_option_result {
930 g.write('\n${cur_line}${tmp_opt}')
931 } else {
932 g.write('\n${cur_line}(*(${plain_val_type_str}*)${tmp_opt}.data)')
933 }
934 } else {
935 keep_option_result := g.index_or_expr_keeps_option(node)
936 opt_val_type := g.styp(val_type.set_flag(.option))
937 g.writeln('${opt_val_type} ${tmp_opt} = {0};')
938 g.writeln('if (${tmp_opt_ptr}) {')
939 if val_sym.kind == .array_fixed {
940 g.writeln('\tmemcpy((${val_type_str}*)${tmp_opt}.data, (${val_type_str}*)${tmp_opt_ptr}, sizeof(${val_type_str}));')
941 } else {
942 g.writeln('\t*((${val_type_str}*)&${tmp_opt}.data) = *((${val_type_str}*)${tmp_opt_ptr});')
943 }
944 g.writeln('} else {')
945 g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = builtin___v_error(_S("map key does not exist"));')
946 g.writeln('}')
947 if !node.is_option {
948 if keep_option_result {
949 g.or_block_on_value(tmp_opt, node.or_expr, val_type.set_flag(.option))
950 } else {
951 g.or_block(tmp_opt, node.or_expr, val_type)
952 }
953 }
954 if is_gen_or_and_assign_rhs {
955 g.set_current_pos_as_last_stmt_pos()
956 }
957 if keep_option_result {
958 g.write('\n${cur_line}${tmp_opt}')
959 } else {
960 g.write('\n${cur_line}(*(${val_type_str}*)${tmp_opt}.data)')
961 }
962 }
963 }
964 }
965}
966