mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
all: implement struct field optional and disallow storing result (#16392)
This commit is contained in:
parent
41dd8985fc
commit
37700502f5
@ -612,6 +612,7 @@ fn (t Tree) struct_field(node ast.StructField) &Node {
|
||||
obj.add_terse('name', t.string_node(node.name))
|
||||
obj.add_terse('typ', t.type_node(node.typ))
|
||||
obj.add('type_pos', t.pos(node.type_pos))
|
||||
obj.add('optional_pos', t.pos(node.optional_pos))
|
||||
obj.add_terse('has_default_expr', t.bool_node(node.has_default_expr))
|
||||
obj.add_terse('default_expr_typ', t.type_node(node.default_expr_typ))
|
||||
obj.add_terse('default_expr', t.expr(node.default_expr))
|
||||
@ -1373,6 +1374,7 @@ fn (t Tree) selector_expr(node ast.SelectorExpr) &Node {
|
||||
obj.add_terse('field_name', t.string_node(node.field_name))
|
||||
obj.add_terse('typ', t.type_node(node.typ))
|
||||
obj.add_terse('name_type', t.type_node(node.name_type))
|
||||
obj.add_terse('or_block', t.or_expr(node.or_block))
|
||||
obj.add_terse('gkind_field', t.enum_node(node.gkind_field))
|
||||
obj.add_terse('from_embed_types', t.array_node_type(node.from_embed_types))
|
||||
obj.add_terse('next_token', t.token_node(node.next_token))
|
||||
|
@ -1,7 +1,7 @@
|
||||
module net
|
||||
|
||||
pub enum SocketOption {
|
||||
// TODO: SO_ACCEPT_CONN is not here becuase windows doesnt support it
|
||||
// TODO: SO_ACCEPT_CONN is not here because windows doesnt support it
|
||||
// and there is no easy way to define it
|
||||
broadcast = C.SO_BROADCAST
|
||||
debug = C.SO_DEBUG
|
||||
|
@ -260,6 +260,7 @@ pub mut:
|
||||
expr_type Type // type of `Foo` in `Foo.bar`
|
||||
typ Type // type of the entire thing (`Foo.bar`)
|
||||
name_type Type // T in `T.name` or typeof in `typeof(expr).name`
|
||||
or_block OrExpr
|
||||
gkind_field GenericKindField // `T.name` => ast.GenericKindField.name, `T.typ` => ast.GenericKindField.typ, or .unknown
|
||||
scope &Scope = unsafe { nil }
|
||||
from_embed_types []Type // holds the type of the embed that the method is called from
|
||||
@ -295,6 +296,7 @@ pub struct StructField {
|
||||
pub:
|
||||
pos token.Pos
|
||||
type_pos token.Pos
|
||||
optional_pos token.Pos
|
||||
comments []Comment
|
||||
i int
|
||||
has_default_expr bool
|
||||
|
@ -17,13 +17,16 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
|
||||
mut right_len := node.right.len
|
||||
mut right_type0 := ast.void_type
|
||||
for i, mut right in node.right {
|
||||
if right in [ast.CallExpr, ast.IfExpr, ast.LockExpr, ast.MatchExpr, ast.DumpExpr] {
|
||||
if right in [ast.CallExpr, ast.IfExpr, ast.LockExpr, ast.MatchExpr, ast.DumpExpr,
|
||||
ast.SelectorExpr] {
|
||||
if right in [ast.IfExpr, ast.MatchExpr] && node.left.len == node.right.len && !is_decl
|
||||
&& node.left[i] in [ast.Ident, ast.SelectorExpr] && !node.left[i].is_blank_ident() {
|
||||
c.expected_type = c.expr(node.left[i])
|
||||
}
|
||||
right_type := c.expr(right)
|
||||
c.fail_if_unreadable(right, right_type, 'right-hand side of assignment')
|
||||
if right in [ast.CallExpr, ast.IfExpr, ast.LockExpr, ast.MatchExpr, ast.DumpExpr] {
|
||||
c.fail_if_unreadable(right, right_type, 'right-hand side of assignment')
|
||||
}
|
||||
if i == 0 {
|
||||
right_type0 = right_type
|
||||
node.right_types = [
|
||||
|
@ -950,6 +950,35 @@ pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type ast.Type) ast
|
||||
c.error('unexpected `!`, the function `${expr.name}` does not return a result',
|
||||
expr.or_block.pos)
|
||||
}
|
||||
} else if expr is ast.SelectorExpr && c.table.sym(ret_type).kind != .chan {
|
||||
if expr.typ.has_flag(.optional) || expr.typ.has_flag(.result) {
|
||||
with_modifier_kind := if expr.typ.has_flag(.optional) {
|
||||
'an option'
|
||||
} else {
|
||||
'a result'
|
||||
}
|
||||
with_modifier := if expr.typ.has_flag(.optional) { '?' } else { '!' }
|
||||
if expr.or_block.kind == .absent {
|
||||
if c.inside_defer {
|
||||
c.error('field `${expr.field_name}` is ${with_modifier_kind}, so it should have an `or {}` block at the end',
|
||||
expr.pos)
|
||||
} else {
|
||||
c.error('field `${expr.field_name}` is ${with_modifier_kind}, so it should have either an `or {}` block, or `${with_modifier}` at the end',
|
||||
expr.pos)
|
||||
}
|
||||
} else {
|
||||
c.check_or_expr(expr.or_block, ret_type, expr.typ)
|
||||
}
|
||||
return ret_type.clear_flag(.optional).clear_flag(.result)
|
||||
} else if expr.or_block.kind == .block {
|
||||
c.error('unexpected `or` block, the field `${expr.field_name}` is neither an optional, nor a result',
|
||||
expr.or_block.pos)
|
||||
} else if expr.or_block.kind == .propagate_option {
|
||||
c.error('unexpected `?`, the field `${expr.field_name}` is not an optional',
|
||||
expr.or_block.pos)
|
||||
} else if expr.or_block.kind == .propagate_result {
|
||||
c.error('unexpected `!`, result fields are not supported', expr.or_block.pos)
|
||||
}
|
||||
} else if expr is ast.IndexExpr {
|
||||
if expr.or_expr.kind != .absent {
|
||||
c.check_or_expr(expr.or_expr, ret_type, ret_type.set_flag(.result))
|
||||
@ -1281,6 +1310,11 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type {
|
||||
}
|
||||
}
|
||||
node.typ = field.typ
|
||||
if node.or_block.kind == .block {
|
||||
c.expected_or_type = node.typ.clear_flag(.optional).clear_flag(.result)
|
||||
c.stmts_ending_with_expression(node.or_block.stmts)
|
||||
c.expected_or_type = ast.void_type
|
||||
}
|
||||
return field.typ
|
||||
}
|
||||
if mut method := c.table.find_method(sym, field_name) {
|
||||
@ -2321,7 +2355,7 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
|
||||
|
||||
unwrapped_expr_type := c.unwrap_generic(node.expr_type)
|
||||
tsym := c.table.sym(unwrapped_expr_type)
|
||||
c.table.dumps[int(unwrapped_expr_type)] = tsym.cname
|
||||
c.table.dumps[int(unwrapped_expr_type.clear_flag(.optional).clear_flag(.result))] = tsym.cname
|
||||
node.cname = tsym.cname
|
||||
return node.expr_type
|
||||
}
|
||||
@ -2422,7 +2456,32 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
|
||||
return c.select_expr(mut node)
|
||||
}
|
||||
ast.SelectorExpr {
|
||||
return c.selector_expr(mut node)
|
||||
mut ret_type := c.selector_expr(mut node)
|
||||
if c.table.sym(ret_type).kind == .chan {
|
||||
return ret_type
|
||||
}
|
||||
|
||||
if !ret_type.has_flag(.optional) && !ret_type.has_flag(.result) {
|
||||
if node.or_block.kind == .block {
|
||||
c.error('unexpected `or` block, the field `${node.field_name}` is neither an optional, nor a result',
|
||||
node.or_block.pos)
|
||||
} else if node.or_block.kind == .propagate_option {
|
||||
c.error('unexpected `?`, the field `${node.field_name}` is neither an optional, nor a result',
|
||||
node.or_block.pos)
|
||||
} else if node.or_block.kind == .propagate_result {
|
||||
c.error('unexpected `!`, the field `${node.field_name}` is neither an optional, nor a result',
|
||||
node.or_block.pos)
|
||||
}
|
||||
}
|
||||
if node.or_block.kind != .absent {
|
||||
if ret_type.has_flag(.optional) {
|
||||
ret_type = ret_type.clear_flag(.optional)
|
||||
}
|
||||
if ret_type.has_flag(.result) {
|
||||
ret_type = ret_type.clear_flag(.result)
|
||||
}
|
||||
}
|
||||
return ret_type
|
||||
}
|
||||
ast.SizeOf {
|
||||
if !node.is_type {
|
||||
|
@ -48,6 +48,9 @@ pub fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
|
||||
}
|
||||
}
|
||||
for i, field in node.fields {
|
||||
if field.typ.has_flag(.result) {
|
||||
c.error('struct field does not support storing result', field.optional_pos)
|
||||
}
|
||||
c.ensure_type_exists(field.typ, field.type_pos) or { return }
|
||||
if field.typ.has_flag(.generic) {
|
||||
has_generic_types = true
|
||||
@ -457,14 +460,6 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
|
||||
node.fields[i].typ = expr_type
|
||||
node.fields[i].expected_type = field_info.typ
|
||||
|
||||
if field_info.typ.has_flag(.optional) {
|
||||
c.error('field `${field_info.name}` is optional, but initialization of optional fields currently unsupported',
|
||||
field.pos)
|
||||
}
|
||||
if field_info.typ.has_flag(.result) {
|
||||
c.error('field `${field_info.name}` is result, but initialization of result fields currently unsupported',
|
||||
field.pos)
|
||||
}
|
||||
if expr_type.is_ptr() && expected_type.is_ptr() {
|
||||
if mut field.expr is ast.Ident {
|
||||
if mut field.expr.obj is ast.Var {
|
||||
|
@ -1,5 +1,5 @@
|
||||
vlib/v/checker/tests/optional_fn_err.vv:13:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
11 |
|
||||
11 |
|
||||
12 | const (
|
||||
13 | const_value = bar(0)
|
||||
| ~~~~~~
|
||||
@ -31,21 +31,21 @@ vlib/v/checker/tests/optional_fn_err.vv:35:16: error: bar() returns an option, s
|
||||
34 | _ := bar(0)
|
||||
35 | println(twice(bar(0)))
|
||||
| ~~~~~~
|
||||
36 |
|
||||
36 |
|
||||
37 | // anon fn
|
||||
vlib/v/checker/tests/optional_fn_err.vv:38:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
36 |
|
||||
36 |
|
||||
37 | // anon fn
|
||||
38 | fn (_ int) {}(bar(0))
|
||||
| ~~~~~~
|
||||
39 |
|
||||
39 |
|
||||
40 | // assert
|
||||
vlib/v/checker/tests/optional_fn_err.vv:41:9: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
39 |
|
||||
39 |
|
||||
40 | // assert
|
||||
41 | assert bar(true)
|
||||
| ~~~~~~~~~
|
||||
42 |
|
||||
42 |
|
||||
43 | // struct
|
||||
vlib/v/checker/tests/optional_fn_err.vv:46:10: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
44 | mut v := Data{
|
||||
@ -54,13 +54,6 @@ vlib/v/checker/tests/optional_fn_err.vv:46:10: error: bar() returns an option, s
|
||||
| ~~~~~~
|
||||
47 | opt: bar(0),
|
||||
48 | }
|
||||
vlib/v/checker/tests/optional_fn_err.vv:47:3: error: field `opt` is optional, but initialization of optional fields currently unsupported
|
||||
45 | f: fn (_ int) {},
|
||||
46 | value: bar(0),
|
||||
47 | opt: bar(0),
|
||||
| ~~~~~~~~~~~
|
||||
48 | }
|
||||
49 | v.add(bar(0)) // call method
|
||||
vlib/v/checker/tests/optional_fn_err.vv:49:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
47 | opt: bar(0),
|
||||
48 | }
|
||||
@ -73,7 +66,7 @@ vlib/v/checker/tests/optional_fn_err.vv:50:6: error: bar() returns an option, so
|
||||
49 | v.add(bar(0)) // call method
|
||||
50 | v.f(bar(0)) // call fn field
|
||||
| ~~~~~~
|
||||
51 |
|
||||
51 |
|
||||
52 | // array
|
||||
vlib/v/checker/tests/optional_fn_err.vv:54:9: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
52 | // array
|
||||
@ -164,11 +157,11 @@ vlib/v/checker/tests/optional_fn_err.vv:70:18: error: bar() returns an option, s
|
||||
69 | println(arr.any(bar(true)))
|
||||
70 | println(arr.all(bar(true)))
|
||||
| ~~~~~~~~~
|
||||
71 |
|
||||
71 |
|
||||
72 | match bar(0) {
|
||||
vlib/v/checker/tests/optional_fn_err.vv:72:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
70 | println(arr.all(bar(true)))
|
||||
71 |
|
||||
71 |
|
||||
72 | match bar(0) {
|
||||
| ~~~~~~
|
||||
73 | 0 { }
|
||||
|
41
vlib/v/checker/tests/struct_field_optional_err.out
Normal file
41
vlib/v/checker/tests/struct_field_optional_err.out
Normal file
@ -0,0 +1,41 @@
|
||||
vlib/v/checker/tests/struct_field_optional_err.vv:3:6: error: struct field does not support storing result
|
||||
1 | struct Foo {
|
||||
2 | mut:
|
||||
3 | foo !int
|
||||
| ^
|
||||
4 | bar ?int = 1
|
||||
5 | baz int = 1
|
||||
vlib/v/checker/tests/struct_field_optional_err.vv:11:8: error: field `bar` is an option, so it should have either an `or {}` block, or `?` at the end
|
||||
9 | mut f := Foo{}
|
||||
10 |
|
||||
11 | _ = f.bar
|
||||
| ~~~
|
||||
12 | _ = f.bar + 1
|
||||
13 |
|
||||
vlib/v/checker/tests/struct_field_optional_err.vv:12:12: error: `+` cannot be used with `?int`
|
||||
10 |
|
||||
11 | _ = f.bar
|
||||
12 | _ = f.bar + 1
|
||||
| ^
|
||||
13 |
|
||||
14 | _ = f.bar!
|
||||
vlib/v/checker/tests/struct_field_optional_err.vv:14:11: error: to propagate a result, the call must also return a result type
|
||||
12 | _ = f.bar + 1
|
||||
13 |
|
||||
14 | _ = f.bar!
|
||||
| ^
|
||||
15 |
|
||||
16 | _ = f.bar or { _ = 1 }
|
||||
vlib/v/checker/tests/struct_field_optional_err.vv:16:19: error: last statement in the `or {}` block should be an expression of type `int` or exit parent scope
|
||||
14 | _ = f.bar!
|
||||
15 |
|
||||
16 | _ = f.bar or { _ = 1 }
|
||||
| ^
|
||||
17 |
|
||||
18 | _ = f.baz?
|
||||
vlib/v/checker/tests/struct_field_optional_err.vv:18:11: error: unexpected `?`, the field `baz` is neither an optional, nor a result
|
||||
16 | _ = f.bar or { _ = 1 }
|
||||
17 |
|
||||
18 | _ = f.baz?
|
||||
| ^
|
||||
19 | }
|
19
vlib/v/checker/tests/struct_field_optional_err.vv
Normal file
19
vlib/v/checker/tests/struct_field_optional_err.vv
Normal file
@ -0,0 +1,19 @@
|
||||
struct Foo {
|
||||
mut:
|
||||
foo !int
|
||||
bar ?int = 1
|
||||
baz int = 1
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut f := Foo{}
|
||||
|
||||
_ = f.bar
|
||||
_ = f.bar + 1
|
||||
|
||||
_ = f.bar!
|
||||
|
||||
_ = f.bar or { _ = 1 }
|
||||
|
||||
_ = f.baz?
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
vlib/v/checker/tests/struct_field_optional_init_err.vv:8:3: error: field `bar` is result, but initialization of result fields currently unsupported
|
||||
6 | fn main() {
|
||||
7 | _ := Foo{
|
||||
8 | bar: 1
|
||||
| ~~~~~~
|
||||
9 | baz: 1
|
||||
10 | }
|
||||
vlib/v/checker/tests/struct_field_optional_init_err.vv:9:3: error: field `baz` is optional, but initialization of optional fields currently unsupported
|
||||
7 | _ := Foo{
|
||||
8 | bar: 1
|
||||
9 | baz: 1
|
||||
| ~~~~~~
|
||||
10 | }
|
||||
11 | }
|
@ -1,11 +0,0 @@
|
||||
struct Foo {
|
||||
bar !int
|
||||
baz ?int
|
||||
}
|
||||
|
||||
fn main() {
|
||||
_ := Foo{
|
||||
bar: 1
|
||||
baz: 1
|
||||
}
|
||||
}
|
@ -2568,6 +2568,7 @@ pub fn (mut f Fmt) selector_expr(node ast.SelectorExpr) {
|
||||
f.expr(node.expr)
|
||||
f.write('.')
|
||||
f.write(node.field_name)
|
||||
f.or_expr(node.or_block)
|
||||
}
|
||||
|
||||
pub fn (mut f Fmt) size_of(node ast.SizeOf) {
|
||||
|
@ -192,7 +192,7 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
|
||||
&& !g.pref.translated
|
||||
g.is_assign_lhs = true
|
||||
g.assign_op = node.op
|
||||
if val_type.has_flag(.optional) {
|
||||
if val_type.has_flag(.optional) || val_type.has_flag(.result) {
|
||||
g.right_is_opt = true
|
||||
}
|
||||
if blank_assign {
|
||||
@ -434,7 +434,11 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
|
||||
*/
|
||||
}
|
||||
if !cloned {
|
||||
if is_fixed_array_var {
|
||||
if (var_type.has_flag(.optional) && !val_type.has_flag(.optional))
|
||||
|| (var_type.has_flag(.result) && !val_type.has_flag(.result)) {
|
||||
tmp_var := g.new_tmp_var()
|
||||
g.expr_with_tmp_var(val, val_type, var_type, tmp_var)
|
||||
} else if is_fixed_array_var {
|
||||
// TODO Instead of the translated check, check if it's a pointer already
|
||||
// and don't generate memcpy &
|
||||
typ_str := g.typ(val_type).trim('*')
|
||||
|
@ -997,7 +997,12 @@ fn struct_auto_str_func(sym &ast.TypeSymbol, _field_type ast.Type, fn_name strin
|
||||
} else if sym.kind == .chan {
|
||||
return '${fn_name}(${deref}it.${c_name(field_name)}${sufix})', true
|
||||
} else {
|
||||
mut method_str := 'it.${c_name(field_name)}'
|
||||
mut method_str := ''
|
||||
if !field_type.is_ptr() && (field_type.has_flag(.optional) || field_type.has_flag(.result)) {
|
||||
method_str = '(*(${sym.name}*)it.${c_name(field_name)}.data)'
|
||||
} else {
|
||||
method_str = 'it.${c_name(field_name)}'
|
||||
}
|
||||
if sym.kind == .bool {
|
||||
return '${method_str} ? _SLIT("true") : _SLIT("false")', false
|
||||
} else if (field_type.is_int_valptr() || field_type.is_float_valptr())
|
||||
|
@ -110,6 +110,7 @@ mut:
|
||||
results_forward []string // to forward
|
||||
results map[string]string // to avoid duplicates
|
||||
done_optionals shared []string // to avoid duplicates
|
||||
done_results shared []string // to avoid duplicates
|
||||
chan_pop_optionals map[string]string // types for `x := <-ch or {...}`
|
||||
chan_push_optionals map[string]string // types for `ch <- x or {...}`
|
||||
mtxs string // array of mutexes if the `lock` has multiple variables
|
||||
@ -120,6 +121,7 @@ mut:
|
||||
inside_map_postfix bool // inside map++/-- postfix expr
|
||||
inside_map_infix bool // inside map<</+=/-= infix expr
|
||||
inside_map_index bool
|
||||
inside_opt_or_res bool
|
||||
inside_opt_data bool
|
||||
inside_if_optional bool
|
||||
inside_if_result bool
|
||||
@ -647,6 +649,7 @@ fn cgen_process_one_file_cb(mut p pool.PoolProcessor, idx int, wid int) &Gen {
|
||||
options_forward: global_g.options_forward
|
||||
results_forward: global_g.results_forward
|
||||
done_optionals: global_g.done_optionals
|
||||
done_results: global_g.done_results
|
||||
is_autofree: global_g.pref.autofree
|
||||
referenced_fns: global_g.referenced_fns
|
||||
is_cc_msvc: global_g.is_cc_msvc
|
||||
@ -1130,6 +1133,9 @@ fn (mut g Gen) write_optionals() {
|
||||
|
||||
fn (mut g Gen) write_results() {
|
||||
mut done := []string{}
|
||||
rlock g.done_results {
|
||||
done = g.done_results.clone()
|
||||
}
|
||||
for base, styp in g.results {
|
||||
if base in done {
|
||||
continue
|
||||
@ -1572,6 +1578,22 @@ pub fn (mut g Gen) write_multi_return_types() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if mr_typ.has_flag(.result) {
|
||||
// result in multi_return
|
||||
// Dont use g.typ() here because it will register
|
||||
// result and we dont want that
|
||||
styp, base := g.result_type_name(mr_typ)
|
||||
lock g.done_results {
|
||||
if base !in g.done_results {
|
||||
g.done_results << base
|
||||
last_text := g.type_definitions.after(start_pos).clone()
|
||||
g.type_definitions.go_back_to(start_pos)
|
||||
g.typedefs.writeln('typedef struct ${styp} ${styp};')
|
||||
g.type_definitions.writeln('${g.result_type_text(styp, base)};')
|
||||
g.type_definitions.write_string(last_text)
|
||||
}
|
||||
}
|
||||
}
|
||||
g.type_definitions.writeln('\t${type_name} arg${i};')
|
||||
}
|
||||
g.type_definitions.writeln('};\n')
|
||||
@ -1776,6 +1798,42 @@ fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) bool {
|
||||
return last_stmt_was_return
|
||||
}
|
||||
|
||||
// expr_with_tmp_var is used in assign expr to `optinal` or `result` type.
|
||||
// applicable to situations where the expr_typ does not have `optinal` and `result`,
|
||||
// e.g. field default: "foo ?int = 1", field assign: "foo = 1", field init: "foo: 1"
|
||||
fn (mut g Gen) expr_with_tmp_var(expr ast.Expr, expr_typ ast.Type, ret_typ ast.Type, tmp_var string) {
|
||||
if !ret_typ.has_flag(.optional) && !ret_typ.has_flag(.result) {
|
||||
panic('cgen: parameter `ret_typ` of function `expr_with_tmp_var()` must have optional or result')
|
||||
}
|
||||
|
||||
stmt_str := g.go_before_stmt(0).trim_space()
|
||||
styp := g.base_type(ret_typ)
|
||||
g.empty_line = true
|
||||
|
||||
if g.table.sym(expr_typ).kind == .none_ {
|
||||
g.write('${g.typ(ret_typ)} ${tmp_var} = ')
|
||||
g.gen_optional_error(ret_typ, expr)
|
||||
g.writeln(';')
|
||||
} else {
|
||||
g.writeln('${g.typ(ret_typ)} ${tmp_var};')
|
||||
if ret_typ.has_flag(.optional) {
|
||||
g.write('_option_ok(&(${styp}[]) { ')
|
||||
} else {
|
||||
g.write('_result_ok(&(${styp}[]) { ')
|
||||
}
|
||||
g.expr_with_cast(expr, expr_typ, ret_typ)
|
||||
if ret_typ.has_flag(.optional) {
|
||||
g.writeln(' }, (${c.option_name}*)(&${tmp_var}), sizeof(${styp}));')
|
||||
} else {
|
||||
g.writeln(' }, (${c.result_name}*)(&${tmp_var}), sizeof(${styp}));')
|
||||
}
|
||||
}
|
||||
|
||||
g.write(stmt_str)
|
||||
g.write(' ')
|
||||
g.write(tmp_var)
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn (mut g Gen) write_v_source_line_info(pos token.Pos) {
|
||||
if g.inside_ternary == 0 && g.pref.is_vlines && g.is_vlines_enabled {
|
||||
@ -3504,6 +3562,22 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
|
||||
if node.expr_type == 0 {
|
||||
g.checker_bug('unexpected SelectorExpr.expr_type = 0', node.pos)
|
||||
}
|
||||
|
||||
if node.or_block.kind != .absent && !g.is_assign_lhs && g.table.sym(node.typ).kind != .chan {
|
||||
stmt_str := g.go_before_stmt(0).trim_space()
|
||||
styp := g.typ(node.typ)
|
||||
g.empty_line = true
|
||||
tmp_var := g.new_tmp_var()
|
||||
g.write('${styp} ${tmp_var} = ${node.expr}.${node.field_name};')
|
||||
g.or_block(tmp_var, node.or_block, node.typ)
|
||||
g.write(stmt_str)
|
||||
g.write(' ')
|
||||
unwrapped_typ := node.typ.clear_flag(.optional).clear_flag(.result)
|
||||
unwrapped_styp := g.typ(unwrapped_typ)
|
||||
g.write('(*(${unwrapped_styp}*)${tmp_var}.data)')
|
||||
return
|
||||
}
|
||||
|
||||
sym := g.table.sym(g.unwrap_generic(node.expr_type))
|
||||
// if node expr is a root ident and an optional
|
||||
mut is_opt_or_res := node.expr is ast.Ident
|
||||
@ -4122,9 +4196,13 @@ fn (mut g Gen) ident(node ast.Ident) {
|
||||
// `x = new_opt()` => `x = new_opt()` (g.right_is_opt == true)
|
||||
// `println(x)` => `println(*(int*)x.data)`
|
||||
if node.info.is_optional && !(g.is_assign_lhs && g.right_is_opt) {
|
||||
g.write('/*opt*/')
|
||||
styp := g.base_type(node.info.typ)
|
||||
g.write('(*(${styp}*)${name}.data)')
|
||||
if g.inside_opt_or_res {
|
||||
g.write('${name}')
|
||||
} else {
|
||||
g.write('/*opt*/')
|
||||
styp := g.base_type(node.info.typ)
|
||||
g.write('(*(${styp}*)${name}.data)')
|
||||
}
|
||||
return
|
||||
}
|
||||
if !g.is_assign_lhs && node.info.share == .shared_t {
|
||||
|
@ -23,6 +23,13 @@ fn (mut g Gen) dump_expr(node ast.DumpExpr) {
|
||||
g.write('&')
|
||||
g.expr(node.expr)
|
||||
g.write('->val')
|
||||
} else if node.expr_type.has_flag(.optional) || node.expr_type.has_flag(.result) {
|
||||
old_inside_opt_or_res := g.inside_opt_or_res
|
||||
g.inside_opt_or_res = true
|
||||
g.write('(*(${name}*)')
|
||||
g.expr(node.expr)
|
||||
g.write('.data)')
|
||||
g.inside_opt_or_res = old_inside_opt_or_res
|
||||
} else {
|
||||
g.expr(node.expr)
|
||||
}
|
||||
@ -43,7 +50,7 @@ fn (mut g Gen) dump_expr_definitions() {
|
||||
typ := ast.Type(dump_type)
|
||||
is_ptr := typ.is_ptr()
|
||||
deref, _ := deref_kind(str_method_expects_ptr, is_ptr, dump_type)
|
||||
to_string_fn_name := g.get_str_fn(typ.clear_flag(.shared_f))
|
||||
to_string_fn_name := g.get_str_fn(typ.clear_flag(.shared_f).clear_flag(.optional).clear_flag(.result))
|
||||
ptr_asterisk := if is_ptr { '*'.repeat(typ.nr_muls()) } else { '' }
|
||||
mut str_dumparg_type := ''
|
||||
if dump_sym.kind == .none_ {
|
||||
|
@ -52,6 +52,11 @@ fn (mut g Gen) string_inter_literal_sb_optimized(call_expr ast.CallExpr) {
|
||||
}
|
||||
|
||||
fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
|
||||
old_inside_opt_or_res := g.inside_opt_or_res
|
||||
g.inside_opt_or_res = true
|
||||
defer {
|
||||
g.inside_opt_or_res = old_inside_opt_or_res
|
||||
}
|
||||
is_shared := etype.has_flag(.shared_f)
|
||||
mut typ := etype
|
||||
if is_shared {
|
||||
|
@ -181,11 +181,6 @@ fn (mut g Gen) struct_init(node ast.StructInit) {
|
||||
continue
|
||||
}
|
||||
field_name := c_name(field.name)
|
||||
if field.typ.has_flag(.optional) || field.typ.has_flag(.result) {
|
||||
g.write('.${field_name} = {EMPTY_STRUCT_INITIALIZATION},')
|
||||
initialized = true
|
||||
continue
|
||||
}
|
||||
if field.typ in info.embeds {
|
||||
continue
|
||||
}
|
||||
@ -271,6 +266,15 @@ fn (mut g Gen) zero_struct_field(field ast.StructField) bool {
|
||||
g.expr_with_cast(field.default_expr, field.default_expr_typ, field.typ)
|
||||
return true
|
||||
}
|
||||
|
||||
if (field.typ.has_flag(.optional) && !field.default_expr_typ.has_flag(.optional))
|
||||
|| (field.typ.has_flag(.result) && !field.default_expr_typ.has_flag(.result)) {
|
||||
tmp_var := g.new_tmp_var()
|
||||
g.expr_with_tmp_var(field.default_expr, field.default_expr_typ, field.typ,
|
||||
tmp_var)
|
||||
return true
|
||||
}
|
||||
|
||||
g.expr(field.default_expr)
|
||||
} else {
|
||||
g.write(g.type_default(field.typ))
|
||||
@ -340,7 +344,7 @@ fn (mut g Gen) struct_decl(s ast.Struct, name string, is_anon bool) {
|
||||
// write the optional in and then continue
|
||||
// FIXME: for parallel cgen (two different files using the same optional in struct fields)
|
||||
if field.typ.has_flag(.optional) {
|
||||
// Dont use g.typ() here becuase it will register
|
||||
// Dont use g.typ() here because it will register
|
||||
// optional and we dont want that
|
||||
styp, base := g.optional_type_name(field.typ)
|
||||
lock g.done_optionals {
|
||||
@ -354,6 +358,21 @@ fn (mut g Gen) struct_decl(s ast.Struct, name string, is_anon bool) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if field.typ.has_flag(.result) {
|
||||
// Dont use g.typ() here because it will register
|
||||
// result and we dont want that
|
||||
styp, base := g.result_type_name(field.typ)
|
||||
lock g.done_results {
|
||||
if base !in g.done_results {
|
||||
g.done_results << base
|
||||
last_text := g.type_definitions.after(start_pos).clone()
|
||||
g.type_definitions.go_back_to(start_pos)
|
||||
g.typedefs.writeln('typedef struct ${styp} ${styp};')
|
||||
g.type_definitions.writeln('${g.result_type_text(styp, base)};')
|
||||
g.type_definitions.write_string(last_text)
|
||||
}
|
||||
}
|
||||
}
|
||||
type_name := g.typ(field.typ)
|
||||
field_name := c_name(field.name)
|
||||
volatile_prefix := if field.is_volatile { 'volatile ' } else { '' }
|
||||
@ -441,7 +460,14 @@ fn (mut g Gen) struct_init_field(sfield ast.StructInitField, language ast.Langua
|
||||
&& !(sfield.typ.is_ptr() || sfield.typ.is_pointer()) && !sfield.typ.is_number() {
|
||||
g.write('/* autoref */&')
|
||||
}
|
||||
g.expr_with_cast(sfield.expr, sfield.typ, sfield.expected_type)
|
||||
|
||||
if (sfield.expected_type.has_flag(.optional) && !sfield.typ.has_flag(.optional))
|
||||
|| (sfield.expected_type.has_flag(.result) && !sfield.typ.has_flag(.result)) {
|
||||
tmp_var := g.new_tmp_var()
|
||||
g.expr_with_tmp_var(sfield.expr, sfield.typ, sfield.expected_type, tmp_var)
|
||||
} else {
|
||||
g.expr_with_cast(sfield.expr, sfield.typ, sfield.expected_type)
|
||||
}
|
||||
}
|
||||
g.inside_cast_in_heap = inside_cast_in_heap // restore value for further struct inits
|
||||
}
|
||||
|
@ -2935,15 +2935,37 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
|
||||
}
|
||||
}
|
||||
pos := if p.name_error { left.pos().extend(name_pos) } else { name_pos }
|
||||
|
||||
mut or_kind := ast.OrKind.absent
|
||||
mut or_stmts := []ast.Stmt{}
|
||||
mut or_pos := token.Pos{}
|
||||
if p.tok.kind == .key_orelse {
|
||||
or_kind = .block
|
||||
or_stmts, or_pos = p.or_block(.with_err_var)
|
||||
} else if p.tok.kind == .not {
|
||||
or_kind = .propagate_result
|
||||
or_pos = p.tok.pos()
|
||||
p.next()
|
||||
} else if p.tok.kind == .question {
|
||||
or_kind = .propagate_option
|
||||
or_pos = p.tok.pos()
|
||||
p.next()
|
||||
}
|
||||
sel_expr := ast.SelectorExpr{
|
||||
expr: left
|
||||
field_name: field_name
|
||||
pos: pos
|
||||
is_mut: is_mut
|
||||
mut_pos: mut_pos
|
||||
or_block: ast.OrExpr{
|
||||
kind: or_kind
|
||||
stmts: or_stmts
|
||||
pos: or_pos
|
||||
}
|
||||
scope: p.scope
|
||||
next_token: p.tok.kind
|
||||
}
|
||||
|
||||
if is_filter {
|
||||
p.close_scope()
|
||||
}
|
||||
|
@ -201,6 +201,7 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
|
||||
mut typ := ast.Type(0)
|
||||
mut type_pos := token.Pos{}
|
||||
mut field_pos := token.Pos{}
|
||||
mut optional_pos := token.Pos{}
|
||||
mut anon_struct_decl := ast.StructDecl{}
|
||||
if is_embed {
|
||||
// struct embedding
|
||||
@ -260,6 +261,9 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
|
||||
}
|
||||
type_pos = p.prev_tok.pos()
|
||||
field_pos = field_start_pos.extend(type_pos)
|
||||
if typ.has_flag(.optional) || typ.has_flag(.result) {
|
||||
optional_pos = p.peek_token(-2).pos()
|
||||
}
|
||||
}
|
||||
// Comments after type (same line)
|
||||
comments << p.eat_comments()
|
||||
@ -292,6 +296,7 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
|
||||
typ: typ
|
||||
pos: field_pos
|
||||
type_pos: type_pos
|
||||
optional_pos: optional_pos
|
||||
comments: comments
|
||||
i: i
|
||||
default_expr: default_expr
|
||||
@ -311,6 +316,7 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
|
||||
typ: typ
|
||||
pos: field_pos
|
||||
type_pos: type_pos
|
||||
optional_pos: optional_pos
|
||||
comments: comments
|
||||
i: i
|
||||
default_expr: default_expr
|
||||
|
@ -1,5 +1,5 @@
|
||||
vlib/v/parser/tests/type_alias_same_type_err.vv:1:1: error: a type alias can not refer to itself: Foo
|
||||
1 | type Foo = Foo
|
||||
| ~~~~~~~~~~~~~~
|
||||
2 |
|
||||
2 |
|
||||
3 | fn main() {
|
||||
|
22
vlib/v/tests/inout/struct_field_optional.out
Normal file
22
vlib/v/tests/inout/struct_field_optional.out
Normal file
@ -0,0 +1,22 @@
|
||||
1
|
||||
[vlib/v/tests/inout/struct_field_optional.vv:11] f.bar: 1
|
||||
2
|
||||
[vlib/v/tests/inout/struct_field_optional.vv:18] f.bar: 2
|
||||
3
|
||||
[vlib/v/tests/inout/struct_field_optional.vv:22] f.bar: 3
|
||||
3
|
||||
[vlib/v/tests/inout/struct_field_optional.vv:26] a: 3
|
||||
9999
|
||||
[vlib/v/tests/inout/struct_field_optional.vv:29] b: 9999
|
||||
4
|
||||
[vlib/v/tests/inout/struct_field_optional.vv:33] sum: 4
|
||||
4
|
||||
[vlib/v/tests/inout/struct_field_optional.vv:36] sum: 4
|
||||
Foo{
|
||||
bar: 3
|
||||
baz: 0
|
||||
}
|
||||
[vlib/v/tests/inout/struct_field_optional.vv:39] f: Foo{
|
||||
bar: 3
|
||||
baz: 0
|
||||
}
|
40
vlib/v/tests/inout/struct_field_optional.vv
Normal file
40
vlib/v/tests/inout/struct_field_optional.vv
Normal file
@ -0,0 +1,40 @@
|
||||
struct Foo {
|
||||
mut:
|
||||
bar ?int = 1
|
||||
baz ?int = none
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// `default value` test
|
||||
mut f := Foo{}
|
||||
println(f.bar?)
|
||||
dump(f.bar?)
|
||||
// `init` test
|
||||
f = Foo{
|
||||
bar: 2
|
||||
baz: none
|
||||
}
|
||||
println(f.bar?)
|
||||
dump(f.bar?)
|
||||
// `assign` test
|
||||
f.bar = 3
|
||||
println(f.bar?)
|
||||
dump(f.bar?)
|
||||
// `or block` test
|
||||
a := f.bar or { 123 }
|
||||
println(a)
|
||||
dump(a)
|
||||
b := f.baz or { 9999 }
|
||||
println(b)
|
||||
dump(b)
|
||||
// `infix expr` test
|
||||
mut sum := f.bar? + 1
|
||||
println(sum)
|
||||
dump(sum)
|
||||
sum = f.bar or { 123 } + 1
|
||||
println(sum)
|
||||
dump(sum)
|
||||
// others test
|
||||
println(f)
|
||||
dump(f)
|
||||
}
|
Loading…
Reference in New Issue
Block a user