parser: disallow ptr type as sum type variant (#6996)
parent
6da6a082c7
commit
32c027a0bf
|
@ -754,7 +754,13 @@ pub:
|
||||||
pos token.Position
|
pos token.Position
|
||||||
comments []Comment
|
comments []Comment
|
||||||
pub mut:
|
pub mut:
|
||||||
sub_types []table.Type
|
variants []SumTypeVariant
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SumTypeVariant {
|
||||||
|
pub:
|
||||||
|
typ table.Type
|
||||||
|
pos token.Position
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FnTypeDecl {
|
pub struct FnTypeDecl {
|
||||||
|
|
|
@ -313,8 +313,11 @@ pub fn (mut c Checker) type_decl(node ast.TypeDecl) {
|
||||||
}
|
}
|
||||||
ast.SumTypeDecl {
|
ast.SumTypeDecl {
|
||||||
c.check_valid_pascal_case(node.name, 'sum type', node.pos)
|
c.check_valid_pascal_case(node.name, 'sum type', node.pos)
|
||||||
for typ in node.sub_types {
|
for variant in node.variants {
|
||||||
mut sym := c.table.get_type_symbol(typ)
|
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 {
|
if sym.kind == .placeholder {
|
||||||
c.error("type `$sym.source_name` doesn't exist", node.pos)
|
c.error("type `$sym.source_name` doesn't exist", node.pos)
|
||||||
} else if sym.kind == .interface_ {
|
} else if sym.kind == .interface_ {
|
||||||
|
|
|
@ -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
|
||||||
|
| ~~~~
|
|
@ -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
|
|
@ -547,8 +547,8 @@ pub fn (mut f Fmt) type_decl(node ast.TypeDecl) {
|
||||||
}
|
}
|
||||||
f.write('type $node.name = ')
|
f.write('type $node.name = ')
|
||||||
mut sum_type_names := []string{}
|
mut sum_type_names := []string{}
|
||||||
for t in node.sub_types {
|
for t in node.variants {
|
||||||
sum_type_names << f.table.type_to_str(t)
|
sum_type_names << f.table.type_to_str(t.typ)
|
||||||
}
|
}
|
||||||
sum_type_names.sort()
|
sum_type_names.sort()
|
||||||
for i, name in sum_type_names {
|
for i, name in sum_type_names {
|
||||||
|
|
|
@ -372,7 +372,7 @@ pub fn (mut g Gen) write_typeof_functions() {
|
||||||
if typ.kind == .sum_type {
|
if typ.kind == .sum_type {
|
||||||
sum_info := typ.info as table.SumType
|
sum_info := typ.info as table.SumType
|
||||||
tidx := g.table.find_type_idx(typ.name)
|
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(' switch(sidx) {')
|
||||||
g.writeln(' case $tidx: return "${util.strip_main_name(typ.name)}";')
|
g.writeln(' case $tidx: return "${util.strip_main_name(typ.name)}";')
|
||||||
for v in sum_info.variants {
|
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)
|
// 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
|
// cast to sum type
|
||||||
if expected_type != table.void_type {
|
if expected_type != table.void_type {
|
||||||
expected_is_ptr := expected_type.is_ptr()
|
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)
|
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.
|
// cestring returns a V string, properly escaped for embeddeding in a C string literal.
|
||||||
fn cestring(s string) string {
|
fn cestring(s string) string {
|
||||||
return s.replace('\\', '\\\\').replace('"', "\'")
|
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,
|
// When encountering a .sum_type, typeof() should be done at runtime,
|
||||||
// because the subtype of the expression may change:
|
// because the subtype of the expression may change:
|
||||||
sum_type_idx := node.expr_type.idx()
|
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.expr(node.expr)
|
||||||
g.write(').typ ))')
|
g.write(').typ ))')
|
||||||
} else if sym.kind == .array_fixed {
|
} else if sym.kind == .array_fixed {
|
||||||
|
|
|
@ -357,7 +357,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if left_sym.kind == .sum_type && node.name == 'type_name' {
|
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.expr(node.left)
|
||||||
g.write(').typ ))')
|
g.write(').typ ))')
|
||||||
return
|
return
|
||||||
|
|
|
@ -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.',
|
p.error_with_pos('single letter capital names are reserved for generic template types.',
|
||||||
decl_pos)
|
decl_pos)
|
||||||
}
|
}
|
||||||
mut sum_variants := []table.Type{}
|
mut sum_variants := []ast.SumTypeVariant{}
|
||||||
p.check(.assign)
|
p.check(.assign)
|
||||||
|
mut type_pos := p.tok.position()
|
||||||
mut comments := []ast.Comment{}
|
mut comments := []ast.Comment{}
|
||||||
if p.tok.kind == .key_fn {
|
if p.tok.kind == .key_fn {
|
||||||
// function type: `type mycallback fn(string, int)`
|
// 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`
|
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 {
|
if p.tok.kind == .pipe {
|
||||||
|
mut type_end_pos := p.prev_tok.position()
|
||||||
|
type_pos = type_pos.extend(type_end_pos)
|
||||||
p.next()
|
p.next()
|
||||||
sum_variants << first_type
|
sum_variants << ast.SumTypeVariant{
|
||||||
|
typ: first_type
|
||||||
|
pos: type_pos
|
||||||
|
}
|
||||||
// type SumType = A | B | c
|
// type SumType = A | B | c
|
||||||
for {
|
for {
|
||||||
|
type_pos = p.tok.position()
|
||||||
variant_type := p.parse_type()
|
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 {
|
if p.tok.kind != .pipe {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
p.check(.pipe)
|
p.check(.pipe)
|
||||||
}
|
}
|
||||||
|
variant_types := sum_variants.map(it.typ)
|
||||||
prepend_mod_name := p.prepend_mod(name)
|
prepend_mod_name := p.prepend_mod(name)
|
||||||
p.table.register_type_symbol(table.TypeSymbol{
|
p.table.register_type_symbol(table.TypeSymbol{
|
||||||
kind: .sum_type
|
kind: .sum_type
|
||||||
|
@ -1958,7 +1973,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
|
||||||
source_name: prepend_mod_name
|
source_name: prepend_mod_name
|
||||||
mod: p.mod
|
mod: p.mod
|
||||||
info: table.SumType{
|
info: table.SumType{
|
||||||
variants: sum_variants
|
variants: variant_types
|
||||||
}
|
}
|
||||||
is_public: is_pub
|
is_public: is_pub
|
||||||
})
|
})
|
||||||
|
@ -1966,7 +1981,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
|
||||||
return ast.SumTypeDecl{
|
return ast.SumTypeDecl{
|
||||||
name: name
|
name: name
|
||||||
is_pub: is_pub
|
is_pub: is_pub
|
||||||
sub_types: sum_variants
|
variants: sum_variants
|
||||||
pos: decl_pos
|
pos: decl_pos
|
||||||
comments: comments
|
comments: comments
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue