53 lines
3.5 KiB
Markdown
53 lines
3.5 KiB
Markdown
---
|
|
id: LSN-0030
|
|
ticket: pbs-service-facade-reserved-metadata
|
|
title: SDK Service Bodies Over Private Reserved Proxies
|
|
created: 2026-04-03
|
|
tags: [compiler, pbs, sdk, stdlib, lowering, service, intrinsic, sdk-interface]
|
|
---
|
|
|
|
## Context
|
|
|
|
PBS originally supported two clear execution shapes for SDK surfaces:
|
|
|
|
- public service wrappers over reserved hosts such as `Log`, `Gfx`, and `Assets`;
|
|
- public builtin roots that lower directly to intrinsics.
|
|
|
|
The `@sdk:input` surface exposed a third requirement: keep the public API as a normal `service`, keep the builtin proxy private, and still allow user code like `Input.touch().x()` and `Input.pad().a().pressed()` to compile and lower correctly.
|
|
|
|
The core pitfall was assuming the public callsite itself still had to lower directly to `CALL_INTRINSIC`. That assumption forced the public API shape back toward builtin roots and broke the intended service abstraction.
|
|
|
|
## Key Decisions
|
|
|
|
### Executable SDK Service Bodies for Builtin/Intrinsic-Backed Public Services
|
|
|
|
**What:**
|
|
The accepted model keeps `SDK_INTERFACE` non-executable by default, but allows a restricted executable subset for SDK service methods. Public callsites such as `Input.touch()` lower as ordinary callable invocations of imported SDK service methods, while intrinsic lowering happens inside those imported method bodies when they call private reserved proxies such as `LowInput`.
|
|
|
|
**Why:**
|
|
This preserves the intended product-facing API shape. SDK authors can write ordinary service bodies with locals and intermediate processing instead of exposing private builtin roots or inventing a second-class metadata-only facade system.
|
|
|
|
**Trade-offs:**
|
|
The compiler must preserve owner/chaining semantics across callable returns, not only across direct intrinsic callsites. It also needs an explicit subset boundary and dedicated diagnostics so the executable exception for `SDK_INTERFACE` does not silently widen into arbitrary interface execution.
|
|
|
|
## Patterns and Algorithms
|
|
|
|
- Model the public SDK API as a normal service surface and keep the private reserved proxy internal.
|
|
- Let the public user callsite lower as `CALL_FUNC` when the service method is the correct public abstraction.
|
|
- Preserve reserved owner metadata on callable returns so chained expressions continue to resolve after the service call boundary.
|
|
- Validate executable SDK service bodies against an explicit initial subset instead of inferring support from whatever lowering happens to tolerate.
|
|
- Emit dedicated diagnostics for unsupported SDK executable constructs rather than collapsing to a generic unresolved-callee error.
|
|
|
|
## Pitfalls
|
|
|
|
- Do not assume every intrinsic-backed public SDK surface should lower directly from the public callsite to `CALL_INTRINSIC`.
|
|
- Do not leak private builtin roots into the public API merely because the existing lowering path already understands them.
|
|
- Do not add a broad executable mode to all `SDK_INTERFACE` declarations when only a narrow service-method subset is intended.
|
|
- Do not rely on old tests that assert direct intrinsic callsites once the accepted model changes to callable public entrypoints plus intrinsic lowering inside imported SDK bodies.
|
|
|
|
## Takeaways
|
|
|
|
- Preserve the public API shape first, then adapt lowering to it when the abstraction is intentional.
|
|
- Owner propagation across callable returns is the critical mechanism that makes service-over-intrinsic wrappers behave like ordinary PBS code.
|
|
- A restricted executable subset plus dedicated diagnostics is safer than implicit support for arbitrary interface code.
|