imports: read resolved C-import paths after mutation, not before

Latent bug from the stdlib-path resolution introduced in 4849cfb.
The earlier shape captured `const ci = decl.data.c_import_decl;`
BEFORE mutating `decl.data.c_import_decl.{sources,includes}` with
the resolved paths, then passed the stale `ci.includes` to
`c_import.processCImport`. Result: `#include "vendors/..."` paths
that resolved via the stdlib branch (i.e. only existed under
sx/library/vendors/) reached clang as the original unresolved
string and failed to parse — silently producing no synthesized
`#foreign` decls.

`#source` survived because the source list is re-read from
decl.data later (collectCImportSources walks the AST), so it
picked up the mutated value. Only `#include`'s synthesis path was
broken.

Fix: do the resolution first inside its own scope, then re-bind
`ci` from `decl.data.c_import_decl` so the include list passed to
processCImport sees the resolved paths.

Caught by ffi-07 baseline (next commit) — the test deliberately
puts its C helper only under library/vendors/ so the path is
findable solely via the stdlib chain.
This commit is contained in:
agra
2026-05-19 11:51:20 +03:00
parent 31ab175d56
commit 43a30e727d

View File

@@ -200,27 +200,29 @@ pub fn resolveImports(
for (root.data.root.decls) |decl| {
if (decl.data == .c_import_decl) {
const ci = decl.data.c_import_decl;
// Resolve `#source` / `#include` paths through the same chain
// as `#import`: importing-file's directory → CWD → stdlib
// search paths. This lets sx-library modules ship their own
// C helpers (e.g. the Android JNI insets bridge) without
// forcing every consumer to vendor an identically-named copy.
if (ci.sources.len > 0) {
var resolved = try allocator.alloc([]const u8, ci.sources.len);
for (ci.sources, 0..) |raw_src, idx| {
resolved[idx] = try resolveImportPath(allocator, io, base_dir, raw_src, null, stdlib_paths);
{
const ci_pre = decl.data.c_import_decl;
if (ci_pre.sources.len > 0) {
var resolved = try allocator.alloc([]const u8, ci_pre.sources.len);
for (ci_pre.sources, 0..) |raw_src, idx| {
resolved[idx] = try resolveImportPath(allocator, io, base_dir, raw_src, null, stdlib_paths);
}
decl.data.c_import_decl.sources = resolved;
}
decl.data.c_import_decl.sources = resolved;
}
if (ci.includes.len > 0) {
var resolved = try allocator.alloc([]const u8, ci.includes.len);
for (ci.includes, 0..) |raw_inc, idx| {
resolved[idx] = try resolveImportPath(allocator, io, base_dir, raw_inc, null, stdlib_paths);
if (ci_pre.includes.len > 0) {
var resolved = try allocator.alloc([]const u8, ci_pre.includes.len);
for (ci_pre.includes, 0..) |raw_inc, idx| {
resolved[idx] = try resolveImportPath(allocator, io, base_dir, raw_inc, null, stdlib_paths);
}
decl.data.c_import_decl.includes = resolved;
}
decl.data.c_import_decl.includes = resolved;
}
const ci = decl.data.c_import_decl;
// Parse headers to get synthetic function declarations
const result = c_import.processCImport(