Skip to content

Commit 88c5db7

Browse files
authored
cgen: fix five invalid C edge cases (#27942)
1 parent c669ee1 commit 88c5db7

10 files changed

Lines changed: 147 additions & 15 deletions

vlib/v/builder/c_error_reproducer.v

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,10 +1139,10 @@ fn repro_uses_local_resource(source string) bool {
11391139
// V tokenizes the call, so whitespace between the name and its `(` is valid
11401140
// (`$embed_file ('asset.bin')`) and an exact-substring check would miss it.
11411141
fn repro_has_comptime_call(source string, name string) bool {
1142-
pat := '\$' + name
1142+
pat := '$' + name
11431143
mut i := 0
11441144
for i + pat.len <= source.len {
1145-
if source[i] != `\$` || source[i..i + pat.len] != pat {
1145+
if source[i] != `$` || source[i..i + pat.len] != pat {
11461146
i++
11471147
continue
11481148
}

vlib/v/checker/checker.v

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5490,6 +5490,15 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
54905490
return node.typ
54915491
}
54925492

5493+
unaliased_from_type := c.table.fully_unaliased_type(from_type)
5494+
unaliased_to_type := c.table.fully_unaliased_type(to_type)
5495+
if unaliased_from_type.is_any_kind_of_pointer() && !unaliased_to_type.is_any_kind_of_pointer()
5496+
&& final_to_sym.kind in [.array, .array_fixed] {
5497+
ft := c.table.type_to_str(from_type)
5498+
tt := c.table.type_to_str(to_type)
5499+
c.error('cannot cast pointer type `${ft}` to array type `${tt}`', node.pos)
5500+
}
5501+
54935502
final_to_is_ptr := to_type.is_ptr() || final_to_type.is_ptr()
54945503
c.markused_castexpr(mut node, to_type, mut final_to_sym)
54955504
if to_type.has_flag(.result) {

vlib/v/checker/for.v

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,9 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
284284
} else if sym.kind == .aggregate&& (sym.info as ast.Aggregate).types.all(c.table.type_kind(it) in [.array, .array_fixed, .string, .map]) {
285285
value_type = c.table.value_type((sym.info as ast.Aggregate).types[0])
286286
}
287-
if value_type == ast.void_type || typ.has_flag(.result) {
287+
cannot_index_option_map_expr := !is_comptime && typ.has_flag(.option)
288+
&& sym.kind == .map && node.cond !is ast.Ident
289+
if value_type == ast.void_type || typ.has_flag(.result) || cannot_index_option_map_expr {
288290
if typ != ast.void_type {
289291
c.error('for in: cannot index `${c.table.type_to_str(typ)}`', node.cond.pos())
290292
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
vlib/v/checker/tests/cast_pointer_to_array_err.vv:5:8: error: cannot cast pointer type `&int` to array type `[]int`
2+
3 | values := [1, 2, 3]!
3+
4 | p := &values[0]
4+
5 | _ := []int(p)
5+
| ^
6+
6 | _ := [3]int(p)
7+
7 | }
8+
vlib/v/checker/tests/cast_pointer_to_array_err.vv:6:8: error: cannot cast pointer type `&int` to array type `[3]int`
9+
4 | p := &values[0]
10+
5 | _ := []int(p)
11+
6 | _ := [3]int(p)
12+
| ^
13+
7 | }
14+
8 | }
15+
vlib/v/checker/tests/cast_pointer_to_array_err.vv:13:8: error: cannot cast pointer type `&u8` to array type `[]int`
16+
11 | unsafe {
17+
12 | byte_value := u8(1)
18+
13 | _ := []int(&byte_value)
19+
| ^
20+
14 | value := 1
21+
15 | p := &value
22+
vlib/v/checker/tests/cast_pointer_to_array_err.vv:17:8: error: cannot cast pointer type `&&int` to array type `[]int`
23+
15 | p := &value
24+
16 | pp := &p
25+
17 | _ := []int(pp)
26+
| ^
27+
18 | raw := voidptr(p)
28+
19 | _ := []int(raw)
29+
vlib/v/checker/tests/cast_pointer_to_array_err.vv:19:8: error: cannot cast pointer type `voidptr` to array type `[]int`
30+
17 | _ := []int(pp)
31+
18 | raw := voidptr(p)
32+
19 | _ := []int(raw)
33+
| ^
34+
20 | }
35+
21 | }
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
fn main() {
2+
unsafe {
3+
values := [1, 2, 3]!
4+
p := &values[0]
5+
_ := []int(p)
6+
_ := [3]int(p)
7+
}
8+
}
9+
10+
fn reject_other_pointer_sources() {
11+
unsafe {
12+
byte_value := u8(1)
13+
_ := []int(&byte_value)
14+
value := 1
15+
p := &value
16+
pp := &p
17+
_ := []int(pp)
18+
raw := voidptr(p)
19+
_ := []int(raw)
20+
}
21+
}
22+
23+
fn preserve_pointer_to_array_reinterpret_casts() {
24+
unsafe {
25+
dynamic := [1, 2, 3]
26+
_ := *&[]int(&dynamic)
27+
fixed := [1, 2, 3]!
28+
_ := *&[3]int(&fixed)
29+
}
30+
}

vlib/v/checker/tests/for_in_index_option.out

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,10 @@ vlib/v/checker/tests/for_in_index_option.vv:4:17: error: for in: cannot index `!
55
| ~~~~~~~
66
5 | println(file)
77
6 | }
8+
vlib/v/checker/tests/for_in_index_option.vv:7:18: error: for in: cannot index `?map[string]int`
9+
5 | println(file)
10+
6 | }
11+
7 | for _, value in maybe_values(true) {
12+
| ~~~~~~~~~~~~~~~~~~
13+
8 | println(value)
14+
9 | }

vlib/v/checker/tests/for_in_index_option.vv

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,16 @@ fn main() {
44
for file in os.ls('.') {
55
println(file)
66
}
7+
for _, value in maybe_values(true) {
8+
println(value)
9+
}
10+
}
11+
12+
fn maybe_values(ok bool) ?map[string]int {
13+
if ok {
14+
return {
15+
'one': 1
16+
}
17+
}
18+
return none
719
}

vlib/v/gen/c/cgen.v

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4099,12 +4099,18 @@ fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) bool {
40994099
inside_assign_context := g.inside_struct_init
41004100
|| g.inside_assign
41014101
|| (!g.inside_return && g.inside_match_option)
4102-
ret_expr_typ := if inside_assign_context {
4102+
expected_if_option_type := g.unwrap_generic(g.last_if_option_type)
4103+
has_expected_if_option_type := expected_if_option_type.has_flag(.option)
4104+
ret_expr_typ := if has_expected_if_option_type {
4105+
expected_if_option_type
4106+
} else if inside_assign_context {
41034107
stmt.typ
41044108
} else {
41054109
g.fn_decl.return_type
41064110
}
4107-
ret_typ := if inside_assign_context {
4111+
ret_typ := if has_expected_if_option_type {
4112+
expected_if_option_type.clear_flag(.option)
4113+
} else if inside_assign_context {
41084114
stmt.typ
41094115
} else {
41104116
g.fn_decl.return_type.clear_flag(.option)
@@ -8330,6 +8336,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
83308336
}
83318337
// struct embedding
83328338
mut has_embed := false
8339+
mut last_embed_is_ptr := false
83338340
if sym.info in [ast.Alias, ast.Struct, ast.Aggregate] {
83348341
if selector_embed_types.len > 0 && sym.info is ast.Aggregate {
83358342
// For aggregate types, check per-variant whether the field is
@@ -8339,18 +8346,18 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
83398346
agg_sym := g.table.sym(sym.info.types[g.aggregate_type_idx])
83408347
if !g.table.struct_has_field(agg_sym, field_name) {
83418348
has_embed = node.from_embed_types.len > 0
8342-
g.write_selector_expr_embed_name(node, node.from_embed_types)
8349+
last_embed_is_ptr = g.write_selector_expr_embed_name(node, node.from_embed_types)
83438350
}
83448351
} else if selector_embed_types.len > 0 {
83458352
has_embed = true
8346-
g.write_selector_expr_embed_name(node, selector_embed_types)
8353+
last_embed_is_ptr = g.write_selector_expr_embed_name(node, selector_embed_types)
83478354
} else if node.generic_from_embed_types.len > 0 && sym.info is ast.Struct {
83488355
if sym.info.embeds.len > 0 {
83498356
mut is_find := false
83508357
for arr_val in node.generic_from_embed_types {
83518358
if arr_val.len > 0 {
83528359
if arr_val[0] == sym.info.embeds[0] {
8353-
g.write_selector_expr_embed_name(node, arr_val)
8360+
last_embed_is_ptr = g.write_selector_expr_embed_name(node, arr_val)
83548361
is_find = true
83558362
has_embed = true
83568363
break
@@ -8359,21 +8366,22 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
83598366
}
83608367
if !is_find {
83618368
has_embed = node.from_embed_types.len > 0
8362-
g.write_selector_expr_embed_name(node, node.from_embed_types)
8369+
last_embed_is_ptr = g.write_selector_expr_embed_name(node,
8370+
node.from_embed_types)
83638371
}
83648372
} else {
83658373
has_embed = node.from_embed_types.len > 0
8366-
g.write_selector_expr_embed_name(node, node.from_embed_types)
8374+
last_embed_is_ptr = g.write_selector_expr_embed_name(node, node.from_embed_types)
83678375
}
83688376
} else if sym.info is ast.Aggregate {
83698377
agg_sym := g.table.sym(sym.info.types[g.aggregate_type_idx])
83708378
if !g.table.struct_has_field(agg_sym, field_name) {
83718379
has_embed = node.from_embed_types.len > 0
8372-
g.write_selector_expr_embed_name(node, node.from_embed_types)
8380+
last_embed_is_ptr = g.write_selector_expr_embed_name(node, node.from_embed_types)
83738381
}
83748382
} else {
83758383
has_embed = node.from_embed_types.len > 0
8376-
g.write_selector_expr_embed_name(node, node.from_embed_types)
8384+
last_embed_is_ptr = g.write_selector_expr_embed_name(node, node.from_embed_types)
83778385
}
83788386
}
83798387
alias_to_ptr := sym.info is ast.Alias && sym.info.parent_type.is_ptr()
@@ -8440,7 +8448,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
84408448
|| (!opt_ptr_already_deref && unwrapped_expr_type.is_ptr()
84418449
&& !is_interface_smartcast_lhs && !smartcast_ident_already_dereferenced)
84428450
}
8443-
if !has_embed && left_is_ptr {
8451+
if (!has_embed && left_is_ptr) || (has_embed && last_embed_is_ptr) {
84448452
g.write('->')
84458453
} else {
84468454
g.write('.')
@@ -8645,7 +8653,7 @@ fn (mut g Gen) gen_closure_fn(expr_styp string, m ast.Fn, name string) {
86458653
g.nr_closures++
86468654
}
86478655

8648-
fn (mut g Gen) write_selector_expr_embed_name(node ast.SelectorExpr, embed_types []ast.Type) {
8656+
fn (mut g Gen) write_selector_expr_embed_name(node ast.SelectorExpr, embed_types []ast.Type) bool {
86498657
mut is_shared := g.type_resolves_to_shared(node.expr_type)
86508658
mut lhs_expr_type := node.expr_type
86518659
if node.expr is ast.Ident && node.expr.obj is ast.Var && (node.expr.obj.typ.has_flag(.generic)
@@ -8676,7 +8684,7 @@ fn (mut g Gen) write_selector_expr_embed_name(node ast.SelectorExpr, embed_types
86768684
is_left_ptr := if i == 0 {
86778685
(resolved_selector_expr_type.is_ptr() || is_auto_heap) && !is_shared
86788686
} else {
8679-
embed_types[i - 1].is_ptr()
8687+
g.table.fully_unaliased_type(embed_types[i - 1]).is_ptr()
86808688
}
86818689
if i == 0 && is_shared {
86828690
g.write('->val')
@@ -8688,6 +8696,7 @@ fn (mut g Gen) write_selector_expr_embed_name(node ast.SelectorExpr, embed_types
86888696
}
86898697
g.write(embed_name)
86908698
}
8699+
return embed_types.len > 0 && g.table.fully_unaliased_type(embed_types.last()).is_ptr()
86918700
}
86928701

86938702
// check_var_scope checks if the variable has its value known from the node position
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
struct Issue27901Inner {
2+
value int = 3
3+
}
4+
5+
type Issue27901InnerPtr = &Issue27901Inner
6+
7+
struct Issue27901Outer {
8+
Issue27901InnerPtr
9+
}
10+
11+
fn test_embedded_pointer_alias_field_access() {
12+
a := Issue27901Outer{
13+
value: 7
14+
}
15+
assert a.value == 7
16+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
fn issue27903_value(x ?string) string {
2+
return x or { 'NONE' }
3+
}
4+
5+
fn issue27903_condition(value bool) bool {
6+
return value
7+
}
8+
9+
fn test_if_expression_passed_as_option_argument() {
10+
assert issue27903_value(if issue27903_condition(true) { 'a' } else { none }) == 'a'
11+
assert issue27903_value(if issue27903_condition(false) { 'a' } else { none }) == 'NONE'
12+
}

0 commit comments

Comments
 (0)