iter 23.4-prep.2: check — bare-name fall-through to implicit-imported free fns
This commit is contained in:
@@ -1806,6 +1806,34 @@ pub(crate) fn synth(
|
|||||||
method: name.clone(),
|
method: name.clone(),
|
||||||
});
|
});
|
||||||
return Ok(inst_ty);
|
return Ok(inst_ty);
|
||||||
|
} else if let Some((owner_module, raw_ty)) = env
|
||||||
|
.imports
|
||||||
|
.values()
|
||||||
|
.find_map(|mod_name| {
|
||||||
|
env.module_globals
|
||||||
|
.get(mod_name)
|
||||||
|
.and_then(|mg| mg.get(name).map(|ty| (mod_name.clone(), ty.clone())))
|
||||||
|
})
|
||||||
|
{
|
||||||
|
// Iter 23.4-prep: bare-name fall-through to free fns of
|
||||||
|
// implicitly-imported modules. Today only the prelude is
|
||||||
|
// implicitly imported (see `build_check_env` and
|
||||||
|
// `check_in_workspace`'s prelude-injection at this file's
|
||||||
|
// ~1170 / ~1285), so the first match is unambiguous. If a
|
||||||
|
// second implicit import lands, the single-match-wins rule
|
||||||
|
// becomes a real ambiguity and this branch needs a
|
||||||
|
// duplicate-detection diagnostic.
|
||||||
|
//
|
||||||
|
// Qualify any bare type-cons referring to a type defined in
|
||||||
|
// the owning module — same treatment the dot-qualified arm
|
||||||
|
// below applies — so signatures cross the module boundary
|
||||||
|
// with fully-qualified Type::Cons.
|
||||||
|
let owner_types = env
|
||||||
|
.module_types
|
||||||
|
.get(&owner_module)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default();
|
||||||
|
qualify_local_types(&raw_ty, &owner_module, &owner_types)
|
||||||
} else if name.matches('.').count() == 1 {
|
} else if name.matches('.').count() == 1 {
|
||||||
let (prefix, suffix) = name.split_once('.').expect("checked");
|
let (prefix, suffix) = name.split_once('.').expect("checked");
|
||||||
let target_module = match env.imports.get(prefix) {
|
let target_module = match env.imports.get(prefix) {
|
||||||
@@ -4935,4 +4963,67 @@ mod tests {
|
|||||||
let diags = check_workspace(&ws);
|
let diags = check_workspace(&ws);
|
||||||
assert!(diags.is_empty(), "expected green; got {diags:?}");
|
assert!(diags.is_empty(), "expected green; got {diags:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iter 23.4-prep Task 2: bare-name resolution falls through to free
|
||||||
|
/// fns of implicitly-imported modules. The consumer calls bare `dbl`,
|
||||||
|
/// declared in `prelude` (the singleton implicit import today). The
|
||||||
|
/// pre-fix behaviour is `unbound-var` because the `Term::Var` lookup
|
||||||
|
/// ladder consults locals → local globals → class_methods →
|
||||||
|
/// dot-qualified, but never the flat free-fn tables of implicit
|
||||||
|
/// imports.
|
||||||
|
#[test]
|
||||||
|
fn bare_name_resolves_through_implicit_import_to_free_fn() {
|
||||||
|
let prelude = Module {
|
||||||
|
schema: SCHEMA.into(),
|
||||||
|
name: "prelude".into(),
|
||||||
|
imports: vec![],
|
||||||
|
defs: vec![fn_def(
|
||||||
|
"dbl",
|
||||||
|
Type::Fn {
|
||||||
|
params: vec![Type::int()],
|
||||||
|
ret: Box::new(Type::int()),
|
||||||
|
effects: vec![],
|
||||||
|
param_modes: vec![],
|
||||||
|
ret_mode: ParamMode::Implicit,
|
||||||
|
},
|
||||||
|
vec!["n"],
|
||||||
|
Term::Var { name: "n".into() },
|
||||||
|
)],
|
||||||
|
};
|
||||||
|
let consumer = Module {
|
||||||
|
schema: SCHEMA.into(),
|
||||||
|
name: "m".into(),
|
||||||
|
imports: vec![],
|
||||||
|
defs: vec![fn_def(
|
||||||
|
"main",
|
||||||
|
Type::Fn {
|
||||||
|
params: vec![],
|
||||||
|
ret: Box::new(Type::int()),
|
||||||
|
effects: vec![],
|
||||||
|
param_modes: vec![],
|
||||||
|
ret_mode: ParamMode::Implicit,
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
Term::App {
|
||||||
|
callee: Box::new(Term::Var { name: "dbl".into() }),
|
||||||
|
args: vec![Term::Lit { lit: Literal::Int { value: 5 } }],
|
||||||
|
tail: false,
|
||||||
|
},
|
||||||
|
)],
|
||||||
|
};
|
||||||
|
let mut modules = BTreeMap::new();
|
||||||
|
modules.insert("prelude".into(), prelude);
|
||||||
|
modules.insert("m".into(), consumer);
|
||||||
|
let ws = Workspace {
|
||||||
|
entry: "m".into(),
|
||||||
|
modules,
|
||||||
|
root_dir: std::path::PathBuf::from("."),
|
||||||
|
registry: ailang_core::workspace::Registry::default(),
|
||||||
|
};
|
||||||
|
let diags = check_workspace(&ws);
|
||||||
|
assert!(
|
||||||
|
diags.is_empty(),
|
||||||
|
"expected bare `dbl` to resolve through implicit prelude import; got {diags:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user