Skip to content

Commit

Permalink
cgen: fix alias to option handling (#18490)
Browse files Browse the repository at this point in the history
  • Loading branch information
felipensp committed Jun 20, 2023
1 parent a7f00e7 commit c657384
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 4 deletions.
6 changes: 6 additions & 0 deletions vlib/v/checker/checker.v
Expand Up @@ -2861,6 +2861,12 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {

if to_sym.language != .c {
c.ensure_type_exists(to_type, node.pos) or {}

if to_sym.kind == .alias && (to_sym.info as ast.Alias).parent_type.has_flag(.option)
&& !to_type.has_flag(.option) {
c.error('alias to Option type requires to be used as Option type (?${to_sym.name}(...))',
node.pos)
}
}
if from_sym.kind == .u8 && from_type.is_ptr() && to_sym.kind == .string && !to_type.is_ptr() {
c.error('to convert a C string buffer pointer to a V string, use x.vstring() instead of string(x)',
Expand Down
42 changes: 42 additions & 0 deletions vlib/v/checker/tests/alias_to_option_err.out
@@ -0,0 +1,42 @@
vlib/v/checker/tests/alias_to_option_err.vv:11:7: error: alias to Option type requires to be used as Option type (?TestInt(...))
9 |
10 | fn main() {
11 | f := TestInt(1)
| ~~~~~~~~~~
12 | dump(f)
13 | println(f)
vlib/v/checker/tests/alias_to_option_err.vv:15:7: error: alias to Option type requires to be used as Option type (?TestString(...))
13 | println(f)
14 |
15 | g := TestString('foo')
| ~~~~~~~~~~~~~~~~~
16 | dump(g)
17 | println(g)
vlib/v/checker/tests/alias_to_option_err.vv:19:7: error: alias to Option type requires to be used as Option type (?TestString(...))
17 | println(g)
18 |
19 | h := TestString(none)
| ~~~~~~~~~~~~~~~~
20 | dump(h)
21 |
vlib/v/checker/tests/alias_to_option_err.vv:22:7: error: alias to Option type requires to be used as Option type (?TestF64(...))
20 | dump(h)
21 |
22 | i := TestF64(none)
| ~~~~~~~~~~~~~
23 | dump(i)
24 |
vlib/v/checker/tests/alias_to_option_err.vv:25:7: error: alias to Option type requires to be used as Option type (?TestStruct(...))
23 | dump(i)
24 |
25 | l := TestStruct(none)
| ~~~~~~~~~~~~~~~~
26 | dump(l)
27 |
vlib/v/checker/tests/alias_to_option_err.vv:28:7: error: alias to Option type requires to be used as Option type (?TestStruct(...))
26 | dump(l)
27 |
28 | k := TestStruct(Struct{})
| ~~~~~~~~~~~~~~~~~~~~
29 | dump(k)
30 | }
30 changes: 30 additions & 0 deletions vlib/v/checker/tests/alias_to_option_err.vv
@@ -0,0 +1,30 @@
type TestInt = ?int
type TestString = ?string
type TestF64 = ?f64

struct Struct {
}

type TestStruct = ?Struct

fn main() {
f := TestInt(1)
dump(f)
println(f)

g := TestString('foo')
dump(g)
println(g)

h := TestString(none)
dump(h)

i := TestF64(none)
dump(i)

l := TestStruct(none)
dump(l)

k := TestStruct(Struct{})
dump(k)
}
42 changes: 38 additions & 4 deletions vlib/v/gen/c/cgen.v
Expand Up @@ -4245,7 +4245,20 @@ fn (mut g Gen) ident(node ast.Ident) {
if is_auto_heap {
g.write('(*(${styp}*)${name}->data)')
} else {
g.write('(*(${styp}*)${name}.data)')
type_sym := g.table.sym(node.info.typ)
if type_sym.kind == .alias {
// Alias to Option type
parent_typ := (type_sym.info as ast.Alias).parent_type
if parent_typ.has_flag(.option) {
g.write('*((${g.base_type(parent_typ)}*)')
}
g.write('(*(${styp}*)${name}.data)')
if parent_typ.has_flag(.option) {
g.write('.data)')
}
} else {
g.write('(*(${styp}*)${name}.data)')
}
}
}
if node.or_expr.kind != .absent && !(g.inside_opt_or_res && g.inside_assign
Expand Down Expand Up @@ -4398,17 +4411,38 @@ fn (mut g Gen) cast_expr(node ast.CastExpr) {
mut cast_label := ''
// `ast.string_type` is done for MSVC's bug
if sym.kind != .alias
|| (sym.info as ast.Alias).parent_type !in [expr_type, ast.string_type] {
|| (!(sym.info as ast.Alias).parent_type.has_flag(.option)
&& (sym.info as ast.Alias).parent_type !in [expr_type, ast.string_type]) {
cast_label = '(${styp})'
}
if node.typ.has_flag(.option) && node.expr is ast.None {
g.gen_option_error(node.typ, node.expr)
} else if node.typ.has_flag(.option) {
if sym.kind == .alias && node.expr_type.has_flag(.option) {
g.expr_opt_with_cast(node.expr, expr_type, node.typ)
if sym.kind == .alias {
if (sym.info as ast.Alias).parent_type.has_flag(.option) {
cur_stmt := g.go_before_stmt(0)
g.empty_line = true
parent_type := (sym.info as ast.Alias).parent_type
tmp_var := g.new_tmp_var()
tmp_var2 := g.new_tmp_var()
g.writeln('${styp} ${tmp_var};')
g.writeln('${g.typ(parent_type)} ${tmp_var2};')
g.write('_option_ok(&(${g.base_type(parent_type)}[]) { ')
g.expr(node.expr)
g.writeln(' }, (${c.option_name}*)(&${tmp_var2}), sizeof(${g.base_type(parent_type)}));')
g.writeln('_option_ok(&(${g.typ(parent_type)}[]) { ${tmp_var2} }, (${c.option_name}*)&${tmp_var}, sizeof(${g.typ(parent_type)}));')
g.write(cur_stmt)
g.write(tmp_var)
} else if node.expr_type.has_flag(.option) {
g.expr_opt_with_cast(node.expr, expr_type, node.typ)
} else {
g.expr_with_opt(node.expr, expr_type, node.typ)
}
} else {
g.expr_with_opt(node.expr, expr_type, node.typ)
}
} else if sym.kind == .alias && (sym.info as ast.Alias).parent_type.has_flag(.option) {
g.expr_with_opt(node.expr, expr_type, (sym.info as ast.Alias).parent_type)
} else {
g.write('(${cast_label}(')
if sym.kind == .alias && g.table.final_sym(node.typ).kind == .string {
Expand Down

0 comments on commit c657384

Please sign in to comment.