1
0
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:
Daniel Däschle 2021-04-02 16:34:48 +02:00 committed by GitHub
parent 3637bac716
commit 6a5f49afb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 114 additions and 73 deletions

View File

@ -1402,7 +1402,6 @@ pub mut:
pub struct None {
pub:
pos token.Position
foo int // todo
}
pub enum SqlStmtKind {

View File

@ -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()]'

View File

@ -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() {

View File

@ -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
}
}

View File

@ -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))

View File

@ -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()

View 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
}
*/