Skip to content

Commit 67d30eb

Browse files
authored
cgen: fix p := unsafe{ &m[key] or { nil } } (fix #25387) (#25408)
1 parent ba79e85 commit 67d30eb

File tree

3 files changed

+63
-0
lines changed

3 files changed

+63
-0
lines changed

vlib/v/gen/c/cgen.v

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ mut:
168168
last_tmp_call_var []string
169169
last_if_option_type ast.Type // stores the expected if type on nested if expr
170170
loop_depth int
171+
unsafe_level int
171172
ternary_names map[string]string
172173
ternary_level_names map[string][]string
173174
arraymap_set_pos int // map or array set value position
@@ -2523,6 +2524,10 @@ fn (mut g Gen) stmt(node ast.Stmt) {
25232524
}
25242525
ast.Block {
25252526
g.write_v_source_line_info_stmt(node)
2527+
if node.is_unsafe {
2528+
g.unsafe_level++
2529+
}
2530+
25262531
if !node.is_unsafe {
25272532
g.writeln('{')
25282533
} else {
@@ -2534,6 +2539,9 @@ fn (mut g Gen) stmt(node ast.Stmt) {
25342539
}
25352540
g.stmts(node.stmts)
25362541
g.writeln('}')
2542+
if node.is_unsafe {
2543+
g.unsafe_level--
2544+
}
25372545
}
25382546
ast.AssignStmt {
25392547
g.write_v_source_line_info_stmt(node)
@@ -4084,7 +4092,9 @@ fn (mut g Gen) expr(node_ ast.Expr) {
40844092
g.typeof_expr(node)
40854093
}
40864094
ast.UnsafeExpr {
4095+
g.unsafe_level++
40874096
g.expr(node.expr)
4097+
g.unsafe_level--
40884098
}
40894099
}
40904100
g.discard_or_result = old_discard_or_result

vlib/v/gen/c/index.v

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,18 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
543543
}
544544
if gen_or {
545545
g.writeln(';')
546+
if g.unsafe_level > 0 && !node.is_option && !node.is_setter
547+
&& node.or_expr.kind == .block && node.or_expr.stmts.len == 1 {
548+
last_stmt := node.or_expr.stmts[0]
549+
if last_stmt is ast.ExprStmt && last_stmt.typ.is_any_kind_of_pointer() {
550+
// handle the case of `p := unsafe{ &m[key] or { nil } }` directly, without an intermediate option + copies etc:
551+
g.write('if (!${tmp_opt_ptr}) { ${tmp_opt_ptr} = ')
552+
g.expr(last_stmt.expr)
553+
g.write('; }')
554+
g.write('\n${cur_line}(*${tmp_opt_ptr})')
555+
return
556+
}
557+
}
546558
opt_val_type := g.styp(val_type.set_flag(.option))
547559
g.writeln('${opt_val_type} ${tmp_opt} = {0};')
548560
g.writeln('if (${tmp_opt_ptr}) {')
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
struct MyObject {
2+
mut:
3+
x int
4+
s string
5+
}
6+
7+
struct Abc {
8+
mut:
9+
mymap map[string]MyObject
10+
}
11+
12+
fn test_codegen_for_unsafe_pointers_to_map_values() {
13+
mut inst := Abc{}
14+
inst.mymap['abc'] = MyObject{123, 'abc'}
15+
inst.mymap['def'] = MyObject{456, 'def'}
16+
dump(inst)
17+
18+
mut p1 := unsafe { &inst.mymap['abc'] or { nil } }
19+
p2 := unsafe { &inst.mymap['def'] or { nil } }
20+
p3 := unsafe { &inst.mymap['zzz_unknown'] or { nil } }
21+
assert typeof(p1).name == '&MyObject'
22+
assert typeof(p2).name == '&MyObject'
23+
assert typeof(p3).name == '&MyObject'
24+
dump(p1)
25+
dump(p2)
26+
dump(p3)
27+
assert p1 != unsafe { nil }
28+
assert p2 != unsafe { nil }
29+
assert p3 == unsafe { nil }
30+
assert p1.x == 123
31+
assert p1.s == 'abc'
32+
assert p2.x == 456
33+
assert p2.s == 'def'
34+
// check that `p1` points to the exact same instance that is stored in the map:
35+
assert inst.mymap['abc'].x == 123
36+
assert inst.mymap['abc'].s == 'abc'
37+
p1.x = 999
38+
p1.s = 'xyz'
39+
assert inst.mymap['abc'].x == 999
40+
assert inst.mymap['abc'].s == 'xyz'
41+
}

0 commit comments

Comments
 (0)