From 013cf9f1bb34cbb34e1189a3ec2be197f90aed04 Mon Sep 17 00:00:00 2001 From: agra Date: Wed, 20 May 2026 12:51:48 +0300 Subject: [PATCH] ffi 2.16c xfail: omitted env in `#jni_call` from cross-function helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A `#jni_call(void)(target, "name", "sig")` inside a helper fn that isn't lexically inside a `#jni_env` block should fall back to a thread-local env read populated by the enclosing `#jni_env(env) { helper(target); }` scope at runtime. Today the lower-side "jni_env_stack empty" diagnostic gets queued but compilation continues to emit_llvm, which fails LLVM verification because env lowers to `Ref.none` (`i64 undef`). The make-green follow-up: - Synthesizes a thread-local `@sx_jni_env_tl` global in emit_llvm. - `#jni_env(env) { body }` emits a `(load TL → saved, store env → TL, defer store saved → TL)` sequence so the TL tracks the innermost-scope env and restores correctly on nesting. - `lowerJniCall`'s omitted-env path falls back to a TL load when `jni_env_stack` is empty, instead of erroring. The lexical-direct optimisation from 2.16b stays the fast path — helpers in the same fn never touch TL. Only cross-fn callees pay the (cheap) TL load. --- examples/ffi-jni-env-03-tl-fallback.sx | 37 +++++++++++++++++++ .../expected/ffi-jni-env-03-tl-fallback.exit | 1 + tests/expected/ffi-jni-env-03-tl-fallback.txt | 14 +++++++ 3 files changed, 52 insertions(+) create mode 100644 examples/ffi-jni-env-03-tl-fallback.sx create mode 100644 tests/expected/ffi-jni-env-03-tl-fallback.exit create mode 100644 tests/expected/ffi-jni-env-03-tl-fallback.txt diff --git a/examples/ffi-jni-env-03-tl-fallback.sx b/examples/ffi-jni-env-03-tl-fallback.sx new file mode 100644 index 0000000..e7a1f12 --- /dev/null +++ b/examples/ffi-jni-env-03-tl-fallback.sx @@ -0,0 +1,37 @@ +// Phase 2 step 2.16c (PLAN-FFI.md): xfail then green for the +// thread-local env fallback in `#jni_call`. When a `#jni_call` site +// has its env arg omitted AND no `#jni_env` block exists in the same +// function (e.g., we're in a helper called FROM such a block), the +// compiler emits a TL load instead of a sema error. +// +// The TL is pushed/popped by the `#jni_env(env) { ... }` enclosing +// scope at runtime; helpers that don't see the lexical scope still +// pick up the env transparently. Cross-function callers no longer +// need to thread env as an explicit parameter. +// +// Today (2.16b only): lowerJniCall errors when env is omitted and +// jni_env_stack is empty, because TL emission isn't wired yet. + +#import "modules/std.sx"; + +g_should_call : bool = false; + +// Helper fn — no `#jni_env` block in scope. Without TL fallback this +// errors because the omitted env can't be resolved. +helper :: (target: *void) { + #jni_call(void)(target, "noop", "()V"); +} + +unused :: (env: *void, target: *void) { + #jni_env(env) { + helper(target); + } +} + +main :: () -> s32 { + if g_should_call { + unused(null, null); + } + print("ok\n"); + 0; +} diff --git a/tests/expected/ffi-jni-env-03-tl-fallback.exit b/tests/expected/ffi-jni-env-03-tl-fallback.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/expected/ffi-jni-env-03-tl-fallback.exit @@ -0,0 +1 @@ +1 diff --git a/tests/expected/ffi-jni-env-03-tl-fallback.txt b/tests/expected/ffi-jni-env-03-tl-fallback.txt new file mode 100644 index 0000000..e8dc2da --- /dev/null +++ b/tests/expected/ffi-jni-env-03-tl-fallback.txt @@ -0,0 +1,14 @@ +LLVM verification failed: Load operand must be a pointer. + %jni.ifs = load ptr, i64 undef, align 8 +Call parameter type does not match function signature! +i64 undef + ptr %jni.cls = call ptr %jni.GetObjectClass(i64 undef, ptr %load) +Call parameter type does not match function signature! +i64 undef + ptr %jni.global.cls = call ptr %jni.NewGlobalRef(i64 undef, ptr %jni.cls) +Call parameter type does not match function signature! +i64 undef + ptr %jni.fresh.mid = call ptr %jni.GetMethodID(i64 undef, ptr %jni.global.cls, ptr @str, ptr @str.1) +Call parameter type does not match function signature! +i64 undef + ptr call void %jni.callfn(i64 undef, ptr %load, ptr %jni.mid)