mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
all: support ?Type(none)
(#9567)
This commit is contained in:
parent
3637bac716
commit
6a5f49afb1
@ -1402,7 +1402,6 @@ pub mut:
|
||||
pub struct None {
|
||||
pub:
|
||||
pos token.Position
|
||||
foo int // todo
|
||||
}
|
||||
|
||||
pub enum SqlStmtKind {
|
||||
|
@ -362,6 +362,9 @@ pub fn (x Expr) str() string {
|
||||
UnsafeExpr {
|
||||
return 'unsafe { $x.expr }'
|
||||
}
|
||||
None {
|
||||
return 'none'
|
||||
}
|
||||
else {}
|
||||
}
|
||||
return '[unhandled expr type $x.type_name()]'
|
||||
|
@ -4351,7 +4351,7 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
|
||||
ast.f64_type
|
||||
})
|
||||
}
|
||||
if !c.table.sumtype_has_variant(node.typ, node.expr_type) {
|
||||
if !c.table.sumtype_has_variant(node.typ, node.expr_type) && !node.typ.has_flag(.optional) {
|
||||
c.error('cannot cast `$from_type_sym.name` to `$to_type_sym.name`', node.pos)
|
||||
}
|
||||
} else if mut to_type_sym.info is ast.Alias {
|
||||
@ -4400,7 +4400,7 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
|
||||
c.type_implements(node.expr_type, node.typ, node.pos)
|
||||
} else if node.typ == ast.bool_type {
|
||||
c.error('cannot cast to bool - use e.g. `some_int != 0` instead', node.pos)
|
||||
} else if node.expr_type == ast.none_type {
|
||||
} else if node.expr_type == ast.none_type && !node.typ.has_flag(.optional) {
|
||||
type_name := c.table.type_to_str(node.typ)
|
||||
c.error('cannot cast `none` to `$type_name`', node.pos)
|
||||
} else if from_type_sym.kind == .struct_ && !node.expr_type.is_ptr() {
|
||||
|
@ -1761,6 +1761,10 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
|
||||
g.write('*')
|
||||
}
|
||||
}
|
||||
if expected_type.has_flag(.optional) && expr is ast.None {
|
||||
g.gen_optional_error(expected_type, expr)
|
||||
return
|
||||
}
|
||||
// no cast
|
||||
g.expr(expr)
|
||||
}
|
||||
@ -2902,61 +2906,7 @@ fn (mut g Gen) expr(node ast.Expr) {
|
||||
*/
|
||||
}
|
||||
ast.CastExpr {
|
||||
// g.write('/*cast*/')
|
||||
if g.is_amp {
|
||||
// &Foo(0) => ((Foo*)0)
|
||||
g.out.go_back(1)
|
||||
}
|
||||
g.is_amp = false
|
||||
sym := g.table.get_type_symbol(node.typ)
|
||||
if sym.kind == .string && !node.typ.is_ptr() {
|
||||
// `string(x)` needs `tos()`, but not `&string(x)
|
||||
// `tos(str, len)`, `tos2(str)`
|
||||
if node.has_arg {
|
||||
g.write('tos((byteptr)')
|
||||
} else {
|
||||
g.write('tos2((byteptr)')
|
||||
}
|
||||
g.expr(node.expr)
|
||||
expr_sym := g.table.get_type_symbol(node.expr_type)
|
||||
if expr_sym.kind == .array {
|
||||
// if we are casting an array, we need to add `.data`
|
||||
g.write('.data')
|
||||
}
|
||||
if node.has_arg {
|
||||
// len argument
|
||||
g.write(', ')
|
||||
g.expr(node.arg)
|
||||
}
|
||||
g.write(')')
|
||||
} else if sym.kind in [.sum_type, .interface_] {
|
||||
g.expr_with_cast(node.expr, node.expr_type, node.typ)
|
||||
} else if sym.kind == .struct_ && !node.typ.is_ptr()
|
||||
&& !(sym.info as ast.Struct).is_typedef {
|
||||
// deprecated, replaced by Struct{...exr}
|
||||
styp := g.typ(node.typ)
|
||||
g.write('*(($styp *)(&')
|
||||
g.expr(node.expr)
|
||||
g.write('))')
|
||||
} else {
|
||||
styp := g.typ(node.typ)
|
||||
mut cast_label := ''
|
||||
// `ast.string_type` is done for MSVC's bug
|
||||
if sym.kind != .alias
|
||||
|| (sym.info as ast.Alias).parent_type !in [node.expr_type, ast.string_type] {
|
||||
cast_label = '($styp)'
|
||||
}
|
||||
g.write('(${cast_label}(')
|
||||
g.expr(node.expr)
|
||||
if node.expr is ast.IntegerLiteral {
|
||||
if node.typ in [ast.u64_type, ast.u32_type, ast.u16_type] {
|
||||
if !node.expr.val.starts_with('-') {
|
||||
g.write('U')
|
||||
}
|
||||
}
|
||||
}
|
||||
g.write('))')
|
||||
}
|
||||
g.cast_expr(node)
|
||||
}
|
||||
ast.ChanInit {
|
||||
elem_typ_str := g.typ(node.elem_type)
|
||||
@ -4403,6 +4353,66 @@ fn (mut g Gen) ident(node ast.Ident) {
|
||||
g.write(g.get_ternary_name(name))
|
||||
}
|
||||
|
||||
fn (mut g Gen) cast_expr(node ast.CastExpr) {
|
||||
if g.is_amp {
|
||||
// &Foo(0) => ((Foo*)0)
|
||||
g.out.go_back(1)
|
||||
}
|
||||
g.is_amp = false
|
||||
sym := g.table.get_type_symbol(node.typ)
|
||||
if sym.kind == .string && !node.typ.is_ptr() {
|
||||
// `string(x)` needs `tos()`, but not `&string(x)
|
||||
// `tos(str, len)`, `tos2(str)`
|
||||
if node.has_arg {
|
||||
g.write('tos((byteptr)')
|
||||
} else {
|
||||
g.write('tos2((byteptr)')
|
||||
}
|
||||
g.expr(node.expr)
|
||||
expr_sym := g.table.get_type_symbol(node.expr_type)
|
||||
if expr_sym.kind == .array {
|
||||
// if we are casting an array, we need to add `.data`
|
||||
g.write('.data')
|
||||
}
|
||||
if node.has_arg {
|
||||
// len argument
|
||||
g.write(', ')
|
||||
g.expr(node.arg)
|
||||
}
|
||||
g.write(')')
|
||||
} else if sym.kind in [.sum_type, .interface_] {
|
||||
g.expr_with_cast(node.expr, node.expr_type, node.typ)
|
||||
} else if sym.kind == .struct_ && !node.typ.is_ptr() && !(sym.info as ast.Struct).is_typedef {
|
||||
// deprecated, replaced by Struct{...exr}
|
||||
styp := g.typ(node.typ)
|
||||
g.write('*(($styp *)(&')
|
||||
g.expr(node.expr)
|
||||
g.write('))')
|
||||
} else {
|
||||
styp := g.typ(node.typ)
|
||||
mut cast_label := ''
|
||||
// `ast.string_type` is done for MSVC's bug
|
||||
if sym.kind != .alias
|
||||
|| (sym.info as ast.Alias).parent_type !in [node.expr_type, ast.string_type] {
|
||||
cast_label = '($styp)'
|
||||
}
|
||||
if node.typ.has_flag(.optional) && node.expr is ast.None {
|
||||
g.gen_optional_error(node.typ, node.expr)
|
||||
} else {
|
||||
g.write('(${cast_label}(')
|
||||
g.expr(node.expr)
|
||||
if node.expr is ast.IntegerLiteral {
|
||||
if node.typ in [ast.u64_type, ast.u32_type, ast.u16_type] {
|
||||
if !node.expr.val.starts_with('-') {
|
||||
g.write('U')
|
||||
}
|
||||
}
|
||||
}
|
||||
g.write('))')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) concat_expr(node ast.ConcatExpr) {
|
||||
styp := g.typ(node.return_type)
|
||||
sym := g.table.get_type_symbol(node.return_type)
|
||||
@ -4598,6 +4608,13 @@ fn (g &Gen) expr_is_multi_return_call(expr ast.Expr) bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) gen_optional_error(target_type ast.Type, expr ast.Expr) {
|
||||
styp := g.typ(target_type)
|
||||
g.write('($styp){ .state=2, .err=')
|
||||
g.expr(expr)
|
||||
g.write(' }')
|
||||
}
|
||||
|
||||
fn (mut g Gen) return_statement(node ast.Return) {
|
||||
g.write_v_source_line_info(node.pos)
|
||||
if node.exprs.len > 0 {
|
||||
@ -4635,18 +4652,10 @@ fn (mut g Gen) return_statement(node ast.Return) {
|
||||
optional_none := node.exprs[0] is ast.None
|
||||
ftyp := g.typ(node.types[0])
|
||||
mut is_regular_option := ftyp in ['Option2', 'Option']
|
||||
if optional_none || is_regular_option {
|
||||
styp := g.typ(g.fn_decl.return_type)
|
||||
g.write('return ($styp){ .state=2, .err=')
|
||||
g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type)
|
||||
g.writeln(' };')
|
||||
return
|
||||
} else if node.types[0] == ast.error_type_idx {
|
||||
// foo() or { return err }
|
||||
styp := g.typ(g.fn_decl.return_type)
|
||||
g.write('return ($styp){.state=2, .err=')
|
||||
g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type)
|
||||
g.writeln(' };')
|
||||
if optional_none || is_regular_option || node.types[0] == ast.error_type_idx {
|
||||
g.write('return ')
|
||||
g.gen_optional_error(g.fn_decl.return_type, node.exprs[0])
|
||||
g.writeln(';')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -1938,14 +1938,16 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
||||
} else {
|
||||
false
|
||||
}
|
||||
is_optional := p.tok.kind == .question
|
||||
// p.warn('name expr $p.tok.lit $p.peek_tok.str()')
|
||||
same_line := p.tok.line_nr == p.peek_tok.line_nr
|
||||
// `(` must be on same line as name token otherwise it's a ParExpr
|
||||
if !same_line && p.peek_tok.kind == .lpar {
|
||||
node = p.parse_ident(language)
|
||||
} else if p.peek_tok.kind == .lpar || p.is_generic_call() {
|
||||
} else if p.peek_tok.kind == .lpar
|
||||
|| (is_optional && p.peek_token(2).kind == .lpar) || p.is_generic_call() {
|
||||
// foo(), foo<int>() or type() cast
|
||||
mut name := p.tok.lit
|
||||
mut name := if is_optional { p.peek_tok.lit } else { p.tok.lit }
|
||||
if mod.len > 0 {
|
||||
name = '${mod}.$name'
|
||||
}
|
||||
@ -1992,6 +1994,9 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
||||
} else {
|
||||
// fn call
|
||||
// println('calling $p.tok.lit')
|
||||
if is_optional {
|
||||
p.error_with_pos('unexpected $p.prev_tok', p.prev_tok.position())
|
||||
}
|
||||
node = p.call_expr(language, mod)
|
||||
}
|
||||
} else if (p.peek_tok.kind == .lcbr || (p.peek_tok.kind == .lt && lit0_is_capital))
|
||||
|
@ -25,7 +25,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
||||
node = p.parse_ident(ast.Language.v)
|
||||
p.is_stmt_ident = is_stmt_ident
|
||||
}
|
||||
.name {
|
||||
.name, .question {
|
||||
if p.tok.lit == 'sql' && p.peek_tok.kind == .name {
|
||||
p.inside_match = true // reuse the same var for perf instead of inside_sql TODO rename
|
||||
node = p.sql_expr()
|
||||
|
25
vlib/v/tests/optional_assign_test.v
Normal file
25
vlib/v/tests/optional_assign_test.v
Normal file
@ -0,0 +1,25 @@
|
||||
type Foo = int | string
|
||||
|
||||
fn test_optional_none_assign() {
|
||||
x := ?Foo(none)
|
||||
// assert x.err == none
|
||||
// assert !x.has_value
|
||||
}
|
||||
|
||||
fn test_optional_none_assign_nonsumtype() {
|
||||
x := ?int(none)
|
||||
// assert x.err == none
|
||||
// assert !x.has_value
|
||||
}
|
||||
|
||||
// TODO: make this working next
|
||||
/*
|
||||
fn test_optional_value_assign() {
|
||||
x := ?Foo('test')
|
||||
}
|
||||
|
||||
fn test_optional_none_reassign() {
|
||||
mut x := ?Foo('test')
|
||||
x = none
|
||||
}
|
||||
*/
|
Loading…
Reference in New Issue
Block a user