Compare commits
2 Commits
d0b91f8526
...
59c0d0c3b4
| Author | SHA1 | Date | |
|---|---|---|---|
| 59c0d0c3b4 | |||
| 79a682b8ec |
@ -1,7 +1,7 @@
|
|||||||
{"type":"meta","next_id":{"DSC":66,"AGD":69,"DEC":49,"PLN":128,"LSN":65,"CLSN":1}}
|
{"type":"meta","next_id":{"DSC":66,"AGD":69,"DEC":50,"PLN":129,"LSN":66,"CLSN":1}}
|
||||||
{"type":"discussion","id":"DSC-0065","status":"open","ticket":"multi-frontend-avoid-premature-abstractions","title":"Evitar abstracoes prematuras na preparacao multi-frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","architecture","multi-frontend","simplicity"],"agendas":[{"id":"AGD-0068","file":"AGD-0068-multi-frontend-avoid-premature-abstractions.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
{"type":"discussion","id":"DSC-0065","status":"open","ticket":"multi-frontend-avoid-premature-abstractions","title":"Evitar abstracoes prematuras na preparacao multi-frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","architecture","multi-frontend","simplicity"],"agendas":[{"id":"AGD-0068","file":"AGD-0068-multi-frontend-avoid-premature-abstractions.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||||
{"type":"discussion","id":"DSC-0064","status":"open","ticket":"multi-frontend-architectural-tests","title":"Testes arquiteturais para fronteiras multi-frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","architecture","tests","multi-frontend"],"agendas":[{"id":"AGD-0067","file":"AGD-0067-multi-frontend-architectural-tests.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
{"type":"discussion","id":"DSC-0064","status":"open","ticket":"multi-frontend-architectural-tests","title":"Testes arquiteturais para fronteiras multi-frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","architecture","tests","multi-frontend"],"agendas":[{"id":"AGD-0067","file":"AGD-0067-multi-frontend-architectural-tests.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||||
{"type":"discussion","id":"DSC-0063","status":"open","ticket":"multi-frontend-synthetic-test-frontend","title":"Frontend sintetico de teste para provar neutralidade do pipeline","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","frontend","tests","backend","multi-frontend"],"agendas":[{"id":"AGD-0066","file":"AGD-0066-multi-frontend-synthetic-test-frontend.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
{"type":"discussion","id":"DSC-0063","status":"done","ticket":"multi-frontend-synthetic-test-frontend","title":"Frontend sintetico de teste para provar neutralidade do pipeline","created_at":"2026-07-15","updated_at":"2026-09-19","tags":["compiler","compiler-general","frontend","tests","backend","multi-frontend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0065","file":"discussion/lessons/DSC-0063-multi-frontend-synthetic-test-frontend/LSN-0065-synthetic-test-frontend-proves-pipeline-neutrality.md","status":"done","created_at":"2026-09-19","updated_at":"2026-09-19"}]}
|
||||||
{"type":"discussion","id":"DSC-0062","status":"done","ticket":"multi-frontend-pvm-neutrality","title":"Neutralidade da PVM e identificacao PBX independente de PBS","created_at":"2026-07-15","updated_at":"2026-09-18","tags":["vm-arch","runtime","pvm","pbx","compiler","multi-frontend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0061","file":"discussion/lessons/DSC-0062-multi-frontend-pvm-neutrality/LSN-0061-pbx-runtime-boundary.md","status":"done","created_at":"2026-09-18","updated_at":"2026-09-18"}]}
|
{"type":"discussion","id":"DSC-0062","status":"done","ticket":"multi-frontend-pvm-neutrality","title":"Neutralidade da PVM e identificacao PBX independente de PBS","created_at":"2026-07-15","updated_at":"2026-09-18","tags":["vm-arch","runtime","pvm","pbx","compiler","multi-frontend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0061","file":"discussion/lessons/DSC-0062-multi-frontend-pvm-neutrality/LSN-0061-pbx-runtime-boundary.md","status":"done","created_at":"2026-09-18","updated_at":"2026-09-18"}]}
|
||||||
{"type":"discussion","id":"DSC-0061","status":"done","ticket":"multi-frontend-sdk-canonical-definition","title":"Auditoria e centralizacao gradual da definicao canonica do SDK","created_at":"2026-07-15","updated_at":"2026-09-19","tags":["compiler","compiler-general","sdk","stdlib","hostcalls","intrinsics","multi-frontend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0064","file":"discussion/lessons/DSC-0061-multi-frontend-sdk-canonical-definition/LSN-0064-runtime-owns-console-abi-pbs-is-delivery.md","status":"done","created_at":"2026-09-19","updated_at":"2026-09-19"}]}
|
{"type":"discussion","id":"DSC-0061","status":"done","ticket":"multi-frontend-sdk-canonical-definition","title":"Auditoria e centralizacao gradual da definicao canonica do SDK","created_at":"2026-07-15","updated_at":"2026-09-19","tags":["compiler","compiler-general","sdk","stdlib","hostcalls","intrinsics","multi-frontend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0064","file":"discussion/lessons/DSC-0061-multi-frontend-sdk-canonical-definition/LSN-0064-runtime-owns-console-abi-pbs-is-delivery.md","status":"done","created_at":"2026-09-19","updated_at":"2026-09-19"}]}
|
||||||
{"type":"discussion","id":"DSC-0060","status":"done","ticket":"multi-frontend-validation-boundaries","title":"Separar validacoes de linguagem e validacoes de plataforma","created_at":"2026-07-15","updated_at":"2026-09-19","tags":["compiler","compiler-general","compiler-pbs","backend","validation","multi-frontend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0063","file":"discussion/lessons/DSC-0060-multi-frontend-validation-boundaries/LSN-0063-language-vs-platform-validation-ownership.md","status":"done","created_at":"2026-09-19","updated_at":"2026-09-19"}]}
|
{"type":"discussion","id":"DSC-0060","status":"done","ticket":"multi-frontend-validation-boundaries","title":"Separar validacoes de linguagem e validacoes de plataforma","created_at":"2026-07-15","updated_at":"2026-09-19","tags":["compiler","compiler-general","compiler-pbs","backend","validation","multi-frontend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0063","file":"discussion/lessons/DSC-0060-multi-frontend-validation-boundaries/LSN-0063-language-vs-platform-validation-ownership.md","status":"done","created_at":"2026-09-19","updated_at":"2026-09-19"}]}
|
||||||
|
|||||||
@ -0,0 +1,148 @@
|
|||||||
|
---
|
||||||
|
id: LSN-0065
|
||||||
|
ticket: multi-frontend-synthetic-test-frontend
|
||||||
|
title: Synthetic test frontend proves common pipeline neutrality
|
||||||
|
created: 2026-09-19
|
||||||
|
tags: [compiler, compiler-general, frontend, tests, backend, multi-frontend]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Synthetic test frontend proves common pipeline neutrality
|
||||||
|
|
||||||
|
## Original Problem
|
||||||
|
|
||||||
|
The common pipeline already had `FrontendProvider`, `IRBackend` as the
|
||||||
|
executable handoff, common lifecycle assembly, and `IRBackend -> IRVM -> PBX`.
|
||||||
|
Direct construction tests proved the backend could lower a handmade
|
||||||
|
`IRBackend`. Import guards proved common backend code did not depend on PBS
|
||||||
|
packages.
|
||||||
|
|
||||||
|
That was not enough. Nothing proved that a second provider, resolved by
|
||||||
|
`languageId`, could enter through `DiscoverPhase` and
|
||||||
|
`FrontendPhasePipelineStage`, assemble lifecycle, and finish
|
||||||
|
`BuilderPipelineService.compile()` at verified bytecode without instantiating
|
||||||
|
PBS, reading `.pbs` files, or loading PBS stdlib.
|
||||||
|
|
||||||
|
`CompileOnlyFrontendProvider` already existed as a local test double. It was
|
||||||
|
not registered, returned an empty `IRBackend`, and never called the pipeline.
|
||||||
|
It did not close this gap.
|
||||||
|
|
||||||
|
## Consolidated Decision
|
||||||
|
|
||||||
|
Pipeline neutrality is proved by a **test-only** compile-only
|
||||||
|
`FrontendProvider`, not by a second product language and not by injecting IR
|
||||||
|
into compile stages.
|
||||||
|
|
||||||
|
Locked shape (former Option A):
|
||||||
|
|
||||||
|
- `languageId` is `synth`.
|
||||||
|
- The provider and `FrontendPhaseService` live only in
|
||||||
|
`prometeu-build-pipeline` test sources.
|
||||||
|
- `languageService()` is empty. There is no parser, grammar, semantic
|
||||||
|
presentation, or stdlib catalog.
|
||||||
|
- `sourceRoots` is `src`. `allowedExtensions` must not collide with `pbs` /
|
||||||
|
`barrel`. The fixture must **not** create a source file of that extension.
|
||||||
|
An empty `src/` directory must still exist because `WireProjectsPhase`
|
||||||
|
resolves `toRealPath()`.
|
||||||
|
- The compiler ignores source text and emits one module, one `frame` body
|
||||||
|
that is a single `RET`, and an `IRLifecycleDeclaration` with exactly one
|
||||||
|
`FRAME_ROOT` whose origin span is filled.
|
||||||
|
- The handoff must not declare `FILE_INIT_FRAGMENT` or `PROJECT_INIT`, and
|
||||||
|
must not pre-materialize wrappers, module-init, or `BOOT_GUARD`. The common
|
||||||
|
assembler produces `__prometeu.lifecycle.frame_wrapper$mN` and the boot
|
||||||
|
guard.
|
||||||
|
- The IR must not contain host bindings, capabilities, intrinsics, PBS types,
|
||||||
|
PBS stdlib, or `__pbs.` names.
|
||||||
|
|
||||||
|
The acceptance path is `compile()` through `VerifyBytecode`. Writing
|
||||||
|
`program.pbx` is not the criterion. `build()` only adds disk I/O.
|
||||||
|
|
||||||
|
Registration stays on the existing static `FrontendRegistryService`.
|
||||||
|
`register` and `unregister` are public. `unregister` refuses to remove the
|
||||||
|
default PBS provider and is a no-op for unknown ids. Tests register `synth`
|
||||||
|
for the duration of the run and unregister in `try/finally`.
|
||||||
|
`bootstrapDefaults()`, `AppContainer`, and the Studio language catalog must
|
||||||
|
not register `synth`.
|
||||||
|
|
||||||
|
Do not inject a `Registry` into the pipeline, add a `frontends/synth` module,
|
||||||
|
scan the classpath, or treat a dummy on-disk extension as a language. Do not
|
||||||
|
replace `LifecycleAssemblerServiceTest`, `LowerToIRVMServiceTest`, or
|
||||||
|
`IRBackendExecutableContractTest` with this fixture.
|
||||||
|
|
||||||
|
A blank `prometeu.json` `language` field still defaults to PBS. The synthetic
|
||||||
|
project must set `language: synth` explicitly.
|
||||||
|
|
||||||
|
## Final Implementation
|
||||||
|
|
||||||
|
`FrontendRegistryService.register(FrontendProvider)` is the explicit
|
||||||
|
registration API. `unregister(String languageId)` removes a non-PBS provider
|
||||||
|
or no-ops if the id is absent. Duplicate `register` still fails.
|
||||||
|
`bootstrapDefaults()` still registers only PBS.
|
||||||
|
|
||||||
|
The test fixture is:
|
||||||
|
|
||||||
|
- `p.studio.compiler.synth.SyntheticFrontendProvider`
|
||||||
|
- `p.studio.compiler.synth.SyntheticFrontendPhaseService`
|
||||||
|
|
||||||
|
`SyntheticFrontendPipelineTest` writes a temporary project
|
||||||
|
(`language: synth`, `target: Game`, numeric `stdlib`, empty `src/`),
|
||||||
|
registers the provider, calls `BuilderPipelineService.compile()`, and
|
||||||
|
asserts:
|
||||||
|
|
||||||
|
- the synthetic compiler ran;
|
||||||
|
- resolved `languageId` is `synth`, not `pbs`;
|
||||||
|
- diagnostics have no errors;
|
||||||
|
- in-memory bytecode is non-empty and verified;
|
||||||
|
- assembled entrypoint is `__prometeu.lifecycle.frame_wrapper$m0`;
|
||||||
|
- a hidden boot guard is present;
|
||||||
|
- no `__pbs.` names;
|
||||||
|
- no `.pbs` / `.barrel` sources were required;
|
||||||
|
- after `unregister`, `listProviders()` is PBS-only.
|
||||||
|
|
||||||
|
Spec 23 section 8.3 records that a compile-only registered provider is a
|
||||||
|
valid pipeline participant and that the common pipeline must not require PBS
|
||||||
|
types, PBS sources, or PBS stdlib to emit verified bytecode. Spec 22 row
|
||||||
|
`G23-8.3` binds that obligation to the synthetic pipeline test.
|
||||||
|
|
||||||
|
PBS production code, Studio, and `AppContainer` were not changed.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Good:
|
||||||
|
|
||||||
|
- register `synth`, compile a temp project with empty `src/`, unregister;
|
||||||
|
- `FRAME_ROOT` with `FileId(0)` and a non-none span, even with no source file
|
||||||
|
in `FileTable`;
|
||||||
|
- assert the synthetic compiler was invoked instead of importing
|
||||||
|
`PBSFrontendPhaseService` from build-pipeline tests.
|
||||||
|
|
||||||
|
Bad:
|
||||||
|
|
||||||
|
- omitting `language` in the manifest so discovery silently uses PBS;
|
||||||
|
- creating `main.dummy` and calling that a second frontend;
|
||||||
|
- putting the provider under `prometeu-compiler/frontends/`;
|
||||||
|
- leaving `synth` registered after the test so later tests see two providers;
|
||||||
|
- using this pipeline test as a substitute for assembler or IRVM unit tests.
|
||||||
|
|
||||||
|
## Pitfalls
|
||||||
|
|
||||||
|
- `WireProjectsPhase` still needs `src/` on disk. Empty is not the same as
|
||||||
|
missing.
|
||||||
|
- Origin span `Span.none()` is rejected by the assembler. Use a real
|
||||||
|
`FileId` even when no file was loaded.
|
||||||
|
- `unregister("pbs")` must fail. A cleanup path that blindly unregisters the
|
||||||
|
resolved language would delete the product default.
|
||||||
|
- `listProviders()` feeds the Studio catalog. A leaked test registration
|
||||||
|
would show `synth` as a new-project language.
|
||||||
|
- Direct `IRBackend` construction tests still own backend-only proofs. The
|
||||||
|
synthetic frontend owns the registry-plus-pipeline proof. Keep both.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- Decision: `DEC-0049`
|
||||||
|
- Plan: `PLN-0128`
|
||||||
|
- Related lessons: `LSN-0055`, `LSN-0056`, `LSN-0059`, `LSN-0062`, `LSN-0061`
|
||||||
|
- `docs/specs/compiler/23. Compiler Pipeline Entry Points Specification.md`
|
||||||
|
- `docs/specs/compiler/22. Backend Spec-to-Test Conformance Matrix.md`
|
||||||
|
- `prometeu-compiler/prometeu-frontend-registry/src/main/java/p/studio/compiler/FrontendRegistryService.java`
|
||||||
|
- `prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/synth/SyntheticFrontendProvider.java`
|
||||||
|
- `prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/SyntheticFrontendPipelineTest.java`
|
||||||
@ -1,77 +0,0 @@
|
|||||||
---
|
|
||||||
id: AGD-0066
|
|
||||||
ticket: multi-frontend-synthetic-test-frontend
|
|
||||||
title: Frontend sintetico de teste para provar neutralidade do pipeline
|
|
||||||
status: open
|
|
||||||
created: 2026-07-15
|
|
||||||
resolved:
|
|
||||||
decision:
|
|
||||||
tags: [compiler, compiler-general, frontend, tests, backend, multi-frontend]
|
|
||||||
---
|
|
||||||
|
|
||||||
# Agenda - Criar um frontend sintetico de teste
|
|
||||||
|
|
||||||
## Objetivo
|
|
||||||
|
|
||||||
Domain owner: `compiler/general`.
|
|
||||||
|
|
||||||
Discutir um frontend exclusivo de testes que prove que o pipeline comum nao depende implicitamente do PBS.
|
|
||||||
|
|
||||||
## Contexto atual
|
|
||||||
|
|
||||||
O criterio arquitetural final exige que um frontend sintetico implemente contratos equivalentes a `FrontendProvider`, `FrontendSpec` e `FrontendCompiler`, produza IR comum minima e passe pelo backend ate PBX/verifier.
|
|
||||||
|
|
||||||
## Escopo
|
|
||||||
|
|
||||||
- Escolher entre frontend totalmente sintetico ou extensao ficticia.
|
|
||||||
- Definir fixture de IR minima com `frame` vazio.
|
|
||||||
- Garantir isolamento em testes.
|
|
||||||
|
|
||||||
## Fora de escopo
|
|
||||||
|
|
||||||
- Expor frontend sintetico no Studio.
|
|
||||||
- Criar segunda linguagem real.
|
|
||||||
- Criar parser completo ou grammar dummy complexa.
|
|
||||||
|
|
||||||
## Arquivos e componentes a inspecionar
|
|
||||||
|
|
||||||
- `prometeu-compiler/prometeu-frontend-registry/...`
|
|
||||||
- `prometeu-compiler/prometeu-build-pipeline/...`
|
|
||||||
- testes do build pipeline e backend
|
|
||||||
- `prometeu-studio/src/main/java/p/studio/projects/...` apenas se a selecao por extensao for exercitada
|
|
||||||
|
|
||||||
## Alteracoes propostas
|
|
||||||
|
|
||||||
Opcao A: provider sintetico que ignora arquivos e retorna IR minima diretamente.
|
|
||||||
|
|
||||||
Opcao B: extensao `.dummy` reconhecida em fixture, com conteudo ignorado ou trivial.
|
|
||||||
|
|
||||||
Recomendacao inicial: preferir Opcao A para provar neutralidade do pipeline sem criar outra superficie de linguagem.
|
|
||||||
|
|
||||||
## Estrategia de implementacao
|
|
||||||
|
|
||||||
Esperar provider/IR/lifecycle estarem suficientemente definidos, entao adicionar fixture de teste que registra provider sintetico e executa pipeline comum sem PBS.
|
|
||||||
|
|
||||||
## Testes necessarios
|
|
||||||
|
|
||||||
- Provider registrado e resolvido por `languageId`.
|
|
||||||
- Extensao/source handling testado se a opcao B for escolhida.
|
|
||||||
- IR comum produzida, lifecycle montado, IRVM gerado, PBX emitido e verificado.
|
|
||||||
|
|
||||||
## Criterios de aceitacao
|
|
||||||
|
|
||||||
- Teste falha se pipeline comum instanciar PBS implicitamente.
|
|
||||||
- Synthetic frontend existe apenas em test fixtures.
|
|
||||||
- PBS nao e afetado.
|
|
||||||
|
|
||||||
## Riscos
|
|
||||||
|
|
||||||
- Fixture sintetica exercitar caminho feliz pequeno demais e nao proteger acoplamentos reais.
|
|
||||||
- Virar uma segunda linguagem informal.
|
|
||||||
|
|
||||||
## Decisoes que devem ser registradas
|
|
||||||
|
|
||||||
- Forma do frontend sintetico.
|
|
||||||
- Escopo minimo da IR fixture.
|
|
||||||
- Onde o provider de teste pode ser registrado.
|
|
||||||
|
|
||||||
@ -90,6 +90,7 @@ to concrete positive/negative test evidence and current status.
|
|||||||
| G23-6.3 | `build` MUST extend `compile` only with terminal artifact persistence. | `MainProjectPipelineIntegrationTest#buildShouldWriteProgramBytecode`; `BuilderPipelinePublicSurfaceTest#buildResultMustExposeArtifactPathAndCompilePayload` | N/A | pass | Coverage asserts that `build` retains the compile payload and writes the filesystem artifact path expected by callers. |
|
| G23-6.3 | `build` MUST extend `compile` only with terminal artifact persistence. | `MainProjectPipelineIntegrationTest#buildShouldWriteProgramBytecode`; `BuilderPipelinePublicSurfaceTest#buildResultMustExposeArtifactPathAndCompilePayload` | N/A | pass | Coverage asserts that `build` retains the compile payload and writes the filesystem artifact path expected by callers. |
|
||||||
| G23-7.1 | `AnalysisSnapshot` MUST expose the minimum shared analysis contract. | `MainProjectPipelineIntegrationTest#analyzeShouldNotWriteProgramBytecode`; `BuilderPipelinePublicSurfaceTest#analysisSnapshotMustExposeMinimumSharedContract` | N/A | pass | Both executable and structural contract checks now cover the shared minimum analysis payload. |
|
| G23-7.1 | `AnalysisSnapshot` MUST expose the minimum shared analysis contract. | `MainProjectPipelineIntegrationTest#analyzeShouldNotWriteProgramBytecode`; `BuilderPipelinePublicSurfaceTest#analysisSnapshotMustExposeMinimumSharedContract` | N/A | pass | Both executable and structural contract checks now cover the shared minimum analysis payload. |
|
||||||
| G23-8.1 | Caller-specific configs/contexts MUST NOT redefine canonical stage semantics. | N/A | N/A | missing | Requires explicit multi-entrypoint composition tests once non-filesystem contexts are implemented. |
|
| G23-8.1 | Caller-specific configs/contexts MUST NOT redefine canonical stage semantics. | N/A | N/A | missing | Requires explicit multi-entrypoint composition tests once non-filesystem contexts are implemented. |
|
||||||
|
| G23-8.3 | A compile-only `FrontendProvider` registered by `languageId` MUST traverse `compile()` to verified bytecode without PBS, `.pbs` sources, or PBS stdlib. | `SyntheticFrontendPipelineTest#compileMustEmitVerifiedBytecodeWithoutPbs` | `FrontendRegistryServiceTest#unregisterMustRefuseToRemoveDefaultPbs` | pass | Synthetic `synth` provider is test-only; common pipeline must not require PBS types, PBS sources, or PBS stdlib. |
|
||||||
| G23-9.1 | Legacy public `run` MUST be removed as the normative entrypoint and filesystem-default behavior MUST be expressed through `build`. | `BuilderPipelinePublicSurfaceTest#publicServiceSurfaceMustExposeExplicitEntrypointsAndNoPublicRun`; `MainProjectPipelineIntegrationTest#buildShouldWriteProgramBytecode` | N/A | partial | Public service coverage now proves `run` is no longer public and `build` is the filesystem artifact path, but the CLI composition path is not yet exercised by a dedicated automated test. |
|
| G23-9.1 | Legacy public `run` MUST be removed as the normative entrypoint and filesystem-default behavior MUST be expressed through `build`. | `BuilderPipelinePublicSurfaceTest#publicServiceSurfaceMustExposeExplicitEntrypointsAndNoPublicRun`; `MainProjectPipelineIntegrationTest#buildShouldWriteProgramBytecode` | N/A | partial | Public service coverage now proves `run` is no longer public and `build` is the filesystem artifact path, but the CLI composition path is not yet exercised by a dedicated automated test. |
|
||||||
| PBS13-12.0 | PBS executable lowering MUST emit `IRBackend` satisfying frontend handoff obligations before common backend lowering. | `IRBackendExecutableContractTest` suite; `PBSFrontendPhaseServiceTest#shouldSynthesizePositiveTopic19FixtureAcrossFileInitProjectInitAndFrame` | `LowerToIRVMServiceTest#lowerMustRejectWhenSyntheticWrapperEntrypointIsMissing`; `LowerToIRVMServiceTest#lowerMustRejectWhenHiddenBootGuardIsMissing`; `LowerToIRVMServiceTest#lowerMustRejectWhenSyntheticCallableOriginIsMissing` | pass | Row group `PBS13-12.x` verifies PBS emission into the common handoff; common backend ownership is covered by G20 rows. |
|
| PBS13-12.0 | PBS executable lowering MUST emit `IRBackend` satisfying frontend handoff obligations before common backend lowering. | `IRBackendExecutableContractTest` suite; `PBSFrontendPhaseServiceTest#shouldSynthesizePositiveTopic19FixtureAcrossFileInitProjectInitAndFrame` | `LowerToIRVMServiceTest#lowerMustRejectWhenSyntheticWrapperEntrypointIsMissing`; `LowerToIRVMServiceTest#lowerMustRejectWhenHiddenBootGuardIsMissing`; `LowerToIRVMServiceTest#lowerMustRejectWhenSyntheticCallableOriginIsMissing` | pass | Row group `PBS13-12.x` verifies PBS emission into the common handoff; common backend ownership is covered by G20 rows. |
|
||||||
| PBS13-12.7 | PBS executable lowering MUST emit a typed lifecycle declaration and MUST NOT assemble derived wrappers. | `PBSFrontendPhaseServiceTest#shouldSynthesizePositiveTopic19FixtureAcrossFileInitProjectInitAndFrame`; `PbsFrontendCompilerTest#shouldExposeGlobalsAndTypedLifecycleDeclarationInBackendFile` | N/A | pass | Frontend tests assert declaration roles and the absence of module init, published wrapper, and boot guard. |
|
| PBS13-12.7 | PBS executable lowering MUST emit a typed lifecycle declaration and MUST NOT assemble derived wrappers. | `PBSFrontendPhaseServiceTest#shouldSynthesizePositiveTopic19FixtureAcrossFileInitProjectInitAndFrame`; `PbsFrontendCompilerTest#shouldExposeGlobalsAndTypedLifecycleDeclarationInBackendFile` | N/A | pass | Frontend tests assert declaration roles and the absence of module init, published wrapper, and boot guard. |
|
||||||
|
|||||||
@ -284,6 +284,10 @@ Provider registration MUST be explicit in v1. The registry contract MUST NOT use
|
|||||||
|
|
||||||
PBS is the only initial real provider for this contract. PBS MAY be registered by an application composition root, but PBS MUST NOT become an exclusive semantic owner of the shared compiler pipeline.
|
PBS is the only initial real provider for this contract. PBS MAY be registered by an application composition root, but PBS MUST NOT become an exclusive semantic owner of the shared compiler pipeline.
|
||||||
|
|
||||||
|
A compile-only `FrontendProvider` registered by `languageId` MUST be a valid participant of `analyze`, `compile`, and `build`. The shared pipeline MUST NOT require PBS frontend types, `.pbs` or `.barrel` sources, or PBS stdlib in order to emit verified bytecode. Test-only frontends MAY exist solely as fixtures. They MUST NOT be product languages, MUST NOT be registered by default bootstrap or Studio composition, and MUST NOT be discovered by plugin scanning, classpath scanning, or external JAR loading.
|
||||||
|
|
||||||
|
The frontend registry MUST expose explicit `register` and `unregister` operations. `unregister` MUST refuse to remove the default PBS provider. Lookup of an unknown `languageId` MUST fail explicitly. `unregister` of an unknown `languageId` MUST be a no-op.
|
||||||
|
|
||||||
## 9. Public Surface Rule
|
## 9. Public Surface Rule
|
||||||
|
|
||||||
The shared compiler public surface MUST expose explicit entrypoints equivalent in meaning to:
|
The shared compiler public surface MUST expose explicit entrypoints equivalent in meaning to:
|
||||||
|
|||||||
@ -0,0 +1,99 @@
|
|||||||
|
package p.studio.compiler.integration;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
import p.studio.compiler.FrontendRegistryService;
|
||||||
|
import p.studio.compiler.messages.BuilderPipelineConfig;
|
||||||
|
import p.studio.compiler.models.BuilderPipelineContext;
|
||||||
|
import p.studio.compiler.models.IRBackendReader;
|
||||||
|
import p.studio.compiler.models.IRHiddenGlobalKind;
|
||||||
|
import p.studio.compiler.synth.SyntheticFrontendProvider;
|
||||||
|
import p.studio.compiler.workspaces.BuilderPipelineService;
|
||||||
|
import p.studio.utilities.logs.LogAggregator;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class SyntheticFrontendPipelineTest {
|
||||||
|
|
||||||
|
@TempDir
|
||||||
|
Path projectRoot;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void compileMustEmitVerifiedBytecodeWithoutPbs() throws IOException {
|
||||||
|
writeSyntheticProject();
|
||||||
|
final var provider = new SyntheticFrontendProvider();
|
||||||
|
FrontendRegistryService.register(provider);
|
||||||
|
try {
|
||||||
|
final var context = BuilderPipelineContext.fromConfig(
|
||||||
|
new BuilderPipelineConfig(false, projectRoot.toString()));
|
||||||
|
final var result = BuilderPipelineService.INSTANCE.compile(context, LogAggregator.empty());
|
||||||
|
|
||||||
|
assertTrue(provider.syntheticCompiler().invoked(), "synthetic compiler must be the effective frontend");
|
||||||
|
assertEquals(
|
||||||
|
SyntheticFrontendProvider.LANGUAGE_ID,
|
||||||
|
result.analysisSnapshot().frontendSpec().getLanguageId());
|
||||||
|
assertFalse("pbs".equals(result.analysisSnapshot().frontendSpec().getLanguageId()));
|
||||||
|
assertTrue(provider.languageService().isEmpty());
|
||||||
|
assertTrue(
|
||||||
|
result.diagnostics().stream().noneMatch(issue -> issue.isError()),
|
||||||
|
result.diagnostics().toString());
|
||||||
|
assertNotNull(result.bytecodeModule());
|
||||||
|
assertNotNull(result.bytecodeBytes());
|
||||||
|
assertTrue(result.bytecodeBytes().length > 0);
|
||||||
|
|
||||||
|
final var backend = result.analysisSnapshot().irBackend();
|
||||||
|
assertEquals("__prometeu.lifecycle.frame_wrapper$m0", backend.getEntryPointCallableName());
|
||||||
|
assertTrue(backend.getGlobals().stream().anyMatch(global ->
|
||||||
|
global.hiddenKind() == IRHiddenGlobalKind.BOOT_GUARD
|
||||||
|
&& "__prometeu.lifecycle.boot_guard$m0".equals(global.name())));
|
||||||
|
assertNoPbsNames(backend);
|
||||||
|
assertTrue(noPbsSources(projectRoot));
|
||||||
|
} finally {
|
||||||
|
FrontendRegistryService.unregister(SyntheticFrontendProvider.LANGUAGE_ID);
|
||||||
|
assertEquals(1, FrontendRegistryService.listProviders().size());
|
||||||
|
assertEquals(
|
||||||
|
"pbs",
|
||||||
|
FrontendRegistryService.listProviders().getFirst().specification().getLanguageId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeSyntheticProject() throws IOException {
|
||||||
|
Files.writeString(projectRoot.resolve("prometeu.json"), """
|
||||||
|
{
|
||||||
|
"name": "synth-fixture",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"language": "synth",
|
||||||
|
"stdlib": "1",
|
||||||
|
"target": "Game",
|
||||||
|
"dependencies": []
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
Files.createDirectory(projectRoot.resolve("src"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNoPbsNames(final IRBackendReader backend) {
|
||||||
|
assertFalse(backend.getEntryPointCallableName().contains("__pbs."));
|
||||||
|
assertTrue(backend.getExecutableFunctions().stream()
|
||||||
|
.noneMatch(function -> function.callableName().contains("__pbs.")));
|
||||||
|
assertTrue(backend.getSyntheticFunctions().stream()
|
||||||
|
.noneMatch(function -> function.callableName().contains("__pbs.")));
|
||||||
|
assertTrue(backend.getGlobals().stream()
|
||||||
|
.noneMatch(global -> global.name().contains("__pbs.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean noPbsSources(final Path root) throws IOException {
|
||||||
|
try (final var files = Files.walk(root)) {
|
||||||
|
return files
|
||||||
|
.filter(Files::isRegularFile)
|
||||||
|
.map(path -> path.getFileName().toString())
|
||||||
|
.noneMatch(name -> name.endsWith(".pbs") || name.endsWith(".barrel"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -95,7 +95,8 @@ class BackendConformanceMatrixSpecTest {
|
|||||||
"PBS13-12.7",
|
"PBS13-12.7",
|
||||||
"PBS13-12.8",
|
"PBS13-12.8",
|
||||||
"PBS13-12.9",
|
"PBS13-12.9",
|
||||||
"G23-5.2");
|
"G23-5.2",
|
||||||
|
"G23-8.3");
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void matrixMustExistAndContainRequiredRequirementRows() throws IOException {
|
void matrixMustExistAndContainRequiredRequirementRows() throws IOException {
|
||||||
|
|||||||
@ -0,0 +1,81 @@
|
|||||||
|
package p.studio.compiler.synth;
|
||||||
|
|
||||||
|
import p.studio.compiler.messages.BuildingIssueSink;
|
||||||
|
import p.studio.compiler.messages.FrontendPhaseContext;
|
||||||
|
import p.studio.compiler.models.IRBackend;
|
||||||
|
import p.studio.compiler.models.IRBackendExecutableFunction;
|
||||||
|
import p.studio.compiler.models.IRLifecycleCallableReference;
|
||||||
|
import p.studio.compiler.models.IRLifecycleCallableRole;
|
||||||
|
import p.studio.compiler.models.IRLifecycleDeclaration;
|
||||||
|
import p.studio.compiler.models.IRLifecycleModuleDeclaration;
|
||||||
|
import p.studio.compiler.services.FrontendPhaseService;
|
||||||
|
import p.studio.compiler.source.Span;
|
||||||
|
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
||||||
|
import p.studio.compiler.source.identifiers.CallableId;
|
||||||
|
import p.studio.compiler.source.identifiers.FileId;
|
||||||
|
import p.studio.compiler.source.identifiers.ModuleId;
|
||||||
|
import p.studio.compiler.source.tables.CallableSignatureRef;
|
||||||
|
import p.studio.compiler.source.tables.ModuleReference;
|
||||||
|
import p.studio.utilities.logs.LogAggregator;
|
||||||
|
import p.studio.utilities.structures.ReadOnlyList;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
public final class SyntheticFrontendPhaseService implements FrontendPhaseService {
|
||||||
|
private final AtomicBoolean invoked = new AtomicBoolean();
|
||||||
|
|
||||||
|
public boolean invoked() {
|
||||||
|
return invoked.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IRBackend compile(
|
||||||
|
final FrontendPhaseContext request,
|
||||||
|
final DiagnosticSink diagnostics,
|
||||||
|
final LogAggregator logs,
|
||||||
|
final BuildingIssueSink issues) {
|
||||||
|
invoked.set(true);
|
||||||
|
|
||||||
|
final var moduleId = new ModuleId(0);
|
||||||
|
final var fileId = new FileId(0);
|
||||||
|
final var callableId = new CallableId(0);
|
||||||
|
final var span = new Span(fileId, 0, 1);
|
||||||
|
final var frame = new IRBackendExecutableFunction(
|
||||||
|
fileId,
|
||||||
|
moduleId,
|
||||||
|
"frame",
|
||||||
|
callableId,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
ReadOnlyList.from(new IRBackendExecutableFunction.Instruction(
|
||||||
|
IRBackendExecutableFunction.InstructionKind.RET,
|
||||||
|
"",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
span)),
|
||||||
|
span);
|
||||||
|
final var frameRoot = new IRLifecycleCallableReference(
|
||||||
|
moduleId,
|
||||||
|
fileId,
|
||||||
|
callableId,
|
||||||
|
IRLifecycleCallableRole.FRAME_ROOT,
|
||||||
|
span);
|
||||||
|
|
||||||
|
return IRBackend.builder()
|
||||||
|
.entryPointCallableName("frame")
|
||||||
|
.entryPointModuleId(moduleId)
|
||||||
|
.modulePool(ReadOnlyList.from(new ModuleReference("synth", ReadOnlyList.from("main"))))
|
||||||
|
.callableSignatures(ReadOnlyList.from(
|
||||||
|
new CallableSignatureRef(moduleId, "frame", 0, "() -> unit")))
|
||||||
|
.executableFunctions(ReadOnlyList.from(frame))
|
||||||
|
.lifecycleDeclaration(new IRLifecycleDeclaration(
|
||||||
|
ReadOnlyList.from(new IRLifecycleModuleDeclaration(moduleId, ReadOnlyList.empty())),
|
||||||
|
null,
|
||||||
|
frameRoot))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package p.studio.compiler.synth;
|
||||||
|
|
||||||
|
import p.studio.compiler.models.FrontendSpec;
|
||||||
|
import p.studio.compiler.services.FrontendPhaseService;
|
||||||
|
import p.studio.compiler.services.FrontendProvider;
|
||||||
|
import p.studio.utilities.structures.ReadOnlySet;
|
||||||
|
|
||||||
|
public final class SyntheticFrontendProvider implements FrontendProvider {
|
||||||
|
public static final String LANGUAGE_ID = "synth";
|
||||||
|
|
||||||
|
private final FrontendSpec specification = FrontendSpec.builder()
|
||||||
|
.languageId(LANGUAGE_ID)
|
||||||
|
.allowedExtensions(ReadOnlySet.from("synth"))
|
||||||
|
.sourceRoots(ReadOnlySet.from("src"))
|
||||||
|
.build();
|
||||||
|
private final SyntheticFrontendPhaseService compiler = new SyntheticFrontendPhaseService();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FrontendSpec specification() {
|
||||||
|
return specification;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FrontendPhaseService compiler() {
|
||||||
|
return compiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SyntheticFrontendPhaseService syntheticCompiler() {
|
||||||
|
return compiler;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -62,7 +62,7 @@ public class FrontendRegistryService {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void register(final FrontendProvider provider) {
|
public static void register(final FrontendProvider provider) {
|
||||||
final var languageId = provider.specification().getLanguageId();
|
final var languageId = provider.specification().getLanguageId();
|
||||||
if (FRONTEND_PROVIDERS.containsKey(languageId)) {
|
if (FRONTEND_PROVIDERS.containsKey(languageId)) {
|
||||||
throw new IllegalArgumentException("Duplicate frontend languageId: " + languageId);
|
throw new IllegalArgumentException("Duplicate frontend languageId: " + languageId);
|
||||||
@ -71,6 +71,16 @@ public class FrontendRegistryService {
|
|||||||
FRONTEND_PROVIDERS.put(languageId, provider);
|
FRONTEND_PROVIDERS.put(languageId, provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void unregister(final String languageId) {
|
||||||
|
if (languageId == null || languageId.isBlank()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (PBSDefinitions.PBS.getLanguageId().equals(languageId)) {
|
||||||
|
throw new IllegalArgumentException("Cannot unregister the default PBS frontend: " + languageId);
|
||||||
|
}
|
||||||
|
FRONTEND_PROVIDERS.remove(languageId);
|
||||||
|
}
|
||||||
|
|
||||||
private static void registerIfAbsent(final FrontendProvider provider) {
|
private static void registerIfAbsent(final FrontendProvider provider) {
|
||||||
FRONTEND_PROVIDERS.putIfAbsent(provider.specification().getLanguageId(), provider);
|
FRONTEND_PROVIDERS.putIfAbsent(provider.specification().getLanguageId(), provider);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,15 @@
|
|||||||
package p.studio.compiler;
|
package p.studio.compiler;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import p.studio.compiler.messages.BuildingIssueSink;
|
||||||
|
import p.studio.compiler.messages.FrontendPhaseContext;
|
||||||
|
import p.studio.compiler.models.FrontendSpec;
|
||||||
|
import p.studio.compiler.models.IRBackend;
|
||||||
|
import p.studio.compiler.services.FrontendPhaseService;
|
||||||
|
import p.studio.compiler.services.FrontendProvider;
|
||||||
|
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
||||||
|
import p.studio.utilities.logs.LogAggregator;
|
||||||
|
import p.studio.utilities.structures.ReadOnlySet;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
@ -41,4 +50,89 @@ class FrontendRegistryServiceTest {
|
|||||||
assertEquals(1, providers.size());
|
assertEquals(1, providers.size());
|
||||||
assertEquals(PBSDefinitions.PBS.getLanguageId(), providers.getFirst().specification().getLanguageId());
|
assertEquals(PBSDefinitions.PBS.getLanguageId(), providers.getFirst().specification().getLanguageId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void registerShouldResolveAndUnregisterTemporaryProvider() {
|
||||||
|
final var provider = new TemporaryFrontendProvider("registry-temp");
|
||||||
|
FrontendRegistryService.register(provider);
|
||||||
|
try {
|
||||||
|
assertSame(provider, FrontendRegistryService.require("registry-temp"));
|
||||||
|
assertEquals(2, FrontendRegistryService.listProviders().size());
|
||||||
|
} finally {
|
||||||
|
FrontendRegistryService.unregister("registry-temp");
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(1, FrontendRegistryService.listProviders().size());
|
||||||
|
assertEquals(PBSDefinitions.PBS.getLanguageId(),
|
||||||
|
FrontendRegistryService.listProviders().getFirst().specification().getLanguageId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void registerShouldRejectDuplicateLanguageId() {
|
||||||
|
final var provider = new TemporaryFrontendProvider("registry-dup");
|
||||||
|
FrontendRegistryService.register(provider);
|
||||||
|
try {
|
||||||
|
final var thrown = assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> FrontendRegistryService.register(new TemporaryFrontendProvider("registry-dup")));
|
||||||
|
assertTrue(thrown.getMessage().contains("registry-dup"));
|
||||||
|
} finally {
|
||||||
|
FrontendRegistryService.unregister("registry-dup");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void unregisterMustRefuseToRemoveDefaultPbs() {
|
||||||
|
final var thrown = assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> FrontendRegistryService.unregister(PBSDefinitions.PBS.getLanguageId()));
|
||||||
|
|
||||||
|
assertTrue(thrown.getMessage().contains(PBSDefinitions.PBS.getLanguageId()));
|
||||||
|
assertEquals(1, FrontendRegistryService.listProviders().size());
|
||||||
|
assertSame(
|
||||||
|
PBSDefinitions.PBS,
|
||||||
|
FrontendRegistryService.require(PBSDefinitions.PBS.getLanguageId()).specification());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void unregisterUnknownLanguageIdMustBeNoOp() {
|
||||||
|
FrontendRegistryService.unregister(null);
|
||||||
|
FrontendRegistryService.unregister(" ");
|
||||||
|
FrontendRegistryService.unregister("unknown-language");
|
||||||
|
|
||||||
|
assertEquals(1, FrontendRegistryService.listProviders().size());
|
||||||
|
assertEquals(PBSDefinitions.PBS.getLanguageId(),
|
||||||
|
FrontendRegistryService.listProviders().getFirst().specification().getLanguageId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class TemporaryFrontendProvider implements FrontendProvider {
|
||||||
|
private final FrontendSpec specification;
|
||||||
|
|
||||||
|
private TemporaryFrontendProvider(final String languageId) {
|
||||||
|
this.specification = FrontendSpec.builder()
|
||||||
|
.languageId(languageId)
|
||||||
|
.allowedExtensions(ReadOnlySet.from("tmp"))
|
||||||
|
.sourceRoots(ReadOnlySet.from("src"))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FrontendSpec specification() {
|
||||||
|
return specification;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FrontendPhaseService compiler() {
|
||||||
|
return new FrontendPhaseService() {
|
||||||
|
@Override
|
||||||
|
public IRBackend compile(
|
||||||
|
final FrontendPhaseContext request,
|
||||||
|
final DiagnosticSink diagnostics,
|
||||||
|
final LogAggregator logs,
|
||||||
|
final BuildingIssueSink issues) {
|
||||||
|
return IRBackend.builder().build();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user