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 idx := 0
mut done := false mut done := false
mut ctype_names := []string mut ctype_names := []string
mut sum_variants := []string
for { for {
// p.tok == .pipe { // p.tok == .pipe {
idx++ idx++
@ -862,14 +863,17 @@ fn (p mut Parser) type_decl() {
if p.pass == .main { if p.pass == .main {
// Update the type's parent // Update the type's parent
// println('child=$child_type_name parent=$name') // 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 == '' { if t.name == '' {
p.error('unknown type `$child_type_name`') p.error('unknown type `$child_type_name`')
} }
t.parent = name p.cgen.consts << '#define SumType_${name}_$child_type_name $idx // DEF2'
p.table.rewrite_type(t)
p.cgen.consts << '#define SumType_$child_type_name $idx // DEF2'
ctype_names << child_type_name 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 { if done {
break break
@ -882,10 +886,7 @@ fn (p mut Parser) type_decl() {
// p.fgen_nl() // p.fgen_nl()
} }
} }
if p.pass == .decl { p.table.sum_types[name] = sum_variants
p.table.sum_types << name
// println(p.table.sum_types)
}
// Register the actual sum type // Register the actual sum type
// println('registering sum $name') // println('registering sum $name')
p.table.register_type(Type{ 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` // e.g. `return InfixExpr{}` in a function expecting `Expr`
if expected != typ && expected in p.table.sum_types { // TODO perf if expected != typ && expected in p.table.sum_types { // TODO perf
//p.warn('SUM CAST exp=$expected typ=$typ p.exp=$p.expected_type') //p.warn('SUM CAST exp=$expected typ=$typ p.exp=$p.expected_type')
T := p.table.find_type(typ) if typ in p.table.sum_types[expected] {
if T.parent == expected {
p.cgen.set_placeholder(start_ph, '/*SUM TYPE CAST2*/ ($expected) { .obj = memdup( &($typ[]) { ') p.cgen.set_placeholder(start_ph, '/*SUM TYPE CAST2*/ ($expected) { .obj = memdup( &($typ[]) { ')
tt := typ.all_after('_') // TODO 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 // `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') p.error('casting `$typ` to `$cast_typ` is not needed')
} }
if typ in p.table.sum_types { if typ in p.table.sum_types {
T := p.table.find_type(cast_typ) if !(cast_typ in p.table.sum_types[typ]) {
if T.parent != typ { p.error('cannot cast `$typ` to `$cast_typ`. `$cast_typ` is not a variant of `$typ`')
p.error('cannot cast `$typ` to `$cast_typ`. `$cast_typ` is not a variant of `$typ`' +
'parent=$T.parent')
} }
p.cgen.set_placeholder(start_ph, '*($cast_typ*)') p.cgen.set_placeholder(start_ph, '*($cast_typ*)')
p.gen('.obj') 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') sum_type:= p.cgen.cur_line.all_after('*) (').replace('.obj', '.typ')
n := cast_typ.all_after('__') 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`"); puts("runtime error: $p.file_name:$p.scanner.line_nr cannot cast sum type `$typ` to `$n`");
exit(1); exit(1);
} }

View File

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

View File

@ -21,8 +21,8 @@ pub mut:
// names []Name // names []Name
max_field_len map[string]int // for vfmt: max_field_len['Parser'] == 12 max_field_len map[string]int // for vfmt: max_field_len['Parser'] == 12
generic_struct_params map[string][]string generic_struct_params map[string][]string
tuple_variants map[string][]string // enum( Bool(BoolExpr) ) tuple_variants map[string][]string // enum( Bool(BoolExpr) )
sum_types []string sum_types map[string][]string // SumType -> [Variants]
} }
struct VargAccess { struct VargAccess {
@ -191,6 +191,8 @@ const (
float_types = ['f32', 'f64'] float_types = ['f32', 'f64']
reserved_type_param_names = ['R', 'S', 'T', 'U', 'W'] reserved_type_param_names = ['R', 'S', 'T', 'U', 'W']
pointer_types = ['byte*', 'byteptr', 'char*', 'charptr', 'void*', 'voidptr', 'voidptr*', 'intptr'] 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 { 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 // Sum type
if expected in p.table.sum_types { if expected in p.table.sum_types {
//println('checking sum') //println('checking sum')
child := p.table.find_type(got) if got in p.table.sum_types[expected] {
if child.parent == expected {
//println('yep $expected') //println('yep $expected')
return true return true
} }

View File

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