function pointers and callbacks

This commit is contained in:
2026-06-15 21:09:46 +02:00
parent 3b7c3fcbd0
commit f5605fd3ec
21 changed files with 1927 additions and 122 deletions
+196 -16
View File
@@ -58,9 +58,11 @@ Kind :: enum u8 {
Pointer,
Slice,
Optional,
Function,
Named,
Alias,
Struct,
Union,
}
Node :: struct {
@@ -68,8 +70,10 @@ Node :: struct {
child: Type,
count: u64,
sentinel: u64,
explicit_size: u64,
field_start: u32,
field_count: u32,
explicit_alignment: u32,
pkg: u32,
name: u32,
qualifier: u32,
@@ -78,14 +82,17 @@ Node :: struct {
many: bool,
has_sentinel: bool,
inferred_count: bool,
c_abi: bool,
variadic: bool,
c_layout: bool,
opaque: bool,
declared: bool,
}
Field :: struct {
name: u32,
type: Type,
name: u32,
type: Type,
offset: u64,
}
Store :: struct {
@@ -118,7 +125,7 @@ clone_store :: proc(source: ^Store, allocator := context.allocator) -> Store {
}
intern :: proc(store: ^Store, candidate: Node) -> Type {
if candidate.kind != .Struct && candidate.kind != .Named {
if candidate.kind != .Struct && candidate.kind != .Union && candidate.kind != .Named {
for existing, index in store.nodes {
if existing == candidate {
return DYNAMIC_START+Type(index)
@@ -133,7 +140,7 @@ intern :: proc(store: ^Store, candidate: Node) -> Type {
named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xffff_ffff) -> Type {
normalized_file := file if qualifier != 0 else u32(0)
for existing, index in store.nodes {
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Struct) &&
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Struct || existing.kind == .Union) &&
existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier &&
existing.file == normalized_file {
return DYNAMIC_START+Type(index)
@@ -144,7 +151,7 @@ named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xf
find_named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0) -> Type {
for existing, index in store.nodes {
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Struct) &&
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Struct || existing.kind == .Union) &&
existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier {
return DYNAMIC_START+Type(index)
}
@@ -164,25 +171,53 @@ define_alias :: proc(store: ^Store, id, child: Type) -> bool {
return true
}
define_struct :: proc(store: ^Store, id: Type, fields: []Field, c_layout, opaque: bool) -> bool {
define_record :: proc(
store: ^Store,
id: Type,
fields: []Field,
c_layout, opaque: bool,
is_union := false,
explicit_size: u64 = 0,
explicit_alignment: u32 = 0,
) -> bool {
existing, ok := node(store, id)
if !ok || (existing.kind != .Named && existing.kind != .Struct) || existing.declared {
if !ok || (existing.kind != .Named && existing.kind != .Struct && existing.kind != .Union) ||
(existing.declared && !existing.opaque) {
return false
}
index := int(id-DYNAMIC_START)
store.nodes[index].kind = .Struct
store.nodes[index].kind = .Union if is_union else .Struct
store.nodes[index].c_layout = c_layout
store.nodes[index].opaque = opaque
store.nodes[index].declared = true
store.nodes[index].explicit_size = explicit_size
store.nodes[index].explicit_alignment = explicit_alignment
store.nodes[index].field_start = u32(len(store.fields))
store.nodes[index].field_count = u32(len(fields))
append(&store.fields, ..fields)
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)
}
fields_for :: proc(store: ^Store, value: Type) -> []Field {
item, ok := node(store, value)
if !ok || item.kind != .Struct {
if !ok || (item.kind != .Struct && item.kind != .Union) {
return nil
}
start := int(item.field_start)
end := start+int(item.field_count)
if start < 0 || end > len(store.fields) {
return nil
}
return store.fields[start:end]
}
params_for :: proc(store: ^Store, value: Type) -> []Field {
item, ok := node(store, value)
if !ok || item.kind != .Function {
return nil
}
start := int(item.field_start)
@@ -350,7 +385,7 @@ is_concrete :: proc(value: Type, store: ^Store = nil) -> bool {
value_kind == .Slice || value_kind == .Optional {
return true
}
if value_kind == .Struct {
if value_kind == .Struct || value_kind == .Union {
item, ok := node(store, value)
return ok && item.declared
}
@@ -373,10 +408,64 @@ is_optional :: proc(value: Type, store: ^Store) -> bool {
return kind(value, store) == .Optional
}
is_function :: proc(value: Type, store: ^Store) -> bool {
return kind(value, store) == .Function
}
is_struct :: proc(value: Type, store: ^Store) -> bool {
return kind(value, store) == .Struct
}
is_record :: proc(value: Type, store: ^Store) -> bool {
value_kind := kind(value, store)
return value_kind == .Struct || value_kind == .Union
}
is_union :: proc(value: Type, store: ^Store) -> bool {
return kind(value, store) == .Union
}
resolve_alias :: proc(value: Type, store: ^Store, depth := 0) -> Type {
if depth > 64 {
return INVALID
}
item, ok := node(store, value)
if !ok || item.kind != .Alias {
return value
}
return resolve_alias(item.child, store, depth+1)
}
is_c_record_field_type :: proc(value: Type, store: ^Store, depth := 0) -> bool {
if depth > 256 {
return false
}
resolved := resolve_alias(value, store)
if is_concrete_scalar(resolved) || is_pointer(resolved, store) || is_optional_pointer(resolved, store) {
return true
}
item, ok := node(store, resolved)
if !ok {
return false
}
if item.kind == .Array {
return item.count > 0 && !item.has_sentinel && !item.inferred_count &&
is_c_record_field_type(item.child, store, depth+1)
}
if item.kind != .Struct && item.kind != .Union {
return false
}
if !item.c_layout || !item.declared || item.opaque || item.field_count == 0 {
return false
}
for field in fields_for(store, resolved) {
if !is_c_record_field_type(field.type, store, depth+1) {
return false
}
}
return true
}
is_optional_pointer :: proc(value: Type, store: ^Store) -> bool {
item, ok := node(store, value)
return ok && item.kind == .Optional && is_pointer(item.child, store)
@@ -390,9 +479,9 @@ is_runtime_value :: proc(value: Type, store: ^Store) -> bool {
if value_kind == .Slice || value_kind == .Array || value_kind == .Optional {
return !contains_c_struct_by_value(value, store)
}
if value_kind == .Struct {
if value_kind == .Struct || value_kind == .Union {
item, ok := node(store, value)
return ok && item.declared && !item.opaque && !contains_c_struct_by_value(value, store)
return ok && item.declared && !item.opaque && (!item.c_layout || item.field_count > 0)
}
return false
}
@@ -409,7 +498,7 @@ contains_c_struct_by_value :: proc(value: Type, store: ^Store, depth := 0) -> bo
return false
}
if item.kind == .Struct {
if item.c_layout {
if item.opaque || (item.c_layout && item.field_count == 0) {
return true
}
for field in fields_for(store, value) {
@@ -419,6 +508,9 @@ contains_c_struct_by_value :: proc(value: Type, store: ^Store, depth := 0) -> bo
}
return false
}
if item.kind == .Union {
return item.opaque || (item.c_layout && item.field_count == 0)
}
if item.kind == .Array || item.kind == .Slice || item.kind == .Optional {
return contains_c_struct_by_value(item.child, store, depth+1)
}
@@ -429,7 +521,8 @@ is_c_signature_type :: proc(value: Type, store: ^Store, allow_void := false) ->
if allow_void && is_void(value) {
return true
}
return is_concrete_scalar(value) || is_pointer(value, store) || is_optional_pointer(value, store)
return is_concrete_scalar(value) || is_pointer(value, store) || is_optional_pointer(value, store) ||
(is_c_struct(value, store) && is_runtime_value(value, store))
}
is_c_integer_promotion_candidate :: proc(value: Type) -> bool {
@@ -518,9 +611,21 @@ container :: proc(value: Type, store: ^Store) -> (Node, bool) {
return array_node, true
}
function_pointer :: proc(value: Type, store: ^Store) -> (pointer_item, function_item: Node, function_type: Type, ok: bool) {
pointer_node, pointer_ok := node(store, value)
if !pointer_ok || pointer_node.kind != .Pointer {
return {}, {}, INVALID, false
}
function_node, function_ok := node(store, pointer_node.child)
if !function_ok || function_node.kind != .Function {
return {}, {}, INVALID, false
}
return pointer_node, function_node, pointer_node.child, true
}
is_c_struct :: proc(value: Type, store: ^Store) -> bool {
item, ok := node(store, value)
return ok && item.kind == .Struct && item.c_layout
return ok && (item.kind == .Struct || item.kind == .Union) && item.c_layout
}
pointer :: proc(
@@ -572,6 +677,47 @@ optional :: proc(store: ^Store, child: Type) -> Type {
return intern(store, Node{kind=.Optional, child=child})
}
function_params_equal :: proc(store: ^Store, item: Node, params: []Type) -> bool {
if item.field_count != u32(len(params)) {
return false
}
start := int(item.field_start)
end := start+int(item.field_count)
if start < 0 || end > len(store.fields) {
return false
}
for param, index in params {
if store.fields[start+index].type != param {
return false
}
}
return true
}
function :: proc(store: ^Store, params: []Type, result: Type, c_abi, variadic: bool) -> Type {
for existing, index in store.nodes {
if existing.kind == .Function &&
existing.child == result &&
existing.c_abi == c_abi &&
existing.variadic == variadic &&
function_params_equal(store, existing, params) {
return DYNAMIC_START+Type(index)
}
}
start := len(store.fields)
for param in params {
append(&store.fields, Field{type=param})
}
return intern(store, Node{
kind=.Function,
child=result,
field_start=u32(start),
field_count=u32(len(params)),
c_abi=c_abi,
variadic=variadic,
})
}
with_array_count :: proc(store: ^Store, value: Type, count: u64) -> Type {
item, ok := node(store, value)
if !ok || item.kind != .Array {
@@ -638,7 +784,7 @@ can_decay_array_pointer :: proc(from, to: Type, store: ^Store) -> bool {
is_opaque_struct :: proc(value: Type, store: ^Store) -> bool {
item, ok := node(store, value)
return ok && item.kind == .Struct && item.opaque
return ok && (item.kind == .Struct || item.kind == .Union) && item.opaque
}
size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
@@ -660,7 +806,13 @@ size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
child_size := size(item.child, store, selected)
child_align := u64(alignment_of(item.child, store, selected))
return ((child_size+1+child_align-1)/child_align)*child_align
case .Function:
return 0
case .Struct:
item, _ := node(store, value)
if item.explicit_size > 0 {
return item.explicit_size
}
offset: u64
max_align: u64 = 1
for field in fields_for(store, value) {
@@ -670,6 +822,18 @@ size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
max_align = max(max_align, field_align)
}
return (offset+max_align-1)/max_align*max_align
case .Union:
item, _ := node(store, value)
if item.explicit_size > 0 {
return item.explicit_size
}
result: u64
max_align: u64 = 1
for field in fields_for(store, value) {
result = max(result, 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
case:
return 0
}
@@ -683,7 +847,23 @@ alignment_of :: proc(value: Type, store: ^Store, selected := target.DEFAULT) ->
return target.pointer_bits(selected)/8
case .Array, .Optional:
return alignment_of(child_type(value, store), store, selected)
case .Function:
return 1
case .Struct:
item, _ := node(store, value)
if item.explicit_alignment > 0 {
return int(item.explicit_alignment)
}
result := 1
for field in fields_for(store, value) {
result = max(result, alignment_of(field.type, store, selected))
}
return result
case .Union:
item, _ := node(store, value)
if item.explicit_alignment > 0 {
return int(item.explicit_alignment)
}
result := 1
for field in fields_for(store, value) {
result = max(result, alignment_of(field.type, store, selected))