From 35282396ecc13a3ddf9e25b62f7e35d1348f8b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=C3=A4schle?= Date: Sat, 25 Dec 2021 16:26:40 +0100 Subject: [PATCH] all: inline sum types (#12912) --- vlib/v/ast/ast.v | 2 +- vlib/v/ast/types.v | 7 +- vlib/v/fmt/tests/inline_sum_type_keep.vv | 13 ++ vlib/v/gen/c/auto_str_methods.v | 24 ++- vlib/v/parser/fn.v | 4 + vlib/v/parser/parse_type.v | 102 ++++++++++-- vlib/v/parser/parser.v | 156 ++++++++---------- .../parser/tests/anon_sum_type_interface.out | 0 .../v/parser/tests/anon_sum_type_interface.vv | 3 + .../tests/anon_sum_type_multi_return_err.out | 5 + .../tests/anon_sum_type_multi_return_err.vv | 3 + .../tests/anon_sum_type_receiver_err.out | 5 + .../tests/anon_sum_type_receiver_err.vv | 3 + vlib/v/parser/tests/anon_sum_type_struct.out | 0 vlib/v/parser/tests/anon_sum_type_struct.vv | 3 + .../tests/inline_sum_type_optional_err.out | 5 + .../tests/inline_sum_type_optional_err.vv | 3 + ...sum_type_return_type_too_many_variants.out | 21 +++ ..._sum_type_return_type_too_many_variants.vv | 9 + .../parser/tests/named_sum_type_none_err.out | 3 + .../v/parser/tests/named_sum_type_none_err.vv | 1 + .../tests/option_sum_type_return_err.out | 5 + .../tests/option_sum_type_return_err.vv | 3 + vlib/v/tests/anon_sum_type_test.v | 41 +++++ vlib/v/tests/sum_type_with_none_type_test.v | 5 - 25 files changed, 312 insertions(+), 114 deletions(-) create mode 100644 vlib/v/fmt/tests/inline_sum_type_keep.vv create mode 100644 vlib/v/parser/tests/anon_sum_type_interface.out create mode 100644 vlib/v/parser/tests/anon_sum_type_interface.vv create mode 100644 vlib/v/parser/tests/anon_sum_type_multi_return_err.out create mode 100644 vlib/v/parser/tests/anon_sum_type_multi_return_err.vv create mode 100644 vlib/v/parser/tests/anon_sum_type_receiver_err.out create mode 100644 vlib/v/parser/tests/anon_sum_type_receiver_err.vv create mode 100644 vlib/v/parser/tests/anon_sum_type_struct.out create mode 100644 vlib/v/parser/tests/anon_sum_type_struct.vv create mode 100644 vlib/v/parser/tests/inline_sum_type_optional_err.out create mode 100644 vlib/v/parser/tests/inline_sum_type_optional_err.vv create mode 100644 vlib/v/parser/tests/inline_sum_type_return_type_too_many_variants.out create mode 100644 vlib/v/parser/tests/inline_sum_type_return_type_too_many_variants.vv create mode 100644 vlib/v/parser/tests/named_sum_type_none_err.out create mode 100644 vlib/v/parser/tests/named_sum_type_none_err.vv create mode 100644 vlib/v/parser/tests/option_sum_type_return_err.out create mode 100644 vlib/v/parser/tests/option_sum_type_return_err.vv create mode 100644 vlib/v/tests/anon_sum_type_test.v delete mode 100644 vlib/v/tests/sum_type_with_none_type_test.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 3d82b6ea1c..8ab3a8c3fd 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -1083,7 +1083,7 @@ pub: comments []Comment } -// New implementation of sum types +// SumTypeDecl is the ast node for `type MySumType = string | int` pub struct SumTypeDecl { pub: name string diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index 93c0befb8e..2a9fcbfc69 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -918,6 +918,7 @@ pub: pub mut: fields []StructField found_fields bool + is_anon bool // generic sumtype support is_generic bool generic_types []Type @@ -1048,6 +1049,10 @@ pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string] } else {} } + } else if sym.info is SumType && (sym.info as SumType).is_anon { + variant_names := sym.info.variants.map(t.shorten_user_defined_typenames(t.sym(it).name, + import_aliases)) + res = '${variant_names.join(' | ')}' } else { res = t.shorten_user_defined_typenames(res, import_aliases) } @@ -1089,7 +1094,7 @@ pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string] res = strings.repeat(`&`, nr_muls) + res } if typ.has_flag(.optional) { - res = '?' + res + res = '?$res' } return res } diff --git a/vlib/v/fmt/tests/inline_sum_type_keep.vv b/vlib/v/fmt/tests/inline_sum_type_keep.vv new file mode 100644 index 0000000000..32f79123f6 --- /dev/null +++ b/vlib/v/fmt/tests/inline_sum_type_keep.vv @@ -0,0 +1,13 @@ +import v.token + +struct Foo { + bar string | int +} + +interface Egg { + milk string | int +} + +fn foo(bar string | int) int | string | token.Position { + return 1 +} diff --git a/vlib/v/gen/c/auto_str_methods.v b/vlib/v/gen/c/auto_str_methods.v index 87d3d3bdce..dffb4aa9c5 100644 --- a/vlib/v/gen/c/auto_str_methods.v +++ b/vlib/v/gen/c/auto_str_methods.v @@ -434,16 +434,22 @@ fn (mut g Gen) gen_str_for_union_sum_type(info ast.SumType, styp string, str_fn_ g.type_definitions.writeln('static string indent_${str_fn_name}($styp x, int indent_count); // auto') mut fn_builder := strings.new_builder(512) fn_builder.writeln('static string indent_${str_fn_name}($styp x, int indent_count) {') - mut clean_sum_type_v_type_name := styp.replace('__', '.') - if styp.ends_with('*') { - clean_sum_type_v_type_name = '&' + clean_sum_type_v_type_name.replace('*', '') + mut clean_sum_type_v_type_name := '' + if info.is_anon { + variant_names := info.variants.map(g.table.sym(it).name) + clean_sum_type_v_type_name = '(${variant_names.join(' | ')})' + } else { + clean_sum_type_v_type_name = styp.replace('__', '.') + if styp.ends_with('*') { + clean_sum_type_v_type_name = '&' + clean_sum_type_v_type_name.replace('*', '') + } + if clean_sum_type_v_type_name.contains('_T_') { + clean_sum_type_v_type_name = + clean_sum_type_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') + + '>' + } + clean_sum_type_v_type_name = util.strip_main_name(clean_sum_type_v_type_name) } - if clean_sum_type_v_type_name.contains('_T_') { - clean_sum_type_v_type_name = - clean_sum_type_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') + - '>' - } - clean_sum_type_v_type_name = util.strip_main_name(clean_sum_type_v_type_name) fn_builder.writeln('\tswitch(x._typ) {') for typ in info.variants { typ_str := g.typ(typ) diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 1f2dcdea67..7137a384fc 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -539,6 +539,10 @@ fn (mut p Parser) fn_decl() ast.FnDecl { } fn (mut p Parser) fn_receiver(mut params []ast.Param, mut rec ReceiverParsingInfo) ? { + p.inside_receiver_param = true + defer { + p.inside_receiver_param = false + } lpar_pos := p.tok.position() p.next() // ( is_shared := p.tok.kind == .key_shared diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 606492a858..32a741682c 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -8,6 +8,10 @@ import v.ast import v.util import v.token +const ( + maximum_inline_sum_type_variants = 3 +) + pub fn (mut p Parser) parse_array_type(expecting token.Kind) ast.Type { p.check(expecting) // fixed array @@ -283,9 +287,74 @@ pub fn (mut p Parser) parse_language() ast.Language { return language } +// parse_inline_sum_type parses the type and registers it in case the type is an anonymous sum type. +// It also takes care of inline sum types where parse_type only parses a standalone type. +pub fn (mut p Parser) parse_inline_sum_type() ast.Type { + variants := p.parse_sum_type_variants() + if variants.len > 1 { + if variants.len > parser.maximum_inline_sum_type_variants { + pos := variants[0].pos.extend(variants[variants.len - 1].pos) + p.warn_with_pos('an inline sum type expects a maximum of $parser.maximum_inline_sum_type_variants types ($variants.len were given)', + pos) + } + mut variant_names := variants.map(p.table.sym(it.typ).name) + variant_names.sort() + // deterministic name + name := '_v_anon_sum_type_${variant_names.join('_')}' + variant_types := variants.map(it.typ) + prepend_mod_name := p.prepend_mod(name) + mut idx := p.table.find_type_idx(prepend_mod_name) + if idx > 0 { + return ast.new_type(idx) + } + idx = p.table.register_type_symbol(ast.TypeSymbol{ + kind: .sum_type + name: prepend_mod_name + cname: util.no_dots(prepend_mod_name) + mod: p.mod + info: ast.SumType{ + is_anon: true + variants: variant_types + } + }) + return ast.new_type(idx) + } else if variants.len == 1 { + return variants[0].typ + } + return ast.Type(0) +} + +// parse_sum_type_variants parses several types separated with a pipe and returns them as a list with at least one node. +// If there is less than one node, it will add an error to the error list. +pub fn (mut p Parser) parse_sum_type_variants() []ast.TypeNode { + p.inside_sum_type = true + defer { + p.inside_sum_type = false + } + mut types := []ast.TypeNode{} + for { + type_start_pos := p.tok.position() + typ := p.parse_type() + // TODO: needs to be its own var, otherwise TCC fails because of a known stack error + prev_tok := p.prev_tok + type_end_pos := prev_tok.position() + type_pos := type_start_pos.extend(type_end_pos) + types << ast.TypeNode{ + typ: typ + pos: type_pos + } + if p.tok.kind != .pipe { + break + } + p.check(.pipe) + } + return types +} + pub fn (mut p Parser) parse_type() ast.Type { // optional mut is_optional := false + optional_pos := p.tok.position() if p.tok.kind == .question { line_nr := p.tok.line_nr p.next() @@ -333,6 +402,10 @@ pub fn (mut p Parser) parse_type() ast.Type { p.error_with_pos('use `?` instead of `?void`', pos) return 0 } + sym := p.table.sym(typ) + if is_optional && sym.info is ast.SumType && (sym.info as ast.SumType).is_anon { + p.error_with_pos('an inline sum type cannot be optional', optional_pos.extend(p.prev_tok.position())) + } } if is_optional { typ = typ.set_flag(.optional) @@ -363,7 +436,6 @@ pub fn (mut p Parser) parse_any_type(language ast.Language, is_ptr bool, check_d name = 'JS.$name' } else if p.peek_tok.kind == .dot && check_dot { // `module.Type` - // /if !(p.tok.lit in p.table.imports) { mut mod := name mut mod_pos := p.tok.position() p.next() @@ -394,7 +466,7 @@ pub fn (mut p Parser) parse_any_type(language ast.Language, is_ptr bool, check_d p.error('imported types must start with a capital letter') return 0 } - } else if p.expr_mod != '' && !p.in_generic_params { // p.expr_mod is from the struct and not from the generic parameter + } else if p.expr_mod != '' && !p.inside_generic_params { // p.expr_mod is from the struct and not from the generic parameter name = p.expr_mod + '.' + name } else if name in p.imported_symbols { name = p.imported_symbols[name] @@ -402,7 +474,6 @@ pub fn (mut p Parser) parse_any_type(language ast.Language, is_ptr bool, check_d // `Foo` in module `mod` means `mod.Foo` name = p.mod + '.' + name } - // p.warn('get type $name') match p.tok.kind { .key_fn { // func @@ -412,16 +483,19 @@ pub fn (mut p Parser) parse_any_type(language ast.Language, is_ptr bool, check_d // array return p.parse_array_type(p.tok.kind) } - .lpar { - // multiple return - if is_ptr { - p.error('parse_type: unexpected `&` before multiple returns') - return 0 - } - return p.parse_multi_return_type() - } else { - // no p.next() + if p.tok.kind == .lpar && !p.inside_sum_type { + // multiple return + if is_ptr { + p.error('parse_type: unexpected `&` before multiple returns') + return 0 + } + return p.parse_multi_return_type() + } + if ((p.peek_tok.kind == .dot && p.peek_token(3).kind == .pipe) + || p.peek_tok.kind == .pipe) && !p.inside_sum_type && !p.inside_receiver_param { + return p.parse_inline_sum_type() + } if name == 'map' { return p.parse_map_type() } @@ -541,7 +615,7 @@ pub fn (mut p Parser) parse_generic_inst_type(name string) ast.Type { mut bs_cname := name start_pos := p.tok.position() p.next() - p.in_generic_params = true + p.inside_generic_params = true bs_name += '<' bs_cname += '_T_' mut concrete_types := []ast.Type{} @@ -564,7 +638,7 @@ pub fn (mut p Parser) parse_generic_inst_type(name string) ast.Type { } concrete_types_pos := start_pos.extend(p.tok.position()) p.check(.gt) - p.in_generic_params = false + p.inside_generic_params = false bs_name += '>' // fmt operates on a per-file basis, so is_instance might be not set correctly. Thus it's ignored. if (is_instance || p.pref.is_fmt) && concrete_types.len > 0 { diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 138825ad41..b7aa9b5312 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -26,64 +26,66 @@ mut: scanner &scanner.Scanner comments_mode scanner.CommentsMode = .skip_comments // see comment in parse_file - tok token.Token - prev_tok token.Token - peek_tok token.Token - table &ast.Table - language ast.Language - fn_language ast.Language // .c for `fn C.abcd()` declarations - inside_vlib_file bool // true for all vlib/ files - inside_test_file bool // when inside _test.v or _test.vv file - inside_if bool - inside_if_expr bool - inside_ct_if_expr bool - inside_or_expr bool - inside_for bool - inside_fn bool // true even with implicit main - inside_unsafe_fn bool - inside_str_interp bool - inside_array_lit bool - inside_in_array bool - or_is_handled bool // ignore `or` in this expression - builtin_mod bool // are we in the `builtin` module? - mod string // current module name - is_manualfree bool // true when `[manualfree] module abc`, makes *all* fns in the current .v file, opt out of autofree - has_globals bool // `[has_globals] module abc` - allow globals declarations, even without -enable-globals, in that single .v file __only__ - is_generated bool // `[generated] module abc` - turn off compiler notices for that single .v file __only__. - attrs []ast.Attr // attributes before next decl stmt - expr_mod string // for constructing full type names in parse_type() - scope &ast.Scope - imports map[string]string // alias => mod_name - ast_imports []ast.Import // mod_names - used_imports []string // alias - auto_imports []string // imports, the user does not need to specify - imported_symbols map[string]string - is_amp bool // for generating the right code for `&Foo{}` - returns bool - inside_match bool // to separate `match A { }` from `Struct{}` - inside_select bool // to allow `ch <- Struct{} {` inside `select` - inside_match_case bool // to separate `match_expr { }` from `Struct{}` - inside_match_body bool // to fix eval not used TODO - inside_unsafe bool - is_stmt_ident bool // true while the beginning of a statement is an ident/selector - expecting_type bool // `is Type`, expecting type - errors []errors.Error - warnings []errors.Warning - notices []errors.Notice - vet_errors []vet.Error - cur_fn_name string - label_names []string - in_generic_params bool // indicates if parsing between `<` and `>` of a method/function - name_error bool // indicates if the token is not a name or the name is on another line - n_asm int // controls assembly labels - inside_asm_template bool - inside_asm bool - global_labels []string - inside_defer bool - comptime_if_cond bool - defer_vars []ast.Ident - should_abort bool // when too many errors/warnings/notices are accumulated, should_abort becomes true, and the parser should stop - codegen_text string + tok token.Token + prev_tok token.Token + peek_tok token.Token + table &ast.Table + language ast.Language + fn_language ast.Language // .c for `fn C.abcd()` declarations + inside_vlib_file bool // true for all vlib/ files + inside_test_file bool // when inside _test.v or _test.vv file + inside_if bool + inside_if_expr bool + inside_ct_if_expr bool + inside_or_expr bool + inside_for bool + inside_fn bool // true even with implicit main + inside_unsafe_fn bool + inside_str_interp bool + inside_array_lit bool + inside_in_array bool + inside_match bool // to separate `match A { }` from `Struct{}` + inside_select bool // to allow `ch <- Struct{} {` inside `select` + inside_match_case bool // to separate `match_expr { }` from `Struct{}` + inside_match_body bool // to fix eval not used TODO + inside_unsafe bool + inside_sum_type bool // to prevent parsing inline sum type again + inside_asm_template bool + inside_asm bool + inside_defer bool + inside_generic_params bool // indicates if parsing between `<` and `>` of a method/function + inside_receiver_param bool // indicates if parsing the receiver parameter inside the first `(` and `)` of a method + or_is_handled bool // ignore `or` in this expression + builtin_mod bool // are we in the `builtin` module? + mod string // current module name + is_manualfree bool // true when `[manualfree] module abc`, makes *all* fns in the current .v file, opt out of autofree + has_globals bool // `[has_globals] module abc` - allow globals declarations, even without -enable-globals, in that single .v file __only__ + is_generated bool // `[generated] module abc` - turn off compiler notices for that single .v file __only__. + attrs []ast.Attr // attributes before next decl stmt + expr_mod string // for constructing full type names in parse_type() + scope &ast.Scope + imports map[string]string // alias => mod_name + ast_imports []ast.Import // mod_names + used_imports []string // alias + auto_imports []string // imports, the user does not need to specify + imported_symbols map[string]string + is_amp bool // for generating the right code for `&Foo{}` + returns bool + is_stmt_ident bool // true while the beginning of a statement is an ident/selector + expecting_type bool // `is Type`, expecting type + errors []errors.Error + warnings []errors.Warning + notices []errors.Notice + vet_errors []vet.Error + cur_fn_name string + label_names []string + name_error bool // indicates if the token is not a name or the name is on another line + n_asm int // controls assembly labels + global_labels []string + comptime_if_cond bool + defer_vars []ast.Ident + should_abort bool // when too many errors/warnings/notices are accumulated, should_abort becomes true, and the parser should stop + codegen_text string } // for tests @@ -3419,32 +3421,16 @@ fn (mut p Parser) type_decl() ast.TypeDecl { comments: comments } } - first_type := p.parse_type() // need to parse the first type before we can check if it's `type A = X | Y` - type_alias_pos := p.tok.position() - if p.tok.kind == .pipe { - mut type_end_pos := p.prev_tok.position() - type_pos = type_pos.extend(type_end_pos) - p.next() - sum_variants << ast.TypeNode{ - typ: first_type - pos: type_pos - } - // type SumType = A | B | c - for { - type_pos = p.tok.position() - variant_type := p.parse_type() - // TODO: needs to be its own var, otherwise TCC fails because of a known stack error - prev_tok := p.prev_tok - type_end_pos = prev_tok.position() - type_pos = type_pos.extend(type_end_pos) - sum_variants << ast.TypeNode{ - typ: variant_type - pos: type_pos + sum_variants << p.parse_sum_type_variants() + // type SumType = A | B | c + if sum_variants.len > 1 { + for variant in sum_variants { + variant_sym := p.table.sym(variant.typ) + // TODO: implement this check for error too + if variant_sym.kind == .none_ { + p.error_with_pos('named sum type cannot have none as its variant', variant.pos) + return ast.AliasTypeDecl{} } - if p.tok.kind != .pipe { - break - } - p.check(.pipe) } variant_types := sum_variants.map(it.typ) prepend_mod_name := p.prepend_mod(name) @@ -3481,7 +3467,8 @@ fn (mut p Parser) type_decl() ast.TypeDecl { p.error_with_pos('generic type aliases are not yet implemented', decl_pos_with_generics) return ast.AliasTypeDecl{} } - parent_type := first_type + // sum_variants will have only one element + parent_type := sum_variants[0].typ parent_sym := p.table.sym(parent_type) pidx := parent_type.idx() p.check_for_impure_v(parent_sym.language, decl_pos) @@ -3505,6 +3492,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl { return ast.AliasTypeDecl{} } if idx == pidx { + type_alias_pos := sum_variants[0].pos p.error_with_pos('a type alias can not refer to itself: $name', decl_pos.extend(type_alias_pos)) return ast.AliasTypeDecl{} } diff --git a/vlib/v/parser/tests/anon_sum_type_interface.out b/vlib/v/parser/tests/anon_sum_type_interface.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/vlib/v/parser/tests/anon_sum_type_interface.vv b/vlib/v/parser/tests/anon_sum_type_interface.vv new file mode 100644 index 0000000000..a2c4c1a8ab --- /dev/null +++ b/vlib/v/parser/tests/anon_sum_type_interface.vv @@ -0,0 +1,3 @@ +interface Foo { + bar string | int +} diff --git a/vlib/v/parser/tests/anon_sum_type_multi_return_err.out b/vlib/v/parser/tests/anon_sum_type_multi_return_err.out new file mode 100644 index 0000000000..4d23dd0c11 --- /dev/null +++ b/vlib/v/parser/tests/anon_sum_type_multi_return_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/anon_sum_type_multi_return_err.vv:1:24: error: invalid expression: unexpected token `|` + 1 | fn abc() (string, int) | string { + | ^ + 2 | return '' + 3 | } diff --git a/vlib/v/parser/tests/anon_sum_type_multi_return_err.vv b/vlib/v/parser/tests/anon_sum_type_multi_return_err.vv new file mode 100644 index 0000000000..123143d4f0 --- /dev/null +++ b/vlib/v/parser/tests/anon_sum_type_multi_return_err.vv @@ -0,0 +1,3 @@ +fn abc() (string, int) | string { + return '' +} diff --git a/vlib/v/parser/tests/anon_sum_type_receiver_err.out b/vlib/v/parser/tests/anon_sum_type_receiver_err.out new file mode 100644 index 0000000000..d29a226703 --- /dev/null +++ b/vlib/v/parser/tests/anon_sum_type_receiver_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/anon_sum_type_receiver_err.vv:1:11: error: unexpected token `|`, expecting `)` + 1 | fn (x int | string) baz() { + | ^ + 2 | println('baz') + 3 | } diff --git a/vlib/v/parser/tests/anon_sum_type_receiver_err.vv b/vlib/v/parser/tests/anon_sum_type_receiver_err.vv new file mode 100644 index 0000000000..ca8a4cb9e1 --- /dev/null +++ b/vlib/v/parser/tests/anon_sum_type_receiver_err.vv @@ -0,0 +1,3 @@ +fn (x int | string) baz() { + println('baz') +} diff --git a/vlib/v/parser/tests/anon_sum_type_struct.out b/vlib/v/parser/tests/anon_sum_type_struct.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/vlib/v/parser/tests/anon_sum_type_struct.vv b/vlib/v/parser/tests/anon_sum_type_struct.vv new file mode 100644 index 0000000000..19165d23d9 --- /dev/null +++ b/vlib/v/parser/tests/anon_sum_type_struct.vv @@ -0,0 +1,3 @@ +struct Foo { + bar string | int +} diff --git a/vlib/v/parser/tests/inline_sum_type_optional_err.out b/vlib/v/parser/tests/inline_sum_type_optional_err.out new file mode 100644 index 0000000000..b339f72337 --- /dev/null +++ b/vlib/v/parser/tests/inline_sum_type_optional_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/inline_sum_type_optional_err.vv:1:10: error: an inline sum type cannot be optional + 1 | fn foo() ?string | int { + | ~~~~~~~~~~~~~ + 2 | return 0 + 3 | } diff --git a/vlib/v/parser/tests/inline_sum_type_optional_err.vv b/vlib/v/parser/tests/inline_sum_type_optional_err.vv new file mode 100644 index 0000000000..bdf6861af1 --- /dev/null +++ b/vlib/v/parser/tests/inline_sum_type_optional_err.vv @@ -0,0 +1,3 @@ +fn foo() ?string | int { + return 0 +} diff --git a/vlib/v/parser/tests/inline_sum_type_return_type_too_many_variants.out b/vlib/v/parser/tests/inline_sum_type_return_type_too_many_variants.out new file mode 100644 index 0000000000..7ca1501204 --- /dev/null +++ b/vlib/v/parser/tests/inline_sum_type_return_type_too_many_variants.out @@ -0,0 +1,21 @@ +vlib/v/parser/tests/inline_sum_type_return_type_too_many_variants.vv:4:6: warning: an inline sum type expects a maximum of 3 types (5 were given) + 2 | + 3 | struct Foo { + 4 | bar int | string | token.Position | bool | u32 + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 5 | } + 6 | +vlib/v/parser/tests/inline_sum_type_return_type_too_many_variants.vv:7:12: warning: an inline sum type expects a maximum of 3 types (5 were given) + 5 | } + 6 | + 7 | fn foo(arg int | string | token.Position | bool | u32) int | string | token.Position | bool | u32 { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 8 | return 1 + 9 | } +vlib/v/parser/tests/inline_sum_type_return_type_too_many_variants.vv:7:56: warning: an inline sum type expects a maximum of 3 types (5 were given) + 5 | } + 6 | + 7 | fn foo(arg int | string | token.Position | bool | u32) int | string | token.Position | bool | u32 { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 8 | return 1 + 9 | } diff --git a/vlib/v/parser/tests/inline_sum_type_return_type_too_many_variants.vv b/vlib/v/parser/tests/inline_sum_type_return_type_too_many_variants.vv new file mode 100644 index 0000000000..d3c7764328 --- /dev/null +++ b/vlib/v/parser/tests/inline_sum_type_return_type_too_many_variants.vv @@ -0,0 +1,9 @@ +import v.token + +struct Foo { + bar int | string | token.Position | bool | u32 +} + +fn foo(arg int | string | token.Position | bool | u32) int | string | token.Position | bool | u32 { + return 1 +} diff --git a/vlib/v/parser/tests/named_sum_type_none_err.out b/vlib/v/parser/tests/named_sum_type_none_err.out new file mode 100644 index 0000000000..c95d73d7b8 --- /dev/null +++ b/vlib/v/parser/tests/named_sum_type_none_err.out @@ -0,0 +1,3 @@ +vlib/v/parser/tests/named_sum_type_none_err.vv:1:34: error: named sum type cannot have none as its variant + 1 | type Abc = string | int | bool | none + | ~~~~ diff --git a/vlib/v/parser/tests/named_sum_type_none_err.vv b/vlib/v/parser/tests/named_sum_type_none_err.vv new file mode 100644 index 0000000000..a0c852c417 --- /dev/null +++ b/vlib/v/parser/tests/named_sum_type_none_err.vv @@ -0,0 +1 @@ +type Abc = string | int | bool | none diff --git a/vlib/v/parser/tests/option_sum_type_return_err.out b/vlib/v/parser/tests/option_sum_type_return_err.out new file mode 100644 index 0000000000..227a2ab21d --- /dev/null +++ b/vlib/v/parser/tests/option_sum_type_return_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/option_sum_type_return_err.vv:1:21: error: an inline sum type cannot be optional + 1 | fn option_sumtype() ?string | int { + | ~~~~~~~~~~~~~ + 2 | return 0 + 3 | } diff --git a/vlib/v/parser/tests/option_sum_type_return_err.vv b/vlib/v/parser/tests/option_sum_type_return_err.vv new file mode 100644 index 0000000000..9c4b0a1d62 --- /dev/null +++ b/vlib/v/parser/tests/option_sum_type_return_err.vv @@ -0,0 +1,3 @@ +fn option_sumtype() ?string | int { + return 0 +} diff --git a/vlib/v/tests/anon_sum_type_test.v b/vlib/v/tests/anon_sum_type_test.v new file mode 100644 index 0000000000..5a7b923fd9 --- /dev/null +++ b/vlib/v/tests/anon_sum_type_test.v @@ -0,0 +1,41 @@ +fn returns_sumtype() int | string { + return 1 +} + +fn returns_sumtype_reverse() int | string { + return 1 +} + +fn test_stringification() { + x := returns_sumtype() + y := returns_sumtype_reverse() + assert '$x' == '$y' +} + +struct Milk { + egg int | string +} + +fn test_struct_with_inline_sumtype() { + m := Milk{ + egg: 1 + } + assert m.egg is int +} + +interface IMilk { + egg int | string +} + +fn receive_imilk(milk IMilk) {} + +fn test_interface_with_inline_sumtype() { + m := Milk{ + egg: 1 + } + receive_imilk(m) +} + +fn returns_sumtype_in_multireturn() (int | string, string) { + return 1, '' +} diff --git a/vlib/v/tests/sum_type_with_none_type_test.v b/vlib/v/tests/sum_type_with_none_type_test.v deleted file mode 100644 index 148381fffa..0000000000 --- a/vlib/v/tests/sum_type_with_none_type_test.v +++ /dev/null @@ -1,5 +0,0 @@ -type MapValue = int | none - -fn test_sum_type_with_none_type() { - assert true -}