Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions compatibility/compiler-expectations.json
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,20 @@
"note": "The shared hudSubheader action lowers to canonical Create HUD Text WIR with the pinned argument order and visibility semantics.",
"semanticEquivalent": true
},
{
"fixture": "synthetic/issue-131-spec-visibility",
"nativeStatus": "success",
"classification": "match",
"comparison": "semantic-wir",
"evidence": [
"oracle:synthetic/issue-131-spec-visibility/oracle.json",
"provenance:synthetic/issue-131-spec-visibility/fixture.json",
"test:opy-rs::compiler::integration_tests::issue_131_spec_visibility::never_maps_to_visible_never_in_canonical_wir"
],
"owner": "opy-rs#131",
"note": "The OPY SpecVisibility.NEVER spelling lowers to the canonical VISIBLE_NEVER enum member and is equivalent to the pinned oracle WIR; unrelated SpecVisibility members remain unchanged.",
"semanticEquivalent": true
},
{
"fixture": "synthetic/issue-47-unsupported",
"nativeStatus": "failure",
Expand Down Expand Up @@ -468,9 +482,9 @@
"provenance:real-world/overpy-crosshair/fixture.json"
],
"owner": "opy-rs#88",
"note": "The full real-world project now passes the implicit-concatenation and hudSubheader boundaries; the first remaining native gap is the unsupported SpecVisibility.NEVER enum member, while the byte-string modifier remains a separate documented gap.",
"note": "The full real-world project now passes the implicit-concatenation, hudSubheader, and SpecVisibility.NEVER boundaries; the remaining native gap is the separate unsupported hudSubtext action.",
"failureClass": "frontend",
"diagnosticCode": "unknown-enum-member"
"diagnosticCode": "unknown-action"
},
{
"fixture": "real-world/overpy-inputhud",
Expand Down
3 changes: 2 additions & 1 deletion compatibility/differential-expectations.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
{"fixture": "real-world/overpy-cronch", "nativeStatus": "failure", "classification": "known-gap", "ruleNames": false, "evidence": ["oracle:real-world/overpy-cronch/oracle.json", "regression:real-world/overpy-cronch/fixture.json"], "note": "The postfix increment regression now resolves; the full project reaches the next unsupported createDummy action at cronch.opy:103."},
{"fixture": "real-world/overpy-broken-weapons", "nativeStatus": "failure", "classification": "known-gap", "ruleNames": false, "evidence": ["oracle:real-world/overpy-broken-weapons/oracle.json", "regression:real-world/overpy-broken-weapons/fixture.json"], "note": "The numeric-range setting regression now resolves; the full project advances to the next known native gap at `isAssemblingHeroes`."},
{"fixture": "real-world/overpy-client-to-server", "nativeStatus": "failure", "classification": "known-gap", "ruleNames": false, "evidence": ["oracle:real-world/overpy-client-to-server/oracle.json", "regression:real-world/overpy-client-to-server/fixture.json"], "note": "The chained ternary and isDummy regressions now resolve; the full project remains an explicit known gap at the next unsupported member (`getHorizontalFacingAngle`)."},
{"fixture": "real-world/overpy-crosshair", "nativeStatus": "failure", "classification": "known-gap", "ruleNames": false, "evidence": ["oracle:real-world/overpy-crosshair/oracle.json", "regression:real-world/overpy-crosshair/fixture.json"], "note": "Implicit string concatenation and hudSubheader now lower; the first remaining native gap is the unsupported SpecVisibility.NEVER enum member, while the byte-string modifier remains a separate documented gap."},
{"fixture": "real-world/overpy-crosshair", "nativeStatus": "failure", "classification": "known-gap", "ruleNames": false, "evidence": ["oracle:real-world/overpy-crosshair/oracle.json", "regression:real-world/overpy-crosshair/fixture.json"], "note": "The SpecVisibility.NEVER and hudSubheader boundaries now lower; the full project reaches the separate unsupported hudSubtext action."},
{"fixture": "real-world/overpy-inputhud", "nativeStatus": "failure", "classification": "known-gap", "ruleNames": false, "evidence": ["oracle:real-world/overpy-inputhud/oracle.json", "regression:real-world/overpy-inputhud/fixture.json"], "note": "Implicit string concatenation now parses; the full project advances to the separate unsupported conditional-value expression gap."},
{"fixture": "real-world/overpy-parabola", "nativeStatus": "failure", "classification": "known-gap", "ruleNames": false, "evidence": ["oracle:real-world/overpy-parabola/oracle.json", "regression:real-world/overpy-parabola/fixture.json"], "note": "The numeric enum member regression now resolves; the full project reaches the next multiline parenthesized expression gap at parabola.opy:50."},
{"fixture": "real-world/overpy-meipocalypse", "nativeStatus": "failure", "classification": "match", "ruleNames": false, "evidence": ["oracle:real-world/overpy-meipocalypse/oracle.json", "provenance:real-world/overpy-meipocalypse/fixture.json"], "note": "The reference rejects the missing script hook; native rejection is retained with a different earlier diagnostic."},
Expand All @@ -58,6 +58,7 @@
{"fixture": "synthetic/issue-65-invalid-binder", "nativeStatus": "failure", "classification": "match", "ruleNames": false, "evidence": ["oracle:synthetic/issue-65-invalid-binder/oracle.json", "provenance:synthetic/issue-65-invalid-binder/fixture.json", "test:opy-rs::compiler::integration_tests::issue_65_player_variable::invalid_range_binder_has_a_stable_source_diagnostic"], "note": "The pinned oracle and native HIR validation reject a non-variable range binder with source-attributed diagnostics; the compiler test also pins the canonical lowering boundary."},
{"fixture": "synthetic/issue-113-is-dummy", "nativeStatus": "success", "classification": "match", "ruleNames": true, "evidence": ["oracle:synthetic/issue-113-is-dummy/oracle.json", "provenance:synthetic/issue-113-is-dummy/fixture.json", "test:opy-rs::compiler::integration_tests::issue_113_is_dummy::is_dummy_member_lowers_to_the_catalog_value_in_canonical_wir"], "note": "The catalog-backed eventPlayer.isDummy() member predicate resolves and lowers to the canonical Workshop value call."},
{"fixture": "synthetic/issue-114-hud-subheader", "nativeStatus": "success", "classification": "match", "ruleNames": true, "evidence": ["oracle:synthetic/issue-114-hud-subheader/oracle.json", "provenance:synthetic/issue-114-hud-subheader/fixture.json", "test:opy-rs::compiler::integration_tests::issue_114_hud_subheader::hud_subheader_matches_the_pinned_canonical_wir"], "note": "The shared hudSubheader action resolves and lowers to canonical Create HUD Text WIR with its argument and visibility contract."},
{"fixture": "synthetic/issue-131-spec-visibility", "nativeStatus": "success", "classification": "match", "ruleNames": true, "evidence": ["oracle:synthetic/issue-131-spec-visibility/oracle.json", "provenance:synthetic/issue-131-spec-visibility/fixture.json", "test:opy-rs::compiler::integration_tests::issue_131_spec_visibility::never_maps_to_visible_never_in_canonical_wir"], "note": "The OPY SpecVisibility.NEVER spelling resolves and lowers to the canonical VISIBLE_NEVER enum member."},
{"fixture": "synthetic/issue-47-control-flow", "nativeStatus": "success", "classification": "match", "ruleNames": false, "evidence": ["oracle:synthetic/issue-47-control-flow/oracle.json", "implementation-invariant:issue-47-control-flow-hir"], "note": "The #47 control-flow slice resolves in the source implementation and its native lowered WIR is directly equivalent to the pinned oracle."},
{"fixture": "synthetic/issue-47-unsupported", "nativeStatus": "success", "classification": "match", "ruleNames": false, "evidence": ["oracle:synthetic/issue-47-unsupported/oracle.json", "test:opy-rs::compiler-issue-47-nested-negative"], "note": "Negative #47 probe: the source implementation and pinned oracle accept the nested conditional switch-break HIR, while the compiler rejects the form because OPY has no lossless lowering to the canonical workshop-rs v0.1.16 control-flow contract."},
{"fixture": "synthetic/issue-47-switch-order", "nativeStatus": "success", "classification": "match", "ruleNames": true, "evidence": ["oracle:synthetic/issue-47-switch-order/oracle.json", "implementation-invariant:issue-47-authored-switch-order"], "note": "The #47 default-before-case probe preserves authored arm order and fallthrough in the source implementation, and the native lowered WIR is directly equivalent to the pinned oracle."},
Expand Down
1 change: 1 addition & 0 deletions compatibility/fixtures/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ repository:
| `issue-59-embedded-postfix-negative` | #59 pinned negative probe for the rejected embedded postfix form with a stable source-attributed parse diagnostic |
| `issue-65-player-range` | #65 pinned oracle-backed player-variable range binder probe for canonical `For Player Variable` lowering |
| `issue-65-invalid-binder` | #65 pinned negative probe for a non-variable range binder with a stable source-attributed diagnostic |
| `issue-131-spec-visibility` | #131 `SpecVisibility.NEVER` to canonical `VISIBLE_NEVER` enum mapping |
| `issue-47-control-flow` | #47 pinned oracle-backed control-flow lowering probe: if/elif/else, while, range-for, do-while expansion, switch fallthrough/default, and direct break |
| `issue-47-unsupported` | #47 negative probe: a break hidden inside a conditional switch arm is accepted by the source implementation/oracle but rejected by the compiler with a stable source-attributed diagnostic |
| `issue-47-switch-order` | #47 pinned oracle probe for a default arm before later case arms and source-order fallthrough |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"schemaVersion": 1,
"id": "synthetic/issue-131-spec-visibility",
"category": "synthetic",
"features": [
"compilation/canonical-enum-mapping",
"hudSubheader spectator visibility"
],
"source": "source.opy",
"expectedStatus": "success",
"provenance": {
"kind": "minimized-regression",
"origin": "wrightkit/opy-rs#131 acceptance reproduction",
"license": "AGPL-3.0-or-later",
"redistributable": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"compile": {
"diagnostics": [],
"exitCode": 0,
"status": "success",
"stdout": "",
"workshop": "rule (\"SpecVisibility.NEVER residual\") {\n event {\n Ongoing - Global;\n }\n actions {\n Create HUD Text(All Players(All Teams), Null, Custom String(\"text\"), Null, Top, 0, Null, Color(White), Null, Visible To, Visible Never);\n }\n}\n",
"workshopExact": "rule (\"SpecVisibility.NEVER residual\") {\n event {\n Ongoing - Global;\n }\n actions {\n Create HUD Text(All Players(All Teams), Null, Custom String(\"text\"), Null, Top, 0, Null, Color(White), Null, Visible To, Visible Never);\n }\n}\n\n",
"workshopSha256": "b0c3e0907fa1d7ae3fa7b5e49d1840750e0cf63fd7fe14643825daccb5a78708"
},
"fixture": "synthetic/issue-131-spec-visibility",
"input": {
"files": [
{
"path": "source.opy",
"sha256": "19bafa849c45605d4d7c4e2ffb90b8ce00dcf121bd18303441a51ef22d324ad2"
}
],
"sha256": "7ed4742471fee5c71f909d71dbbf15a3333524ec8f70817c8eef1e284d88e432",
"source": "source.opy"
},
"oracle": {
"gitHead": "1e2688954302a402d076944b46db07efb14d7b61",
"integrity": "sha512-oX17nauJcPTaKIrRFY/rD0Rl8atqFUVv9Hg2TKH+A68/fC8+ZO344Mkd1A/Y0oOVp1hr5tktMBjzMEDDnMEYUw==",
"language": "en-US",
"license": "GPL-3.0-only",
"name": "overpy",
"registryTarball": "https://registry.npmjs.org/overpy/-/overpy-9.7.10.tgz",
"repository": "https://github.com/Zezombye/overpy",
"version": "9.7.10"
},
"schemaVersion": 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
rule "SpecVisibility.NEVER residual":
@Event global
hudSubheader(getAllPlayers(), "text", HudPosition.TOP, 0, Color.WHITE, HudReeval.VISIBILITY, SpecVisibility.NEVER)
2 changes: 2 additions & 0 deletions crates/opy-rs/src/compiler/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ mod issue_114_hud_subheader;
mod issue_115_string_continuation;
#[path = "tests/issue_129_included_main_file.rs"]
mod issue_129_included_main_file;
#[path = "tests/issue_131_spec_visibility.rs"]
mod issue_131_spec_visibility;
#[path = "tests/issue_42_oracle.rs"]
mod issue_42_oracle;
#[path = "tests/issue_46_oracle.rs"]
Expand Down
87 changes: 87 additions & 0 deletions crates/opy-rs/src/compiler/tests/issue_131_spec_visibility.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//! Canonical WIR lowering coverage for issue #131.

use std::path::{Path, PathBuf};

use crate::Compiler;
use workshop_rs::catalog::{Catalog, Locale};
use workshop_rs::roundtrip::equivalent;
use workshop_rs::wir::{Action, Value};

fn fixture_dir() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../compatibility/fixtures/synthetic/issue-131-spec-visibility")
}

fn oracle_workshop() -> String {
let value: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string(fixture_dir().join("oracle.json"))
.expect("oracle must be readable"),
)
.expect("oracle must parse");
value["compile"]["workshop"]
.as_str()
.expect("oracle must contain Workshop output")
.to_string()
}

#[test]
fn never_maps_to_visible_never_in_canonical_wir() {
let dir = fixture_dir();
let source = std::fs::read_to_string(dir.join("source.opy")).expect("source is readable");
let hir = crate::compile(&source, "source.opy", &dir).expect("source must resolve");
let artifact = Compiler::new()
.expect("released Workshop contract must load")
.compile_hir(&hir)
.expect("SpecVisibility.NEVER must lower");

let catalog = Catalog::builtin().expect("catalog must load");
let oracle = workshop_rs::parser::parse(&oracle_workshop(), &catalog, &Locale::new("en-US"))
.expect("oracle output must reparse");
assert!(
equivalent(&artifact.wir, &oracle),
"SpecVisibility.NEVER WIR diverged from the pinned oracle\n--- native ---\n{}\n--- oracle ---\n{}",
artifact.emitted,
oracle_workshop()
);

let rule = artifact
.wir
.rules
.get(workshop_rs::wir::RuleId::from_index(0))
.expect("fixture has one rule");
let Action::Call { args, .. } = artifact.wir.actions.get(rule.actions[0]).unwrap() else {
panic!("hudSubheader must lower to a canonical action call");
};
assert!(matches!(
&artifact.wir.values.get(args[10]).unwrap().value,
Value::Enum { value_type, value }
if value_type == "SpecVisibility" && value == "VISIBLE_NEVER"
));
}

#[test]
fn unrelated_always_member_keeps_its_existing_diagnostic() {
let source = "rule \"r\":\n @Event global\n hudSubheader(getAllPlayers(), \"text\", HudPosition.TOP, 0, Color.WHITE, HudReeval.VISIBILITY, SpecVisibility.ALWAYS)\n";
let error = crate::compile(source, "source.opy", Path::new("."))
.expect_err("SpecVisibility.ALWAYS is outside issue #131 scope");
assert_eq!(error.code, "unknown-enum-member");
assert_eq!(
error.message,
"enum 'SpecVisibility' has no member 'ALWAYS'"
);
}

#[test]
fn invalid_spec_visibility_members_keep_source_attributed_diagnostics() {
let source = "rule \"r\":\n @Event global\n hudSubheader(getAllPlayers(), \"text\", HudPosition.TOP, 0, Color.WHITE, HudReeval.VISIBILITY, SpecVisibility.INVALID)\n";
let error =
crate::compile(source, "source.opy", Path::new(".")).expect_err("invalid member must fail");
assert_eq!(error.code, "unknown-enum-member");
assert_eq!(
error.message,
"enum 'SpecVisibility' has no member 'INVALID'"
);
let span = error.span.expect("diagnostic provenance");
assert_eq!(span.start.line, 3);
assert_eq!(span.start.col, 98);
}
6 changes: 5 additions & 1 deletion crates/opy-rs/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1161,14 +1161,18 @@ impl Lowerer {
// against the canonical Workshop catalog.
if self.manifest.domain_identity(name) {
let locale = Locale::new("en-US");
let catalog_member = match (name.as_str(), member) {
("SpecVisibility", "NEVER") => "VISIBLE_NEVER",
_ => member,
};
let canonical_member = self
.catalog
.enum_domain(name)
.and_then(|domain| {
domain
.members
.iter()
.find(|candidate| candidate.member == member)
.find(|candidate| candidate.member == catalog_member)
.map(|candidate| candidate.member.clone())
})
.or_else(|| {
Expand Down
10 changes: 8 additions & 2 deletions crates/opy-rs/tests/differential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,12 @@ fn declared_corpus() -> BTreeMap<&'static str, Case> {
true,
"Issue #114 shared hudSubheader action; canonical WIR lowering is constrained by the dedicated compiler test.",
);
resolve(
&mut cases,
"synthetic/issue-131-spec-visibility",
true,
"Issue #131 maps SpecVisibility.NEVER to the canonical VISIBLE_NEVER member; canonical WIR lowering is constrained by the dedicated compiler test.",
);
resolve(
&mut cases,
"synthetic/issue-47-control-flow",
Expand Down Expand Up @@ -469,8 +475,8 @@ fn declared_corpus() -> BTreeMap<&'static str, Case> {
diagnostic(
&mut cases,
"real-world/overpy-crosshair",
Some("parse-error"),
"reference accepts; the native frontend rejects the `b\"…\"` byte-string modifier (baseline category 1b, legacy-quirk/demand-driven, explicit rejection). Gap: reference accepts, native rejects (documented).",
Some("unknown-action"),
"the SpecVisibility.NEVER and hudSubheader boundaries now resolve; the full project reaches the separate unsupported hudSubtext action. Gap: reference accepts, native rejects (documented).",
);
diagnostic(
&mut cases,
Expand Down
Loading