iter mq.1: class-ref canonical-form extension + workspace-internal class-name qualification
Three schema fields move bare → canonical (bare for same-module,
<module>.<Class> for cross-module): InstanceDef.class,
Constraint.class, SuperclassRef.class. Symmetric to ct.1's
Type::Con.name rule. ClassDef.name stays bare.
validate_canonical_type_names gains three field-walks via new
check_class_ref helper + two sibling diagnostics
BareCrossModuleClassRef / BadCrossModuleClassRef.
check_class_name_fields narrowed to ClassDef.name-only.
Workspace-internal class-name keys all qualified:
class_def_module, class_by_name, registry entries.0,
ClassMethodEntry.class_name, class_superclasses, mono's
class_index, Origin::Class.class_name. New qualify_class_ref
helper at workspace.rs scope plus sibling
qualify_class_ref_in_check in ailang-check.
Two new positive on-disk fixtures (mq1_xmod_constraint_class{,_dep})
exercise the qualified Constraint.class shape.
Recon claim that all existing fixtures' class-refs are intra-module
was empirically wrong — 10 fixtures required minimal canonical-form
migration. Duplicate-instance test architecturally restructured:
post-mq.1 orphan-freedom makes two-modules-on-same-(class,type)
structurally impossible; new test_22b1_dup_same_module fixture
lands both instances in the class's module.
Plan defects fixed inline: Origin::Class.class_name single
construction site (plan recon off-by-one); build_class_index in
mono.rs needed qualifying; build_module_globals needed Def::Instance
carve-out; check_fn declared-constraint matching needed inline
canonical-form lifting; NoInstance Float-aware message arm needed
prelude.Eq/prelude.Ord match; coherence type-leg needed qualified-
head split-and-lookup. Tasks 3-6 ran as one coherent fix.
7/7 tasks, 520 tests green. bench/compile_check.py + cross_lang.py
exit 0; bench/check.py exit 1 (4 improvements-beyond-tolerance,
audit-ratifiable, not a regression).
MethodNameCollision + dispatch path unchanged; iters mq.2 + mq.3
land them.
This commit is contained in:
@@ -86,14 +86,18 @@ fn check_json_emits_bad_cross_module_type_ref() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Property: a qualified class name in an `InstanceDef.class` field
|
||||
/// (here `prelude.Eq`) is rejected by `ail check --json` with
|
||||
/// diagnostic code `qualified-class-name` and non-zero exit. ct.1
|
||||
/// reserves module qualification for type names; classes stay bare in
|
||||
/// this milestone. Guards against `workspace_error_to_diagnostic`
|
||||
/// losing the `QualifiedClassName` arm.
|
||||
/// Property: mq.1 — a qualified class name in an `InstanceDef.class`
|
||||
/// field is the canonical form, not a rejection. The
|
||||
/// `test_ct1_qualified_class_rejected` fixture (declares
|
||||
/// `instance prelude.Eq Int` outside prelude and outside Int's
|
||||
/// defining module) is now rejected by the downstream coherence
|
||||
/// check with `orphan-instance` instead of the pre-mq.1
|
||||
/// `qualified-class-name`. Guards against
|
||||
/// `workspace_error_to_diagnostic` losing the OrphanInstance arm
|
||||
/// AND against any regression that would reintroduce
|
||||
/// `qualified-class-name` on a referencing field.
|
||||
#[test]
|
||||
fn check_json_emits_qualified_class_name() {
|
||||
fn check_json_emits_orphan_instance_on_xmod_class_without_coherence_post_mq1() {
|
||||
let fixture = examples_dir().join("test_ct1_qualified_class_rejected.ail.json");
|
||||
let output = Command::new(ail_bin())
|
||||
.args(["check", "--json", fixture.to_str().unwrap()])
|
||||
@@ -101,7 +105,7 @@ fn check_json_emits_qualified_class_name() {
|
||||
.expect("ail binary must launch");
|
||||
assert!(
|
||||
!output.status.success(),
|
||||
"ail check must fail on qualified class name; stdout={} stderr={}",
|
||||
"ail check must fail; stdout={} stderr={}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr),
|
||||
);
|
||||
@@ -110,8 +114,12 @@ fn check_json_emits_qualified_class_name() {
|
||||
serde_json::from_str(&stdout).expect("--json mode emits a JSON array on stdout");
|
||||
let arr = diags.as_array().expect("diagnostics is a JSON array");
|
||||
assert!(
|
||||
arr.iter().any(|d| d["code"] == "qualified-class-name"),
|
||||
"expected `qualified-class-name` in diagnostics array; got {stdout}"
|
||||
arr.iter().any(|d| d["code"] == "orphan-instance"),
|
||||
"expected `orphan-instance` in diagnostics array; got {stdout}"
|
||||
);
|
||||
assert!(
|
||||
!arr.iter().any(|d| d["code"] == "qualified-class-name"),
|
||||
"must NOT fire `qualified-class-name` on a referencing field post-mq.1; got {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,12 @@ fn class_method_is_in_module_globals() {
|
||||
mod_globals.has_class_method("show"),
|
||||
"class method `show` must appear in module globals"
|
||||
);
|
||||
// mq.1: class_method_class returns the qualified form
|
||||
// `<defining_module>.<Class>`.
|
||||
assert_eq!(
|
||||
mod_globals.class_method_class("show"),
|
||||
Some("Show"),
|
||||
"class method `show` must remember its class is `Show`"
|
||||
Some("test_22b2_class_method_lookup.Show"),
|
||||
"class method `show` must remember its class is `Show` (qualified post-mq.1)"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +148,8 @@ fn collect_mono_targets_single_concrete_call_site() {
|
||||
let t = &targets[0];
|
||||
match t {
|
||||
ailang_check::mono::MonoTarget::ClassMethod { class, method, type_, defining_module } => {
|
||||
assert_eq!(class, "Show");
|
||||
// mq.1: MonoTarget.class carries the qualified workspace-key shape.
|
||||
assert_eq!(class, "test_22b2_instance_present.Show");
|
||||
assert_eq!(method, "show");
|
||||
assert!(
|
||||
matches!(type_, ailang_core::ast::Type::Con { name, args } if name == "Int" && args.is_empty()),
|
||||
|
||||
Reference in New Issue
Block a user