self-referential c record imports
This commit is contained in:
@@ -415,6 +415,11 @@ populate_record :: proc(ctx: ^Context, index: u32, declaration: CXCursor) {
|
|||||||
ctx.result.records[index].kind = .Union if ctx.api.get_cursor_kind(definition) == CXCursor_UnionDecl else .Struct
|
ctx.result.records[index].kind = .Union if ctx.api.get_cursor_kind(definition) == CXCursor_UnionDecl else .Struct
|
||||||
ctx.result.records[index].size = u64(size)
|
ctx.result.records[index].size = u64(size)
|
||||||
ctx.result.records[index].alignment = u32(alignment)
|
ctx.result.records[index].alignment = u32(alignment)
|
||||||
|
// Mark in-progress before visiting fields: a self-referential field (e.g. the
|
||||||
|
// `struct __sFILE *` inside FILE) re-enters populate_record for this same record;
|
||||||
|
// the top guard now short-circuits instead of recursing forever. The final line
|
||||||
|
// recomputes the real value from `reason`.
|
||||||
|
ctx.result.records[index].complete = true
|
||||||
field_ctx := Record_Field_Context{ctx=ctx, record=index}
|
field_ctx := Record_Field_Context{ctx=ctx, record=index}
|
||||||
_ = ctx.api.visit_children(definition, visit_record_field, &field_ctx)
|
_ = ctx.api.visit_children(definition, visit_record_field, &field_ctx)
|
||||||
ctx.result.records[index].complete = len(ctx.result.records[index].reason) == 0
|
ctx.result.records[index].complete = len(ctx.result.records[index].reason) == 0
|
||||||
|
|||||||
@@ -640,9 +640,23 @@ is_c_signature_type :: proc(value: Type, store: ^Store, allow_void := false) ->
|
|||||||
(is_c_struct(value, store) && is_runtime_value(value, store))
|
(is_c_struct(value, store) && is_runtime_value(value, store))
|
||||||
}
|
}
|
||||||
|
|
||||||
contains_distinct :: proc(value: Type, store: ^Store, depth := 0) -> bool {
|
// contains_distinct reports whether a `distinct` type is reachable from `value`
|
||||||
if depth > 256 {
|
// (by value, behind a pointer, through fields/params/children). A pointer to a
|
||||||
return true
|
// distinct type still counts — distinct types do not cross the C ABI even behind
|
||||||
|
// indirection. The `seen` set makes the graph walk terminate on self-referential
|
||||||
|
// records (e.g. `?*mut Node` inside `Node`), which previously recursed until the
|
||||||
|
// depth cap and wrongly reported `true`.
|
||||||
|
contains_distinct :: proc(value: Type, store: ^Store) -> bool {
|
||||||
|
seen: [dynamic]Type
|
||||||
|
defer delete(seen)
|
||||||
|
return contains_distinct_seen(value, store, &seen)
|
||||||
|
}
|
||||||
|
|
||||||
|
contains_distinct_seen :: proc(value: Type, store: ^Store, seen: ^[dynamic]Type) -> bool {
|
||||||
|
for visited in seen {
|
||||||
|
if visited == value {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
item, ok := node(store, value)
|
item, ok := node(store, value)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -651,21 +665,22 @@ contains_distinct :: proc(value: Type, store: ^Store, depth := 0) -> bool {
|
|||||||
if item.kind == .Distinct {
|
if item.kind == .Distinct {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
append(seen, value)
|
||||||
if item.kind == .Struct || item.kind == .Union {
|
if item.kind == .Struct || item.kind == .Union {
|
||||||
for field in fields_for(store, value) {
|
for field in fields_for(store, value) {
|
||||||
if contains_distinct(field.type, store, depth+1) {
|
if contains_distinct_seen(field.type, store, seen) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if item.kind == .Function {
|
if item.kind == .Function {
|
||||||
for param in params_for(store, value) {
|
for param in params_for(store, value) {
|
||||||
if contains_distinct(param.type, store, depth+1) {
|
if contains_distinct_seen(param.type, store, seen) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return is_valid(item.child) && contains_distinct(item.child, store, depth+1)
|
return is_valid(item.child) && contains_distinct_seen(item.child, store, seen)
|
||||||
}
|
}
|
||||||
|
|
||||||
is_c_integer_promotion_candidate :: proc(value: Type) -> bool {
|
is_c_integer_promotion_candidate :: proc(value: Type) -> bool {
|
||||||
|
|||||||
@@ -1972,6 +1972,20 @@ by_value_c_records_and_unions_compile_and_link :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, state.exit_code, 1)
|
testing.expect_value(t, state.exit_code, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
self_referential_c_records_import_and_compile :: proc(t: ^testing.T) {
|
||||||
|
// `struct Node { struct Node *next; int value; }`: the importer must not recurse
|
||||||
|
// forever populating the self-referential record, and the checker must accept the
|
||||||
|
// self-referential `?*mut Node` field as C-layout-compatible.
|
||||||
|
output := "/tmp/brolang-test-recursive"
|
||||||
|
defer _ = os.remove(output)
|
||||||
|
c_options := cimport.Options{include_paths=[]string{"examples/interop/recursive/include"}}
|
||||||
|
status := compiler_core.compile_package("examples/interop/recursive/app", output, nil, target.DEFAULT, c_options)
|
||||||
|
testing.expect_value(t, status, 0)
|
||||||
|
state := run_executable(output)
|
||||||
|
testing.expect_value(t, state.exit_code, 0)
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
unsupported_c_header_members_diagnose_only_when_referenced :: proc(t: ^testing.T) {
|
unsupported_c_header_members_diagnose_only_when_referenced :: proc(t: ^testing.T) {
|
||||||
output := "/tmp/brolang-test-header-unsupported"
|
output := "/tmp/brolang-test-header-unsupported"
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
native :: import "../include/native.h"
|
||||||
|
|
||||||
|
# Exercises a self-referential imported C record (`struct Node { struct Node *next; int value; }`):
|
||||||
|
# the importer must not recurse forever populating it, and the checker must accept the
|
||||||
|
# self-referential `?*mut Node` field as C-layout-compatible.
|
||||||
|
|
||||||
|
main :: func() i32 {
|
||||||
|
node native.Node = native.Node { next = none, value = 7 }
|
||||||
|
if node.next |_| {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if node.value == 7 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 2
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
typedef struct Node {
|
||||||
|
struct Node *next;
|
||||||
|
int value;
|
||||||
|
} Node;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# generated by brolang translate-c from testbed/cstdio.h
|
||||||
|
|
||||||
|
printf :: c_func(arg0 ?*c_char, ...) c_int
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
int printf(const char *format, ...);
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
printf :: c_func(format *c_char, ...) c_int
|
|
||||||
|
|
||||||
PlayerID :: distinct u32
|
PlayerID :: distinct u32
|
||||||
|
|
||||||
Tier :: enum(u8) {
|
Tier :: enum(u8) {
|
||||||
|
|||||||
Reference in New Issue
Block a user