tagged unions
This commit is contained in:
@@ -229,6 +229,7 @@ define_record :: proc(
|
||||
is_union := false,
|
||||
explicit_size: u64 = 0,
|
||||
explicit_alignment: u32 = 0,
|
||||
tag: Type = INVALID,
|
||||
) -> bool {
|
||||
existing, ok := node(store, id)
|
||||
if !ok || (existing.kind != .Named && existing.kind != .Struct && existing.kind != .Union) ||
|
||||
@@ -242,12 +243,31 @@ define_record :: proc(
|
||||
store.nodes[index].declared = true
|
||||
store.nodes[index].explicit_size = explicit_size
|
||||
store.nodes[index].explicit_alignment = explicit_alignment
|
||||
// A tagged union stashes its discriminant enum in `child` (untagged unions and
|
||||
// structs leave it INVALID); the per-variant tag value is derived from the enum
|
||||
// member whose name matches the variant, so no extra storage is needed.
|
||||
store.nodes[index].child = tag
|
||||
store.nodes[index].field_start = u32(len(store.fields))
|
||||
store.nodes[index].field_count = u32(len(fields))
|
||||
append(&store.fields, ..fields)
|
||||
return true
|
||||
}
|
||||
|
||||
// enum_anonymous interns an unnamed enum (used as the synthesized discriminant of a
|
||||
// `union(enum)` tagged union). Members carry the variant names with dense 0-based
|
||||
// values, so the same name→member→value lookup used for `union(Enum)` resolves tags.
|
||||
enum_anonymous :: proc(store: ^Store, members: []Enum_Member, backing: Type) -> Type {
|
||||
start := u32(len(store.enum_members))
|
||||
append(&store.enum_members, ..members)
|
||||
return intern(store, Node{
|
||||
kind=.Enum,
|
||||
child=backing,
|
||||
field_start=start,
|
||||
field_count=u32(len(members)),
|
||||
declared=true,
|
||||
})
|
||||
}
|
||||
|
||||
define_struct :: proc(store: ^Store, id: Type, fields: []Field, c_layout, opaque: bool) -> bool {
|
||||
return define_record(store, id, fields, c_layout, opaque)
|
||||
}
|
||||
@@ -541,6 +561,36 @@ is_union :: proc(value: Type, store: ^Store) -> bool {
|
||||
return kind(value, store) == .Union
|
||||
}
|
||||
|
||||
// A tagged union is a `.Union` whose `child` is a valid enum (the discriminant).
|
||||
is_tagged_union :: proc(value: Type, store: ^Store) -> bool {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.kind == .Union && is_enum(item.child, store)
|
||||
}
|
||||
|
||||
union_tag_enum :: proc(value: Type, store: ^Store) -> Type {
|
||||
if !is_tagged_union(value, store) {
|
||||
return INVALID
|
||||
}
|
||||
item, _ := node(store, value)
|
||||
return item.child
|
||||
}
|
||||
|
||||
// union_payload_offset is the byte offset of a tagged union's payload carrier (after
|
||||
// the discriminant), shared by `size` and the LLVM emitter so construction, field
|
||||
// access, and layout agree. Zero for untagged unions.
|
||||
union_payload_offset :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
||||
if !is_tagged_union(value, store) {
|
||||
return 0
|
||||
}
|
||||
item, _ := node(store, value)
|
||||
tag_size := size(item.child, store, selected)
|
||||
payload_align: u64 = 1
|
||||
for field in fields_for(store, value) {
|
||||
payload_align = max(payload_align, u64(alignment_of(field.type, store, selected)))
|
||||
}
|
||||
return (tag_size+payload_align-1)/payload_align*payload_align
|
||||
}
|
||||
|
||||
is_distinct :: proc(value: Type, store: ^Store) -> bool {
|
||||
return kind(value, store) == .Distinct
|
||||
}
|
||||
@@ -1048,13 +1098,18 @@ size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
||||
if item.explicit_size > 0 {
|
||||
return item.explicit_size
|
||||
}
|
||||
result: u64
|
||||
carrier_size: u64
|
||||
max_align: u64 = 1
|
||||
for field in fields_for(store, value) {
|
||||
result = max(result, size(field.type, store, selected))
|
||||
carrier_size = max(carrier_size, size(field.type, store, selected))
|
||||
max_align = max(max_align, u64(alignment_of(field.type, store, selected)))
|
||||
}
|
||||
return (result+max_align-1)/max_align*max_align
|
||||
if is_enum(item.child, store) {
|
||||
payload_offset := union_payload_offset(value, store, selected)
|
||||
total_align := max(max_align, u64(alignment_of(item.child, store, selected)))
|
||||
return (payload_offset+carrier_size+total_align-1)/total_align*total_align
|
||||
}
|
||||
return (carrier_size+max_align-1)/max_align*max_align
|
||||
case:
|
||||
return 0
|
||||
}
|
||||
@@ -1091,6 +1146,9 @@ alignment_of :: proc(value: Type, store: ^Store, selected := target.DEFAULT) ->
|
||||
for field in fields_for(store, value) {
|
||||
result = max(result, alignment_of(field.type, store, selected))
|
||||
}
|
||||
if is_enum(item.child, store) {
|
||||
result = max(result, alignment_of(item.child, store, selected))
|
||||
}
|
||||
return result
|
||||
case:
|
||||
return 1
|
||||
|
||||
Reference in New Issue
Block a user