tagged unions
This commit is contained in:
@@ -1730,6 +1730,22 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
|
||||
parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout: bool, is_union := false) {
|
||||
start := advance(parser)
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol))
|
||||
// A tagged union spells its discriminant in parens: `union(Enum)` reuses an existing
|
||||
// enum; `union(enum)` synthesizes one from the variant names after the body is parsed.
|
||||
tag := types.INVALID
|
||||
inferred_tag := false
|
||||
if is_union {
|
||||
if _, ok := allow(parser, .Left_Paren); ok {
|
||||
if _, enum_ok := allow(parser, .Keyword_Enum); enum_ok {
|
||||
inferred_tag = true
|
||||
} else {
|
||||
tag = parse_type(parser)
|
||||
}
|
||||
if _, close_ok := allow(parser, .Right_Paren); !close_ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ')' after union tag")
|
||||
}
|
||||
}
|
||||
}
|
||||
ended_by_newline := current(parser).kind == .Newline
|
||||
skip_newlines(parser)
|
||||
if current(parser).kind != .Left_Brace {
|
||||
@@ -1739,7 +1755,7 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout: bool, is_unio
|
||||
"native union declarations require a body" if is_union else "native struct declarations require a body",
|
||||
)
|
||||
}
|
||||
if !types.define_record(&parser.module.type_store, id, nil, c_layout, true, is_union) {
|
||||
if !types.define_record(&parser.module.type_store, id, nil, c_layout, true, is_union, tag=tag) {
|
||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||
}
|
||||
if !ended_by_newline {
|
||||
@@ -1775,12 +1791,38 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout: bool, is_unio
|
||||
if _, ok := allow(parser, .Right_Brace); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '}' after struct fields")
|
||||
}
|
||||
if !types.define_record(&parser.module.type_store, id, fields[:], c_layout, false, is_union) {
|
||||
if inferred_tag {
|
||||
tag = synthesize_union_tag(parser, fields[:])
|
||||
}
|
||||
if !types.define_record(&parser.module.type_store, id, fields[:], c_layout, false, is_union, tag=tag) {
|
||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||
}
|
||||
_ = finish_statement(parser)
|
||||
}
|
||||
|
||||
// synthesize_union_tag builds the anonymous discriminant enum for a `union(enum)`:
|
||||
// one member per variant, dense 0-based, in the smallest fitting unsigned backing
|
||||
// (mirroring `parse_enum`'s unbacked pick).
|
||||
synthesize_union_tag :: proc(parser: ^Parser, fields: []types.Field) -> types.Type {
|
||||
members := make([]types.Enum_Member, len(fields), parser.module.allocator)
|
||||
defer delete(members, parser.module.allocator)
|
||||
for field, index in fields {
|
||||
members[index] = types.Enum_Member{name=field.name, value=i128(index)}
|
||||
}
|
||||
max_value := u64(max(len(fields)-1, 0))
|
||||
backing := types.U8
|
||||
if max_value > 0xff {
|
||||
backing = types.U16
|
||||
}
|
||||
if max_value > 0xffff {
|
||||
backing = types.U32
|
||||
}
|
||||
if max_value > 0xffff_ffff {
|
||||
backing = types.U64
|
||||
}
|
||||
return types.enum_anonymous(&parser.module.type_store, members, backing)
|
||||
}
|
||||
|
||||
parse_distinct :: proc(parser: ^Parser, name: token.Token) {
|
||||
start := advance(parser)
|
||||
child := parse_type(parser)
|
||||
|
||||
Reference in New Issue
Block a user