compiler: allow a type to be used as a variant of multiple sum types

pull/3851/head
joe-conigliaro 2020-02-26 15:15:38 +11:00
parent bc3d1eaf6e
commit c4e83faa57
5 changed files with 39 additions and 23 deletions

View File

@ -850,6 +850,7 @@ fn (p mut Parser) type_decl() {
mut idx := 0
mut done := false
mut ctype_names := []string
mut sum_variants := []string
for {
// p.tok == .pipe {
idx++
@ -862,14 +863,17 @@ fn (p mut Parser) type_decl() {
if p.pass == .main {
// Update the type's parent
// println('child=$child_type_name parent=$name')
mut t := p.find_type(child_type_name)
t := p.find_type(child_type_name)
if t.name == '' {
p.error('unknown type `$child_type_name`')
}
t.parent = name
p.table.rewrite_type(t)
p.cgen.consts << '#define SumType_$child_type_name $idx // DEF2'
p.cgen.consts << '#define SumType_${name}_$child_type_name $idx // DEF2'
ctype_names << child_type_name
sum_variants << if p.mod in ['builtin', 'main'] || child_type_name in builtin_types {
child_type_name
} else {
p.prepend_mod(child_type_name)
}
}
if done {
break
@ -882,10 +886,7 @@ fn (p mut Parser) type_decl() {
// p.fgen_nl()
}
}
if p.pass == .decl {
p.table.sum_types << name
// println(p.table.sum_types)
}
p.table.sum_types[name] = sum_variants
// Register the actual sum type
// println('registering sum $name')
p.table.register_type(Type{

View File

@ -66,11 +66,10 @@ fn (p mut Parser) bool_expression() string {
// e.g. `return InfixExpr{}` in a function expecting `Expr`
if expected != typ && expected in p.table.sum_types { // TODO perf
//p.warn('SUM CAST exp=$expected typ=$typ p.exp=$p.expected_type')
T := p.table.find_type(typ)
if T.parent == expected {
if typ in p.table.sum_types[expected] {
p.cgen.set_placeholder(start_ph, '/*SUM TYPE CAST2*/ ($expected) { .obj = memdup( &($typ[]) { ')
tt := typ.all_after('_') // TODO
p.gen('}, sizeof($typ) ), .typ = SumType_${tt} }')//${val}_type }')
p.gen('}, sizeof($typ) ), .typ = SumType_${expected}_${tt} }')//${val}_type }')
}
}
// `as` cast
@ -90,10 +89,8 @@ fn (p mut Parser) key_as(typ string, start_ph int) string {
p.error('casting `$typ` to `$cast_typ` is not needed')
}
if typ in p.table.sum_types {
T := p.table.find_type(cast_typ)
if T.parent != typ {
p.error('cannot cast `$typ` to `$cast_typ`. `$cast_typ` is not a variant of `$typ`' +
'parent=$T.parent')
if !(cast_typ in p.table.sum_types[typ]) {
p.error('cannot cast `$typ` to `$cast_typ`. `$cast_typ` is not a variant of `$typ`')
}
p.cgen.set_placeholder(start_ph, '*($cast_typ*)')
p.gen('.obj')
@ -102,7 +99,7 @@ fn (p mut Parser) key_as(typ string, start_ph int) string {
sum_type:= p.cgen.cur_line.all_after('*) (').replace('.obj', '.typ')
n := cast_typ.all_after('__')
p.cgen.insert_before('if (($sum_type != SumType_$n) {
p.cgen.insert_before('if (($sum_type != SumType_${typ}_$n) {
puts("runtime error: $p.file_name:$p.scanner.line_nr cannot cast sum type `$typ` to `$n`");
exit(1);
}

View File

@ -140,7 +140,7 @@ fn (p mut Parser) match_statement(is_expr bool) string {
if is_sum_type {
sum_child_type = p.get_type2().name
tt := sum_child_type.all_after('_')
p.gen('SumType_$tt')
p.gen('SumType_${typ}_$tt')
// println('got child $sum_child_type')
p.register_var(Var{
name: 'it'

View File

@ -21,8 +21,8 @@ pub mut:
// names []Name
max_field_len map[string]int // for vfmt: max_field_len['Parser'] == 12
generic_struct_params map[string][]string
tuple_variants map[string][]string // enum( Bool(BoolExpr) )
sum_types []string
tuple_variants map[string][]string // enum( Bool(BoolExpr) )
sum_types map[string][]string // SumType -> [Variants]
}
struct VargAccess {
@ -191,6 +191,8 @@ const (
float_types = ['f32', 'f64']
reserved_type_param_names = ['R', 'S', 'T', 'U', 'W']
pointer_types = ['byte*', 'byteptr', 'char*', 'charptr', 'void*', 'voidptr', 'voidptr*', 'intptr']
builtin_types = ['int', 'i8', 'char', 'byte', 'i16', 'u16', 'u32', 'i64', 'u64',
'f64', 'f32', 'byteptr', 'charptr', 'voidptr', 'intptr', 'string', 'ustring']
)
fn is_number_type(typ string) bool {
@ -745,8 +747,7 @@ fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool {
// Sum type
if expected in p.table.sum_types {
//println('checking sum')
child := p.table.find_type(got)
if child.parent == expected {
if got in p.table.sum_types[expected] {
//println('yep $expected')
return true
}

View File

@ -20,7 +20,9 @@ fn test_person_str() {
struct Foo {}
type Expr = Foo | BoolExpr | BinExpr | UnaryExpr
type Expr = Foo | BoolExpr | BinExpr | UnaryExpr | DeclExprA | DeclExprB
type DeclExpr = DeclExprA | DeclExprB
struct BoolExpr {
foo int
@ -31,6 +33,14 @@ struct BinExpr {
name string
}
struct DeclExprA {
name string
}
struct DeclExprB {
name string
}
fn expr1() Expr {
mut e := Expr{}
e = BinExpr{'binexpr'}
@ -46,11 +56,14 @@ struct UnaryExpr {
}
fn handle_expr(e Expr) {
}
fn handle_decl_expr(de DeclExpr) {
}
fn parse_bool() BoolExpr {
return BoolExpr{}
}
@ -64,6 +77,10 @@ fn test_sum_type_cast() {
fn test_sum_types() {
b := parse_bool()
handle_expr(b)
de := DeclExprA{}
handle_expr(de)
handle_decl_expr(de)
}
/*