error handling kickoff
This commit is contained in:
+299
-3
@@ -72,11 +72,13 @@ Kind :: enum u8 {
|
||||
Enum,
|
||||
Struct,
|
||||
Union,
|
||||
Fallible,
|
||||
}
|
||||
|
||||
Node :: struct {
|
||||
kind: Kind,
|
||||
child: Type,
|
||||
extra: Type,
|
||||
count: u64,
|
||||
sentinel: u64,
|
||||
explicit_size: u64,
|
||||
@@ -110,10 +112,29 @@ Enum_Member :: struct {
|
||||
value: i128,
|
||||
}
|
||||
|
||||
Variant :: struct {
|
||||
name: u32,
|
||||
payload: Type,
|
||||
id: u16,
|
||||
}
|
||||
|
||||
Sum_Variant :: struct {
|
||||
name: u32,
|
||||
payload: Type,
|
||||
id: u16,
|
||||
}
|
||||
|
||||
Compose_Error :: enum u8 {
|
||||
None,
|
||||
Unsupported,
|
||||
Conflict,
|
||||
}
|
||||
|
||||
Store :: struct {
|
||||
nodes: [dynamic]Node,
|
||||
fields: [dynamic]Field,
|
||||
enum_members: [dynamic]Enum_Member,
|
||||
variants: [dynamic]Variant,
|
||||
selected: target.Target,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
@@ -123,6 +144,7 @@ init_store :: proc(allocator := context.allocator) -> Store {
|
||||
store.nodes.allocator = allocator
|
||||
store.fields.allocator = allocator
|
||||
store.enum_members.allocator = allocator
|
||||
store.variants.allocator = allocator
|
||||
store.selected = target.DEFAULT
|
||||
store.allocator = allocator
|
||||
return store
|
||||
@@ -132,6 +154,7 @@ destroy_store :: proc(store: ^Store) {
|
||||
delete(store.nodes)
|
||||
delete(store.fields)
|
||||
delete(store.enum_members)
|
||||
delete(store.variants)
|
||||
}
|
||||
|
||||
clone_store :: proc(source: ^Store, allocator := context.allocator) -> Store {
|
||||
@@ -139,6 +162,7 @@ clone_store :: proc(source: ^Store, allocator := context.allocator) -> Store {
|
||||
append(&store.nodes, ..source.nodes[:])
|
||||
append(&store.fields, ..source.fields[:])
|
||||
append(&store.enum_members, ..source.enum_members[:])
|
||||
append(&store.variants, ..source.variants[:])
|
||||
store.selected = source.selected
|
||||
return store
|
||||
}
|
||||
@@ -230,6 +254,7 @@ define_record :: proc(
|
||||
explicit_size: u64 = 0,
|
||||
explicit_alignment: u32 = 0,
|
||||
tag: Type = INVALID,
|
||||
declared_tag: Type = INVALID,
|
||||
) -> bool {
|
||||
existing, ok := node(store, id)
|
||||
if !ok || (existing.kind != .Named && existing.kind != .Struct && existing.kind != .Union) ||
|
||||
@@ -247,6 +272,7 @@ define_record :: proc(
|
||||
// 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].extra = declared_tag
|
||||
store.nodes[index].field_start = u32(len(store.fields))
|
||||
store.nodes[index].field_count = u32(len(fields))
|
||||
append(&store.fields, ..fields)
|
||||
@@ -268,6 +294,180 @@ enum_anonymous :: proc(store: ^Store, members: []Enum_Member, backing: Type) ->
|
||||
})
|
||||
}
|
||||
|
||||
union_anonymous :: proc(store: ^Store, fields: []Field, tag: Type) -> Type {
|
||||
start := u32(len(store.fields))
|
||||
append(&store.fields, ..fields)
|
||||
return intern(store, Node{
|
||||
kind=.Union,
|
||||
child=tag,
|
||||
field_start=start,
|
||||
field_count=u32(len(fields)),
|
||||
declared=true,
|
||||
})
|
||||
}
|
||||
|
||||
variant_id :: proc(store: ^Store, name: u32, payload: Type) -> (u16, bool) {
|
||||
for variant in store.variants {
|
||||
if variant.name == name && variant.payload == payload {
|
||||
return variant.id, true
|
||||
}
|
||||
}
|
||||
next := len(store.variants)+1
|
||||
if next > 0xffff {
|
||||
return 0, false
|
||||
}
|
||||
id := u16(next)
|
||||
append(&store.variants, Variant{name=name, payload=payload, id=id})
|
||||
return id, true
|
||||
}
|
||||
|
||||
fallible :: proc(store: ^Store, success, error: Type) -> Type {
|
||||
return intern(store, Node{kind=.Fallible, child=success, extra=error, declared=true})
|
||||
}
|
||||
|
||||
fallible_success :: proc(value: Type, store: ^Store) -> Type {
|
||||
item, ok := node(store, value)
|
||||
return item.child if ok && item.kind == .Fallible else INVALID
|
||||
}
|
||||
|
||||
fallible_error :: proc(value: Type, store: ^Store) -> Type {
|
||||
item, ok := node(store, value)
|
||||
return item.extra if ok && item.kind == .Fallible else INVALID
|
||||
}
|
||||
|
||||
append_sum_variants :: proc(store: ^Store, value: Type, out: ^[dynamic]Sum_Variant) -> bool {
|
||||
item, ok := node(store, value)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if item.kind == .Enum {
|
||||
if item.explicit_backing {
|
||||
return false
|
||||
}
|
||||
for member in enum_members_for(store, value) {
|
||||
if member.value <= 0 || member.value > 0xffff {
|
||||
return false
|
||||
}
|
||||
append(out, Sum_Variant{name=member.name, payload=VOID, id=u16(member.value)})
|
||||
}
|
||||
return true
|
||||
}
|
||||
if item.kind != .Union || !is_enum(item.child, store) || item.c_layout {
|
||||
return false
|
||||
}
|
||||
tag_members := enum_members_for(store, item.child)
|
||||
for field in fields_for(store, value) {
|
||||
found := false
|
||||
for member in tag_members {
|
||||
if member.name == field.name {
|
||||
if member.value <= 0 || member.value > 0xffff {
|
||||
return false
|
||||
}
|
||||
append(out, Sum_Variant{name=field.name, payload=field.type, id=u16(member.value)})
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
compose_sum :: proc(store: ^Store, left, right: Type) -> (Type, Compose_Error) {
|
||||
variants: [dynamic]Sum_Variant
|
||||
variants.allocator = store.allocator
|
||||
defer delete(variants)
|
||||
if !append_sum_variants(store, left, &variants) {
|
||||
return INVALID, .Unsupported
|
||||
}
|
||||
right_variants: [dynamic]Sum_Variant
|
||||
right_variants.allocator = store.allocator
|
||||
defer delete(right_variants)
|
||||
if !append_sum_variants(store, right, &right_variants) {
|
||||
return INVALID, .Unsupported
|
||||
}
|
||||
for candidate in right_variants {
|
||||
merged := false
|
||||
for existing in variants {
|
||||
if existing.id == candidate.id {
|
||||
merged = true
|
||||
break
|
||||
}
|
||||
if existing.name == candidate.name && existing.payload != candidate.payload {
|
||||
return INVALID, .Conflict
|
||||
}
|
||||
}
|
||||
if !merged {
|
||||
append(&variants, candidate)
|
||||
}
|
||||
}
|
||||
all_void := true
|
||||
for variant in variants {
|
||||
all_void = all_void && variant.payload == VOID
|
||||
}
|
||||
members := make([]Enum_Member, len(variants), store.allocator)
|
||||
defer delete(members, store.allocator)
|
||||
for variant, index in variants {
|
||||
members[index] = Enum_Member{name=variant.name, value=i128(variant.id)}
|
||||
}
|
||||
if all_void {
|
||||
return enum_anonymous(store, members, U16), .None
|
||||
}
|
||||
fields := make([]Field, len(variants), store.allocator)
|
||||
defer delete(fields, store.allocator)
|
||||
for variant, index in variants {
|
||||
fields[index] = Field{name=variant.name, type=variant.payload}
|
||||
}
|
||||
tag := enum_anonymous(store, members, U16)
|
||||
return union_anonymous(store, fields, tag), .None
|
||||
}
|
||||
|
||||
sum_has_name :: proc(store: ^Store, value: Type, name: u32) -> bool {
|
||||
variants: [dynamic]Sum_Variant
|
||||
variants.allocator = store.allocator
|
||||
defer delete(variants)
|
||||
if !append_sum_variants(store, value, &variants) {
|
||||
return false
|
||||
}
|
||||
for variant in variants {
|
||||
if variant.name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
can_sum_widen :: proc(from, to: Type, store: ^Store) -> bool {
|
||||
if from == to {
|
||||
return true
|
||||
}
|
||||
from_variants: [dynamic]Sum_Variant
|
||||
from_variants.allocator = store.allocator
|
||||
defer delete(from_variants)
|
||||
to_variants: [dynamic]Sum_Variant
|
||||
to_variants.allocator = store.allocator
|
||||
defer delete(to_variants)
|
||||
if !append_sum_variants(store, from, &from_variants) ||
|
||||
!append_sum_variants(store, to, &to_variants) {
|
||||
return false
|
||||
}
|
||||
for needed in from_variants {
|
||||
found := false
|
||||
for available in to_variants {
|
||||
if available.id == needed.id && available.payload == needed.payload {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
define_struct :: proc(store: ^Store, id: Type, fields: []Field, c_layout, opaque: bool) -> bool {
|
||||
return define_record(store, id, fields, c_layout, opaque)
|
||||
}
|
||||
@@ -514,7 +714,7 @@ is_concrete :: proc(value: Type, store: ^Store = nil) -> bool {
|
||||
value_kind := kind(value, store)
|
||||
if value_kind == .Scalar || value_kind == .Array || value_kind == .Pointer ||
|
||||
value_kind == .Slice || value_kind == .Range || value_kind == .Optional ||
|
||||
value_kind == .Enum {
|
||||
value_kind == .Enum || value_kind == .Fallible {
|
||||
return true
|
||||
}
|
||||
if value_kind == .Struct || value_kind == .Union {
|
||||
@@ -575,6 +775,14 @@ union_tag_enum :: proc(value: Type, store: ^Store) -> Type {
|
||||
return item.child
|
||||
}
|
||||
|
||||
union_declared_tag_enum :: proc(value: Type, store: ^Store) -> Type {
|
||||
if !is_tagged_union(value, store) {
|
||||
return INVALID
|
||||
}
|
||||
item, _ := node(store, value)
|
||||
return item.extra if is_valid(item.extra) else 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.
|
||||
@@ -591,6 +799,16 @@ union_payload_offset :: proc(value: Type, store: ^Store, selected := target.DEFA
|
||||
return (tag_size+payload_align-1)/payload_align*payload_align
|
||||
}
|
||||
|
||||
sum_tag_type :: proc(value: Type, store: ^Store) -> Type {
|
||||
if is_enum(value, store) {
|
||||
return value
|
||||
}
|
||||
if is_tagged_union(value, store) {
|
||||
return union_tag_enum(value, store)
|
||||
}
|
||||
return INVALID
|
||||
}
|
||||
|
||||
is_distinct :: proc(value: Type, store: ^Store) -> bool {
|
||||
return kind(value, store) == .Distinct
|
||||
}
|
||||
@@ -666,6 +884,13 @@ is_runtime_value :: proc(value: Type, store: ^Store, depth := 0) -> bool {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.declared && !item.opaque && (!item.c_layout || item.field_count > 0)
|
||||
}
|
||||
if value_kind == .Fallible {
|
||||
item, ok := node(store, value)
|
||||
if !ok || !is_runtime_value(item.extra, store, depth+1) {
|
||||
return false
|
||||
}
|
||||
return is_void(item.child) || is_runtime_value(item.child, store, depth+1)
|
||||
}
|
||||
if value_kind == .Distinct || value_kind == .Enum {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.declared && is_runtime_value(item.child, store, depth+1)
|
||||
@@ -689,6 +914,59 @@ runtime_representation :: proc(value: Type, store: ^Store, depth := 0) -> Type {
|
||||
return runtime_representation(item.child, store, depth+1)
|
||||
}
|
||||
|
||||
sum_payload_size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
||||
if is_enum(value, store) {
|
||||
return 0
|
||||
}
|
||||
if !is_tagged_union(value, store) {
|
||||
return size(value, store, selected)
|
||||
}
|
||||
result: u64
|
||||
for field in fields_for(store, value) {
|
||||
result = max(result, size(field.type, store, selected))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
sum_payload_alignment :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
||||
if is_enum(value, store) {
|
||||
return 1
|
||||
}
|
||||
if !is_tagged_union(value, store) {
|
||||
return u64(alignment_of(value, store, selected))
|
||||
}
|
||||
result: u64 = 1
|
||||
for field in fields_for(store, value) {
|
||||
result = max(result, u64(alignment_of(field.type, store, selected)))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fallible_payload_offset :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
||||
item, ok := node(store, value)
|
||||
if !ok || item.kind != .Fallible {
|
||||
return 0
|
||||
}
|
||||
payload_align := max(
|
||||
u64(1),
|
||||
max(
|
||||
u64(alignment_of(item.child, store, selected)) if !is_void(item.child) else u64(1),
|
||||
sum_payload_alignment(item.extra, store, selected),
|
||||
),
|
||||
)
|
||||
tag_size := size(U16, store, selected)
|
||||
return (tag_size+payload_align-1)/payload_align*payload_align
|
||||
}
|
||||
|
||||
fallible_payload_size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
||||
item, ok := node(store, value)
|
||||
if !ok || item.kind != .Fallible {
|
||||
return 0
|
||||
}
|
||||
success_size := u64(0) if is_void(item.child) else size(item.child, store, selected)
|
||||
return max(success_size, sum_payload_size(item.extra, store, selected))
|
||||
}
|
||||
|
||||
contains_c_struct_by_value :: proc(value: Type, store: ^Store, depth := 0) -> bool {
|
||||
if depth > 256 {
|
||||
return true
|
||||
@@ -717,6 +995,10 @@ contains_c_struct_by_value :: proc(value: Type, store: ^Store, depth := 0) -> bo
|
||||
if item.kind == .Array || item.kind == .Slice || item.kind == .Range || item.kind == .Optional {
|
||||
return contains_c_struct_by_value(item.child, store, depth+1)
|
||||
}
|
||||
if item.kind == .Fallible {
|
||||
return contains_c_struct_by_value(item.child, store, depth+1) ||
|
||||
contains_c_struct_by_value(item.extra, store, depth+1)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -774,7 +1056,8 @@ contains_distinct_seen :: proc(value: Type, store: ^Store, seen: ^[dynamic]Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
return is_valid(item.child) && contains_distinct_seen(item.child, store, seen)
|
||||
return (is_valid(item.child) && contains_distinct_seen(item.child, store, seen)) ||
|
||||
(is_valid(item.extra) && contains_distinct_seen(item.extra, store, seen))
|
||||
}
|
||||
|
||||
is_c_integer_promotion_candidate :: proc(value: Type) -> bool {
|
||||
@@ -1110,6 +1393,11 @@ size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
||||
return (payload_offset+carrier_size+total_align-1)/total_align*total_align
|
||||
}
|
||||
return (carrier_size+max_align-1)/max_align*max_align
|
||||
case .Fallible:
|
||||
payload_offset := fallible_payload_offset(value, store, selected)
|
||||
payload_size := fallible_payload_size(value, store, selected)
|
||||
total_align := u64(alignment_of(value, store, selected))
|
||||
return (payload_offset+payload_size+total_align-1)/total_align*total_align
|
||||
case:
|
||||
return 0
|
||||
}
|
||||
@@ -1150,6 +1438,14 @@ alignment_of :: proc(value: Type, store: ^Store, selected := target.DEFAULT) ->
|
||||
result = max(result, alignment_of(item.child, store, selected))
|
||||
}
|
||||
return result
|
||||
case .Fallible:
|
||||
item, _ := node(store, value)
|
||||
result := alignment_of(U16, store, selected)
|
||||
if !is_void(item.child) {
|
||||
result = max(result, alignment_of(item.child, store, selected))
|
||||
}
|
||||
result = max(result, int(sum_payload_alignment(item.extra, store, selected)))
|
||||
return result
|
||||
case:
|
||||
return 1
|
||||
}
|
||||
@@ -1271,6 +1567,6 @@ name :: proc(value: Type) -> string {
|
||||
case C_DOUBLE: return "c_double"
|
||||
case C_LONGDOUBLE: return "c_longdouble"
|
||||
case:
|
||||
return fmt.tprintf("<type %d>", value)
|
||||
return "fallible" if kind(value) == .Fallible else fmt.tprintf("<type %d>", value)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user