function pointers and callbacks
This commit is contained in:
+196
-16
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user