diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 4615571a755da6..b710ac013ef817 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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)', diff --git a/vlib/v/checker/tests/alias_to_option_err.out b/vlib/v/checker/tests/alias_to_option_err.out new file mode 100644 index 00000000000000..ac0bc93df341f2 --- /dev/null +++ b/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 | } diff --git a/vlib/v/checker/tests/alias_to_option_err.vv b/vlib/v/checker/tests/alias_to_option_err.vv new file mode 100644 index 00000000000000..5885823ba2e7dd --- /dev/null +++ b/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) +} diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 28b33f96957602..cea9960ece9897 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -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 @@ -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 {