From b75b2a5825ee7b9d775fa8cbe31793046116f974 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Fri, 27 Mar 2026 18:23:20 +0000 Subject: [PATCH] implements PLN-0008 --- discussion/index.ndjson | 4 +- ...ring-host-metadata-and-callsite-rewrite.md | 196 ++++++++++++++++++ .../pbs/12. Diagnostics Specification.md | 7 + .../13. Lowering IRBackend Specification.md | 1 + .../pbs/4. Static Semantics Specification.md | 7 +- ...ing and Loader Resolution Specification.md | 5 +- ...ent Packaging and Loading Specification.md | 7 +- ...RBackend to IRVM Lowering Specification.md | 2 + .../pbs/PbsReservedMetadataExtractor.java | 12 ++ .../lowering/PbsExecutableBodyLowerer.java | 40 +++- .../PbsExecutableCallsiteEmitter.java | 17 ++ .../PbsDeclarationSemanticsValidator.java | 57 ++++- .../resources/stdlib/1/sdk/asset/main.pbs | 3 +- .../compiler/pbs/PbsFrontendCompilerTest.java | 67 +++++- .../PbsGateUSdkInterfaceConformanceTest.java | 6 +- .../PbsInterfaceModuleSemanticsTest.java | 21 +- .../pbs/stdlib/InterfaceModuleLoaderTest.java | 3 +- .../services/PBSFrontendPhaseServiceTest.java | 3 +- .../compiler/models/IRReservedMetadata.java | 4 + 19 files changed, 444 insertions(+), 18 deletions(-) create mode 100644 discussion/workflow/plans/PLN-0008-pbs-assetlowering-host-metadata-and-callsite-rewrite.md diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 47eb56dc..6c4909ae 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,9 +1,9 @@ -{"type":"meta","next_id":{"DSC":9,"AGD":9,"DEC":7,"PLN":8,"LSN":24,"CLSN":1}} +{"type":"meta","next_id":{"DSC":9,"AGD":9,"DEC":7,"PLN":9,"LSN":24,"CLSN":1}} {"type":"discussion","id":"DSC-0001","status":"done","ticket":"studio-docs-import","title":"Import docs/studio into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["studio","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0001-assets-workspace-execution-wave-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0002","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0002-bank-composition-editor-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0003","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0003-mental-model-asset-mutations-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0004","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0004-mental-model-assets-workspace-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0005","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0005-mental-model-studio-events-and-components-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0006","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0006-mental-model-studio-shell-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0007","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0007-pack-wizard-shell-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0008","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0008-project-scoped-state-and-activity-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0016","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0016-studio-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]} {"type":"discussion","id":"DSC-0002","status":"open","ticket":"palette-management-in-studio","title":"Palette Management in Studio","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["studio","legacy-import","palette-management","tile-bank","packer-boundary"],"agendas":[{"id":"AGD-0002","file":"AGD-0002-palette-management-in-studio.md","status":"open","created_at":"2026-03-26","updated_at":"2026-03-26"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0003","status":"done","ticket":"packer-docs-import","title":"Import docs/packer into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0009","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0009-mental-model-packer-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0010","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0010-asset-identity-and-runtime-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0011","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0011-foundations-workspace-runtime-and-build-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0012","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0012-runtime-ownership-and-studio-boundary-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0013","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0013-metadata-convergence-and-runtime-sink-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0014","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0014-pack-wizard-summary-validation-and-pack-execution-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0015","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0015-tile-bank-packing-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0017","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0017-packer-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]} {"type":"discussion","id":"DSC-0004","status":"open","ticket":"tilemap-and-metatile-runtime-binary-layout","title":"Tilemap and Metatile Runtime Binary Layout","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","legacy-import","tilemap","metatile","runtime-layout"],"agendas":[{"id":"AGD-0004","file":"AGD-0004-tilemap-and-metatile-runtime-binary-layout.md","status":"open","created_at":"2026-03-26","updated_at":"2026-03-26"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0005","status":"open","ticket":"variable-tile-bank-palette-serialization","title":"Variable Tile Bank Palette Serialization","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","legacy-import","tile-bank","palette-serialization","versioning"],"agendas":[{"id":"AGD-0005","file":"AGD-0005-variable-tile-bank-palette-serialization.md","status":"open","created_at":"2026-03-26","updated_at":"2026-03-26"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0006","status":"open","ticket":"pbs-game-facing-asset-refs-and-call-result-discard","title":"PBS Game-Facing Asset References and Ignored Call Result Lowering","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","ergonomics","lowering","runtime","asset-identity","expression-statements"],"agendas":[{"id":"AGD-0006","file":"AGD-0006-pbs-game-facing-asset-refs-and-call-result-discard.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[{"id":"DEC-0005","file":"DEC-0005-pbs-asset-address-surface-and-be-lowering.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-03-27","ref_agenda":"AGD-0006"},{"id":"DEC-0006","file":"DEC-0006-pbs-ignored-values-lowering-and-warning.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-03-27","ref_agenda":"AGD-0006"}],"plans":[{"id":"PLN-0005","file":"PLN-0005-pbs-asset-address-surface-spec-propagation.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27","ref_decisions":["DEC-0005"]},{"id":"PLN-0006","file":"PLN-0006-pbs-asset-address-surface-implementation.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27","ref_decisions":["DEC-0005"]},{"id":"PLN-0007","file":"PLN-0007-pbs-ignored-values-warning-implementation.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27","ref_decisions":["DEC-0006"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0006","status":"open","ticket":"pbs-game-facing-asset-refs-and-call-result-discard","title":"PBS Game-Facing Asset References and Ignored Call Result Lowering","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","ergonomics","lowering","runtime","asset-identity","expression-statements"],"agendas":[{"id":"AGD-0006","file":"AGD-0006-pbs-game-facing-asset-refs-and-call-result-discard.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[{"id":"DEC-0005","file":"DEC-0005-pbs-asset-address-surface-and-be-lowering.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-03-27","ref_agenda":"AGD-0006"},{"id":"DEC-0006","file":"DEC-0006-pbs-ignored-values-lowering-and-warning.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-03-27","ref_agenda":"AGD-0006"}],"plans":[{"id":"PLN-0005","file":"PLN-0005-pbs-asset-address-surface-spec-propagation.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27","ref_decisions":["DEC-0005"]},{"id":"PLN-0006","file":"PLN-0006-pbs-asset-address-surface-implementation.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27","ref_decisions":["DEC-0005"]},{"id":"PLN-0007","file":"PLN-0007-pbs-ignored-values-warning-implementation.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27","ref_decisions":["DEC-0006"]},{"id":"PLN-0008","file":"PLN-0008-pbs-assetlowering-host-metadata-and-callsite-rewrite.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27","ref_decisions":["DEC-0005"]}],"lessons":[]} {"type":"discussion","id":"DSC-0007","status":"done","ticket":"pbs-learn-to-discussion-lessons-migration","title":"Migrate PBS Learn Documents into Discussion Lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","migration","discussion-framework","lessons","learn-prune"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0018","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0018-pbs-ast-and-parser-contract-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0019","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0019-pbs-name-resolution-and-linking-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0020","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0020-pbs-runtime-values-identity-memory-boundaries-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0021","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0021-pbs-diagnostics-and-conformance-governance-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0022","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0022-pbs-globals-lifecycle-and-published-entrypoint-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} {"type":"discussion","id":"DSC-0008","status":"done","ticket":"pbs-low-level-asset-manager-surface","title":"PBS Low-Level Asset Manager Surface for Runtime AssetManager","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","runtime","asset-manager","host-abi","stdlib","asset"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0023","file":"discussion/lessons/DSC-0008-pbs-low-level-asset-manager-surface/LSN-0023-lowassets-runtime-aligned-sdk-surface.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} diff --git a/discussion/workflow/plans/PLN-0008-pbs-assetlowering-host-metadata-and-callsite-rewrite.md b/discussion/workflow/plans/PLN-0008-pbs-assetlowering-host-metadata-and-callsite-rewrite.md new file mode 100644 index 00000000..477eefd1 --- /dev/null +++ b/discussion/workflow/plans/PLN-0008-pbs-assetlowering-host-metadata-and-callsite-rewrite.md @@ -0,0 +1,196 @@ +--- +id: PLN-0008 +ticket: pbs-game-facing-asset-refs-and-call-result-discard +title: Implement Mandatory AssetLowering Host Metadata and Backend Callsite Rewrite +status: done +created: 2026-03-27 +completed: 2026-03-27 +tags: [compiler, pbs, implementation, assetlowering, host-metadata, backend-lowering, correction] +--- + +## Objective + +Complete the missing normative part of `DEC-0005` by implementing mandatory host-backed `AssetLowering` metadata and using it to rewrite `Addressable` arguments into runtime-facing `asset_id` at backend callsite lowering time. + +## Background + +`DEC-0005` did not only require: + +- `FESurfaceContext = List`, +- symbolic `assets...` semantics, +- and backend ownership of final validation. + +It also normatively locked a second requirement: + +- asset-aware lowering metadata must live on the host-backed declaration surface; +- the backend must use that metadata to identify which host parameter is asset-facing; +- and host-backed lowering must rewrite the `Addressable` argument to operational `asset_id` before the final `LowAssets` path. + +`PLN-0006` implemented the symbolic surface and direct symbolic-to-`asset_id` expression lowering, but it did not implement: + +- reserved attribute `[AssetLowering(param = N)]`, +- reserved-metadata propagation for host bindings, +- host-signature validation for asset-lowered parameters, +- or host-callsite rewriting driven by parameter metadata. + +This plan exists to correct that gap without reinterpreting `DEC-0005`. + +## Scope + +### Included +- Add `AssetLowering(param = N)` as a PBS reserved attribute on host-backed signatures. +- Validate the attribute shape and its admissible target surface. +- Extend backend host-binding metadata to carry asset-lowering parameter information. +- Rewrite host call lowering so asset-facing parameters consume symbolic `Addressable` values and lower them to `asset_id` according to host metadata. +- Align tests and specs so the normative path is explicit and covered. +- Correct any now-misleading tests or implementation shortcuts introduced by `PLN-0006`. + +### Excluded +- Redesign of `DEC-0005`. +- Changes to the `LowAssets` ABI already fixed by `DEC-0004`. +- Generalization to non-asset metadata families beyond the exact `AssetLowering` rule. +- Runtime-dynamic asset lookup exceptions. + +## Execution Steps + +### Step 1 - Add Reserved Attribute Surface For AssetLowering + +**What:** +Introduce `[AssetLowering(param = N)]` as a normative reserved attribute on host-backed callable surfaces. + +**How:** +Extend the PBS reserved attribute extractor and declaration validation so: + +- `AssetLowering` is recognized as reserved metadata; +- it is admitted only on supported host-backed callable declarations; +- `param` is mandatory and must be a valid zero-based parameter index; +- the targeted parameter must match the `Addressable` author-facing contract required by `DEC-0005`; +- duplicate or malformed declarations fail deterministically. + +**File(s):** +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsReservedMetadataExtractor.java` +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsDeclarationSemanticsValidator.java` +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsSemanticsErrors.java` +- tests under `prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/` + +### Step 2 - Propagate AssetLowering Through Reserved Metadata + +**What:** +Make host binding metadata carry the asset-lowering contract instead of relying on implicit expression-level lowering. + +**How:** +Extend `IRReservedMetadata.HostMethodBinding` or the equivalent host-binding metadata shape so each binding can expose: + +- whether asset lowering is required; +- which parameter index is asset-facing. + +Update metadata construction, merging, indexing, and any downstream readers so the information survives all backend-facing frontend phases. + +**File(s):** +- `prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRReservedMetadata.java` +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsReservedMetadataExtractor.java` +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableLoweringModels.java` +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableMetadataIndexFactory.java` +- metadata conformance tests under `prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/` + +### Step 3 - Rewrite Host Calls Using AssetLowering Metadata + +**What:** +Move final `Addressable -> asset_id` host-argument rewriting into the backend-owned host call path mandated by `DEC-0005`. + +**How:** +Update executable lowering so: + +- host callsites inspect `AssetLowering(param = N)` metadata from the resolved host binding; +- the backend validates that the selected argument is a terminal backend-known `Addressable`; +- the selected argument is lowered to operational `asset_id`; +- non-asset-facing arguments are lowered normally; +- the final emitted host call matches the already accepted `LowAssets` runtime path. + +This step must remove reliance on the current shortcut where symbolic asset references are lowered generically without host-parameter ownership. + +**File(s):** +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableBodyLowerer.java` +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableCallsiteEmitter.java` +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableLoweringContext.java` +- related lowering metadata/index files +- targeted lowering tests under `prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/` + +### Step 4 - Realign Symbolic Expression Lowering With The Host-Metadata Contract + +**What:** +Correct the partial implementation from `PLN-0006` so symbolic asset expressions support the normative host-metadata path instead of bypassing it. + +**How:** +Review the current direct lowering of `assets...` expressions and keep only the behavior that remains valid under `DEC-0005`. + +At minimum: + +- standalone symbolic `Addressable` semantics must remain correct; +- host-backed `AssetLowering` callsites must no longer depend on an implicit generic shortcut; +- tests that previously “passed” through direct expression lowering must be updated to assert the host-metadata path explicitly. + +If any `PLN-0006` behavior contradicts `DEC-0005`, correct the code and the tests rather than preserving the shortcut. + +**File(s):** +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/` +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/` +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java` +- `prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java` + +### Step 5 - Propagate And Pin The Contract In Specs And Tests + +**What:** +Ensure the normative wording and regression coverage now match the corrected implementation. + +**How:** +Update specs and tests so they explicitly cover: + +- `AssetLowering(param = N)` as mandatory host-backed metadata for asset-aware lowering; +- the backend-owned rewrite from symbolic `Addressable` to operational `asset_id`; +- rejection of malformed attribute usage and invalid parameter targeting; +- a representative `LowAssets`-aligned host call path using the metadata-bearing surface. + +**File(s):** +- `docs/specs/compiler-languages/pbs/4. Static Semantics Specification.md` +- `docs/specs/compiler-languages/pbs/12. Diagnostics Specification.md` +- `docs/specs/compiler-languages/pbs/13. Lowering IRBackend Specification.md` +- `docs/specs/compiler/20. IRBackend to IRVM Lowering Specification.md` +- targeted PBS frontend/compiler tests + +## Test Requirements + +### Unit Tests +- Reserved-attribute tests for valid and invalid `AssetLowering(param = N)` usage. +- Metadata tests proving host bindings preserve the asset-lowering parameter index. +- Negative tests for duplicate, malformed, or out-of-range `AssetLowering` declarations. + +### Integration Tests +- End-to-end PBS frontend compilation tests where a host-backed asset API uses `Addressable` plus `[AssetLowering(param = N)]`. +- Lowering tests proving the backend rewrites the marked argument to `asset_id` before the final host call. +- Regression tests proving `LowAssets`-aligned callsites still lower correctly after removing the shortcut path. + +### Manual Verification +- Inspect extracted reserved metadata and verify `AssetLowering` is carried on the host binding. +- Inspect lowered instruction streams for an asset-aware host call and verify the asset-facing argument becomes `asset_id`. +- Confirm malformed host metadata fails with deterministic source-facing diagnostics. + +## Acceptance Criteria + +- [ ] PBS supports `[AssetLowering(param = N)]` as a validated reserved attribute on the correct host-backed surface. +- [ ] Host binding metadata preserves the asset-lowering parameter contract through backend lowering preparation. +- [ ] Backend host-call lowering uses the metadata to rewrite the selected `Addressable` argument into runtime-facing `asset_id`. +- [ ] The final asset-aware host path remains aligned with `LowAssets` and no longer depends on a generic shortcut that bypasses host metadata. +- [ ] Tests and specs explicitly cover the normative `AssetLowering` path from `DEC-0005`. + +## Dependencies + +- `DEC-0005-pbs-asset-address-surface-and-be-lowering` +- `DEC-0004` low-level `LowAssets` contract +- existing `PLN-0006` implementation as the correction baseline + +## Risks + +- The current direct symbolic lowering path may overlap with the normative host-metadata path and require careful removal or restriction. +- Extending host metadata may require synchronized updates across multiple compiler layers, increasing the chance of partial propagation if done carelessly. +- If tests keep asserting the shortcut path instead of the normative path, the repository may regress into the same discrepancy again. diff --git a/docs/specs/compiler-languages/pbs/12. Diagnostics Specification.md b/docs/specs/compiler-languages/pbs/12. Diagnostics Specification.md index 2bd67633..e5eaa726 100644 --- a/docs/specs/compiler-languages/pbs/12. Diagnostics Specification.md +++ b/docs/specs/compiler-languages/pbs/12. Diagnostics Specification.md @@ -207,6 +207,13 @@ At minimum, the PBS diagnostics baseline must cover: - a stable warning when a materialized value is produced and then ignored, - and no warning for unit-like expression statements. +Reserved-attribute diagnostics for host-backed asset lowering must also cover: + +- duplicate `AssetLowering` declarations on the same host signature, +- missing or malformed `AssetLowering(param = N)` arguments, +- out-of-range `AssetLowering.param` indexes, +- and `AssetLowering` targets whose selected parameter is not statically typed as `Addressable`. + At minimum, host-admission diagnostics must cover missing or malformed host capability metadata and unknown or undeclared capability names. Only backend-originated failures that remain source-attributable and user-actionable belong to the PBS-facing diagnostics contract. diff --git a/docs/specs/compiler-languages/pbs/13. Lowering IRBackend Specification.md b/docs/specs/compiler-languages/pbs/13. Lowering IRBackend Specification.md index aaee5221..907040c0 100644 --- a/docs/specs/compiler-languages/pbs/13. Lowering IRBackend Specification.md +++ b/docs/specs/compiler-languages/pbs/13. Lowering IRBackend Specification.md @@ -185,6 +185,7 @@ When available at this boundary, declared host ABI shape (`arg_slots`, `ret_slot When a host-backed callsite also depends on backend-owned symbolic asset lowering: - the frontend boundary MUST preserve which callsite argument is asset-facing; +- for PBS v1 this asset-facing designation is carried by reserved host metadata `[AssetLowering(param = N)]`; - the preserved symbolic operand MUST remain attributable to a backend-provided `Addressable` identity; - backend stages MUST be able to rewrite that symbolic operand into runtime-facing `asset_id` before the final low-level host path is emitted. diff --git a/docs/specs/compiler-languages/pbs/4. Static Semantics Specification.md b/docs/specs/compiler-languages/pbs/4. Static Semantics Specification.md index 6999550b..1b724bec 100644 --- a/docs/specs/compiler-languages/pbs/4. Static Semantics Specification.md +++ b/docs/specs/compiler-languages/pbs/4. Static Semantics Specification.md @@ -80,14 +80,19 @@ Rules: - Attributes are not first-class values and are not reflectable in v1 core. - Attributes do not automatically survive into runtime or bytecode artifacts. - An attribute affects runtime artifacts only when another specification defines an explicit lowering for its semantic effect. -- In v1 core, the normative reserved attributes are `Host`, `Capability`, `BuiltinType`, `BuiltinConst`, `IntrinsicCall`, `Init`, `Frame`, and `InitAllowed`. +- In v1 core, the normative reserved attributes are `Host`, `Capability`, `AssetLowering`, `BuiltinType`, `BuiltinConst`, `IntrinsicCall`, `Init`, `Frame`, and `InitAllowed`. - `Host` is valid only on a host method signature declared directly inside a reserved stdlib/interface-module `declare host` body. - `Capability` is valid only on a host method signature declared directly inside a reserved stdlib/interface-module `declare host` body. +- `AssetLowering` is valid only on a host method signature declared directly inside a reserved stdlib/interface-module `declare host` body. - `Init` is valid only on a top-level userland `fn` declaration with lifecycle-admissible signature. - `Frame` is valid only on a top-level userland `fn` declaration with lifecycle-admissible signature. - `InitAllowed` is valid only on a host method signature declared directly inside a reserved stdlib/interface-module `declare host` body. - `Host` is invalid on ordinary user-authored modules, top-level `fn`, struct methods, service methods, callbacks, contracts, and constants. - `Host` metadata is consumed by the compiler during host-binding lowering. +- `AssetLowering` MUST declare exactly the named argument `param`. +- `AssetLowering.param` MUST be a zero-based integer literal that names a valid host parameter position. +- The parameter targeted by `AssetLowering.param` MUST be statically typed as `Addressable`. +- `AssetLowering` metadata is consumed by backend-owned host-call lowering to rewrite the targeted symbolic `Addressable` operand into runtime-facing `asset_id`. - The `Host` attribute syntax itself is not exported as runtime metadata; instead, its canonical identity participates in PBX host-binding emission as defined by the Host ABI Binding specification. - `BuiltinType` is valid only on a reserved top-level `declare builtin type` declaration. - `BuiltinConst` is valid only on a reserved top-level `declare const` declaration that omits an initializer. diff --git a/docs/specs/compiler-languages/pbs/6.2. Host ABI Binding and Loader Resolution Specification.md b/docs/specs/compiler-languages/pbs/6.2. Host ABI Binding and Loader Resolution Specification.md index ae4cbb19..0a5c99e7 100644 --- a/docs/specs/compiler-languages/pbs/6.2. Host ABI Binding and Loader Resolution Specification.md +++ b/docs/specs/compiler-languages/pbs/6.2. Host ABI Binding and Loader Resolution Specification.md @@ -143,7 +143,8 @@ For example, the PBS-facing declaration: declare host LowAssets { [Host(module = "asset", name = "load", version = 1)] [Capability(name = "asset")] - fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); + [AssetLowering(param = 0)] + fn load(addressable: Addressable, slot: int) -> (status: int, loading_handle: int); } ``` @@ -155,6 +156,8 @@ maps to the canonical runtime identity: The loader MUST NOT derive runtime identity from the source owner spelling `LowAssets`. +At the PBS-facing declaration layer, `AssetLowering(param = 0)` marks the author-facing `Addressable` parameter as the backend-owned symbolic operand that must be rewritten to operational `asset_id` before the final low-level host path is emitted. + ## 7. PBX Host Binding Section ### 7.1 Temporary section contract diff --git a/docs/specs/compiler-languages/pbs/8. Stdlib Environment Packaging and Loading Specification.md b/docs/specs/compiler-languages/pbs/8. Stdlib Environment Packaging and Loading Specification.md index 94370840..fa848dd4 100644 --- a/docs/specs/compiler-languages/pbs/8. Stdlib Environment Packaging and Loading Specification.md +++ b/docs/specs/compiler-languages/pbs/8. Stdlib Environment Packaging and Loading Specification.md @@ -214,7 +214,8 @@ or: declare host LowAssets { [Host(module = "asset", name = "load", version = 1)] [Capability(name = "asset")] - fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); + [AssetLowering(param = 0)] + fn load(addressable: Addressable, slot: int) -> (status: int, loading_handle: int); } ``` @@ -228,8 +229,8 @@ Rules: - stdlib line `1` MUST expose the low-level asset interface module at `@sdk:asset`, - that module MUST declare `LowAssets`, - that module MUST use runtime module `asset` and capability `asset`, -- that module MUST use the runtime-aligned signatures: - - `load(asset_id: int, slot: int) -> (status: int, loading_handle: int)` +- that module MUST use the runtime-aligned signatures and reserved lowering metadata: + - `load(addressable: Addressable, slot: int) -> (status: int, loading_handle: int)` together with `[AssetLowering(param = 0)]` - `status(loading_handle: int) -> int` - `commit(loading_handle: int) -> int` - `cancel(loading_handle: int) -> int` diff --git a/docs/specs/compiler/20. IRBackend to IRVM Lowering Specification.md b/docs/specs/compiler/20. IRBackend to IRVM Lowering Specification.md index 1034b76b..24470f49 100644 --- a/docs/specs/compiler/20. IRBackend to IRVM Lowering Specification.md +++ b/docs/specs/compiler/20. IRBackend to IRVM Lowering Specification.md @@ -150,6 +150,8 @@ Before bytecode emission, backend must run structural pre-verification on lowere 7. and structural validity of host/intrinsic call forms. 8. and structural validity of backend-owned symbolic-to-operational rewrites such as `Addressable -> asset_id`. +For PBS host-backed asset calls in v1, this validation includes the rewrite selected by preserved host metadata such as `[AssetLowering(param = N)]`. + This pre-verification does not replace runtime verifier authority. ## 11. Deterministic Rejection Policy diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsReservedMetadataExtractor.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsReservedMetadataExtractor.java index 401a35bb..9d95138c 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsReservedMetadataExtractor.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsReservedMetadataExtractor.java @@ -15,6 +15,7 @@ public final class PbsReservedMetadataExtractor { private static final String ATTR_BUILTIN_TYPE = "BuiltinType"; private static final String ATTR_INTRINSIC_CALL = "IntrinsicCall"; private static final String ATTR_BUILTIN_CONST = "BuiltinConst"; + private static final String ATTR_ASSET_LOWERING = "AssetLowering"; public IRReservedMetadata extract( final PbsAst.File ast, @@ -68,6 +69,7 @@ public final class PbsReservedMetadataExtractor { final var capability = capabilityAttribute .flatMap(attr -> stringArgument(attr, "name")) .orElse(""); + final var assetLoweringAttribute = firstAttributeNamed(signature.attributes(), ATTR_ASSET_LOWERING); hostMethodBindings.add(new IRReservedMetadata.HostMethodBinding( hostDecl.name(), signature.name(), @@ -78,6 +80,9 @@ public final class PbsReservedMetadataExtractor { case INFERRED_UNIT, EXPLICIT_UNIT -> 0; case PLAIN, RESULT -> 1; }, + assetLoweringAttribute.isPresent() + ? safeToInteger(longArgument(assetLoweringAttribute.get(), "param").orElse(-1L)) + : null, capabilityAttribute.isPresent(), capability, signature.span())); @@ -261,4 +266,11 @@ public final class PbsReservedMetadataExtractor { } return ReadOnlyList.wrap(required.stream().toList()); } + + private Integer safeToInteger(final long value) { + if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) { + return null; + } + return (int) value; + } } diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableBodyLowerer.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableBodyLowerer.java index d6ae2c03..cd989be8 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableBodyLowerer.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableBodyLowerer.java @@ -415,10 +415,28 @@ final class PbsExecutableBodyLowerer { final PbsAst.CallExpr callExpr, final PbsExecutableLoweringContext context) { lowerCallsiteReceiver(callExpr.callee(), context); - lowerExpressionList(callExpr.arguments(), context); + lowerCallArguments(callExpr, context); callsiteEmitter.emitCallsite(callExpr, context); } + private void lowerCallArguments( + final PbsAst.CallExpr callExpr, + final PbsExecutableLoweringContext context) { + final var hostBinding = callsiteEmitter.resolveUniqueHostBinding(callExpr, context); + if (hostBinding == null || hostBinding.assetLoweringParam() == null) { + lowerExpressionList(callExpr.arguments(), context); + return; + } + for (int i = 0; i < callExpr.arguments().size(); i++) { + final var argument = callExpr.arguments().get(i); + if (i == hostBinding.assetLoweringParam()) { + lowerAssetFacingArgument(argument, hostBinding, context); + continue; + } + lowerExpression(argument, context); + } + } + private void lowerExpressionList( final ReadOnlyList expressions, final PbsExecutableLoweringContext context) { @@ -461,6 +479,26 @@ final class PbsExecutableBodyLowerer { lowerExpression(memberExpr.receiver(), context); } + private void lowerAssetFacingArgument( + final PbsAst.Expression expression, + final p.studio.compiler.models.IRReservedMetadata.HostMethodBinding hostBinding, + final PbsExecutableLoweringContext context) { + final var assetReference = flattenAssetReference(expression); + if (assetReference == null) { + lowerExpression(expression, context); + return; + } + final var assetId = context.resolveAssetId(assetReference); + if (assetId == null) { + reportUnsupportedLowering( + "asset-facing host parameter does not resolve in backend-owned surface: " + assetReference, + expression.span(), + context); + return; + } + emitPushI32(assetId, expression.span(), context); + } + private void lowerIfExpression( final PbsAst.IfExpr ifExpr, final PbsExecutableLoweringContext context) { diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableCallsiteEmitter.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableCallsiteEmitter.java index 9ac7d2fc..c3a33130 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableCallsiteEmitter.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/lowering/PbsExecutableCallsiteEmitter.java @@ -16,6 +16,23 @@ import java.util.Map; import static p.studio.compiler.pbs.lowering.PbsExecutableLoweringModels.*; final class PbsExecutableCallsiteEmitter { + IRReservedMetadata.HostMethodBinding resolveUniqueHostBinding( + final PbsAst.CallExpr callExpr, + final PbsExecutableLoweringContext context) { + final var calleeIdentity = resolveCalleeIdentity(callExpr.callee(), context.nameTable()); + if (calleeIdentity == null) { + return null; + } + final var candidates = resolveCallsiteCandidates(callExpr, calleeIdentity, context); + if (!candidates.callableCandidates().isEmpty() || !candidates.intrinsicCandidates().isEmpty()) { + return null; + } + if (candidates.hostCandidates().size() != 1) { + return null; + } + return candidates.hostCandidates().getFirst(); + } + int returnSlotsOf( final PbsAst.CallExpr callExpr, final PbsExecutableLoweringContext context) { diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsDeclarationSemanticsValidator.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsDeclarationSemanticsValidator.java index d648e779..52548e04 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsDeclarationSemanticsValidator.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/semantics/PbsDeclarationSemanticsValidator.java @@ -18,6 +18,7 @@ public final class PbsDeclarationSemanticsValidator { private static final String ATTR_BUILTIN_CONST = "BuiltinConst"; private static final String ATTR_INTRINSIC_CALL = "IntrinsicCall"; private static final String ATTR_INIT_ALLOWED = "InitAllowed"; + private static final String ATTR_ASSET_LOWERING = "AssetLowering"; private static final Set RESERVED_ATTRIBUTES = Set.of( ATTR_HOST, @@ -25,7 +26,8 @@ public final class PbsDeclarationSemanticsValidator { ATTR_BUILTIN_TYPE, ATTR_BUILTIN_CONST, ATTR_INTRINSIC_CALL, - ATTR_INIT_ALLOWED); + ATTR_INIT_ALLOWED, + ATTR_ASSET_LOWERING); private final NameTable nameTable; private final PbsConstSemanticsValidator constSemanticsValidator = new PbsConstSemanticsValidator(); @@ -403,13 +405,15 @@ public final class PbsDeclarationSemanticsValidator { final DiagnosticSink diagnostics) { final var hostAttributes = attributesNamed(signature.attributes(), ATTR_HOST); final var initAllowedAttributes = attributesNamed(signature.attributes(), ATTR_INIT_ALLOWED); + final var assetLoweringAttributes = attributesNamed(signature.attributes(), ATTR_ASSET_LOWERING); for (final var attribute : signature.attributes()) { if (!isReservedAttribute(attribute.name())) { continue; } if (ATTR_HOST.equals(attribute.name()) || ATTR_CAPABILITY.equals(attribute.name()) - || ATTR_INIT_ALLOWED.equals(attribute.name())) { + || ATTR_INIT_ALLOWED.equals(attribute.name()) + || ATTR_ASSET_LOWERING.equals(attribute.name())) { continue; } reportInvalidReservedAttributeTarget( @@ -438,10 +442,19 @@ public final class PbsDeclarationSemanticsValidator { } } + if (assetLoweringAttributes.size() > 1) { + for (int i = 1; i < assetLoweringAttributes.size(); i++) { + reportDuplicateReservedAttribute(assetLoweringAttributes.get(i), ATTR_ASSET_LOWERING, diagnostics); + } + } + validateHostAttributeShape(hostAttributes.getFirst(), diagnostics); if (!initAllowedAttributes.isEmpty()) { validateInitAllowedAttributeShape(initAllowedAttributes.getFirst(), diagnostics); } + if (!assetLoweringAttributes.isEmpty()) { + validateAssetLoweringAttributeShape(assetLoweringAttributes.getFirst(), signature, diagnostics); + } } private void validateBuiltinTypeAttribute( @@ -587,6 +600,39 @@ public final class PbsDeclarationSemanticsValidator { } } + private void validateAssetLoweringAttributeShape( + final PbsAst.Attribute attribute, + final PbsAst.FunctionSignature signature, + final DiagnosticSink diagnostics) { + final var args = validateNamedArguments(attribute, Set.of("param"), Set.of(), diagnostics); + if (args == null) { + return; + } + validateRequiredIntArgument(attribute, args, "param", false, diagnostics); + final var value = args.get("param"); + if (!(value instanceof PbsAst.AttributeIntValue intValue)) { + return; + } + final var paramIndex = intValue.value(); + if (paramIndex < 0 || paramIndex >= signature.parameters().size()) { + p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + PbsSemanticsErrors.E_SEM_MALFORMED_RESERVED_ATTRIBUTE.name(), + "AssetLowering param index %d is out of range for host signature '%s'".formatted(paramIndex, signature.name()), + attribute.span()); + return; + } + final var parameter = signature.parameters().get((int) paramIndex); + final var parameterType = unwrapGroup(parameter.typeRef()); + if (parameterType == null + || parameterType.kind() != PbsAst.TypeRefKind.SIMPLE + || !"Addressable".equals(parameterType.name())) { + p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + PbsSemanticsErrors.E_SEM_MALFORMED_RESERVED_ATTRIBUTE.name(), + "AssetLowering target parameter '%s' must have Addressable type".formatted(parameter.name()), + attribute.span()); + } + } + private Map validateNamedArguments( final PbsAst.Attribute attribute, final Set requiredNames, @@ -690,6 +736,13 @@ public final class PbsDeclarationSemanticsValidator { return trimmed.isEmpty(); } + private PbsAst.TypeRef unwrapGroup(final PbsAst.TypeRef typeRef) { + if (typeRef == null || typeRef.kind() != PbsAst.TypeRefKind.GROUP) { + return typeRef; + } + return unwrapGroup(typeRef.inner()); + } + private List attributesNamed( final ReadOnlyList attributes, final String name) { diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/asset/main.pbs b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/asset/main.pbs index 862cc0a4..3337caab 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/asset/main.pbs +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/stdlib/1/sdk/asset/main.pbs @@ -1,7 +1,8 @@ declare host LowAssets { [Host(module = "asset", name = "load", version = 1)] [Capability(name = "asset")] - fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); + [AssetLowering(param = 0)] + fn load(addressable: Addressable, slot: int) -> (status: int, loading_handle: int); [Host(module = "asset", name = "status", version = 1)] [Capability(name = "asset")] diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java index 8b045f4c..b770057a 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java @@ -9,12 +9,18 @@ import p.studio.compiler.models.IRGlobalVisibility; import p.studio.compiler.models.IRSyntheticCallableKind; import p.studio.compiler.models.SourceKind; import p.studio.compiler.pbs.lexer.LexErrors; +import p.studio.compiler.pbs.lexer.PbsLexer; +import p.studio.compiler.pbs.parser.PbsParser; import p.studio.compiler.pbs.semantics.PbsSemanticsErrors; import p.studio.compiler.source.diagnostics.DiagnosticPhase; import p.studio.compiler.source.diagnostics.DiagnosticSink; import p.studio.compiler.source.identifiers.FileId; +import p.studio.compiler.source.identifiers.ModuleId; +import p.studio.compiler.source.tables.NameTable; import p.studio.utilities.structures.ReadOnlyList; +import java.util.Map; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -433,7 +439,8 @@ class PbsFrontendCompilerTest { declare host LowAssets { [Host(module = "asset", name = "load", version = 1)] [Capability(name = "asset")] - fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); + [AssetLowering(param = 0)] + fn load(addressable: Addressable, slot: int) -> (status: int, loading_handle: int); [Host(module = "asset", name = "status", version = 1)] [Capability(name = "asset")] @@ -462,7 +469,8 @@ class PbsFrontendCompilerTest { .anyMatch(h -> h.ownerName().equals("LowAssets") && h.abiModule().equals("asset") && h.abiMethod().equals("load") - && h.abiVersion() == 1)); + && h.abiVersion() == 1 + && java.util.Objects.equals(h.assetLoweringParam(), 0))); } @Test @@ -493,6 +501,61 @@ class PbsFrontendCompilerTest { && "37".equals(i.label()))); } + @Test + void shouldRewriteAssetLoweringHostParameterToAssetId() { + final var interfaceSource = """ + declare host LowAssets { + [Host(module = "asset", name = "load", version = 1)] + [Capability(name = "asset")] + [AssetLowering(param = 0)] + fn load(addressable: Addressable, slot: int) -> (status: int, loading_handle: int); + } + """; + final var source = """ + fn boot() -> (status: int, loading_handle: int) { + return LowAssets.load(assets.ui.panel, 2); + } + """; + + final var diagnostics = DiagnosticSink.empty(); + final var compiler = new PbsFrontendCompiler(); + final var interfaceBackend = compiler.compileFile(new FileId(16), interfaceSource, diagnostics, SourceKind.SDK_INTERFACE); + final var sourceFileId = new FileId(17); + final var sourceTokens = PbsLexer.lex(source, sourceFileId, diagnostics); + final var sourceAst = PbsParser.parse(sourceTokens, sourceFileId, diagnostics); + final var interfaceTokens = PbsLexer.lex(interfaceSource, new FileId(18), diagnostics); + final var interfaceAst = PbsParser.parse(interfaceTokens, new FileId(18), diagnostics, PbsParser.ParseMode.INTERFACE_MODULE); + final var fileBackend = compiler.compileParsedFile( + sourceFileId, + sourceAst, + diagnostics, + SourceKind.PROJECT, + ModuleId.none(), + ReadOnlyList.empty(), + HostAdmissionContext.permissiveDefault(), + new FESurfaceContext(ReadOnlyList.from(new Addressable("assets.ui.panel", 55))), + new NameTable(), + interfaceAst.topDecls(), + ReadOnlyList.empty(), + ReadOnlyList.empty(), + interfaceBackend.reservedMetadata(), + Map.of()); + + assertEquals(0, diagnostics.errorCount(), diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString()); + final var executableBoot = fileBackend.executableFunctions().stream() + .filter(fn -> fn.callableName().equals("boot")) + .findFirst() + .orElseThrow(); + assertTrue(executableBoot.instructions().stream().anyMatch(i -> + i.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.PUSH_I32 + && "55".equals(i.label()))); + assertTrue(executableBoot.instructions().stream().anyMatch(i -> + i.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_HOST + && i.hostCall() != null + && "asset".equals(i.hostCall().module()) + && "load".equals(i.hostCall().name()))); + } + @Test void shouldRejectUnresolvedBackendOwnedAssetReference() { final var source = """ diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java index 30076ef4..d42f3206 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsGateUSdkInterfaceConformanceTest.java @@ -332,7 +332,8 @@ class PbsGateUSdkInterfaceConformanceTest { declare host LowAssets { [Host(module = "asset", name = "load", version = 1)] [Capability(name = "asset")] - fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); + [AssetLowering(param = 0)] + fn load(addressable: Addressable, slot: int) -> (status: int, loading_handle: int); [Host(module = "asset", name = "status", version = 1)] [Capability(name = "asset")] @@ -361,8 +362,9 @@ class PbsGateUSdkInterfaceConformanceTest { assertEquals("asset", backend.reservedMetadata().requiredCapabilities().getFirst()); assertTrue(backend.reservedMetadata().hostMethodBindings().stream() .anyMatch(h -> h.ownerName().equals("LowAssets") + && java.util.Objects.equals(h.assetLoweringParam(), 0) && h.abiModule().equals("asset") - && h.abiMethod().equals("cancel") + && h.abiMethod().equals("load") && h.abiVersion() == 1)); } diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/semantics/PbsInterfaceModuleSemanticsTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/semantics/PbsInterfaceModuleSemanticsTest.java index f383f3d0..30581bc3 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/semantics/PbsInterfaceModuleSemanticsTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/semantics/PbsInterfaceModuleSemanticsTest.java @@ -126,7 +126,8 @@ class PbsInterfaceModuleSemanticsTest { declare host LowAssets { [Host(module = "asset", name = "load", version = 1)] [Capability(name = "asset")] - fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); + [AssetLowering(param = 0)] + fn load(addressable: Addressable, slot: int) -> (status: int, loading_handle: int); [Host(module = "asset", name = "status", version = 1)] [Capability(name = "asset")] @@ -140,6 +141,24 @@ class PbsInterfaceModuleSemanticsTest { assertTrue(diagnostics.isEmpty(), diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString()); } + @Test + void shouldRejectMalformedAssetLoweringOnHostSurface() { + final var source = """ + declare host LowAssets { + [Host(module = "asset", name = "load", version = 1)] + [Capability(name = "asset")] + [AssetLowering(param = 1)] + fn load(addressable: Addressable) -> int; + } + """; + final var diagnostics = DiagnosticSink.empty(); + + new PbsFrontendCompiler().compileFile(new FileId(5), source, diagnostics, SourceKind.SDK_INTERFACE); + + assertTrue(diagnostics.stream().anyMatch(d -> + d.getCode().equals(PbsSemanticsErrors.E_SEM_MALFORMED_RESERVED_ATTRIBUTE.name()))); + } + @Test void shouldRejectBuiltinFieldWithNonAdmissibleLayoutType() { final var source = """ diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/stdlib/InterfaceModuleLoaderTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/stdlib/InterfaceModuleLoaderTest.java index d35c53ab..8e4afe1f 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/stdlib/InterfaceModuleLoaderTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/stdlib/InterfaceModuleLoaderTest.java @@ -69,7 +69,8 @@ class InterfaceModuleLoaderTest { declare host LowAssets { [Host(module = "asset", name = "load", version = 1)] [Capability(name = "asset")] - fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); + [AssetLowering(param = 0)] + fn load(addressable: Addressable, slot: int) -> (status: int, loading_handle: int); } """))), "pub host LowAssets;"); diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java index 3edacf72..d6b6e267 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/services/PBSFrontendPhaseServiceTest.java @@ -950,7 +950,8 @@ class PBSFrontendPhaseServiceTest { assertTrue(irBackend.getReservedMetadata().hostMethodBindings().stream() .anyMatch(h -> h.ownerName().equals("LowAssets") && h.sourceMethodName().equals("load") - && h.abiModule().equals("asset"))); + && h.abiModule().equals("asset") + && java.util.Objects.equals(h.assetLoweringParam(), 0))); } @Test diff --git a/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRReservedMetadata.java b/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRReservedMetadata.java index e262a8a3..d2bc0577 100644 --- a/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRReservedMetadata.java +++ b/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRReservedMetadata.java @@ -33,6 +33,7 @@ public record IRReservedMetadata( String abiMethod, long abiVersion, int retSlots, + Integer assetLoweringParam, boolean capabilityDeclared, String requiredCapability, Span span) { @@ -44,6 +45,9 @@ public record IRReservedMetadata( if (retSlots < 0) { throw new IllegalArgumentException("host retSlots must be non-negative"); } + if (assetLoweringParam != null && assetLoweringParam < 0) { + throw new IllegalArgumentException("assetLoweringParam must be non-negative"); + } requiredCapability = requiredCapability == null ? "" : requiredCapability; span = Objects.requireNonNull(span, "span"); }