Skip to content

Commit 87340bc

Browse files
authored
checker,cgen: fix alias for interface and option types (fix #26551) (#26565)
1 parent 715e8e6 commit 87340bc

9 files changed

Lines changed: 110 additions & 13 deletions

File tree

vlib/v/checker/checker.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1297,7 +1297,7 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to
12971297
utyp := c.unwrap_generic(typ)
12981298
styp := c.table.type_to_str(utyp)
12991299
typ_sym := c.table.sym(utyp)
1300-
mut inter_sym := c.table.sym(interface_type)
1300+
mut inter_sym := c.table.final_sym(interface_type)
13011301
if !inter_sym.is_pub && inter_sym.mod !in [typ_sym.mod, c.mod] && typ_sym.mod != 'builtin' {
13021302
c.error('`${styp}` cannot implement private interface `${inter_sym.name}` of other module',
13031303
pos)

vlib/v/checker/infix.v

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -879,8 +879,11 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
879879
node.promoted_type = return_type
880880
return return_type
881881
}
882-
left_is_option := left_type.has_flag(.option)
882+
left_is_option := left_type.has_flag(.option) || (left_sym.kind == .alias
883+
&& (left_sym.info as ast.Alias).parent_type.has_flag(.option))
883884
right_is_option := right_type.has_flag(.option)
885+
|| (right_sym.kind == .alias
886+
&& (right_sym.info as ast.Alias).parent_type.has_flag(.option))
884887
if (node.right is ast.None && left_is_option)
885888
|| (node.left is ast.None && right_is_option) {
886889
return ast.bool_type
@@ -1093,7 +1096,9 @@ fn (mut c Checker) check_option_infix_expr(node ast.InfixExpr, left_type ast.Typ
10931096
return
10941097
}
10951098
left_is_option := left_type.has_flag(.option)
1099+
|| (left_sym.kind == .alias && (left_sym.info as ast.Alias).parent_type.has_flag(.option))
10961100
right_is_option := right_type.has_flag(.option)
1101+
|| (right_sym.kind == .alias && (right_sym.info as ast.Alias).parent_type.has_flag(.option))
10971102
if (node.left is ast.None && right_is_option)
10981103
|| (node.right is ast.None && left_is_option)
10991104
|| (left_sym.kind == .none || right_sym.kind == .none) {

vlib/v/gen/c/assign.v

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,10 @@ fn (mut g Gen) expr_opt_with_cast(expr ast.Expr, expr_typ ast.Type, ret_typ ast.
127127
g.past_tmp_var_done(past)
128128
}
129129
unwrapped_ret := g.unwrap_generic(ret_typ)
130-
styp := g.base_type(unwrapped_ret)
130+
// Unwrap type aliases to ensure sizeof uses the base type size, not the alias size
131+
// This fixes the ASAN stack-buffer-overflow issue when using type aliases like MaybeInt = ?int
132+
unaliased_ret := g.table.unaliased_type(unwrapped_ret)
133+
styp := g.base_type(unaliased_ret)
131134
decl_styp := g.styp(unwrapped_ret).replace('*', '_ptr')
132135
g.writeln('${decl_styp} ${past.tmp_var};')
133136
is_none := expr is ast.CastExpr && expr.expr is ast.None

vlib/v/gen/c/auto_eq_methods.v

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,9 @@ fn (mut g Gen) gen_map_equality_fn(left_type ast.Type) string {
590590
}
591591
else {
592592
if value.typ.has_flag(.option) {
593-
fn_builder.writeln('\t\tif (memcmp(v.data, ((${ptr_value_styp}*)builtin__map_get(${b}, k, &(${ptr_value_styp}[]){ 0 }))->data, sizeof(${g.base_type(value.typ)})) != 0) {')
593+
// For option types, use the unaliased type to get the correct sizeof
594+
unaliased_typ := g.table.unaliased_type(value.typ)
595+
fn_builder.writeln('\t\tif (memcmp(v.data, ((${ptr_value_styp}*)builtin__map_get(${b}, k, &(${ptr_value_styp}[]){ 0 }))->data, sizeof(${g.base_type(unaliased_typ)})) != 0) {')
594596
} else {
595597
fn_builder.writeln('\t\tif (*(${ptr_value_styp}*)builtin__map_get(${b}, k, &(${ptr_value_styp}[]){ 0 }) != v) {')
596598
}

vlib/v/gen/c/auto_str_methods.v

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,26 @@ fn (mut g Gen) final_gen_str(typ StrType) {
119119
}
120120
if typ.typ.has_flag(.option) {
121121
opt_typ := if typ.typ.has_flag(.option_mut_param_t) { styp.replace('*', '') } else { styp }
122-
g.gen_str_for_option(typ.typ, opt_typ, str_fn_name)
122+
// Check if this is a type alias to an option type
123+
mut type_name := 'Option'
124+
mut unwrapped_typ := g.table.unaliased_type(typ.typ)
125+
if unwrapped_typ != typ.typ {
126+
// This is a type alias, check if it's an alias to an option type
127+
alias_sym := g.table.sym(typ.typ)
128+
if alias_sym.kind == .alias && alias_sym.info is ast.Alias {
129+
// Check if the parent type has the option flag
130+
if alias_sym.info.parent_type.has_flag(.option) {
131+
// This is an alias to an option type (e.g., type MyOpt = ?MyStruct)
132+
// Use the alias name instead of "Option"
133+
mut alias_name := alias_sym.name
134+
if alias_name.contains('.') {
135+
alias_name = alias_name.all_after_last('.')
136+
}
137+
type_name = '?${alias_name}'
138+
}
139+
}
140+
}
141+
g.gen_str_for_option(typ.typ, opt_typ, str_fn_name, type_name)
123142
return
124143
}
125144
if typ.typ.has_flag(.result) {
@@ -177,7 +196,7 @@ fn (mut g Gen) final_gen_str(typ StrType) {
177196
}
178197
}
179198

180-
fn (mut g Gen) gen_str_for_option(typ ast.Type, styp string, str_fn_name string) {
199+
fn (mut g Gen) gen_str_for_option(typ ast.Type, styp string, str_fn_name string, type_name string) {
181200
$if trace_autostr ? {
182201
eprintln('> gen_str_for_option: ${typ.debug()} | ${styp} | ${str_fn_name}')
183202
}
@@ -224,9 +243,9 @@ fn (mut g Gen) gen_str_for_option(typ ast.Type, styp string, str_fn_name string)
224243
g.auto_str_funcs.writeln('\t\tres = ${parent_str_fn_name}(${deref}it.data);')
225244
}
226245
}
227-
g.auto_str_funcs.writeln('\t\treturn ${str_intp_sub('Option(%%)', 'res')};')
246+
g.auto_str_funcs.writeln('\t\treturn ${str_intp_sub('${type_name}(%%)', 'res')};')
228247
g.auto_str_funcs.writeln('\t}')
229-
g.auto_str_funcs.writeln('\treturn _S("Option(none)");')
248+
g.auto_str_funcs.writeln('\treturn _S("${type_name}(none)");')
230249
g.auto_str_funcs.writeln('}')
231250
}
232251

vlib/v/gen/c/cgen.v

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,6 +1380,11 @@ fn (mut g Gen) option_type_name(t ast.Type) (string, string) {
13801380
mut base := g.base_type(t)
13811381
mut styp := ''
13821382
sym := g.table.sym(t)
1383+
// If this is a type alias to an option type, use the parent type's option name
1384+
// This ensures that ?int and MaybeInt (where MaybeInt = ?int) generate the same C type
1385+
if sym.kind == .alias && sym.info is ast.Alias && sym.info.parent_type.has_flag(.option) {
1386+
return g.option_type_name(sym.info.parent_type)
1387+
}
13831388
if sym.info is ast.FnType {
13841389
base = 'anon_fn_${g.table.fn_type_signature(sym.info.func)}'
13851390
}
@@ -1396,12 +1401,16 @@ fn (mut g Gen) option_type_name(t ast.Type) (string, string) {
13961401

13971402
fn (mut g Gen) result_type_name(t ast.Type) (string, string) {
13981403
mut base := g.base_type(t)
1404+
mut styp := ''
1405+
sym := g.table.sym(t)
1406+
// If this is a type alias to a result type, use the parent type's result name
1407+
if sym.kind == .alias && sym.info is ast.Alias && sym.info.parent_type.has_flag(.result) {
1408+
return g.result_type_name(sym.info.parent_type)
1409+
}
13991410
if t.has_flag(.option) {
14001411
g.register_option(t)
14011412
base = '_option_' + base
14021413
}
1403-
mut styp := ''
1404-
sym := g.table.sym(t)
14051414
if sym.info is ast.FnType {
14061415
base = 'anon_fn_${g.table.fn_type_signature(sym.info.func)}'
14071416
}
@@ -3182,7 +3191,7 @@ fn (mut g Gen) expr_with_fixed_array(expr ast.Expr, got_type_raw ast.Type, expec
31823191
// use instead of expr() when you need to cast to a different type
31833192
fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_type ast.Type) {
31843193
got_type := ast.mktyp(got_type_raw)
3185-
exp_sym := g.table.sym(expected_type)
3194+
exp_sym := g.table.final_sym(expected_type)
31863195
got_sym := g.table.sym(got_type)
31873196
expected_is_ptr := expected_type.is_ptr()
31883197
got_is_ptr := got_type.is_ptr()

vlib/v/gen/c/infix.v

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,12 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
112112
g.gen_plain_infix_expr(node)
113113
return
114114
}
115-
left_is_option := left_type.has_flag(.option)
116-
right_is_option := right_type.has_flag(.option)
115+
left_sym := g.table.sym(left_type)
116+
right_sym := g.table.sym(right_type)
117+
left_is_option := left_type.has_flag(.option) || (left_sym.kind == .alias
118+
&& left_sym.info is ast.Alias && left_sym.info.parent_type.has_flag(.option))
119+
right_is_option := right_type.has_flag(.option) || (right_sym.kind == .alias
120+
&& right_sym.info is ast.Alias && right_sym.info.parent_type.has_flag(.option))
117121
is_none_check := left_is_option && node.right is ast.None
118122
if is_none_check {
119123
g.gen_is_none_check(node)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
module main
2+
3+
// Define a simple interface
4+
pub interface Node {
5+
kind() int
6+
name() string
7+
}
8+
9+
// Implement the interface
10+
pub struct MyNode {
11+
pub:
12+
value int
13+
text string
14+
}
15+
16+
pub fn (n MyNode) kind() int {
17+
return 1
18+
}
19+
20+
pub fn (n MyNode) name() string {
21+
return n.text
22+
}
23+
24+
// Define a type alias to the interface
25+
pub type Expr = Node
26+
27+
// Function using the type alias
28+
pub fn process_node(expr Expr) string {
29+
return expr.name()
30+
}
31+
32+
fn test_alias_interface() {
33+
node := MyNode{
34+
value: 42
35+
text: 'test'
36+
}
37+
result := process_node(node)
38+
println('Result: ${result}')
39+
assert result == 'test'
40+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module main
2+
3+
// Test option type alias
4+
pub type MaybeInt = ?int
5+
6+
pub fn is_some(val MaybeInt) bool {
7+
return val != none
8+
}
9+
10+
fn test_alias_option() {
11+
v := ?int(42)
12+
result := is_some(v)
13+
println('Has value: ${result}')
14+
assert result
15+
}

0 commit comments

Comments
 (0)