Rust Foundation Adapters & FFI
Relevant source files
The following files were used as context for generating this wiki page:
Rust Foundation Adapters and the Foreign Function Interface (FFI) mechanism in Pop Lang provide a structured, type-safe bridge between the Pop Lang runtime and native host implementations. This system is primarily utilized by the foundational libraries—Pop.Internal and Pop.Standard—to implement performance-critical intrinsics, garbage collection (GC) bridges, and platform-specific services like I/O and networking.
The architecture ensures that native transitions are explicit and backend-neutral. Rather than exposing raw C pointers or unmanaged memory directly to the user, the FFI layer uses typed descriptors and a mandatory attribute-driven contract to maintain safety and consistency across different backends, such as LLVM or the MIR interpreter.
Sources: architecture/16-base-libraries.md:1-15, crates/libraries/README.md:1-10
Architecture and Design Principles
The FFI system is built on the principle that Pop Lang remains a native, strongly typed language where every runtime operation has a compiler-proven type. Native adapters are restricted to specific "bridge" modules within the library crates and are never authorized to redefine language semantics.
Component Hierarchy
The relationship between Pop code, the Rust bridge, and the runtime is strictly defined to prevent unauthorized dependencies.
This diagram shows the flow from high-level Pop source code down to the execution of native logic via the specialized adapter layer.
Sources: architecture/16-base-libraries.md:5-15, crates/libraries/README.md:124-140
Key Constraints
| Constraint | Description |
|---|---|
| No String Dispatch | Native functions are resolved via stable semantic IDs, not by searching for strings at runtime. |
| Explicit Effects | Native transitions must declare their effects (e.g., AmbientIo) which are tracked by the compiler. |
| Backend Neutrality | HIR and MIR see backend-neutral identities, ensuring the same Pop code works on LLVM and interpreters. |
| Explicit Inventory | Every native export must be included in a static NATIVE_EXPORTS slice for verification. |
| Sources: architecture/decisions/0037-typed-rust-foundation-adapter-attribute.md:15-30, architecture/08.1-closed-design-questions.md:275-290 |
The #[poplib] Attribute
The core of the FFI system is the #[poplib] procedural macro. This attribute is applied to Rust functions to export them to the Pop Lang environment. It performs compile-time validation of the Rust function signature against the declared Pop metadata.
Metadata Requirements
The macro requires specific parameters to bind a Rust function to a Pop declaration:
- bubble: Specifies the target Bubble (e.g.,
StandardorInternal). - namespace: The Pop namespace containing the declaration.
- name: The specific function name in Pop.
- parameters: An array of types matching the Pop function signature.
- results: An array of result types.
- effects: A list of effects allowed during the function's execution.
#[poplib(
bubble = Standard,
namespace = "Pop",
name = "print",
parameters(Int),
results(),
effects(AmbientIo),
)]
pub extern "C" fn pop_std_print_int(value: i64) {
// Native implementation logic
}
Sources: crates/libraries/macros/src/lib.rs:5-15, crates/libraries/README.md:144-155
Native Export Descriptors
When the #[poplib] macro is used, it generates a NativeExport descriptor. These descriptors are collected into a static inventory within the library crates. The runtime uses these inventories to link Pop call sites to the appropriate native machine code.
Descriptor Structure
The pop-library-bridge crate defines the structure of these descriptors to ensure they can be shared between the compiler-time macro and the runtime.
| Field | Type | Purpose |
|---|---|---|
bubble | BubbleIdentity | Identifies if the export belongs to Internal or Standard. |
symbol_id | SymbolId | A stable, unique identifier for the function. |
native_fn | extern "C" fn(...) | The actual function pointer to the Rust implementation. |
signature_hash | u64 | A hash of the parameter/result types for integrity verification. |
| Sources: crates/libraries/bridge/src/lib.rs:10-50, architecture/decisions/0037-typed-rust-foundation-adapter-attribute.md:35-50 |
Execution Flow
The sequence of a native call involves the PLRI resolving the SymbolId provided by the MIR and invoking the function pointer stored in the descriptor.
This diagram illustrates the verification and invocation sequence during a call to a native-backed Pop function.
Sources: crates/libraries/bridge/src/lib.rs:1-15, crates/tools/architecture-tests/src/tests.rs:590-610
Implementation and Verification
The project enforces strict directory and crate boundaries to prevent FFI logic from leaking into the core compiler.
Directory Layout
crates/libraries/bridge/: Defines theNativeExportshared descriptors.crates/libraries/macros/: Implements the#[poplib]procedural macro.crates/libraries/standard/src/: Contains native adapters for the Standard library.crates/libraries/internal/src/: Contains trusted intrinsic and GC adapters.
Sources: crates/libraries/README.md:40-55, crates/tools/architecture-tests/src/tests.rs:10-50
Conformance Testing
Architecture tests verify that:
- Native adapters do not use
#[unsafe(no_mangle)]directly, favoring the#[poplib]contract. NATIVE_EXPORTSinventories are explicitly declared and visible.- Forbidden dependencies (like
std::fsin standard library adapters) are not introduced without authorization.
Sources: crates/tools/architecture-tests/src/tests.rs:595-615, crates/libraries/README.md:158-170
Conclusion
The Rust Foundation Adapters and FFI system provide a robust framework for extending Pop Lang with native capabilities while maintaining the language's core invariants of safety and backend-neutrality. By utilizing procedural macros and static inventories, the system ensures that every native transition is verified, documented, and explicitly authorized by the project's architecture.