iter 23.1.4: E2E fixture — bare LT match via implicit prelude import
This commit is contained in:
@@ -895,6 +895,19 @@ fn workspace_build_runs_imported_fn() {
|
|||||||
assert_eq!(stdout.trim(), "5", "ws_lib.add(2,3) should print 5");
|
assert_eq!(stdout.trim(), "5", "ws_lib.add(2,3) should print 5");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Guards Iter 23.1: `examples/ordering_match.ail.json` pattern-matches
|
||||||
|
/// on the prelude `Ordering` ctor `LT` without an explicit prelude import.
|
||||||
|
/// End-to-end: typecheck → codegen → link → run → stdout exactly `"1\n"`.
|
||||||
|
/// Property: the implicit prelude import wired by `build_check_env` /
|
||||||
|
/// `check_in_workspace` (Iter 23.1 Task 3) propagates through codegen so
|
||||||
|
/// the bare ctor name `LT` resolves cross-module without an explicit
|
||||||
|
/// `imports: ["prelude"]` entry in the source module.
|
||||||
|
#[test]
|
||||||
|
fn ordering_match_via_prelude_prints_1() {
|
||||||
|
let stdout = build_and_run("ordering_match.ail.json");
|
||||||
|
assert_eq!(stdout, "1\n");
|
||||||
|
}
|
||||||
|
|
||||||
/// Guards Iter 5d: `ail manifest --workspace --json` lists defs from all
|
/// Guards Iter 5d: `ail manifest --workspace --json` lists defs from all
|
||||||
/// modules of the workspace — visible via a `module` field per entry.
|
/// modules of the workspace — visible via a `module` field per entry.
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1639,6 +1639,16 @@ impl<'a> Emitter<'a> {
|
|||||||
/// under a swapped `module_name` (see `emit_specialised_fn`)
|
/// under a swapped `module_name` (see `emit_specialised_fn`)
|
||||||
/// resolve its bare ctor references against the *owner* module's
|
/// resolve its bare ctor references against the *owner* module's
|
||||||
/// ctor table, not the consumer's.
|
/// ctor table, not the consumer's.
|
||||||
|
///
|
||||||
|
/// Iter 23.1: bare-type lookup now falls back to imported modules
|
||||||
|
/// when the ctor is not local — mirroring `lookup_ctor_in_pattern`.
|
||||||
|
/// The typechecker (Iter 23.1 Task 3) already accepts a bare
|
||||||
|
/// `Term::Ctor { type_name, ctor }` whose `type_name` is owned by
|
||||||
|
/// an imported module (the implicit `prelude` import is the
|
||||||
|
/// motivating case: an `Ordering` ctor `LT` written without an
|
||||||
|
/// explicit `imports: ["prelude"]`). Codegen must honour the same
|
||||||
|
/// resolution path so the lockstep `Pattern::Ctor ↔ Term::Ctor ↔
|
||||||
|
/// codegen` is preserved.
|
||||||
pub(crate) fn lookup_ctor_by_type(
|
pub(crate) fn lookup_ctor_by_type(
|
||||||
&self,
|
&self,
|
||||||
type_name: &str,
|
type_name: &str,
|
||||||
@@ -1669,24 +1679,41 @@ impl<'a> Emitter<'a> {
|
|||||||
}
|
}
|
||||||
Ok(cref)
|
Ok(cref)
|
||||||
} else {
|
} else {
|
||||||
let cref = self
|
// Local-first: current module's ctor table wins on conflict.
|
||||||
|
if let Some(cref) = self
|
||||||
.module_ctor_index
|
.module_ctor_index
|
||||||
.get(self.module_name)
|
.get(self.module_name)
|
||||||
.and_then(|m| m.get(ctor_name))
|
.and_then(|m| m.get(ctor_name))
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| {
|
{
|
||||||
CodegenError::Internal(format!(
|
if cref.type_name != type_name {
|
||||||
"unknown ctor `{ctor_name}` in module `{}`",
|
return Err(CodegenError::Internal(format!(
|
||||||
self.module_name
|
"ctor `{ctor_name}` belongs to `{}`, not `{type_name}`",
|
||||||
))
|
cref.type_name
|
||||||
})?;
|
)));
|
||||||
if cref.type_name != type_name {
|
}
|
||||||
return Err(CodegenError::Internal(format!(
|
return Ok(cref);
|
||||||
"ctor `{ctor_name}` belongs to `{}`, not `{type_name}`",
|
|
||||||
cref.type_name
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
Ok(cref)
|
// Iter 23.1: fallback — scan other modules for a bare ctor
|
||||||
|
// whose declared `type_name` matches. The typechecker has
|
||||||
|
// already pinned uniqueness (an ambiguous bare type name
|
||||||
|
// is rejected with `CheckError::AmbiguousType`), so the
|
||||||
|
// first hit whose owning type matches the requested
|
||||||
|
// `type_name` is unique. Mirrors `lookup_ctor_in_pattern`.
|
||||||
|
for (mname, ctors) in self.module_ctor_index.iter() {
|
||||||
|
if mname == self.module_name {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(cref) = ctors.get(ctor_name).cloned() {
|
||||||
|
if cref.type_name == type_name {
|
||||||
|
return Ok(cref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(CodegenError::Internal(format!(
|
||||||
|
"unknown ctor `{ctor_name}` in module `{}`",
|
||||||
|
self.module_name
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"schema": "ailang/v0",
|
||||||
|
"name": "ordering_match",
|
||||||
|
"imports": [],
|
||||||
|
"defs": [
|
||||||
|
{
|
||||||
|
"kind": "fn",
|
||||||
|
"name": "main",
|
||||||
|
"type": {
|
||||||
|
"k": "fn",
|
||||||
|
"params": [],
|
||||||
|
"ret": { "k": "con", "name": "Unit" },
|
||||||
|
"effects": ["IO"]
|
||||||
|
},
|
||||||
|
"params": [],
|
||||||
|
"doc": "Iter 23.1 fixture: pattern-match on prelude Ordering ctor without explicit prelude import.",
|
||||||
|
"body": {
|
||||||
|
"t": "do",
|
||||||
|
"op": "io/print_int",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"t": "match",
|
||||||
|
"scrutinee": {
|
||||||
|
"t": "ctor",
|
||||||
|
"type": "Ordering",
|
||||||
|
"ctor": "LT",
|
||||||
|
"args": []
|
||||||
|
},
|
||||||
|
"arms": [
|
||||||
|
{
|
||||||
|
"pat": { "p": "ctor", "ctor": "LT", "fields": [] },
|
||||||
|
"body": { "t": "lit", "lit": { "kind": "int", "value": 1 } }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pat": { "p": "ctor", "ctor": "EQ", "fields": [] },
|
||||||
|
"body": { "t": "lit", "lit": { "kind": "int", "value": 2 } }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pat": { "p": "ctor", "ctor": "GT", "fields": [] },
|
||||||
|
"body": { "t": "lit", "lit": { "kind": "int", "value": 3 } }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user