implements PR-19.9 conformance fixtures diagnostics and gate closure

This commit is contained in:
bQUARKz 2026-03-26 19:47:00 +00:00
parent 58c5ab26d7
commit 459f5e3b87
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
10 changed files with 322 additions and 12 deletions

View File

@ -3,7 +3,7 @@
Status: Draft v1 (Traceability Baseline)
Applies to: backend conformance traceability for `IRBackend -> IRVM -> OptimizeIRVM -> EmitBytecode`
Last Updated: 2026-03-09
Last Updated: 2026-03-26
## 1. Purpose
@ -45,7 +45,7 @@ to concrete positive/negative test evidence and current status.
| G20-7.2 | Jump immediates MUST resolve to u32 function-relative offsets before emission. | `LowerToIRVMServiceTest#lowerMustResolveJumpTargetsFromLabels`; `BytecodeEmitterTest#emitMustEncodeJumpOpcodesWithU32Immediate` | `LowerToIRVMServiceTest#lowerMustRejectMissingJumpTargetLabel` | pass | |
| G20-7.3 | Jump targets MUST be instruction-boundary valid. | N/A | `IRVMValidatorTest#validateMustRejectInvalidJumpTarget` | pass | |
| G20-7.4 | Reachable fallthrough beyond function end MUST be rejected. | N/A | `LowerToIRVMServiceTest#lowerMustRejectUnterminatedFunction`; `IRVMValidatorTest#validateMustRejectRetShapeMismatch` | pass | |
| G20-8.1 | Qualified entrypoint function id MUST be 0. | `LowerToIRVMServiceTest#lowerMustAssignEntrypointIdZeroAndSortRemainingDeterministically`; `LowerToIRVMServiceTest#lowerMustUseQualifiedEntrypointIdentityWhenProvided` | `LowerToIRVMServiceTest#lowerMustRejectWhenEntrypointDeclarationIsMissing`; `LowerToIRVMServiceTest#lowerMustRejectMissingQualifiedEntrypointIdentity`; `LowerToIRVMServiceTest#lowerMustRejectWhenEntrypointIsAmbiguous` | pass | Entrypoint is resolved strictly by `EntrypointRef(entryPointModuleId, entryPointCallableName)`. |
| G20-8.1 | Qualified entrypoint function id MUST be 0. | `LowerToIRVMServiceTest#lowerMustAssignEntrypointIdZeroAndSortRemainingDeterministically`; `LowerToIRVMServiceTest#lowerMustUseQualifiedEntrypointIdentityWhenProvided`; `LowerToIRVMServiceTest#lowerMustPublishSyntheticWrapperAtFunctionZeroAndKeepFinalRetInWrapperPath` | `LowerToIRVMServiceTest#lowerMustRejectWhenEntrypointDeclarationIsMissing`; `LowerToIRVMServiceTest#lowerMustRejectMissingQualifiedEntrypointIdentity`; `LowerToIRVMServiceTest#lowerMustRejectWhenEntrypointIsAmbiguous`; `LowerToIRVMServiceTest#lowerMustRejectWhenPublishedWrapperWouldNotOccupyFunctionZero` | pass | Entrypoint is resolved strictly by qualified identity; PBS wrapper publication now has dedicated zero-index coverage. |
| G20-8.2 | Remaining function ids MUST follow deterministic moduleId-only ordering. | `LowerToIRVMServiceTest#lowerMustAssignEntrypointIdZeroAndSortRemainingDeterministically`; `LowerToIRVMServiceTest#lowerMustUseModulePoolCanonicalIdentityForNonEntrypointOrdering` | N/A | pass | Ordering key is `(moduleId -> modulePool canonical key, callable_name, source_start)`. |
| G20-8.3 | Same admitted input graph MUST produce identical function-id assignment. | `BackendSafetyGateSUTest#fullPipelineMustProduceDeterministicBytecodeForSameInput` | N/A | pass | Artifact determinism implies stable function mapping for fixed input. |
| G20-9.3 | `CALL_INTRINSIC` MUST remain distinct from host-binding path and resolve ids from canonical registry artifact. | `LowerToIRVMServiceTest#lowerMustMapHostAndIntrinsicCallsites`; `BackendGateIIntegrationTest#gateI_validIntrinsicPath`; `IRVMIntrinsicRegistryParityTest#registryResourceMustExposeExpectedCanonicalEntries`; `IRVMIntrinsicRegistryParityTest#registryMustStayInSyncWithRuntimeBuiltinsTable` | `IRVMValidatorTest#validateProgramMustRejectIntrinsicWithoutSignatureMetadata` | pass | Distinct operation kinds and canonical registry parity checks are enforced. |
@ -66,7 +66,7 @@ to concrete positive/negative test evidence and current status.
| G21-9.1 | Validation MUST include optimized-vs-non-optimized equivalence fixtures. | `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForLoweredHostIntrinsicFixture`; `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForConditionalJoinFixture`; `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForSimpleLoopFixture`; `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForLinearCallFixture` | N/A | pass | Dedicated opt on/off harness with reusable interpreter and deterministic trace assertions is in place. |
| G21-9.2 | Validation MUST preserve known negative loader/verifier behavior. | `BackendSafetyGateSUTest#emitStageMustExposeMarshalingLinkageFailureDeterministically`; `BackendGateIIntegrationTest` rejection suite | N/A | pass | |
| G21-9.3 | Validation MUST preserve deterministic artifact-level invariants. | `BackendSafetyGateSUTest#fullPipelineMustProduceDeterministicBytecodeForSameInput`; `BytecodeEmitterTest#emitMustRemainDeterministicAfterInterning` | N/A | pass | |
| PBS13-12.0 | Executable backend handoff MUST satisfy addendum obligations. | `IRBackendExecutableContractTest` suite | N/A | pass | Row group `PBS13-12.x` details each obligation. |
| PBS13-12.0 | Executable backend handoff MUST satisfy addendum obligations. | `IRBackendExecutableContractTest` suite; `PBSFrontendPhaseServiceTest#shouldSynthesizePositiveTopic19FixtureAcrossFileInitProjectInitAndFrame` | `LowerToIRVMServiceTest#lowerMustRejectWhenSyntheticWrapperEntrypointIsMissing`; `LowerToIRVMServiceTest#lowerMustRejectWhenHiddenBootGuardIsMissing`; `LowerToIRVMServiceTest#lowerMustRejectWhenSyntheticCallableOriginIsMissing` | pass | Row group `PBS13-12.x` details each obligation; topic 19 closure now includes lifecycle wrapper/guard/origin evidence. |
| PBS13-12.1.1 | Callable identity MUST be preserved at handoff. | `IRBackendExecutableContractTest#aggregatorMustPreserveExecutableFunctionOrderDeterministically` | N/A | pass | |
| PBS13-12.1.2 | Observable callable signature MUST be preserved at handoff. | `IRBackendExecutableContractTest#functionContractMustRejectInvalidSlotAndSpanBounds` | N/A | pass | |
| PBS13-12.1.3 | Callable category MUST be preserved at handoff. | `PbsFrontendCompilerTest#shouldLowerExecutableFunctionsWithCallsiteCategories` | N/A | pass | |

View File

@ -416,6 +416,10 @@ public final class PbsFrontendCompiler {
return "__pbs.frame_wrapper$m" + normalizedModuleIndex(moduleId);
}
public static String bootGuardGlobalName(final ModuleId moduleId) {
return "__pbs.boot_guard$m" + normalizedModuleIndex(moduleId);
}
private static int normalizedModuleIndex(final ModuleId moduleId) {
final var normalized = moduleId == null ? ModuleId.none() : moduleId;
return normalized.isNone() ? -1 : normalized.getIndex();

View File

@ -119,6 +119,7 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
private IRBackend synthesizePublishedLifecycle(final IRBackend baseBackend) {
final var callableSignatures = new ArrayList<>(baseBackend.getCallableSignatures().asList());
final var executableFunctions = new ArrayList<>(baseBackend.getExecutableFunctions().asList());
final var globals = new ArrayList<>(baseBackend.getGlobals().asList());
final var fileInitFunctionsByModule = new LinkedHashMap<ModuleId, List<IRBackendExecutableFunction>>();
final var projectInitAnchorsByModule = new LinkedHashMap<ModuleId, String>();
final var wrapperNamesByModule = new LinkedHashMap<ModuleId, String>();
@ -151,8 +152,9 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
entryPointModuleId = wrapperNamesByModule.keySet().iterator().next();
entryPointCallableName = wrapperNamesByModule.get(entryPointModuleId);
} else {
entryPointModuleId = baseBackend.getEntryPointModuleId();
entryPointCallableName = baseBackend.getEntryPointCallableName();
final var legacyEntrypoint = resolveLegacyEntrypoint(baseBackend);
entryPointModuleId = legacyEntrypoint.moduleId();
entryPointCallableName = legacyEntrypoint.callableName();
}
final var sortedModuleIds = new ArrayList<ModuleId>();
@ -281,6 +283,17 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
1,
ReadOnlyList.wrap(instructions),
frameExecutable.span()));
if (globals.stream().noneMatch(global ->
moduleIdsMatch(entryPointModuleId, global.moduleId())
&& PbsFrontendCompiler.bootGuardGlobalName(entryPointModuleId).equals(global.name()))) {
globals.add(new p.studio.compiler.models.IRGlobal(
frameExecutable.fileId(),
entryPointModuleId,
PbsFrontendCompiler.bootGuardGlobalName(entryPointModuleId),
"bool",
frameExecutable.span()));
}
}
return IRBackend.builder()
@ -288,7 +301,7 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
.entryPointModuleId(entryPointModuleId)
.functions(baseBackend.getFunctions())
.syntheticFunctions(baseBackend.getSyntheticFunctions())
.globals(baseBackend.getGlobals())
.globals(ReadOnlyList.wrap(globals))
.executableFunctions(ReadOnlyList.wrap(executableFunctions))
.modulePool(baseBackend.getModulePool())
.callableSignatures(ReadOnlyList.wrap(callableSignatures))
@ -384,6 +397,19 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
return (int) value;
}
private LegacyEntrypoint resolveLegacyEntrypoint(final IRBackend backend) {
for (final var candidateName : List.of("frame", "main")) {
final var candidates = backend.getExecutableFunctions().stream()
.filter(function -> candidateName.equals(function.callableName()))
.filter(function -> function.moduleId() != null && !function.moduleId().isNone())
.toList();
if (candidates.size() == 1) {
return new LegacyEntrypoint(candidates.getFirst().moduleId(), candidateName);
}
}
return new LegacyEntrypoint(backend.getEntryPointModuleId(), backend.getEntryPointCallableName());
}
private Set<ModuleId> blockedModulesByDependency(
final Set<ModuleId> failedModuleIds,
final Map<ModuleId, Set<ModuleId>> dependenciesByModule) {
@ -414,4 +440,9 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
ModuleId moduleId,
p.studio.compiler.models.IRBackendFile irBackendFile) {
}
private record LegacyEntrypoint(
ModuleId moduleId,
String callableName) {
}
}

View File

@ -308,6 +308,8 @@ class PBSFrontendPhaseServiceTest {
assertTrue(diagnostics.isEmpty(), diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString());
assertTrue(irBackend.getEntryPointCallableName().startsWith("__pbs.frame_wrapper$m"));
assertTrue(irBackend.getGlobals().stream().anyMatch(global ->
global.name().startsWith("__pbs.boot_guard$m")));
final var wrapper = irBackend.getExecutableFunctions().stream()
.filter(function -> irBackend.getEntryPointCallableName().equals(function.callableName()))
.findFirst()
@ -319,6 +321,73 @@ class PBSFrontendPhaseServiceTest {
assertEquals(p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.RET, wrapper.instructions().get(3).kind());
}
@Test
void shouldSynthesizePositiveTopic19FixtureAcrossFileInitProjectInitAndFrame() throws IOException {
final var projectRoot = tempDir.resolve("project-topic-19-positive");
final var sourceRoot = projectRoot.resolve("src");
final var modulePath = sourceRoot.resolve("game");
Files.createDirectories(modulePath);
final var globalsSource = modulePath.resolve("globals.pbs");
final var mainSource = modulePath.resolve("main.pbs");
final var modBarrel = modulePath.resolve("mod.barrel");
Files.writeString(globalsSource, """
declare global SCORE: int = 0;
[Init]
fn warmup() -> void { return; }
""");
Files.writeString(mainSource, """
[Init]
fn boot() -> void { return; }
[Frame]
fn frame() -> void { return; }
""");
Files.writeString(modBarrel, """
pub fn warmup() -> void;
pub fn boot() -> void;
pub fn frame() -> void;
pub global SCORE;
""");
final var projectTable = new ProjectTable();
final var fileTable = new FileTable(1);
final var projectId = projectTable.register(ProjectDescriptor.builder()
.rootPath(projectRoot)
.name("core")
.version("1.0.0")
.sourceRoots(ReadOnlyList.wrap(List.of(sourceRoot)))
.build());
registerFile(projectId, projectRoot, globalsSource, fileTable);
registerFile(projectId, projectRoot, mainSource, fileTable);
registerFile(projectId, projectRoot, modBarrel, fileTable);
final var ctx = new FrontendPhaseContext(
projectTable,
fileTable,
new BuildStack(ReadOnlyList.wrap(List.of(projectId))));
final var diagnostics = DiagnosticSink.empty();
final var irBackend = new PBSFrontendPhaseService().compile(
ctx,
diagnostics,
LogAggregator.empty(),
BuildingIssueSink.empty());
assertTrue(diagnostics.isEmpty(), diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString());
assertTrue(irBackend.getEntryPointCallableName().startsWith("__pbs.frame_wrapper$m"));
assertTrue(irBackend.getExecutableFunctions().stream().anyMatch(function ->
function.callableName().startsWith("__pbs.file_init$f")));
assertTrue(irBackend.getExecutableFunctions().stream().anyMatch(function ->
function.callableName().startsWith("__pbs.module_init$m")));
assertTrue(irBackend.getExecutableFunctions().stream().anyMatch(function ->
function.callableName().startsWith("__pbs.project_init$m")));
assertTrue(irBackend.getGlobals().stream().anyMatch(global -> "SCORE".equals(global.name())));
assertTrue(irBackend.getGlobals().stream().anyMatch(global -> global.name().startsWith("__pbs.boot_guard$m")));
}
@Test
void shouldSkipIrLoweringForSourcesMarkedAsSdkInterface() throws IOException {
final var projectRoot = tempDir.resolve("project-sdk-interface");

View File

@ -5,6 +5,10 @@ public enum IRVMLoweringErrorCode {
LOWER_IRVM_ENTRYPOINT_DECLARATION_MISSING,
LOWER_IRVM_ENTRYPOINT_MISSING,
LOWER_IRVM_ENTRYPOINT_AMBIGUOUS,
LOWER_IRVM_SYNTHETIC_WRAPPER_ENTRYPOINT_MISSING,
LOWER_IRVM_PUBLISHED_ENTRYPOINT_NOT_ZERO,
LOWER_IRVM_HIDDEN_BOOT_GUARD_MISSING,
LOWER_IRVM_SYNTHETIC_ORIGIN_MISSING,
LOWER_IRVM_MISSING_CALLEE,
LOWER_IRVM_INVALID_INTRINSIC_ID,
LOWER_IRVM_CALL_ARG_SLOTS_MISMATCH,

View File

@ -4,6 +4,7 @@ import p.studio.compiler.backend.bytecode.BytecodeEmitter;
import p.studio.compiler.backend.bytecode.BytecodeModule;
import p.studio.compiler.models.IRBackend;
import p.studio.compiler.models.IRBackendExecutableFunction;
import p.studio.compiler.models.IRSyntheticCallableKind;
import p.studio.compiler.source.Span;
import p.studio.compiler.source.identifiers.CallableId;
import p.studio.compiler.source.identifiers.ModuleId;
@ -38,6 +39,7 @@ public class LowerToIRVMService {
"IRBackend has no executable functions");
}
validatePbsLifecycleStructure(backend);
final var ordered = orderFunctions(backend);
final var funcIdByCallableId = new HashMap<CallableId, Integer>();
for (var i = 0; i < ordered.size(); i++) {
@ -452,6 +454,14 @@ public class LowerToIRVMService {
entryPointModuleId.getIndex()));
}
final var entrypoint = entrypoints.getFirst();
final var publishedWrapper = resolvePublishedWrapper(backend);
if (publishedWrapper != null
&& (!entryPointCallableName.equals(publishedWrapper.callableName())
|| !sameModuleIdentity(entryPointModuleId, publishedWrapper.moduleId()))) {
throw new IRVMLoweringException(
IRVMLoweringErrorCode.LOWER_IRVM_PUBLISHED_ENTRYPOINT_NOT_ZERO,
"published entrypoint is not function index 0 because entrypoint identity does not select the published wrapper");
}
final var ordered = new ArrayList<IRBackendExecutableFunction>(sorted.size());
ordered.add(entrypoint);
for (final var fn : sorted) {
@ -462,6 +472,83 @@ public class LowerToIRVMService {
return ReadOnlyList.wrap(ordered);
}
private void validatePbsLifecycleStructure(final IRBackend backend) {
if (backend.getSyntheticFunctions().isEmpty()) {
return;
}
for (final var syntheticFunction : backend.getSyntheticFunctions()) {
if (syntheticFunction.origin() == null
|| syntheticFunction.origin().fileId() == null
|| syntheticFunction.origin().anchorCallableName().isBlank()
|| syntheticFunction.origin().span() == null
|| syntheticFunction.origin().span().isNone()) {
throw new IRVMLoweringException(
IRVMLoweringErrorCode.LOWER_IRVM_SYNTHETIC_ORIGIN_MISSING,
"synthetic callable origin missing for " + syntheticFunction.callableName());
}
}
final var publishedWrapper = resolvePublishedWrapper(backend);
if (publishedWrapper == null) {
return;
}
final var wrapperExecutablePresent = backend.getExecutableFunctions().stream()
.anyMatch(function -> publishedWrapper.callableName().equals(function.callableName())
&& sameModuleIdentity(publishedWrapper.moduleId(), function.moduleId()));
if (!wrapperExecutablePresent) {
throw new IRVMLoweringException(
IRVMLoweringErrorCode.LOWER_IRVM_SYNTHETIC_WRAPPER_ENTRYPOINT_MISSING,
"synthetic wrapper entrypoint missing: " + publishedWrapper.callableName());
}
final var expectedBootGuardName = bootGuardName(publishedWrapper.moduleId());
final var bootGuardPresent = backend.getGlobals().stream()
.anyMatch(global -> expectedBootGuardName.equals(global.name())
&& sameModuleIdentity(publishedWrapper.moduleId(), global.moduleId()));
if (!bootGuardPresent) {
throw new IRVMLoweringException(
IRVMLoweringErrorCode.LOWER_IRVM_HIDDEN_BOOT_GUARD_MISSING,
"hidden boot guard is missing for moduleId=%d".formatted(
publishedWrapper.moduleId() == null || publishedWrapper.moduleId().isNone()
? -1
: publishedWrapper.moduleId().getIndex()));
}
}
private p.studio.compiler.models.IRSyntheticFunction resolvePublishedWrapper(final IRBackend backend) {
p.studio.compiler.models.IRSyntheticFunction publishedWrapper = null;
for (final var syntheticFunction : backend.getSyntheticFunctions()) {
if (syntheticFunction.kind() != IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER) {
continue;
}
if (publishedWrapper != null) {
throw new IRVMLoweringException(
IRVMLoweringErrorCode.LOWER_IRVM_ENTRYPOINT_AMBIGUOUS,
"multiple published frame wrappers found in executable backend");
}
publishedWrapper = syntheticFunction;
}
return publishedWrapper;
}
private String bootGuardName(final ModuleId moduleId) {
final var normalizedModuleId = moduleId == null ? ModuleId.none() : moduleId;
return "__pbs.boot_guard$m" + (normalizedModuleId.isNone() ? -1 : normalizedModuleId.getIndex());
}
private boolean sameModuleIdentity(
final ModuleId left,
final ModuleId right) {
final var normalizedLeft = left == null ? ModuleId.none() : left;
final var normalizedRight = right == null ? ModuleId.none() : right;
if (normalizedLeft.isNone() && normalizedRight.isNone()) {
return true;
}
return !normalizedLeft.isNone()
&& !normalizedRight.isNone()
&& normalizedLeft.getIndex() == normalizedRight.getIndex();
}
private String moduleSortKey(
final IRBackend backend,
final ModuleId moduleId) {

View File

@ -3,6 +3,10 @@ package p.studio.compiler.backend.irvm;
import org.junit.jupiter.api.Test;
import p.studio.compiler.models.IRBackend;
import p.studio.compiler.models.IRBackendExecutableFunction;
import p.studio.compiler.models.IRGlobal;
import p.studio.compiler.models.IRSyntheticCallableKind;
import p.studio.compiler.models.IRSyntheticFunction;
import p.studio.compiler.models.IRSyntheticOrigin;
import p.studio.compiler.source.Span;
import p.studio.compiler.source.identifiers.CallableId;
import p.studio.compiler.source.identifiers.FileId;
@ -62,6 +66,17 @@ class LowerToIRVMServiceTest {
final var backend = IRBackend.builder()
.entryPointCallableName("__pbs.frame_wrapper$m0")
.entryPointModuleId(new ModuleId(0))
.globals(ReadOnlyList.from(new IRGlobal(
new FileId(0),
new ModuleId(0),
"__pbs.boot_guard$m0",
"bool",
Span.none())))
.syntheticFunctions(ReadOnlyList.from(new IRSyntheticFunction(
new ModuleId(0),
"__pbs.frame_wrapper$m0",
IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER,
new IRSyntheticOrigin(new FileId(0), "frame", new Span(new FileId(0), 1, 10)))))
.executableFunctions(ReadOnlyList.from(
fn("__pbs.file_init$f1", "app", 30, ReadOnlyList.from(ret())),
fn("__pbs.module_init$m0", "app", 31, ReadOnlyList.from(
@ -95,6 +110,105 @@ class LowerToIRVMServiceTest {
.op());
}
@Test
void lowerMustRejectWhenSyntheticWrapperEntrypointIsMissing() {
final var backend = IRBackend.builder()
.entryPointCallableName("__pbs.frame_wrapper$m0")
.entryPointModuleId(new ModuleId(0))
.globals(ReadOnlyList.from(new IRGlobal(
new FileId(0),
new ModuleId(0),
"__pbs.boot_guard$m0",
"bool",
Span.none())))
.syntheticFunctions(ReadOnlyList.from(new IRSyntheticFunction(
new ModuleId(0),
"__pbs.frame_wrapper$m0",
IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER,
new IRSyntheticOrigin(new FileId(0), "frame", new Span(new FileId(0), 1, 10)))))
.executableFunctions(ReadOnlyList.from(
fn("frame", "app", 20, ReadOnlyList.from(ret()))))
.build();
final var thrown = assertThrows(IRVMLoweringException.class, () -> new LowerToIRVMService().lower(backend));
assertEquals(IRVMLoweringErrorCode.LOWER_IRVM_SYNTHETIC_WRAPPER_ENTRYPOINT_MISSING, thrown.code());
}
@Test
void lowerMustRejectWhenPublishedWrapperWouldNotOccupyFunctionZero() {
final var backend = IRBackend.builder()
.entryPointCallableName("frame")
.entryPointModuleId(new ModuleId(0))
.globals(ReadOnlyList.from(new IRGlobal(
new FileId(0),
new ModuleId(0),
"__pbs.boot_guard$m0",
"bool",
Span.none())))
.syntheticFunctions(ReadOnlyList.from(new IRSyntheticFunction(
new ModuleId(0),
"__pbs.frame_wrapper$m0",
IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER,
new IRSyntheticOrigin(new FileId(0), "frame", new Span(new FileId(0), 1, 10)))))
.executableFunctions(ReadOnlyList.from(
fn("__pbs.frame_wrapper$m0", "app", 40, ReadOnlyList.from(
callFunc("app", "frame", 20),
ret())),
fn("frame", "app", 20, ReadOnlyList.from(ret()))))
.build();
final var thrown = assertThrows(IRVMLoweringException.class, () -> new LowerToIRVMService().lower(backend));
assertEquals(IRVMLoweringErrorCode.LOWER_IRVM_PUBLISHED_ENTRYPOINT_NOT_ZERO, thrown.code());
}
@Test
void lowerMustRejectWhenHiddenBootGuardIsMissing() {
final var backend = IRBackend.builder()
.entryPointCallableName("__pbs.frame_wrapper$m0")
.entryPointModuleId(new ModuleId(0))
.syntheticFunctions(ReadOnlyList.from(new IRSyntheticFunction(
new ModuleId(0),
"__pbs.frame_wrapper$m0",
IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER,
new IRSyntheticOrigin(new FileId(0), "frame", new Span(new FileId(0), 1, 10)))))
.executableFunctions(ReadOnlyList.from(
fn("__pbs.frame_wrapper$m0", "app", 40, ReadOnlyList.from(
callFunc("app", "frame", 20),
ret())),
fn("frame", "app", 20, ReadOnlyList.from(ret()))))
.build();
final var thrown = assertThrows(IRVMLoweringException.class, () -> new LowerToIRVMService().lower(backend));
assertEquals(IRVMLoweringErrorCode.LOWER_IRVM_HIDDEN_BOOT_GUARD_MISSING, thrown.code());
}
@Test
void lowerMustRejectWhenSyntheticCallableOriginIsMissing() {
final var backend = IRBackend.builder()
.entryPointCallableName("__pbs.frame_wrapper$m0")
.entryPointModuleId(new ModuleId(0))
.globals(ReadOnlyList.from(new IRGlobal(
new FileId(0),
new ModuleId(0),
"__pbs.boot_guard$m0",
"bool",
Span.none())))
.syntheticFunctions(ReadOnlyList.from(new IRSyntheticFunction(
new ModuleId(0),
"__pbs.frame_wrapper$m0",
IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER,
new IRSyntheticOrigin(new FileId(0), "", Span.none()))))
.executableFunctions(ReadOnlyList.from(
fn("__pbs.frame_wrapper$m0", "app", 40, ReadOnlyList.from(
callFunc("app", "frame", 20),
ret())),
fn("frame", "app", 20, ReadOnlyList.from(ret()))))
.build();
final var thrown = assertThrows(IRVMLoweringException.class, () -> new LowerToIRVMService().lower(backend));
assertEquals(IRVMLoweringErrorCode.LOWER_IRVM_SYNTHETIC_ORIGIN_MISSING, thrown.code());
}
@Test
void lowerMustMapHostAndIntrinsicCallsites() {
final var backend = IRBackend.builder()

View File

@ -13,9 +13,9 @@ import static org.junit.jupiter.api.Assertions.fail;
class BackendClaimScopeSpecTest {
private static final String LOWERING_SPEC_RELATIVE_PATH =
"docs/compiler/general/specs/20. IRBackend to IRVM Lowering Specification.md";
"docs/specs/compiler/20. IRBackend to IRVM Lowering Specification.md";
private static final String MATRIX_RELATIVE_PATH =
"docs/compiler/general/specs/22. Backend Spec-to-Test Conformance Matrix.md";
"docs/specs/compiler/22. Backend Spec-to-Test Conformance Matrix.md";
private static final String DECISION_RELATIVE_PATH =
"docs/compiler/pbs/decisions/SPAWN-YIELD v1 Claim Rescope Decision.md";
@ -63,7 +63,7 @@ class BackendClaimScopeSpecTest {
private Path locateRepoRoot() {
var cursor = Path.of(System.getProperty("user.dir")).toAbsolutePath().normalize();
while (cursor != null) {
final var hasDocs = Files.isDirectory(cursor.resolve("docs/compiler/general/specs"));
final var hasDocs = Files.isDirectory(cursor.resolve("docs/specs/compiler"));
final var hasCompiler = Files.isDirectory(cursor.resolve("prometeu-compiler"));
if (hasDocs && hasCompiler) {
return cursor;

View File

@ -14,7 +14,7 @@ import static org.junit.jupiter.api.Assertions.*;
class BackendConformanceMatrixSpecTest {
private static final String MATRIX_RELATIVE_PATH =
"docs/compiler/general/specs/22. Backend Spec-to-Test Conformance Matrix.md";
"docs/specs/compiler/22. Backend Spec-to-Test Conformance Matrix.md";
private static final String MATRIX_HARD_GATE_ENV = "PROMETEU_MATRIX_HARD_GATE";
private static final String CHANGED_FILES_ENV = "PROMETEU_CHANGED_FILES";
private static final List<String> HARD_GATE_PATH_PREFIXES = List.of(
@ -158,8 +158,9 @@ class BackendConformanceMatrixSpecTest {
var cursor = Path.of(System.getProperty("user.dir")).toAbsolutePath().normalize();
while (cursor != null) {
final var hasDocs = Files.isDirectory(cursor.resolve("docs/compiler/general/specs"));
final var hasCurrentDocs = Files.isDirectory(cursor.resolve("docs/specs/compiler"));
final var hasCompiler = Files.isDirectory(cursor.resolve("prometeu-compiler"));
if (hasDocs && hasCompiler) {
if ((hasDocs || hasCurrentDocs) && hasCompiler) {
return cursor;
}
cursor = cursor.getParent();

View File

@ -62,7 +62,7 @@ class NoModuleKeyRegressionSpecTest {
private Path locateRepoRoot() {
var cursor = Path.of(System.getProperty("user.dir")).toAbsolutePath().normalize();
while (cursor != null) {
final var hasDocs = Files.isDirectory(cursor.resolve("docs/compiler/general/specs"));
final var hasDocs = Files.isDirectory(cursor.resolve("docs/specs/compiler"));
final var hasCompiler = Files.isDirectory(cursor.resolve("prometeu-compiler"));
if (hasDocs && hasCompiler) {
return cursor;