function pointers and callbacks
This commit is contained in:
+168
-4
@@ -168,6 +168,26 @@ translate_c_type :: proc(
|
||||
pointer := types.pointer(&state.module.type_store, child, item.mutable, true)
|
||||
translated = types.optional(&state.module.type_store, pointer)
|
||||
}
|
||||
case .Array:
|
||||
child := translate_c_type(state, result, item.child, pkg, record_mapping, type_mapping)
|
||||
if types.is_valid(child) {
|
||||
translated = types.array(&state.module.type_store, child, item.count, item.mutable)
|
||||
}
|
||||
case .Function:
|
||||
params := make([]types.Type, len(item.params), state.allocator)
|
||||
for param, index in item.params {
|
||||
params[index] = translate_c_type(state, result, param, pkg, record_mapping, type_mapping)
|
||||
if !types.is_valid(params[index]) {
|
||||
delete(params, state.allocator)
|
||||
type_mapping[value] = types.INVALID
|
||||
return types.INVALID
|
||||
}
|
||||
}
|
||||
result_type := translate_c_type(state, result, item.child, pkg, record_mapping, type_mapping)
|
||||
if types.is_valid(result_type) {
|
||||
translated = types.function(&state.module.type_store, params, result_type, true, item.variadic)
|
||||
}
|
||||
delete(params, state.allocator)
|
||||
case .Record:
|
||||
if int(item.record) < len(record_mapping) {
|
||||
translated = record_mapping[item.record]
|
||||
@@ -189,6 +209,93 @@ function_signatures_equal :: proc(left: ast.Function, params: []ast.Param, resul
|
||||
return true
|
||||
}
|
||||
|
||||
RECORD_DEPENDENCY_PENDING :: "C record contains an incomplete or unsupported record field"
|
||||
|
||||
record_layout_reason :: proc(
|
||||
store: ^types.Store,
|
||||
record: cimport.Record,
|
||||
fields: []types.Field,
|
||||
selected: target.Target,
|
||||
) -> string {
|
||||
if len(fields) == 0 {
|
||||
if record.size > 0 {
|
||||
return "anonymous C record fields are not supported"
|
||||
}
|
||||
return "empty C records are not supported"
|
||||
}
|
||||
if len(fields) != len(record.fields) || record.alignment == 0 {
|
||||
return "C record metadata is incomplete"
|
||||
}
|
||||
size: u64
|
||||
alignment: u64 = 1
|
||||
if record.kind == .Union {
|
||||
for field in fields {
|
||||
if !types.is_runtime_value(field.type, store) {
|
||||
return RECORD_DEPENDENCY_PENDING
|
||||
}
|
||||
if field.offset != 0 {
|
||||
return "non-natural C record layouts are not supported"
|
||||
}
|
||||
size = max(size, types.size(field.type, store, selected))
|
||||
alignment = max(alignment, u64(types.alignment_of(field.type, store, selected)))
|
||||
}
|
||||
} else {
|
||||
offset: u64
|
||||
for field in fields {
|
||||
if !types.is_runtime_value(field.type, store) {
|
||||
return RECORD_DEPENDENCY_PENDING
|
||||
}
|
||||
field_alignment := u64(types.alignment_of(field.type, store, selected))
|
||||
offset = (offset+field_alignment-1)/field_alignment*field_alignment
|
||||
if field.offset != offset {
|
||||
if field.offset < offset {
|
||||
return "packed C records are not supported"
|
||||
}
|
||||
return "non-natural C record layouts are not supported"
|
||||
}
|
||||
offset += types.size(field.type, store, selected)
|
||||
alignment = max(alignment, field_alignment)
|
||||
}
|
||||
size = offset
|
||||
}
|
||||
size = (size+alignment-1)/alignment*alignment
|
||||
if u64(record.alignment) > alignment {
|
||||
return "over-aligned C records are not supported"
|
||||
}
|
||||
if u64(record.alignment) < alignment || record.size < size {
|
||||
return "packed C records are not supported"
|
||||
}
|
||||
if record.size != size {
|
||||
return "non-natural C record layouts are not supported"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
c_record_by_value_reason :: proc(result: ^cimport.Result, value: cimport.Type_Id, depth := 0) -> string {
|
||||
if depth > 64 || value == cimport.INVALID_TYPE || int(value) < 0 || int(value) >= len(result.types) {
|
||||
return ""
|
||||
}
|
||||
item := result.types[value]
|
||||
#partial switch item.kind {
|
||||
case .Pointer:
|
||||
return ""
|
||||
case .Array:
|
||||
return c_record_by_value_reason(result, item.child, depth+1)
|
||||
case .Record:
|
||||
if int(item.record) >= len(result.records) {
|
||||
return "C record metadata is incomplete"
|
||||
}
|
||||
record := result.records[item.record]
|
||||
if len(record.reason) > 0 {
|
||||
return record.reason
|
||||
}
|
||||
if !record.complete {
|
||||
return "incomplete C records are pointer-only"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
load_header :: proc(state: ^State, path: string, import_span: source.Span) -> ast.Package_Id {
|
||||
canonical, ok := filepath.abs(path, state.allocator)
|
||||
if !ok {
|
||||
@@ -229,7 +336,7 @@ load_header :: proc(state: ^State, path: string, import_span: source.Span) -> as
|
||||
name = fmt.tprintf("__c_record_%d", len(state.record_types))
|
||||
}
|
||||
record_type = types.named(&state.module.type_store, u32(pkg_id), u32(symbol.intern(state.symbols, name)))
|
||||
_ = types.define_struct(&state.module.type_store, record_type, nil, true, true)
|
||||
_ = types.define_record(&state.module.type_store, record_type, nil, true, true, record.kind == .Union)
|
||||
append(&state.record_identities, strings.clone(record.identity, state.allocator))
|
||||
append(&state.record_types, record_type)
|
||||
}
|
||||
@@ -238,6 +345,42 @@ load_header :: proc(state: ^State, path: string, import_span: source.Span) -> as
|
||||
type_mapping := make([]types.Type, len(result.types), state.allocator)
|
||||
defer delete(type_mapping, state.allocator)
|
||||
|
||||
for _ in 0..<len(result.records) {
|
||||
changed := false
|
||||
for record, record_index in result.records {
|
||||
if !record.complete || len(record.reason) > 0 {
|
||||
continue
|
||||
}
|
||||
record_type := record_mapping[record_index]
|
||||
item, ok := types.node(&state.module.type_store, record_type)
|
||||
if !ok || (item.declared && !item.opaque) {
|
||||
continue
|
||||
}
|
||||
fields := make([]types.Field, len(record.fields), state.allocator)
|
||||
for field, field_index in record.fields {
|
||||
fields[field_index] = types.Field{
|
||||
name=u32(symbol.intern(state.symbols, field.name)),
|
||||
type=translate_c_type(state, &result, field.type, pkg_id, record_mapping, type_mapping),
|
||||
offset=field.offset,
|
||||
}
|
||||
}
|
||||
layout_reason := record_layout_reason(&state.module.type_store, record, fields, state.selected)
|
||||
if len(layout_reason) == 0 {
|
||||
changed = types.define_record(
|
||||
&state.module.type_store, record_type, fields, true, false,
|
||||
record.kind == .Union, record.size, record.alignment,
|
||||
) || changed
|
||||
} else if layout_reason != RECORD_DEPENDENCY_PENDING && len(record.reason) == 0 {
|
||||
delete(result.records[record_index].reason, result.allocator)
|
||||
result.records[record_index].reason = fmt.aprintf("%s", layout_reason, allocator=result.allocator)
|
||||
}
|
||||
delete(fields, state.allocator)
|
||||
}
|
||||
if !changed {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for alias in result.aliases {
|
||||
name := symbol.intern(state.symbols, alias.name)
|
||||
id := types.named(&state.module.type_store, u32(pkg_id), u32(name))
|
||||
@@ -263,17 +406,28 @@ load_header :: proc(state: ^State, path: string, import_span: source.Span) -> as
|
||||
}
|
||||
function_result := translate_c_type(state, &result, function.result, pkg_id, record_mapping, type_mapping)
|
||||
unsupported_reason := function.reason
|
||||
if len(unsupported_reason) == 0 {
|
||||
for param_type in function.params {
|
||||
if reason := c_record_by_value_reason(&result, param_type); len(reason) > 0 {
|
||||
unsupported_reason = reason
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(unsupported_reason) == 0 {
|
||||
unsupported_reason = c_record_by_value_reason(&result, function.result)
|
||||
}
|
||||
if len(unsupported_reason) == 0 {
|
||||
for param in params {
|
||||
if types.contains_c_struct_by_value(param.type, &state.module.type_store) {
|
||||
unsupported_reason = "C records passed by value are not supported"
|
||||
unsupported_reason = "C record parameter is incomplete or has an unsupported layout"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(unsupported_reason) == 0 &&
|
||||
types.contains_c_struct_by_value(function_result, &state.module.type_store) {
|
||||
unsupported_reason = "C records returned by value are not supported"
|
||||
unsupported_reason = "C record result is incomplete or has an unsupported layout"
|
||||
}
|
||||
name := symbol.intern(state.symbols, function.name)
|
||||
duplicate := false
|
||||
@@ -522,7 +676,7 @@ canonical_type :: proc(
|
||||
mapping[index] = value if !types.is_valid(resolved) else resolved
|
||||
return mapping[index]
|
||||
}
|
||||
if item.kind == .Struct {
|
||||
if item.kind == .Struct || item.kind == .Union {
|
||||
mapping[index] = value
|
||||
fields := types.fields_for(&module.type_store, value)
|
||||
for &field in fields {
|
||||
@@ -530,6 +684,16 @@ canonical_type :: proc(
|
||||
}
|
||||
return value
|
||||
}
|
||||
if item.kind == .Function {
|
||||
params := make([]types.Type, int(item.field_count), context.temp_allocator)
|
||||
for param, param_index in types.params_for(&module.type_store, value) {
|
||||
params[param_index] = canonical_type(module, param.type, mapping, visiting)
|
||||
}
|
||||
result := canonical_type(module, item.child, mapping, visiting)
|
||||
resolved := types.function(&module.type_store, params, result, item.c_abi, item.variadic)
|
||||
mapping[index] = resolved
|
||||
return resolved
|
||||
}
|
||||
if types.is_valid(item.child) {
|
||||
item.child = canonical_type(module, item.child, mapping, visiting)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user