diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 46dec82d1b..eea9bae317 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -749,12 +749,18 @@ pub: // New implementation of sum types pub struct SumTypeDecl { pub: - name string - is_pub bool - pos token.Position - comments []Comment + name string + is_pub bool + pos token.Position + comments []Comment pub mut: - sub_types []table.Type + variants []SumTypeVariant +} + +pub struct SumTypeVariant { +pub: + typ table.Type + pos token.Position } pub struct FnTypeDecl { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index bcd0acd844..aa633b1bdd 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -313,8 +313,11 @@ pub fn (mut c Checker) type_decl(node ast.TypeDecl) { } ast.SumTypeDecl { c.check_valid_pascal_case(node.name, 'sum type', node.pos) - for typ in node.sub_types { - mut sym := c.table.get_type_symbol(typ) + for variant in node.variants { + if variant.typ.is_ptr() { + c.error('sum type cannot hold a reference type', variant.pos) + } + mut sym := c.table.get_type_symbol(variant.typ) if sym.kind == .placeholder { c.error("type `$sym.source_name` doesn't exist", node.pos) } else if sym.kind == .interface_ { diff --git a/vlib/v/checker/tests/sum_type_ref_variant_err.out b/vlib/v/checker/tests/sum_type_ref_variant_err.out new file mode 100644 index 0000000000..d893a2b13e --- /dev/null +++ b/vlib/v/checker/tests/sum_type_ref_variant_err.out @@ -0,0 +1,18 @@ +vlib/v/checker/tests/sum_type_ref_variant_err.vv:7:33: error: sum type cannot hold a reference type + 5 | foo string + 6 | } + 7 | type Alphabet1 = Abc | string | &Xyz + | ~~~~ + 8 | type Alphabet2 = Abc | &Xyz | string + 9 | type Alphabet3 = &Xyz | Abc | string +vlib/v/checker/tests/sum_type_ref_variant_err.vv:8:24: error: sum type cannot hold a reference type + 6 | } + 7 | type Alphabet1 = Abc | string | &Xyz + 8 | type Alphabet2 = Abc | &Xyz | string + | ~~~~ + 9 | type Alphabet3 = &Xyz | Abc | string +vlib/v/checker/tests/sum_type_ref_variant_err.vv:9:18: error: sum type cannot hold a reference type + 7 | type Alphabet1 = Abc | string | &Xyz + 8 | type Alphabet2 = Abc | &Xyz | string + 9 | type Alphabet3 = &Xyz | Abc | string + | ~~~~ \ No newline at end of file diff --git a/vlib/v/checker/tests/sum_type_ref_variant_err.vv b/vlib/v/checker/tests/sum_type_ref_variant_err.vv new file mode 100644 index 0000000000..def2a54995 --- /dev/null +++ b/vlib/v/checker/tests/sum_type_ref_variant_err.vv @@ -0,0 +1,9 @@ +struct Abc { + val string +} +struct Xyz { + foo string +} +type Alphabet1 = Abc | string | &Xyz +type Alphabet2 = Abc | &Xyz | string +type Alphabet3 = &Xyz | Abc | string \ No newline at end of file diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index b8932f5584..42dce6ef57 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -547,8 +547,8 @@ pub fn (mut f Fmt) type_decl(node ast.TypeDecl) { } f.write('type $node.name = ') mut sum_type_names := []string{} - for t in node.sub_types { - sum_type_names << f.table.type_to_str(t) + for t in node.variants { + sum_type_names << f.table.type_to_str(t.typ) } sum_type_names.sort() for i, name in sum_type_names { diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index b43af05890..7cd9aba694 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -372,7 +372,7 @@ pub fn (mut g Gen) write_typeof_functions() { if typ.kind == .sum_type { sum_info := typ.info as table.SumType tidx := g.table.find_type_idx(typ.name) - g.writeln('char * v_typeof_unionsumtype_${tidx}(int sidx) { /* $typ.name */ ') + g.writeln('char * v_typeof_sumtype_${tidx}(int sidx) { /* $typ.name */ ') g.writeln(' switch(sidx) {') g.writeln(' case $tidx: return "${util.strip_main_name(typ.name)}";') for v in sum_info.variants { @@ -1250,7 +1250,7 @@ fn (mut g Gen) for_in(it ast.ForInStmt) { } // use instead of expr() when you need to cast to union sum type (can add other casts also) -fn (mut g Gen) union_expr_with_cast(expr ast.Expr, got_type table.Type, expected_type table.Type) { +fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type table.Type, expected_type table.Type) { // cast to sum type if expected_type != table.void_type { expected_is_ptr := expected_type.is_ptr() @@ -1322,64 +1322,6 @@ fn (mut g Gen) union_expr_with_cast(expr ast.Expr, got_type table.Type, expected g.expr(expr) } -// use instead of expr() when you need to cast to sum type (can add other casts also) -fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type table.Type, expected_type table.Type) { - sym := g.table.get_type_symbol(expected_type) - if sym.kind == .sum_type { - g.union_expr_with_cast(expr, got_type, expected_type) - return - } - // cast to sum type - if expected_type != table.void_type { - expected_is_ptr := expected_type.is_ptr() - expected_deref_type := if expected_is_ptr { expected_type.deref() } else { expected_type } - got_is_ptr := got_type.is_ptr() - got_deref_type := if got_is_ptr { got_type.deref() } else { got_type } - if g.table.sumtype_has_variant(expected_deref_type, got_deref_type) { - exp_styp := g.typ(expected_type) - got_styp := g.typ(got_type) - got_idx := got_type.idx() - got_sym := g.table.get_type_symbol(got_type) - if expected_is_ptr && got_is_ptr { - exp_der_styp := g.typ(expected_deref_type) - g.write('/* sum type cast */ ($exp_styp) memdup(&($exp_der_styp){._object = ') - g.expr(expr) - g.write(', .typ = $got_idx /* $got_sym.name */}, sizeof($exp_der_styp))') - } else if expected_is_ptr { - exp_der_styp := g.typ(expected_deref_type) - g.write('/* sum type cast */ ($exp_styp) memdup(&($exp_der_styp){._object = memdup(&($got_styp[]) {') - g.expr(expr) - g.write('}, sizeof($got_styp)), .typ = $got_idx /* $got_sym.name */}, sizeof($exp_der_styp))') - } else if got_is_ptr { - g.write('/* sum type cast */ ($exp_styp) {._object = ') - g.expr(expr) - g.write(', .typ = $got_idx /* $got_sym.name */}') - } else { - g.write('/* sum type cast */ ($exp_styp) {._object = memdup(&($got_styp[]) {') - g.expr(expr) - g.write('}, sizeof($got_styp)), .typ = $got_idx /* $got_sym.name */}') - } - return - } - } - // Generic dereferencing logic - expected_sym := g.table.get_type_symbol(expected_type) - got_is_ptr := got_type.is_ptr() - expected_is_ptr := expected_type.is_ptr() - neither_void := table.voidptr_type !in [got_type, expected_type] - if got_is_ptr && !expected_is_ptr && neither_void && expected_sym.kind !in [.interface_, .placeholder] { - got_deref_type := got_type.deref() - deref_sym := g.table.get_type_symbol(got_deref_type) - deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx] - got_is_opt := got_type.has_flag(.optional) - if deref_will_match || got_is_opt { - g.write('*') - } - } - // no cast - g.expr(expr) -} - // cestring returns a V string, properly escaped for embeddeding in a C string literal. fn cestring(s string) string { return s.replace('\\', '\\\\').replace('"', "\'") @@ -2646,7 +2588,7 @@ fn (mut g Gen) typeof_expr(node ast.TypeOf) { // When encountering a .sum_type, typeof() should be done at runtime, // because the subtype of the expression may change: sum_type_idx := node.expr_type.idx() - g.write('tos3( /* $sym.name */ v_typeof_unionsumtype_${sum_type_idx}( (') + g.write('tos3( /* $sym.name */ v_typeof_sumtype_${sum_type_idx}( (') g.expr(node.expr) g.write(').typ ))') } else if sym.kind == .array_fixed { diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index c4dd694c9c..70b5f63ad1 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -357,7 +357,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) { } } if left_sym.kind == .sum_type && node.name == 'type_name' { - g.write('tos3( /* $left_sym.name */ v_typeof_unionsumtype_${node.receiver_type}( (') + g.write('tos3( /* $left_sym.name */ v_typeof_sumtype_${node.receiver_type}( (') g.expr(node.left) g.write(').typ ))') return diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index c05f8a7ba9..80cbc34609 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -1922,8 +1922,9 @@ fn (mut p Parser) type_decl() ast.TypeDecl { p.error_with_pos('single letter capital names are reserved for generic template types.', decl_pos) } - mut sum_variants := []table.Type{} + mut sum_variants := []ast.SumTypeVariant{} p.check(.assign) + mut type_pos := p.tok.position() mut comments := []ast.Comment{} if p.tok.kind == .key_fn { // function type: `type mycallback fn(string, int)` @@ -1940,17 +1941,31 @@ fn (mut p Parser) type_decl() ast.TypeDecl { } first_type := p.parse_type() // need to parse the first type before we can check if it's `type A = X | Y` 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 << first_type + sum_variants << ast.SumTypeVariant{ + typ: first_type + pos: type_pos + } // type SumType = A | B | c for { + type_pos = p.tok.position() variant_type := p.parse_type() - sum_variants << variant_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.SumTypeVariant{ + typ: variant_type + pos: type_pos + } if p.tok.kind != .pipe { break } p.check(.pipe) } + variant_types := sum_variants.map(it.typ) prepend_mod_name := p.prepend_mod(name) p.table.register_type_symbol(table.TypeSymbol{ kind: .sum_type @@ -1958,7 +1973,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl { source_name: prepend_mod_name mod: p.mod info: table.SumType{ - variants: sum_variants + variants: variant_types } is_public: is_pub }) @@ -1966,7 +1981,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl { return ast.SumTypeDecl{ name: name is_pub: is_pub - sub_types: sum_variants + variants: sum_variants pos: decl_pos comments: comments }