Skip to main content

nova_typeck/
check.rs

1//! AST → typed HIR checking: signature collection, body inference,
2//! desugaring, and Maranget-based exhaustiveness/reachability analysis.
3
4use nova_ast as ast;
5use nova_ast::item::{ExternItem, TraitItem, TypeDef};
6use nova_diagnostics::{suggest, Diagnostic, FileDb, Span, Spanned};
7use nova_hir as hir;
8use nova_hir::{LocalId, Ty, TyHead};
9use nova_resolver::{
10    Builtin, DefId, DefKind, Definitions, Index, LocalFlags, MethodOwner, ModuleId, Res, Role,
11    Target,
12};
13use rustc_hash::{FxHashMap, FxHashSet};
14
15use crate::infer::InferCtx;
16use crate::usefulness;
17use crate::{
18    display_ty, display_ty_named, CheckOptions, CheckResult, Member, MemberKind, ProbePoint,
19    ProbeResult,
20};
21
22mod fixes;
23
24use fixes::{Binding, Need};
25
26/// A collected function (or method) signature.
27#[derive(Debug, Clone)]
28struct FnSig {
29    generics: u32,
30    /// Trait bounds per generic parameter.
31    bounds: Vec<Vec<DefId>>,
32    /// Parameter types. For a method that declares a `self` receiver,
33    /// `params[0]` is that receiver. An associated function (an impl method with
34    /// no `self`, tracked in `Checker::selfless`) holds only its declared
35    /// parameters, so `params` stays positionally aligned with the AST's.
36    params: Vec<Ty>,
37    ret: Ty,
38}
39
40/// Outcome of resolving a method name against a receiver type.
41enum MethodRes {
42    /// An inherent method (compiled function `DefId`).
43    Inherent(DefId),
44    /// A trait method: `(trait_id, method_index)`.
45    Trait(DefId, u32),
46    /// No method of that name applies.
47    None,
48    /// More than one trait provides the method.
49    Ambiguous,
50}
51
52/// The `next` a `for` loop drives, as [`Checker::iterator_next`] resolved it.
53///
54/// The `Option` sum and its two variant indices travel with the method because
55/// they are read off the *same* trait declaration: a caller that looked them up
56/// separately could pair a `next` from one trait with the variant numbering of
57/// another sum, which lowers to a `match` switching on the wrong tags.
58struct IteratorNext {
59    trait_id: DefId,
60    method_idx: u32,
61    /// The sum `next` returns — `Option` in std, but only its shape is required.
62    option: DefId,
63    /// Index of the one-field `Some` variant, and of the empty `None` variant.
64    some: u32,
65    none: u32,
66}
67
68/// How a trait call names its `Self` type — the *only* difference between a
69/// method call (`x.cmp(y)`) and an associated-function call (`Int::default()`).
70///
71/// Bundling the receiver together with the `Self` type it determines is what
72/// lets [`Checker::emit_trait_call`] be the single emitter for both: there is
73/// no way to pass a receiver without `Self` being derived from it, no way to
74/// pass both a receiver and an unrelated `Self`, and no way to pass neither.
75/// Two sibling emitters instead had to keep the flat `Param` substitution
76/// layout (`[Self] ++ method generics`) in step by hand, and a divergence there
77/// silently dispatches to a wrongly specialized function.
78enum TraitCallSelf<'a> {
79    /// `receiver.name(args)`: `Self` is the receiver's resolved type and the
80    /// receiver becomes the callee's `self` argument. Valid only for a method
81    /// the trait declares *with* a `self` receiver ([`hir::TraitMethod::has_self`]).
82    ///
83    /// The receiver's **AST** travels alongside its checked form for the same
84    /// reason [`Checker::check_mutable_receiver`] takes one: `place_root` walks
85    /// the field/index projection shape, which the checked `hir::Expr` has
86    /// already lost. Carrying it *in the variant* rather than as a separate
87    /// parameter is what makes the mutable-receiver rule unbypassable by route —
88    /// there is no way to hand `emit_trait_call` a receiver without also handing
89    /// it the place to classify, so a future caller cannot reintroduce the gap
90    /// ADR 0005 §1 recorded by simply forgetting a check.
91    Receiver(hir::Expr, &'a Spanned<ast::Expr>),
92    /// `Type::name(args)` or `T::name(args)`: `Self` comes from the path
93    /// qualifier and there is no receiver. Valid only for a receiver-less
94    /// method (a trait associated function).
95    Qualifier(Ty),
96}
97
98/// The `Self` of an `impl` block, for resolving `Self::Item` written inside it.
99///
100/// An impl's `Self` cannot be a `generics` entry the way a trait body's is: a
101/// trait body's `Self` is an implicit type *parameter* at `Param(0)`, whereas an
102/// impl's is a concrete (possibly compound) type — `W<Param(0)>` for
103/// `impl<T> Tr for W<T>` — with no parameter index and therefore no slot in the
104/// by-index bound table either. Its candidate traits are not bounds at all but
105/// the single trait the impl implements.
106#[derive(Debug, Clone)]
107struct ImplSelf {
108    /// The impl's self type, with the impl's own `Param(k)` still in it.
109    ty: Ty,
110    /// The trait this impl implements, or `None` for an inherent impl — which
111    /// implements no trait and so has no associated type to project onto.
112    trait_id: Option<DefId>,
113}
114
115/// The AST location and flavor of a method to compile.
116#[derive(Debug, Clone, Copy)]
117struct MethodLoc {
118    item_index: usize,
119    method_index: usize,
120    owner: MethodOwner,
121}
122
123/// Type-check a parsed file against its resolved definitions.
124pub fn check(file: &ast::File, defs: &Definitions) -> CheckResult {
125    check_with(file, defs, &CheckOptions::default())
126}
127
128/// [`check`], with the language server's probe (spec
129/// `docs/superpowers/specs/2026-10-08-phase-3-2-lsp-core-design.md` §4.1).
130pub fn check_with(file: &ast::File, defs: &Definitions, options: &CheckOptions) -> CheckResult {
131    let mut checker = Checker {
132        file,
133        defs,
134        cur_module: ModuleId(0),
135        sigs: FxHashMap::default(),
136        method_locs: FxHashMap::default(),
137        selfless: FxHashSet::default(),
138        mut_self: FxHashSet::default(),
139        sums: Vec::new(),
140        records: Vec::new(),
141        supertraits: FxHashMap::default(),
142        traits: Vec::new(),
143        impls: Vec::new(),
144        impl_self: None,
145        impl_selves: FxHashMap::default(),
146        extra_functions: Vec::new(),
147        next_closure_def: defs.defs().len() as u32,
148        type_arity: FxHashMap::default(),
149        externs: Vec::new(),
150        diagnostics: Vec::new(),
151        probe: options.probe,
152        probe_result: ProbeResult::default(),
153        probe_pending: None,
154        probe_locals_pending: None,
155        index: options.index.then(Index::default),
156        type_params: Vec::new(),
157        sources: options.sources,
158        bindings: FxHashMap::default(),
159        qualified_callee: None,
160    };
161    // Before any collection pass: every later pass builds a generic scope, and
162    // one containing `Self` would give the name two meanings at once.
163    checker.note_item_declarations();
164    checker.reject_self_type_params();
165    checker.collect_type_arities();
166    // Before `collect_records`: since Task 1 of the iterator-finishing plan,
167    // `collect_records` resolves each record's bounds and calls
168    // `expand_bounds`, which folds transitive supertraits in from
169    // `Checker::supertraits` — the very table `collect_supertraits` builds.
170    // Moved ahead of `collect_records` (and `collect_sums`, which never reads
171    // it but has no reason to run first either) so that table is populated
172    // before the first `expand_bounds` call, not just before
173    // `collect_traits`'s. `collect_supertraits` itself only reads trait
174    // declarations via `self.defs` (already fully resolved before `check`
175    // runs) and `decl.supertraits`, so it has no ordering dependency on
176    // `collect_type_arities`, `collect_records`, or `collect_sums`.
177    //
178    // It did change the *order* diagnostics are rendered in, and nothing sorts
179    // `self.diagnostics` before rendering, so pass order is emission order.
180    // Measured on one program carrying an unresolvable supertrait, a duplicate
181    // record generic and a sum-type bound: `E0001` -> `E0403` -> `E0900` now,
182    // `E0403` -> `E0900` -> `E0001` with this call back in its old slot (the
183    // move was re-run in both directions to confirm it, not inferred from the
184    // pass list). Cosmetic — every diagnostic is still reported, with its own
185    // span — and recorded rather than fixed: stabilizing order would mean
186    // sorting by span, which is a separate decision affecting every diagnostic
187    // in the compiler.
188    checker.collect_supertraits();
189    checker.collect_records();
190    checker.collect_sums();
191    checker.collect_traits();
192    checker.collect_impls();
193    checker.collect_signatures();
194    checker.collect_externs();
195
196    let mut functions = Vec::new();
197    let fn_ids: Vec<(DefId, usize)> = defs.functions().collect();
198    for (def_id, item_index) in fn_ids {
199        if let Some(f) = checker.check_function(def_id, item_index) {
200            functions.push(f);
201        }
202    }
203    let method_ids: Vec<DefId> = checker.method_locs.keys().copied().collect();
204    for def_id in method_ids {
205        if let Some(f) = checker.check_method(def_id) {
206            functions.push(f);
207        }
208    }
209    for (def_id, item_index) in checker.const_ids() {
210        if let Some(f) = checker.check_const(def_id, item_index) {
211            functions.push(f);
212        }
213    }
214    checker.check_const_cycles(&functions);
215    // Closure and bare-fn-wrapper functions synthesized while checking.
216    functions.append(&mut checker.extra_functions);
217
218    CheckResult {
219        module: hir::Module {
220            sums: checker.sums,
221            records: checker.records,
222            traits: checker.traits,
223            impls: checker.impls,
224            functions,
225            externs: checker.externs,
226        },
227        diagnostics: checker.diagnostics,
228        probe: checker.probe_result,
229        index: checker.index,
230    }
231}
232
233struct Checker<'a> {
234    file: &'a ast::File,
235    defs: &'a Definitions,
236    /// Module owning the item currently being processed; name resolution is
237    /// performed relative to it. Set at each per-item entry point.
238    cur_module: ModuleId,
239    sigs: FxHashMap<DefId, FnSig>,
240    /// AST location of each method `DefId`, for the compile pass.
241    method_locs: FxHashMap<DefId, MethodLoc>,
242    /// Impl methods that declare no `self` receiver (associated functions).
243    /// Their `sigs` entry holds only the declared parameters — no prepended
244    /// self type — so `check_fn_body`'s params/sig zip stays aligned.
245    selfless: FxHashSet<DefId>,
246    /// Impl methods whose `self` receiver is declared `mut`. Calling one requires
247    /// a mutable receiver place at the call site, so `mut` keeps the meaning it
248    /// already has for `arr[i] = v` and `rec.f = v` (see ADR 0005 §1).
249    ///
250    /// Populated in `collect_impls` for **every** impl method, inherent or in a
251    /// trait impl, and read for two different purposes. An *inherent* callee is
252    /// looked up here at its call site ([`Checker::check_mutable_receiver`]). A
253    /// *trait* callee is not: the call site consults
254    /// [`hir::TraitMethod::mut_self`] instead, because trait dispatch resolves
255    /// to `(trait_id, method_index)` with no impl to look up. The trait-impl
256    /// entries are what
257    /// [`Checker::check_impl_method_signatures`] compares against the trait's
258    /// declaration, so the two flags cannot silently disagree.
259    mut_self: FxHashSet<DefId>,
260    sums: Vec<hir::SumType>,
261    records: Vec<hir::RecordType>,
262    /// Direct supertraits of every trait (`trait Ord: Eq` → `Ord ↦ [Eq]`),
263    /// resolved by [`Checker::collect_supertraits`] *before* any bound list is
264    /// built. Expansion cannot read them off the incrementally-populated
265    /// `traits` table: a bound may name a trait declared later in the file, so
266    /// some bound lists would be expanded and others not — and
267    /// `check_impl_conformance` compares a trait method's bound set against the
268    /// impl method's for equality, which a half-expanded table turns into a
269    /// bogus `E0072`.
270    supertraits: FxHashMap<DefId, Vec<DefId>>,
271    traits: Vec<hir::TraitDef>,
272    impls: Vec<hir::ImplInfo>,
273    /// The `Self` of the `impl` block whose signatures or body are being
274    /// converted right now, or `None` outside one. Read by [`Checker::convert_ty`]
275    /// to resolve `Self::Item` inside an impl.
276    ///
277    /// Ambient rather than a `convert_ty` parameter because `convert_ty` has 18
278    /// call sites and only two of them are inside an impl. The discipline is
279    /// that every per-item entry point sets it: `collect_impls` per item,
280    /// `check_method` per method (`None` for a trait default body, whose `Self`
281    /// is the trait's own `Param(0)` in `generics` instead), and
282    /// `check_function` / `check_const` clear it.
283    impl_self: Option<ImplSelf>,
284    /// Each impl's `Self`, keyed by AST item index, so [`Checker::check_method`]
285    /// can restore it when compiling a body in a later pass without re-running
286    /// (and re-diagnosing) the self-type conversion.
287    impl_selves: FxHashMap<usize, ImplSelf>,
288    /// Lifted closure / fn-wrapper functions, appended to the module.
289    extra_functions: Vec<hir::Function>,
290    /// Next synthetic `DefId` for a closure/wrapper (starts past all
291    /// resolver-assigned defs so it never collides).
292    next_closure_def: u32,
293    /// Generic-parameter arity of every record/sum type, precomputed so a
294    /// type's arity never depends on collection order (a field or variant may
295    /// reference a type collected later, including the implicit prelude).
296    type_arity: FxHashMap<DefId, u32>,
297    /// `extern` (FFI) function declarations, collected into the HIR module.
298    externs: Vec<hir::ExternFn>,
299    diagnostics: Vec<Diagnostic>,
300    /// The language server's probe (spec 3.2 §4.1), what it has found, and
301    /// the receiver it met in the function being checked. That receiver's
302    /// type is read when inference finishes the function
303    /// (`finalize_function`).
304    probe: Option<ProbePoint>,
305    probe_result: ProbeResult,
306    probe_pending: Option<ProbePending>,
307    /// The locals in scope at the probe, with their types as inference had
308    /// them then; read with the function's final substitution.
309    probe_locals_pending: Option<Vec<(String, Ty)>>,
310    /// The language server's index (spec 3.4a §3), when asked for. Written
311    /// here and never read.
312    index: Option<Index>,
313    /// The type parameters of the item being checked, names and
314    /// declaration spans, the impl's first (plan decision 4). Set by
315    /// [`Checker::enter_type_params`] at each item entry point.
316    type_params: Vec<(String, Span)>,
317    /// The program's sources (spec 3.4b §4.7), when the caller gave them.
318    sources: Option<&'a FileDb>,
319    /// How each local the user wrote was bound, by its declaration span
320    /// (spec 3.4b §4.1; plan decision 5).
321    bindings: FxHashMap<Span, Binding>,
322    /// Set by `check_call` to its argument count just before it checks a
323    /// two-segment callee path; taken by `check_path` (plan decision 7).
324    qualified_callee: Option<usize>,
325}
326
327/// What the probe met, before its function's inference is finished.
328struct ProbePending {
329    receiver: Ty,
330    /// The function's bounds, for a type-parameter receiver.
331    bounds: Vec<Vec<DefId>>,
332    /// The module the access is in, for the visibility rule.
333    module: ModuleId,
334}
335
336/// Per-function checking state.
337struct FnCtx {
338    icx: InferCtx,
339    locals: Vec<hir::Local>,
340    scopes: Vec<FxHashMap<String, LocalId>>,
341    /// Generic parameter names of the enclosing function.
342    generics: FxHashMap<String, u32>,
343    /// Trait bounds per generic parameter, indexed like `generics` values.
344    param_bounds: Vec<Vec<DefId>>,
345    ret_ty: Ty,
346    /// Nesting depth of enclosing loops in the *current* function body.
347    /// Reset to 0 inside a closure body (a `break`/`continue` there cannot
348    /// target an outer loop).
349    loop_depth: usize,
350    /// Whether the function body currently being checked is `async`, which is
351    /// what makes `.await` legal. Set from `f.is_async` on entry and reset to
352    /// `false` inside a closure body — the same discipline `loop_depth` uses
353    /// above, and for the same reason: a closure is its own non-async
354    /// function even when written inside an `async fn`.
355    in_async: bool,
356    /// Closure / wrapper functions lifted out while checking this function's
357    /// body; finalized with this context's `icx` and then emitted.
358    pending_closures: Vec<hir::Function>,
359}
360
361impl FnCtx {
362    fn lookup(&self, name: &str) -> Option<LocalId> {
363        self.scopes
364            .iter()
365            .rev()
366            .find_map(|scope| scope.get(name).copied())
367    }
368
369    fn new_local(&mut self, name: String, ty: Ty, is_mut: bool, span: Span) -> LocalId {
370        let id = self.new_local_unscoped(name.clone(), ty, is_mut, span);
371        if name != "_" {
372            if let Some(scope) = self.scopes.last_mut() {
373                scope.insert(name, id);
374            }
375        }
376        id
377    }
378
379    /// Allocate a local that is *not* inserted into the name-resolution
380    /// scope — used for compiler-synthesized temporaries (e.g. a for-loop
381    /// counter) so they cannot be captured by, or collide with, source
382    /// identifiers.
383    fn new_local_unscoped(&mut self, name: String, ty: Ty, is_mut: bool, span: Span) -> LocalId {
384        let id = LocalId(self.locals.len() as u32);
385        self.locals.push(hir::Local {
386            name,
387            ty,
388            is_mut,
389            span,
390        });
391        id
392    }
393}
394
395impl<'a> Checker<'a> {
396    // === Collection ===
397
398    /// Report `E0403` for any generic parameter name that repeats within a
399    /// single declaration — `fn f<U, U>`, `record R<T, T>`, `impl<T, T> …`, a
400    /// trait method `fn m<U, U>`, etc. A silent duplicate leaves the type
401    /// checker's name→index map keeping only the last binding, so the earlier
402    /// parameter becomes a phantom the program can never name. `owner` names the
403    /// declaration kind for the message (e.g. "function", "type", "method").
404    /// Reject a user-written type parameter named `Self` (`E0076`), at every
405    /// place a generic parameter can be declared.
406    ///
407    /// `Self` already means something in two scopes, and in neither is it a
408    /// parameter the user declared: inside a trait body it is the trait's own
409    /// implicit type, bound at `Param(0)` by [`self_generic_scope`]; inside an
410    /// impl it is that impl's self type, read from [`Checker::impl_self`] by
411    /// [`Checker::convert_ty`]. But `Token::SelfUpper` parses to the plain
412    /// string `"Self"`, so the parser admits it as an ordinary identifier and
413    /// [`generic_scope`] would bind it like any other name — giving `Self` a
414    /// second meaning in the very scope where it already has one, and making
415    /// `Self::Item` mean two different things depending on whether the user
416    /// happened to declare a parameter by that name. `E0076` and not `E0900`:
417    /// this is a name that will never be legal, not a feature arriving later.
418    ///
419    /// One pass over every item rather than a call beside each
420    /// [`Checker::check_duplicate_generics`] — those five sites cover neither a
421    /// trait's own generics nor an impl method's, and a generic-carrying
422    /// construct added later would silently opt out of a per-site check. This
423    /// walks all six of the AST's `generics` fields.
424    ///
425    /// The rejected name is still bound by `generic_scope` afterwards, so the
426    /// rest of the declaration resolves and the user gets this one error rather
427    /// than a cascade of "cannot find type `Self`".
428    fn reject_self_type_params(&mut self) {
429        // Copy the `&'a File` so the item borrow is tied to `'a`, not to `self`.
430        let file: &'a ast::File = self.file;
431        for item in &file.items {
432            match &item.value {
433                ast::Item::Function(f) => self.reject_self_generic_name(&f.generics),
434                ast::Item::Record(r) => self.reject_self_generic_name(&r.generics),
435                ast::Item::Type(t) => self.reject_self_generic_name(&t.generics),
436                ast::Item::Trait(t) => {
437                    self.reject_self_generic_name(&t.generics);
438                    for ti in &t.items {
439                        match ti {
440                            TraitItem::Required(sig) => {
441                                self.reject_self_generic_name(&sig.generics)
442                            }
443                            TraitItem::Provided(f) => self.reject_self_generic_name(&f.generics),
444                            // An associated type declares no generics of its
445                            // own — Nova has no generic associated types.
446                            TraitItem::AssocType { .. } => {}
447                        }
448                    }
449                }
450                ast::Item::Impl(b) => {
451                    self.reject_self_generic_name(&b.generics);
452                    for f in &b.functions {
453                        self.reject_self_generic_name(&f.generics);
454                    }
455                }
456                ast::Item::Extern(b) => {
457                    for ei in &b.items {
458                        match ei {
459                            ExternItem::Fn(sig) => self.reject_self_generic_name(&sig.generics),
460                        }
461                    }
462                }
463                ast::Item::Const(_) | ast::Item::Import(_) | ast::Item::Module(_) => {}
464            }
465        }
466    }
467
468    /// The per-declaration half of [`Checker::reject_self_type_params`]. One
469    /// diagnostic per offending parameter, so a second is not hidden behind the
470    /// first.
471    fn reject_self_generic_name(&mut self, generics: &[ast::TypeParam]) {
472        for g in generics {
473            if g.name.value == "Self" {
474                self.error(
475                    "E0076",
476                    "a generic parameter may not be named `Self`: in a trait `Self` is the \
477                     implementing type, and in an impl it is that impl's self type",
478                    g.name.span,
479                );
480            }
481        }
482    }
483
484    fn check_duplicate_generics(&mut self, generics: &[ast::TypeParam], owner: &str) {
485        let mut seen: Vec<&str> = Vec::new();
486        for g in generics {
487            let name = g.name.value.as_str();
488            if seen.contains(&name) {
489                self.error(
490                    "E0403",
491                    format!(
492                        "the name `{name}` is already used for a generic parameter of this {owner}"
493                    ),
494                    g.name.span,
495                );
496            } else {
497                seen.push(name);
498            }
499        }
500    }
501
502    /// Reject a trait bound written on a sum type's *own* generic parameter
503    /// (`type Wrap<T: Hash> = …`).
504    ///
505    /// Record type parameters used to go through this too, but no longer do:
506    /// since the iterator-finishing plan's Task 1, a bound on a record's
507    /// parameter is instead resolved by `resolve_bounds` + `expand_bounds` in
508    /// `collect_records` and passed to `convert_ty`, as a **resolution scope**
509    /// for a projection in a field type (`f: fn(I::Item) -> U`) — not a
510    /// constraint. See the comment at that call site for why enforcement is
511    /// still out of scope. Sum types have no such use case yet (no variant
512    /// payload needs to name a projection on the sum's own parameter), so the
513    /// bound stays rejected here.
514    ///
515    /// Such a bound parses but nothing honours it: [`hir::SumType`] carries no
516    /// `bounds` field, and monomorphization discharges only *function* and
517    /// *impl* bounds (`nova-mir`'s `mono.rs` walks a worklist of function
518    /// instances). Enforcing it would need a notion of "sum type instantiation
519    /// site" that no pass has — a sum type's type arguments survive only
520    /// inside the enclosing expression's `Ty`, `ExprKind::MakeVariant` does not
521    /// record them, and MIR erases them to `Ptr` — so, exactly as for `trait B
522    /// where Self: A`, the construct is rejected loudly rather than left
523    /// reading as meaningful. Put the bound on an `impl` block instead, which
524    /// *is* enforced (this is what `std/collections`' `Map<K, V>` does).
525    ///
526    /// One diagnostic per bounded parameter, so a second offender is not hidden
527    /// behind the first. The bound names are deliberately **not** resolved: an
528    /// unknown trait here would stack an `E0001` cascade on top of the real
529    /// error. `owner` is the plural noun phrase for the message; the one
530    /// caller left passes "sum type parameters".
531    fn reject_type_param_bounds(&mut self, generics: &[ast::TypeParam], owner: &str) {
532        for g in generics {
533            if g.bounds.is_empty() {
534                continue;
535            }
536            // Cover `K: Hash + Eq`, not just `K`. Guarded rather than a bare
537            // `merge`, which debug-asserts that both spans share a file.
538            let mut span = g.name.span;
539            for b in &g.bounds {
540                if b.span.file == span.file {
541                    span = span.merge(b.span);
542                }
543            }
544            self.diagnostics.push(
545                Diagnostic::error(
546                    "E0900",
547                    format!("trait bounds on {owner} are not supported yet"),
548                )
549                .with_primary_label(
550                    span,
551                    format!("bound on the type parameter `{}`", g.name.value),
552                )
553                .with_note(
554                    "this bound is not enforced, so it is rejected rather than \
555                     silently ignored; write it on an `impl` block instead, \
556                     where it is enforced",
557                ),
558            );
559        }
560    }
561
562    fn collect_records(&mut self) {
563        for (i, def) in self.defs.defs().iter().enumerate() {
564            let DefKind::Record { item_index } = &def.kind else {
565                continue;
566            };
567            let ast::Item::Record(decl) = &self.file.items[*item_index].value else {
568                continue;
569            };
570            self.cur_module = self.defs.module_of(*item_index);
571            self.check_duplicate_generics(&decl.generics, "type");
572            let generics = generic_scope(&decl.generics);
573            self.enter_type_params(&[&decl.generics]);
574            for (fi, field) in decl.fields.iter().enumerate() {
575                self.note_decl(field.name.span, Target::Field(DefId(i as u32), fi as u32));
576            }
577            // A bound on a record's type parameter is a RESOLUTION SCOPE, not a
578            // constraint: it exists so a field type may name a projection on
579            // that parameter (`f: fn(I::Item) -> U`), which is what makes a
580            // lazy iterator adapter expressible. It is deliberately NOT checked
581            // at construction — `MakeRecord` carries no type arguments, and
582            // monomorphization visits only instances reachable from `main`, so
583            // enforcement would fire *sometimes*, which is worse than not at
584            // all (the Phase 2.2a assessment; ADR 0007 records it). Safety comes
585            // from the impl: `impl<I: Iterator, U> Iterator for MapIter<I, U>`
586            // requires the bound, so a `MapIter<Int, U>` simply has no
587            // `Iterator` impl and is inert.
588            let mut bounds = self.resolve_bounds(&decl.generics);
589            self.expand_bounds(&mut bounds);
590            let fields = decl
591                .fields
592                .iter()
593                .map(|f| hir::RecordField {
594                    name: f.name.value.clone(),
595                    ty: self.convert_ty(&f.ty, &generics, &bounds),
596                })
597                .collect();
598            self.records.push(hir::RecordType {
599                def_id: DefId(i as u32),
600                name: def.name.clone(),
601                generics: decl.generics.len() as u32,
602                fields,
603            });
604        }
605    }
606
607    /// Precompute the generic arity of every record and sum type from the AST,
608    /// before any type is converted. A record field or variant payload may
609    /// mention a type whose `RecordType`/`SumType` entry has not been built yet
610    /// (collection order, or the implicit prelude — `std/core` — registered
611    /// last), so arity must not be read from the incrementally-populated tables.
612    fn collect_type_arities(&mut self) {
613        let mut arities: Vec<(DefId, u32)> = Vec::new();
614        for (i, def) in self.defs.defs().iter().enumerate() {
615            let n = match &def.kind {
616                DefKind::Record { item_index } => match &self.file.items[*item_index].value {
617                    ast::Item::Record(r) => r.generics.len() as u32,
618                    _ => continue,
619                },
620                DefKind::Sum { item_index, .. } => match &self.file.items[*item_index].value {
621                    ast::Item::Type(t) => t.generics.len() as u32,
622                    _ => continue,
623                },
624                _ => continue,
625            };
626            arities.push((DefId(i as u32), n));
627        }
628        for (id, n) in arities {
629            self.type_arity.insert(id, n);
630        }
631    }
632
633    fn collect_sums(&mut self) {
634        for (i, def) in self.defs.defs().iter().enumerate() {
635            let DefKind::Sum { item_index, .. } = &def.kind else {
636                continue;
637            };
638            let ast::Item::Type(decl) = &self.file.items[*item_index].value else {
639                continue;
640            };
641            let TypeDef::Sum(variants) = &decl.def else {
642                continue;
643            };
644            self.cur_module = self.defs.module_of(*item_index);
645            self.check_duplicate_generics(&decl.generics, "type");
646            self.reject_type_param_bounds(&decl.generics, "sum type parameters");
647            let generics = generic_scope(&decl.generics);
648            self.enter_type_params(&[&decl.generics]);
649            let variants = variants
650                .iter()
651                .map(|v| hir::Variant {
652                    name: v.name.value.clone(),
653                    // No bounds: `reject_type_param_bounds` above rejects any
654                    // bound on a sum type's generic parameters outright.
655                    fields: v
656                        .fields
657                        .iter()
658                        .map(|t| self.convert_ty(t, &generics, &[]))
659                        .collect(),
660                })
661                .collect();
662            self.sums.push(hir::SumType {
663                def_id: DefId(i as u32),
664                name: def.name.clone(),
665                generics: decl.generics.len() as u32,
666                variants,
667            });
668        }
669    }
670
671    /// Resolve every trait's declared supertraits (`trait B: A`) into
672    /// [`Checker::supertraits`]. Runs before [`Checker::collect_traits`] *and*
673    /// [`Checker::collect_records`] (the first two callers that expand a bound
674    /// list) so the whole supertrait graph is known by the time the first
675    /// `expand_bounds` call is made, whatever order the traits are declared
676    /// in.
677    ///
678    /// Deduplicated per trait, mirroring [`Checker::resolve_bounds`]: a repeated
679    /// trait id reads as two providers of the same method and yields a false
680    /// `E0015` "ambiguous method call" at the call site.
681    fn collect_supertraits(&mut self) {
682        // Copy the `&'a File` reference so the item borrow outlives `&mut self`.
683        let file: &'a ast::File = self.file;
684        let traits: Vec<(DefId, usize)> = self
685            .defs
686            .defs()
687            .iter()
688            .enumerate()
689            .filter_map(|(i, d)| match d.kind {
690                DefKind::Trait { item_index } => Some((DefId(i as u32), item_index)),
691                _ => None,
692            })
693            .collect();
694        for (trait_id, item_index) in traits {
695            let Some(item) = file.items.get(item_index) else {
696                continue;
697            };
698            let ast::Item::Trait(decl) = &item.value else {
699                continue;
700            };
701            self.cur_module = self.defs.module_of(item_index);
702            let mut ids: Vec<DefId> = Vec::new();
703            for path in &decl.supertraits {
704                let name = path
705                    .value
706                    .segments
707                    .last()
708                    .map(|s| s.value.as_str())
709                    .unwrap_or("");
710                match self.defs.resolve_trait(self.cur_module, name) {
711                    Some(id) => {
712                        if let Some(seg) = path.value.segments.last() {
713                            self.note_use(seg.span, Target::Def(id));
714                        }
715                        if !ids.contains(&id) {
716                            ids.push(id);
717                        }
718                    }
719                    None => {
720                        self.error("E0001", format!("cannot find trait `{name}`"), path.span);
721                        if let Some(seg) = path.value.segments.last() {
722                            self.name_fixes(seg.clone(), Need::Trait, Vec::new());
723                        }
724                    }
725                }
726            }
727            self.supertraits.insert(trait_id, ids);
728        }
729    }
730
731    /// Expand a bound list with the transitive supertraits of each trait, so a
732    /// bound `T: B` also provides `B`'s supertrait `A`. Deduplicated, because a
733    /// repeated trait id would read as two method providers (a false `E0015`) —
734    /// which a diamond (`C: A + B` with `A: X` and `B: X`) reaches easily.
735    ///
736    /// The walk appends to `out` and never revisits an id already in it, so it
737    /// terminates even on a cyclic `trait A: B` / `trait B: A` declaration. An
738    /// infinite loop here would hang the compiler, which is far worse than the
739    /// missing diagnostic for the cycle itself.
740    fn with_supertraits(&self, bounds: &[DefId]) -> Vec<DefId> {
741        let mut out: Vec<DefId> = Vec::with_capacity(bounds.len());
742        for &id in bounds {
743            if !out.contains(&id) {
744                out.push(id);
745            }
746        }
747        // Breadth-first over `out` itself: the declared bounds keep their source
748        // order and each trait's supertraits follow the traits already queued.
749        let mut i = 0;
750        while let Some(&id) = out.get(i) {
751            i += 1;
752            let Some(supers) = self.supertraits.get(&id) else {
753                continue;
754            };
755            for &s in supers {
756                if !out.contains(&s) {
757                    out.push(s);
758                }
759            }
760        }
761        out
762    }
763
764    /// Apply [`Checker::with_supertraits`] to every generic parameter's bound
765    /// list, in place. Called once per declaration *after* all of its bound
766    /// sources (inline bounds and any `where` clause) have been folded in, so the
767    /// expansion sees the complete set. Only the contents of each entry change —
768    /// never its index, which `FnSig.bounds`, `ImplInfo.bounds` and
769    /// `TraitMethod.bounds` all address positionally.
770    fn expand_bounds(&self, bounds: &mut [Vec<DefId>]) {
771        for slot in bounds.iter_mut() {
772            *slot = self.with_supertraits(slot);
773        }
774    }
775
776    /// Collect trait declarations and their method signatures. `Self` is
777    /// encoded as `Ty::Param(0)` in stored method signatures.
778    fn collect_traits(&mut self) {
779        // Map (trait item_index, method_index) → default-method DefId.
780        let default_defs: FxHashMap<(usize, usize), DefId> = self
781            .defs
782            .methods()
783            .filter(|(_, _, _, owner)| *owner == MethodOwner::TraitDefault)
784            .map(|(id, ii, mi, _)| ((ii, mi), id))
785            .collect();
786
787        for (i, def) in self.defs.defs().iter().enumerate() {
788            let DefKind::Trait { item_index } = def.kind else {
789                continue;
790            };
791            let ast::Item::Trait(decl) = &self.file.items[item_index].value else {
792                continue;
793            };
794            self.cur_module = self.defs.module_of(item_index);
795            // Needed before the items loop below: an associated type's `Def`
796            // (`DefKind::AssocType { trait_def }`) points back to this trait's
797            // own id, so matching them up requires knowing it up front rather
798            // than only after `methods` is built.
799            let def_id = DefId(i as u32);
800            if !decl.generics.is_empty() {
801                self.unsupported(decl.name.span, "generic traits");
802            }
803            // `trait B where Self: A` is a second spelling of a supertrait
804            // requirement, distinct from the `trait B: A` shorthand that
805            // `collect_supertraits` resolves. Routing it into the same graph
806            // is a feature addition beyond parsing it, so — like `where`
807            // clauses on trait methods just below — it is rejected outright
808            // rather than silently discarded.
809            if !decl.where_clause.is_empty() {
810                self.unsupported(decl.name.span, "`where` clauses on trait declarations");
811            }
812            let self_scope = self_generic_scope();
813            let mut methods = Vec::new();
814            // The resolver pushes one `Def` per `TraitItem::AssocType` for
815            // this trait, in the same order it walked `t.items` — the same
816            // order `decl.items` is walked just below. Filtering preserves
817            // that relative order, so draining this front-to-back as each
818            // `AssocType` item is encountered lines each one up with its
819            // `DefId` without needing a second, item-indexed lookup table.
820            let mut assoc_type_ids =
821                self.defs
822                    .defs()
823                    .iter()
824                    .enumerate()
825                    .filter_map(|(di, d)| match d.kind {
826                        DefKind::AssocType { trait_def } if trait_def == def_id => {
827                            Some(DefId(di as u32))
828                        }
829                        _ => None,
830                    });
831            let mut assoc_types = Vec::new();
832            for (mi, item) in decl.items.iter().enumerate() {
833                let (name, generics, where_clause, params, ret, is_default, is_async) = match item {
834                    TraitItem::Required(sig) => (
835                        &sig.name,
836                        &sig.generics,
837                        &sig.where_clause,
838                        &sig.params,
839                        &sig.return_ty,
840                        false,
841                        sig.is_async,
842                    ),
843                    TraitItem::Provided(f) => (
844                        &f.name,
845                        &f.generics,
846                        &f.where_clause,
847                        &f.params,
848                        &f.return_ty,
849                        true,
850                        f.is_async,
851                    ),
852                    TraitItem::AssocType { name, bounds, .. } => {
853                        if !bounds.is_empty() {
854                            self.unsupported(name.span, "trait bounds on an associated type");
855                        }
856                        // Drained unconditionally, *before* the duplicate check
857                        // can `continue`: this iterator is positional (see its
858                        // comment above), so skipping a `next()` would hand the
859                        // *next* associated type this one's `DefId` and every
860                        // later one would be off by one. The whole point of the
861                        // rejection is that a duplicate name is ambiguous, so
862                        // misaligning the survivors while reporting it would be
863                        // the same bug one seat over.
864                        let assoc_def_id = assoc_type_ids.next();
865                        // Two declarations of one name leave which `DefId` a
866                        // projection resolves to up to `find_assoc_type`'s scan
867                        // order — it takes the first, so the second is silently
868                        // dead: nothing binds it, nothing can name it, and
869                        // conformance matches bindings by *name* so the missing
870                        // binding is not reported either. Keep the first and
871                        // reject the rest, the same `E0403` a duplicate generic
872                        // parameter and a duplicate associated-type *binding* in
873                        // an impl already get.
874                        if assoc_types.iter().any(|(n, _)| n == &name.value) {
875                            self.error(
876                                "E0403",
877                                format!(
878                                    "the name `{}` is already used for an associated type of \
879                                     this trait",
880                                    name.value
881                                ),
882                                name.span,
883                            );
884                            continue;
885                        }
886                        if let Some(assoc_def_id) = assoc_def_id {
887                            assoc_types.push((name.value.clone(), assoc_def_id));
888                        }
889                        continue;
890                    }
891                };
892                // The same hole as the duplicate associated type above, wearing
893                // different clothes, and the plan's Step 2 asked for it to be
894                // checked rather than assumed: `trait It { fn g(self) -> Int\n
895                // fn g(self) -> Bool }` was accepted, and the second declaration
896                // is dead in exactly the same way. `trait_method_index` and
897                // `check_impl_method_signatures` both take the *first* match by
898                // name, so an impl conforms to signature one while signature two
899                // constrains nothing — a trait can promise two contradictory
900                // things and the impl is checked against whichever is written
901                // first.
902                //
903                // On the name alone, not on `(name, has_self)`. The two lookups
904                // that consume this list partition it by receiver
905                // (`trait_method_index` wants `has_self`,
906                // `trait_assoc_fn_index` wants `!has_self`), so allowing
907                // `fn g(self)` beside `fn g()` would make `g` mean two different
908                // things depending on the *call syntax* — which is the very
909                // ambiguity being rejected, not an exception to it.
910                //
911                // Reported before the signature work below rather than after, so
912                // a rejected duplicate does not also emit its own `E0900`s and
913                // `E0001`s: one mistake, one diagnostic.
914                if methods
915                    .iter()
916                    .any(|m: &hir::TraitMethod| m.name == name.value)
917                {
918                    self.error(
919                        "E0403",
920                        format!(
921                            "the name `{}` is already used for a method of this trait",
922                            name.value
923                        ),
924                        name.span,
925                    );
926                    continue;
927                }
928                // Trait methods stay `E0900`-rejected even though free
929                // functions and impl/inherent methods no longer are (Phase
930                // 2.3a Task 2): trait async needs associated-type futures,
931                // out of scope here. Checked on the *table* entry, not only
932                // in the default-body pass below, so a declaration-only
933                // (`Required`) trait method — which the default-body pass
934                // never visits — is covered too.
935                if is_async {
936                    self.unsupported(name.span, "async methods");
937                }
938                if !where_clause.is_empty() {
939                    self.error(
940                        "E0900",
941                        "`where` clauses on trait methods are not supported yet",
942                        name.span,
943                    );
944                }
945                self.enter_type_params(&[generics.as_slice()]);
946                self.check_duplicate_generics(generics, "method");
947                // A generic trait method (`fn map<U>(self, …)`) binds `Self` at
948                // Param(0) and its own generic parameters at Param(1..).
949                let mut m_scope = self_scope.clone();
950                for (j, g) in generics.iter().enumerate() {
951                    m_scope.insert(g.name.value.clone(), 1 + j as u32);
952                }
953                // `Self` (at index 0, matching `m_scope`) is bounded by the
954                // enclosing trait plus its supertraits — needed so this
955                // method's OWN signature can resolve `Self::Item` (e.g.
956                // `Iterator::next`'s `Option<Self::Item>`) before this
957                // trait's `hir::TraitDef` even exists (see `find_assoc_type`).
958                // Computed and expanded *before* `method_sig_parts` so
959                // `convert_ty` can see it; `TraitMethod.bounds` stays indexed
960                // 0..generics with no Self slot, so it is sliced off below
961                // rather than stored as-is.
962                let mut sig_bounds = vec![vec![def_id]];
963                sig_bounds.extend(self.resolve_bounds(generics));
964                // A `where` clause on a trait method is rejected above, so the
965                // inline bounds are the complete set.
966                self.expand_bounds(&mut sig_bounds);
967                let (m_params, m_ret) = self.method_sig_parts(params, ret, &m_scope, &sig_bounds);
968                let m_bounds = sig_bounds[1..].to_vec();
969                let default_def = if is_default {
970                    default_defs.get(&(item_index, mi)).copied()
971                } else {
972                    None
973                };
974                // Spec 3.4a §3.2: a required and a provided method alike are
975                // declared as the trait's method, at the index `methods`
976                // gives it, which is `MethodRes::Trait`'s.
977                self.note_decl(name.span, Target::TraitMethod(def_id, methods.len() as u32));
978                if !is_default {
979                    self.note_params(params);
980                }
981                methods.push(hir::TraitMethod {
982                    name: name.value.clone(),
983                    params: m_params,
984                    ret: m_ret,
985                    // `any`, not `first`: `method_sig_parts` strips *every*
986                    // parameter named `self`, and the parser accepts a misplaced
987                    // receiver (`fn m(x: Int, self)`), so the same predicate must
988                    // decide both or `params` and `has_self` disagree. Mirrors
989                    // `collect_impls`.
990                    has_self: params.iter().any(|p| p.name.value == "self"),
991                    // The same predicate `collect_impls` uses for the impl side
992                    // of this flag, for the same reason `has_self` uses `any`:
993                    // `method_sig_parts` strips a `self` at any position, so a
994                    // `params[0]`-shaped predicate would classify a misplaced
995                    // receiver as a non-mutator while the signature machinery
996                    // still treated it as a receiver. Both sides of the
997                    // conformance comparison below must be decided the same way
998                    // or the comparison itself is the bug.
999                    mut_self: params.iter().any(|p| p.name.value == "self" && p.is_mut),
1000                    generics: generics.len() as u32,
1001                    bounds: m_bounds,
1002                    default_def,
1003                });
1004            }
1005            self.traits.push(hir::TraitDef {
1006                def_id,
1007                name: def.name.clone(),
1008                supertraits: self.supertraits.get(&def_id).cloned().unwrap_or_default(),
1009                methods,
1010                assoc_types,
1011            });
1012        }
1013
1014        // Signatures for default-method function bodies: generic over Self
1015        // (`Param(0)`), bounded by the enclosing trait.
1016        let trait_items: Vec<(DefId, usize)> = self
1017            .defs
1018            .defs()
1019            .iter()
1020            .enumerate()
1021            .filter_map(|(i, d)| match d.kind {
1022                DefKind::Trait { item_index } => Some((DefId(i as u32), item_index)),
1023                _ => None,
1024            })
1025            .collect();
1026        for (trait_id, item_index) in trait_items {
1027            let ast::Item::Trait(decl) = &self.file.items[item_index].value else {
1028                continue;
1029            };
1030            self.cur_module = self.defs.module_of(item_index);
1031            let self_scope = self_generic_scope();
1032            for (mi, item) in decl.items.iter().enumerate() {
1033                let TraitItem::Provided(f) = item else {
1034                    continue;
1035                };
1036                let Some(def_id) = default_defs.get(&(item_index, mi)).copied() else {
1037                    continue;
1038                };
1039                // `async` and `where` clauses on trait methods are both rejected
1040                // in the table loop above; don't build a body signature for one.
1041                if f.is_async {
1042                    continue;
1043                }
1044                if !f.where_clause.is_empty() {
1045                    continue;
1046                }
1047                self.enter_type_params(&[&f.generics]);
1048                // `Self` at Param(0), the method's own generics at Param(1..).
1049                let mut scope = self_scope.clone();
1050                for (j, g) in f.generics.iter().enumerate() {
1051                    scope.insert(g.name.value.clone(), 1 + j as u32);
1052                }
1053                // One generic for `Self` (bounded by the trait) plus the method's
1054                // own generics, at the same flat Param indices. Computed *before*
1055                // `method_sig_parts` (reordered from its previous position after
1056                // it) so `convert_ty` can resolve a `Self::Item` this default
1057                // body's own signature might mention.
1058                let mut bounds = vec![vec![trait_id]];
1059                bounds.extend(self.resolve_bounds(&f.generics));
1060                // `Self`'s bound is the enclosing trait, so expanding it is what
1061                // lets a `trait B: A` default body call `self.a()` — and lets its
1062                // signature resolve `Self::Item` when `Item` is declared by `A`.
1063                self.expand_bounds(&mut bounds);
1064                let (mut params, ret) =
1065                    self.method_sig_parts(&f.params, &f.return_ty, &scope, &bounds);
1066                // Prepend the `self` receiver typed as `Self` (`Param(0)`) — but
1067                // only for a method that declares one. A default-bodied
1068                // associated function (`fn zero() -> Self { … }`) has no
1069                // receiver, and prepending one would desynchronise `sig.params`
1070                // from the AST parameter list that `check_fn_body` zips against
1071                // it. Mirrors the same conditional in `collect_impls`.
1072                if f.params.iter().any(|p| p.name.value == "self") {
1073                    params.insert(0, Ty::Param(0));
1074                }
1075                self.sigs.insert(
1076                    def_id,
1077                    FnSig {
1078                        generics: 1 + f.generics.len() as u32,
1079                        bounds,
1080                        params,
1081                        ret,
1082                    },
1083                );
1084                self.method_locs.insert(
1085                    def_id,
1086                    MethodLoc {
1087                        item_index,
1088                        method_index: mi,
1089                        owner: MethodOwner::TraitDefault,
1090                    },
1091                );
1092            }
1093        }
1094    }
1095
1096    /// Collect impl blocks into the impl table and method signatures.
1097    fn collect_impls(&mut self) {
1098        let impl_methods: FxHashMap<(usize, usize), DefId> = self
1099            .defs
1100            .methods()
1101            .filter(|(_, _, _, owner)| *owner == MethodOwner::Impl)
1102            .map(|(id, ii, mi, _)| ((ii, mi), id))
1103            .collect();
1104
1105        // Source span of each collected impl (aligned with `self.impls`), for
1106        // the post-collection coherence check.
1107        let mut impl_spans: Vec<Span> = Vec::new();
1108
1109        for (item_index, item) in self.file.items.iter().enumerate() {
1110            let ast::Item::Impl(block) = &item.value else {
1111                continue;
1112            };
1113            self.cur_module = self.defs.module_of(item_index);
1114            // Cleared up front, not only on the way out: several paths below
1115            // `continue` past the end of the loop body, and a stale `Self` from
1116            // the previous impl would then resolve a `Self::Item` written in
1117            // this one against the wrong type.
1118            self.impl_self = None;
1119            self.check_duplicate_generics(&block.generics, "impl");
1120            // The impl's generic parameters (`impl<T> …`) are in scope in the
1121            // self type and every method signature/body.
1122            let impl_generics = generic_scope(&block.generics);
1123            self.enter_type_params(&[&block.generics]);
1124            let mut impl_bounds = self.resolve_bounds(&block.generics);
1125            self.apply_where(&mut impl_bounds, &block.where_clause, &impl_generics);
1126            self.expand_bounds(&mut impl_bounds);
1127            let self_ty = self.convert_ty(&block.ty, &impl_generics, &impl_bounds);
1128            // A projection anywhere in the self type is rejected outright, and
1129            // *before* the head check below so the message names the real
1130            // problem: a bare `impl<T: It> Tr for T::Item` otherwise reports
1131            // "impl blocks are only supported on named types", which is
1132            // misleading because `T::Item` may well resolve to a named type.
1133            //
1134            // Two independent defects compound here, which is why this is not a
1135            // position to leave accepted-and-broken. Measured on `25db453`, both
1136            // on the same five-line program:
1137            //
1138            //  * **The impl can never be selected.** `Ty::match_pattern`
1139            //    recovers an impl's type arguments by matching its self type
1140            //    structurally against a ground type, and it cannot invert
1141            //    `T::Item` to find `T`. So the impl is dead code.
1142            //  * **It is invisible to coherence.** `hir::self_types_overlap`'s
1143            //    helpers do not understand `Assoc`, so
1144            //    `impl<T: It> Tr for W<T::Item>` and `impl Tr for W<Int>` do not
1145            //    conflict — while the control, `impl<T> Tr for W<T>` in place of
1146            //    the first, correctly reports `E0074`. Any `T` with
1147            //    `Item = Int` makes both apply to `W<Int>`.
1148            //
1149            // Dead code that also silently defeats overlap checking is the worst
1150            // of the two, which is why Rust forbids the position outright.
1151            //
1152            // `E0900` and not a new code, deliberately. The construct is not
1153            // meaningless — it becomes implementable the moment impl selection
1154            // can invert a projection — and every other "the parser accepts it,
1155            // no machinery implements it" rejection in this checker is `E0900`
1156            // (generic traits, `where` on trait methods, bounds on an associated
1157            // type, module-qualified type paths). A dedicated code would assert a
1158            // *permanent* illegality this project has not decided, and the design
1159            // doc has already been falsified twice on exactly this subject.
1160            if self_ty.has_assoc() {
1161                let tystr = display_ty(&self_ty, self.defs);
1162                self.diagnostics.push(
1163                    Diagnostic::error(
1164                        "E0900",
1165                        format!(
1166                            "an associated-type projection in an impl's self type \
1167                             (`{tystr}`) is not supported yet"
1168                        ),
1169                    )
1170                    .with_primary_label(block.ty.span, "not supported yet")
1171                    .with_note(
1172                        "an impl's type arguments are recovered by matching its self type \
1173                         against a concrete one, which cannot invert a projection, so this \
1174                         impl could never be selected — and overlap checking cannot see \
1175                         through the projection either, so it would not conflict with an \
1176                         impl that does apply"
1177                            .to_string(),
1178                    )
1179                    .with_note(
1180                        "the Phase 1 MVP compiler supports a subset of Nova; \
1181                         this feature arrives in a later milestone"
1182                            .to_string(),
1183                    ),
1184                );
1185                continue;
1186            }
1187            let Some(self_head) = self_ty.head() else {
1188                self.error(
1189                    "E0010",
1190                    "impl blocks are only supported on named types",
1191                    block.ty.span,
1192                );
1193                continue;
1194            };
1195            // Every impl generic parameter must appear in the self type, or its
1196            // type argument could never be recovered at a call site (and, for an
1197            // inherent method that ignores it, the parameter would leak an
1198            // unconstrained inference variable). Cf. Rust's E0207.
1199            let mut has_unused = false;
1200            for (i, g) in block.generics.iter().enumerate() {
1201                if !self_ty.mentions_param(i as u32) {
1202                    has_unused = true;
1203                    let tystr = display_ty(&self_ty, self.defs);
1204                    self.error(
1205                        "E0073",
1206                        format!(
1207                            "impl type parameter `{}` is not used in the self type `{tystr}`",
1208                            g.name.value
1209                        ),
1210                        g.name.span,
1211                    );
1212                }
1213            }
1214            if has_unused {
1215                continue;
1216            }
1217            let trait_id = match &block.trait_ {
1218                Some(tr) => {
1219                    let name = tr
1220                        .value
1221                        .segments
1222                        .last()
1223                        .map(|s| s.value.as_str())
1224                        .unwrap_or("");
1225                    match self.defs.resolve_trait(self.cur_module, name) {
1226                        Some(id) => {
1227                            if let Some(seg) = tr.value.segments.last() {
1228                                self.note_use(seg.span, Target::Def(id));
1229                            }
1230                            Some(id)
1231                        }
1232                        None => {
1233                            self.error("E0001", format!("cannot find trait `{name}`"), tr.span);
1234                            if let Some(seg) = tr.value.segments.last() {
1235                                self.name_fixes(seg.clone(), Need::Trait, Vec::new());
1236                            }
1237                            continue;
1238                        }
1239                    }
1240                }
1241                None => None,
1242            };
1243
1244            // From here on, `Self` inside this impl means this impl's self type.
1245            // Recorded by item index too, so `check_method` can restore it when
1246            // it compiles a body in a later pass.
1247            let imp_self = ImplSelf {
1248                ty: self_ty.clone(),
1249                trait_id,
1250            };
1251            self.impl_selves.insert(item_index, imp_self.clone());
1252            self.impl_self = Some(imp_self);
1253
1254            let impl_count = block.generics.len() as u32;
1255            // Associated-type bindings (`type Item = T`), keyed by the
1256            // associated type's own `DefId` — the same key
1257            // `TraitDef::assoc_types` uses, so a projection resolved to
1258            // `Ty::Assoc { assoc, .. }` looks its binding up directly.
1259            //
1260            // The bound type is converted in the impl's own generic scope, so
1261            // it may contain the impl's `Param(k)`; normalization substitutes
1262            // the impl's type arguments before using it. Converting it here
1263            // rather than lazily also means `type Item = Nope` reports its own
1264            // "cannot find type" once, at the binding, and not once per use.
1265            //
1266            // Whether the *set* of bindings matches the trait's is
1267            // `check_impl_conformance`'s job, beside the method set. All that
1268            // happens here is resolving each name to its `DefId`: a name the
1269            // trait does not declare has no `DefId` to be keyed under, so it is
1270            // dropped from this list and reported there.
1271            let mut assoc_bindings: Vec<(DefId, Ty)> = Vec::new();
1272            // Span of each kept binding's name, aligned with `assoc_bindings`,
1273            // so the cycle check below can point at the offending binding rather
1274            // than at the impl header.
1275            let mut assoc_spans: Vec<Span> = Vec::new();
1276
1277            // Associated constants are parsed but not implemented. `ImplBlock`
1278            // carries them in a third parallel vector beside `functions` and
1279            // `assoc_types`, and until this check existed **nothing in the
1280            // workspace read it** — so `impl K { const LIMIT: Int = 99 }`
1281            // compiled, ran, and silently dropped the constant, after which
1282            // `K::LIMIT` reported `no variant \`LIMIT\` on type \`K\``: a
1283            // message about a construct the user did not write.
1284            //
1285            // Refusing it is the honest state. Accepting a declaration and
1286            // discarding it is strictly worse than rejecting it, because the
1287            // program looks correct and the constant simply is not there. This
1288            // is deliberately *not* wired into the top-level `const` path
1289            // (which compiles a constant as a zero-argument function): doing
1290            // that is a real feature with its own questions — visibility,
1291            // whether `Self` may appear in the type, cycle detection across
1292            // impls — and it belongs in an increment that answers them.
1293            for c in &block.consts {
1294                // `unsupported` appends "are not supported yet", so this reads
1295                // as a plural phrase.
1296                self.unsupported(
1297                    c.name.span,
1298                    &format!("associated constants like `{}`", c.name.value),
1299                );
1300            }
1301            for b in &block.assoc_types {
1302                let ty = self.convert_ty(&b.ty, &impl_generics, &impl_bounds);
1303                let resolved = trait_id.and_then(|tid| self.find_assoc_type(tid, &b.name.value));
1304                let Some(assoc) = resolved else {
1305                    // An inherent impl has no trait, so it can never bind an
1306                    // associated type — and `check_impl_conformance` does not
1307                    // run for it, so this is the only place that can say so.
1308                    if trait_id.is_none() {
1309                        self.error(
1310                            "E0071",
1311                            format!(
1312                                "associated type `{}` cannot be bound by an inherent impl, \
1313                                 which implements no trait",
1314                                b.name.value
1315                            ),
1316                            b.name.span,
1317                        );
1318                    }
1319                    continue;
1320                };
1321                self.note_use(b.name.span, Target::Def(assoc));
1322                // Two bindings for one associated type would leave which one
1323                // normalization picks up to list order. Keep the first and
1324                // reject the rest, the way a duplicate generic parameter is
1325                // handled (`check_duplicate_generics`, same code).
1326                if assoc_bindings.iter().any(|(d, _)| *d == assoc) {
1327                    self.error(
1328                        "E0403",
1329                        format!(
1330                            "the associated type `{}` is already bound by this impl",
1331                            b.name.value
1332                        ),
1333                        b.name.span,
1334                    );
1335                    continue;
1336                }
1337                assoc_bindings.push((assoc, ty));
1338                assoc_spans.push(b.name.span);
1339            }
1340            self.check_assoc_binding_cycles(&mut assoc_bindings, &assoc_spans, &self_ty);
1341            let mut methods = Vec::new();
1342            for (mi, f) in block.functions.iter().enumerate() {
1343                let Some(def_id) = impl_methods.get(&(item_index, mi)).copied() else {
1344                    continue;
1345                };
1346                self.enter_type_params(&[&block.generics, &f.generics]);
1347                // The method's generic scope: the impl's parameters (`impl<T> …`)
1348                // at indices [0, impl_count), then the method's own parameters
1349                // (`fn map<U>`) at [impl_count, …). A single flat `type_args`
1350                // vector — impl args recovered from the receiver, method args
1351                // inferred from the call — then drives substitution, bound
1352                // checking, and monomorphization uniformly.
1353                let mut scope = impl_generics.clone();
1354                let mut bounds = impl_bounds.clone();
1355                let mut seen: Vec<&str> = Vec::new();
1356                for (j, g) in f.generics.iter().enumerate() {
1357                    let gname = g.name.value.as_str();
1358                    if impl_generics.contains_key(gname) {
1359                        self.error(
1360                            "E0403",
1361                            format!("the name `{gname}` shadows the impl's generic parameter"),
1362                            g.name.span,
1363                        );
1364                    } else if seen.contains(&gname) {
1365                        self.error(
1366                            "E0403",
1367                            format!(
1368                                "the name `{gname}` is already used for a generic \
1369                                 parameter of this method"
1370                            ),
1371                            g.name.span,
1372                        );
1373                    }
1374                    seen.push(gname);
1375                    scope.insert(g.name.value.clone(), impl_count + j as u32);
1376                }
1377                bounds.extend(self.resolve_bounds(&f.generics));
1378                let method_generic_count = f.generics.len() as u32;
1379                // A method's `where` clause may constrain the impl's or its own
1380                // type parameters; fold it into the combined bounds.
1381                self.apply_where(&mut bounds, &f.where_clause, &scope);
1382                // The leading `impl_count` entries are already expanded (they are
1383                // a clone of `impl_bounds`); re-expanding them is a no-op.
1384                self.expand_bounds(&mut bounds);
1385                // Non-self params + ret in terms of the self type, resolving the
1386                // impl's and this method's generic parameters.
1387                let (mut params, ret) =
1388                    self.method_sig_parts(&f.params, &f.return_ty, &scope, &bounds);
1389                // An async method's declared return type is its future's
1390                // OUTPUT — the spec's own `pub async fn join(self) -> T`
1391                // reads `T`, not `Future<T>` — so the signature callers and
1392                // `check_impl_method_signatures` compare against is
1393                // `Future<ret>`, the same wrapping `collect_signatures` does
1394                // for a free async fn. `check_fn_body` unwraps it back to
1395                // `ret` to check the method's body.
1396                let ret = if f.is_async {
1397                    Ty::Future(Box::new(ret))
1398                } else {
1399                    ret
1400                };
1401                // `self` is stripped by `method_sig_parts` and re-inserted here as
1402                // the receiver — but only for methods that actually declare one.
1403                // For an associated function (`fn new() -> P`) inserting it would
1404                // shift every parameter by one against `f.params`, which
1405                // `check_fn_body` zips positionally. The predicate mirrors
1406                // `method_sig_parts`, which strips *every* param named `self`, so
1407                // `sig.params.len()` equals `f.params.len()` for a method with
1408                // zero or exactly one `self` parameter — the only shapes this
1409                // insert re-aligns. Nothing enforces that shape: a duplicated
1410                // receiver (`impl P { fn g(self, self) -> Int { 1 } }`) still
1411                // checks `ok`, because `method_sig_parts` strips both `self`s but
1412                // only one is re-inserted here, leaving `sig.params` one short of
1413                // `f.params` and desynchronising the positional zip again —
1414                // calling `p.g()` ICEs in codegen. Pre-existing, not fixed here.
1415                let has_self = f.params.iter().any(|p| p.name.value == "self");
1416                if has_self {
1417                    params.insert(0, self_ty.clone());
1418                } else {
1419                    self.selfless.insert(def_id);
1420                }
1421                // `mut self` makes the method a mutator, which its callers must
1422                // opt into with a mutable receiver (ADR 0005). `any`, for the
1423                // same reason `has_self` uses it: `method_sig_parts` strips a
1424                // `self` at *any* position, so both predicates must scan the
1425                // whole list or they can disagree about the same parameter.
1426                if f.params.iter().any(|p| p.name.value == "self" && p.is_mut) {
1427                    self.mut_self.insert(def_id);
1428                }
1429                self.sigs.insert(
1430                    def_id,
1431                    FnSig {
1432                        generics: impl_count + method_generic_count,
1433                        bounds,
1434                        params,
1435                        ret,
1436                    },
1437                );
1438                self.method_locs.insert(
1439                    def_id,
1440                    MethodLoc {
1441                        item_index,
1442                        method_index: mi,
1443                        owner: MethodOwner::Impl,
1444                    },
1445                );
1446                methods.push((f.name.value.clone(), def_id));
1447            }
1448
1449            // Conformance: a trait impl must define exactly the trait's
1450            // methods that lack defaults, and nothing foreign. Only the *set* of
1451            // items is decided here — each method's signature is compared by
1452            // `check_impl_method_signatures` after the loop, because that
1453            // comparison normalizes associated-type projections and so needs the
1454            // finished impl table.
1455            if let Some(tid) = trait_id {
1456                self.check_impl_conformance(tid, &methods, &block.assoc_types, block.ty.span);
1457            }
1458
1459            self.impls.push(hir::ImplInfo {
1460                trait_id,
1461                self_head,
1462                self_ty,
1463                generics: block.generics.len() as u32,
1464                bounds: impl_bounds,
1465                methods,
1466                assoc_bindings,
1467            });
1468            impl_spans.push(block.ty.span);
1469        }
1470        // No impl is in scope for `collect_signatures` / `collect_externs`,
1471        // which run next.
1472        self.impl_self = None;
1473
1474        // Ordered first among the three post-collection passes so that an
1475        // `E0072` about a method signature still precedes `check_supertrait_impls`'s
1476        // own `E0072`, as it did when the signature check ran inside the loop.
1477        self.check_impl_method_signatures(&impl_spans);
1478        self.check_impl_coherence(&impl_spans);
1479        self.check_supertrait_impls(&impl_spans);
1480    }
1481
1482    /// Report `E0077` for an associated type this impl binds in terms of
1483    /// itself, and poison the offending binding to `Ty::Error`.
1484    ///
1485    /// One binding may legitimately name another of the same impl's associated
1486    /// types — `type A = Self::B` with `type B = Int` is legal, and it is the
1487    /// reason [`hir::normalize_ty`] re-normalizes its own result. That is
1488    /// precisely the walk that never terminates on `type Item = Self::Item`, or
1489    /// on a mutual `A = Self::B` / `B = Self::A`, so the cycle has to be
1490    /// rejected where it is created. `normalize_ty`'s step guard is the
1491    /// backstop for a cycle built by some path this does not see, not a
1492    /// substitute for this check: a hang is worse than any diagnostic, and a
1493    /// compiler-limit message is the wrong thing to show a user who wrote a
1494    /// two-line cycle.
1495    ///
1496    /// **Poisoning to `Ty::Error` rather than dropping the binding** keeps the
1497    /// bound *set* complete, so `check_impl_conformance` does not add a spurious
1498    /// `E0070` for a binding that is present but bad. The cycle is already
1499    /// reported, so this is suppression of a *second* error, never silence.
1500    ///
1501    /// **Poisoning is not self-suppressing, and this comment used to claim it
1502    /// was.** It said `Ty::Error` "unifies with anything, so no use of the
1503    /// projection cascades either". That is true of `InferCtx::unify`, where
1504    /// `Ty::Error` absorbs — and false of every consumer that compares two `Ty`s
1505    /// with the derived `PartialEq`, where an `Error` on one side *forces* a
1506    /// mismatch. `check_impl_method_signatures` is such a consumer, and it
1507    /// reported `method `get` returns `Int` but trait `It` declares `{error}``
1508    /// after this very check had already explained the cycle. It now skips a
1509    /// comparison whose either side [`hir::Ty::has_error`]; the suppression has
1510    /// to live at each `PartialEq` consumer, because poisoning cannot do it from
1511    /// here.
1512    fn check_assoc_binding_cycles(
1513        &mut self,
1514        bindings: &mut [(DefId, Ty)],
1515        spans: &[Span],
1516        self_ty: &Ty,
1517    ) {
1518        // Edges: this impl's associated type `d` refers to `e` when its bound
1519        // type projects onto `Self::e` anywhere inside it.
1520        let mut edges: FxHashMap<DefId, Vec<DefId>> = FxHashMap::default();
1521        for (d, ty) in bindings.iter() {
1522            let mut refs = Vec::new();
1523            collect_self_projections(ty, self_ty, &mut refs);
1524            edges.insert(*d, refs);
1525        }
1526        // `zip` rather than an index: the two are pushed in the same step by
1527        // `collect_impls`, and zipping makes a misalignment inexpressible rather
1528        // than something a fallback span would paper over.
1529        for ((d, ty), &span) in bindings.iter_mut().zip(spans) {
1530            if !reaches_self(*d, &edges) {
1531                continue;
1532            }
1533            let name = self.defs.def(*d).name.clone();
1534            *ty = Ty::Error;
1535            self.diagnostics.push(
1536                Diagnostic::error(
1537                    "E0077",
1538                    format!("the associated type `{name}` is defined in terms of itself"),
1539                )
1540                .with_primary_label(span, "cyclic associated type")
1541                .with_note(
1542                    "an associated type's binding may name another of this impl's \
1543                     associated types, but the references must bottom out in a \
1544                     concrete type"
1545                        .to_string(),
1546                ),
1547            );
1548        }
1549    }
1550
1551    /// Every trait impl must be accompanied by an impl of each of the trait's
1552    /// supertraits for the same self type: `trait B: A` says a `B` *is* an `A`,
1553    /// so `impl B for R` without `impl A for R` lets the bound `T: B` promise
1554    /// methods no impl provides — the call resolves in the type checker and then
1555    /// finds no impl at monomorphization.
1556    ///
1557    /// Only *direct* supertraits are required here. That is enough transitively:
1558    /// the `impl A for R` this pass demands is itself an impl in the table, so it
1559    /// is visited too and made to supply `A`'s own supertraits. Checking the
1560    /// transitive closure instead would report the same missing impl once per
1561    /// subtrait in the chain.
1562    ///
1563    /// Runs *after* the whole impl table is built, beside
1564    /// [`Checker::check_impl_coherence`], rather than from
1565    /// [`Checker::check_impl_conformance`]: conformance is called from the middle
1566    /// of `collect_impls`, before the impl being checked has even been pushed, so
1567    /// it sees only impls from *earlier* items and would reject an `impl B for R`
1568    /// written above its `impl A for R`. Nova has no declaration-order rule for
1569    /// impls, and `resolve_method_on` does not impose one either.
1570    fn check_supertrait_impls(&mut self, spans: &[Span]) {
1571        let mut errors: Vec<(String, Span)> = Vec::new();
1572        for (imp, &span) in self.impls.iter().zip(spans) {
1573            let Some(trait_id) = imp.trait_id else {
1574                continue;
1575            };
1576            let Some(tr) = self.traits.iter().find(|t| t.def_id == trait_id) else {
1577                continue;
1578            };
1579            for &super_id in &tr.supertraits {
1580                // A generic impl's self type is a pattern, so the supertrait impl
1581                // must cover the whole family: `match_args` treats the supertrait
1582                // impl's self type as the pattern and this impl's as the ground
1583                // term, which an `impl A for W<Int>` fails against
1584                // `impl<T> B for W<T>` — as it should, since it leaves every other
1585                // `W<T>` without an `A`.
1586                let satisfied = self.impls.iter().any(|other| {
1587                    other.trait_id == Some(super_id) && other.match_args(&imp.self_ty).is_some()
1588                });
1589                if satisfied {
1590                    continue;
1591                }
1592                let sname = self
1593                    .traits
1594                    .iter()
1595                    .find(|t| t.def_id == super_id)
1596                    .map(|t| t.name.as_str())
1597                    .unwrap_or("?");
1598                errors.push((
1599                    format!(
1600                        "the trait `{}` requires `{sname}`, which `{}` does not implement",
1601                        tr.name,
1602                        display_ty(&imp.self_ty, self.defs),
1603                    ),
1604                    span,
1605                ));
1606            }
1607        }
1608        for (msg, span) in errors {
1609            self.error("E0072", msg, span);
1610        }
1611    }
1612
1613    /// Reject overlapping implementations (Phase 1 has no specialization): two
1614    /// trait impls of the same trait whose self types share a ground instance
1615    /// conflict outright, and two inherent impls that overlap conflict on any
1616    /// method they both define. Without this, dispatch would depend on impl
1617    /// declaration order.
1618    fn check_impl_coherence(&mut self, spans: &[Span]) {
1619        let mut conflicts: Vec<(String, Span)> = Vec::new();
1620        for (i, a) in self.impls.iter().enumerate() {
1621            for (b, &b_span) in self.impls.iter().zip(spans).skip(i + 1) {
1622                if a.self_head != b.self_head || a.trait_id != b.trait_id {
1623                    continue;
1624                }
1625                if !hir::self_types_overlap(&a.self_ty, a.generics, &b.self_ty, b.generics) {
1626                    continue;
1627                }
1628                match a.trait_id {
1629                    Some(tid) => {
1630                        let tname = self
1631                            .traits
1632                            .iter()
1633                            .find(|t| t.def_id == tid)
1634                            .map(|t| t.name.clone())
1635                            .unwrap_or_else(|| "?".to_string());
1636                        conflicts.push((
1637                            format!(
1638                                "conflicting implementations of trait `{tname}` for \
1639                                 overlapping types"
1640                            ),
1641                            b_span,
1642                        ));
1643                    }
1644                    None => {
1645                        let dup = a
1646                            .methods
1647                            .iter()
1648                            .map(|(n, _)| n)
1649                            .find(|n| b.methods.iter().any(|(m, _)| m == *n))
1650                            .cloned();
1651                        if let Some(m) = dup {
1652                            conflicts.push((
1653                                format!(
1654                                    "method `{m}` is defined by multiple overlapping \
1655                                     inherent impls"
1656                                ),
1657                                b_span,
1658                            ));
1659                        }
1660                    }
1661                }
1662            }
1663        }
1664        for (msg, span) in conflicts {
1665            self.error("E0074", msg, span);
1666        }
1667    }
1668
1669    /// Render a set of trait bounds for a diagnostic, e.g. `` `Show + Ord` ``
1670    /// or `(none)` when empty.
1671    fn render_bound_set(&self, bounds: &[DefId]) -> String {
1672        if bounds.is_empty() {
1673            return "(none)".to_string();
1674        }
1675        let names: Vec<String> = bounds
1676            .iter()
1677            .map(|d| {
1678                self.traits
1679                    .iter()
1680                    .find(|t| t.def_id == *d)
1681                    .map(|t| t.name.clone())
1682                    .unwrap_or_else(|| format!("{d:?}"))
1683            })
1684            .collect();
1685        format!("`{}`", names.join(" + "))
1686    }
1687
1688    /// Verify a trait impl provides exactly the trait's *set* of items: every
1689    /// required method, every declared associated type, and nothing foreign in
1690    /// either category.
1691    ///
1692    /// **Set membership only.** Whether each provided method's *signature*
1693    /// agrees with the trait's declaration is
1694    /// [`Checker::check_impl_method_signatures`], which cannot run from here —
1695    /// see its doc comment for why. The split follows the diagnostic codes
1696    /// exactly: `E0070` (missing a required item) and `E0071` (not a member of
1697    /// the trait) are decided here, from names alone; `E0072` (the item exists
1698    /// on both sides but its shape disagrees) is decided there, from types.
1699    ///
1700    /// The *whole* per-method comparison moved, not only the two checks that
1701    /// compare a type. The receiver, generic-count and generic-bound checks need
1702    /// nothing from the impl table and could have stayed — but they are the early
1703    /// `continue`s that decide whether the type comparison runs at all, so
1704    /// splitting the loop would mean duplicating each of those guards on both
1705    /// sides of the split and keeping the two copies in agreement forever.
1706    ///
1707    /// `provided_assoc` is the impl's associated-type bindings as written, by
1708    /// name and span: this is the set-membership check in both directions, so
1709    /// it needs the names the trait *doesn't* declare, which have no `DefId`
1710    /// and are therefore absent from `ImplInfo::assoc_bindings`.
1711    fn check_impl_conformance(
1712        &mut self,
1713        trait_id: DefId,
1714        provided: &[(String, DefId)],
1715        provided_assoc: &[ast::AssocTypeBinding],
1716        span: Span,
1717    ) {
1718        let Some(tr) = self.traits.iter().find(|t| t.def_id == trait_id).cloned() else {
1719            return;
1720        };
1721        for (name, _) in provided {
1722            if !tr.methods.iter().any(|m| &m.name == name) {
1723                self.error(
1724                    "E0071",
1725                    format!("method `{name}` is not a member of trait `{}`", tr.name),
1726                    span,
1727                );
1728            }
1729        }
1730        // An associated type the trait never declared: the E0071 "not a member
1731        // of the trait" case, the same as a foreign method above, and reported
1732        // at the binding itself rather than at the impl's self type because
1733        // there is a precise span for it.
1734        for b in provided_assoc {
1735            if !tr.assoc_types.iter().any(|(n, _)| n == &b.name.value) {
1736                self.error(
1737                    "E0071",
1738                    format!(
1739                        "associated type `{}` is not a member of trait `{}`",
1740                        b.name.value, tr.name
1741                    ),
1742                    b.name.span,
1743                );
1744            }
1745        }
1746        let missing: Vec<String> = tr
1747            .methods
1748            .iter()
1749            .filter(|m| m.default_def.is_none() && !provided.iter().any(|(n, _)| n == &m.name))
1750            .map(|m| format!("`{}`", m.name))
1751            .collect();
1752        // Associated types have no defaults in this increment, so every
1753        // declared one is required: the filter is simply "not provided", with
1754        // no `default_def.is_none()` counterpart to exempt anything. If
1755        // defaults are ever added, this is the line that must learn about them.
1756        let missing_assoc: Vec<String> = tr
1757            .assoc_types
1758            .iter()
1759            .filter(|(n, _)| !provided_assoc.iter().any(|b| &b.name.value == n))
1760            .map(|(n, _)| format!("`{n}`"))
1761            .collect();
1762        // One diagnostic for both categories — but naming only the ones
1763        // actually missing, so a missing `Item` never reads as a missing
1764        // *method*. With only methods missing this is byte-identical to the
1765        // message this site emitted before associated types existed.
1766        let mut kinds: Vec<String> = Vec::new();
1767        if !missing.is_empty() {
1768            kinds.push(format!("method(s): {}", missing.join(", ")));
1769        }
1770        if !missing_assoc.is_empty() {
1771            kinds.push(format!("associated type(s): {}", missing_assoc.join(", ")));
1772        }
1773        if !kinds.is_empty() {
1774            self.error(
1775                "E0070",
1776                format!(
1777                    "impl of trait `{}` is missing {}",
1778                    tr.name,
1779                    kinds.join(" and ")
1780                ),
1781                span,
1782            );
1783        }
1784    }
1785
1786    /// Verify each impl method's signature against the trait's declaration,
1787    /// with `Self` bound to the impl's self type and **both sides normalized**
1788    /// — normalization seam 2 of three (design doc §4.1, and its named risk 2).
1789    ///
1790    /// Without this check the call site programs against the trait's signature
1791    /// while codegen dispatches to the impl's method, and a mismatch
1792    /// miscompiles or is memory-unsafe. Every diagnostic here is `E0072`.
1793    ///
1794    /// **Both sides are normalized, not just the trait's.** The trait's
1795    /// declaration may spell a type as `Self::Item`; an impl may spell the same
1796    /// type either way (design doc §5.1 pins that both are accepted). Normalize
1797    /// only the trait side and the `-> Self::Item` spelling breaks, since the
1798    /// impl's stored signature still holds the projection; normalize only the
1799    /// impl side and the `-> T` spelling stays broken. Substituting is not
1800    /// enough on its own either: it turns `Self::Item` into
1801    /// `Assoc { on: W<Param(0)> }`, which is still a projection.
1802    ///
1803    /// **Runs after the whole impl table is built**, beside
1804    /// [`Checker::check_impl_coherence`] and
1805    /// [`Checker::check_supertrait_impls`], rather than from
1806    /// [`Checker::check_impl_conformance`] where it used to live — and for the
1807    /// same reason `check_supertrait_impls` was moved out of conformance.
1808    /// `collect_impls` calls conformance ten lines *before* it pushes the impl
1809    /// being checked, so a `normalize` there would see neither this impl's own
1810    /// bindings nor those of any impl written below it. Pushing the `ImplInfo`
1811    /// above the conformance call fixes the first half only: normalization
1812    /// consults the whole table, and a projection can need a *different*
1813    /// impl's binding. It is reachable from ordinary source, because
1814    /// `collect_traits` puts a trait's expanded supertraits in `sig_bounds[0]`,
1815    /// so `Self::Elem` inside `trait Ext: Base` is `Base`'s associated type and
1816    /// substituting `Self` yields a projection whose binding lives in
1817    /// `impl Base for W` — which may be written either side of
1818    /// `impl Ext for W`. Nova has no declaration-order rule for impls, and
1819    /// `conformance_resolves_a_projection_bound_by_a_later_declared_impl` pins
1820    /// that it has none here either. Measured under a hoisted push: that test's
1821    /// `impl Ext` first ordering still reports `E0072`, and it is the *only*
1822    /// test in the suite that separates the two designs.
1823    ///
1824    /// **What moving this changed with no test to pin it: diagnostic order.**
1825    /// Every `E0070`/`E0071` for every impl now precedes every `E0072` about a
1826    /// method signature, where before both came from one call per impl in source
1827    /// order. Nothing sorts diagnostics between here and the terminal, so the
1828    /// order is user-visible: an impl on line 5 with a bad signature and one on
1829    /// line 7 missing a method now report line 7 first. No test asserts a code
1830    /// *sequence* spanning both bands, so this is deliberate and unpinned rather
1831    /// than accidentally green — pinning it would freeze an ordering that is not
1832    /// itself a designed property.
1833    fn check_impl_method_signatures(&mut self, spans: &[Span]) {
1834        // The table is cloned rather than walked in place: `normalize` and
1835        // `error` both need `&mut self`, and `normalize` — reading `self.impls`
1836        // — is the entire point of this pass. `check_supertrait_impls` avoids the
1837        // clone by deferring its diagnostics to a second loop; that is not open
1838        // here, because `normalize` itself needs `&mut self` to report `E0078`.
1839        let impls = self.impls.clone();
1840        for (imp, &span) in impls.iter().zip(spans) {
1841            let Some(trait_id) = imp.trait_id else {
1842                continue;
1843            };
1844            let Some(tr) = self.traits.iter().find(|t| t.def_id == trait_id).cloned() else {
1845                continue;
1846            };
1847            for (name, def_id) in &imp.methods {
1848                let Some(trait_method) = tr.methods.iter().find(|m| &m.name == name) else {
1849                    // A method the trait does not declare: reported as `E0071`
1850                    // by `check_impl_conformance`, and there is no trait
1851                    // signature here to compare it against.
1852                    continue;
1853                };
1854                // Spec 3.4a §3.2: the family links, where the checker matches
1855                // an impl method to its trait's.
1856                if let Some(idx) = tr.methods.iter().position(|m| &m.name == name) {
1857                    if let Some(index) = self.index.as_mut() {
1858                        index.implement(*def_id, (trait_id, idx as u32));
1859                    }
1860                }
1861                let Some(impl_sig) = self.sigs.get(def_id).cloned() else {
1862                    continue;
1863                };
1864                let impl_count = imp.generics;
1865                // The receiver must agree. Nothing below can catch a
1866                // disagreement: neither `params` list stores `self`, so an impl
1867                // that adds or drops the receiver still compares equal
1868                // parameter-for-parameter and return-type-wise. Yet a call site
1869                // programs against the trait's signature while codegen
1870                // dispatches to the impl's function, so the two differ by
1871                // exactly one leading argument — Cranelift rejects the module
1872                // ("mismatched argument count") and the compiler ICEs on source
1873                // that `nova check` accepted.
1874                let impl_has_self = !self.selfless.contains(def_id);
1875                if impl_has_self != trait_method.has_self {
1876                    let (want, got) = if trait_method.has_self {
1877                        ("a `self` receiver", "none")
1878                    } else {
1879                        ("no `self` receiver", "one")
1880                    };
1881                    self.error(
1882                        "E0072",
1883                        format!(
1884                            "method `{name}` has {got} but trait `{}` declares {want}",
1885                            tr.name
1886                        ),
1887                        span,
1888                    );
1889                    continue;
1890                }
1891                // The receiver's *mutability* must agree too, and nothing else
1892                // catches it either: `method_sig_parts` strips a `self`
1893                // parameter whether or not it is `mut`, so neither `params` list
1894                // records it, and the parameter and return comparisons below
1895                // therefore pass a disagreeing pair. Yet the two halves are read
1896                // by *different* consumers — `emit_trait_call` gates the call
1897                // site on the trait's flag while `check_fn_body` gates the body
1898                // on the impl's own `mut` — so a disagreement means the
1899                // receiver's mutability requirement is decided by whichever
1900                // table the reader happened to consult. Trait `mut self` with an
1901                // impl `self` demands `let mut` at every call site for a method
1902                // that cannot mutate; the reverse lets an impl mutate through a
1903                // binding no caller ever granted the permission to.
1904                //
1905                // Deliberately **no `continue`**, unlike the receiver-*presence*
1906                // mismatch above. That one `continue`s because both parameter
1907                // lists are then misaligned by one and every later comparison is
1908                // noise; a `mut` disagreement misaligns nothing, so the rest of
1909                // the signature is still worth checking in the same pass.
1910                let impl_mut_self = self.mut_self.contains(def_id);
1911                if impl_mut_self != trait_method.mut_self {
1912                    let (want, got) = if trait_method.mut_self {
1913                        ("a `mut self` receiver", "a plain `self` receiver")
1914                    } else {
1915                        ("a plain `self` receiver", "a `mut self` receiver")
1916                    };
1917                    self.error(
1918                        "E0072",
1919                        format!(
1920                            "method `{name}` has {got} but trait `{}` declares {want}",
1921                            tr.name
1922                        ),
1923                        span,
1924                    );
1925                }
1926                // The impl method's own generics = its total minus the impl's,
1927                // and must match the trait method's generic count.
1928                let impl_method_generics = impl_sig.generics.saturating_sub(impl_count);
1929                if impl_method_generics != trait_method.generics {
1930                    self.error(
1931                        "E0072",
1932                        format!(
1933                            "method `{name}` has {impl_method_generics} generic parameter(s) \
1934                             but trait `{}` declares {}",
1935                            tr.name, trait_method.generics
1936                        ),
1937                        span,
1938                    );
1939                    continue;
1940                }
1941                // Each method generic must carry exactly the trait's declared
1942                // bounds — neither dropped nor added. The impl method's own
1943                // generics live at `impl_sig.bounds[impl_count + k]`, aligned
1944                // with the trait method's `bounds[k]`. Without this the trait
1945                // signature the call site programs against is not the contract
1946                // the impl honors: an impl that drops a bound accepts calls the
1947                // trait forbids (unsound), and one that adds a bound rejects
1948                // trait-valid calls only later, at monomorphization.
1949                for k in 0..trait_method.generics as usize {
1950                    let want = trait_method.bounds[k].as_slice();
1951                    let got = impl_sig
1952                        .bounds
1953                        .get(impl_count as usize + k)
1954                        .map(Vec::as_slice)
1955                        .unwrap_or(&[]);
1956                    if !same_bound_set(want, got) {
1957                        let want_str = self.render_bound_set(want);
1958                        let got_str = self.render_bound_set(got);
1959                        self.error(
1960                            "E0072",
1961                            format!(
1962                                "generic parameter {} of method `{name}` has bound(s) \
1963                                 {got_str} but trait `{}` declares {want_str}",
1964                                k + 1,
1965                                tr.name,
1966                            ),
1967                            span,
1968                        );
1969                    }
1970                }
1971                // Map the trait method's Param space into the impl's: `Self`
1972                // (Param(0)) -> the impl self type; method generic k
1973                // (Param(1+k)) -> the impl method's own generic at
1974                // Param(impl_count + k).
1975                let mut subst = Vec::with_capacity(1 + trait_method.generics as usize);
1976                subst.push(imp.self_ty.clone());
1977                for k in 0..trait_method.generics {
1978                    subst.push(Ty::Param(impl_count + k));
1979                }
1980                // `impl_sig.params[0]` is the `self` receiver — skip it and
1981                // compare the declared parameters (and the return type) against
1982                // the trait method's, which `method_sig_parts` also stores
1983                // without `self`. An associated function has no receiver stored,
1984                // so there is nothing to skip (and `[1..]` would panic on its
1985                // empty parameter list).
1986                let impl_params: &[Ty] = if self.selfless.contains(def_id) {
1987                    &impl_sig.params
1988                } else {
1989                    impl_sig.params.get(1..).unwrap_or_default()
1990                };
1991                if impl_params.len() != trait_method.params.len() {
1992                    self.error(
1993                        "E0072",
1994                        format!(
1995                            "method `{name}` has {} parameter(s) but trait `{}` declares {}",
1996                            impl_params.len(),
1997                            tr.name,
1998                            trait_method.params.len()
1999                        ),
2000                        span,
2001                    );
2002                    continue;
2003                }
2004                // Normalized on both sides, and *after* the count check so a
2005                // wrong arity is still reported as an arity error rather than as
2006                // a per-parameter mismatch — `zip` truncates to the shorter list,
2007                // so with the count check gone a missing parameter reports
2008                // *nothing at all*.
2009                //
2010                // Normalized unconditionally, with no `Ty::has_assoc` guard of
2011                // the kind `Checker::instantiate` carries. There, the guard
2012                // preserves the exact pre-existing `subst`-only path for a
2013                // projection-free call; here `normalize_ty` on a projection-free
2014                // type is a structural clone and returns an equal value, so a
2015                // guard would be unobservable *and* unpinnable, and would only
2016                // add a second thing to keep true.
2017                let expected: Vec<Ty> = trait_method
2018                    .params
2019                    .iter()
2020                    .map(|p| {
2021                        let p = p.subst(&subst);
2022                        self.normalize(&p, span)
2023                    })
2024                    .collect();
2025                let got_params: Vec<Ty> = impl_params
2026                    .iter()
2027                    .map(|p| self.normalize(p, span))
2028                    .collect();
2029                for (i, (got, want)) in got_params.iter().zip(expected.iter()).enumerate() {
2030                    // A poisoned side is not a mismatch — see `signatures_are
2031                    // _not_compared_when_either_side_is_already_poisoned`.
2032                    if got.has_error() || want.has_error() {
2033                        continue;
2034                    }
2035                    if got != want {
2036                        self.error(
2037                            "E0072",
2038                            format!(
2039                                "parameter {} of method `{name}` has type `{}` but trait \
2040                                 `{}` declares `{}`",
2041                                i + 1,
2042                                display_ty(got, self.defs),
2043                                tr.name,
2044                                display_ty(want, self.defs),
2045                            ),
2046                            span,
2047                        );
2048                    }
2049                }
2050                let expected_ret = trait_method.ret.subst(&subst);
2051                let expected_ret = self.normalize(&expected_ret, span);
2052                let impl_ret = self.normalize(&impl_sig.ret, span);
2053                // `Ty::Error` on either side means a diagnostic about *that* type
2054                // was already reported, so comparing is worse than useless here:
2055                // `Ty` derives `PartialEq` with no `Error` absorption, so a
2056                // poisoned side does not merely fail to help — it *forces* a
2057                // mismatch, and the follow-on `E0072` renders it as `{error}`,
2058                // which names nothing a user can act on. This is the exact
2059                // opposite of `Ty::Error`'s behaviour at `unify`, where it
2060                // absorbs; the two consumers of the same sentinel need opposite
2061                // handling, and only `unify` got it for free.
2062                //
2063                // Transitive (`has_error`) rather than `== Ty::Error`: an impl
2064                // binding an unresolvable type makes the trait's
2065                // `Option<Self::Item>` normalize to `Option<{error}>`, so a
2066                // top-level check would still leak `{error}` through a wrapper.
2067                //
2068                // Suppression of a *second* error only. Both routes to a poisoned
2069                // side — `E0077` for a cyclic binding and `E0001` for an
2070                // unresolvable one — have already reported the real problem.
2071                if impl_ret.has_error() || expected_ret.has_error() {
2072                    continue;
2073                }
2074                if impl_ret != expected_ret {
2075                    self.error(
2076                        "E0072",
2077                        format!(
2078                            "method `{name}` returns `{}` but trait `{}` declares `{}`",
2079                            display_ty(&impl_ret, self.defs),
2080                            tr.name,
2081                            display_ty(&expected_ret, self.defs),
2082                        ),
2083                        span,
2084                    );
2085                }
2086            }
2087        }
2088    }
2089
2090    /// Convert a method's non-`self` parameter types and return type using
2091    /// `scope` (which maps `Self`/generics to `Param` indices) and `bounds`
2092    /// (indexed the same way — see `convert_ty`).
2093    fn method_sig_parts(
2094        &mut self,
2095        params: &[ast::Param],
2096        ret: &Option<Spanned<ast::Type>>,
2097        scope: &FxHashMap<String, u32>,
2098        bounds: &[Vec<DefId>],
2099    ) -> (Vec<Ty>, Ty) {
2100        let converted = params
2101            .iter()
2102            .filter(|p| p.name.value != "self")
2103            .map(|p| self.convert_ty(&p.ty, scope, bounds))
2104            .collect();
2105        let ret_ty = ret
2106            .as_ref()
2107            .map(|t| self.convert_ty(t, scope, bounds))
2108            .unwrap_or(Ty::Unit);
2109        (converted, ret_ty)
2110    }
2111
2112    fn collect_signatures(&mut self) {
2113        let fn_ids: Vec<(DefId, usize)> = self.defs.functions().collect();
2114        for (def_id, item_index) in fn_ids {
2115            let ast::Item::Function(f) = &self.file.items[item_index].value else {
2116                continue;
2117            };
2118            self.cur_module = self.defs.module_of(item_index);
2119            self.check_duplicate_generics(&f.generics, "function");
2120            let generics = generic_scope(&f.generics);
2121            self.enter_type_params(&[&f.generics]);
2122            let mut bounds = self.resolve_bounds(&f.generics);
2123            self.apply_where(&mut bounds, &f.where_clause, &generics);
2124            self.expand_bounds(&mut bounds);
2125            let params = f
2126                .params
2127                .iter()
2128                .map(|p| self.convert_ty(&p.ty, &generics, &bounds))
2129                .collect();
2130            let ret = f
2131                .return_ty
2132                .as_ref()
2133                .map(|t| self.convert_ty(t, &generics, &bounds))
2134                .unwrap_or(Ty::Unit);
2135            // An async fn's declared return type is its future's OUTPUT, per
2136            // the spec's own signatures (`pub async fn join(self) -> T`), so
2137            // the signature every caller sees is `Future<ret>`. `check_fn_body`
2138            // unwraps it back to `ret` to check this fn's own body.
2139            let ret = if f.is_async {
2140                Ty::Future(Box::new(ret))
2141            } else {
2142                ret
2143            };
2144            self.sigs.insert(
2145                def_id,
2146                FnSig {
2147                    generics: f.generics.len() as u32,
2148                    bounds,
2149                    params,
2150                    ret,
2151                },
2152            );
2153        }
2154        // A constant compiles to a zero-argument function returning its
2155        // value; register its signature here.
2156        for (def_id, item_index) in self.const_ids() {
2157            let ast::Item::Const(c) = &self.file.items[item_index].value else {
2158                continue;
2159            };
2160            self.cur_module = self.defs.module_of(item_index);
2161            let ret = self.convert_ty(&c.ty, &FxHashMap::default(), &[]);
2162            self.sigs.insert(
2163                def_id,
2164                FnSig {
2165                    generics: 0,
2166                    bounds: Vec::new(),
2167                    params: Vec::new(),
2168                    ret,
2169                },
2170            );
2171        }
2172    }
2173
2174    /// Collect and validate `extern` (FFI) function declarations. Each becomes a
2175    /// zero-generic `FnSig` (so calls type-check through the normal path) plus a
2176    /// `hir::ExternFn` carrying its raw C symbol for codegen. Only the C ABI and
2177    /// FFI-safe scalar types are supported; anything else is a diagnostic.
2178    fn collect_externs(&mut self) {
2179        let externs: Vec<(DefId, usize, usize)> = self.defs.extern_functions().collect();
2180        for (def_id, item_index, fn_index) in externs {
2181            let ast::Item::Extern(block) = &self.file.items[item_index].value else {
2182                continue;
2183            };
2184            let ExternItem::Fn(sig) = &block.items[fn_index];
2185            self.cur_module = self.defs.module_of(item_index);
2186
2187            // The symbol is emitted raw (unmangled), so it shares a namespace
2188            // with the compiler's own symbols. Reserve the `nova_` prefix (the
2189            // runtime's `nova_rt_*` and the `nova_main` entry) and `main` so an
2190            // extern can't shadow or alias an internal symbol.
2191            if sig.name.value == "main" || sig.name.value.starts_with("nova_") {
2192                self.error(
2193                    "E0900",
2194                    format!(
2195                        "extern symbol `{}` is reserved by the compiler",
2196                        sig.name.value
2197                    ),
2198                    sig.name.span,
2199                );
2200                continue;
2201            }
2202
2203            // Only the C ABI (explicit `"C"` or omitted) is supported.
2204            if !matches!(block.abi.as_deref(), None | Some("C")) {
2205                let abi = block.abi.clone().unwrap_or_default();
2206                self.error(
2207                    "E0900",
2208                    format!("extern ABI `\"{abi}\"` is not supported; only the C ABI is supported"),
2209                    sig.name.span,
2210                );
2211            }
2212            if sig.is_async {
2213                self.unsupported(sig.name.span, "async extern functions");
2214            }
2215            if !sig.where_clause.is_empty() {
2216                self.unsupported(sig.name.span, "`where` clauses on extern functions");
2217            }
2218            if !sig.generics.is_empty() {
2219                // Skip type conversion: the generic params aren't in scope, which
2220                // would cascade a false "cannot find type" on top of this error.
2221                self.unsupported(sig.name.span, "generic extern functions");
2222                continue;
2223            }
2224
2225            self.enter_type_params(&[]);
2226            self.note_params(&sig.params);
2227            let empty = FxHashMap::default();
2228            let params: Vec<Ty> = sig
2229                .params
2230                .iter()
2231                .map(|p| {
2232                    let ty = self.convert_ty(&p.ty, &empty, &[]);
2233                    self.require_ffi_safe(&ty, p.ty.span, false);
2234                    ty
2235                })
2236                .collect();
2237            let ret = match &sig.return_ty {
2238                Some(t) => {
2239                    let ty = self.convert_ty(t, &empty, &[]);
2240                    self.require_ffi_safe(&ty, t.span, true);
2241                    ty
2242                }
2243                None => Ty::Unit,
2244            };
2245
2246            self.sigs.insert(
2247                def_id,
2248                FnSig {
2249                    generics: 0,
2250                    bounds: Vec::new(),
2251                    params: params.clone(),
2252                    ret: ret.clone(),
2253                },
2254            );
2255            self.externs.push(hir::ExternFn {
2256                def_id,
2257                symbol: sig.name.value.clone(),
2258                params,
2259                ret,
2260                span: sig.name.span,
2261            });
2262        }
2263    }
2264
2265    /// Reject a non-FFI-safe type in an `extern` signature. The FFI-safe types
2266    /// are the scalars `Int`, `Float`, `Bool` (and, for a return, unit / `void`).
2267    fn require_ffi_safe(&mut self, ty: &Ty, span: Span, is_return: bool) {
2268        let ok = match ty {
2269            Ty::Int | Ty::Float | Ty::Bool => true,
2270            Ty::Unit => is_return,
2271            // `convert_ty` already reported why this type is unusable.
2272            Ty::Error => true,
2273            _ => false,
2274        };
2275        if !ok {
2276            self.error(
2277                "E0900",
2278                format!(
2279                    "`{}` is not FFI-safe in an extern signature yet; only Int, Float, and Bool \
2280                     (and a unit return) are supported",
2281                    display_ty(ty, self.defs)
2282                ),
2283                span,
2284            );
2285        }
2286    }
2287
2288    /// All constant definitions as `(DefId, item_index)`.
2289    fn const_ids(&self) -> Vec<(DefId, usize)> {
2290        self.defs
2291            .defs()
2292            .iter()
2293            .enumerate()
2294            .filter_map(|(i, d)| match d.kind {
2295                DefKind::Const { item_index } => Some((DefId(i as u32), item_index)),
2296                _ => None,
2297            })
2298            .collect()
2299    }
2300
2301    /// Resolve each generic parameter's trait bounds to trait `DefId`s.
2302    fn resolve_bounds(&mut self, generics: &[ast::TypeParam]) -> Vec<Vec<DefId>> {
2303        generics
2304            .iter()
2305            .map(|g| {
2306                let mut ids: Vec<DefId> = Vec::new();
2307                for b in &g.bounds {
2308                    let name = b
2309                        .value
2310                        .segments
2311                        .last()
2312                        .map(|s| s.value.as_str())
2313                        .unwrap_or("");
2314                    match self.defs.resolve_trait(self.cur_module, name) {
2315                        // Deduplicate: `T: Show + Show` means the same as
2316                        // `T: Show`. A repeated trait must not later read as two
2317                        // distinct method providers (a false E0015 ambiguity).
2318                        Some(id) => {
2319                            if let Some(seg) = b.value.segments.last() {
2320                                self.note_use(seg.span, Target::Def(id));
2321                            }
2322                            if !ids.contains(&id) {
2323                                ids.push(id);
2324                            }
2325                        }
2326                        None => {
2327                            self.error("E0001", format!("cannot find trait `{name}`"), b.span);
2328                            if let Some(seg) = b.value.segments.last() {
2329                                self.name_fixes(seg.clone(), Need::Trait, Vec::new());
2330                            }
2331                        }
2332                    }
2333                }
2334                ids
2335            })
2336            .collect()
2337    }
2338
2339    /// Fold a `where` clause into a per-parameter bound list (as produced by
2340    /// [`Self::resolve_bounds`]). A `where` bound is just an out-of-line spelling
2341    /// of an inline `<T: Trait>`: each `T: Trait` must constrain one of the
2342    /// item's own type parameters (resolved through `scope`), and its traits
2343    /// accumulate on top of any inline bounds. Constraints on concrete or
2344    /// compound types (`where Box<T>: Trait`) are not supported yet.
2345    fn apply_where(
2346        &mut self,
2347        bounds: &mut [Vec<DefId>],
2348        where_clause: &[ast::WhereBound],
2349        scope: &FxHashMap<String, u32>,
2350    ) {
2351        for wb in where_clause {
2352            let idx = match self.convert_ty(&wb.ty, scope, bounds) {
2353                Ty::Param(i) => i as usize,
2354                // `convert_ty` already reported an unknown/invalid type.
2355                Ty::Error => continue,
2356                _ => {
2357                    self.error(
2358                        "E0900",
2359                        "a `where` clause may only constrain a type parameter",
2360                        wb.ty.span,
2361                    );
2362                    continue;
2363                }
2364            };
2365            let Some(slot) = bounds.get_mut(idx) else {
2366                continue;
2367            };
2368            for b in &wb.bounds {
2369                let name = b
2370                    .value
2371                    .segments
2372                    .last()
2373                    .map(|s| s.value.as_str())
2374                    .unwrap_or("");
2375                match self.defs.resolve_trait(self.cur_module, name) {
2376                    // Deduplicate against inline and earlier `where` bounds, so a
2377                    // trait named twice is not read as two method providers.
2378                    Some(id) => {
2379                        if let Some(seg) = b.value.segments.last() {
2380                            self.note_use(seg.span, Target::Def(id));
2381                        }
2382                        if !slot.contains(&id) {
2383                            slot.push(id);
2384                        }
2385                    }
2386                    None => {
2387                        self.error("E0001", format!("cannot find trait `{name}`"), b.span);
2388                        if let Some(seg) = b.value.segments.last() {
2389                            self.name_fixes(seg.clone(), Need::Trait, Vec::new());
2390                        }
2391                    }
2392                }
2393            }
2394        }
2395    }
2396
2397    /// Convert an AST type annotation to a `Ty`, resolving names. `bounds` is
2398    /// the trait-bound list of each of `generics`' parameters, indexed the
2399    /// same way (`bounds[i]` bounds whichever name maps to `i` in `generics`,
2400    /// mirroring `FnCtx.param_bounds`) — needed to resolve a two-segment path
2401    /// like `I::Item` against the traits `I` is actually bounded by.
2402    fn convert_ty(
2403        &mut self,
2404        ty: &Spanned<ast::Type>,
2405        generics: &FxHashMap<String, u32>,
2406        bounds: &[Vec<DefId>],
2407    ) -> Ty {
2408        match &ty.value {
2409            ast::Type::Path { path, args } => {
2410                if path.segments.len() == 2 {
2411                    let base = path.segments[0].value.as_str();
2412                    let assoc_name = path.segments[1].value.as_str();
2413                    // A two-segment path is a **projection** when its first
2414                    // segment names something a projection can be *on*, and a
2415                    // module-qualified path otherwise. There are exactly two
2416                    // such things, and they are different mechanisms:
2417                    //
2418                    //  * an in-scope generic parameter, i.e. a `generics` key.
2419                    //    That includes a trait body's `Self`, which
2420                    //    `self_generic_scope` inserts as the trait's own
2421                    //    implicit parameter at `Param(0)`. It does *not*
2422                    //    include a user-written parameter spelled `Self`:
2423                    //    `E0076` rejects that name outright, so `Self` never
2424                    //    has two meanings in one scope.
2425                    //  * `Self` inside an `impl` block, which is not a
2426                    //    parameter at all but the impl's own self type — a
2427                    //    possibly compound `W<Param(0)>` with no parameter
2428                    //    index, and therefore no slot in the by-index `bounds`
2429                    //    table. Its candidates are not bounds but the single
2430                    //    trait the impl implements.
2431                    let by_index = generics.get(base).copied();
2432                    let in_impl = by_index.is_none() && base == "Self";
2433                    if by_index.is_some() || (in_impl && self.impl_self.is_some()) {
2434                        // Nova has no generic associated types: a non-empty
2435                        // `args` here (`I::Item<Int>`) has nowhere to go, so
2436                        // it must be flagged rather than silently dropped —
2437                        // mirroring the two single-segment branches below
2438                        // (generic parameter, primitive) that already guard
2439                        // this the same way. Deliberately says only
2440                        // "`{base}::{assoc_name}`", not "associated type
2441                        // `{base}::{assoc_name}`": `assoc_name` may not even
2442                        // be one (`I::Nope<Int>` reports this alongside
2443                        // resolve_projection's own E0001 for `Nope`, and the
2444                        // two must not read as contradicting each other).
2445                        if !args.is_empty() {
2446                            self.error(
2447                                "E0012",
2448                                format!("`{base}::{assoc_name}` takes no type arguments"),
2449                                ty.span,
2450                            );
2451                        }
2452                        if by_index.is_some() {
2453                            if let Some(t) = self.type_param_target(base) {
2454                                self.note_use(path.segments[0].span, t);
2455                            }
2456                        }
2457                        let (on, candidates) = match by_index {
2458                            // Find the associated type among the traits
2459                            // bounding this parameter. Searching the bounds
2460                            // (rather than every trait) is what makes
2461                            // `I::Item` mean "the Item of the trait I is
2462                            // bounded by". `expand_bounds` has already folded
2463                            // supertraits into every entry here, so a bound of
2464                            // `Ord` also carries `Eq` — `I::Item` resolves
2465                            // against the transitive bound set as a
2466                            // consequence of that ordering, not a separate
2467                            // decision made here.
2468                            Some(idx) => (
2469                                Ty::Param(idx),
2470                                bounds.get(idx as usize).cloned().unwrap_or_default(),
2471                            ),
2472                            None => {
2473                                // `in_impl` and `impl_self` is `Some`, per the
2474                                // guard above.
2475                                let imp = match self.impl_self.clone() {
2476                                    Some(imp) => imp,
2477                                    None => return Ty::Error,
2478                                };
2479                                match imp.trait_id {
2480                                    // Supertrait-expanded, so `Self::Elem` in
2481                                    // `impl Ext for W` resolves against `Base`
2482                                    // when `trait Ext: Base` declares nothing
2483                                    // itself. This is not an extra feature: the
2484                                    // *trait* side already does it, because
2485                                    // `collect_traits` seeds `sig_bounds[0]` with
2486                                    // the trait and then calls `expand_bounds`.
2487                                    // Without it, `trait Ext: Base { fn peek
2488                                    // (self) -> Self::Elem }` compiled while
2489                                    // `impl Ext for W { fn peek(self) ->
2490                                    // Self::Elem }` — the same signature, echoed
2491                                    // — was `E0001`, so the one spelling §5.1
2492                                    // pins as accepted was rejected on the side
2493                                    // that has to write it.
2494                                    Some(tid) => (imp.ty, self.with_supertraits(&[tid])),
2495                                    // An inherent impl implements no trait, so
2496                                    // no trait declares an associated type for
2497                                    // this to name. Given its own wording
2498                                    // rather than `resolve_projection`'s
2499                                    // empty-candidate message, which says "on
2500                                    // any bound of `Self`" — an inherent
2501                                    // impl's `Self` has no bounds, so that
2502                                    // would describe a lookup that never
2503                                    // happened.
2504                                    None => {
2505                                        let on = display_ty(&imp.ty, self.defs);
2506                                        self.error(
2507                                            "E0001",
2508                                            format!(
2509                                                "no associated type `{assoc_name}` on `{on}`: \
2510                                                 an inherent impl implements no trait, so \
2511                                                 nothing declares one"
2512                                            ),
2513                                            ty.span,
2514                                        );
2515                                        return Ty::Error;
2516                                    }
2517                                }
2518                            }
2519                        };
2520                        let projected =
2521                            self.resolve_projection(on, base, assoc_name, &candidates, ty.span);
2522                        if let Ty::Assoc { assoc, .. } = &projected {
2523                            self.note_use(path.segments[1].span, Target::Def(*assoc));
2524                        }
2525                        return projected;
2526                    }
2527                    self.unsupported(ty.span, "module-qualified type paths");
2528                    return Ty::Error;
2529                }
2530                if path.segments.len() != 1 {
2531                    self.unsupported(ty.span, "module-qualified type paths");
2532                    return Ty::Error;
2533                }
2534                let name = path.segments[0].value.as_str();
2535                if let Some(&idx) = generics.get(name) {
2536                    if let Some(t) = self.type_param_target(name) {
2537                        self.note_use(path.segments[0].span, t);
2538                    }
2539                    if !args.is_empty() {
2540                        self.error(
2541                            "E0012",
2542                            format!("generic parameter `{name}` takes no type arguments"),
2543                            ty.span,
2544                        );
2545                    }
2546                    return Ty::Param(idx);
2547                }
2548                // `Future<T>` — the one built-in type name with an argument.
2549                // Handled ahead of the nullary `prim` table below, whose
2550                // `args.is_empty()` guard means the opposite here.
2551                if name == "Future" {
2552                    self.note_use(path.segments[0].span, Target::Primitive("Future"));
2553                    if args.len() != 1 {
2554                        self.error(
2555                            "E0012",
2556                            format!(
2557                                "`Future` takes exactly one type argument, found {}",
2558                                args.len()
2559                            ),
2560                            ty.span,
2561                        );
2562                        return Ty::Error;
2563                    }
2564                    let out = self.convert_ty(&args[0], generics, bounds);
2565                    return Ty::Future(Box::new(out));
2566                }
2567                // Every name matched here must also be in
2568                // `nova_resolver::RESERVED_TYPE_NAMES`: that list is what
2569                // turns a user type declared under one of these names into
2570                // `E0089` instead of silently unreachable. A new nullary
2571                // built-in added to this table without joining that list
2572                // would recreate the defect the list exists to close.
2573                let prim = match name {
2574                    "Int" => Some(Ty::Int),
2575                    "Float" => Some(Ty::Float),
2576                    "Bool" => Some(Ty::Bool),
2577                    "Char" => Some(Ty::Char),
2578                    "String" => Some(Ty::String),
2579                    "Bytes" => Some(Ty::Bytes),
2580                    _ => None,
2581                };
2582                if let Some(p) = prim {
2583                    if let Some(name) = primitive(name) {
2584                        self.note_use(path.segments[0].span, Target::Primitive(name));
2585                    }
2586                    if !args.is_empty() {
2587                        self.error(
2588                            "E0012",
2589                            format!("`{name}` takes no type arguments"),
2590                            ty.span,
2591                        );
2592                    }
2593                    return p;
2594                }
2595                if let Some(def_id) = self.defs.resolve_type(self.cur_module, name) {
2596                    self.note_use(path.segments[0].span, Target::Def(def_id));
2597                    let is_record = matches!(self.defs.def(def_id).kind, DefKind::Record { .. });
2598                    // Arity is precomputed (see `collect_type_arities`) so it is
2599                    // independent of whether this type has been collected yet.
2600                    let expected = self.type_arity.get(&def_id).copied().unwrap_or(0);
2601                    let converted: Vec<Ty> = args
2602                        .iter()
2603                        .map(|a| self.convert_ty(a, generics, bounds))
2604                        .collect();
2605                    if converted.len() != expected as usize {
2606                        self.error(
2607                            "E0012",
2608                            format!(
2609                                "type `{name}` expects {expected} type argument(s), found {}",
2610                                converted.len()
2611                            ),
2612                            ty.span,
2613                        );
2614                        return Ty::Error;
2615                    }
2616                    return if is_record {
2617                        Ty::Record {
2618                            def_id,
2619                            args: converted,
2620                        }
2621                    } else {
2622                        Ty::Sum {
2623                            def_id,
2624                            args: converted,
2625                        }
2626                    };
2627                }
2628                self.error("E0001", format!("cannot find type `{name}`"), ty.span);
2629                self.name_fixes(
2630                    path.segments[0].clone(),
2631                    Need::Type,
2632                    generics.keys().cloned().collect(),
2633                );
2634                Ty::Error
2635            }
2636            ast::Type::Tuple(items) if items.is_empty() => Ty::Unit,
2637            ast::Type::Fn { params, ret } => Ty::Fn {
2638                params: params
2639                    .iter()
2640                    .map(|p| self.convert_ty(p, generics, bounds))
2641                    .collect(),
2642                ret: Box::new(self.convert_ty(ret, generics, bounds)),
2643            },
2644            ast::Type::Tuple(_) => {
2645                self.unsupported(ty.span, "tuple types");
2646                Ty::Error
2647            }
2648            ast::Type::Ref { .. } | ast::Type::Ptr { .. } => {
2649                self.unsupported(ty.span, "reference and pointer types");
2650                Ty::Error
2651            }
2652            ast::Type::Array(elem) => Ty::Array(Box::new(self.convert_ty(elem, generics, bounds))),
2653            ast::Type::Optional(_) => {
2654                self.unsupported(ty.span, "the `T?` optional sugar");
2655                Ty::Error
2656            }
2657            ast::Type::Infer => {
2658                self.unsupported(ty.span, "`_` type placeholders");
2659                Ty::Error
2660            }
2661        }
2662    }
2663
2664    /// Resolve a two-segment type path `<base>::<assoc_name>` into a projection
2665    /// on `on`, searching `candidate_traits` for the one that declares
2666    /// `assoc_name`.
2667    ///
2668    /// `on` is the type the projection is *on*, chosen by the caller: a generic
2669    /// parameter's `Ty::Param(idx)`, or an impl's own (possibly compound) self
2670    /// type. It is never `Ty::Var` — see the `Ty::Assoc` doc comment on why that
2671    /// distinction matters to the unifier.
2672    ///
2673    /// `candidate_traits` is likewise the caller's: a parameter's
2674    /// already-supertrait-expanded bounds, or the single trait an impl
2675    /// implements. `base` is only for rendering the two error messages, and both
2676    /// of them read the list as bounds — a caller whose candidates are not
2677    /// bounds must handle its own empty case before calling (see the inherent
2678    /// impl in `convert_ty`).
2679    fn resolve_projection(
2680        &mut self,
2681        on: Ty,
2682        base: &str,
2683        assoc_name: &str,
2684        candidate_traits: &[DefId],
2685        span: Span,
2686    ) -> Ty {
2687        let candidates: Vec<DefId> = candidate_traits
2688            .iter()
2689            .filter_map(|&trait_id| self.find_assoc_type(trait_id, assoc_name))
2690            .collect();
2691        match candidates.as_slice() {
2692            [assoc] => Ty::Assoc {
2693                on: Box::new(on),
2694                assoc: *assoc,
2695            },
2696            [] => {
2697                self.error(
2698                    "E0001",
2699                    format!("no associated type `{assoc_name}` on any bound of `{base}`"),
2700                    span,
2701                );
2702                Ty::Error
2703            }
2704            _ => {
2705                self.error(
2706                    "E0015",
2707                    format!(
2708                        "ambiguous associated type `{base}::{assoc_name}`: more than one \
2709                         bound of `{base}` declares it"
2710                    ),
2711                    span,
2712                );
2713                Ty::Error
2714            }
2715        }
2716    }
2717
2718    /// The `DefId` of the associated type named `name` declared directly by
2719    /// trait `trait_id`, if any.
2720    ///
2721    /// Searches `self.defs` (the resolver's output) rather than the
2722    /// incrementally-built `self.traits`: a trait's own method signature can
2723    /// name its own associated type (`Self::Item` in `Iterator::next`, which
2724    /// has no default body) before `collect_traits` has pushed *that very
2725    /// trait's* `hir::TraitDef`, and a bound can equally name a trait
2726    /// declared later in the file. That is the same ordering hazard
2727    /// `Checker::supertraits` exists to avoid for supertrait expansion (see
2728    /// its doc comment) — `self.defs` carries every `DefKind::AssocType` from
2729    /// resolution, before any of `collect_traits`'s incremental order exists.
2730    fn find_assoc_type(&self, trait_id: DefId, name: &str) -> Option<DefId> {
2731        self.defs
2732            .defs()
2733            .iter()
2734            .enumerate()
2735            .find_map(|(di, d)| match d.kind {
2736                DefKind::AssocType { trait_def } if trait_def == trait_id && d.name == name => {
2737                    Some(DefId(di as u32))
2738                }
2739                _ => None,
2740            })
2741    }
2742
2743    /// Substitute a call's type arguments into a stored signature type and
2744    /// resolve any projection the result contains — the call-site half of
2745    /// normalization seam 1.
2746    ///
2747    /// `subst` alone is not enough at a call site. A call's type arguments start
2748    /// as fresh inference variables that the argument unification then solves, so
2749    /// a projection on a generic parameter substitutes to `Assoc { on: Var(k) }`,
2750    /// which has no [`Ty::head`] and cannot be resolved. Applying the current
2751    /// substitution first turns it into `Assoc { on: <concrete> }`.
2752    ///
2753    /// **The `has_assoc` guard is not an optimization, and no test can kill it.**
2754    /// Applying the substitution early is, as far as I can reason, unobservable —
2755    /// `unify` walks through variables anyway, `show` applies before rendering,
2756    /// and `finalize_function` applies again at the end — and the full suite is
2757    /// green with the guard removed, so nothing pins it. It is here because that
2758    /// reasoning, not the compiler, is what makes the unguarded version safe: with
2759    /// the guard, a call whose signature has no projection takes exactly the plain
2760    /// `subst` path it took before this task, and the reasoning only has to hold
2761    /// for the projection case. Deliberate, unpinned, and recorded as such rather
2762    /// than left to look like a mutation that got away.
2763    ///
2764    /// This is also where the design's one admitted hole shows: the variable has
2765    /// to be *already solved*, which for a call means the argument that
2766    /// determines it must come first. `fn f<I: It>(y: I::Item, x: I)` still fails
2767    /// — see the design doc §4.2, whose claim that `Assoc { on: Var(_) }` is
2768    /// unreachable holds for receivers but not for this shape.
2769    ///
2770    /// # One known site that should call this and does not
2771    ///
2772    /// [`Checker::emit_inherent_call`] substitutes an inherent method's
2773    /// parameter types (`let expected = param.subst(&type_args)`) and its return
2774    /// type (`let ret = sig.ret.subst(&type_args)`) with **no** normalization,
2775    /// although it has already unified the receiver against the impl's self type
2776    /// a few lines above — so the projection's root is concrete by then and this
2777    /// helper's precondition is met. [`Checker::emit_trait_call`] routes the same
2778    /// two positions through here. The asymmetry rejects a valid program:
2779    ///
2780    /// ```text
2781    /// record W<T> { v: T }
2782    /// impl<I: Iterator> W<I> { fn echo(self, d: I::Item) -> I::Item { d } }
2783    /// // with `Cur: Iterator, Item = Bool` and `w: W<Cur>`:
2784    /// //   w.echo(true)
2785    /// //   => error[E0010]: argument has type `Bool` but `Cur::Item` was expected
2786    /// ```
2787    ///
2788    /// Measured, both directions: routing those two lines through `instantiate`
2789    /// makes that program compile and run, with the whole workspace suite still
2790    /// green and every gate configuration passing; reverting brings the `E0010`
2791    /// straight back. **Left unfixed on purpose.** It is a behaviour
2792    /// change — programs that are rejected today would start compiling — so it
2793    /// needs its own test and its own review, and it is *not* a defect this
2794    /// increment introduced: it needs only an impl-block bound plus Phase 2.2c
2795    /// associated types, no feature from the record-parameter-bounds work.
2796    ///
2797    /// This is recorded here because the enumeration of raw-`subst` sites
2798    /// reachable by a projection — the thing the 2.2c design doc's own
2799    /// correction says to consult *instead of* a seam count — was believed
2800    /// complete at five and is not. `emit_assoc_call` has the same textual
2801    /// shape but is a different case: with no receiver there is nothing to pin
2802    /// the impl's parameters, so it fails as `?N::Item` (this helper's admitted
2803    /// hole above), not as a missing normalization.
2804    fn instantiate(&mut self, ty: &Ty, args: &[Ty], icx: &InferCtx, span: Span) -> Ty {
2805        let ty = ty.subst(args);
2806        if !ty.has_assoc() {
2807            return ty;
2808        }
2809        self.normalize(&icx.apply(&ty), span)
2810    }
2811
2812    /// Resolve the associated-type projections in `ty` through the impl table —
2813    /// normalization seam 1 of three (design doc §4.1).
2814    ///
2815    /// A thin wrapper: [`hir::normalize_ty`] holds all of the logic, over a plain
2816    /// `&[hir::ImplInfo]` slice, so that monomorphization can call the identical
2817    /// code without reaching through a half-built `Checker`. All this adds is the
2818    /// diagnostic, which `nova-hir` cannot emit.
2819    ///
2820    /// **Precondition: `collect_impls` has returned.** It fills `self.impls`, and
2821    /// it runs before `collect_signatures` and before any body is checked, so every
2822    /// seam that exists today sees the complete table. Nothing enforces this, and
2823    /// it is easy to get wrong in one specific way: `collect_impls` pushes each
2824    /// `ImplInfo` at the *end* of its loop body, ten lines after it calls
2825    /// `check_impl_conformance`, so a `normalize` added inside conformance cannot
2826    /// see the bindings of the very impl it is checking. Measured — with both sides
2827    /// of the conformance return-type comparison normalized, `impl<T> It for W<T>
2828    /// { type Item = T  fn get_item(self) -> T }` still reports `E0072`.
2829    ///
2830    /// **This comment used to prescribe hoisting the push above the conformance
2831    /// call. Do not do that — it is measured to be wrong.** It fixes the case
2832    /// above and leaves a worse one: normalization consults the *whole* impl
2833    /// table, so a binding that resolves through a **later-declared** impl would
2834    /// still fail, which is a declaration-order dependency Nova deliberately does
2835    /// not have for impls. Exactly one test in the suite separates the two
2836    /// designs, and both of the obvious hand-written tests pass under the wrong
2837    /// one. The seam was instead added as a separate pass that runs once
2838    /// `collect_impls` has returned — see [`Checker::check_impl_method_signatures`],
2839    /// which follows [`Checker::check_supertrait_impls`]'s precedent for the same
2840    /// reason.
2841    ///
2842    /// **Never called from `unify`.** The whole design rests on projections being
2843    /// gone by the time the unifier sees a type; normalizing inside it would give
2844    /// the unifier the impl-table dependency it deliberately does not have.
2845    fn normalize(&mut self, ty: &Ty, span: Span) -> Ty {
2846        match hir::normalize_ty(ty, &self.impls) {
2847            Ok(t) => t,
2848            Err(hir::NormalizeOverflow { at, limit }) => {
2849                // Reported, not silently swallowed: `Ty::Error` unifies with
2850                // anything, so returning it without a diagnostic would turn a
2851                // hang into a wrong answer.
2852                //
2853                // **`E0078` is reachable from ordinary source**, and an earlier
2854                // version of this comment said the opposite ("reaching here means
2855                // some other path built a projection that does not converge — a
2856                // compiler defect"), which would send a maintainer hunting an ICE
2857                // that does not exist. `E0077` catches every cyclic *binding* and
2858                // poisons it, but a binding chain that is long or wide is not a
2859                // cycle, so `E0077` never sees it. Measured, both on programs
2860                // `nova check` otherwise accepts: a linear chain of 64 links
2861                // (`ok` at 63, `E0078` at 64), and `type A(k) =
2862                // Pair<Self::A(k+1), Self::A(k+1)>`, which resolves to a
2863                // `2^k`-node type.
2864                //
2865                // Those two are different failures with different fixes, so the
2866                // message distinguishes them rather than quoting one number for
2867                // both. Both name the type *this call* was asked about — which is
2868                // what the user wrote — not the intermediate the walk ran out on.
2869                let detail = match limit {
2870                    hir::NormalizeLimit::Depth => format!(
2871                        "the chain of bindings is more than {} deep, or does not terminate",
2872                        hir::NORMALIZE_DEPTH_LIMIT,
2873                    ),
2874                    hir::NormalizeLimit::Steps => format!(
2875                        "it resolves to more than {} type nodes",
2876                        hir::NORMALIZE_STEP_LIMIT,
2877                    ),
2878                };
2879                self.error(
2880                    "E0078",
2881                    format!(
2882                        "could not resolve the associated types in `{}`: {}",
2883                        display_ty(&at, self.defs),
2884                        detail,
2885                    ),
2886                    span,
2887                );
2888                Ty::Error
2889            }
2890        }
2891    }
2892
2893    // === Function bodies ===
2894
2895    fn check_function(&mut self, def_id: DefId, item_index: usize) -> Option<hir::Function> {
2896        let ast::Item::Function(f) = &self.file.items[item_index].value else {
2897            return None;
2898        };
2899        self.cur_module = self.defs.module_of(item_index);
2900        // A free function is not inside any impl, so `Self::Item` in its body
2901        // is a module-qualified path. Cleared rather than assumed, because
2902        // `check_method` may have run first and left an impl in scope.
2903        self.impl_self = None;
2904        let generics = generic_scope(&f.generics);
2905        self.enter_type_params(&[&f.generics]);
2906        self.check_fn_body(def_id, f, generics)
2907    }
2908
2909    /// Compile an impl or trait-default method body.
2910    fn check_method(&mut self, def_id: DefId) -> Option<hir::Function> {
2911        let loc = *self.method_locs.get(&def_id)?;
2912        self.cur_module = self.defs.module_of(loc.item_index);
2913        // `self.file` is a `&'a File`; copy the reference so the method
2914        // borrow is tied to `'a`, not to `self` (which we mutate below).
2915        let file: &'a ast::File = self.file;
2916        let f: &'a ast::Function = match loc.owner {
2917            MethodOwner::Impl => {
2918                let ast::Item::Impl(block) = &file.items[loc.item_index].value else {
2919                    return None;
2920                };
2921                &block.functions[loc.method_index]
2922            }
2923            MethodOwner::TraitDefault => {
2924                let ast::Item::Trait(decl) = &file.items[loc.item_index].value else {
2925                    return None;
2926                };
2927                match &decl.items[loc.method_index] {
2928                    TraitItem::Provided(f) => f,
2929                    // Neither has a body to check: a required method has none,
2930                    // and `loc.method_index` never actually points at an
2931                    // associated type — the resolver only ever creates a
2932                    // `MethodOwner::TraitDefault` `Def` for a `Provided` item —
2933                    // but the match must stay exhaustive over `TraitItem`.
2934                    TraitItem::Required(_) | TraitItem::AssocType { .. } => return None,
2935                }
2936            }
2937        };
2938        // `Self` inside an impl method's body means the impl's self type, the
2939        // same as in its signature; inside a trait default body it is the
2940        // trait's own implicit `Param(0)`, which `self_generic_scope` puts in
2941        // `generics` below instead.
2942        self.impl_self = match loc.owner {
2943            MethodOwner::Impl => self.impl_selves.get(&loc.item_index).cloned(),
2944            MethodOwner::TraitDefault => None,
2945        };
2946        let generics = match loc.owner {
2947            // An impl method sees the impl's generic parameters (`impl<T> …`) at
2948            // [0, impl_count), then its own generic parameters after them. Must
2949            // mirror `collect_impls`.
2950            MethodOwner::Impl => match &file.items[loc.item_index].value {
2951                ast::Item::Impl(block) => {
2952                    let mut scope = generic_scope(&block.generics);
2953                    let impl_count = block.generics.len() as u32;
2954                    for (j, g) in f.generics.iter().enumerate() {
2955                        scope.insert(g.name.value.clone(), impl_count + j as u32);
2956                    }
2957                    scope
2958                }
2959                _ => FxHashMap::default(),
2960            },
2961            // A trait default body sees `Self` at Param(0), then the method's own
2962            // generic parameters at Param(1..). Mirrors `collect_traits`.
2963            MethodOwner::TraitDefault => {
2964                let mut scope = self_generic_scope();
2965                for (j, g) in f.generics.iter().enumerate() {
2966                    scope.insert(g.name.value.clone(), 1 + j as u32);
2967                }
2968                scope
2969            }
2970        };
2971        match (loc.owner, &file.items[loc.item_index].value) {
2972            (MethodOwner::Impl, ast::Item::Impl(block)) => {
2973                self.enter_type_params(&[&block.generics, &f.generics])
2974            }
2975            _ => self.enter_type_params(&[&f.generics]),
2976        }
2977        self.check_fn_body(def_id, f, generics)
2978    }
2979
2980    /// Shared body-checking for functions and methods.
2981    fn check_fn_body(
2982        &mut self,
2983        def_id: DefId,
2984        f: &ast::Function,
2985        generics: FxHashMap<String, u32>,
2986    ) -> Option<hir::Function> {
2987        let mut sig = self.sigs.get(&def_id)?.clone();
2988        // Normalization seam: the declared return type. `impl It for K { type
2989        // Item = Int  fn get(self) -> Self::Item { 1 } }` writes `K::Item` where
2990        // the body produces `Int`, and the unifier cannot decide a projection
2991        // against a concrete type.
2992        //
2993        // Done once, here, rather than at the `unify` below, because `sig.ret`
2994        // is read three times for the same contract — `fcx.ret_ty` (which every
2995        // `return e` in the body checks against), this unify, and
2996        // `hir::Function::ret_ty` — and normalizing only one of them would let
2997        // `return 1` and a trailing `1` disagree about the same signature.
2998        //
2999        // `sig.params` for the same reason: each becomes a local's type below, so
3000        // `fn put(self, x: Self::Item) -> Int { x + 1 }` would type `x` as an
3001        // unresolved projection and reject every operation on it.
3002        sig.ret = self.normalize(&sig.ret, f.name.span);
3003        let params: Vec<Ty> = sig
3004            .params
3005            .iter()
3006            .map(|p| self.normalize(p, f.name.span))
3007            .collect();
3008        sig.params = params;
3009        let name = self.defs.def(def_id).name.clone();
3010        // `sig.ret` is the fn's *signature* type — already `Future<output>`
3011        // for an async fn, wrapped by `collect_signatures`/`collect_impls`.
3012        // The BODY produces the output directly (`async fn f() -> Int { 1 }`
3013        // has a body of type `Int`, not `Future<Int>`), so of the three
3014        // same-contract reads the comment above describes, the two that face
3015        // the body — `fcx.ret_ty` and this function's own unify below — must
3016        // name `body_ret_ty`, not `sig.ret`; only `hir::Function::ret_ty`
3017        // (the external-facing signature) keeps the wrapped type.
3018        let body_ret_ty = if f.is_async {
3019            match &sig.ret {
3020                Ty::Future(out) => (**out).clone(),
3021                // Unreachable by construction: `collect_signatures` and
3022                // `collect_impls` wrap an async fn/method's `ret` in
3023                // `Ty::Future` before it ever reaches `self.sigs`, and
3024                // `normalize_within`'s `Future` arm recurses into the
3025                // payload without changing the top-level constructor. Matched
3026                // defensively rather than unwrapped, so a future change that
3027                // breaks the invariant degrades instead of panicking in this
3028                // library path — the same discipline `mir_ty` documents for
3029                // its own should-be-impossible arm.
3030                other => other.clone(),
3031            }
3032        } else {
3033            sig.ret.clone()
3034        };
3035        let mut fcx = FnCtx {
3036            icx: InferCtx::default(),
3037            locals: Vec::new(),
3038            scopes: vec![FxHashMap::default()],
3039            generics,
3040            param_bounds: sig.bounds.clone(),
3041            ret_ty: body_ret_ty.clone(),
3042            loop_depth: 0,
3043            in_async: f.is_async,
3044            pending_closures: Vec::new(),
3045        };
3046        for (p, ty) in f.params.iter().zip(sig.params.iter()) {
3047            self.bind_local(
3048                &mut fcx,
3049                p.name.value.clone(),
3050                ty.clone(),
3051                p.is_mut,
3052                p.name.span,
3053                Binding::Param,
3054            );
3055        }
3056
3057        let body = self.check_block(&mut fcx, &f.body.value, f.body.span);
3058        if !fcx.icx.unify(&body.ty, &body_ret_ty) {
3059            let span = body_result_span(&f.body);
3060            self.error(
3061                "E0010",
3062                format!(
3063                    "`{}` should return `{}` but its body has type `{}`",
3064                    name,
3065                    self.show(&body_ret_ty, &fcx),
3066                    self.show(&body.ty, &fcx),
3067                ),
3068                span,
3069            );
3070        }
3071
3072        let names = param_names(&fcx.generics);
3073        let mut func = hir::Function {
3074            def_id,
3075            name,
3076            generics: sig.generics,
3077            bounds: sig.bounds,
3078            takes_env: false,
3079            capture_count: 0,
3080            params: f.params.len() as u32,
3081            locals: fcx.locals,
3082            ret_ty: sig.ret,
3083            is_async: f.is_async,
3084            body,
3085            span: f.name.span,
3086        };
3087        self.finalize_function(&mut func, &fcx.icx);
3088        self.note_local_types(&func.locals, &names);
3089        // Finalize and emit any closures/wrappers lifted from this body,
3090        // using the same inference context so their types resolve.
3091        let mut closures = std::mem::take(&mut fcx.pending_closures);
3092        for c in &mut closures {
3093            self.finalize_function(c, &fcx.icx);
3094            self.note_local_types(&c.locals, &names);
3095        }
3096        self.extra_functions.append(&mut closures);
3097        Some(func)
3098    }
3099
3100    /// Allocate a fresh synthetic `DefId` for a closure/wrapper function.
3101    fn fresh_closure_def(&mut self) -> DefId {
3102        let id = DefId(self.next_closure_def);
3103        self.next_closure_def += 1;
3104        id
3105    }
3106
3107    /// Compile a constant as a zero-argument function returning its value.
3108    fn check_const(&mut self, def_id: DefId, item_index: usize) -> Option<hir::Function> {
3109        // Copy the `&'a File` reference so the value borrow outlives `&mut self`.
3110        let file: &'a ast::File = self.file;
3111        let ast::Item::Const(c) = &file.items[item_index].value else {
3112            return None;
3113        };
3114        self.cur_module = self.defs.module_of(item_index);
3115        // A top-level `const` is not inside any impl (an impl's own `const`
3116        // items are never collected at all — `ast::ImplBlock::consts` has no
3117        // consumer), so nothing here can see an impl's `Self`.
3118        self.impl_self = None;
3119        self.enter_type_params(&[]);
3120        let value_ast = &c.value;
3121        let sig = self.sigs.get(&def_id)?.clone();
3122        let name = self.defs.def(def_id).name.clone();
3123        let mut fcx = FnCtx {
3124            icx: InferCtx::default(),
3125            locals: Vec::new(),
3126            scopes: vec![FxHashMap::default()],
3127            generics: FxHashMap::default(),
3128            param_bounds: Vec::new(),
3129            ret_ty: sig.ret.clone(),
3130            loop_depth: 0,
3131            // A `const` value has no surface syntax for `async` (`ast::ConstDecl`
3132            // has no `is_async` field), so `.await` is always illegal in one.
3133            in_async: false,
3134            pending_closures: Vec::new(),
3135        };
3136        let value = self.check_expr(&mut fcx, value_ast);
3137        if !fcx.icx.unify(&value.ty, &sig.ret) {
3138            self.error(
3139                "E0010",
3140                format!(
3141                    "constant `{name}` has type `{}` but is declared `{}`",
3142                    self.show(&value.ty, &fcx),
3143                    self.show(&sig.ret, &fcx),
3144                ),
3145                value_ast.span,
3146            );
3147        }
3148        let mut func = hir::Function {
3149            def_id,
3150            name,
3151            generics: 0,
3152            bounds: Vec::new(),
3153            takes_env: false,
3154            capture_count: 0,
3155            params: 0,
3156            locals: fcx.locals,
3157            ret_ty: sig.ret,
3158            is_async: false,
3159            body: value,
3160            span: value_ast.span,
3161        };
3162        self.finalize_function(&mut func, &fcx.icx);
3163        self.note_local_types(&func.locals, &[]);
3164        let mut closures = std::mem::take(&mut fcx.pending_closures);
3165        for cl in &mut closures {
3166            self.finalize_function(cl, &fcx.icx);
3167            self.note_local_types(&cl.locals, &[]);
3168        }
3169        self.extra_functions.append(&mut closures);
3170        Some(func)
3171    }
3172
3173    /// Report `E0081` for a constant defined (transitively) in terms of
3174    /// itself — otherwise the generated zero-arg functions would recurse
3175    /// forever at run time.
3176    fn check_const_cycles(&mut self, functions: &[hir::Function]) {
3177        let const_ids: FxHashSet<DefId> = self.const_ids().into_iter().map(|(id, _)| id).collect();
3178        // Edges: const → the constants its value references.
3179        let mut edges: FxHashMap<DefId, Vec<DefId>> = FxHashMap::default();
3180        for f in functions {
3181            if !const_ids.contains(&f.def_id) {
3182                continue;
3183            }
3184            let mut callees = Vec::new();
3185            collect_const_calls(&f.body, &const_ids, &mut callees);
3186            edges.insert(f.def_id, callees);
3187        }
3188        // A constant is cyclic iff it can reach itself through the edges.
3189        let mut cyclic: Vec<DefId> = const_ids
3190            .iter()
3191            .copied()
3192            .filter(|&c| reaches_self(c, &edges))
3193            .collect();
3194        cyclic.sort();
3195        for c in cyclic {
3196            let name = &self.defs.def(c).name;
3197            self.diagnostics.push(
3198                Diagnostic::error(
3199                    "E0081",
3200                    format!("constant `{name}` is defined in terms of itself"),
3201                )
3202                .with_primary_label(self.defs.def(c).span, "cyclic constant")
3203                .with_note("a constant's value cannot depend on its own value".to_string()),
3204            );
3205        }
3206    }
3207
3208    /// Apply the final substitution everywhere and report residual
3209    /// inference variables as E0011.
3210    fn finalize_function(&mut self, func: &mut hir::Function, icx: &InferCtx) {
3211        // Spec 3.2 §4.1: what the probe met in this function, read with the
3212        // function's final substitution. The function is finalized before
3213        // the closures lifted from it, so it takes what the probe met even
3214        // inside one of them.
3215        if let Some(pending) = self.probe_pending.take() {
3216            let receiver = icx.apply(&pending.receiver);
3217            self.probe_result.members =
3218                self.probe_members(&receiver, &pending.bounds, pending.module);
3219            self.probe_result.receiver = Some(receiver);
3220        }
3221        if let Some(locals) = self.probe_locals_pending.take() {
3222            self.probe_result.locals = locals
3223                .into_iter()
3224                .map(|(name, ty)| (name, icx.apply(&ty)))
3225                .collect();
3226        }
3227        let mut residual: Vec<Span> = Vec::new();
3228        // Resolve the return type — for closures it may be an inference
3229        // variable (normal functions carry a concrete signature type).
3230        func.ret_ty = icx.apply(&func.ret_ty);
3231        if func.ret_ty.has_vars() {
3232            residual.push(func.span);
3233        }
3234        for local in &mut func.locals {
3235            local.ty = icx.apply(&local.ty);
3236            if local.ty.has_vars() {
3237                residual.push(local.span);
3238            }
3239        }
3240        finalize_expr(&mut func.body, icx, &mut residual);
3241        for span in residual {
3242            self.error(
3243                "E0011",
3244                "cannot infer the type here; add a type annotation",
3245                span,
3246            );
3247        }
3248    }
3249
3250    fn check_block(&mut self, fcx: &mut FnCtx, block: &ast::Block, span: Span) -> hir::Expr {
3251        fcx.scopes.push(FxHashMap::default());
3252        let mut stmts = Vec::new();
3253        for stmt in &block.stmts {
3254            // Decision 4: the probe, between the block's start and this
3255            // statement, sees the scope as it is before the statement.
3256            self.probe_locals(fcx, span.file, span.start, stmt.span.start);
3257            match &stmt.value {
3258                ast::Stmt::Let {
3259                    is_mut,
3260                    pattern,
3261                    ty,
3262                    init,
3263                } => {
3264                    let Some(init) = init else {
3265                        self.unsupported(stmt.span, "`let` without an initializer");
3266                        continue;
3267                    };
3268                    let mut value = self.check_expr(fcx, init);
3269                    if let Some(annot) = ty {
3270                        let annot_ty =
3271                            self.convert_ty(annot, &fcx.generics.clone(), &fcx.param_bounds);
3272                        // Normalization seam: a projection written in a `let`
3273                        // annotation. Needed twice over — for the unify just
3274                        // below, and because `value.ty = annot_ty` *replaces* the
3275                        // initializer's type with the annotation, so an
3276                        // unnormalized projection would become the binding's type
3277                        // and propagate to every later use of the local.
3278                        let annot_ty = self.normalize(&annot_ty, annot.span);
3279                        if !fcx.icx.unify(&value.ty, &annot_ty) {
3280                            self.error(
3281                                "E0010",
3282                                format!(
3283                                    "type mismatch: expected `{}`, found `{}`",
3284                                    self.show(&annot_ty, fcx),
3285                                    self.show(&value.ty, fcx),
3286                                ),
3287                                init.span,
3288                            );
3289                        }
3290                        value.ty = annot_ty;
3291                    }
3292                    let (name, pat_mut, name_span) = match &pattern.value {
3293                        ast::Pattern::Ident { is_mut, name } => {
3294                            (name.value.clone(), *is_mut, name.span)
3295                        }
3296                        ast::Pattern::Wildcard => ("_".to_string(), false, pattern.span),
3297                        _ => {
3298                            self.error(
3299                                "E0022",
3300                                "only irrefutable patterns (a name or `_`) are allowed in `let`",
3301                                pattern.span,
3302                            );
3303                            ("_".to_string(), false, pattern.span)
3304                        }
3305                    };
3306                    let local = self.bind_local(
3307                        fcx,
3308                        name,
3309                        value.ty.clone(),
3310                        *is_mut || pat_mut,
3311                        name_span,
3312                        Binding::Let,
3313                    );
3314                    stmts.push(hir::Expr {
3315                        kind: hir::ExprKind::Let {
3316                            local,
3317                            init: Box::new(value),
3318                        },
3319                        ty: Ty::Unit,
3320                        span: stmt.span,
3321                    });
3322                }
3323                ast::Stmt::Expr(e) => {
3324                    stmts.push(self.check_expr(fcx, e));
3325                }
3326                ast::Stmt::Item(_) => {
3327                    self.unsupported(stmt.span, "items nested inside function bodies");
3328                }
3329            }
3330        }
3331        let trailing = block
3332            .trailing
3333            .as_ref()
3334            .map(|e| Box::new(self.check_expr(fcx, e)));
3335        // Decision 4: anywhere else in the block sees all of its locals.
3336        self.probe_locals(fcx, span.file, span.start, span.end);
3337        fcx.scopes.pop();
3338
3339        let ty = trailing.as_ref().map(|e| e.ty.clone()).unwrap_or(Ty::Unit);
3340        hir::Expr {
3341            kind: hir::ExprKind::Block { stmts, trailing },
3342            ty,
3343            span,
3344        }
3345    }
3346
3347    fn check_expr(&mut self, fcx: &mut FnCtx, expr: &Spanned<ast::Expr>) -> hir::Expr {
3348        let span = expr.span;
3349        match &expr.value {
3350            ast::Expr::Lit(lit) => lit_expr(lit, span),
3351            ast::Expr::StringInterp(parts) => self.check_interp(fcx, parts, span),
3352            ast::Expr::Path(path) => self.check_path(fcx, path, span),
3353            ast::Expr::Call { callee, args } => self.check_call(fcx, callee, args, span),
3354            ast::Expr::Binary { op, lhs, rhs } => self.check_binary(fcx, *op, lhs, rhs, span),
3355            ast::Expr::Unary { op, expr: inner } => self.check_unary(fcx, *op, inner, span),
3356            ast::Expr::Block(block) => self.check_block(fcx, block, span),
3357            ast::Expr::If { cond, then, else_ } => {
3358                let cond = self.check_expr(fcx, cond);
3359                self.expect_ty(fcx, &cond, &Ty::Bool, "an `if` condition");
3360                let then_expr = self.check_block(fcx, &then.value, then.span);
3361                match else_ {
3362                    Some(else_branch) => {
3363                        let else_expr = self.check_expr(fcx, else_branch);
3364                        // The branches' types unify only when neither
3365                        // diverges; the `if`'s type is the non-diverging
3366                        // branch (a diverging branch imposes no constraint —
3367                        // `Never` is the bottom type).
3368                        let then_d = matches!(fcx.icx.apply(&then_expr.ty), Ty::Never);
3369                        let else_d = matches!(fcx.icx.apply(&else_expr.ty), Ty::Never);
3370                        if !then_d && !else_d && !fcx.icx.unify(&then_expr.ty, &else_expr.ty) {
3371                            self.error(
3372                                "E0010",
3373                                format!(
3374                                    "`if` and `else` have incompatible types: `{}` vs `{}`",
3375                                    self.show(&then_expr.ty, fcx),
3376                                    self.show(&else_expr.ty, fcx),
3377                                ),
3378                                else_expr.span,
3379                            );
3380                        }
3381                        let ty = if then_d {
3382                            else_expr.ty.clone()
3383                        } else {
3384                            then_expr.ty.clone()
3385                        };
3386                        hir::Expr {
3387                            kind: hir::ExprKind::If {
3388                                cond: Box::new(cond),
3389                                then: Box::new(then_expr),
3390                                else_: Some(Box::new(else_expr)),
3391                            },
3392                            ty,
3393                            span,
3394                        }
3395                    }
3396                    None => {
3397                        if !fcx.icx.unify(&then_expr.ty, &Ty::Unit) {
3398                            self.error(
3399                                "E0010",
3400                                format!(
3401                                    "an `if` without `else` must have type `()`, found `{}`",
3402                                    self.show(&then_expr.ty, fcx),
3403                                ),
3404                                then_expr.span,
3405                            );
3406                        }
3407                        hir::Expr {
3408                            kind: hir::ExprKind::If {
3409                                cond: Box::new(cond),
3410                                then: Box::new(then_expr),
3411                                else_: None,
3412                            },
3413                            ty: Ty::Unit,
3414                            span,
3415                        }
3416                    }
3417                }
3418            }
3419            ast::Expr::While { cond, body } => {
3420                // The condition is inside the loop for break/continue targeting
3421                // (e.g. `while (if done { break } else { c }) { ... }`).
3422                fcx.loop_depth += 1;
3423                let cond = self.check_expr(fcx, cond);
3424                self.expect_ty(fcx, &cond, &Ty::Bool, "a `while` condition");
3425                let body = self.check_block(fcx, &body.value, body.span);
3426                fcx.loop_depth -= 1;
3427                hir::Expr {
3428                    kind: hir::ExprKind::While {
3429                        cond: Box::new(cond),
3430                        body: Box::new(body),
3431                    },
3432                    ty: Ty::Unit,
3433                    span,
3434                }
3435            }
3436            ast::Expr::Match { scrutinee, arms } => self.check_match(fcx, scrutinee, arms, span),
3437            ast::Expr::Return(value) => {
3438                let value = value.as_ref().map(|v| self.check_expr(fcx, v));
3439                let value_ty = value.as_ref().map(|v| v.ty.clone()).unwrap_or(Ty::Unit);
3440                let ret_ty = fcx.ret_ty.clone();
3441                if !fcx.icx.unify(&value_ty, &ret_ty) {
3442                    self.error(
3443                        "E0010",
3444                        format!(
3445                            "`return` value has type `{}` but the function returns `{}`",
3446                            self.show(&value_ty, fcx),
3447                            self.show(&ret_ty, fcx),
3448                        ),
3449                        span,
3450                    );
3451                }
3452                hir::Expr {
3453                    kind: hir::ExprKind::Return(value.map(Box::new)),
3454                    ty: Ty::Never,
3455                    span,
3456                }
3457            }
3458            ast::Expr::Assign { op, lhs, rhs } => self.check_assign(fcx, *op, lhs, rhs, span),
3459            ast::Expr::Tuple(items) if items.is_empty() => hir::Expr {
3460                kind: hir::ExprKind::Unit,
3461                ty: Ty::Unit,
3462                span,
3463            },
3464            ast::Expr::For {
3465                pattern,
3466                iter,
3467                body,
3468            } => self.check_for(fcx, pattern, iter, body, span),
3469            ast::Expr::Range { .. } => {
3470                self.unsupported(span, "ranges outside a `for` loop");
3471                error_expr(span)
3472            }
3473            ast::Expr::Break(value) => {
3474                if value.is_some() {
3475                    self.unsupported(span, "`break` with a value");
3476                    return error_expr(span);
3477                }
3478                if fcx.loop_depth == 0 {
3479                    self.error("E0080", "`break` outside of a loop", span);
3480                    return error_expr(span);
3481                }
3482                hir::Expr {
3483                    kind: hir::ExprKind::Break,
3484                    ty: Ty::Never,
3485                    span,
3486                }
3487            }
3488            ast::Expr::Continue => {
3489                if fcx.loop_depth == 0 {
3490                    self.error("E0080", "`continue` outside of a loop", span);
3491                    return error_expr(span);
3492                }
3493                hir::Expr {
3494                    kind: hir::ExprKind::Continue,
3495                    ty: Ty::Never,
3496                    span,
3497                }
3498            }
3499            ast::Expr::Array(elems) => self.check_array_literal(fcx, elems, span),
3500            ast::Expr::ArrayRepeat { init, len } => self.check_array_repeat(fcx, init, len, span),
3501            ast::Expr::Index { target, index } => self.check_index(fcx, target, index, span),
3502            // --- deferred constructs ---
3503            ast::Expr::Tuple(_) => self.unsupported_expr(span, "tuple expressions"),
3504            ast::Expr::Closure { params, ret, body } => {
3505                self.check_closure(fcx, params, ret, body, span)
3506            }
3507            ast::Expr::Record { path, fields, base } => {
3508                self.check_record_literal(fcx, path, fields, base.as_deref(), span)
3509            }
3510            ast::Expr::Field { target, field } => self.check_field(fcx, target, field, span),
3511            ast::Expr::Try(_) => self.unsupported_expr(span, "the `?` operator"),
3512            ast::Expr::Await(inner) => {
3513                let e = self.check_expr(fcx, inner);
3514                if !fcx.in_async {
3515                    self.diagnostics.push(
3516                        Diagnostic::error(
3517                            "E0086",
3518                            "`.await` is only allowed inside an `async fn`".to_string(),
3519                        )
3520                        .with_primary_label(span, "await outside an async function")
3521                        .with_note(
3522                            "make the enclosing function `async`, or drive the \
3523                             future to completion with `block_on`"
3524                                .to_string(),
3525                        ),
3526                    );
3527                    return error_expr(span);
3528                }
3529                let out = match fcx.icx.apply(&e.ty) {
3530                    Ty::Future(out) => (*out).clone(),
3531                    Ty::Error => return error_expr(span),
3532                    other => {
3533                        self.diagnostics.push(
3534                            Diagnostic::error(
3535                                "E0087",
3536                                format!(
3537                                    "`.await` expects a future, found `{}`",
3538                                    display_ty(&other, self.defs)
3539                                ),
3540                            )
3541                            .with_primary_label(inner.span, "not a future"),
3542                        );
3543                        return error_expr(span);
3544                    }
3545                };
3546                hir::Expr {
3547                    kind: hir::ExprKind::Await(Box::new(e)),
3548                    ty: out,
3549                    span,
3550                }
3551            }
3552            ast::Expr::Cast { .. } => self.unsupported_expr(span, "`as` casts"),
3553        }
3554    }
3555
3556    fn check_interp(
3557        &mut self,
3558        fcx: &mut FnCtx,
3559        parts: &[ast::StringPart],
3560        span: Span,
3561    ) -> hir::Expr {
3562        let mut pieces = Vec::new();
3563        for part in parts {
3564            match part {
3565                ast::StringPart::Lit(s) => pieces.push(hir::Expr {
3566                    kind: hir::ExprKind::StrLit(s.clone()),
3567                    ty: Ty::String,
3568                    span,
3569                }),
3570                ast::StringPart::Expr(e) => {
3571                    let value = self.check_expr(fcx, e);
3572                    let resolved = fcx.icx.apply(&value.ty);
3573                    match resolved {
3574                        Ty::String => pieces.push(value),
3575                        Ty::Int | Ty::Float | Ty::Bool | Ty::Char | Ty::Error => {
3576                            let part_span = value.span;
3577                            pieces.push(hir::Expr {
3578                                kind: hir::ExprKind::ToStr(Box::new(value)),
3579                                ty: Ty::String,
3580                                span: part_span,
3581                            });
3582                        }
3583                        other => {
3584                            // Bridge to a user-defined `Display` trait: if the
3585                            // value's type has a `fmt(self) -> String` in scope,
3586                            // interpolation calls it.
3587                            match self.try_display(fcx, value, e, &other) {
3588                                Some(fmt_call) => pieces.push(fmt_call),
3589                                None => {
3590                                    self.error(
3591                                        "E0013",
3592                                        format!(
3593                                            "`{}` cannot be interpolated into a string; \
3594                                             implement `Display` (fmt(self) -> String) for it",
3595                                            self.show(&other, fcx),
3596                                        ),
3597                                        // value was moved into try_display on the
3598                                        // Some path; use the interp span here.
3599                                        span,
3600                                    );
3601                                }
3602                            }
3603                        }
3604                    }
3605                }
3606            }
3607        }
3608        hir::Expr {
3609            kind: hir::ExprKind::StrConcat(pieces),
3610            ty: Ty::String,
3611            span,
3612        }
3613    }
3614
3615    fn check_path(&mut self, fcx: &mut FnCtx, path: &ast::Path, span: Span) -> hir::Expr {
3616        let callee_args = self.qualified_callee.take();
3617        if path.segments.len() == 2 {
3618            // `Type::Variant` — qualified variant reference.
3619            let ty_name = path.segments[0].value.as_str();
3620            let v_name = path.segments[1].value.as_str();
3621            if let Some(def_id) = self.defs.resolve_type(self.cur_module, ty_name) {
3622                self.note_use(path.segments[0].span, Target::Def(def_id));
3623                if let Some(vi) = self.variant_index(def_id, v_name) {
3624                    self.note_use(path.segments[1].span, Target::Variant(def_id, vi as u32));
3625                    return self.make_variant(fcx, def_id, vi, Vec::new(), span);
3626                }
3627                self.error(
3628                    "E0001",
3629                    format!("no variant `{v_name}` on type `{ty_name}`"),
3630                    span,
3631                );
3632                return error_expr(span);
3633            }
3634            self.unsupported(span, "module-qualified paths");
3635            // Spec 3.4b §4.2, §4.3: a qualifier this module never imported,
3636            // or misspelled.
3637            self.name_fixes(
3638                path.segments[0].clone(),
3639                Need::Qualifier {
3640                    member: v_name.to_string(),
3641                    call: callee_args,
3642                },
3643                fcx.generics.keys().cloned().collect(),
3644            );
3645            return error_expr(span);
3646        }
3647        if path.segments.len() != 1 {
3648            self.unsupported(span, "module-qualified paths");
3649            return error_expr(span);
3650        }
3651        let name = path.segments[0].value.as_str();
3652        // Spec 3.2 §4.1: a name at the probe sees these locals.
3653        self.probe_locals(fcx, span.file, span.start, span.end);
3654        if let Some(local) = fcx.lookup(name) {
3655            self.note_use(
3656                path.segments[0].span,
3657                Target::Local(fcx.locals[local.0 as usize].span),
3658            );
3659            let ty = fcx.locals[local.0 as usize].ty.clone();
3660            return hir::Expr {
3661                kind: hir::ExprKind::Local(local),
3662                ty,
3663                span,
3664            };
3665        }
3666        match self.defs.resolve_value(self.cur_module, name) {
3667            Some(Res::Def(def_id)) => match &self.defs.def(def_id).kind {
3668                DefKind::Fn { .. } => {
3669                    self.note_use(path.segments[0].span, Target::Def(def_id));
3670                    let Some(sig) = self.sigs.get(&def_id).cloned() else {
3671                        return error_expr(span);
3672                    };
3673                    let type_args: Vec<Ty> = (0..sig.generics).map(|_| fcx.icx.fresh()).collect();
3674                    let param_types: Vec<Ty> =
3675                        sig.params.iter().map(|p| p.subst(&type_args)).collect();
3676                    let ret = sig.ret.subst(&type_args);
3677                    // A bare function used as a value becomes a fat pointer to
3678                    // a synthesized wrapper `(env, params) { def(params) }`.
3679                    self.make_fn_wrapper(fcx, def_id, type_args, param_types, ret, span)
3680                }
3681                DefKind::Const { .. } => {
3682                    self.note_use(path.segments[0].span, Target::Def(def_id));
3683                    // A constant reference is a call to its zero-arg function.
3684                    let ret = self
3685                        .sigs
3686                        .get(&def_id)
3687                        .map(|s| s.ret.clone())
3688                        .unwrap_or(Ty::Error);
3689                    hir::Expr {
3690                        kind: hir::ExprKind::Call {
3691                            func: hir::Callee::Def(def_id),
3692                            type_args: Vec::new(),
3693                            args: Vec::new(),
3694                        },
3695                        ty: ret,
3696                        span,
3697                    }
3698                }
3699                _ => {
3700                    self.unsupported(span, "referencing this kind of definition as a value");
3701                    error_expr(span)
3702                }
3703            },
3704            Some(Res::Variant(sum_id, vi)) => {
3705                self.note_use(path.segments[0].span, Target::Variant(sum_id, vi as u32));
3706                self.make_variant(fcx, sum_id, vi, Vec::new(), span)
3707            }
3708            Some(Res::Builtin(_)) => {
3709                self.unsupported(span, "using builtins as values");
3710                error_expr(span)
3711            }
3712            None => {
3713                self.error("E0001", format!("cannot find `{name}` in this scope"), span);
3714                let nearby = fixes::visible_locals(fcx);
3715                self.name_fixes(path.segments[0].clone(), Need::Value, nearby);
3716                error_expr(span)
3717            }
3718        }
3719    }
3720
3721    fn check_call(
3722        &mut self,
3723        fcx: &mut FnCtx,
3724        callee: &Spanned<ast::Expr>,
3725        args: &[Spanned<ast::Expr>],
3726        span: Span,
3727    ) -> hir::Expr {
3728        // Method call: `receiver.method(args)`.
3729        if let ast::Expr::Field { target, field } = &callee.value {
3730            let receiver = self.check_expr(fcx, target);
3731            let checked: Vec<hir::Expr> = args.iter().map(|a| self.check_expr(fcx, a)).collect();
3732            // `target` (the receiver's *AST*) rides along because the mutable-
3733            // receiver rule needs `place_root`, which walks AST projections; the
3734            // checked `hir::Expr` has already lost that shape.
3735            return self.check_method_call(fcx, receiver, target, field, checked, span);
3736        }
3737
3738        // Direct-call forms: a path naming a function, variant, or builtin.
3739        if let ast::Expr::Path(path) = &callee.value {
3740            if path.segments.len() == 1 {
3741                let name = path.segments[0].value.as_str();
3742                if fcx.lookup(name).is_none() {
3743                    match self.defs.resolve_value(self.cur_module, name) {
3744                        Some(Res::Def(def_id)) => {
3745                            if matches!(
3746                                self.defs.def(def_id).kind,
3747                                DefKind::Fn { .. } | DefKind::ExternFn { .. }
3748                            ) {
3749                                self.note_use(path.segments[0].span, Target::Def(def_id));
3750                                return self.check_direct_call(fcx, def_id, args, span);
3751                            }
3752                        }
3753                        Some(Res::Variant(sum_id, vi)) => {
3754                            self.note_use(
3755                                path.segments[0].span,
3756                                Target::Variant(sum_id, vi as u32),
3757                            );
3758                            let checked: Vec<hir::Expr> =
3759                                args.iter().map(|a| self.check_expr(fcx, a)).collect();
3760                            return self.make_variant(fcx, sum_id, vi, checked, span);
3761                        }
3762                        Some(Res::Builtin(b)) => {
3763                            self.note_use(path.segments[0].span, Target::Builtin(b));
3764                            return self.check_builtin_call(fcx, b, args, span);
3765                        }
3766                        None => {
3767                            self.error(
3768                                "E0001",
3769                                format!("cannot find function `{name}` in this scope"),
3770                                callee.span,
3771                            );
3772                            let nearby = fixes::visible_locals(fcx);
3773                            self.name_fixes(
3774                                path.segments[0].clone(),
3775                                Need::Call(args.len()),
3776                                nearby,
3777                            );
3778                            return error_expr(span);
3779                        }
3780                    }
3781                }
3782            } else if path.segments.len() == 2 {
3783                // `Type::Variant(args)`, `Type::assoc_fn(args)`, or `T::assoc_fn(args)`.
3784                let ty_name = path.segments[0].value.as_str();
3785                let name = path.segments[1].value.as_str();
3786                // `T::zero()` where `T` is a generic parameter: dispatch through
3787                // its bounds, exactly as a bounded instance method call does.
3788                // Checked before the type namespace so a generic parameter
3789                // shadows a same-named type, matching `convert_ty`.
3790                if let Some(&k) = fcx.generics.get(ty_name) {
3791                    if let Some(t) = self.type_param_target(ty_name) {
3792                        self.note_use(path.segments[0].span, t);
3793                    }
3794                    let matches: Vec<(DefId, u32)> = fcx
3795                        .param_bounds
3796                        .get(k as usize)
3797                        .into_iter()
3798                        .flatten()
3799                        .filter_map(|&tid| self.trait_assoc_fn_index(tid, name).map(|i| (tid, i)))
3800                        .collect();
3801                    let checked: Vec<hir::Expr> =
3802                        args.iter().map(|a| self.check_expr(fcx, a)).collect();
3803                    return match matches.as_slice() {
3804                        [(tid, idx)] => {
3805                            self.note_use(path.segments[1].span, Target::TraitMethod(*tid, *idx));
3806                            self.emit_trait_call(
3807                                fcx,
3808                                *tid,
3809                                *idx,
3810                                TraitCallSelf::Qualifier(Ty::Param(k)),
3811                                checked,
3812                                span,
3813                            )
3814                        }
3815                        [] => {
3816                            // A bound may still declare `name` — just as a
3817                            // method with a `self` receiver rather than an
3818                            // associated function. "none of its bounds
3819                            // declares one" would be false in that case, so
3820                            // check for it and name the real reason.
3821                            let declared_as_method = fcx
3822                                .param_bounds
3823                                .get(k as usize)
3824                                .into_iter()
3825                                .flatten()
3826                                .any(|&tid| self.trait_method_index(tid, name).is_some());
3827                            if declared_as_method {
3828                                self.error(
3829                                    "E0001",
3830                                    format!(
3831                                        "no associated function `{name}` on generic parameter \
3832                                         `{ty_name}`; its bound declares `{name}` as a method \
3833                                         with a `self` receiver, so it must be called on a value"
3834                                    ),
3835                                    callee.span,
3836                                );
3837                            } else {
3838                                self.error(
3839                                    "E0001",
3840                                    format!(
3841                                        "no associated function `{name}` on generic parameter \
3842                                         `{ty_name}`; none of its bounds declares one"
3843                                    ),
3844                                    callee.span,
3845                                );
3846                            }
3847                            error_expr(span)
3848                        }
3849                        _ => {
3850                            self.error(
3851                                "E0015",
3852                                format!(
3853                                    "ambiguous associated function `{ty_name}::{name}`: more \
3854                                     than one bound of `{ty_name}` provides it"
3855                                ),
3856                                callee.span,
3857                            );
3858                            error_expr(span)
3859                        }
3860                    };
3861                }
3862                // `Type::Variant(args)` keeps its existing meaning. Only a
3863                // nominal type has variants, so this stays on the `resolve_type`
3864                // path and is tried before any associated function.
3865                if let Some(def_id) = self.defs.resolve_type(self.cur_module, ty_name) {
3866                    if let Some(vi) = self.variant_index(def_id, name) {
3867                        self.note_use(path.segments[0].span, Target::Def(def_id));
3868                        self.note_use(path.segments[1].span, Target::Variant(def_id, vi as u32));
3869                        let checked: Vec<hir::Expr> =
3870                            args.iter().map(|a| self.check_expr(fcx, a)).collect();
3871                        return self.make_variant(fcx, def_id, vi, checked, span);
3872                    }
3873                }
3874                // Then an associated function: on an inherent impl first (they
3875                // take priority over trait methods, as for instance calls), then
3876                // through a trait impl for the qualifier's type. `Int::zero()`
3877                // reaches both only via `qualifier_self_ty` — a primitive name is
3878                // absent from the resolver's type namespace entirely.
3879                if let Some(self_ty) = self.qualifier_self_ty(fcx, ty_name) {
3880                    let inherent = self_ty
3881                        .head()
3882                        .map(|head| self.find_assoc_fns(head, name))
3883                        .unwrap_or_default();
3884                    match inherent.as_slice() {
3885                        [assoc_id] => {
3886                            let assoc_id = *assoc_id;
3887                            self.note_qualifier(&path.segments[0]);
3888                            self.note_use(path.segments[1].span, Target::Def(assoc_id));
3889                            let checked: Vec<hir::Expr> =
3890                                args.iter().map(|a| self.check_expr(fcx, a)).collect();
3891                            return self.emit_assoc_call(fcx, assoc_id, checked, span);
3892                        }
3893                        [] => {}
3894                        // Two inherent impls sharing the head both declare it.
3895                        // `check_impl_coherence` lets a *disjoint concrete*
3896                        // pair (`impl Box<Int>` / `impl Box<Bool>`) through, so
3897                        // this is the only guard against declaration-order
3898                        // dispatch here.
3899                        _ => {
3900                            self.error(
3901                                "E0015",
3902                                format!(
3903                                    "ambiguous associated function `{ty_name}::{name}`: more \
3904                                     than one inherent impl of `{ty_name}` provides it"
3905                                ),
3906                                callee.span,
3907                            );
3908                            return error_expr(span);
3909                        }
3910                    }
3911                    let matches = self.find_trait_assoc_fns(&self_ty, name);
3912                    match matches.as_slice() {
3913                        [(tid, idx)] => {
3914                            let (tid, idx) = (*tid, *idx);
3915                            self.note_qualifier(&path.segments[0]);
3916                            self.note_use(path.segments[1].span, Target::TraitMethod(tid, idx));
3917                            let checked: Vec<hir::Expr> =
3918                                args.iter().map(|a| self.check_expr(fcx, a)).collect();
3919                            return self.emit_trait_call(
3920                                fcx,
3921                                tid,
3922                                idx,
3923                                TraitCallSelf::Qualifier(self_ty),
3924                                checked,
3925                                span,
3926                            );
3927                        }
3928                        [] => {
3929                            // A primitive is invisible to `resolve_type`, so the
3930                            // fall-through below would blame "module-qualified
3931                            // paths are not supported" for what is really a
3932                            // missing associated function.
3933                            if self.defs.resolve_type(self.cur_module, ty_name).is_none() {
3934                                self.error(
3935                                    "E0001",
3936                                    format!("no associated function `{name}` on type `{ty_name}`"),
3937                                    callee.span,
3938                                );
3939                                return error_expr(span);
3940                            }
3941                            // A nominal qualifier whose type arguments are the
3942                            // reason nothing matched: some impl sharing this
3943                            // type's head does declare `name` through a trait,
3944                            // but `find_trait_assoc_fns` could not select it
3945                            // because `self_ty`'s arguments are still unresolved
3946                            // inference variables (see that function's doc
3947                            // comment) — `match_args` cannot line a concrete
3948                            // impl's arguments up with a variable. Name that
3949                            // reason here rather than let this fall through to
3950                            // `check_path`, which would report a missing
3951                            // variant and blame the wrong feature entirely.
3952                            if let Some(head) = self_ty.head() {
3953                                let head_declares_it = self
3954                                    .impls
3955                                    .iter()
3956                                    .filter(|i| i.self_head == head)
3957                                    .filter_map(|i| i.trait_id)
3958                                    .any(|tid| self.trait_assoc_fn_index(tid, name).is_some());
3959                                if head_declares_it {
3960                                    self.error(
3961                                        "E0011",
3962                                        format!(
3963                                            "cannot call `{ty_name}::{name}()`: an impl provides \
3964                                             it, but `{ty_name}`'s type arguments could not be \
3965                                             determined from the qualifier alone to select it"
3966                                        ),
3967                                        callee.span,
3968                                    );
3969                                    return error_expr(span);
3970                                }
3971                            }
3972                            // Otherwise a nominal qualifier keeps falling
3973                            // through to `check_path`, which reports the
3974                            // pre-existing "no variant" diagnostic.
3975                        }
3976                        _ => {
3977                            self.error(
3978                                "E0015",
3979                                format!(
3980                                    "ambiguous associated function `{ty_name}::{name}`: more \
3981                                     than one trait in scope provides it"
3982                                ),
3983                                callee.span,
3984                            );
3985                            return error_expr(span);
3986                        }
3987                    }
3988                }
3989            }
3990        }
3991
3992        // Indirect call through any fn-typed value expression (a local, a
3993        // constant of function type, a field, another call's result, …).
3994        // Plan decision 7: a two-segment callee's E0900 knows it is a
3995        // call's, and its argument count.
3996        if let ast::Expr::Path(path) = &callee.value {
3997            if path.segments.len() == 2 {
3998                self.qualified_callee = Some(args.len());
3999            }
4000        }
4001        let callee_expr = self.check_expr(fcx, callee);
4002        // Taken by `check_path`; cleared here too, so it reaches no other
4003        // path.
4004        self.qualified_callee = None;
4005        let checked: Vec<hir::Expr> = args.iter().map(|a| self.check_expr(fcx, a)).collect();
4006        let ret = fcx.icx.fresh();
4007        let expected = Ty::Fn {
4008            params: checked.iter().map(|a| a.ty.clone()).collect(),
4009            ret: Box::new(ret.clone()),
4010        };
4011        if !fcx.icx.unify(&callee_expr.ty, &expected) {
4012            self.error(
4013                "E0010",
4014                format!(
4015                    "this value has type `{}` and cannot be called with these arguments",
4016                    self.show(&callee_expr.ty, fcx),
4017                ),
4018                callee_expr.span,
4019            );
4020            return error_expr(span);
4021        }
4022        // Indirect calls dispatch through a local. If the callee is already a
4023        // local, use it directly; otherwise bind it to a fresh local first
4024        // (evaluated before the arguments, preserving left-to-right order).
4025        if let hir::ExprKind::Local(local) = callee_expr.kind {
4026            return hir::Expr {
4027                kind: hir::ExprKind::Call {
4028                    func: hir::Callee::Local(local),
4029                    type_args: Vec::new(),
4030                    args: checked,
4031                },
4032                ty: ret,
4033                span,
4034            };
4035        }
4036        let callee_ty = callee_expr.ty.clone();
4037        let tmp =
4038            fcx.new_local_unscoped("__callee".to_string(), callee_ty, false, callee_expr.span);
4039        let bind = hir::Expr {
4040            kind: hir::ExprKind::Let {
4041                local: tmp,
4042                init: Box::new(callee_expr),
4043            },
4044            ty: Ty::Unit,
4045            span,
4046        };
4047        let call = hir::Expr {
4048            kind: hir::ExprKind::Call {
4049                func: hir::Callee::Local(tmp),
4050                type_args: Vec::new(),
4051                args: checked,
4052            },
4053            ty: ret.clone(),
4054            span,
4055        };
4056        hir::Expr {
4057            kind: hir::ExprKind::Block {
4058                stmts: vec![bind],
4059                trailing: Some(Box::new(call)),
4060            },
4061            ty: ret,
4062            span,
4063        }
4064    }
4065
4066    fn check_direct_call(
4067        &mut self,
4068        fcx: &mut FnCtx,
4069        def_id: DefId,
4070        args: &[Spanned<ast::Expr>],
4071        span: Span,
4072    ) -> hir::Expr {
4073        let Some(sig) = self.sigs.get(&def_id).cloned() else {
4074            return error_expr(span);
4075        };
4076        let name = self.defs.def(def_id).name.clone();
4077        if args.len() != sig.params.len() {
4078            self.error(
4079                "E0016",
4080                format!(
4081                    "`{name}` takes {} argument(s) but {} were supplied",
4082                    sig.params.len(),
4083                    args.len()
4084                ),
4085                span,
4086            );
4087            return error_expr(span);
4088        }
4089        let type_args: Vec<Ty> = (0..sig.generics).map(|_| fcx.icx.fresh()).collect();
4090        let mut checked = Vec::new();
4091        for (arg, param) in args.iter().zip(sig.params.iter()) {
4092            let a = self.check_expr(fcx, arg);
4093            // Normalization seam: a generic function may declare a parameter or
4094            // its return type as a projection on one of its own type parameters
4095            // (`fn take<I: It>(x: I, y: I::Item)`), and `type_args` has just been
4096            // narrowed by the earlier arguments.
4097            let expected = self.instantiate(param, &type_args, &fcx.icx, a.span);
4098            if !fcx.icx.unify(&a.ty, &expected) {
4099                self.error(
4100                    "E0010",
4101                    format!(
4102                        "argument to `{name}` has type `{}` but `{}` was expected",
4103                        self.show(&a.ty, fcx),
4104                        self.show(&expected, fcx),
4105                    ),
4106                    a.span,
4107                );
4108            }
4109            checked.push(a);
4110        }
4111        let ret = self.instantiate(&sig.ret, &type_args, &fcx.icx, span);
4112        hir::Expr {
4113            kind: hir::ExprKind::Call {
4114                func: hir::Callee::Def(def_id),
4115                type_args,
4116                args: checked,
4117            },
4118            ty: ret,
4119            span,
4120        }
4121    }
4122
4123    fn check_builtin_call(
4124        &mut self,
4125        fcx: &mut FnCtx,
4126        builtin: Builtin,
4127        args: &[Spanned<ast::Expr>],
4128        span: Span,
4129    ) -> hir::Expr {
4130        let (params, ret) = builtin_signature(builtin);
4131        if args.len() != params.len() {
4132            self.error(
4133                "E0016",
4134                format!(
4135                    "`{}` takes {} argument{} but {} were supplied",
4136                    builtin.name(),
4137                    params.len(),
4138                    if params.len() == 1 { "" } else { "s" },
4139                    args.len()
4140                ),
4141                span,
4142            );
4143            return error_expr(span);
4144        }
4145        // The print family's hint is specific to it: passing a non-`String`
4146        // there is nearly always a missing interpolation, whereas the
4147        // std-only builtins are called from several sites across std/core and
4148        // std/strings, with no single wrong-argument pattern common to all of
4149        // them.
4150        let hint = match builtin {
4151            Builtin::Println
4152            | Builtin::Print
4153            | Builtin::EPrint
4154            | Builtin::EPrintln
4155            | Builtin::Panic => " (use string interpolation: \"${value}\")",
4156            Builtin::StrCmp
4157            | Builtin::StrHash
4158            | Builtin::CharToInt
4159            | Builtin::StrLenChars
4160            | Builtin::StrChars
4161            | Builtin::StrFromChars
4162            | Builtin::StrJoin
4163            | Builtin::StrIndexOf
4164            | Builtin::JsonQuote
4165            | Builtin::StrToUpper
4166            | Builtin::StrToLower
4167            | Builtin::FloatFixed
4168            | Builtin::StrToFloat
4169            | Builtin::TestSelector
4170            | Builtin::TaskSpawn
4171            | Builtin::TaskIsDone
4172            | Builtin::TaskRelease
4173            | Builtin::TaskDrive
4174            | Builtin::TaskOutput
4175            | Builtin::TaskYieldFuture
4176            | Builtin::TaskSleepFutureNanos
4177            | Builtin::TaskJoinFuture
4178            | Builtin::TaskTimeoutFuture
4179            | Builtin::FsReadToString
4180            | Builtin::FsWriteString
4181            | Builtin::FsTakeString
4182            | Builtin::FsLastErrorMessage
4183            | Builtin::FsTempDir
4184            | Builtin::FsExists
4185            | Builtin::FsCreateDir
4186            | Builtin::FsCreateDirAll
4187            | Builtin::FsRemoveFile
4188            | Builtin::FsRemoveDirAll
4189            | Builtin::FsReadDir
4190            | Builtin::FsTakeStringArray
4191            | Builtin::FsKind
4192            | Builtin::FsRead
4193            | Builtin::FsTakeBytes
4194            | Builtin::FsWrite
4195            | Builtin::FileOpen
4196            | Builtin::FileClose
4197            | Builtin::FileRead
4198            | Builtin::FileWrite
4199            | Builtin::FileFlush
4200            | Builtin::IoStdinRead
4201            | Builtin::IoStdoutWrite
4202            | Builtin::IoStderrWrite
4203            | Builtin::IoStdoutFlush
4204            | Builtin::IoStderrFlush
4205            | Builtin::NetConnect
4206            | Builtin::NetClose
4207            | Builtin::NetRead
4208            | Builtin::NetWrite
4209            | Builtin::NetReadTimeout
4210            | Builtin::NetListen
4211            | Builtin::NetLocalPort
4212            | Builtin::NetAccept
4213            | Builtin::BytesLen
4214            | Builtin::BytesFromString
4215            | Builtin::BytesIsUtf8
4216            | Builtin::BytesToStringUnchecked
4217            | Builtin::BytesAt
4218            | Builtin::BytesSlice
4219            | Builtin::BytesConcat
4220            | Builtin::BytesToInts
4221            | Builtin::BytesFromInts
4222            | Builtin::BytesEq
4223            | Builtin::IntHashSeed
4224            | Builtin::HttpParseRequest
4225            | Builtin::CryptoHash
4226            | Builtin::CryptoRandomBytes
4227            | Builtin::CryptoRandomInt
4228            | Builtin::TimeNowNanos
4229            | Builtin::TimeNowEpochNanos
4230            | Builtin::LogConfigLevel
4231            | Builtin::LogConfigToStderr
4232            | Builtin::LogSetConfig
4233            | Builtin::ShutdownRequested
4234            | Builtin::ProcessArgCount
4235            | Builtin::ProcessArg
4236            | Builtin::ProcessExit => "",
4237        };
4238        let mut checked = Vec::with_capacity(args.len());
4239        for (arg, param) in args.iter().zip(&params) {
4240            let arg = self.check_expr(fcx, arg);
4241            if !fcx.icx.unify(&arg.ty, param) {
4242                self.error(
4243                    "E0010",
4244                    format!(
4245                        "`{}` expects a `{}`, found `{}`{hint}",
4246                        builtin.name(),
4247                        self.show(param, fcx),
4248                        self.show(&arg.ty, fcx),
4249                    ),
4250                    arg.span,
4251                );
4252            }
4253            checked.push(arg);
4254        }
4255        hir::Expr {
4256            kind: hir::ExprKind::Call {
4257                func: hir::Callee::Builtin(builtin),
4258                type_args: Vec::new(),
4259                args: checked,
4260            },
4261            ty: ret,
4262            span,
4263        }
4264    }
4265
4266    fn make_variant(
4267        &mut self,
4268        fcx: &mut FnCtx,
4269        sum_id: DefId,
4270        variant: usize,
4271        args: Vec<hir::Expr>,
4272        span: Span,
4273    ) -> hir::Expr {
4274        let Some(sum) = self.sums.iter().find(|s| s.def_id == sum_id).cloned() else {
4275            return error_expr(span);
4276        };
4277        let v = &sum.variants[variant];
4278        if args.len() != v.fields.len() {
4279            self.error(
4280                "E0016",
4281                format!(
4282                    "variant `{}` has {} field(s) but {} were supplied",
4283                    v.name,
4284                    v.fields.len(),
4285                    args.len()
4286                ),
4287                span,
4288            );
4289            return error_expr(span);
4290        }
4291        let type_args: Vec<Ty> = (0..sum.generics).map(|_| fcx.icx.fresh()).collect();
4292        for (arg, field) in args.iter().zip(v.fields.iter()) {
4293            let expected = field.subst(&type_args);
4294            if !fcx.icx.unify(&arg.ty, &expected) {
4295                self.error(
4296                    "E0010",
4297                    format!(
4298                        "field of `{}` has type `{}` but `{}` was supplied",
4299                        v.name,
4300                        self.show(&expected, fcx),
4301                        self.show(&arg.ty, fcx),
4302                    ),
4303                    arg.span,
4304                );
4305            }
4306        }
4307        hir::Expr {
4308            kind: hir::ExprKind::MakeVariant {
4309                sum: sum_id,
4310                variant: variant as u32,
4311                args,
4312            },
4313            ty: Ty::Sum {
4314                def_id: sum_id,
4315                args: type_args,
4316            },
4317            span,
4318        }
4319    }
4320
4321    fn check_array_literal(
4322        &mut self,
4323        fcx: &mut FnCtx,
4324        elems: &[Spanned<ast::Expr>],
4325        span: Span,
4326    ) -> hir::Expr {
4327        let elem_ty = fcx.icx.fresh();
4328        let mut checked = Vec::with_capacity(elems.len());
4329        for e in elems {
4330            let v = self.check_expr(fcx, e);
4331            if !fcx.icx.unify(&v.ty, &elem_ty) {
4332                self.error(
4333                    "E0010",
4334                    format!(
4335                        "array elements must share a type: expected `{}`, found `{}`",
4336                        self.show(&elem_ty, fcx),
4337                        self.show(&v.ty, fcx),
4338                    ),
4339                    e.span,
4340                );
4341            }
4342            checked.push(v);
4343        }
4344        hir::Expr {
4345            kind: hir::ExprKind::MakeArray { elems: checked },
4346            ty: Ty::Array(Box::new(elem_ty)),
4347            span,
4348        }
4349    }
4350
4351    /// `[init; n]` — an `n`-slot array whose every slot is `init`. The element
4352    /// type comes from `init` (so no `Default` bound is needed and a fresh array
4353    /// is never uninitialized), and `n` must be an `Int` evaluated at runtime.
4354    ///
4355    /// `init` is evaluated once and the same value goes into every slot, so for
4356    /// a heap element type the slots are one object, not `n` of them — see
4357    /// `hir::ExprKind::ArrayRepeat`.
4358    fn check_array_repeat(
4359        &mut self,
4360        fcx: &mut FnCtx,
4361        init: &Spanned<ast::Expr>,
4362        len: &Spanned<ast::Expr>,
4363        span: Span,
4364    ) -> hir::Expr {
4365        let init_hir = self.check_expr(fcx, init);
4366        let len_hir = self.check_expr(fcx, len);
4367        self.expect_ty(fcx, &len_hir, &Ty::Int, "an array length");
4368        let elem_ty = fcx.icx.apply(&init_hir.ty);
4369        hir::Expr {
4370            kind: hir::ExprKind::ArrayRepeat {
4371                init: Box::new(init_hir),
4372                len: Box::new(len_hir),
4373            },
4374            ty: Ty::Array(Box::new(elem_ty)),
4375            span,
4376        }
4377    }
4378
4379    fn check_index(
4380        &mut self,
4381        fcx: &mut FnCtx,
4382        target: &Spanned<ast::Expr>,
4383        index: &Spanned<ast::Expr>,
4384        span: Span,
4385    ) -> hir::Expr {
4386        let arr = self.check_expr(fcx, target);
4387        let idx = self.check_expr(fcx, index);
4388        self.expect_ty(fcx, &idx, &Ty::Int, "an array index");
4389        let elem_ty = fcx.icx.fresh();
4390        if !fcx
4391            .icx
4392            .unify(&arr.ty, &Ty::Array(Box::new(elem_ty.clone())))
4393        {
4394            self.error(
4395                "E0014",
4396                format!("cannot index a value of type `{}`", self.show(&arr.ty, fcx)),
4397                target.span,
4398            );
4399            return error_expr(span);
4400        }
4401        hir::Expr {
4402            kind: hir::ExprKind::Index {
4403                target: Box::new(arr),
4404                index: Box::new(idx),
4405            },
4406            ty: elem_ty,
4407            span,
4408        }
4409    }
4410
4411    /// `for i in lo..hi { body }` desugars to a counter-driven `while`:
4412    /// `{ let i = lo; let end = hi; while i < end { body; i = i + 1 } }`
4413    /// (`<=` for an inclusive range). Any other iterable is an iterator —
4414    /// see [`Checker::check_for_iterator`].
4415    ///
4416    /// The first two lines were stranded on `check_array_literal`, which had been
4417    /// inserted into the middle of this comment; rejoined here.
4418    fn check_for(
4419        &mut self,
4420        fcx: &mut FnCtx,
4421        pattern: &Spanned<ast::Pattern>,
4422        iter: &Spanned<ast::Expr>,
4423        body: &Spanned<ast::Block>,
4424        span: Span,
4425    ) -> hir::Expr {
4426        let ast::Expr::Range { lo, hi, inclusive } = &iter.value else {
4427            return self.check_for_iterator(fcx, pattern, iter, body, span);
4428        };
4429
4430        let lo = self.check_expr(fcx, lo);
4431        self.expect_ty(fcx, &lo, &Ty::Int, "a range bound");
4432        let hi = self.check_expr(fcx, hi);
4433        self.expect_ty(fcx, &hi, &Ty::Int, "a range bound");
4434
4435        // Hidden counter/bound locals are unscoped so they neither collide
4436        // with nor shadow source identifiers, and the counter is separate
4437        // from the user's (immutable) loop variable so assigning the loop
4438        // variable in the body is rejected (E0060) and cannot corrupt the
4439        // trip count.
4440        let counter = fcx.new_local_unscoped("__i".to_string(), Ty::Int, true, span);
4441        let end = fcx.new_local_unscoped("__end".to_string(), Ty::Int, false, span);
4442
4443        fcx.scopes.push(FxHashMap::default());
4444        let (var_name, var_span) = self.for_loop_var(pattern);
4445        let i = self.bind_local(fcx, var_name, Ty::Int, false, var_span, Binding::Other);
4446        fcx.loop_depth += 1;
4447        let body_hir = self.check_block(fcx, &body.value, body.span);
4448        fcx.loop_depth -= 1;
4449        fcx.scopes.pop();
4450
4451        let int = |kind| hir::Expr {
4452            kind,
4453            ty: Ty::Int,
4454            span,
4455        };
4456        let read = |local| int(hir::ExprKind::Local(local));
4457        let assign = |local, value| hir::Expr {
4458            kind: hir::ExprKind::Assign {
4459                local,
4460                value: Box::new(value),
4461            },
4462            ty: Ty::Unit,
4463            span,
4464        };
4465        let let_stmt = |local, init| hir::Expr {
4466            kind: hir::ExprKind::Let {
4467                local,
4468                init: Box::new(init),
4469            },
4470            ty: Ty::Unit,
4471            span,
4472        };
4473        // Bind the immutable loop variable to the counter each iteration.
4474        let bind_i = let_stmt(i, read(counter));
4475        // increment: __i = __i + 1
4476        let incr = assign(
4477            counter,
4478            int(hir::ExprKind::Binary {
4479                op: hir::BinOp::Add,
4480                lhs: Box::new(read(counter)),
4481                rhs: Box::new(int(hir::ExprKind::IntLit(1))),
4482            }),
4483        );
4484
4485        let mut outer_stmts = vec![let_stmt(counter, lo), let_stmt(end, hi)];
4486
4487        let while_expr = if *inclusive {
4488            // Inclusive ranges use a run flag so that iterating up to
4489            // `Int::MAX` terminates instead of wrapping past the bound.
4490            let run = fcx.new_local_unscoped("__run".to_string(), Ty::Bool, true, span);
4491            let bool_expr = |kind| hir::Expr {
4492                kind,
4493                ty: Ty::Bool,
4494                span,
4495            };
4496            let cmp = |op| {
4497                bool_expr(hir::ExprKind::Binary {
4498                    op,
4499                    lhs: Box::new(read(counter)),
4500                    rhs: Box::new(read(end)),
4501                })
4502            };
4503            outer_stmts.push(let_stmt(run, cmp(hir::BinOp::Le)));
4504            // Advance the counter and run-flag *before* the body so that a
4505            // `continue` (which jumps to the loop header) still progresses.
4506            let update_run = assign(run, cmp(hir::BinOp::Lt));
4507            let while_body = hir::Expr {
4508                kind: hir::ExprKind::Block {
4509                    stmts: vec![bind_i, update_run, incr, body_hir],
4510                    trailing: None,
4511                },
4512                ty: Ty::Unit,
4513                span,
4514            };
4515            hir::Expr {
4516                kind: hir::ExprKind::While {
4517                    cond: Box::new(bool_expr(hir::ExprKind::Local(run))),
4518                    body: Box::new(while_body),
4519                },
4520                ty: Ty::Unit,
4521                span,
4522            }
4523        } else {
4524            let cond = hir::Expr {
4525                kind: hir::ExprKind::Binary {
4526                    op: hir::BinOp::Lt,
4527                    lhs: Box::new(read(counter)),
4528                    rhs: Box::new(read(end)),
4529                },
4530                ty: Ty::Bool,
4531                span,
4532            };
4533            // Increment before the body so `continue` still advances.
4534            let while_body = hir::Expr {
4535                kind: hir::ExprKind::Block {
4536                    stmts: vec![bind_i, incr, body_hir],
4537                    trailing: None,
4538                },
4539                ty: Ty::Unit,
4540                span,
4541            };
4542            hir::Expr {
4543                kind: hir::ExprKind::While {
4544                    cond: Box::new(cond),
4545                    body: Box::new(while_body),
4546                },
4547                ty: Ty::Unit,
4548                span,
4549            }
4550        };
4551
4552        outer_stmts.push(while_expr);
4553        hir::Expr {
4554            kind: hir::ExprKind::Block {
4555                stmts: outer_stmts,
4556                trailing: None,
4557            },
4558            ty: Ty::Unit,
4559            span,
4560        }
4561    }
4562
4563    /// The name and span a `for` loop binds its element to. Shared by the range
4564    /// and iterator desugars: they bind at different types but accept exactly
4565    /// the same pattern forms, so `E0022` — and the choice to fall back to `_`
4566    /// and keep checking the body rather than bail — is stated once here instead
4567    /// of in two copies that can drift.
4568    ///
4569    /// A `for` loop variable is immutable in both desugars, so assigning it is
4570    /// `E0060` — measured: `for i in 0..3 { i = i + 1 }` is
4571    /// `error[E0060]: cannot assign to immutable variable 'i'`.
4572    ///
4573    /// The `..` in the `Ident` arm below discards `ast::Pattern::Ident`'s
4574    /// `is_mut`, and that discard is **currently unreachable from source**: the
4575    /// parser rejects a `mut` in this position before any pattern exists, so
4576    /// there is no binding to make immutable and no `E0060` to compare against.
4577    /// Measured, both spellings: `for mut i in 0..3` and `for mut x in v.iter()`
4578    /// are each `error[P0001]: expected pattern (in statement), found 'mut'`.
4579    /// An earlier version of this comment said `for mut i in …` "gets the same
4580    /// immutable binding (and the same `E0060` on assignment) as `for i in …`",
4581    /// which describes a program that cannot be written. The discard stays as
4582    /// the right answer for if the parser ever accepts the form — not as a
4583    /// statement about today's behaviour.
4584    fn for_loop_var(&mut self, pattern: &Spanned<ast::Pattern>) -> (String, Span) {
4585        match &pattern.value {
4586            ast::Pattern::Ident { name, .. } => (name.value.clone(), name.span),
4587            ast::Pattern::Wildcard => ("_".to_string(), pattern.span),
4588            _ => {
4589                self.error(
4590                    "E0022",
4591                    "a `for` loop variable must be a name or `_`",
4592                    pattern.span,
4593                );
4594                ("_".to_string(), pattern.span)
4595            }
4596        }
4597    }
4598
4599    /// `for x in it { body }` desugars to
4600    /// `{ let mut __it = it
4601    ///    while true { match __it.next() { Some(x) => body, None => break } } }`.
4602    ///
4603    /// `while true`, not `loop`: Nova has no `loop` keyword — `loop { … }`
4604    /// parses as an identifier followed by a record literal.
4605    ///
4606    /// `__it` is unscoped (so it can neither collide with nor shadow a source
4607    /// identifier) and `mut` (so `next`'s `mut self` receiver is satisfied
4608    /// without the user writing `mut`, per ADR 0005 §1). The user's `x` stays
4609    /// immutable, exactly as in the range form, so assigning it is `E0060`.
4610    ///
4611    /// **The iterable is checked here, not at monomorphization.** An earlier
4612    /// revision of this comment claimed the opposite — "the `Iterator` bound is
4613    /// discharged at monomorphization (`E0013`) like every other bound, not
4614    /// here" — and that is measured false: `for x in 5` is
4615    /// `error[E0900]: 'for' loops over anything but an integer range ('a..b')
4616    /// or a value implementing 'Iterator' (try '.iter()') are not supported
4617    /// yet`, reported by the `iterator_next` lookup below, in typeck. There is
4618    /// no `Iterator` *bound* to discharge anywhere: the desugar needs an impl
4619    /// in hand to learn the item type at all, so failing to find one is
4620    /// necessarily an error at this point rather than a constraint deferred to
4621    /// mono.
4622    ///
4623    /// What it therefore keys on is `next`'s *shape*, not std's `Iterator`'s
4624    /// identity — the same duck-typing [`Checker::try_display`] uses for `fmt`,
4625    /// and forced by the same thing: the checker has no name-based handle on a
4626    /// std trait, and a user `trait It { type Item  fn next(…) }` is as good an
4627    /// iterator as std's. std/core already documents that `next` is *not*
4628    /// soft-reserved, so a user trait declaring one on a primitive makes
4629    /// `for x in 3` legal; that is the accepted consequence, not an oversight.
4630    fn check_for_iterator(
4631        &mut self,
4632        fcx: &mut FnCtx,
4633        pattern: &Spanned<ast::Pattern>,
4634        iter: &Spanned<ast::Expr>,
4635        body: &Spanned<ast::Block>,
4636        span: Span,
4637    ) -> hir::Expr {
4638        // One name for the hidden iterator. The local, the path `place_root`
4639        // resolves, and the scope entry it resolves through must all agree, so
4640        // they read it from here rather than repeating a literal three times.
4641        const IT: &str = "__it";
4642
4643        let iterable = self.check_expr(fcx, iter);
4644        let iter_ty = fcx.icx.apply(&iterable.ty);
4645        // The iterable's own mistake has already been reported. `Ty::Error` has
4646        // no head, so resolution below would fail and add `E0900` on top —
4647        // two diagnostics for one mistake.
4648        if matches!(iter_ty, Ty::Error) {
4649            return error_expr(span);
4650        }
4651        let Some(next) = self.iterator_next(fcx, &iter_ty) else {
4652            self.unsupported(
4653                iter.span,
4654                "`for` loops over anything but an integer range (`a..b`) or a value \
4655                 implementing `Iterator` (try `.iter()`)",
4656            );
4657            return error_expr(span);
4658        };
4659
4660        // The iterator is live for the whole loop and `next` takes `mut self`,
4661        // so `__it` is `mut`; it is unscoped so no source identifier can reach
4662        // it or be shadowed by it.
4663        let it_local = fcx.new_local_unscoped(IT.to_string(), iter_ty.clone(), true, span);
4664        let receiver = hir::Expr {
4665            kind: hir::ExprKind::Local(it_local),
4666            ty: iter_ty,
4667            span: iter.span,
4668        };
4669        // `emit_trait_call` classifies its receiver with `place_root`, which
4670        // resolves an *AST* name against the scopes — the checked `hir::Expr`
4671        // has lost the shape it needs. So the receiver has to be spelled as a
4672        // path and be findable for the one call emitted below. The scope is
4673        // pushed and popped around that single call: no source expression is
4674        // checked inside it, so `__it` stays unreachable from user code, while
4675        // `place_root` still reads the local's real `is_mut` — dropping the
4676        // `mut` above is `E0060`, not a silently accepted mutation.
4677        let it_ast = Spanned::new(
4678            ast::Expr::Path(ast::Path::single(Spanned::new(IT.to_string(), iter.span))),
4679            iter.span,
4680        );
4681        fcx.scopes.push(FxHashMap::default());
4682        fcx.scopes
4683            .last_mut()
4684            .expect("just pushed")
4685            .insert(IT.to_string(), it_local);
4686        let next_call = self.emit_trait_call(
4687            fcx,
4688            next.trait_id,
4689            next.method_idx,
4690            TraitCallSelf::Receiver(receiver, &it_ast),
4691            Vec::new(),
4692            span,
4693        );
4694        fcx.scopes.pop();
4695
4696        // `next`'s declared `Option<Self::Item>` comes back from
4697        // `emit_trait_call` with `Self` substituted and the projection already
4698        // normalized against the impl that call selected, so this *result's*
4699        // argument list is the item type. Reading it off the call is what keeps
4700        // the desugar on one impl-selection path: resolving `Self::Item` again
4701        // here would be the second lookup that has twice drifted out of step
4702        // with `match_args` and shipped as a miscompile.
4703        let item_ty = match fcx.icx.apply(&next_call.ty) {
4704            Ty::Sum { def_id, args } if def_id == next.option => self
4705                .sums
4706                .iter()
4707                .find(|s| s.def_id == next.option)
4708                .and_then(|s| s.variants.get(next.some as usize))
4709                .and_then(|v| v.fields.first())
4710                .map(|payload| payload.subst(&args))
4711                .unwrap_or(Ty::Error),
4712            // `emit_trait_call` bailed (it reported why). `Ty::Error` unifies
4713            // with anything, so the body is still checked for its own mistakes
4714            // without the loop variable manufacturing new ones.
4715            _ => Ty::Error,
4716        };
4717
4718        fcx.scopes.push(FxHashMap::default());
4719        let (var_name, var_span) = self.for_loop_var(pattern);
4720        let elem = self.bind_local(fcx, var_name, item_ty, false, var_span, Binding::Other);
4721        fcx.loop_depth += 1;
4722        let body_hir = self.check_block(fcx, &body.value, body.span);
4723        fcx.loop_depth -= 1;
4724        fcx.scopes.pop();
4725
4726        let unit = |kind| hir::Expr {
4727            kind,
4728            ty: Ty::Unit,
4729            span,
4730        };
4731        let variant = |v, binders| hir::Pattern::Variant {
4732            sum: next.option,
4733            variant: v,
4734            binders,
4735        };
4736        let arms = vec![
4737            hir::Arm {
4738                pattern: variant(next.some, vec![Some(elem)]),
4739                // Wrapped in a block so the arm is `Unit` whatever the body
4740                // evaluates to — the range desugar discards the body's value
4741                // the same way, by making it a statement.
4742                body: unit(hir::ExprKind::Block {
4743                    stmts: vec![body_hir],
4744                    trailing: None,
4745                }),
4746                span: body.span,
4747            },
4748            hir::Arm {
4749                pattern: variant(next.none, Vec::new()),
4750                body: hir::Expr {
4751                    kind: hir::ExprKind::Break,
4752                    ty: Ty::Never,
4753                    span,
4754                },
4755                span,
4756            },
4757        ];
4758        let while_expr = unit(hir::ExprKind::While {
4759            cond: Box::new(hir::Expr {
4760                kind: hir::ExprKind::BoolLit(true),
4761                ty: Ty::Bool,
4762                span,
4763            }),
4764            body: Box::new(unit(hir::ExprKind::Match {
4765                scrutinee: Box::new(next_call),
4766                arms,
4767            })),
4768        });
4769        let bind_it = unit(hir::ExprKind::Let {
4770            local: it_local,
4771            init: Box::new(iterable),
4772        });
4773        unit(hir::ExprKind::Block {
4774            stmts: vec![bind_it, while_expr],
4775            trailing: None,
4776        })
4777    }
4778
4779    /// Resolve the `next` a `for` loop will drive on a value of type `recv_ty`.
4780    /// `None` — which is what makes `for` reject the iterable — when the type
4781    /// has no method of that name, when more than one trait provides one, or
4782    /// when the one it finds is not shaped like `Iterator::next`.
4783    ///
4784    /// Resolution goes through [`Checker::resolve_method_on`], the checker's one
4785    /// method-lookup routine, which filters candidate impls by
4786    /// `ImplInfo::match_args` and not by head alone.
4787    fn iterator_next(&self, fcx: &FnCtx, recv_ty: &Ty) -> Option<IteratorNext> {
4788        let MethodRes::Trait(trait_id, method_idx) = self.resolve_method_on(recv_ty, fcx, "next")
4789        else {
4790            return None;
4791        };
4792        let tm = self
4793            .traits
4794            .iter()
4795            .find(|t| t.def_id == trait_id)?
4796            .methods
4797            .get(method_idx as usize)?;
4798        // `next(mut self) -> Option<…>`: no further arguments, and no generics
4799        // of its own, so the item type is ground the moment `Self` is known
4800        // rather than an inference variable the loop body has to pin down.
4801        if !tm.params.is_empty() || tm.generics != 0 {
4802            return None;
4803        }
4804        // An `Option`-shaped return: exactly the two variants `Some(x)` and
4805        // `None`. Read off the *declaration*, before any call is emitted, so a
4806        // wrongly shaped `next` reports only `E0900` and not also whatever
4807        // `emit_trait_call` would have said about its receiver or arity.
4808        let Ty::Sum { def_id, .. } = tm.ret else {
4809            return None;
4810        };
4811        let sum = self.sums.iter().find(|s| s.def_id == def_id)?;
4812        if sum.variants.len() != 2 {
4813            return None;
4814        }
4815        Some(IteratorNext {
4816            trait_id,
4817            method_idx,
4818            option: def_id,
4819            some: sum
4820                .variants
4821                .iter()
4822                .position(|v| v.name == "Some" && v.fields.len() == 1)? as u32,
4823            none: sum
4824                .variants
4825                .iter()
4826                .position(|v| v.name == "None" && v.fields.is_empty())? as u32,
4827        })
4828    }
4829
4830    /// Check a closure literal `|params| body`, lifting it into its own
4831    /// function. Free variables referring to enclosing locals are captured
4832    /// by value; the closure value is a fat pointer `{ code, env }`.
4833    fn check_closure(
4834        &mut self,
4835        fcx: &mut FnCtx,
4836        params: &[ast::Param],
4837        ret: &Option<Spanned<ast::Type>>,
4838        body: &Spanned<ast::Expr>,
4839        span: Span,
4840    ) -> hir::Expr {
4841        // Locals allocated below this index belong to enclosing scopes and
4842        // become captures if the body references them.
4843        let snapshot = fcx.locals.len() as u32;
4844        let generics_scope = fcx.generics.clone();
4845
4846        fcx.scopes.push(FxHashMap::default());
4847        let mut param_locals = Vec::new();
4848        let mut param_types = Vec::new();
4849        for p in params {
4850            let ty = if matches!(p.ty.value, ast::Type::Infer) {
4851                fcx.icx.fresh()
4852            } else {
4853                self.convert_ty(&p.ty, &generics_scope, &fcx.param_bounds)
4854            };
4855            let local = self.bind_local(
4856                fcx,
4857                p.name.value.clone(),
4858                ty.clone(),
4859                p.is_mut,
4860                p.name.span,
4861                Binding::Param,
4862            );
4863            param_locals.push(local);
4864            param_types.push(ty);
4865        }
4866        let ret_ty = match ret {
4867            Some(rt) => self.convert_ty(rt, &generics_scope, &fcx.param_bounds),
4868            None => fcx.icx.fresh(),
4869        };
4870        // A `break`/`continue` in the closure body cannot target a loop in
4871        // the enclosing function.
4872        let saved_loop_depth = fcx.loop_depth;
4873        fcx.loop_depth = 0;
4874        // Likewise `.await`: Nova has no `async` closure syntax, so a closure
4875        // is always its own non-async function, even one written inside an
4876        // `async fn`'s body.
4877        let saved_in_async = fcx.in_async;
4878        fcx.in_async = false;
4879        let body_hir = self.check_expr(fcx, body);
4880        fcx.loop_depth = saved_loop_depth;
4881        fcx.in_async = saved_in_async;
4882        if !fcx.icx.unify(&body_hir.ty, &ret_ty) {
4883            self.error(
4884                "E0010",
4885                format!(
4886                    "closure body has type `{}` but its declared return type is `{}`",
4887                    self.show(&body_hir.ty, fcx),
4888                    self.show(&ret_ty, fcx),
4889                ),
4890                body.span,
4891            );
4892        }
4893        fcx.scopes.pop();
4894
4895        let closure_ty = Ty::Fn {
4896            params: param_types,
4897            ret: Box::new(ret_ty.clone()),
4898        };
4899        self.lift_closure(
4900            fcx,
4901            snapshot,
4902            param_locals.len() as u32,
4903            ret_ty,
4904            body_hir,
4905            closure_ty,
4906            span,
4907        )
4908    }
4909
4910    /// Extract a checked closure body into a standalone function, remapping
4911    /// captured locals to leading env slots and the closure's own locals to
4912    /// fresh indices. Returns the `MakeClosure` expression.
4913    #[allow(clippy::too_many_arguments)]
4914    fn lift_closure(
4915        &mut self,
4916        fcx: &mut FnCtx,
4917        snapshot: u32,
4918        param_count: u32,
4919        ret_ty: Ty,
4920        body: hir::Expr,
4921        closure_ty: Ty,
4922        span: Span,
4923    ) -> hir::Expr {
4924        // Captured locals: those referenced by the body with index < snapshot.
4925        let mut captures: Vec<LocalId> = Vec::new();
4926        collect_captures(&body, snapshot, &mut captures);
4927        let capture_count = captures.len() as u32;
4928
4929        // Remap old local ids to the lifted function's local space:
4930        // captures → 0..C, then the closure's own locals (>= snapshot).
4931        let mut remap: FxHashMap<u32, u32> = FxHashMap::default();
4932        let mut new_locals: Vec<hir::Local> = Vec::new();
4933        for (i, cap) in captures.iter().enumerate() {
4934            remap.insert(cap.0, i as u32);
4935            new_locals.push(fcx.locals[cap.0 as usize].clone());
4936        }
4937        for old in (snapshot as usize)..fcx.locals.len() {
4938            let new_id = capture_count + (old as u32 - snapshot);
4939            remap.insert(old as u32, new_id);
4940            new_locals.push(fcx.locals[old].clone());
4941        }
4942
4943        let mut lifted_body = body;
4944        remap_locals(&mut lifted_body, &remap);
4945
4946        let generics = fcx.generics.len() as u32;
4947        let type_args: Vec<Ty> = (0..generics).map(Ty::Param).collect();
4948        let cdef = self.fresh_closure_def();
4949        let closure_fn = hir::Function {
4950            def_id: cdef,
4951            name: format!("closure${}", cdef.0),
4952            generics,
4953            bounds: fcx.param_bounds.clone(),
4954            takes_env: true,
4955            capture_count,
4956            params: param_count,
4957            locals: new_locals,
4958            ret_ty,
4959            // Nova has no `async` closure syntax (see the reset in
4960            // `check_closure`), so a lifted closure body is never async.
4961            is_async: false,
4962            body: lifted_body,
4963            span,
4964        };
4965        fcx.pending_closures.push(closure_fn);
4966
4967        let capture_exprs: Vec<hir::Expr> = captures
4968            .iter()
4969            .map(|c| hir::Expr {
4970                kind: hir::ExprKind::Local(*c),
4971                ty: fcx.locals[c.0 as usize].ty.clone(),
4972                span,
4973            })
4974            .collect();
4975        hir::Expr {
4976            kind: hir::ExprKind::MakeClosure {
4977                func: cdef,
4978                type_args,
4979                captures: capture_exprs,
4980            },
4981            ty: closure_ty,
4982            span,
4983        }
4984    }
4985
4986    /// Synthesize a fat-pointer wrapper for a bare function used as a value:
4987    /// `(env, params) { target(params) }`, captured environment empty.
4988    fn make_fn_wrapper(
4989        &mut self,
4990        fcx: &mut FnCtx,
4991        target: DefId,
4992        target_type_args: Vec<Ty>,
4993        param_types: Vec<Ty>,
4994        ret: Ty,
4995        span: Span,
4996    ) -> hir::Expr {
4997        let generics = fcx.generics.len() as u32;
4998        let type_args: Vec<Ty> = (0..generics).map(Ty::Param).collect();
4999        let cdef = self.fresh_closure_def();
5000
5001        let mut locals = Vec::new();
5002        let mut call_args = Vec::new();
5003        for (i, pty) in param_types.iter().enumerate() {
5004            locals.push(hir::Local {
5005                name: format!("__a{i}"),
5006                ty: pty.clone(),
5007                is_mut: false,
5008                span,
5009            });
5010            call_args.push(hir::Expr {
5011                kind: hir::ExprKind::Local(LocalId(i as u32)),
5012                ty: pty.clone(),
5013                span,
5014            });
5015        }
5016        let body = hir::Expr {
5017            kind: hir::ExprKind::Call {
5018                func: hir::Callee::Def(target),
5019                type_args: target_type_args,
5020                args: call_args,
5021            },
5022            ty: ret.clone(),
5023            span,
5024        };
5025        let wrapper = hir::Function {
5026            def_id: cdef,
5027            name: format!("fnval${}", cdef.0),
5028            generics,
5029            bounds: fcx.param_bounds.clone(),
5030            takes_env: true,
5031            capture_count: 0,
5032            params: param_types.len() as u32,
5033            locals,
5034            ret_ty: ret.clone(),
5035            // The wrapper's body is a synthetic forwarding `Call`, never a
5036            // `.await` — irrespective of whether `target` itself is async
5037            // (then `ret` is already its `Future<_>`, forwarded unchanged).
5038            is_async: false,
5039            body,
5040            span,
5041        };
5042        fcx.pending_closures.push(wrapper);
5043        hir::Expr {
5044            kind: hir::ExprKind::MakeClosure {
5045                func: cdef,
5046                type_args,
5047                captures: Vec::new(),
5048            },
5049            ty: Ty::Fn {
5050                params: param_types,
5051                ret: Box::new(ret),
5052            },
5053            span,
5054        }
5055    }
5056
5057    fn check_record_literal(
5058        &mut self,
5059        fcx: &mut FnCtx,
5060        path: &ast::Path,
5061        fields: &[ast::FieldInit],
5062        base: Option<&Spanned<ast::Expr>>,
5063        span: Span,
5064    ) -> hir::Expr {
5065        if path.segments.len() != 1 {
5066            self.unsupported(span, "module-qualified record paths");
5067            return error_expr(span);
5068        }
5069        let name = path.segments[0].value.as_str();
5070        let Some(def_id) = self.defs.resolve_type(self.cur_module, name) else {
5071            self.error("E0001", format!("cannot find record `{name}`"), span);
5072            self.name_fixes(path.segments[0].clone(), Need::Record, Vec::new());
5073            return error_expr(span);
5074        };
5075        let Some(record) = self.records.iter().find(|r| r.def_id == def_id).cloned() else {
5076            self.error("E0010", format!("`{name}` is not a record type"), span);
5077            return error_expr(span);
5078        };
5079        self.note_use(path.segments[0].span, Target::Def(def_id));
5080        let type_args: Vec<Ty> = (0..record.generics).map(|_| fcx.icx.fresh()).collect();
5081        let record_ty = Ty::Record {
5082            def_id,
5083            args: type_args.clone(),
5084        };
5085
5086        // Evaluate initializers into locals in *source order* (then the
5087        // base), so their side effects fire left-to-right, consistent with
5088        // how function arguments and `let` bindings evaluate. Field slots
5089        // are then assembled from these locals in declaration order.
5090        let mut stmts: Vec<hir::Expr> = Vec::new();
5091        let mut provided: FxHashMap<String, LocalId> = FxHashMap::default();
5092        for init in fields {
5093            let fname = init.name.value.clone();
5094            let Some(field) = record.fields.iter().find(|f| f.name == fname) else {
5095                self.error(
5096                    "E0014",
5097                    format!("record `{name}` has no field `{fname}`"),
5098                    init.name.span,
5099                );
5100                let given: Vec<&str> = fields.iter().map(|f| f.name.value.as_str()).collect();
5101                let fix = suggest::change_to(
5102                    &init.name,
5103                    record
5104                        .fields
5105                        .iter()
5106                        .map(|f| f.name.as_str())
5107                        .filter(|n| !given.contains(n)),
5108                );
5109                self.push_fix(fix);
5110                continue;
5111            };
5112            if let Some(fi) = record.fields.iter().position(|f| f.name == fname) {
5113                self.note_use(init.name.span, Target::Field(def_id, fi as u32));
5114            }
5115            // Normalization seam: a record's field may declare a projection on
5116            // the record's own bounded type parameter (`f: fn(I::Item) -> U`),
5117            // and `type_args` carries this literal's fresh per-field inference
5118            // vars — so once an earlier field (`it: I`) has pinned one of them
5119            // to a concrete type via `unify` below, a later field's `subst`
5120            // can leave a projection rooted in that now-concrete type. Plain
5121            // `subst` cannot see that; `instantiate` applies the current
5122            // bindings and, only if a projection remains, resolves it through
5123            // the impl table — the same seam `check_direct_call` and
5124            // `emit_trait_call` use for a generic function or trait method's
5125            // parameter and return types.
5126            //
5127            // Same admitted hole as `instantiate`'s own doc comment describes
5128            // for a call (above, `fn instantiate`): the pinning variable has to
5129            // already be solved. `for init in fields` walks the *literal's*
5130            // written order, not declaration order, so this only resolves
5131            // `I::Item` when the field that pins `I` (`it: I`) is written
5132            // before the field naming the projection (`f: fn(I::Item) -> U`)
5133            // in the literal. `M { f: |x| x + 1, it: Counter { n: 0 } }` — the
5134            // same fields, reversed — still fails with the pre-fix symptom
5135            // (an unresolved `Assoc` reaching `unify`), because `type_args`'s
5136            // slot for `I` is still a free variable when `f`'s turn comes.
5137            // Wider than the call case: a function's parameter order is fixed
5138            // by its signature, but a record literal's field order is free
5139            // syntax with no natural declaration-order requirement, so a
5140            // caller has no signature to read this constraint off of.
5141            // `a_record_field_initializer_is_order_sensitive_when_it_names_a_
5142            // projection` (this file's test module) pins the current
5143            // behavior so a future fix to field-order independence flips it
5144            // deliberately rather than silently.
5145            let expected = self.instantiate(&field.ty, &type_args, &fcx.icx, init.name.span);
5146            let value = match &init.value {
5147                Some(v) => self.check_expr(fcx, v),
5148                // Shorthand `{ x }` binds the local named `x`.
5149                None => self.check_path(fcx, &ast::Path::single(init.name.clone()), init.name.span),
5150            };
5151            // `{ x }`: the field and the value share the span (spec §3.2).
5152            if init.value.is_none() {
5153                self.mark_shorthand(init.name.span);
5154            }
5155            if !fcx.icx.unify(&value.ty, &expected) {
5156                self.error(
5157                    "E0010",
5158                    format!(
5159                        "field `{fname}` expects `{}` but found `{}`",
5160                        self.show(&expected, fcx),
5161                        self.show(&value.ty, fcx),
5162                    ),
5163                    init.name.span,
5164                );
5165            }
5166            let value_ty = value.ty.clone();
5167            let local = fcx.new_local(format!("__f_{fname}"), value_ty, false, init.name.span);
5168            stmts.push(hir::Expr {
5169                kind: hir::ExprKind::Let {
5170                    local,
5171                    init: Box::new(value),
5172                },
5173                ty: Ty::Unit,
5174                span,
5175            });
5176            if provided.insert(fname.clone(), local).is_some() {
5177                self.error(
5178                    "E0014",
5179                    format!("field `{fname}` specified more than once"),
5180                    init.name.span,
5181                );
5182            }
5183        }
5184
5185        // A `..base` spread fills any fields not given explicitly. Bind the
5186        // base to a local so it is evaluated exactly once, after the fields.
5187        let base_local = match base {
5188            Some(base_expr) => {
5189                let checked = self.check_expr(fcx, base_expr);
5190                if !fcx.icx.unify(&checked.ty, &record_ty) {
5191                    self.error(
5192                        "E0010",
5193                        format!(
5194                            "the `..` base has type `{}` but `{name}` was expected",
5195                            self.show(&checked.ty, fcx),
5196                        ),
5197                        base_expr.span,
5198                    );
5199                }
5200                let local = fcx.new_local("__base".to_string(), record_ty.clone(), false, span);
5201                stmts.push(hir::Expr {
5202                    kind: hir::ExprKind::Let {
5203                        local,
5204                        init: Box::new(checked),
5205                    },
5206                    ty: Ty::Unit,
5207                    span,
5208                });
5209                Some(local)
5210            }
5211            None => None,
5212        };
5213
5214        // Assemble field slots in declaration order from the evaluated locals.
5215        let mut ordered = Vec::with_capacity(record.fields.len());
5216        let mut missing = Vec::new();
5217        for (idx, field) in record.fields.iter().enumerate() {
5218            // Same normalization seam as above, but **defence in depth, not a
5219            // load-bearing seam — and no test pins it.** Deliberate, and
5220            // recorded as such rather than left to look like a mutation that
5221            // got away (the convention `instantiate`'s own doc comment sets for
5222            // its `has_assoc` guard).
5223            //
5224            // Measured: replacing this line with the pre-increment
5225            // `field.ty.subst(&type_args)` leaves the entire suite green — zero
5226            // failures, every gate configuration included — and the two probes
5227            // that exercise the shape are byte-identical either way
5228            // (`MapIter { it: 5, f: |x| x }` is one `E0079` from mono;
5229            // `f: |x: Int| x` is one `E0010` plus four `E0011`).
5230            //
5231            // The reason is structural, so it is not a coverage gap that a new
5232            // test would close. These types do flow into `MakeRecord`'s
5233            // `hir::Expr`s, but `Specializer::expr` (`crates/nova-mir/src/mono.rs`)
5234            // rewrites `ty: self.ty(&expr.ty)` on **every** expression node it
5235            // clones, and `MakeRecord`'s field exprs go through `self.exprs`
5236            // like any other operand. `Specializer::ty` substitutes *and*
5237            // normalizes, recording `E0079` when a projection cannot resolve.
5238            // So a raw `subst` here cannot reach `mir_ty`: normalization seam 3
5239            // re-normalizes it first, and the worst case is a diagnostic, never
5240            // the `MirTy::Unit` an earlier version of this comment claimed
5241            // ("would not be a diagnostic gap but a miscompile" — measured
5242            // false, by the mutation above).
5243            //
5244            // Contrast the sibling seam at `let expected = …` above, which is
5245            // the real one: there the type is unified against a field
5246            // initializer *inside typeck*, so failing to normalize is a
5247            // spurious `E0010` that mono never gets to see — a genuine
5248            // diagnostic gap, and pinned by
5249            // `a_record_field_initializer_normalizes_a_projection_once_its_
5250            // parameter_is_concrete`. Keep both routed through `instantiate`
5251            // anyway: one seam per read of `field.ty` is the invariant that
5252            // makes this function easy to reason about, and relying on a
5253            // downstream crate to clean up after this one is a coupling worth
5254            // paying a redundant call to avoid.
5255            let field_ty = self.instantiate(&field.ty, &type_args, &fcx.icx, span);
5256            if let Some(local) = provided.get(&field.name) {
5257                ordered.push(hir::Expr {
5258                    kind: hir::ExprKind::Local(*local),
5259                    ty: field_ty,
5260                    span,
5261                });
5262            } else if let Some(base_local) = base_local {
5263                ordered.push(hir::Expr {
5264                    kind: hir::ExprKind::FieldGet {
5265                        target: Box::new(hir::Expr {
5266                            kind: hir::ExprKind::Local(base_local),
5267                            ty: record_ty.clone(),
5268                            span,
5269                        }),
5270                        index: idx as u32,
5271                    },
5272                    ty: field_ty,
5273                    span,
5274                });
5275            } else {
5276                missing.push(format!("`{}`", field.name));
5277            }
5278        }
5279        if !missing.is_empty() {
5280            self.error(
5281                "E0014",
5282                format!("missing field(s) in `{name}`: {}", missing.join(", ")),
5283                span,
5284            );
5285            return error_expr(span);
5286        }
5287
5288        let make = hir::Expr {
5289            kind: hir::ExprKind::MakeRecord {
5290                record: def_id,
5291                fields: ordered,
5292            },
5293            ty: record_ty.clone(),
5294            span,
5295        };
5296        if stmts.is_empty() {
5297            make
5298        } else {
5299            hir::Expr {
5300                kind: hir::ExprKind::Block {
5301                    stmts,
5302                    trailing: Some(Box::new(make)),
5303                },
5304                ty: record_ty,
5305                span,
5306            }
5307        }
5308    }
5309
5310    /// Spec 3.2 §4.1: if the probe's offset lies after a member access's
5311    /// receiver and no later than the end of its name, remember the receiver.
5312    /// The name may be the empty one of an unfinished `x.`, or a name on a
5313    /// later line: `v.` then `println("x")` parses as `v.println("x")`. At the
5314    /// receiver's own end, as in `items|.len()`, the name is the receiver's,
5315    /// so nothing is remembered. The first match wins, which in a chain
5316    /// `a.b.c` is the innermost.
5317    fn probe_receiver(&mut self, fcx: &FnCtx, recv_ty: &Ty, receiver: Span, name: Span) {
5318        let Some(p) = self.probe else {
5319            return;
5320        };
5321        if self.probe_pending.is_some() || self.probe_result.receiver.is_some() {
5322            return;
5323        }
5324        if name.file == p.file && receiver.end < p.offset && p.offset <= name.end {
5325            self.probe_pending = Some(ProbePending {
5326                receiver: recv_ty.clone(),
5327                bounds: fcx.param_bounds.clone(),
5328                module: self.cur_module,
5329            });
5330        }
5331    }
5332
5333    /// The members of `recv_ty` the probe offers, seen from module `from`
5334    /// (spec 3.2 §4.2):
5335    /// - its fields;
5336    /// - its inherent methods that take `self`;
5337    /// - the methods of the traits it implements;
5338    /// - for a type parameter, its bounds' methods;
5339    /// - for an array, `len`.
5340    ///
5341    /// A field or inherent method declared without `pub` is listed only
5342    /// inside its own module. The first of a name wins, so an inherent method
5343    /// hides a trait method of the same name, as `resolve_method_on` does.
5344    fn probe_members(&self, recv_ty: &Ty, bounds: &[Vec<DefId>], from: ModuleId) -> Vec<Member> {
5345        fn push(out: &mut Vec<Member>, m: Member) {
5346            if !out.iter().any(|o| o.name == m.name) {
5347                out.push(m);
5348            }
5349        }
5350        let mut out = Vec::new();
5351        match recv_ty {
5352            Ty::Param(k) => {
5353                for &tid in bounds.get(*k as usize).into_iter().flatten() {
5354                    for m in self.trait_members(tid) {
5355                        push(&mut out, m);
5356                    }
5357                }
5358            }
5359            Ty::Array(_) => push(
5360                &mut out,
5361                Member {
5362                    name: "len".to_string(),
5363                    kind: MemberKind::Method,
5364                    decl: None,
5365                },
5366            ),
5367            _ => {
5368                if let Ty::Record { def_id, .. } = recv_ty {
5369                    for m in self.record_members(*def_id, from) {
5370                        push(&mut out, m);
5371                    }
5372                }
5373                if let Some(head) = recv_ty.head() {
5374                    let fits =
5375                        |i: &&hir::ImplInfo| i.self_head == head && i.match_args(recv_ty).is_some();
5376                    let inherent: Vec<DefId> = self
5377                        .impls
5378                        .iter()
5379                        .filter(fits)
5380                        .filter(|i| i.trait_id.is_none())
5381                        .flat_map(|i| i.methods.iter().map(|(_, d)| *d))
5382                        .filter(|d| !self.selfless.contains(d))
5383                        .collect();
5384                    for d in inherent {
5385                        if let Some(m) = self.inherent_member(d, from) {
5386                            push(&mut out, m);
5387                        }
5388                    }
5389                    let traits: Vec<DefId> = self
5390                        .impls
5391                        .iter()
5392                        .filter(fits)
5393                        .filter_map(|i| i.trait_id)
5394                        .collect();
5395                    for tid in traits {
5396                        for m in self.trait_members(tid) {
5397                            push(&mut out, m);
5398                        }
5399                    }
5400                }
5401            }
5402        }
5403        out
5404    }
5405
5406    /// A record's fields, as `from` may see them.
5407    fn record_members(&self, record: DefId, from: ModuleId) -> Vec<Member> {
5408        let DefKind::Record { item_index } = self.defs.def(record).kind else {
5409            return Vec::new();
5410        };
5411        let ast::Item::Record(decl) = &self.file.items[item_index].value else {
5412            return Vec::new();
5413        };
5414        let own = self.defs.module_of(item_index) == from;
5415        decl.fields
5416            .iter()
5417            .filter(|f| own || matches!(f.vis, ast::Visibility::Pub))
5418            .map(|f| Member {
5419                name: f.name.value.clone(),
5420                kind: MemberKind::Field,
5421                decl: Some(f.ty.span),
5422            })
5423            .collect()
5424    }
5425
5426    /// An inherent method, if `from` may see it.
5427    fn inherent_member(&self, method: DefId, from: ModuleId) -> Option<Member> {
5428        let loc = self.method_locs.get(&method)?;
5429        let ast::Item::Impl(block) = &self.file.items[loc.item_index].value else {
5430            return None;
5431        };
5432        let f = block.functions.get(loc.method_index)?;
5433        let own = self.defs.module_of(loc.item_index) == from;
5434        (own || matches!(f.vis, ast::Visibility::Pub)).then(|| Member {
5435            name: f.name.value.clone(),
5436            kind: MemberKind::Method,
5437            decl: Some(f.name.span),
5438        })
5439    }
5440
5441    /// A trait's methods that take `self`, each with its name's span in the
5442    /// trait's declaration.
5443    fn trait_members(&self, trait_id: DefId) -> Vec<Member> {
5444        let Some(t) = self.traits.iter().find(|t| t.def_id == trait_id) else {
5445            return Vec::new();
5446        };
5447        let mut spans: FxHashMap<&str, Span> = FxHashMap::default();
5448        if let DefKind::Trait { item_index } = self.defs.def(trait_id).kind {
5449            if let ast::Item::Trait(decl) = &self.file.items[item_index].value {
5450                for item in &decl.items {
5451                    match item {
5452                        TraitItem::Required(sig) => {
5453                            spans.insert(sig.name.value.as_str(), sig.name.span);
5454                        }
5455                        TraitItem::Provided(f) => {
5456                            spans.insert(f.name.value.as_str(), f.name.span);
5457                        }
5458                        _ => {}
5459                    }
5460                }
5461            }
5462        }
5463        t.methods
5464            .iter()
5465            .filter(|m| m.has_self)
5466            .map(|m| Member {
5467                name: m.name.clone(),
5468                kind: MemberKind::Method,
5469                decl: spans.get(m.name.as_str()).copied(),
5470            })
5471            .collect()
5472    }
5473
5474    /// The locals in scope now, innermost first, each name once, and within
5475    /// one scope in name order, with their types as inference has them.
5476    fn scope_snapshot(fcx: &FnCtx) -> Vec<(String, Ty)> {
5477        let mut seen: FxHashSet<&str> = FxHashSet::default();
5478        let mut out = Vec::new();
5479        for scope in fcx.scopes.iter().rev() {
5480            let mut names: Vec<(&String, &LocalId)> = scope.iter().collect();
5481            names.sort_by(|a, b| a.0.cmp(b.0));
5482            for (name, id) in names {
5483                if seen.insert(name.as_str()) {
5484                    out.push((name.clone(), fcx.locals[id.0 as usize].ty.clone()));
5485                }
5486            }
5487        }
5488        out
5489    }
5490
5491    /// Record the locals in scope if the probe's offset lies in `lo..=hi`
5492    /// of `file` (decision 4). The first record wins.
5493    fn probe_locals(&mut self, fcx: &FnCtx, file: nova_diagnostics::FileId, lo: u32, hi: u32) {
5494        let Some(p) = self.probe else {
5495            return;
5496        };
5497        if self.probe_locals_pending.is_some() || !self.probe_result.locals.is_empty() {
5498            return;
5499        }
5500        if file == p.file && lo <= p.offset && p.offset <= hi {
5501            self.probe_locals_pending = Some(Self::scope_snapshot(fcx));
5502        }
5503    }
5504
5505    fn check_field(
5506        &mut self,
5507        fcx: &mut FnCtx,
5508        target: &Spanned<ast::Expr>,
5509        field: &Spanned<String>,
5510        span: Span,
5511    ) -> hir::Expr {
5512        let recv = self.check_expr(fcx, target);
5513        let recv_ty = fcx.icx.apply(&recv.ty);
5514        self.probe_receiver(fcx, &recv_ty, target.span, field.span);
5515        // An unfinished `x.` has an empty name, and the parser has already
5516        // reported it (spec 3.2 §3.4).
5517        if field.value.is_empty() {
5518            return error_expr(span);
5519        }
5520        if let Some((index, field_ty)) =
5521            self.record_field_index_and_ty(fcx, &recv_ty, &field.value, span)
5522        {
5523            self.note_field(fcx, &recv_ty, index, field.span);
5524            return hir::Expr {
5525                kind: hir::ExprKind::FieldGet {
5526                    target: Box::new(recv),
5527                    index,
5528                },
5529                ty: field_ty,
5530                span,
5531            };
5532        }
5533        // A broken receiver already reported its own error; another one here
5534        // would just cascade.
5535        if matches!(recv_ty, Ty::Error) {
5536            return error_expr(span);
5537        }
5538        self.error(
5539            "E0014",
5540            self.no_field_message(fcx, &recv_ty, &field.value),
5541            field.span,
5542        );
5543        let fix = self.field_fix(&recv_ty, field);
5544        self.push_fix(fix);
5545        error_expr(span)
5546    }
5547
5548    /// The "no such field" message for a resolved, non-`Error` receiver type
5549    /// that failed `record_field_index_and_ty`: names the record when the
5550    /// receiver is one, otherwise describes the type. Shared by the field
5551    /// read and write paths (`check_field`, `check_field_set`) for the same
5552    /// reason `record_field_index_and_ty` itself is shared — so the two
5553    /// cannot independently drift on how the same mistake is phrased, which
5554    /// is exactly what had happened before this was pulled out (the write
5555    /// path said "no field `x` on type `P`" and had no separate wording for a
5556    /// non-record receiver at all).
5557    fn no_field_message(&self, fcx: &FnCtx, recv_ty: &Ty, field_name: &str) -> String {
5558        let record_name = match recv_ty {
5559            Ty::Record { def_id, .. } => self
5560                .records
5561                .iter()
5562                .find(|r| r.def_id == *def_id)
5563                .map(|r| r.name.clone()),
5564            _ => None,
5565        };
5566        match record_name {
5567            Some(record_name) => format!("no field `{field_name}` on record `{record_name}`"),
5568            None => format!(
5569                "cannot access field `{field_name}` on `{}`",
5570                self.show(recv_ty, fcx)
5571            ),
5572        }
5573    }
5574
5575    /// Resolve a field name on a record type to its `(index, instantiated type)`.
5576    /// Shared by the field read and field write paths so they cannot disagree
5577    /// about layout or about how a field's declared type is instantiated.
5578    ///
5579    /// "Instantiated", not merely "substituted": the type goes through
5580    /// `instantiate`, which substitutes the record's type arguments, applies the
5581    /// current inference bindings, and normalizes a projection that survives
5582    /// both. That matters because a field type may name a projection on one of
5583    /// the record's own bounded parameters (`f: fn(I::Item) -> U`), and `unify`
5584    /// never normalizes — see `instantiate`'s own doc comment.
5585    ///
5586    /// Emits no diagnostics: a `None` means "no such field on this type", and
5587    /// each caller phrases that in its own terms.
5588    fn record_field_index_and_ty(
5589        &mut self,
5590        fcx: &mut FnCtx,
5591        recv_ty: &Ty,
5592        field: &str,
5593        span: Span,
5594    ) -> Option<(u32, Ty)> {
5595        let Ty::Record { def_id, args } = fcx.icx.apply(recv_ty) else {
5596            return None;
5597        };
5598        let record = self.records.iter().find(|r| r.def_id == def_id)?;
5599        let index = record.fields.iter().position(|f| f.name == field)?;
5600        // The field's declared type is written in terms of the record's own
5601        // type parameters, so it must be substituted with this instantiation's
5602        // arguments before it means anything to the caller. Cloned out of
5603        // `record` (rather than substituted in place) so the borrow of
5604        // `self.records` ends here, before the `&mut self` call below.
5605        let declared_ty = record.fields[index].ty.clone();
5606        // Normalization seam, the read-side mirror of the one
5607        // `check_record_literal` needed for construction: by the time a field
5608        // is *read* (`m.f`, `c.hit`), `recv_ty`'s arguments may already be
5609        // concrete (`m`/`c` were already built), so a field type naming a
5610        // projection on the record's own bounded parameter (`f: fn(I::Item)
5611        // -> U`, `hit: I::Item`) can substitute to an unnormalized `Assoc`
5612        // just as a field *initializer*'s expected type could. Plain `subst`
5613        // cannot see that; `instantiate` applies the current bindings and, if
5614        // a projection remains, resolves it through the impl table.
5615        let field_ty = self.instantiate(&declared_ty, &args, &fcx.icx, span);
5616        Some((index as u32, field_ty))
5617    }
5618
5619    /// Resolve a method `name` on a receiver of (resolved) type `recv_ty`,
5620    /// without emitting diagnostics.
5621    fn resolve_method_on(&self, recv_ty: &Ty, fcx: &FnCtx, name: &str) -> MethodRes {
5622        match recv_ty {
5623            Ty::Param(k) => {
5624                let bounds = fcx.param_bounds.get(*k as usize);
5625                let matches: Vec<(DefId, u32)> = bounds
5626                    .into_iter()
5627                    .flatten()
5628                    .filter_map(|&tid| self.trait_method_index(tid, name).map(|i| (tid, i)))
5629                    .collect();
5630                match matches.len() {
5631                    0 => MethodRes::None,
5632                    1 => MethodRes::Trait(matches[0].0, matches[0].1),
5633                    _ => MethodRes::Ambiguous,
5634                }
5635            }
5636            _ => {
5637                let Some(head) = recv_ty.head() else {
5638                    return MethodRes::None;
5639                };
5640                // Inherent methods take priority over trait methods — but only
5641                // an impl that actually fits the receiver counts. A restricted
5642                // inherent impl (`impl<T> Pair<T, T>`) must not shadow an
5643                // applicable trait method when the receiver (`Pair<Int, Str>`)
5644                // does not fit it.
5645                if let Some(def) = self.find_inherent_method(recv_ty, head, name) {
5646                    return MethodRes::Inherent(def);
5647                }
5648                let matches: Vec<(DefId, u32)> = self
5649                    .impls
5650                    .iter()
5651                    .filter(|i| i.self_head == head && i.match_args(recv_ty).is_some())
5652                    .filter_map(|i| i.trait_id)
5653                    .filter_map(|tid| self.trait_method_index(tid, name).map(|idx| (tid, idx)))
5654                    .collect();
5655                match matches.len() {
5656                    0 => MethodRes::None,
5657                    1 => MethodRes::Trait(matches[0].0, matches[0].1),
5658                    _ => MethodRes::Ambiguous,
5659                }
5660            }
5661        }
5662    }
5663
5664    /// The index of the trait method `name` that can be *called on a receiver*.
5665    /// A receiver-less method is skipped, exactly as `find_inherent_method` skips
5666    /// a `selfless` inherent method: there is no receiver slot to bind `x` into,
5667    /// so `x.zero()` must report the existing `E0014: no method` rather than
5668    /// dispatch a receiver into a signature that has no place for it — which
5669    /// lowered to a call with one argument too many and ICEd in codegen.
5670    fn trait_method_index(&self, trait_id: DefId, name: &str) -> Option<u32> {
5671        self.traits
5672            .iter()
5673            .find(|t| t.def_id == trait_id)?
5674            .methods
5675            .iter()
5676            .position(|m| m.name == name && m.has_self)
5677            .map(|i| i as u32)
5678    }
5679
5680    /// The index of the trait *associated function* (no `self` receiver) named
5681    /// `name` — the `Type::name(…)` counterpart of `trait_method_index`.
5682    fn trait_assoc_fn_index(&self, trait_id: DefId, name: &str) -> Option<u32> {
5683        self.traits
5684            .iter()
5685            .find(|t| t.def_id == trait_id)?
5686            .methods
5687            .iter()
5688            .position(|m| m.name == name && !m.has_self)
5689            .map(|i| i as u32)
5690    }
5691
5692    fn find_inherent_method(&self, recv_ty: &Ty, head: TyHead, name: &str) -> Option<DefId> {
5693        self.impls
5694            .iter()
5695            .filter(|i| i.trait_id.is_none() && i.self_head == head)
5696            // The receiver must fit the impl's self-type pattern, not just its
5697            // head, so `impl<T> Pair<T, T>` is skipped for `Pair<Int, String>`.
5698            .filter(|i| i.match_args(recv_ty).is_some())
5699            .find_map(|i| {
5700                // An associated function has no receiver to bind, so it is not a
5701                // candidate for `x.m()`; skipping it here reports the existing
5702                // `E0014: no method` instead of dispatching a receiver into a
5703                // signature that has no slot for it.
5704                i.methods
5705                    .iter()
5706                    .find(|(n, d)| n == name && !self.selfless.contains(d))
5707                    .map(|(_, d)| *d)
5708            })
5709    }
5710
5711    /// Find every self-less method named `name` on an inherent impl whose self
5712    /// type has the head `head`. Associated functions have no receiver, so
5713    /// selection is by the impl's nominal head only — there is no receiver type
5714    /// to run `match_args` against, unlike `find_inherent_method`. That is
5715    /// deliberately permissive: `Box::make(5)` must reach
5716    /// `impl Box<Int> { fn make(…) }` even though the qualifier's type argument
5717    /// is still an inference variable at this point, so filtering by
5718    /// `match_args` (which compares structurally, and so cannot line `Int` up
5719    /// with a variable) would reject the single-candidate case users want.
5720    ///
5721    /// The price of that permissiveness is that two *disjoint concrete* impls
5722    /// of a generic type — `impl Box<Int>` and `impl Box<Bool>` — are both
5723    /// candidates. `check_impl_coherence` does not catch that pair either
5724    /// (their self types do not overlap, so there is no `E0074`), so returning
5725    /// the first would make dispatch depend on impl declaration order, the
5726    /// exact invariant that check exists to protect. Hence every candidate is
5727    /// returned and the caller reports `E0015`, mirroring
5728    /// `find_trait_assoc_fns`.
5729    ///
5730    /// Keyed on the head rather than a type `DefId` so that `impl Int { fn … }`
5731    /// is reachable: a primitive has no `DefId` at all (it never enters the
5732    /// resolver's type namespace), yet `collect_impls` records an impl on one
5733    /// under its primitive head just like any other.
5734    fn find_assoc_fns(&self, head: TyHead, name: &str) -> Vec<DefId> {
5735        self.impls
5736            .iter()
5737            .filter(|i| i.trait_id.is_none() && i.self_head == head)
5738            .filter_map(|i| {
5739                i.methods
5740                    .iter()
5741                    .find(|(n, d)| n == name && self.selfless.contains(d))
5742                    .map(|(_, d)| *d)
5743            })
5744            .collect()
5745    }
5746
5747    /// The self type named by a two-segment path's qualifier (`Int::zero()`,
5748    /// `P::new()`, `Box::make(…)`), for associated-function lookup.
5749    ///
5750    /// A primitive type name never enters the resolver's *type* namespace —
5751    /// `insert_type` is only called for `record`/`type` items, so
5752    /// `Definitions::resolve_type(module, "Int")` is always `None` — and is
5753    /// instead a separate arm of `convert_ty`. Matching those names here produces
5754    /// exactly the `Ty` `convert_ty` produces, hence exactly the `TyHead` that
5755    /// `collect_impls` recorded as an impl's `self_head`, so `impl Zero for Int`
5756    /// is reachable from `Int::zero()`.
5757    ///
5758    /// A generic nominal type's arguments become fresh inference variables,
5759    /// recovered from the call's context the way `emit_assoc_call` recovers an
5760    /// impl's generics; an unresolvable one is reported by the residual
5761    /// inference-variable check.
5762    fn qualifier_self_ty(&self, fcx: &mut FnCtx, name: &str) -> Option<Ty> {
5763        // Every name matched here must also be in
5764        // `nova_resolver::RESERVED_TYPE_NAMES`, the same requirement
5765        // `convert_ty`'s own nullary table is already held to: a new
5766        // nullary built-in added to this match without joining that list
5767        // would leave a user type under its name declarable, which is
5768        // exactly the defect the list exists to close.
5769        match name {
5770            "Int" => return Some(Ty::Int),
5771            "Float" => return Some(Ty::Float),
5772            "Bool" => return Some(Ty::Bool),
5773            "Char" => return Some(Ty::Char),
5774            "String" => return Some(Ty::String),
5775            "Bytes" => return Some(Ty::Bytes),
5776            // `Future` is compiler-constructed and carries no associated
5777            // functions; `Future::f()` is not a qualifier. Returning None here
5778            // (rather than falling through to resolve_type) keeps the
5779            // diagnostic about the qualifier instead of about a missing type.
5780            "Future" => return None,
5781            _ => {}
5782        }
5783        let def_id = self.defs.resolve_type(self.cur_module, name)?;
5784        let arity = self.type_arity.get(&def_id).copied().unwrap_or(0);
5785        let args: Vec<Ty> = (0..arity).map(|_| fcx.icx.fresh()).collect();
5786        match self.defs.def(def_id).kind {
5787            DefKind::Record { .. } => Some(Ty::Record { def_id, args }),
5788            DefKind::Sum { .. } => Some(Ty::Sum { def_id, args }),
5789            _ => None,
5790        }
5791    }
5792
5793    /// Find a receiver-less trait method `name` reachable through a trait impl
5794    /// for `self_ty`. Selection mirrors `resolve_method_on`'s trait branch — an
5795    /// impl must share the head *and* fit the self type — restricted to methods
5796    /// the trait declares without a `self` receiver, since a `Type::name(…)` call
5797    /// site has no receiver to pass. Returns every candidate so the caller can
5798    /// report ambiguity rather than silently picking one.
5799    ///
5800    /// A qualifier whose generic arguments are still inference variables
5801    /// (`Box::zero()`) only matches an impl whose self type is generic in the
5802    /// same positions (`impl<T> Zero for Box<T>`), because `match_args` compares
5803    /// structurally: a concrete `impl Zero for Box<Int>` is not found until the
5804    /// qualifier's argument is already known, which at a two-segment path it
5805    /// never is. Concrete and fully-generic self types both work; the caller
5806    /// reports the unresolved-argument case as E0011, naming the type
5807    /// arguments as the reason rather than reporting a missing associated
5808    /// function that in fact exists.
5809    fn find_trait_assoc_fns(&self, self_ty: &Ty, name: &str) -> Vec<(DefId, u32)> {
5810        let Some(head) = self_ty.head() else {
5811            return Vec::new();
5812        };
5813        self.impls
5814            .iter()
5815            .filter(|i| i.self_head == head && i.match_args(self_ty).is_some())
5816            .filter_map(|i| i.trait_id)
5817            .filter_map(|tid| self.trait_assoc_fn_index(tid, name).map(|idx| (tid, idx)))
5818            .collect()
5819    }
5820
5821    /// Check `receiver.method(args)`. `receiver` is the already-checked HIR;
5822    /// `receiver_ast` is the same expression before checking, needed because the
5823    /// mutable-receiver rule classifies it with `place_root`.
5824    ///
5825    /// **The rule covers both dispatch paths.** An inherent callee is checked
5826    /// here, keyed on the impl method's `DefId` in `Checker::mut_self`; a trait
5827    /// callee is checked inside [`Checker::emit_trait_call`], keyed on
5828    /// [`hir::TraitMethod::mut_self`], because that is the one point *every*
5829    /// receiver route converges on and this arm is not (see the note there).
5830    /// Either way the receiver AST is what gets classified, which is why it is
5831    /// threaded through [`TraitCallSelf::Receiver`] rather than left behind.
5832    fn check_method_call(
5833        &mut self,
5834        fcx: &mut FnCtx,
5835        receiver: hir::Expr,
5836        receiver_ast: &Spanned<ast::Expr>,
5837        method: &Spanned<String>,
5838        args: Vec<hir::Expr>,
5839        span: Span,
5840    ) -> hir::Expr {
5841        let recv_ty = fcx.icx.apply(&receiver.ty);
5842        self.probe_receiver(fcx, &recv_ty, receiver.span, method.span);
5843        if matches!(recv_ty, Ty::Error) {
5844            return error_expr(span);
5845        }
5846        if matches!(recv_ty, Ty::Var(_)) {
5847            self.error(
5848                "E0011",
5849                "cannot infer the receiver's type; add a type annotation",
5850                receiver.span,
5851            );
5852            return error_expr(span);
5853        }
5854        // Built-in array methods.
5855        if matches!(recv_ty, Ty::Array(_)) {
5856            if method.value == "len" && args.is_empty() {
5857                self.note_use(method.span, Target::BuiltinMethod("len"));
5858                return hir::Expr {
5859                    kind: hir::ExprKind::ArrayLen {
5860                        target: Box::new(receiver),
5861                    },
5862                    ty: Ty::Int,
5863                    span,
5864                };
5865            }
5866            self.error(
5867                "E0014",
5868                format!(
5869                    "no method `{}` on array type `{}`",
5870                    method.value,
5871                    self.show(&recv_ty, fcx)
5872                ),
5873                method.span,
5874            );
5875            self.push_fix(suggest::change_to(method, ["len"]));
5876            return error_expr(span);
5877        }
5878        match self.resolve_method_on(&recv_ty, fcx, &method.value) {
5879            MethodRes::Inherent(def_id) => {
5880                self.note_use(method.span, Target::Def(def_id));
5881                self.check_mutable_receiver(fcx, def_id, receiver_ast, span);
5882                self.emit_inherent_call(fcx, def_id, receiver, args, span)
5883            }
5884            MethodRes::Trait(trait_id, method_idx) => {
5885                self.note_use(method.span, Target::TraitMethod(trait_id, method_idx));
5886                self.emit_trait_call(
5887                    fcx,
5888                    trait_id,
5889                    method_idx,
5890                    TraitCallSelf::Receiver(receiver, receiver_ast),
5891                    args,
5892                    span,
5893                )
5894            }
5895            MethodRes::Ambiguous => {
5896                self.error(
5897                    "E0015",
5898                    format!(
5899                        "ambiguous method call `{}`: more than one trait in scope provides it",
5900                        method.value
5901                    ),
5902                    method.span,
5903                );
5904                error_expr(span)
5905            }
5906            MethodRes::None => {
5907                self.error(
5908                    "E0014",
5909                    format!(
5910                        "no method `{}` on type `{}`",
5911                        method.value,
5912                        self.show(&recv_ty, fcx)
5913                    ),
5914                    method.span,
5915                );
5916                let fix = self.method_fix(&recv_ty, fcx, method);
5917                self.push_fix(fix);
5918                error_expr(span)
5919            }
5920        }
5921    }
5922
5923    /// A method declaring `mut self` mutates its receiver, so the receiver must
5924    /// be reachable through a mutable binding — the same requirement, and the
5925    /// same `place_root` walk, as `arr[i] = v` and `rec.f = v`. Without it
5926    /// `v.push(x)` would mutate `v` after `let v = …` while `v.field = x` on the
5927    /// same binding was rejected: one operation, two answers (ADR 0005).
5928    ///
5929    /// A no-op for any method that does not declare `mut self`, so a plain
5930    /// `self` reader still works on an immutable binding.
5931    fn check_mutable_receiver(
5932        &mut self,
5933        fcx: &FnCtx,
5934        def_id: DefId,
5935        receiver_ast: &Spanned<ast::Expr>,
5936        span: Span,
5937    ) {
5938        if !self.mut_self.contains(&def_id) {
5939            return;
5940        }
5941        let mname = self.defs.def(def_id).name.clone();
5942        self.require_mutable_place(fcx, receiver_ast, span, MutTarget::Receiver(mname));
5943    }
5944
5945    fn emit_inherent_call(
5946        &mut self,
5947        fcx: &mut FnCtx,
5948        def_id: DefId,
5949        receiver: hir::Expr,
5950        args: Vec<hir::Expr>,
5951        span: Span,
5952    ) -> hir::Expr {
5953        // Everything below assumes a receiver at `sig.params[0]`; an associated
5954        // function has none, so the arity arithmetic and the receiver unification
5955        // would both be off by one. `find_inherent_method` already skips these —
5956        // this guard keeps any future caller from reintroducing the codegen ICE.
5957        if self.selfless.contains(&def_id) {
5958            let mname = self.defs.def(def_id).name.clone();
5959            self.error(
5960                "E0014",
5961                format!(
5962                    "`{mname}` is an associated function with no `self` receiver; \
5963                     call it as `Type::{mname}(…)`"
5964                ),
5965                span,
5966            );
5967            return error_expr(span);
5968        }
5969        let Some(sig) = self.sigs.get(&def_id).cloned() else {
5970            return error_expr(span);
5971        };
5972        // sig.params[0] is `self`; the rest are the declared parameters.
5973        let expected_args = sig.params.len().saturating_sub(1);
5974        if args.len() != expected_args {
5975            // One wording for every arity error in this family (see also
5976            // `emit_assoc_call`, `emit_trait_call`, and the free-function call in
5977            // `check_call`), so the same mistake always reads the same way.
5978            let mname = self.defs.def(def_id).name.clone();
5979            self.error(
5980                "E0016",
5981                format!(
5982                    "`{mname}` takes {expected_args} argument(s) but {} were supplied",
5983                    args.len()
5984                ),
5985                span,
5986            );
5987            return error_expr(span);
5988        }
5989        // A method on a generic impl (`impl<T> Box<T> { … }`) is generic over
5990        // the impl's parameters; instantiate them with fresh inference vars so
5991        // the receiver/args recover them (e.g. `T = Int` from a `Box<Int>`).
5992        let type_args: Vec<Ty> = (0..sig.generics).map(|_| fcx.icx.fresh()).collect();
5993        let self_param = sig.params[0].subst(&type_args);
5994        // The receiver must fit the impl's self-type pattern exactly — for a
5995        // repeated/partially-concrete self type (`impl<T> Pair<T, T>`) merely
5996        // sharing a head is not enough, and a silent mismatch would dispatch to
5997        // a wrongly specialized method.
5998        if !fcx.icx.unify(&receiver.ty, &self_param) {
5999            let mname = self.defs.def(def_id).name.clone();
6000            self.error(
6001                "E0014",
6002                format!(
6003                    "method `{mname}` does not apply to receiver of type `{}`",
6004                    self.show(&receiver.ty, fcx),
6005                ),
6006                span,
6007            );
6008            return error_expr(span);
6009        }
6010        for (arg, param) in args.iter().zip(sig.params[1..].iter()) {
6011            let expected = param.subst(&type_args);
6012            if !fcx.icx.unify(&arg.ty, &expected) {
6013                self.error(
6014                    "E0010",
6015                    format!(
6016                        "argument has type `{}` but `{}` was expected",
6017                        self.show(&arg.ty, fcx),
6018                        self.show(&expected, fcx),
6019                    ),
6020                    arg.span,
6021                );
6022            }
6023        }
6024        let ret = sig.ret.subst(&type_args);
6025        let mut call_args = Vec::with_capacity(args.len() + 1);
6026        call_args.push(receiver);
6027        call_args.extend(args);
6028        hir::Expr {
6029            kind: hir::ExprKind::Call {
6030                func: hir::Callee::Def(def_id),
6031                type_args,
6032                args: call_args,
6033            },
6034            ty: ret,
6035            span,
6036        }
6037    }
6038
6039    /// Emit a call to an associated function (`Type::f(args)`). Unlike
6040    /// `emit_inherent_call`, there is no receiver: `sig.params` holds exactly
6041    /// the declared parameters (see `Checker::selfless`), so arity is compared
6042    /// directly rather than via `saturating_sub(1)`. The impl's generic
6043    /// parameters cannot be recovered from a receiver either, so they become
6044    /// fresh inference variables resolved by the surrounding context (e.g. a
6045    /// `let` annotation); an unresolved one is reported by the existing
6046    /// residual-inference-variable check.
6047    fn emit_assoc_call(
6048        &mut self,
6049        fcx: &mut FnCtx,
6050        def_id: DefId,
6051        args: Vec<hir::Expr>,
6052        span: Span,
6053    ) -> hir::Expr {
6054        let Some(sig) = self.sigs.get(&def_id).cloned() else {
6055            return error_expr(span);
6056        };
6057        if args.len() != sig.params.len() {
6058            let fname = self.defs.def(def_id).name.clone();
6059            self.error(
6060                "E0016",
6061                format!(
6062                    "`{fname}` takes {} argument(s) but {} were supplied",
6063                    sig.params.len(),
6064                    args.len()
6065                ),
6066                span,
6067            );
6068            return error_expr(span);
6069        }
6070        let type_args: Vec<Ty> = (0..sig.generics).map(|_| fcx.icx.fresh()).collect();
6071        for (arg, param) in args.iter().zip(sig.params.iter()) {
6072            let expected = param.subst(&type_args);
6073            if !fcx.icx.unify(&arg.ty, &expected) {
6074                self.error(
6075                    "E0010",
6076                    format!(
6077                        "argument has type `{}` but `{}` was expected",
6078                        self.show(&arg.ty, fcx),
6079                        self.show(&expected, fcx),
6080                    ),
6081                    arg.span,
6082                );
6083            }
6084        }
6085        let ret = sig.ret.subst(&type_args);
6086        hir::Expr {
6087            kind: hir::ExprKind::Call {
6088                func: hir::Callee::Def(def_id),
6089                type_args,
6090                args,
6091            },
6092            ty: ret,
6093            span,
6094        }
6095    }
6096
6097    /// Emit a trait method call — the sole constructor of
6098    /// [`hir::ExprKind::TraitCall`]. `dispatch` says where `Self` comes from and
6099    /// so distinguishes a receiver call (`x.cmp(y)`) from an associated-function
6100    /// call (`Int::default()`, or `T::default()` inside a generic function);
6101    /// everything else — the `has_self`/receiver agreement check, the flat
6102    /// substitution, arity, argument unification, the result type — is shared,
6103    /// which is the point. For [`TraitCallSelf::Qualifier`], `Self` is a concrete
6104    /// type for `Int::zero()` or `Param(k)` when dispatching through a generic
6105    /// parameter's bound (`T::zero()` inside `fn f<T: Zero>()`), which
6106    /// monomorphization resolves once `T` is known, exactly as it resolves a
6107    /// bounded instance method call.
6108    fn emit_trait_call(
6109        &mut self,
6110        fcx: &mut FnCtx,
6111        trait_id: DefId,
6112        method_idx: u32,
6113        dispatch: TraitCallSelf<'_>,
6114        args: Vec<hir::Expr>,
6115        span: Span,
6116    ) -> hir::Expr {
6117        let Some(tm) = self
6118            .traits
6119            .iter()
6120            .find(|t| t.def_id == trait_id)
6121            .and_then(|t| t.methods.get(method_idx as usize))
6122            .cloned()
6123        else {
6124            return error_expr(span);
6125        };
6126        // The receiver's presence must agree with what the trait declares.
6127        // `tm.params` never stores `self`, so it cannot reveal a mismatch: the
6128        // arity check below would pass and MIR would lower one argument too many
6129        // (or too few), which is the Cranelift "mismatched argument count" ICE in
6130        // one direction or the other. `trait_method_index` and
6131        // `trait_assoc_fn_index` already partition candidates by `has_self`, so
6132        // neither arm is reachable today — this keeps any future caller from
6133        // reintroducing the ICE, mirroring `emit_inherent_call`, and being one
6134        // check covering both directions it cannot rot on only one side.
6135        let (self_ty, receiver) = match dispatch {
6136            TraitCallSelf::Receiver(..) if !tm.has_self => {
6137                self.error(
6138                    "E0014",
6139                    format!(
6140                        "`{}` is an associated function with no `self` receiver; \
6141                         call it as `Type::{}(…)`",
6142                        tm.name, tm.name
6143                    ),
6144                    span,
6145                );
6146                return error_expr(span);
6147            }
6148            // `Self` is the receiver's type; deriving it here rather than taking
6149            // it from the caller is what makes the two impossible to disagree.
6150            TraitCallSelf::Receiver(recv, recv_ast) => {
6151                // The mutable-receiver rule (ADR 0005 §1), on the trait path.
6152                // Keyed on the *trait method's* flag, not on any impl's: for a
6153                // generic receiver there is no single impl, and the trait's
6154                // declaration is what the call site programs against anyway —
6155                // `check_impl_method_signatures` is what keeps the impl in step.
6156                //
6157                // Here rather than in `check_method_call`'s `MethodRes::Trait`
6158                // arm, which is where ADR 0005's migration path put it, because
6159                // that arm is not the only receiver route: `try_display` reaches
6160                // a `fmt(mut self) -> String` straight from string interpolation
6161                // without passing through it at all. This is the one point every
6162                // receiver route converges on, so the rule cannot be dodged by
6163                // finding another way in. Runs *before* the arity and argument
6164                // checks below and does not return early, so a missing `let mut`
6165                // stays one diagnostic rather than cascading — the same order
6166                // and the same reasoning as `MethodRes::Inherent`.
6167                if tm.mut_self {
6168                    // The callee is named by the trait's declared method name,
6169                    // unqualified: `Self` may still be a generic parameter here,
6170                    // so there is no impl to qualify with. That is the same
6171                    // choice every other diagnostic at this dispatch site makes,
6172                    // and `arity_errors_name_the_callee_uniformly` pins it.
6173                    self.require_mutable_place(
6174                        fcx,
6175                        recv_ast,
6176                        span,
6177                        MutTarget::Receiver(tm.name.clone()),
6178                    );
6179                }
6180                (fcx.icx.apply(&recv.ty), Some(Box::new(recv)))
6181            }
6182            TraitCallSelf::Qualifier(_) if tm.has_self => {
6183                self.error(
6184                    "E0014",
6185                    format!(
6186                        "`{}` is a method with a `self` receiver; \
6187                         call it on a value as `value.{}(…)`",
6188                        tm.name, tm.name
6189                    ),
6190                    span,
6191                );
6192                return error_expr(span);
6193            }
6194            TraitCallSelf::Qualifier(ty) => (ty, None),
6195        };
6196        // Substitution over the trait method's flat Param space: `Self` is
6197        // Param(0); the method's own generics are Param(1..) and become fresh
6198        // inference vars, recovered from the argument types like a generic fn.
6199        // `hir::TraitMethod::bounds` is indexed by that same flat position, so
6200        // this order is not a convention but a requirement.
6201        let type_args: Vec<Ty> = (0..tm.generics).map(|_| fcx.icx.fresh()).collect();
6202        let mut subst = Vec::with_capacity(1 + type_args.len());
6203        subst.push(self_ty.clone());
6204        subst.extend(type_args.iter().cloned());
6205        if args.len() != tm.params.len() {
6206            self.error(
6207                "E0016",
6208                format!(
6209                    "`{}` takes {} argument(s) but {} were supplied",
6210                    tm.name,
6211                    tm.params.len(),
6212                    args.len()
6213                ),
6214                span,
6215            );
6216            return error_expr(span);
6217        }
6218        for (arg, param) in args.iter().zip(tm.params.iter()) {
6219            // Same seam as the return type below: a trait method may declare a
6220            // *parameter* as `Self::Item` (`fn put(mut self, x: Self::Item)`) and
6221            // the argument the caller passes is a concrete type.
6222            let expected = self.instantiate(param, &subst, &fcx.icx, arg.span);
6223            if !fcx.icx.unify(&arg.ty, &expected) {
6224                self.error(
6225                    "E0010",
6226                    format!(
6227                        "argument has type `{}` but `{}` was expected",
6228                        self.show(&arg.ty, fcx),
6229                        self.show(&expected, fcx),
6230                    ),
6231                    arg.span,
6232                );
6233            }
6234        }
6235        // Normalization seam: the method-call return type. A trait method
6236        // declaring `-> Self::Item` gives `Assoc { on: Param(0) }`, and `subst`
6237        // has just replaced `Param(0)` with the receiver's *concrete* type — so
6238        // this is the first point at which the impl table can answer it, and the
6239        // last point before the type escapes into the caller's unification.
6240        //
6241        // `self_ty` came from `fcx.icx.apply(&recv.ty)` above, so the `Self` half
6242        // of `subst` is already resolved; `instantiate` covers the rest, for a
6243        // generic trait method projecting onto one of its own parameters.
6244        let ret = self.instantiate(&tm.ret, &subst, &fcx.icx, span);
6245        hir::Expr {
6246            kind: hir::ExprKind::TraitCall {
6247                trait_id,
6248                method: method_idx,
6249                self_ty,
6250                type_args,
6251                receiver,
6252                args,
6253            },
6254            ty: ret,
6255            span,
6256        }
6257    }
6258
6259    /// If `recv_ty` has a `Display`-style `fmt(self) -> String` method in
6260    /// scope, build the call that produces its string. Used to interpolate
6261    /// user types. Returns `None` (leaving `value` consumed) if no such
6262    /// method resolves.
6263    ///
6264    /// `value_ast` is `value` before checking, threaded through purely so
6265    /// [`Checker::emit_trait_call`] can apply the mutable-receiver rule: nothing
6266    /// stops a user trait from declaring `fn fmt(mut self) -> String`, and this
6267    /// is a receiver route that never passes through
6268    /// [`Checker::check_method_call`]. `std/core`'s own `Display` declares a
6269    /// plain `self`, so the flag is false for every interpolation in std and the
6270    /// check is a no-op there.
6271    fn try_display(
6272        &mut self,
6273        fcx: &mut FnCtx,
6274        value: hir::Expr,
6275        value_ast: &Spanned<ast::Expr>,
6276        recv_ty: &Ty,
6277    ) -> Option<hir::Expr> {
6278        let (trait_id, method_idx) = match self.resolve_method_on(recv_ty, fcx, "fmt") {
6279            MethodRes::Trait(t, i) => (t, i),
6280            _ => return None,
6281        };
6282        // The method must take no extra args and return `String`.
6283        let tm = self
6284            .traits
6285            .iter()
6286            .find(|t| t.def_id == trait_id)?
6287            .methods
6288            .get(method_idx as usize)?;
6289        if !tm.params.is_empty() || tm.ret != Ty::String {
6290            return None;
6291        }
6292        let span = value.span;
6293        Some(self.emit_trait_call(
6294            fcx,
6295            trait_id,
6296            method_idx,
6297            TraitCallSelf::Receiver(value, value_ast),
6298            Vec::new(),
6299            span,
6300        ))
6301    }
6302
6303    fn check_binary(
6304        &mut self,
6305        fcx: &mut FnCtx,
6306        op: ast::BinOp,
6307        lhs: &Spanned<ast::Expr>,
6308        rhs: &Spanned<ast::Expr>,
6309        span: Span,
6310    ) -> hir::Expr {
6311        // Short-circuit operators get dedicated control-flow nodes.
6312        if matches!(op, ast::BinOp::And | ast::BinOp::Or) {
6313            let l = self.check_expr(fcx, lhs);
6314            let r = self.check_expr(fcx, rhs);
6315            self.expect_ty(fcx, &l, &Ty::Bool, "a logical operand");
6316            self.expect_ty(fcx, &r, &Ty::Bool, "a logical operand");
6317            let kind = if matches!(op, ast::BinOp::And) {
6318                hir::ExprKind::LogicalAnd {
6319                    lhs: Box::new(l),
6320                    rhs: Box::new(r),
6321                }
6322            } else {
6323                hir::ExprKind::LogicalOr {
6324                    lhs: Box::new(l),
6325                    rhs: Box::new(r),
6326                }
6327            };
6328            return hir::Expr {
6329                kind,
6330                ty: Ty::Bool,
6331                span,
6332            };
6333        }
6334
6335        let l = self.check_expr(fcx, lhs);
6336        let r = self.check_expr(fcx, rhs);
6337        let hir_op = convert_binop(op);
6338        let ty = self.binary_result_ty(fcx, hir_op, &l, &r, span);
6339        hir::Expr {
6340            kind: hir::ExprKind::Binary {
6341                op: hir_op,
6342                lhs: Box::new(l),
6343                rhs: Box::new(r),
6344            },
6345            ty,
6346            span,
6347        }
6348    }
6349
6350    /// Determine the result type of a binary operation and validate the
6351    /// operand types (E0013 when an operator isn't defined for a type).
6352    fn binary_result_ty(
6353        &mut self,
6354        fcx: &mut FnCtx,
6355        op: hir::BinOp,
6356        lhs: &hir::Expr,
6357        rhs: &hir::Expr,
6358        span: Span,
6359    ) -> Ty {
6360        use hir::BinOp::*;
6361        if !fcx.icx.unify(&lhs.ty, &rhs.ty) {
6362            self.error(
6363                "E0010",
6364                format!(
6365                    "mismatched operand types: `{}` vs `{}`",
6366                    self.show(&lhs.ty, fcx),
6367                    self.show(&rhs.ty, fcx),
6368                ),
6369                span,
6370            );
6371            return Ty::Error;
6372        }
6373        let mut operand = fcx.icx.apply(&lhs.ty);
6374        if matches!(operand, Ty::Var(_)) {
6375            // Unconstrained operand (e.g. two fresh generic results):
6376            // default to Int, matching the spec's literal-defaulting rule.
6377            fcx.icx.unify(&operand, &Ty::Int);
6378            operand = Ty::Int;
6379        }
6380        match op {
6381            Add | Sub | Mul | Div | Rem => match operand {
6382                Ty::Int | Ty::Float | Ty::Never | Ty::Error => operand,
6383                other => {
6384                    self.op_not_defined(fcx, "arithmetic", &other, span);
6385                    Ty::Error
6386                }
6387            },
6388            Lt | Le | Gt | Ge => match operand {
6389                Ty::Int | Ty::Float | Ty::Char | Ty::Never | Ty::Error => Ty::Bool,
6390                other => {
6391                    self.op_not_defined(fcx, "comparison", &other, span);
6392                    Ty::Bool
6393                }
6394            },
6395            Eq | Ne => match operand {
6396                Ty::Int
6397                | Ty::Float
6398                | Ty::Bool
6399                | Ty::Char
6400                | Ty::String
6401                | Ty::Unit
6402                | Ty::Never
6403                | Ty::Error => Ty::Bool,
6404                other => {
6405                    self.op_not_defined(fcx, "equality", &other, span);
6406                    Ty::Bool
6407                }
6408            },
6409            BitAnd | BitOr | BitXor | Shl | Shr => match operand {
6410                Ty::Int | Ty::Never | Ty::Error => Ty::Int,
6411                other => {
6412                    self.op_not_defined(fcx, "bitwise", &other, span);
6413                    Ty::Error
6414                }
6415            },
6416        }
6417    }
6418
6419    fn op_not_defined(&mut self, fcx: &FnCtx, kind: &str, ty: &Ty, span: Span) {
6420        self.error(
6421            "E0013",
6422            format!(
6423                "{kind} operators are not defined for `{}` \
6424                 (operator traits arrive later in Phase 1)",
6425                self.show(ty, fcx),
6426            ),
6427            span,
6428        );
6429    }
6430
6431    fn check_unary(
6432        &mut self,
6433        fcx: &mut FnCtx,
6434        op: ast::UnOp,
6435        inner: &Spanned<ast::Expr>,
6436        span: Span,
6437    ) -> hir::Expr {
6438        let value = self.check_expr(fcx, inner);
6439        let (hir_op, ty) = match op {
6440            ast::UnOp::Neg => {
6441                let t = fcx.icx.apply(&value.ty);
6442                let t = match t {
6443                    Ty::Var(_) => {
6444                        fcx.icx.unify(&t, &Ty::Int);
6445                        Ty::Int
6446                    }
6447                    Ty::Int | Ty::Float | Ty::Never | Ty::Error => t,
6448                    other => {
6449                        self.op_not_defined(fcx, "negation", &other, span);
6450                        Ty::Error
6451                    }
6452                };
6453                (hir::UnOp::Neg, t)
6454            }
6455            ast::UnOp::Not => {
6456                self.expect_ty(fcx, &value, &Ty::Bool, "the `!` operator");
6457                (hir::UnOp::Not, Ty::Bool)
6458            }
6459            ast::UnOp::BitNot => {
6460                self.expect_ty(fcx, &value, &Ty::Int, "the `~` operator");
6461                (hir::UnOp::BitNot, Ty::Int)
6462            }
6463            ast::UnOp::Ref | ast::UnOp::RefMut | ast::UnOp::Deref => {
6464                self.unsupported(span, "reference operators");
6465                return error_expr(span);
6466            }
6467        };
6468        hir::Expr {
6469            kind: hir::ExprKind::Unary {
6470                op: hir_op,
6471                expr: Box::new(value),
6472            },
6473            ty,
6474            span,
6475        }
6476    }
6477
6478    fn check_assign(
6479        &mut self,
6480        fcx: &mut FnCtx,
6481        op: ast::AssignOp,
6482        lhs: &Spanned<ast::Expr>,
6483        rhs: &Spanned<ast::Expr>,
6484        span: Span,
6485    ) -> hir::Expr {
6486        // Element assignment `arr[i] = v`.
6487        if let ast::Expr::Index { target, index } = &lhs.value {
6488            return self.check_index_set(fcx, op, target, index, rhs, span);
6489        }
6490        // Field assignment `rec.field = v`.
6491        if let ast::Expr::Field { target, field } = &lhs.value {
6492            return self.check_field_set(fcx, op, target, field, rhs, span);
6493        }
6494        let ast::Expr::Path(path) = &lhs.value else {
6495            // `arr[i] = v` and `rec.f = v` are handled above, so what is left is
6496            // a target with no assignable place at all (a call result, a
6497            // literal, a parenthesized expression, …).
6498            self.unsupported(
6499                lhs.span,
6500                "assignments to anything but a local variable, array element, or record field",
6501            );
6502            return error_expr(span);
6503        };
6504        let name = if path.segments.len() == 1 {
6505            path.segments[0].value.as_str()
6506        } else {
6507            self.unsupported(lhs.span, "assignment to paths");
6508            return error_expr(span);
6509        };
6510        let Some(local) = fcx.lookup(name) else {
6511            self.error(
6512                "E0001",
6513                format!("cannot find `{name}` in this scope"),
6514                lhs.span,
6515            );
6516            let nearby = fixes::visible_locals(fcx);
6517            self.push_fix(suggest::change_to(
6518                &path.segments[0],
6519                nearby.iter().map(String::as_str),
6520            ));
6521            return error_expr(span);
6522        };
6523        self.note_use(
6524            path.segments[0].span,
6525            Target::Local(fcx.locals[local.0 as usize].span),
6526        );
6527        let info = fcx.locals[local.0 as usize].clone();
6528        if !info.is_mut {
6529            self.error(
6530                "E0060",
6531                format!("cannot assign to immutable variable `{name}`"),
6532                span,
6533            );
6534            // Spec 3.4b §3.3: where a fix is offered, it says what the note
6535            // would.
6536            match self.mutable_fix(name, info.span) {
6537                Some(fix) => self.push_fix(Some(fix)),
6538                None => self
6539                    .diagnostics
6540                    .last_mut()
6541                    .expect("just pushed")
6542                    .notes
6543                    .push(format!(
6544                        "declare it as `let mut {name}` to allow assignment"
6545                    )),
6546            }
6547        }
6548        let value = self.check_expr(fcx, rhs);
6549
6550        // Desugar compound assignment: `x += e` → `x = x + e`.
6551        let final_value = match assign_binop(op) {
6552            None => value,
6553            Some(bin) => {
6554                let lhs_read = hir::Expr {
6555                    kind: hir::ExprKind::Local(local),
6556                    ty: info.ty.clone(),
6557                    span: lhs.span,
6558                };
6559                let ty = self.binary_result_ty(fcx, bin, &lhs_read, &value, span);
6560                hir::Expr {
6561                    kind: hir::ExprKind::Binary {
6562                        op: bin,
6563                        lhs: Box::new(lhs_read),
6564                        rhs: Box::new(value),
6565                    },
6566                    ty,
6567                    span,
6568                }
6569            }
6570        };
6571        if !fcx.icx.unify(&final_value.ty, &info.ty) {
6572            self.error(
6573                "E0010",
6574                format!(
6575                    "cannot assign `{}` to `{name}` which has type `{}`",
6576                    self.show(&final_value.ty, fcx),
6577                    self.show(&info.ty, fcx),
6578                ),
6579                span,
6580            );
6581        }
6582        hir::Expr {
6583            kind: hir::ExprKind::Assign {
6584                local,
6585                value: Box::new(final_value),
6586            },
6587            ty: Ty::Unit,
6588            span,
6589        }
6590    }
6591
6592    /// Classify the root binding of an lvalue place, walking through element
6593    /// (`arr[i]`) and field (`rec.f`) projections. The mutability of the base
6594    /// binding governs whether the reachable heap storage may be mutated.
6595    fn place_root(&self, fcx: &FnCtx, expr: &Spanned<ast::Expr>) -> PlaceRoot {
6596        match &expr.value {
6597            ast::Expr::Path(p) if p.segments.len() == 1 => {
6598                match fcx.lookup(&p.segments[0].value) {
6599                    Some(l) if fcx.locals[l.0 as usize].is_mut => PlaceRoot::Mutable,
6600                    Some(l) => PlaceRoot::ImmutableLocal(
6601                        p.segments[0].value.clone(),
6602                        fcx.locals[l.0 as usize].span,
6603                    ),
6604                    // A constant, a multi-segment path, or an unknown name is
6605                    // not a mutable place.
6606                    None => PlaceRoot::NotAPlace,
6607                }
6608            }
6609            ast::Expr::Index { target, .. } => self.place_root(fcx, target),
6610            ast::Expr::Field { target, .. } => self.place_root(fcx, target),
6611            // Any other base (call result, literal, block, …) is a temporary
6612            // with no assignable root.
6613            _ => PlaceRoot::NotAPlace,
6614        }
6615    }
6616
6617    /// Require that `target`'s storage be reachable through a mutable binding,
6618    /// reporting `E0060` if it is not.
6619    ///
6620    /// The one place all three mutation forms share: `arr[i] = v`,
6621    /// `rec.f = v`, and a call to a method declaring `mut self`. They differ
6622    /// only in how the diagnostic names what is being mutated (`what`), so the
6623    /// `place_root` classification, the error code, and — crucially — the
6624    /// actionable note live here rather than in three hand-maintained copies.
6625    fn require_mutable_place(
6626        &mut self,
6627        fcx: &FnCtx,
6628        target: &Spanned<ast::Expr>,
6629        span: Span,
6630        what: MutTarget,
6631    ) {
6632        match self.place_root(fcx, target) {
6633            PlaceRoot::Mutable => {}
6634            PlaceRoot::ImmutableLocal(name, decl) => {
6635                self.error("E0060", what.immutable_message(&name), span);
6636                // Spec 3.4b §3.3: where a fix is offered, it says what the
6637                // note would.
6638                if let Some(fix) = self.mutable_fix(&name, decl) {
6639                    self.push_fix(Some(fix));
6640                    return;
6641                }
6642                // `let mut self` is not Nova syntax — a receiver's mutability is
6643                // declared in the signature, so a `self` root needs the other
6644                // advice entirely. `place_root` hands back the root's *name*,
6645                // which is all it takes to tell the two apart; `self` is only
6646                // ever bound as a method receiver.
6647                let note = if name == "self" {
6648                    "declare the enclosing method's receiver as `mut self` to allow mutation"
6649                        .to_string()
6650                } else {
6651                    format!("declare it as `let mut {name}` to allow mutation")
6652                };
6653                self.diagnostics
6654                    .last_mut()
6655                    .expect("just pushed")
6656                    .notes
6657                    .push(note);
6658            }
6659            PlaceRoot::NotAPlace => {
6660                self.error("E0060", what.not_a_place_message(), span);
6661            }
6662        }
6663    }
6664
6665    /// Check `arr[index] = value`.
6666    ///
6667    /// Invariant shared with [`Checker::check_field_set`]: `rhs` is always
6668    /// type-checked, whatever early return this function takes. A mistake in
6669    /// the right-hand side (an unresolved call, say) is independent of
6670    /// whatever is wrong with the assignment target — an unsupported
6671    /// compound-assignment shape, a non-array receiver, an unknown field —
6672    /// so it is reported exactly as it already is on the path where the
6673    /// target is fine. The one thing deliberately *not* repeated is a second
6674    /// diagnostic about the target itself once one has already been reported
6675    /// for it (`check_field_set`'s `Ty::Error`-receiver case): that would be
6676    /// the same mistake twice, not an independent one.
6677    fn check_index_set(
6678        &mut self,
6679        fcx: &mut FnCtx,
6680        op: ast::AssignOp,
6681        target: &Spanned<ast::Expr>,
6682        index: &Spanned<ast::Expr>,
6683        rhs: &Spanned<ast::Expr>,
6684        span: Span,
6685    ) -> hir::Expr {
6686        if !matches!(op, ast::AssignOp::Assign) {
6687            self.unsupported(span, "compound assignment to an array element");
6688            self.check_expr(fcx, rhs);
6689            return error_expr(span);
6690        }
6691        // The array's storage must be reachable through a mutable binding.
6692        // Walk the whole index/field chain to its root local — `grid[0][1]`,
6693        // `rec.data[0]`, and `make()[0]` all bypass a single-segment check.
6694        self.require_mutable_place(fcx, target, span, MutTarget::Element);
6695        let arr = self.check_expr(fcx, target);
6696        let idx = self.check_expr(fcx, index);
6697        self.expect_ty(fcx, &idx, &Ty::Int, "an array index");
6698        let elem_ty = fcx.icx.fresh();
6699        if !fcx
6700            .icx
6701            .unify(&arr.ty, &Ty::Array(Box::new(elem_ty.clone())))
6702        {
6703            self.error(
6704                "E0014",
6705                format!(
6706                    "cannot index-assign a value of type `{}`",
6707                    self.show(&arr.ty, fcx)
6708                ),
6709                target.span,
6710            );
6711            self.check_expr(fcx, rhs);
6712            return error_expr(span);
6713        }
6714        let value = self.check_expr(fcx, rhs);
6715        if !fcx.icx.unify(&value.ty, &elem_ty) {
6716            self.error(
6717                "E0010",
6718                format!(
6719                    "array element has type `{}` but `{}` was assigned",
6720                    self.show(&elem_ty, fcx),
6721                    self.show(&value.ty, fcx),
6722                ),
6723                rhs.span,
6724            );
6725        }
6726        hir::Expr {
6727            kind: hir::ExprKind::IndexSet {
6728                target: Box::new(arr),
6729                index: Box::new(idx),
6730                value: Box::new(value),
6731            },
6732            ty: Ty::Unit,
6733            span,
6734        }
6735    }
6736
6737    /// Check `target.field = value`. Shares `check_index_set`'s invariant
6738    /// that `rhs` is type-checked on every early return, not only the path
6739    /// where the target resolves cleanly.
6740    fn check_field_set(
6741        &mut self,
6742        fcx: &mut FnCtx,
6743        op: ast::AssignOp,
6744        target: &Spanned<ast::Expr>,
6745        field: &Spanned<String>,
6746        rhs: &Spanned<ast::Expr>,
6747        span: Span,
6748    ) -> hir::Expr {
6749        if !matches!(op, ast::AssignOp::Assign) {
6750            self.unsupported(span, "compound assignment to a record field");
6751            self.check_expr(fcx, rhs);
6752            return error_expr(span);
6753        }
6754        // The record's storage must be reachable through a mutable binding.
6755        // Walk the whole field/index chain to its root local — `rec.inner.f`
6756        // and `make().f` both bypass a single-segment check. Records have no
6757        // per-field `mut`, so mutability is a property of the binding.
6758        self.require_mutable_place(fcx, target, span, MutTarget::Field);
6759        let rec = self.check_expr(fcx, target);
6760        let recv_ty = fcx.icx.apply(&rec.ty);
6761        // A broken receiver already reported its own error; another one here
6762        // would just cascade, as it would on the read path.
6763        if matches!(recv_ty, Ty::Error) {
6764            self.check_expr(fcx, rhs);
6765            return error_expr(span);
6766        }
6767        // Resolve the field to its index and declared type through the same
6768        // lookup `FieldGet` uses for reads, so reads and writes cannot disagree
6769        // about layout.
6770        let Some((index, field_ty)) =
6771            self.record_field_index_and_ty(fcx, &recv_ty, &field.value, span)
6772        else {
6773            // Same wording as the read path (`no_field_message`), including
6774            // its separate not-a-record case.
6775            self.error(
6776                "E0014",
6777                self.no_field_message(fcx, &recv_ty, &field.value),
6778                field.span,
6779            );
6780            let fix = self.field_fix(&recv_ty, field);
6781            self.push_fix(fix);
6782            self.check_expr(fcx, rhs);
6783            return error_expr(span);
6784        };
6785        self.note_field(fcx, &recv_ty, index, field.span);
6786        let value = self.check_expr(fcx, rhs);
6787        if !fcx.icx.unify(&value.ty, &field_ty) {
6788            self.error(
6789                "E0010",
6790                format!(
6791                    "field `{}` has type `{}` but `{}` was assigned",
6792                    field.value,
6793                    self.show(&field_ty, fcx),
6794                    self.show(&value.ty, fcx),
6795                ),
6796                rhs.span,
6797            );
6798        }
6799        hir::Expr {
6800            kind: hir::ExprKind::FieldSet {
6801                target: Box::new(rec),
6802                index,
6803                value: Box::new(value),
6804            },
6805            ty: Ty::Unit,
6806            span,
6807        }
6808    }
6809
6810    fn check_match(
6811        &mut self,
6812        fcx: &mut FnCtx,
6813        scrutinee: &Spanned<ast::Expr>,
6814        arms: &[ast::MatchArm],
6815        span: Span,
6816    ) -> hir::Expr {
6817        let scrut = self.check_expr(fcx, scrutinee);
6818        let result_ty = fcx.icx.fresh();
6819        let mut hir_arms = Vec::new();
6820        // Normalized (pattern, has-guard, its pattern's span, the whole arm's
6821        // span) per arm, for the exhaustiveness and reachability analysis
6822        // after all arms are checked.
6823        let mut arm_pats: Vec<(usefulness::Pat, bool, Span, Span)> = Vec::new();
6824
6825        for arm in arms {
6826            let guarded = arm.guard.is_some();
6827            if guarded {
6828                self.unsupported(arm.pattern.span, "match guards");
6829            }
6830            fcx.scopes.push(FxHashMap::default());
6831            let pattern = self.check_pattern(fcx, &arm.pattern, &scrut.ty);
6832            let upat = to_useful_pat(&pattern);
6833            let body = self.check_expr(fcx, &arm.body);
6834            fcx.scopes.pop();
6835            // A diverging arm (`Never`) imposes no constraint on the result
6836            // type; only unify arms that actually produce a value, so a
6837            // `break`/`return`/`continue` arm does not pin the match to
6838            // `Never`.
6839            if !matches!(fcx.icx.apply(&body.ty), Ty::Never) && !fcx.icx.unify(&body.ty, &result_ty)
6840            {
6841                self.error(
6842                    "E0010",
6843                    format!(
6844                        "match arms have incompatible types: expected `{}`, found `{}`",
6845                        self.show(&result_ty, fcx),
6846                        self.show(&body.ty, fcx),
6847                    ),
6848                    body.span,
6849                );
6850            }
6851            arm_pats.push((
6852                upat,
6853                guarded,
6854                arm.pattern.span,
6855                arm.pattern.span.merge(arm.body.span),
6856            ));
6857            hir_arms.push(hir::Arm {
6858                pattern,
6859                body,
6860                span: arm.pattern.span,
6861            });
6862        }
6863
6864        self.check_match_usefulness(fcx, &scrut, &arm_pats, span);
6865
6866        // If every arm diverged the result was never constrained; it is then
6867        // itself `Never`.
6868        let result_ty = if matches!(fcx.icx.apply(&result_ty), Ty::Var(_)) {
6869            Ty::Never
6870        } else {
6871            result_ty
6872        };
6873        hir::Expr {
6874            kind: hir::ExprKind::Match {
6875                scrutinee: Box::new(scrut),
6876                arms: hir_arms,
6877            },
6878            ty: result_ty,
6879            span,
6880        }
6881    }
6882
6883    /// Exhaustiveness and reachability via Maranget's usefulness algorithm
6884    /// (see `usefulness.rs`): an unguarded arm is unreachable when it is not
6885    /// useful against the earlier arms (E0021), and the match is non-exhaustive
6886    /// when a wildcard row is still useful against all arms — the witnesses
6887    /// name the uncovered values (E0020).
6888    fn check_match_usefulness(
6889        &mut self,
6890        fcx: &mut FnCtx,
6891        scrut: &hir::Expr,
6892        arm_pats: &[(usefulness::Pat, bool, Span, Span)],
6893        span: Span,
6894    ) {
6895        let scrut_ty = fcx.icx.apply(&scrut.ty);
6896        // A type we cannot yet resolve to constructors (an unresolved inference
6897        // variable or generic parameter) carries no enumerable signature, so we
6898        // cannot analyze its arms — with one exception: a match with *no* arms
6899        // still leaves every value of an inhabited type uncovered. `Never` is
6900        // uninhabited (an empty match is fine) and `Error` already reported a
6901        // type error, so neither should pile on here.
6902        if matches!(scrut_ty, Ty::Var(_) | Ty::Param(_) | Ty::Error | Ty::Never) {
6903            if arm_pats.is_empty() && matches!(scrut_ty, Ty::Var(_) | Ty::Param(_)) {
6904                self.error(
6905                    "E0020",
6906                    "non-exhaustive match: this match has no arms",
6907                    span,
6908                );
6909                self.diagnostics
6910                    .last_mut()
6911                    .expect("just pushed")
6912                    .notes
6913                    .push("a `match` on an inhabited type needs at least one arm".to_string());
6914            }
6915            return;
6916        }
6917        // Clone the sum table so the analysis context does not hold a borrow of
6918        // `self` across the diagnostic calls below.
6919        let sums = self.sums.clone();
6920        let cx = usefulness::MatchCx::new(&sums);
6921        let col = [scrut_ty];
6922
6923        // Reachability: each unguarded arm must cover a value no earlier
6924        // (unguarded) arm does. A guarded arm's match is conditional, so it
6925        // neither is reported nor counts toward coverage.
6926        let mut prior: Vec<Vec<usefulness::Pat>> = Vec::new();
6927        for (pat, guarded, arm_span, arm) in arm_pats {
6928            if *guarded {
6929                continue;
6930            }
6931            if cx
6932                .usefulness(&prior, std::slice::from_ref(pat), &col)
6933                .is_empty()
6934            {
6935                let mut d = Diagnostic::warning("E0021", "unreachable match arm")
6936                    .with_primary_label(*arm_span, "this arm is never reached")
6937                    .with_note(
6938                        "an earlier arm already matches every value this one would".to_string(),
6939                    );
6940                // Spec 3.4b §4.5.
6941                if let Some(fix) = self.remove_arm_fix(*arm) {
6942                    d = d.with_fix(fix);
6943                }
6944                self.diagnostics.push(d);
6945            }
6946            prior.push(vec![pat.clone()]);
6947        }
6948
6949        // Exhaustiveness: a wildcard must not be useful against all arms.
6950        let witnesses = cx.usefulness(&prior, &[usefulness::Pat::Wild], &col);
6951        if !witnesses.is_empty() {
6952            let mut rendered: Vec<String> = witnesses
6953                .iter()
6954                .filter_map(|w| w.first())
6955                .map(|p| self.render_witness(p))
6956                .collect();
6957            rendered.dedup();
6958            self.error(
6959                "E0020",
6960                format!(
6961                    "non-exhaustive match: pattern{} {} not covered",
6962                    if rendered.len() == 1 { "" } else { "s" },
6963                    rendered
6964                        .iter()
6965                        .map(|w| format!("`{w}`"))
6966                        .collect::<Vec<_>>()
6967                        .join(", "),
6968                ),
6969                span,
6970            );
6971            self.diagnostics
6972                .last_mut()
6973                .expect("just pushed")
6974                .notes
6975                .push("add the missing arms or a `_ => ...` catch-all".to_string());
6976        }
6977    }
6978
6979    /// Render a witness pattern for a non-exhaustiveness diagnostic.
6980    fn render_witness(&self, pat: &usefulness::Pat) -> String {
6981        match pat {
6982            usefulness::Pat::Wild => "_".to_string(),
6983            usefulness::Pat::Ctor(ctor, args) => match ctor {
6984                usefulness::Ctor::Bool(b) => b.to_string(),
6985                usefulness::Ctor::Int(v) => v.to_string(),
6986                usefulness::Ctor::Str(s) => format!("{s:?}"),
6987                usefulness::Ctor::Variant(sum, vi) => {
6988                    let name = self
6989                        .sums
6990                        .iter()
6991                        .find(|s| s.def_id == *sum)
6992                        .and_then(|s| s.variants.get(*vi as usize))
6993                        .map(|v| v.name.clone())
6994                        .unwrap_or_else(|| "?".to_string());
6995                    if args.is_empty() {
6996                        name
6997                    } else {
6998                        let inner: Vec<String> =
6999                            args.iter().map(|a| self.render_witness(a)).collect();
7000                        format!("{name}({})", inner.join(", "))
7001                    }
7002                }
7003            },
7004        }
7005    }
7006
7007    fn check_pattern(
7008        &mut self,
7009        fcx: &mut FnCtx,
7010        pattern: &Spanned<ast::Pattern>,
7011        scrut_ty: &Ty,
7012    ) -> hir::Pattern {
7013        match &pattern.value {
7014            ast::Pattern::Wildcard => hir::Pattern::Wildcard,
7015            ast::Pattern::Lit(lit) => match lit {
7016                ast::Literal::Int(v) => {
7017                    self.unify_pattern_ty(fcx, &Ty::Int, scrut_ty, pattern.span);
7018                    hir::Pattern::LitInt(*v)
7019                }
7020                ast::Literal::Bool(v) => {
7021                    self.unify_pattern_ty(fcx, &Ty::Bool, scrut_ty, pattern.span);
7022                    hir::Pattern::LitBool(*v)
7023                }
7024                ast::Literal::Str(v) => {
7025                    self.unify_pattern_ty(fcx, &Ty::String, scrut_ty, pattern.span);
7026                    hir::Pattern::LitStr(v.clone())
7027                }
7028                ast::Literal::Float(_) | ast::Literal::Char(_) => {
7029                    self.unsupported(pattern.span, "float and char patterns");
7030                    hir::Pattern::Wildcard
7031                }
7032            },
7033            ast::Pattern::Ident { is_mut, name } => {
7034                // An identifier that names a payload-less variant of the
7035                // scrutinee's sum type is a variant pattern, not a binding.
7036                if let Some(Res::Variant(sum_id, vi)) =
7037                    self.defs.resolve_value(self.cur_module, &name.value)
7038                {
7039                    if self.variant_matches_scrutinee(fcx, sum_id, scrut_ty) {
7040                        self.note_use(name.span, Target::Variant(sum_id, vi as u32));
7041                        return self.variant_pattern(fcx, sum_id, vi, &[], scrut_ty, pattern.span);
7042                    }
7043                    // The name is a known constructor, but of a different type:
7044                    // reject it rather than silently binding a catch-all (which
7045                    // would mask uncovered cases), mirroring the `Path` and
7046                    // `TupleStruct` arms.
7047                    self.error(
7048                        "E0001",
7049                        format!("`{}` is not a variant of the matched type", name.value),
7050                        pattern.span,
7051                    );
7052                    return hir::Pattern::Wildcard;
7053                }
7054                let local = self.bind_local(
7055                    fcx,
7056                    name.value.clone(),
7057                    scrut_ty.clone(),
7058                    *is_mut,
7059                    name.span,
7060                    Binding::Other,
7061                );
7062                hir::Pattern::Bind(local)
7063            }
7064            ast::Pattern::Path(path) if path.segments.len() == 1 => {
7065                let name = &path.segments[0].value;
7066                if let Some(Res::Variant(sum_id, vi)) =
7067                    self.defs.resolve_value(self.cur_module, name)
7068                {
7069                    if self.variant_matches_scrutinee(fcx, sum_id, scrut_ty) {
7070                        self.note_use(path.segments[0].span, Target::Variant(sum_id, vi as u32));
7071                        return self.variant_pattern(fcx, sum_id, vi, &[], scrut_ty, pattern.span);
7072                    }
7073                }
7074                self.error(
7075                    "E0001",
7076                    format!("`{name}` is not a variant of the matched type"),
7077                    pattern.span,
7078                );
7079                hir::Pattern::Wildcard
7080            }
7081            ast::Pattern::Path(path) if path.segments.len() == 2 => {
7082                let ty_name = path.segments[0].value.as_str();
7083                let v_name = path.segments[1].value.as_str();
7084                if let Some(sum_id) = self.defs.resolve_type(self.cur_module, ty_name) {
7085                    if let Some(vi) = self.variant_index(sum_id, v_name) {
7086                        if self.variant_matches_scrutinee(fcx, sum_id, scrut_ty) {
7087                            self.note_use(path.segments[0].span, Target::Def(sum_id));
7088                            self.note_use(
7089                                path.segments[1].span,
7090                                Target::Variant(sum_id, vi as u32),
7091                            );
7092                            return self.variant_pattern(
7093                                fcx,
7094                                sum_id,
7095                                vi,
7096                                &[],
7097                                scrut_ty,
7098                                pattern.span,
7099                            );
7100                        }
7101                    }
7102                }
7103                self.error(
7104                    "E0001",
7105                    format!("`{ty_name}::{v_name}` is not a variant of the matched type"),
7106                    pattern.span,
7107                );
7108                // Spec 3.4b §4.2: a qualifier this module never imported.
7109                if self.defs.resolve_type(self.cur_module, ty_name).is_none() {
7110                    let scrutinee = fixes::scrutinee_sum(fcx, scrut_ty);
7111                    self.name_fixes(
7112                        path.segments[0].clone(),
7113                        Need::Pattern {
7114                            variant: v_name.to_string(),
7115                            arity: 0,
7116                            scrutinee,
7117                        },
7118                        Vec::new(),
7119                    );
7120                }
7121                hir::Pattern::Wildcard
7122            }
7123            ast::Pattern::TupleStruct { path, fields } => {
7124                let resolved = if path.segments.len() == 1 {
7125                    match self
7126                        .defs
7127                        .resolve_value(self.cur_module, &path.segments[0].value)
7128                    {
7129                        Some(Res::Variant(sum_id, vi)) => Some((sum_id, vi)),
7130                        _ => None,
7131                    }
7132                } else if path.segments.len() == 2 {
7133                    self.defs
7134                        .resolve_type(self.cur_module, &path.segments[0].value)
7135                        .and_then(|sum_id| {
7136                            self.variant_index(sum_id, &path.segments[1].value)
7137                                .map(|vi| (sum_id, vi))
7138                        })
7139                } else {
7140                    None
7141                };
7142                let Some((sum_id, vi)) = resolved else {
7143                    self.error(
7144                        "E0001",
7145                        "cannot resolve this pattern to a sum type variant",
7146                        pattern.span,
7147                    );
7148                    // Spec 3.4b §4.2: a qualifier this module never imported.
7149                    if path.segments.len() == 2
7150                        && self
7151                            .defs
7152                            .resolve_type(self.cur_module, &path.segments[0].value)
7153                            .is_none()
7154                    {
7155                        let scrutinee = fixes::scrutinee_sum(fcx, scrut_ty);
7156                        self.name_fixes(
7157                            path.segments[0].clone(),
7158                            Need::Pattern {
7159                                variant: path.segments[1].value.clone(),
7160                                arity: fields.len(),
7161                                scrutinee,
7162                            },
7163                            Vec::new(),
7164                        );
7165                    }
7166                    return hir::Pattern::Wildcard;
7167                };
7168                if !self.variant_matches_scrutinee(fcx, sum_id, scrut_ty) {
7169                    self.error(
7170                        "E0010",
7171                        "this variant does not belong to the matched type",
7172                        pattern.span,
7173                    );
7174                    return hir::Pattern::Wildcard;
7175                }
7176                if path.segments.len() == 2 {
7177                    self.note_use(path.segments[0].span, Target::Def(sum_id));
7178                }
7179                if let Some(last) = path.segments.last() {
7180                    self.note_use(last.span, Target::Variant(sum_id, vi as u32));
7181                }
7182                self.variant_pattern(fcx, sum_id, vi, fields, scrut_ty, pattern.span)
7183            }
7184            ast::Pattern::Path(_)
7185            | ast::Pattern::Binding { .. }
7186            | ast::Pattern::Record { .. }
7187            | ast::Pattern::Tuple(_)
7188            | ast::Pattern::Array(_)
7189            | ast::Pattern::Or(_)
7190            | ast::Pattern::Range { .. } => {
7191                self.unsupported(pattern.span, "this pattern form");
7192                hir::Pattern::Wildcard
7193            }
7194        }
7195    }
7196
7197    /// Unify the scrutinee with `Sum { def_id, fresh vars }`, so payload
7198    /// field types pick up the scrutinee's generic arguments.
7199    fn variant_matches_scrutinee(&mut self, fcx: &mut FnCtx, sum_id: DefId, scrut_ty: &Ty) -> bool {
7200        let generics = self
7201            .sums
7202            .iter()
7203            .find(|s| s.def_id == sum_id)
7204            .map(|s| s.generics)
7205            .unwrap_or(0);
7206        let args: Vec<Ty> = (0..generics).map(|_| fcx.icx.fresh()).collect();
7207        fcx.icx.unify(
7208            scrut_ty,
7209            &Ty::Sum {
7210                def_id: sum_id,
7211                args,
7212            },
7213        )
7214    }
7215
7216    fn variant_pattern(
7217        &mut self,
7218        fcx: &mut FnCtx,
7219        sum_id: DefId,
7220        variant: usize,
7221        fields: &[Spanned<ast::Pattern>],
7222        scrut_ty: &Ty,
7223        span: Span,
7224    ) -> hir::Pattern {
7225        let Some(sum) = self.sums.iter().find(|s| s.def_id == sum_id).cloned() else {
7226            return hir::Pattern::Wildcard;
7227        };
7228        let v = &sum.variants[variant];
7229        if fields.len() != v.fields.len() {
7230            self.error(
7231                "E0016",
7232                format!(
7233                    "variant `{}` has {} field(s) but the pattern has {}",
7234                    v.name,
7235                    v.fields.len(),
7236                    fields.len()
7237                ),
7238                span,
7239            );
7240            return hir::Pattern::Wildcard;
7241        }
7242        // The scrutinee was already unified with `Sum { fresh args }` by
7243        // `variant_matches_scrutinee`; read its args to type the binders.
7244        let scrut_args = match fcx.icx.apply(scrut_ty) {
7245            Ty::Sum { args, .. } => args,
7246            _ => Vec::new(),
7247        };
7248        let mut binders = Vec::new();
7249        for (field_pat, field_ty) in fields.iter().zip(v.fields.iter()) {
7250            let bound_ty = field_ty.subst(&scrut_args);
7251            match &field_pat.value {
7252                ast::Pattern::Wildcard => binders.push(None),
7253                ast::Pattern::Ident { is_mut, name } => {
7254                    let local = self.bind_local(
7255                        fcx,
7256                        name.value.clone(),
7257                        bound_ty,
7258                        *is_mut,
7259                        name.span,
7260                        Binding::Other,
7261                    );
7262                    binders.push(Some(local));
7263                }
7264                _ => {
7265                    self.unsupported(field_pat.span, "nested patterns inside variants");
7266                    binders.push(None);
7267                }
7268            }
7269        }
7270        hir::Pattern::Variant {
7271            sum: sum_id,
7272            variant: variant as u32,
7273            binders,
7274        }
7275    }
7276
7277    // === Helpers ===
7278
7279    // === The language server's index (spec 3.4a §3) ===
7280
7281    fn note(&mut self, span: Span, role: Role, target: Target) {
7282        if let Some(index) = self.index.as_mut() {
7283            index.record(span, role, target);
7284        }
7285    }
7286
7287    fn note_use(&mut self, span: Span, target: Target) {
7288        self.note(span, Role::Use, target);
7289    }
7290
7291    fn note_decl(&mut self, span: Span, target: Target) {
7292        self.note(span, Role::Declaration, target);
7293    }
7294
7295    /// Declare every resolver definition (spec §3.2): each item by its
7296    /// `Def`, and a sum's variants beside it. A trait's provided method is
7297    /// declared as its `TraitMethod` by `collect_traits`, never by its
7298    /// default body's `Def`.
7299    fn note_item_declarations(&mut self) {
7300        if self.index.is_none() {
7301            return;
7302        }
7303        let defs: &'a Definitions = self.defs;
7304        for (i, def) in defs.defs().iter().enumerate() {
7305            let id = DefId(i as u32);
7306            match &def.kind {
7307                DefKind::Method {
7308                    owner: MethodOwner::TraitDefault,
7309                    ..
7310                } => {}
7311                DefKind::Sum { variants, .. } => {
7312                    self.note_decl(def.span, Target::Def(id));
7313                    for (vi, v) in variants.iter().enumerate() {
7314                        self.note_decl(v.span, Target::Variant(id, vi as u32));
7315                    }
7316                }
7317                _ => self.note_decl(def.span, Target::Def(id)),
7318            }
7319        }
7320    }
7321
7322    /// Enter an item's type parameters, the impl's first: declare them, and
7323    /// let the item's uses find them by name (plan decision 4).
7324    fn enter_type_params(&mut self, lists: &[&[ast::TypeParam]]) {
7325        self.type_params.clear();
7326        for list in lists {
7327            for g in list.iter() {
7328                self.type_params.push((g.name.value.clone(), g.name.span));
7329                self.note_decl(g.name.span, Target::TypeParam(g.name.span));
7330            }
7331        }
7332    }
7333
7334    fn type_param_target(&self, name: &str) -> Option<Target> {
7335        self.type_params
7336            .iter()
7337            .rev()
7338            .find(|(n, _)| n == name)
7339            .map(|(_, span)| Target::TypeParam(*span))
7340    }
7341
7342    /// `fcx.new_local` for a name the user wrote, declaring it (spec §3.2).
7343    /// `_` declares nothing. The checker's own locals call `new_local`
7344    /// directly and are never recorded. `binding` says how it was bound
7345    /// (spec 3.4b §4.1), which the index also keeps (3.4b §4.6).
7346    fn bind_local(
7347        &mut self,
7348        fcx: &mut FnCtx,
7349        name: String,
7350        ty: Ty,
7351        is_mut: bool,
7352        span: Span,
7353        binding: Binding,
7354    ) -> LocalId {
7355        if name != "_" {
7356            self.note_decl(span, Target::Local(span));
7357            if let Some(index) = self.index.as_mut() {
7358                index.locals.insert(
7359                    span,
7360                    LocalFlags {
7361                        parameter: binding == Binding::Param,
7362                        mutable: is_mut,
7363                    },
7364                );
7365            }
7366        }
7367        self.bindings.insert(span, binding);
7368        fcx.new_local(name, ty, is_mut, span)
7369    }
7370
7371    /// Declare parameters that no body binds: a trait's required method's
7372    /// and an extern function's.
7373    fn note_params(&mut self, params: &[ast::Param]) {
7374        for p in params {
7375            self.note_decl(p.name.span, Target::Local(p.name.span));
7376            if let Some(index) = self.index.as_mut() {
7377                index.locals.insert(
7378                    p.name.span,
7379                    LocalFlags {
7380                        parameter: true,
7381                        mutable: p.is_mut,
7382                    },
7383                );
7384            }
7385        }
7386    }
7387
7388    /// Each declared local's type as hover shows it (spec §3.4), read after
7389    /// inference, with `names` for the type parameters. A wholly unknown
7390    /// type is not stored.
7391    fn note_local_types(&mut self, locals: &[hir::Local], names: &[String]) {
7392        let defs = self.defs;
7393        let Some(index) = self.index.as_mut() else {
7394            return;
7395        };
7396        for local in locals {
7397            if !index.has(local.span, Role::Declaration, Target::Local(local.span)) {
7398                continue;
7399            }
7400            if matches!(local.ty, Ty::Var(_) | Ty::Error) {
7401                continue;
7402            }
7403            let shown = display_ty_named(&local.ty, defs, names);
7404            index.types.insert(local.span, shown);
7405        }
7406    }
7407
7408    /// A two-segment path's qualifier, `Point` in `Point::origin`, by
7409    /// `qualifier_self_ty`'s order: a primitive, then a nominal type.
7410    fn note_qualifier(&mut self, seg: &Spanned<String>) {
7411        if let Some(name) = primitive(&seg.value) {
7412            self.note_use(seg.span, Target::Primitive(name));
7413        } else if let Some(def_id) = self.defs.resolve_type(self.cur_module, &seg.value) {
7414            self.note_use(seg.span, Target::Def(def_id));
7415        }
7416    }
7417
7418    /// A field access's field (spec §3.2).
7419    fn note_field(&mut self, fcx: &FnCtx, recv_ty: &Ty, field: u32, span: Span) {
7420        if let Ty::Record { def_id, .. } = fcx.icx.apply(recv_ty) {
7421            self.note_use(span, Target::Field(def_id, field));
7422        }
7423    }
7424
7425    fn mark_shorthand(&mut self, span: Span) {
7426        if let Some(index) = self.index.as_mut() {
7427            index.mark_shorthand(span);
7428        }
7429    }
7430
7431    fn variant_index(&self, sum_id: DefId, name: &str) -> Option<usize> {
7432        self.sums
7433            .iter()
7434            .find(|s| s.def_id == sum_id)?
7435            .variants
7436            .iter()
7437            .position(|v| v.name == name)
7438    }
7439
7440    fn unify_pattern_ty(&mut self, fcx: &mut FnCtx, pat_ty: &Ty, scrut_ty: &Ty, span: Span) {
7441        if !fcx.icx.unify(pat_ty, scrut_ty) {
7442            self.error(
7443                "E0010",
7444                format!(
7445                    "pattern has type `{}` but the matched value has type `{}`",
7446                    self.show(pat_ty, fcx),
7447                    self.show(scrut_ty, fcx),
7448                ),
7449                span,
7450            );
7451        }
7452    }
7453
7454    fn expect_ty(&mut self, fcx: &mut FnCtx, expr: &hir::Expr, expected: &Ty, what: &str) {
7455        if !fcx.icx.unify(&expr.ty, expected) {
7456            self.error(
7457                "E0010",
7458                format!(
7459                    "{what} must have type `{}`, found `{}`",
7460                    self.show(expected, fcx),
7461                    self.show(&expr.ty, fcx),
7462                ),
7463                expr.span,
7464            );
7465        }
7466    }
7467
7468    fn show(&self, ty: &Ty, fcx: &FnCtx) -> String {
7469        display_ty(&fcx.icx.apply(ty), self.defs)
7470    }
7471
7472    fn error(&mut self, code: &str, message: impl Into<String>, span: Span) {
7473        self.diagnostics
7474            .push(Diagnostic::error(code, message).with_primary_label(span, "here"));
7475    }
7476
7477    fn unsupported(&mut self, span: Span, what: &str) {
7478        self.diagnostics.push(
7479            Diagnostic::error("E0900", format!("{what} are not supported yet"))
7480                .with_primary_label(span, "not supported yet")
7481                .with_note(
7482                    "the Phase 1 MVP compiler supports a subset of Nova; \
7483                     this feature arrives in a later milestone"
7484                        .to_string(),
7485                ),
7486        );
7487    }
7488
7489    fn unsupported_expr(&mut self, span: Span, what: &str) -> hir::Expr {
7490        self.unsupported(span, what);
7491        error_expr(span)
7492    }
7493}
7494
7495// === Free helpers ===
7496
7497/// Collect the constants a constant's body references (calls to zero-arg
7498/// const functions), for cycle detection.
7499fn collect_const_calls(expr: &hir::Expr, consts: &FxHashSet<DefId>, out: &mut Vec<DefId>) {
7500    if let hir::ExprKind::Call {
7501        func: hir::Callee::Def(d),
7502        ..
7503    } = &expr.kind
7504    {
7505        if consts.contains(d) && !out.contains(d) {
7506            out.push(*d);
7507        }
7508    }
7509    for child in child_exprs(expr) {
7510        collect_const_calls(child, consts, out);
7511    }
7512}
7513
7514/// Collect, without duplicates, the associated types that `ty` projects onto
7515/// `self_ty` — every `Assoc { on, assoc }` inside `ty` whose `on` is exactly
7516/// `self_ty`, which inside an impl block is what `Self::Name` converts to (see
7517/// `convert_ty`'s two-segment path case). The edge set for
7518/// `check_assoc_binding_cycles`.
7519///
7520/// **Recurses into compound types.** `type Item = [Self::Item]` names an
7521/// infinitely large type just as `type Item = Self::Item` does, and a walk that
7522/// only inspected the top level would accept it and leave `normalize_ty` growing
7523/// one `[…]` layer per step.
7524///
7525/// A projection onto anything *other* than the impl's own self type is not an
7526/// edge: `type Item = T::Item` for `impl<T: It> It for W<T>` names the
7527/// argument's associated type, and every normalization step strips a `W<…>`
7528/// layer, so it bottoms out. Flagging it would reject legitimate code.
7529fn collect_self_projections(ty: &Ty, self_ty: &Ty, out: &mut Vec<DefId>) {
7530    match ty {
7531        Ty::Assoc { on, assoc } => {
7532            if **on == *self_ty && !out.contains(assoc) {
7533                out.push(*assoc);
7534            }
7535            // A projection *on* a projection (`Self::A::B` is unwritable today,
7536            // but `subst` can build one) still has to be searched.
7537            collect_self_projections(on, self_ty, out);
7538        }
7539        Ty::Fn { params, ret } => {
7540            for p in params {
7541                collect_self_projections(p, self_ty, out);
7542            }
7543            collect_self_projections(ret, self_ty, out);
7544        }
7545        Ty::Sum { args, .. } | Ty::Record { args, .. } => {
7546            for a in args {
7547                collect_self_projections(a, self_ty, out);
7548            }
7549        }
7550        Ty::Array(elem) => collect_self_projections(elem, self_ty, out),
7551        Ty::Future(fut_out) => collect_self_projections(fut_out, self_ty, out),
7552        Ty::Int
7553        | Ty::Float
7554        | Ty::Bool
7555        | Ty::Char
7556        | Ty::String
7557        | Ty::Bytes
7558        | Ty::Unit
7559        | Ty::Param(_)
7560        | Ty::Var(_)
7561        | Ty::Never
7562        | Ty::Error => {}
7563    }
7564}
7565
7566/// Whether `start` can reach itself through the dependency edges (i.e.
7567/// participates in a cycle). Shared by the two definition-cycle checks —
7568/// `check_const_cycles` (a constant defined in terms of its own value) and
7569/// `check_assoc_binding_cycles` (an associated type bound in terms of itself);
7570/// the graph is `DefId -> DefId` in both and the walk is identical, so keeping
7571/// one copy is what stops the two from drifting on cycle detection itself.
7572///
7573/// `seen` is what makes this terminate on the cyclic input it exists to find:
7574/// a node is expanded at most once.
7575fn reaches_self(start: DefId, edges: &FxHashMap<DefId, Vec<DefId>>) -> bool {
7576    let mut stack: Vec<DefId> = edges.get(&start).cloned().unwrap_or_default();
7577    let mut seen: FxHashSet<DefId> = FxHashSet::default();
7578    while let Some(n) = stack.pop() {
7579        if n == start {
7580            return true;
7581        }
7582        if !seen.insert(n) {
7583            continue;
7584        }
7585        if let Some(succ) = edges.get(&n) {
7586            stack.extend(succ.iter().copied());
7587        }
7588    }
7589    false
7590}
7591
7592/// Walk a closure body and collect the locals it captures — those with an
7593/// index below `snapshot` (declared in an enclosing scope) — in first-seen
7594/// order, without duplicates.
7595fn collect_captures(expr: &hir::Expr, snapshot: u32, out: &mut Vec<LocalId>) {
7596    use hir::ExprKind as K;
7597    let mut consider = |id: &LocalId| {
7598        if id.0 < snapshot && !out.contains(id) {
7599            out.push(*id);
7600        }
7601    };
7602    // Every position that *references* an existing local is a capture
7603    // candidate: reads, assignment targets, and indirect-call callees. (A
7604    // `Let` binder and match binders introduce fresh locals — never < the
7605    // snapshot — so they are not candidates.)
7606    match &expr.kind {
7607        K::Local(id) => consider(id),
7608        K::Assign { local, .. } => consider(local),
7609        K::Call {
7610            func: hir::Callee::Local(id),
7611            ..
7612        } => consider(id),
7613        _ => {}
7614    }
7615    for child in child_exprs(expr) {
7616        collect_captures(child, snapshot, out);
7617    }
7618}
7619
7620/// Rewrite every local id in a lifted closure body through `map`.
7621fn remap_locals(expr: &mut hir::Expr, map: &FxHashMap<u32, u32>) {
7622    use hir::ExprKind as K;
7623    let remap = |id: &mut LocalId| {
7624        if let Some(&new) = map.get(&id.0) {
7625            id.0 = new;
7626        }
7627    };
7628    match &mut expr.kind {
7629        K::Local(id) => remap(id),
7630        K::Let { local, .. } | K::Assign { local, .. } => remap(local),
7631        K::Call {
7632            func: hir::Callee::Local(id),
7633            ..
7634        } => remap(id),
7635        K::Match { arms, .. } => {
7636            for arm in arms.iter_mut() {
7637                remap_pattern(&mut arm.pattern, &remap);
7638            }
7639        }
7640        _ => {}
7641    }
7642    for child in child_exprs_mut(&mut expr.kind) {
7643        remap_locals(child, map);
7644    }
7645}
7646
7647fn remap_pattern(pat: &mut hir::Pattern, remap: &impl Fn(&mut LocalId)) {
7648    match pat {
7649        hir::Pattern::Bind(local) => remap(local),
7650        hir::Pattern::Variant { binders, .. } => {
7651            for b in binders.iter_mut().flatten() {
7652                remap(b);
7653            }
7654        }
7655        _ => {}
7656    }
7657}
7658
7659/// Immutable iterator over an expression's direct sub-expressions.
7660fn child_exprs(expr: &hir::Expr) -> Vec<&hir::Expr> {
7661    use hir::ExprKind as K;
7662    let mut out: Vec<&hir::Expr> = Vec::new();
7663    match &expr.kind {
7664        K::MakeClosure { captures, .. } => out.extend(captures.iter()),
7665        K::Call { args, .. } => out.extend(args.iter()),
7666        K::MakeVariant { args, .. }
7667        | K::MakeRecord { fields: args, .. }
7668        | K::MakeArray { elems: args }
7669        | K::StrConcat(args) => out.extend(args.iter()),
7670        K::FieldGet { target, .. } | K::ArrayLen { target } => out.push(target),
7671        K::FieldSet { target, value, .. } => {
7672            out.push(target);
7673            out.push(value);
7674        }
7675        K::ArrayRepeat { init, len } => {
7676            out.push(init);
7677            out.push(len);
7678        }
7679        K::Index { target, index } => {
7680            out.push(target);
7681            out.push(index);
7682        }
7683        K::IndexSet {
7684            target,
7685            index,
7686            value,
7687        } => {
7688            out.push(target);
7689            out.push(index);
7690            out.push(value);
7691        }
7692        K::TraitCall { receiver, args, .. } => {
7693            // `None` for a trait associated function — no receiver to visit.
7694            out.extend(receiver.iter().map(|r| r.as_ref()));
7695            out.extend(args.iter());
7696        }
7697        K::Binary { lhs, rhs, .. } | K::LogicalAnd { lhs, rhs } | K::LogicalOr { lhs, rhs } => {
7698            out.push(lhs);
7699            out.push(rhs);
7700        }
7701        K::Unary { expr: e, .. } | K::ToStr(e) | K::Await(e) => out.push(e),
7702        K::Let { init, .. } => out.push(init),
7703        K::Assign { value, .. } => out.push(value),
7704        K::Block { stmts, trailing } => {
7705            out.extend(stmts.iter());
7706            if let Some(t) = trailing {
7707                out.push(t);
7708            }
7709        }
7710        K::If { cond, then, else_ } => {
7711            out.push(cond);
7712            out.push(then);
7713            if let Some(e) = else_ {
7714                out.push(e);
7715            }
7716        }
7717        K::While { cond, body } => {
7718            out.push(cond);
7719            out.push(body);
7720        }
7721        K::Match { scrutinee, arms } => {
7722            out.push(scrutinee);
7723            out.extend(arms.iter().map(|a| &a.body));
7724        }
7725        K::Return(v) => {
7726            if let Some(v) = v {
7727                out.push(v);
7728            }
7729        }
7730        K::IntLit(_)
7731        | K::FloatLit(_)
7732        | K::BoolLit(_)
7733        | K::StrLit(_)
7734        | K::CharLit(_)
7735        | K::Unit
7736        | K::Break
7737        | K::Continue
7738        | K::Local(_) => {}
7739    }
7740    out
7741}
7742
7743/// Mutable iterator over a kind's direct sub-expressions.
7744fn child_exprs_mut(kind: &mut hir::ExprKind) -> Vec<&mut hir::Expr> {
7745    use hir::ExprKind as K;
7746    let mut out: Vec<&mut hir::Expr> = Vec::new();
7747    match kind {
7748        K::MakeClosure { captures, .. } => out.extend(captures.iter_mut()),
7749        K::Call { args, .. } => out.extend(args.iter_mut()),
7750        K::MakeVariant { args, .. }
7751        | K::MakeRecord { fields: args, .. }
7752        | K::MakeArray { elems: args }
7753        | K::StrConcat(args) => out.extend(args.iter_mut()),
7754        K::FieldGet { target, .. } | K::ArrayLen { target } => out.push(target),
7755        K::FieldSet { target, value, .. } => {
7756            out.push(target);
7757            out.push(value);
7758        }
7759        K::ArrayRepeat { init, len } => {
7760            out.push(init);
7761            out.push(len);
7762        }
7763        K::Index { target, index } => {
7764            out.push(target);
7765            out.push(index);
7766        }
7767        K::IndexSet {
7768            target,
7769            index,
7770            value,
7771        } => {
7772            out.push(target);
7773            out.push(index);
7774            out.push(value);
7775        }
7776        K::TraitCall { receiver, args, .. } => {
7777            out.extend(receiver.iter_mut().map(|r| r.as_mut()));
7778            out.extend(args.iter_mut());
7779        }
7780        K::Binary { lhs, rhs, .. } | K::LogicalAnd { lhs, rhs } | K::LogicalOr { lhs, rhs } => {
7781            out.push(lhs);
7782            out.push(rhs);
7783        }
7784        K::Unary { expr: e, .. } | K::ToStr(e) | K::Await(e) => out.push(e),
7785        K::Let { init, .. } => out.push(init),
7786        K::Assign { value, .. } => out.push(value),
7787        K::Block { stmts, trailing } => {
7788            out.extend(stmts.iter_mut());
7789            if let Some(t) = trailing {
7790                out.push(t);
7791            }
7792        }
7793        K::If { cond, then, else_ } => {
7794            out.push(cond);
7795            out.push(then);
7796            if let Some(e) = else_ {
7797                out.push(e);
7798            }
7799        }
7800        K::While { cond, body } => {
7801            out.push(cond);
7802            out.push(body);
7803        }
7804        K::Match { scrutinee, arms } => {
7805            out.push(scrutinee);
7806            out.extend(arms.iter_mut().map(|a| &mut a.body));
7807        }
7808        K::Return(v) => {
7809            if let Some(v) = v {
7810                out.push(v);
7811            }
7812        }
7813        K::IntLit(_)
7814        | K::FloatLit(_)
7815        | K::BoolLit(_)
7816        | K::StrLit(_)
7817        | K::CharLit(_)
7818        | K::Unit
7819        | K::Break
7820        | K::Continue
7821        | K::Local(_) => {}
7822    }
7823    out
7824}
7825
7826fn generic_scope(generics: &[ast::TypeParam]) -> FxHashMap<String, u32> {
7827    generics
7828        .iter()
7829        .enumerate()
7830        .map(|(i, g)| (g.name.value.clone(), i as u32))
7831        .collect()
7832}
7833
7834/// Type parameter names by index, for [`display_ty_named`].
7835fn param_names(generics: &FxHashMap<String, u32>) -> Vec<String> {
7836    let n = generics
7837        .values()
7838        .map(|&i| i as usize + 1)
7839        .max()
7840        .unwrap_or(0);
7841    let mut names = vec![String::new(); n];
7842    for (name, &i) in generics {
7843        names[i as usize] = name.clone();
7844    }
7845    names
7846}
7847
7848/// A built-in type's name, as the index's `Primitive` holds it.
7849fn primitive(name: &str) -> Option<&'static str> {
7850    nova_resolver::RESERVED_TYPE_NAMES
7851        .iter()
7852        .copied()
7853        .find(|n| *n == name)
7854}
7855
7856/// The generic scope inside a trait definition / default method: `Self`
7857/// maps to `Param(0)`.
7858fn self_generic_scope() -> FxHashMap<String, u32> {
7859    let mut m = FxHashMap::default();
7860    m.insert("Self".to_string(), 0u32);
7861    m
7862}
7863
7864/// Set equality on two trait-bound lists, ignoring order and duplicates.
7865fn same_bound_set(a: &[DefId], b: &[DefId]) -> bool {
7866    a.iter().all(|d| b.contains(d)) && b.iter().all(|d| a.contains(d))
7867}
7868
7869fn lit_expr(lit: &ast::Literal, span: Span) -> hir::Expr {
7870    let (kind, ty) = match lit {
7871        ast::Literal::Int(v) => (hir::ExprKind::IntLit(*v), Ty::Int),
7872        ast::Literal::Float(v) => (hir::ExprKind::FloatLit(*v), Ty::Float),
7873        ast::Literal::Str(v) => (hir::ExprKind::StrLit(v.clone()), Ty::String),
7874        ast::Literal::Char(v) => (hir::ExprKind::CharLit(*v), Ty::Char),
7875        ast::Literal::Bool(v) => (hir::ExprKind::BoolLit(*v), Ty::Bool),
7876    };
7877    hir::Expr { kind, ty, span }
7878}
7879
7880fn error_expr(span: Span) -> hir::Expr {
7881    hir::Expr {
7882        kind: hir::ExprKind::Unit,
7883        ty: Ty::Error,
7884        span,
7885    }
7886}
7887
7888/// Every builtin's parameter types and result type — the whole of what
7889/// `check_builtin_call` needs to know about one, so that arity and argument
7890/// checking is a single shared code path rather than an arm per builtin.
7891///
7892/// It has to be a table rather than per-builtin code because most of these
7893/// are *not callable from a user program*: `Builtin::STD_ONLY` members are
7894/// seeded only into std modules' scopes, so their arity/type diagnostics are
7895/// unreachable from any Nova source and cannot be tested through it. Sharing
7896/// one checking path means the reachable builtins (`println`/`print`/`panic`)
7897/// exercise it, and this function's own table is directly unit-testable —
7898/// see `builtin_signatures_are_what_the_std_call_sites_use`. Its `match` is
7899/// exhaustive over the *type* `Builtin` (the compiler rejects a missing
7900/// variant there), and it drives that match with every entry of
7901/// `Builtin::ALL` — itself generated by the `builtins!` macro from the same
7902/// list that declares the enum, so *that* list cannot omit or duplicate a
7903/// variant either (see its doc comment). Neither piece alone would close the
7904/// gap; together they do.
7905///
7906/// **`Ty::Param(0)` here means "the caller's first type parameter", not "any
7907/// type".** `unify` relates two `Ty::Param`s only when their indices are equal
7908/// (`infer.rs`), so a signature mentioning `Param(0)` type-checks against a
7909/// call site inside a generic function whose *first* type parameter is the one
7910/// being passed, and against nothing else. That is exactly the shape
7911/// `std/task`'s wrappers have (`fn spawn<T>(fut: Future<T>)` and friends), and
7912/// it is what lets a builtin be generic at all without a signature
7913/// instantiation step: monomorphization substitutes the call expression's own
7914/// type along with every other, so `nova-mir` sees the concrete one. A
7915/// `std/task` function that declared a second type parameter *before* `T`
7916/// would fail here with `E0010` rather than miscompiling.
7917pub(crate) fn builtin_signature(builtin: Builtin) -> (Vec<Ty>, Ty) {
7918    // `Future<T>` where `T` is the caller's first type parameter — see above.
7919    let future_of_param0 = || Ty::Future(Box::new(Ty::Param(0)));
7920    match builtin {
7921        Builtin::Println | Builtin::Print | Builtin::EPrint | Builtin::EPrintln => {
7922            (vec![Ty::String], Ty::Unit)
7923        }
7924        Builtin::Panic => (vec![Ty::String], Ty::Never),
7925        Builtin::StrCmp => (vec![Ty::String, Ty::String], Ty::Int),
7926        Builtin::StrHash => (vec![Ty::String], Ty::Int),
7927        Builtin::CharToInt => (vec![Ty::Char], Ty::Int),
7928        Builtin::StrLenChars => (vec![Ty::String], Ty::Int),
7929        Builtin::StrChars => (vec![Ty::String], Ty::Array(Box::new(Ty::Char))),
7930        Builtin::StrFromChars => (vec![Ty::Array(Box::new(Ty::Char))], Ty::String),
7931        Builtin::StrJoin => (
7932            vec![Ty::String, Ty::Array(Box::new(Ty::String))],
7933            Ty::String,
7934        ),
7935        Builtin::StrIndexOf => (vec![Ty::String, Ty::String], Ty::Int),
7936        Builtin::JsonQuote => (vec![Ty::String], Ty::String),
7937        Builtin::StrToUpper | Builtin::StrToLower => (vec![Ty::String], Ty::String),
7938        Builtin::FloatFixed => (vec![Ty::Float, Ty::Int], Ty::String),
7939        Builtin::StrToFloat => (vec![Ty::String], Ty::Float),
7940        Builtin::TestSelector => (vec![], Ty::Int),
7941        Builtin::TaskSpawn => (vec![future_of_param0()], Ty::Int),
7942        Builtin::TaskIsDone => (vec![future_of_param0()], Ty::Bool),
7943        Builtin::TaskRelease => (vec![future_of_param0()], Ty::Unit),
7944        Builtin::TaskDrive => (vec![future_of_param0()], Ty::Unit),
7945        Builtin::TaskOutput => (vec![future_of_param0()], Ty::Param(0)),
7946        Builtin::TaskYieldFuture => (vec![], Ty::Future(Box::new(Ty::Unit))),
7947        Builtin::TaskSleepFutureNanos => (vec![Ty::Int], Ty::Future(Box::new(Ty::Unit))),
7948        Builtin::TaskJoinFuture => (vec![future_of_param0()], Ty::Future(Box::new(Ty::Unit))),
7949        Builtin::TaskTimeoutFuture => (
7950            vec![Ty::Int, future_of_param0()],
7951            Ty::Future(Box::new(Ty::Int)),
7952        ),
7953        Builtin::FsReadToString => (vec![Ty::String], Ty::Int),
7954        Builtin::FsWriteString => (vec![Ty::String, Ty::String], Ty::Int),
7955        Builtin::FsTakeString => (vec![], Ty::String),
7956        Builtin::FsLastErrorMessage => (vec![], Ty::String),
7957        Builtin::FsTempDir => (vec![], Ty::String),
7958        Builtin::FsExists => (vec![Ty::String], Ty::Bool),
7959        Builtin::FsCreateDir => (vec![Ty::String], Ty::Int),
7960        Builtin::FsCreateDirAll => (vec![Ty::String], Ty::Int),
7961        Builtin::FsRemoveFile => (vec![Ty::String], Ty::Int),
7962        Builtin::FsRemoveDirAll => (vec![Ty::String], Ty::Int),
7963        Builtin::FsReadDir => (vec![Ty::String], Ty::Int),
7964        Builtin::FsTakeStringArray => (vec![], Ty::Array(Box::new(Ty::String))),
7965        Builtin::FsKind => (vec![Ty::String], Ty::Int),
7966        Builtin::FsRead => (vec![Ty::String], Ty::Int),
7967        Builtin::FsTakeBytes => (vec![], Ty::Bytes),
7968        Builtin::FsWrite => (vec![Ty::String, Ty::Bytes], Ty::Int),
7969        Builtin::FileOpen => (
7970            vec![
7971                Ty::String,
7972                Ty::Bool,
7973                Ty::Bool,
7974                Ty::Bool,
7975                Ty::Bool,
7976                Ty::Bool,
7977                Ty::Bool,
7978            ],
7979            Ty::Int,
7980        ),
7981        Builtin::FileClose => (vec![Ty::Int], Ty::Int),
7982        Builtin::FileRead => (vec![Ty::Int, Ty::Int], Ty::Int),
7983        Builtin::FileWrite => (vec![Ty::Int, Ty::Bytes], Ty::Int),
7984        Builtin::FileFlush => (vec![Ty::Int], Ty::Int),
7985        Builtin::IoStdinRead => (vec![Ty::Int], Ty::Int),
7986        Builtin::IoStdoutWrite => (vec![Ty::Bytes], Ty::Int),
7987        Builtin::IoStderrWrite => (vec![Ty::Bytes], Ty::Int),
7988        Builtin::IoStdoutFlush => (vec![], Ty::Int),
7989        Builtin::IoStderrFlush => (vec![], Ty::Int),
7990        // Most of this group are future constructors, not status words —
7991        // `Ty::Future(Box::new(Ty::Int))`, not a plain `Ty::Int` — unlike
7992        // every `Fs*`/`File*`/`Io*` builtin above. `.await`ing the future
7993        // produces the `Int` status. `NetClose`, `NetListen` and
7994        // `NetLocalPort` are the exceptions and stay plain statuses, matching
7995        // `Builtin::FileClose`'s shape exactly: none of their *syscalls* can
7996        // block, so none of them has a suspension to model.
7997        //
7998        // **That is a claim about the syscalls, not about every argument.**
7999        // `NetClose` and `NetLocalPort` take an `Int` fd, so there is no
8000        // address for `std`'s `ToSocketAddrs` to resolve and no blocking lookup
8001        // to reach. Their syscalls are immediate kernel bookkeeping -- `close`
8002        // on the dropped handle, `getsockname` for the port -- so for those two
8003        // the syscall claim is the whole story. `NetListen` takes a `String` a
8004        // user program chooses, and a
8005        // hostname in it resolves through `std`'s `ToSocketAddrs` — a blocking
8006        // lookup with no future to park on. `resolve_addr`'s own doc comment in
8007        // `crates/nova-runtime/src/net.rs` records that caveat for `connect`,
8008        // and it applies to `NetListen` unchanged: out of scope rather than
8009        // handled. `bind` and `listen` not blocking is what makes `Ty::Int` the
8010        // right return for it; resolving a name it was handed is a separate
8011        // path that can still block this thread.
8012        //
8013        // `NetListen` and `NetLocalPort` returning `Ty::Int` rather than a
8014        // future is exactly what makes `std/net`'s `bind` and `local_port`
8015        // plain `fn`s.
8016        //
8017        // **`NetAccept` takes an `Int` fd as well and is not an exception**,
8018        // which is the reading a reader could otherwise take from the
8019        // paragraph above: an `Int` parameter is what removes the
8020        // *resolution* caveat, not what makes an operation non-suspending.
8021        // `accept` waits on a peer -- the one thing none of the three status
8022        // words does -- so it is a `Ty::Future` despite sharing
8023        // `NetLocalPort`'s exact parameter list.
8024        Builtin::NetConnect => (vec![Ty::String], Ty::Future(Box::new(Ty::Int))),
8025        Builtin::NetClose => (vec![Ty::Int], Ty::Int),
8026        Builtin::NetRead => (vec![Ty::Int, Ty::Int], Ty::Future(Box::new(Ty::Int))),
8027        Builtin::NetWrite => (vec![Ty::Int, Ty::Bytes], Ty::Future(Box::new(Ty::Int))),
8028        Builtin::NetReadTimeout => (
8029            vec![Ty::Int, Ty::Int, Ty::Int],
8030            Ty::Future(Box::new(Ty::Int)),
8031        ),
8032        Builtin::NetListen => (vec![Ty::String], Ty::Int),
8033        Builtin::NetLocalPort => (vec![Ty::Int], Ty::Int),
8034        Builtin::NetAccept => (vec![Ty::Int], Ty::Future(Box::new(Ty::Int))),
8035        Builtin::BytesLen => (vec![Ty::Bytes], Ty::Int),
8036        Builtin::BytesFromString => (vec![Ty::String], Ty::Bytes),
8037        Builtin::BytesIsUtf8 => (vec![Ty::Bytes], Ty::Bool),
8038        Builtin::BytesToStringUnchecked => (vec![Ty::Bytes], Ty::String),
8039        Builtin::BytesAt => (vec![Ty::Bytes, Ty::Int], Ty::Int),
8040        Builtin::BytesSlice => (vec![Ty::Bytes, Ty::Int, Ty::Int], Ty::Bytes),
8041        Builtin::BytesConcat => (vec![Ty::Bytes, Ty::Bytes], Ty::Bytes),
8042        Builtin::BytesToInts => (vec![Ty::Bytes], Ty::Array(Box::new(Ty::Int))),
8043        Builtin::BytesFromInts => (vec![Ty::Array(Box::new(Ty::Int))], Ty::Bytes),
8044        Builtin::BytesEq => (vec![Ty::Bytes, Ty::Bytes], Ty::Bool),
8045        Builtin::IntHashSeed => (vec![], Ty::Int),
8046        Builtin::HttpParseRequest => (vec![Ty::Bytes], Ty::Array(Box::new(Ty::Int))),
8047        Builtin::CryptoHash => (vec![Ty::Int, Ty::Bytes, Ty::Bytes, Ty::Bytes], Ty::Int),
8048        Builtin::CryptoRandomBytes => (vec![Ty::Int], Ty::Int),
8049        Builtin::CryptoRandomInt => (vec![Ty::Int, Ty::Int], Ty::Int),
8050        Builtin::TimeNowNanos => (vec![], Ty::Int),
8051        Builtin::TimeNowEpochNanos => (vec![], Ty::Int),
8052        Builtin::LogConfigLevel => (vec![], Ty::Int),
8053        Builtin::LogConfigToStderr => (vec![], Ty::Int),
8054        Builtin::LogSetConfig => (vec![Ty::Int, Ty::Int], Ty::Unit),
8055        Builtin::ShutdownRequested => (vec![], Ty::Bool),
8056        Builtin::ProcessArgCount => (vec![], Ty::Int),
8057        Builtin::ProcessArg => (vec![Ty::Int], Ty::String),
8058        Builtin::ProcessExit => (vec![Ty::Int], Ty::Unit),
8059    }
8060}
8061
8062/// Normalize a checked HIR pattern into the usefulness-algorithm form.
8063/// Bindings and wildcards are both "match anything"; variant payloads are
8064/// irrefutable in Phase 1, so each contributes a wildcard sub-pattern.
8065fn to_useful_pat(p: &hir::Pattern) -> usefulness::Pat {
8066    use usefulness::{Ctor, Pat};
8067    match p {
8068        hir::Pattern::Wildcard | hir::Pattern::Bind(_) => Pat::Wild,
8069        hir::Pattern::LitInt(v) => Pat::Ctor(Ctor::Int(*v), Vec::new()),
8070        hir::Pattern::LitBool(v) => Pat::Ctor(Ctor::Bool(*v), Vec::new()),
8071        hir::Pattern::LitStr(v) => Pat::Ctor(Ctor::Str(v.clone()), Vec::new()),
8072        hir::Pattern::Variant {
8073            sum,
8074            variant,
8075            binders,
8076        } => Pat::Ctor(
8077            Ctor::Variant(*sum, *variant),
8078            vec![Pat::Wild; binders.len()],
8079        ),
8080    }
8081}
8082
8083/// What `require_mutable_place` is being asked to mutate — the only thing that
8084/// differs between the three callers, and so the only thing that varies in the
8085/// `E0060` they report.
8086enum MutTarget {
8087    /// `arr[i] = v`.
8088    Element,
8089    /// `rec.f = v`.
8090    Field,
8091    /// A call to a method declaring `mut self`; carries the callee's
8092    /// impl-qualified `Def` name, the spelling every other inherent-method
8093    /// diagnostic uses.
8094    Receiver(String),
8095}
8096
8097impl MutTarget {
8098    /// The message when the place is rooted at an immutable local named `name`.
8099    fn immutable_message(&self, name: &str) -> String {
8100        match self {
8101            MutTarget::Element => format!("cannot assign to an element of immutable `{name}`"),
8102            MutTarget::Field => format!("cannot assign to a field of immutable `{name}`"),
8103            MutTarget::Receiver(m) => {
8104                format!("`{m}` mutates its receiver, but `{name}` is immutable")
8105            }
8106        }
8107    }
8108
8109    /// The message when the place has no assignable root at all.
8110    fn not_a_place_message(&self) -> String {
8111        match self {
8112            MutTarget::Element => {
8113                "cannot assign to an element of a temporary or non-assignable value".to_string()
8114            }
8115            MutTarget::Field => {
8116                "cannot assign to a field of a temporary or non-assignable value".to_string()
8117            }
8118            MutTarget::Receiver(m) => {
8119                format!("`{m}` mutates its receiver, which cannot be a temporary")
8120            }
8121        }
8122    }
8123}
8124
8125/// The mutability classification of the root binding of an assignment place.
8126enum PlaceRoot {
8127    /// Rooted at a mutable local — mutation through it is allowed.
8128    Mutable,
8129    /// Rooted at an immutable local: its name, and its declaration's span
8130    /// (spec 3.4b §4.1).
8131    ImmutableLocal(String, Span),
8132    /// No assignable root: a temporary (call result, literal), a constant, or
8133    /// an unresolved/multi-segment path.
8134    NotAPlace,
8135}
8136
8137fn convert_binop(op: ast::BinOp) -> hir::BinOp {
8138    match op {
8139        ast::BinOp::Add => hir::BinOp::Add,
8140        ast::BinOp::Sub => hir::BinOp::Sub,
8141        ast::BinOp::Mul => hir::BinOp::Mul,
8142        ast::BinOp::Div => hir::BinOp::Div,
8143        ast::BinOp::Rem => hir::BinOp::Rem,
8144        ast::BinOp::Eq => hir::BinOp::Eq,
8145        ast::BinOp::Ne => hir::BinOp::Ne,
8146        ast::BinOp::Lt => hir::BinOp::Lt,
8147        ast::BinOp::Le => hir::BinOp::Le,
8148        ast::BinOp::Gt => hir::BinOp::Gt,
8149        ast::BinOp::Ge => hir::BinOp::Ge,
8150        ast::BinOp::BitAnd => hir::BinOp::BitAnd,
8151        ast::BinOp::BitOr => hir::BinOp::BitOr,
8152        ast::BinOp::BitXor => hir::BinOp::BitXor,
8153        ast::BinOp::Shl => hir::BinOp::Shl,
8154        ast::BinOp::Shr => hir::BinOp::Shr,
8155        // And/Or are handled before conversion.
8156        ast::BinOp::And | ast::BinOp::Or => hir::BinOp::Eq,
8157    }
8158}
8159
8160fn assign_binop(op: ast::AssignOp) -> Option<hir::BinOp> {
8161    match op {
8162        ast::AssignOp::Assign => None,
8163        ast::AssignOp::AddAssign => Some(hir::BinOp::Add),
8164        ast::AssignOp::SubAssign => Some(hir::BinOp::Sub),
8165        ast::AssignOp::MulAssign => Some(hir::BinOp::Mul),
8166        ast::AssignOp::DivAssign => Some(hir::BinOp::Div),
8167        ast::AssignOp::RemAssign => Some(hir::BinOp::Rem),
8168        ast::AssignOp::BitOrAssign => Some(hir::BinOp::BitOr),
8169        ast::AssignOp::BitAndAssign => Some(hir::BinOp::BitAnd),
8170        ast::AssignOp::BitXorAssign => Some(hir::BinOp::BitXor),
8171        ast::AssignOp::ShlAssign => Some(hir::BinOp::Shl),
8172        ast::AssignOp::ShrAssign => Some(hir::BinOp::Shr),
8173    }
8174}
8175
8176/// The span to point at when a function body's type doesn't match its
8177/// declared return type: the trailing expression if there is one.
8178fn body_result_span(body: &Spanned<ast::Block>) -> Span {
8179    body.value
8180        .trailing
8181        .as_ref()
8182        .map(|e| e.span)
8183        .unwrap_or(body.span)
8184}
8185
8186/// Deeply apply the substitution to every type stored in an expression,
8187/// collecting spans whose types still contain inference variables.
8188fn finalize_expr(expr: &mut hir::Expr, icx: &InferCtx, residual: &mut Vec<Span>) {
8189    expr.ty = icx.apply(&expr.ty);
8190    if expr.ty.has_vars() {
8191        residual.push(expr.span);
8192    }
8193    match &mut expr.kind {
8194        hir::ExprKind::Call {
8195            type_args, args, ..
8196        } => {
8197            for t in type_args.iter_mut() {
8198                *t = icx.apply(t);
8199                if t.has_vars() {
8200                    residual.push(expr.span);
8201                }
8202            }
8203            for a in args {
8204                finalize_expr(a, icx, residual);
8205            }
8206        }
8207        hir::ExprKind::MakeClosure {
8208            type_args,
8209            captures,
8210            ..
8211        } => {
8212            for t in type_args.iter_mut() {
8213                *t = icx.apply(t);
8214                if t.has_vars() {
8215                    residual.push(expr.span);
8216                }
8217            }
8218            for c in captures {
8219                finalize_expr(c, icx, residual);
8220            }
8221        }
8222        hir::ExprKind::MakeVariant { args, .. }
8223        | hir::ExprKind::MakeRecord { fields: args, .. }
8224        | hir::ExprKind::MakeArray { elems: args }
8225        | hir::ExprKind::StrConcat(args) => {
8226            for a in args {
8227                finalize_expr(a, icx, residual);
8228            }
8229        }
8230        hir::ExprKind::FieldGet { target, .. } | hir::ExprKind::ArrayLen { target } => {
8231            finalize_expr(target, icx, residual);
8232        }
8233        hir::ExprKind::FieldSet { target, value, .. } => {
8234            finalize_expr(target, icx, residual);
8235            finalize_expr(value, icx, residual);
8236        }
8237        hir::ExprKind::ArrayRepeat { init, len } => {
8238            finalize_expr(init, icx, residual);
8239            finalize_expr(len, icx, residual);
8240        }
8241        hir::ExprKind::Index { target, index } => {
8242            finalize_expr(target, icx, residual);
8243            finalize_expr(index, icx, residual);
8244        }
8245        hir::ExprKind::IndexSet {
8246            target,
8247            index,
8248            value,
8249        } => {
8250            finalize_expr(target, icx, residual);
8251            finalize_expr(index, icx, residual);
8252            finalize_expr(value, icx, residual);
8253        }
8254        hir::ExprKind::TraitCall {
8255            self_ty,
8256            type_args,
8257            receiver,
8258            args,
8259            ..
8260        } => {
8261            *self_ty = icx.apply(self_ty);
8262            if self_ty.has_vars() {
8263                residual.push(expr.span);
8264            }
8265            // Resolve the method's own generic args; an uninferable one is E0011.
8266            for t in type_args.iter_mut() {
8267                *t = icx.apply(t);
8268                if t.has_vars() {
8269                    residual.push(expr.span);
8270                }
8271            }
8272            if let Some(receiver) = receiver {
8273                finalize_expr(receiver, icx, residual);
8274            }
8275            for a in args {
8276                finalize_expr(a, icx, residual);
8277            }
8278        }
8279        hir::ExprKind::Binary { lhs, rhs, .. }
8280        | hir::ExprKind::LogicalAnd { lhs, rhs }
8281        | hir::ExprKind::LogicalOr { lhs, rhs } => {
8282            finalize_expr(lhs, icx, residual);
8283            finalize_expr(rhs, icx, residual);
8284        }
8285        hir::ExprKind::Unary { expr: inner, .. }
8286        | hir::ExprKind::ToStr(inner)
8287        | hir::ExprKind::Let { init: inner, .. }
8288        | hir::ExprKind::Assign { value: inner, .. }
8289        | hir::ExprKind::Await(inner) => {
8290            finalize_expr(inner, icx, residual);
8291        }
8292        hir::ExprKind::Block { stmts, trailing } => {
8293            for s in stmts {
8294                finalize_expr(s, icx, residual);
8295            }
8296            if let Some(t) = trailing {
8297                finalize_expr(t, icx, residual);
8298            }
8299        }
8300        hir::ExprKind::If { cond, then, else_ } => {
8301            finalize_expr(cond, icx, residual);
8302            finalize_expr(then, icx, residual);
8303            if let Some(e) = else_ {
8304                finalize_expr(e, icx, residual);
8305            }
8306        }
8307        hir::ExprKind::While { cond, body } => {
8308            finalize_expr(cond, icx, residual);
8309            finalize_expr(body, icx, residual);
8310        }
8311        hir::ExprKind::Match { scrutinee, arms } => {
8312            finalize_expr(scrutinee, icx, residual);
8313            for arm in arms {
8314                finalize_expr(&mut arm.body, icx, residual);
8315            }
8316        }
8317        hir::ExprKind::Return(value) => {
8318            if let Some(v) = value {
8319                finalize_expr(v, icx, residual);
8320            }
8321        }
8322        hir::ExprKind::IntLit(_)
8323        | hir::ExprKind::FloatLit(_)
8324        | hir::ExprKind::BoolLit(_)
8325        | hir::ExprKind::StrLit(_)
8326        | hir::ExprKind::CharLit(_)
8327        | hir::ExprKind::Unit
8328        | hir::ExprKind::Break
8329        | hir::ExprKind::Continue
8330        | hir::ExprKind::Local(_) => {}
8331    }
8332}
8333
8334#[cfg(test)]
8335mod tests {
8336    use super::*;
8337    use nova_diagnostics::FileId;
8338    use nova_lexer::lex;
8339    use nova_parser::parse;
8340    use nova_resolver::{resolve, TestFn};
8341
8342    fn check_src(src: &str) -> CheckResult {
8343        let file_id = FileId::DUMMY;
8344        let (tokens, lex_errors) = lex(src, file_id);
8345        assert!(lex_errors.is_empty(), "lex errors: {lex_errors:?}");
8346        let (ast, parse_errors) = parse(&tokens, file_id);
8347        assert!(parse_errors.is_empty(), "parse errors: {parse_errors:?}");
8348        let ast = ast.expect("no AST");
8349        let resolved = resolve(&ast);
8350        // Type-check against the merged file (input + the implicit prelude,
8351        // which is `std/core` — see ADR 0004), whose `item_index`es the
8352        // definitions refer to. Resolver diagnostics (Task 2's `@test`
8353        // attribute validation among them: E0082-E0085) used to be asserted
8354        // empty here and dropped; they are prepended into the returned
8355        // `CheckResult` instead, in the same order the driver renders them
8356        // (resolve, then check), since callers now legitimately exercise
8357        // resolver-level errors through this helper. This is a no-op for
8358        // every caller that never triggered a resolver diagnostic in the
8359        // first place — which, before this change, was every caller, since
8360        // the old assert would otherwise have failed their test.
8361        let mut checked = check(&resolved.file, &resolved.definitions);
8362        let mut diagnostics = resolved.diagnostics;
8363        diagnostics.append(&mut checked.diagnostics);
8364        checked.diagnostics = diagnostics;
8365        checked
8366    }
8367
8368    /// Resolve `src` and return its collected `@test` functions, in the order
8369    /// `nova_resolver::resolve`'s item walk visited them — source order. Kept
8370    /// separate from `check_src`/`CheckResult` (which has no field for this):
8371    /// `TestFn` lives on the resolver's output, and this test needs nothing
8372    /// from the checker.
8373    fn collect_tests_of(src: &str) -> Vec<TestFn> {
8374        let file_id = FileId::DUMMY;
8375        let (tokens, lex_errors) = lex(src, file_id);
8376        assert!(lex_errors.is_empty(), "lex errors: {lex_errors:?}");
8377        let (ast, parse_errors) = parse(&tokens, file_id);
8378        assert!(parse_errors.is_empty(), "parse errors: {parse_errors:?}");
8379        let ast = ast.expect("no AST");
8380        let resolved = resolve(&ast);
8381        assert!(
8382            resolved.diagnostics.is_empty(),
8383            "resolve errors: {:?}",
8384            resolved.diagnostics
8385        );
8386        resolved.tests
8387    }
8388
8389    fn error_codes(result: &CheckResult) -> Vec<&str> {
8390        result
8391            .diagnostics
8392            .iter()
8393            .filter(|d| d.severity == nova_diagnostics::Severity::Error)
8394            .map(|d| d.code.as_str())
8395            .collect()
8396    }
8397
8398    /// Every expression in the named function's body, outermost first, for
8399    /// tests that need to inspect what the checker actually built rather than
8400    /// trust the absence of diagnostics.
8401    fn exprs_in<'m>(module: &'m hir::Module, fn_name: &str) -> Vec<&'m hir::Expr> {
8402        fn walk<'e>(e: &'e hir::Expr, out: &mut Vec<&'e hir::Expr>) {
8403            out.push(e);
8404            for c in child_exprs(e) {
8405                walk(c, out);
8406            }
8407        }
8408        let f = module
8409            .functions
8410            .iter()
8411            .find(|f| f.name == fn_name)
8412            .expect("the named function exists");
8413        let mut exprs = Vec::new();
8414        walk(&f.body, &mut exprs);
8415        exprs
8416    }
8417
8418    /// Every `FieldSet` in the named function, for tests that need to inspect
8419    /// the store the checker actually built rather than trust the absence of
8420    /// diagnostics.
8421    fn field_sets_in<'m>(module: &'m hir::Module, fn_name: &str) -> Vec<&'m hir::Expr> {
8422        exprs_in(module, fn_name)
8423            .into_iter()
8424            .filter(|e| matches!(e.kind, hir::ExprKind::FieldSet { .. }))
8425            .collect()
8426    }
8427
8428    #[test]
8429    fn hello_world_checks() {
8430        let r = check_src("fn main() { println(\"hi\") }");
8431        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
8432        // `functions` is whole-program — it also carries every method
8433        // std/core defines on Option/Result — so a total count shifts every
8434        // time std/core grows. Assert the relationship this test is actually
8435        // about instead: checking produced exactly one `main`, taking no
8436        // parameters.
8437        let mains: Vec<_> = r
8438            .module
8439            .functions
8440            .iter()
8441            .filter(|f| f.name == "main")
8442            .collect();
8443        assert_eq!(
8444            mains.len(),
8445            1,
8446            "expected exactly one `main`, got {:?}",
8447            r.module
8448                .functions
8449                .iter()
8450                .map(|f| f.name.as_str())
8451                .collect::<Vec<_>>()
8452        );
8453        assert_eq!(mains[0].params, 0, "main should take no parameters");
8454    }
8455
8456    #[test]
8457    fn fibonacci_checks() {
8458        let r = check_src(
8459            "fn fib(n: Int) -> Int {\n\
8460                 if n <= 1 { n } else { fib(n - 1) + fib(n - 2) }\n\
8461             }\n\
8462             fn main() { println(\"${fib(10)}\") }",
8463        );
8464        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
8465    }
8466
8467    #[test]
8468    fn type_mismatch_reports_e0010() {
8469        let r = check_src("fn main() { let x: Int = \"hello\" }");
8470        assert!(error_codes(&r).contains(&"E0010"), "{:?}", r.diagnostics);
8471    }
8472
8473    #[test]
8474    fn inherent_method_generic_infers_and_checks() {
8475        // `map<U>` introduces its own `U`, inferred from the mapper's return.
8476        let r = check_src(
8477            "record Box<T> { value: T }\n\
8478             impl<T> Box<T> {\n\
8479                 fn map<U>(self, f: fn(T) -> U) -> Box<U> { Box { value: f(self.value) } }\n\
8480             }\n\
8481             fn twice(n: Int) -> Int { n * 2 }\n\
8482             fn main() {\n\
8483                 let a = Box { value: 5 }\n\
8484                 let b = a.map(twice)\n\
8485                 println(\"${b.value}\")\n\
8486             }",
8487        );
8488        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
8489    }
8490
8491    #[test]
8492    fn generic_trait_method_typechecks() {
8493        // A trait method with its own generic parameter, implemented and called.
8494        let r = check_src(
8495            "trait Mapper { fn remap<U>(self, f: fn(Int) -> U) -> U }\n\
8496             record Thing { v: Int }\n\
8497             impl Mapper for Thing { fn remap<U>(self, f: fn(Int) -> U) -> U { f(self.v) } }\n\
8498             fn dbl(n: Int) -> Int { n * 2 }\n\
8499             fn main() { let t = Thing { v: 21 }\n println(\"${t.remap(dbl)}\") }",
8500        );
8501        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
8502    }
8503
8504    #[test]
8505    fn generic_trait_method_arity_mismatch_reports_e0072() {
8506        // An impl method whose generic arity disagrees with the trait's.
8507        let r = check_src(
8508            "trait Mapper { fn remap<U>(self, f: fn(Int) -> U) -> U }\n\
8509             record Thing { v: Int }\n\
8510             impl Mapper for Thing { fn remap(self, f: fn(Int) -> Int) -> Int { f(self.v) } }\n\
8511             fn main() { }",
8512        );
8513        assert!(error_codes(&r).contains(&"E0072"), "{:?}", r.diagnostics);
8514    }
8515
8516    #[test]
8517    fn method_generic_shadowing_impl_generic_reports_e0403() {
8518        let r = check_src(
8519            "record Box<T> { value: T }\n\
8520             impl<T> Box<T> { fn weird<T>(self, x: T) -> T { x } }\n\
8521             fn main() { let a = Box { value: 1 }\n println(\"${a.weird(2)}\") }",
8522        );
8523        assert!(error_codes(&r).contains(&"E0403"), "{:?}", r.diagnostics);
8524    }
8525
8526    #[test]
8527    fn duplicate_method_generic_name_reports_e0403() {
8528        // Two method generics with the same name — rejected at the declaration,
8529        // not left as a silently-uncallable method.
8530        let r = check_src(
8531            "record Box<T> { value: T }\n\
8532             impl<T> Box<T> { fn weird<U, U>(self, a: U, b: U) -> U { a } }\n\
8533             fn main() { let a = Box { value: 1 }\n println(\"${a.value}\") }",
8534        );
8535        assert!(error_codes(&r).contains(&"E0403"), "{:?}", r.diagnostics);
8536    }
8537
8538    #[test]
8539    fn duplicate_generic_on_free_function_reports_e0403() {
8540        // `fn f<U, U>` — the same name twice in a free function's generic list.
8541        let r = check_src(
8542            "fn f<U, U>(a: U, b: U) -> U { a }\n\
8543             fn main() { }",
8544        );
8545        assert!(error_codes(&r).contains(&"E0403"), "{:?}", r.diagnostics);
8546    }
8547
8548    #[test]
8549    fn duplicate_generic_on_record_reports_e0403() {
8550        let r = check_src(
8551            "record R<T, T> { a: T }\n\
8552             fn main() { }",
8553        );
8554        assert!(error_codes(&r).contains(&"E0403"), "{:?}", r.diagnostics);
8555    }
8556
8557    #[test]
8558    fn duplicate_generic_on_sum_reports_e0403() {
8559        let r = check_src(
8560            "type E<T, T> = | A(T) | B\n\
8561             fn main() { }",
8562        );
8563        assert!(error_codes(&r).contains(&"E0403"), "{:?}", r.diagnostics);
8564    }
8565
8566    #[test]
8567    fn duplicate_generic_on_trait_method_reports_e0403() {
8568        let r = check_src(
8569            "trait Tr { fn m<U, U>(self, a: U) -> U }\n\
8570             fn main() { }",
8571        );
8572        assert!(error_codes(&r).contains(&"E0403"), "{:?}", r.diagnostics);
8573    }
8574
8575    #[test]
8576    fn duplicate_generic_on_impl_block_reports_e0403() {
8577        let r = check_src(
8578            "record Box<T> { value: T }\n\
8579             impl<T, T> Box<T> { fn get(self) -> Int { 0 } }\n\
8580             fn main() { }",
8581        );
8582        assert!(error_codes(&r).contains(&"E0403"), "{:?}", r.diagnostics);
8583    }
8584
8585    #[test]
8586    fn distinct_generics_are_accepted() {
8587        // Guard: distinct names on a function must not trip the duplicate check.
8588        let r = check_src(
8589            "fn f<T, U>(a: T, b: U) -> T { a }\n\
8590             fn main() { let x = f(1, \"s\")\n println(\"${x}\") }",
8591        );
8592        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
8593    }
8594
8595    #[test]
8596    fn generic_impl_method_for_nongeneric_trait_method_reports_e0072() {
8597        // A generic method in a trait impl whose trait method is non-generic is
8598        // an arity mismatch (the impl declares generics the trait does not), and
8599        // must not cascade a false E0001 for the method's own generic.
8600        let r = check_src(
8601            "trait Mapper { fn remap(self) -> Int }\n\
8602             record Box<T> { value: T }\n\
8603             impl<T> Mapper for Box<T> { fn remap<U>(self, x: U) -> Int { 0 } }\n\
8604             fn main() { }",
8605        );
8606        let codes = error_codes(&r);
8607        assert!(codes.contains(&"E0072"), "{:?}", r.diagnostics);
8608        assert!(
8609            !codes.contains(&"E0001"),
8610            "spurious E0001: {:?}",
8611            r.diagnostics
8612        );
8613    }
8614
8615    #[test]
8616    fn impl_dropping_trait_method_bound_reports_e0072() {
8617        // The impl drops the trait method's declared generic bound
8618        // (`<U: Show>` -> `<U>`). Conformance must reject it (E0072); otherwise
8619        // the trait's bound is not a contract the call site can rely on and an
8620        // invalid call slips through to run unsoundly.
8621        let r = check_src(
8622            "trait Show { fn show(self) -> String }\n\
8623             trait Tagger { fn tag<U: Show>(self, x: U) -> String }\n\
8624             record Thing { v: Int }\n\
8625             impl Tagger for Thing { fn tag<U>(self, x: U) -> String { \"no-show\" } }\n\
8626             fn main() { }",
8627        );
8628        assert!(error_codes(&r).contains(&"E0072"), "{:?}", r.diagnostics);
8629    }
8630
8631    #[test]
8632    fn impl_adding_undeclared_method_bound_reports_e0072() {
8633        // The impl adds a bound the trait does not declare (`<U>` -> `<U: Show>`).
8634        // Conformance must reject it up front, rather than letting a valid-per-
8635        // trait program fail later at monomorphization with a misattributed span.
8636        let r = check_src(
8637            "trait Show { fn show(self) -> String }\n\
8638             trait Tagger { fn tag<U>(self, x: U) -> String }\n\
8639             record Thing { v: Int }\n\
8640             impl Tagger for Thing { fn tag<U: Show>(self, x: U) -> String { \"tagged\" } }\n\
8641             fn main() { }",
8642        );
8643        assert!(error_codes(&r).contains(&"E0072"), "{:?}", r.diagnostics);
8644    }
8645
8646    #[test]
8647    fn faithful_method_bound_impl_typechecks() {
8648        // An impl that repeats the trait method's bound exactly must NOT trip the
8649        // bound-conformance check.
8650        let r = check_src(
8651            "trait Show { fn show(self) -> String }\n\
8652             trait Tagger { fn tag<U: Show>(self, x: U) -> String }\n\
8653             record Thing { v: Int }\n\
8654             impl Tagger for Thing { fn tag<U: Show>(self, x: U) -> String { \"ok\" } }\n\
8655             fn main() { }",
8656        );
8657        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
8658    }
8659
8660    #[test]
8661    fn async_trait_method_declaration_reports_e0900() {
8662        // `async` on a trait-method *declaration* (no body) must still be
8663        // rejected, same as a default (bodied) trait method and an extern fn —
8664        // Phase 2.3a Task 2 lifts this same rejection for free functions and
8665        // impl/inherent methods (see `async_inherent_method_is_accepted`), but
8666        // trait methods are the half that stays rejected (see
8667        // `async_trait_method_still_reports_e0900`).
8668        let r = check_src(
8669            "trait Foo { async fn bar(self) -> Int }\n\
8670             record Thing { v: Int }\n\
8671             impl Foo for Thing { fn bar(self) -> Int { self.v } }\n\
8672             fn main() { let t = Thing { v: 1 } }",
8673        );
8674        assert!(error_codes(&r).contains(&"E0900"), "{:?}", r.diagnostics);
8675    }
8676
8677    #[test]
8678    fn panic_typechecks_as_never_in_match_arm() {
8679        // `panic` diverges, so the match's type comes from the other arm.
8680        let r = check_src(
8681            "fn get(o: Option<Int>) -> Int {\n\
8682                 match o { Some(v) => v, None => panic(\"none\") }\n\
8683             }\n\
8684             fn main() { println(\"${get(Some(3))}\") }",
8685        );
8686        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
8687    }
8688
8689    #[test]
8690    fn panic_rejects_non_string_argument() {
8691        let r = check_src("fn main() { panic(7) }");
8692        assert!(error_codes(&r).contains(&"E0010"), "{:?}", r.diagnostics);
8693    }
8694
8695    #[test]
8696    fn async_default_trait_method_reports_e0900_once() {
8697        // A bodied (default) async trait method is still rejected — exactly once,
8698        // not duplicated by both the table and default-body passes.
8699        let r = check_src(
8700            "trait Foo { async fn bar(self) -> Int { 7 } }\n\
8701             record Thing { v: Int }\n\
8702             impl Foo for Thing { fn bar(self) -> Int { self.v } }\n\
8703             fn main() { let t = Thing { v: 1 } }",
8704        );
8705        let n = error_codes(&r).iter().filter(|c| **c == "E0900").count();
8706        assert_eq!(n, 1, "{:?}", r.diagnostics);
8707    }
8708
8709    #[test]
8710    fn async_fn_returns_a_future_of_its_declared_type() {
8711        // The declared return type is the future's OUTPUT, per the spec's own
8712        // signatures (`pub async fn join(self) -> T`). So passing the CALL of an
8713        // async fn where the output type is expected must be a mismatch.
8714        let r = check_src(
8715            "async fn f() -> Int { 1 }\n\
8716             fn g() -> Int { f() }\n\
8717             fn main() {}",
8718        );
8719        let d = r
8720            .diagnostics
8721            .iter()
8722            .find(|d| d.code == "E0010")
8723            .unwrap_or_else(|| {
8724                panic!(
8725                    "calling an async fn yields Future<Int>, not Int; got {:?}",
8726                    r.diagnostics.iter().map(|d| &d.code).collect::<Vec<_>>()
8727                )
8728            });
8729        // A code-only assertion here would survive a reversal (declared and
8730        // actual body types swapped in the message) or a wrong-layer bug
8731        // (e.g. the body type shown as bare `Int` instead of `Future<Int>`,
8732        // which would mean the E0010 fired for some other reason than the
8733        // one this test exists to pin). Tying each type name to its role via
8734        // the surrounding words, not just checking both names appear
8735        // somewhere, is what catches a reversal: after swapping, the message
8736        // would contain "return `Future<Int>`" and "type `Int`" instead.
8737        // Measured, not assumed: `` `g` should return `Int` but its body has
8738        // type `Future<Int>` ``.
8739        assert!(
8740            d.message.contains("return `Int`") && d.message.contains("has type `Future<Int>`"),
8741            "E0010 must name the declared return type (`Int`) and the actual \
8742             body type (`Future<Int>`) in their correct roles; got {:?}",
8743            d.message
8744        );
8745    }
8746
8747    #[test]
8748    fn awaiting_a_future_yields_its_output_type() {
8749        // The positive case: inside an async fn, `.await` unwraps to the output
8750        // and type-checks against it. Assert CLEAN, and assert on the messages so
8751        // a spurious diagnostic is visible rather than counted.
8752        let r = check_src(
8753            "async fn f() -> Int { 1 }\n\
8754             async fn g() -> Int { f().await }\n\
8755             fn main() {}",
8756        );
8757        assert!(
8758            r.diagnostics.is_empty(),
8759            "expected no diagnostics, got {:?}",
8760            r.diagnostics
8761                .iter()
8762                .map(|d| d.message.clone())
8763                .collect::<Vec<_>>()
8764        );
8765    }
8766
8767    #[test]
8768    fn awaiting_yields_the_output_not_the_future() {
8769        // Discriminates "await returns T" from "await returns Future<T>" by
8770        // giving `g` a DIFFERENT Output type (Bool) than `f` (Int): a wrong
8771        // `.await` result is named directly in an Int-vs-Bool mismatch here.
8772        // (Measured: in the current implementation a total forgot-to-unwrap
8773        // bug also happens to fail the clean test above, via its own
8774        // body-vs-declared-return unify — but this test does not depend on
8775        // that coincidence, and would still catch a subtler bug, e.g. one
8776        // that always produces `Int`, which would pass the clean test by luck.)
8777        let r = check_src(
8778            "async fn f() -> Int { 1 }\n\
8779             async fn g() -> Bool { f().await }\n\
8780             fn main() {}",
8781        );
8782        let msgs: Vec<String> = r.diagnostics.iter().map(|d| d.message.clone()).collect();
8783        assert!(
8784            msgs.iter().any(|m| m.contains("Int") && m.contains("Bool")),
8785            "expected an Int-vs-Bool mismatch naming both types, got {msgs:?}"
8786        );
8787    }
8788
8789    #[test]
8790    fn await_outside_an_async_fn_reports_e0086() {
8791        let r = check_src(
8792            "async fn f() -> Int { 1 }\n\
8793             fn g() -> Int { f().await }\n\
8794             fn main() {}",
8795        );
8796        let d = r
8797            .diagnostics
8798            .iter()
8799            .find(|d| d.code == "E0086")
8800            .unwrap_or_else(|| {
8801                panic!(
8802                    "expected E0086, got {:?}",
8803                    r.diagnostics.iter().map(|d| &d.code).collect::<Vec<_>>()
8804                )
8805            });
8806        // Assert the message names the actual problem. A code-only assertion here
8807        // would survive swapping E0086's and E0087's message text.
8808        assert!(
8809            d.message.contains("async"),
8810            "E0086 must explain that await requires an async fn; got {:?}",
8811            d.message
8812        );
8813    }
8814
8815    #[test]
8816    fn await_on_a_non_future_reports_e0087() {
8817        let r = check_src(
8818            "async fn g() -> Int { let x = 1\n x.await }\n\
8819             fn main() {}",
8820        );
8821        let d = r
8822            .diagnostics
8823            .iter()
8824            .find(|d| d.code == "E0087")
8825            .unwrap_or_else(|| {
8826                panic!(
8827                    "expected E0087, got {:?}",
8828                    r.diagnostics.iter().map(|d| &d.code).collect::<Vec<_>>()
8829                )
8830            });
8831        assert!(
8832            d.message.contains("Int"),
8833            "E0087 must name the type that is not a future; got {:?}",
8834            d.message
8835        );
8836    }
8837
8838    /// `std/task`'s spawn-and-join surface, at `Float`.
8839    ///
8840    /// `Float` rather than `Int` deliberately: `mir_ty` maps `Int`, `Char`,
8841    /// `String`, `Fn`, `Sum`, `Record` and `Array` all onto the same 64-bit
8842    /// class, so an `Int` fixture proves nothing about a value that has to
8843    /// travel through the executor's `i64` output slot. `Float` is the one class
8844    /// that does not, and a `spawn`/`join` pair that lost the output's type
8845    /// would show up here as a `Float`-vs-something mismatch rather than as a
8846    /// clean compile with wrong bits at run time.
8847    #[test]
8848    fn std_task_spawn_and_join_typecheck() {
8849        let r = check_src(
8850            "async fn f() -> Float { 1.5 }\n\
8851             async fn g() -> Float { let h = spawn(f())\n h.join().await }\n\
8852             fn main() { }",
8853        );
8854        assert!(
8855            r.diagnostics.is_empty(),
8856            "expected no diagnostics, got {:?}",
8857            r.diagnostics
8858                .iter()
8859                .map(|d| d.message.clone())
8860                .collect::<Vec<_>>()
8861        );
8862        // Not vacuous on an empty diagnostic list alone: `Ty::Error` unifies
8863        // with anything, so a `spawn` that failed to resolve would also produce
8864        // no *further* diagnostics. Pin that `g`'s body really is typed `Float`.
8865        let awaited = exprs_in(&r.module, "g")
8866            .into_iter()
8867            .find(|e| matches!(e.kind, hir::ExprKind::Await(_)))
8868            .expect("`h.join().await` is an await");
8869        assert_eq!(
8870            awaited.ty,
8871            Ty::Float,
8872            "`join().await` on a `JoinHandle<Float>` must be typed `Float`"
8873        );
8874    }
8875
8876    /// `block_on` is how async is entered *from* sync code, so it must not be
8877    /// mistaken for an await: `E0086` is the diagnostic for awaiting outside an
8878    /// `async fn`, and a `block_on` that tripped it would leave no way to run
8879    /// async code from `main` or from a `@test` function at all.
8880    #[test]
8881    fn block_on_outside_async_is_allowed() {
8882        let r = check_src("async fn f() -> Int { 1 }\nfn main() { let x = block_on(f()) }");
8883        assert!(
8884            r.diagnostics.is_empty(),
8885            "expected no diagnostics, got {:?}",
8886            r.diagnostics
8887                .iter()
8888                .map(|d| d.message.clone())
8889                .collect::<Vec<_>>()
8890        );
8891    }
8892
8893    /// `yield_now` is an `async fn` returning unit, so it is only usable with
8894    /// `.await` and only inside another `async fn` — the shape the gate fixture
8895    /// depends on.
8896    #[test]
8897    fn yield_now_is_awaitable_inside_an_async_fn() {
8898        let r = check_src(
8899            "async fn f() -> Int { yield_now().await\n 1 }\n\
8900             fn main() { let x = block_on(f()) }",
8901        );
8902        assert!(
8903            r.diagnostics.is_empty(),
8904            "expected no diagnostics, got {:?}",
8905            r.diagnostics
8906                .iter()
8907                .map(|d| d.message.clone())
8908                .collect::<Vec<_>>()
8909        );
8910    }
8911
8912    /// No std-only builtin is *callable* from a user module, which for the
8913    /// `Future`-taking ones is a type-safety property and not only a
8914    /// namespacing one.
8915    ///
8916    /// Their signatures mention `Ty::Param(0)`, meaning "the caller's first type
8917    /// parameter" — which only exists inside a generic function. Called from an
8918    /// ordinary `fn main`, such a builtin's result expression would be typed
8919    /// `Param(0)` with nothing to substitute it, and `mir_ty` maps a surviving
8920    /// `Param` to `MirTy::Unit`: a value silently dropped rather than a
8921    /// diagnostic. So the fact that these names do not resolve in user code is
8922    /// what keeps that unreachable, and it must not be quietly relaxed by moving
8923    /// one to `Builtin::GLOBAL`.
8924    ///
8925    /// Driven off `Builtin::STD_ONLY` and `b.name()` rather than a list of
8926    /// spellings, so a builtin added to that constant is covered the moment it
8927    /// joins — and so renaming one cannot leave this passing against a name
8928    /// nothing declares. `nova-resolver`'s `no_std_only_builtin_is_a_reserved_word`
8929    /// is the other direction: a user *definition* of one of these names is
8930    /// allowed.
8931    #[test]
8932    fn no_std_only_builtin_is_callable_from_user_code() {
8933        for b in Builtin::STD_ONLY {
8934            let name = b.name();
8935            let r = check_src(&format!("fn main() {{ {name}() }}"));
8936            assert!(
8937                error_codes(&r).contains(&"E0001"),
8938                "`{name}` must not resolve in a user module, got {:?}",
8939                r.diagnostics
8940                    .iter()
8941                    .map(|d| (d.code.clone(), d.message.clone()))
8942                    .collect::<Vec<_>>()
8943            );
8944        }
8945    }
8946
8947    #[test]
8948    fn async_inherent_method_is_accepted() {
8949        // NOT optional: the spec's JoinHandle::join is `pub async fn join(self) -> T`,
8950        // an inherent async method. Task 7 cannot write std/task without this.
8951        let r = check_src(
8952            "record W { v: Int }\n\
8953             impl W { async fn get(self) -> Int { self.v } }\n\
8954             fn main() {}",
8955        );
8956        assert!(
8957            r.diagnostics.is_empty(),
8958            "expected no diagnostics, got {:?}",
8959            r.diagnostics
8960                .iter()
8961                .map(|d| d.message.clone())
8962                .collect::<Vec<_>>()
8963        );
8964    }
8965
8966    #[test]
8967    fn async_trait_method_still_reports_e0900() {
8968        // Trait async needs associated-type futures; out of scope for 2.3a.
8969        // This pins the HALF-lift: :852 becomes conditional, it does not vanish.
8970        let r = check_src("trait T { async fn m(self) -> Int }\nfn main() {}");
8971        let d = r
8972            .diagnostics
8973            .iter()
8974            .find(|d| d.code == "E0900")
8975            .unwrap_or_else(|| panic!("expected E0900, got {:?}", r.diagnostics));
8976        // A code-only assertion here would survive the message changing to
8977        // name a different unsupported construct entirely (e.g. if this
8978        // E0900 came from the unrelated "where clauses on trait methods"
8979        // check instead, which fires on the same kind of source). Measured,
8980        // not assumed: "async methods are not supported yet".
8981        assert!(
8982            d.message.contains("async methods"),
8983            "E0900 must identify async methods as the unsupported construct; got {:?}",
8984            d.message
8985        );
8986    }
8987
8988    /// An `async` method in a trait *impl* is refused as a return-type
8989    /// mismatch, not as `E0900`.
8990    ///
8991    /// The `E0900` check was removed from `collect_impls`, so what catches this
8992    /// is trait-conformance: an `async fn` returns `Future<T>`, the trait
8993    /// declared `T`, and those are different types. The refusal is what matters
8994    /// and it is legible on its own, but the *code* is `E0072` — pinned here
8995    /// because the sibling above covers only the declaration, which is why the
8996    /// claim that `E0900` applies "in a trait declaration, a default body, and
8997    /// an impl alike" was able to stand in two documents while being false for
8998    /// the third position. The trait's own `m` is deliberately **not** `async`:
8999    /// were it async, `E0900` would fire on the declaration and mask which
9000    /// check refused the impl.
9001    #[test]
9002    fn async_trait_impl_method_is_refused_as_a_return_type_mismatch() {
9003        let r = check_src(
9004            "trait T { fn m(self) -> Int }\n\
9005             record C { v: Int }\n\
9006             impl T for C { async fn m(self) -> Int { self.v } }\n\
9007             fn main() {}",
9008        );
9009        let d = r
9010            .diagnostics
9011            .iter()
9012            .find(|d| d.code == "E0072")
9013            .unwrap_or_else(|| panic!("expected E0072, got {:?}", r.diagnostics));
9014        // Naming both types, not just the code: the message is the whole reason
9015        // this refusal is acceptable without a dedicated diagnostic, so a
9016        // version of it that said only "signature mismatch" would not be.
9017        assert!(
9018            d.message.contains("Future<Int>") && d.message.contains("declares `Int`"),
9019            "the mismatch must name the future and what the trait declared; got {:?}",
9020            d.message
9021        );
9022        // And it must NOT be reported as the unsupported-construct code, which
9023        // is what the corrected documentation now says.
9024        assert!(
9025            !error_codes(&r).contains(&"E0900"),
9026            "an impl-side async method is not `E0900`; got {:?}",
9027            r.diagnostics
9028        );
9029    }
9030
9031    #[test]
9032    fn async_extern_fn_still_reports_e0900() {
9033        let r = check_src("extern \"C\" { async fn c_thing() -> Int }\nfn main() {}");
9034        let d = r
9035            .diagnostics
9036            .iter()
9037            .find(|d| d.code == "E0900")
9038            .unwrap_or_else(|| panic!("expected E0900, got {:?}", r.diagnostics));
9039        // Measured, not assumed: "async extern functions are not supported
9040        // yet". Distinguishes this from the trait-method message above (and
9041        // from an extern block's OTHER E0900 sites, e.g. an unsupported ABI
9042        // or generic extern fn) rather than accepting any E0900 on the input.
9043        assert!(
9044            d.message.contains("async extern functions"),
9045            "E0900 must identify async extern functions as the unsupported construct; got {:?}",
9046            d.message
9047        );
9048    }
9049
9050    #[test]
9051    fn generic_async_fn_instantiates_at_float() {
9052        // Float, not Int/Bool: mir_ty collapses Int/Char to I64 and five variants
9053        // to Ptr (= i64 on x86-64), so an Int-vs-String pair tests nothing at any
9054        // seam. Float is F64 and crosses register banks.
9055        let r = check_src(
9056            "async fn id<T>(x: T) -> T { x }\n\
9057             async fn g() -> Float { id(1.5).await }\n\
9058             fn main() {}",
9059        );
9060        assert!(
9061            r.diagnostics.is_empty(),
9062            "expected no diagnostics, got {:?}",
9063            r.diagnostics
9064                .iter()
9065                .map(|d| d.message.clone())
9066                .collect::<Vec<_>>()
9067        );
9068    }
9069
9070    #[test]
9071    fn closure_inside_async_fn_resets_in_async_so_await_is_e0086() {
9072        // Nova has no `async` closure syntax, so a closure is its own non-async
9073        // function even when written inside an `async fn`'s body — the same
9074        // discipline `loop_depth` uses for `break`/`continue` (`check_closure`
9075        // saves/resets/restores `fcx.in_async` around the closure body). Without
9076        // that reset this `.await` would wrongly inherit `g`'s `in_async = true`
9077        // and typecheck clean instead of reporting E0086.
9078        // Written `|n: Int|`, not `||`: the lexer greedily tokenizes `||` as
9079        // one `PipePipe` (logical-or) token rather than two `Pipe`s, so a
9080        // truly zero-parameter closure is not spellable — an unrelated,
9081        // pre-existing lexer fact (measured: `|| { .. }` fails to parse with
9082        // "expected expression ... found `||`"), not anything this task
9083        // changes. One unused parameter sidesteps it without adding anything
9084        // relevant to what this test checks.
9085        let r = check_src(
9086            "async fn f() -> Int { 1 }\n\
9087             async fn g() -> Int {\n\
9088                 let h = |n: Int| { f().await }\n\
9089                 0\n\
9090             }\n\
9091             fn main() {}",
9092        );
9093        let d = r
9094            .diagnostics
9095            .iter()
9096            .find(|d| d.code == "E0086")
9097            .unwrap_or_else(|| {
9098                panic!(
9099                    "expected E0086, got {:?}",
9100                    r.diagnostics.iter().map(|d| &d.code).collect::<Vec<_>>()
9101                )
9102            });
9103        assert!(
9104            d.message.contains("async"),
9105            "E0086 must explain that await requires an async fn; got {:?}",
9106            d.message
9107        );
9108    }
9109
9110    #[test]
9111    fn generic_identity_instantiates() {
9112        let r = check_src(
9113            "fn identity<T>(x: T) -> T { x }\n\
9114             fn main() { let n = identity(1) + 1\n let s = identity(\"s\")\n println(\"${n}${s}\") }",
9115        );
9116        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9117        let main = r
9118            .module
9119            .functions
9120            .iter()
9121            .find(|f| f.name == "main")
9122            .expect("main exists");
9123        // The two calls to identity must record Int and String type args.
9124        let mut type_args = Vec::new();
9125        collect_call_type_args(&main.body, &mut type_args);
9126        assert!(type_args.contains(&vec![Ty::Int]));
9127        assert!(type_args.contains(&vec![Ty::String]));
9128    }
9129
9130    fn collect_call_type_args(expr: &hir::Expr, out: &mut Vec<Vec<Ty>>) {
9131        if let hir::ExprKind::Call {
9132            type_args, args, ..
9133        } = &expr.kind
9134        {
9135            if !type_args.is_empty() {
9136                out.push(type_args.clone());
9137            }
9138            for a in args {
9139                collect_call_type_args(a, out);
9140            }
9141        }
9142        if let hir::ExprKind::Block { stmts, trailing } = &expr.kind {
9143            for s in stmts {
9144                collect_call_type_args(s, out);
9145            }
9146            if let Some(t) = trailing {
9147                collect_call_type_args(t, out);
9148            }
9149        }
9150        if let hir::ExprKind::Let { init, .. } = &expr.kind {
9151            collect_call_type_args(init, out);
9152        }
9153        if let hir::ExprKind::Binary { lhs, rhs, .. } = &expr.kind {
9154            collect_call_type_args(lhs, out);
9155            collect_call_type_args(rhs, out);
9156        }
9157        if let hir::ExprKind::StrConcat(parts) = &expr.kind {
9158            for p in parts {
9159                collect_call_type_args(p, out);
9160            }
9161        }
9162        if let hir::ExprKind::ToStr(inner) = &expr.kind {
9163            collect_call_type_args(inner, out);
9164        }
9165    }
9166
9167    #[test]
9168    fn non_exhaustive_match_reports_e0020() {
9169        let r = check_src(
9170            "type Shape = | Circle(Int) | Empty\n\
9171             fn f(s: Shape) -> Int { match s { Circle(r) => r, } }\n\
9172             fn main() { }",
9173        );
9174        assert!(error_codes(&r).contains(&"E0020"), "{:?}", r.diagnostics);
9175    }
9176
9177    #[test]
9178    fn exhaustive_match_ok() {
9179        let r = check_src(
9180            "type Shape = | Circle(Int) | Empty\n\
9181             fn f(s: Shape) -> Int { match s { Circle(r) => r, Empty => 0, } }\n\
9182             fn main() { }",
9183        );
9184        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9185    }
9186
9187    // === Maranget exhaustiveness / reachability ===
9188
9189    #[test]
9190    fn bool_match_true_and_false_is_exhaustive() {
9191        // Regression: `true | false` covers `Bool` with no wildcard needed.
9192        let r = check_src(
9193            "fn f(b: Bool) -> Int { match b { true => 1, false => 0 } }\n\
9194             fn main() { }",
9195        );
9196        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9197    }
9198
9199    #[test]
9200    fn bool_match_missing_false_reports_e0020() {
9201        let r = check_src("fn f(b: Bool) -> Int { match b { true => 1 } }\nfn main() { }");
9202        assert!(error_codes(&r).contains(&"E0020"), "{:?}", r.diagnostics);
9203        // The witness names the uncovered value.
9204        assert!(
9205            r.diagnostics.iter().any(|d| d.message.contains("false")),
9206            "expected a `false` witness: {:?}",
9207            r.diagnostics
9208        );
9209    }
9210
9211    #[test]
9212    fn int_match_without_catch_all_reports_e0020() {
9213        let r = check_src("fn f(n: Int) -> Int { match n { 0 => 1, 1 => 2 } }\nfn main() { }");
9214        assert!(error_codes(&r).contains(&"E0020"), "{:?}", r.diagnostics);
9215    }
9216
9217    #[test]
9218    fn int_match_with_catch_all_ok() {
9219        let r = check_src("fn f(n: Int) -> Int { match n { 0 => 1, _ => 2 } }\nfn main() { }");
9220        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9221    }
9222
9223    #[test]
9224    fn non_exhaustive_witness_names_missing_variant() {
9225        let r = check_src(
9226            "type Opt = | Some(Int) | None\n\
9227             fn f(o: Opt) -> Int { match o { Some(x) => x } }\n\
9228             fn main() { }",
9229        );
9230        assert!(error_codes(&r).contains(&"E0020"), "{:?}", r.diagnostics);
9231        assert!(
9232            r.diagnostics.iter().any(|d| d.message.contains("None")),
9233            "expected a `None` witness: {:?}",
9234            r.diagnostics
9235        );
9236    }
9237
9238    #[test]
9239    fn duplicate_variant_arm_is_unreachable_e0021() {
9240        // A repeated variant arm is now flagged even without a preceding
9241        // catch-all — usefulness detects it directly.
9242        let r = check_src(
9243            "type Opt = | Some(Int) | None\n\
9244             fn f(o: Opt) -> Int { match o { None => 0, Some(x) => x, None => 9 } }\n\
9245             fn main() { }",
9246        );
9247        assert!(
9248            r.diagnostics.iter().any(|d| d.code == "E0021"),
9249            "expected E0021 unreachable: {:?}",
9250            r.diagnostics
9251        );
9252    }
9253
9254    #[test]
9255    fn arm_after_catch_all_is_unreachable_e0021() {
9256        let r = check_src("fn f(n: Int) -> Int { match n { _ => 0, 1 => 2 } }\nfn main() { }");
9257        assert!(
9258            r.diagnostics.iter().any(|d| d.code == "E0021"),
9259            "expected E0021 unreachable: {:?}",
9260            r.diagnostics
9261        );
9262    }
9263
9264    #[test]
9265    fn empty_match_on_generic_param_reports_e0020() {
9266        // Regression (adversarial review): an empty match on a type parameter
9267        // was silently accepted (skipped as unanalyzable) and trapped at
9268        // runtime; it must be reported non-exhaustive.
9269        let r = check_src(
9270            "fn oops<T>(x: T) -> Int { match x { } }\n\
9271             fn main() { let r = oops(7)\n println(\"${r}\") }",
9272        );
9273        assert!(error_codes(&r).contains(&"E0020"), "{:?}", r.diagnostics);
9274    }
9275
9276    #[test]
9277    fn foreign_variant_in_ident_pattern_reports_e0001() {
9278        // Regression (adversarial review): a bare identifier naming a variant
9279        // of a *different* sum type must be rejected, not silently bound as a
9280        // catch-all (which masked uncovered cases).
9281        let r = check_src(
9282            "type A = | Foo | Bar\n\
9283             type B = | Baz\n\
9284             fn f(a: A) -> Int { match a { Baz => 1, Foo => 2, Bar => 3 } }\n\
9285             fn main() { }",
9286        );
9287        assert!(error_codes(&r).contains(&"E0001"), "{:?}", r.diagnostics);
9288    }
9289
9290    #[test]
9291    fn unknown_name_reports_e0001() {
9292        let r = check_src("fn main() { let x = nope() }");
9293        assert!(error_codes(&r).contains(&"E0001"), "{:?}", r.diagnostics);
9294    }
9295
9296    #[test]
9297    fn assign_to_immutable_reports_e0060() {
9298        let r = check_src("fn main() { let x = 1\n x = 2 }");
9299        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
9300    }
9301
9302    #[test]
9303    fn mutable_assign_ok() {
9304        let r = check_src("fn main() { let mut x = 1\n x = 2\n x += 3\n println(\"${x}\") }");
9305        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9306    }
9307
9308    #[test]
9309    fn fn_as_value_ok() {
9310        let r = check_src(
9311            "fn double(n: Int) -> Int { n * 2 }\n\
9312             fn apply_twice<T>(f: fn(T) -> T, x: T) -> T { f(f(x)) }\n\
9313             fn main() { println(\"${apply_twice(double, 5)}\") }",
9314        );
9315        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9316    }
9317
9318    #[test]
9319    fn wrong_arity_reports_e0016() {
9320        let r = check_src("fn f(a: Int) -> Int { a }\nfn main() { let x = f(1, 2) }");
9321        assert!(error_codes(&r).contains(&"E0016"), "{:?}", r.diagnostics);
9322    }
9323
9324    #[test]
9325    fn record_literal_and_field_access_ok() {
9326        let r = check_src(
9327            "record Point { x: Int, y: Int }\n\
9328             fn main() {\n\
9329                 let p = Point { x: 3, y: 4 }\n\
9330                 println(\"${p.x} ${p.y}\")\n\
9331             }",
9332        );
9333        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9334    }
9335
9336    #[test]
9337    fn record_field_type_mismatch_reports_e0010() {
9338        let r = check_src(
9339            "record Point { x: Int, y: Int }\n\
9340             fn main() { let p = Point { x: 3, y: \"no\" } }",
9341        );
9342        assert!(error_codes(&r).contains(&"E0010"), "{:?}", r.diagnostics);
9343    }
9344
9345    #[test]
9346    fn missing_field_reports_e0014() {
9347        let r = check_src(
9348            "record Point { x: Int, y: Int }\n\
9349             fn main() { let p = Point { x: 3 } }",
9350        );
9351        assert!(error_codes(&r).contains(&"E0014"), "{:?}", r.diagnostics);
9352    }
9353
9354    #[test]
9355    fn unknown_field_access_reports_e0014() {
9356        let r = check_src(
9357            "record Point { x: Int, y: Int }\n\
9358             fn main() { let p = Point { x: 3, y: 4 }\n let z = p.z }",
9359        );
9360        assert!(error_codes(&r).contains(&"E0014"), "{:?}", r.diagnostics);
9361        // Exact wording, not just the code: the read and write paths share
9362        // `no_field_message` precisely so they cannot independently drift,
9363        // but until this assertion existed only the write path's
9364        // `assignment_to_unknown_field_reports_e0014` pinned any wording at
9365        // all — reverting the read path to inline its own message would have
9366        // passed every test in this file.
9367        let d = r
9368            .diagnostics
9369            .iter()
9370            .find(|d| d.code == "E0014")
9371            .expect("an E0014 was reported");
9372        assert_eq!(d.message, "no field `z` on record `Point`");
9373    }
9374
9375    #[test]
9376    fn field_access_on_non_record_reports_e0014() {
9377        // The read-path mirror of `field_assignment_on_non_record_reports_e0014`:
9378        // a receiver with no fields at all gets `no_field_message`'s other
9379        // wording, not the "no field `x` on record `P`" phrasing that implies
9380        // a record exists. No read-path test exercised this case before.
9381        let r = check_src("fn main() { let n = 1\n let z = n.v }");
9382        let d = r
9383            .diagnostics
9384            .iter()
9385            .find(|d| d.code == "E0014")
9386            .expect("an E0014 was reported");
9387        assert_eq!(d.message, "cannot access field `v` on `Int`");
9388    }
9389
9390    #[test]
9391    fn generic_record_instantiates() {
9392        let r = check_src(
9393            "record Pair<A, B> { first: A, second: B }\n\
9394             fn main() {\n\
9395                 let p = Pair { first: 1, second: \"two\" }\n\
9396                 println(\"${p.first} ${p.second}\")\n\
9397             }",
9398        );
9399        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9400    }
9401
9402    #[test]
9403    fn record_spread_base_ok() {
9404        let r = check_src(
9405            "record Point { x: Int, y: Int }\n\
9406             fn main() {\n\
9407                 let p = Point { x: 1, y: 2 }\n\
9408                 let q = Point { x: 10, ..p }\n\
9409                 println(\"${q.x} ${q.y}\")\n\
9410             }",
9411        );
9412        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9413    }
9414
9415    #[test]
9416    fn inherent_method_ok() {
9417        let r = check_src(
9418            "record Point { x: Int, y: Int }\n\
9419             impl Point { fn sum(self) -> Int { self.x + self.y } }\n\
9420             fn main() { let p = Point { x: 1, y: 2 }\n println(\"${p.sum()}\") }",
9421        );
9422        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9423    }
9424
9425    #[test]
9426    fn trait_impl_and_default_method_ok() {
9427        let r = check_src(
9428            "record P { v: Int }\n\
9429             trait Show { fn name(self) -> String\n fn shout(self) -> String { self.name() } }\n\
9430             impl Show for P { fn name(self) -> String { \"p\" } }\n\
9431             fn main() { let p = P { v: 1 }\n println(p.shout()) }",
9432        );
9433        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9434    }
9435
9436    #[test]
9437    fn generic_bound_ok() {
9438        let r = check_src(
9439            "record P { v: Int }\n\
9440             trait Show { fn name(self) -> String }\n\
9441             impl Show for P { fn name(self) -> String { \"p\" } }\n\
9442             fn label<T: Show>(x: T) -> String { x.name() }\n\
9443             fn main() { println(label(P { v: 1 })) }",
9444        );
9445        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9446    }
9447
9448    #[test]
9449    fn no_method_reports_e0014() {
9450        let r = check_src(
9451            "record P { v: Int }\n\
9452             fn main() { let p = P { v: 1 }\n let s = p.missing() }",
9453        );
9454        assert!(error_codes(&r).contains(&"E0014"), "{:?}", r.diagnostics);
9455    }
9456
9457    #[test]
9458    fn impl_missing_required_method_reports_e0070() {
9459        let r = check_src(
9460            "record P { v: Int }\n\
9461             trait Show { fn name(self) -> String }\n\
9462             impl Show for P { }\n\
9463             fn main() { }",
9464        );
9465        assert!(error_codes(&r).contains(&"E0070"), "{:?}", r.diagnostics);
9466    }
9467
9468    #[test]
9469    fn method_not_in_trait_reports_e0071() {
9470        let r = check_src(
9471            "record P { v: Int }\n\
9472             trait Show { fn name(self) -> String }\n\
9473             impl Show for P { fn name(self) -> String { \"p\" }\n fn extra(self) -> Int { 0 } }\n\
9474             fn main() { }",
9475        );
9476        assert!(error_codes(&r).contains(&"E0071"), "{:?}", r.diagnostics);
9477    }
9478
9479    #[test]
9480    fn impl_wrong_param_type_reports_e0072() {
9481        let r = check_src(
9482            "record P { v: Int }\n\
9483             trait T { fn m(self, x: Int) -> String }\n\
9484             impl T for P { fn m(self, x: String) -> String { x } }\n\
9485             fn main() { }",
9486        );
9487        assert!(error_codes(&r).contains(&"E0072"), "{:?}", r.diagnostics);
9488    }
9489
9490    #[test]
9491    fn impl_wrong_return_type_reports_e0072() {
9492        let r = check_src(
9493            "record P { v: Int }\n\
9494             trait T { fn m(self) -> Int }\n\
9495             impl T for P { fn m(self) -> String { \"x\" } }\n\
9496             fn main() { }",
9497        );
9498        assert!(error_codes(&r).contains(&"E0072"), "{:?}", r.diagnostics);
9499    }
9500
9501    #[test]
9502    fn impl_wrong_arity_reports_e0072() {
9503        let r = check_src(
9504            "record P { v: Int }\n\
9505             trait T { fn m(self, x: Int) -> Int }\n\
9506             impl T for P { fn m(self) -> Int { 0 } }\n\
9507             fn main() { }",
9508        );
9509        assert!(error_codes(&r).contains(&"E0072"), "{:?}", r.diagnostics);
9510    }
9511
9512    #[test]
9513    fn for_loop_over_range_ok() {
9514        let r = check_src(
9515            "fn main() {\n\
9516                 let mut s = 0\n\
9517                 for i in 0..10 { s = s + i }\n\
9518                 for j in 1..=3 { s = s + j }\n\
9519                 println(\"${s}\")\n\
9520             }",
9521        );
9522        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9523    }
9524
9525    #[test]
9526    fn closure_no_capture_ok() {
9527        let r = check_src("fn main() { let inc = |n| n + 1\n println(\"${inc(41)}\") }");
9528        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9529    }
9530
9531    #[test]
9532    fn closure_capturing_local_ok() {
9533        let r = check_src(
9534            "fn main() {\n\
9535                 let base = 10\n\
9536                 let f = |n| n + base\n\
9537                 println(\"${f(5)}\")\n\
9538             }",
9539        );
9540        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9541        // A closure and a wrapper are lifted; at least one extra function.
9542        assert!(r.module.functions.len() >= 2);
9543    }
9544
9545    #[test]
9546    fn closure_to_higher_order_ok() {
9547        let r = check_src(
9548            "fn apply(f: fn(Int) -> Int, x: Int) -> Int { f(x) }\n\
9549             fn main() { let k = 3\n println(\"${apply(|n| n * k, 4)}\") }",
9550        );
9551        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9552    }
9553
9554    #[test]
9555    fn bare_fn_as_value_ok() {
9556        let r = check_src(
9557            "fn dbl(n: Int) -> Int { n * 2 }\n\
9558             fn apply(f: fn(Int) -> Int, x: Int) -> Int { f(x) }\n\
9559             fn main() { println(\"${apply(dbl, 5)}\") }",
9560        );
9561        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9562    }
9563
9564    #[test]
9565    fn closure_assigns_captured_variable_ok() {
9566        // Regression: an assignment-only capture must still be captured
9567        // (previously miscompiled / ICE'd because collect_captures ignored
9568        // the Assign target).
9569        let r = check_src(
9570            "fn main() {\n\
9571                 let mut acc = 100\n\
9572                 let keep = 9\n\
9573                 let f = |n: Int| { acc = n\n keep + n }\n\
9574                 println(\"${f(5)}\")\n\
9575             }",
9576        );
9577        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9578    }
9579
9580    #[test]
9581    fn closure_calls_captured_fn_value_ok() {
9582        // Regression: a captured fn-value used only as a call target must be
9583        // captured and its Callee::Local remapped.
9584        let r = check_src(
9585            "fn inc(x: Int) -> Int { x + 1 }\n\
9586             fn dbl(x: Int) -> Int { x * 2 }\n\
9587             fn main() {\n\
9588                 let f = inc\n let g = dbl\n\
9589                 let compose = |x: Int| f(g(x))\n\
9590                 println(\"${compose(5)}\")\n\
9591             }",
9592        );
9593        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9594    }
9595
9596    #[test]
9597    fn nested_closure_calling_local_ok() {
9598        // Regression: a nested closure bound to a local and then called
9599        // (Callee::Local) must be remapped when the outer closure is lifted.
9600        let r = check_src(
9601            "fn main() {\n\
9602                 let z = 1\n\
9603                 let outer = |a: Int| { let inner = |b: Int| a + b\n inner(100) }\n\
9604                 println(\"${outer(7)}\")\n\
9605             }",
9606        );
9607        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9608    }
9609
9610    #[test]
9611    fn assigning_for_loop_variable_reports_e0060() {
9612        let r = check_src("fn main() { for i in 0..5 { i = i + 1 } }");
9613        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
9614    }
9615
9616    #[test]
9617    fn for_loop_does_not_shadow_user_end() {
9618        // Hidden counter locals are unscoped, so a user `__end` is unaffected.
9619        let r = check_src(
9620            "fn main() {\n\
9621                 let __end = 100\n\
9622                 for i in 0..3 { println(\"${__end}\") }\n\
9623             }",
9624        );
9625        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9626    }
9627
9628    #[test]
9629    fn break_and_continue_in_loops_ok() {
9630        let r = check_src(
9631            "fn main() {\n\
9632                 let mut s = 0\n\
9633                 while true { if s > 3 { break }\n s = s + 1 }\n\
9634                 for i in 0..10 { if i % 2 == 1 { continue }\n s = s + i }\n\
9635                 println(\"${s}\")\n\
9636             }",
9637        );
9638        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9639    }
9640
9641    #[test]
9642    fn arrays_literal_index_len_ok() {
9643        let r = check_src(
9644            "fn main() {\n\
9645                 let xs = [10, 20, 30]\n\
9646                 let mut ys = [1, 2]\n\
9647                 ys[0] = xs[1]\n\
9648                 println(\"${xs.len()} ${ys[0]}\")\n\
9649             }",
9650        );
9651        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9652    }
9653
9654    #[test]
9655    fn array_param_and_return_ok() {
9656        let r = check_src(
9657            "fn first(xs: [Int]) -> Int { xs[0] }\n\
9658             fn make() -> [Int] { [7, 8, 9] }\n\
9659             fn main() { println(\"${first(make())}\") }",
9660        );
9661        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9662    }
9663
9664    #[test]
9665    fn heterogeneous_array_reports_e0010() {
9666        let r = check_src("fn main() { let xs = [1, \"two\"] }");
9667        assert!(error_codes(&r).contains(&"E0010"), "{:?}", r.diagnostics);
9668    }
9669
9670    #[test]
9671    fn non_int_index_reports_e0010() {
9672        let r = check_src("fn main() { let xs = [1, 2]\n let y = xs[\"a\"] }");
9673        assert!(error_codes(&r).contains(&"E0010"), "{:?}", r.diagnostics);
9674    }
9675
9676    #[test]
9677    fn repeat_array_typechecks_and_has_array_type() {
9678        let r =
9679            check_src("fn main() { let n = 3\n let a = [7; n]\n println(\"${a.len()} ${a[0]}\") }");
9680        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9681        // An empty diagnostic list alone is a weak assertion: `Ty::Error`
9682        // unifies with anything, so a broken arm could report nothing and
9683        // still have inferred a useless type. Pin what was actually built.
9684        let repeat = exprs_in(&r.module, "main")
9685            .into_iter()
9686            .find(|e| matches!(e.kind, hir::ExprKind::ArrayRepeat { .. }))
9687            .expect("the checker built an `ArrayRepeat`");
9688        assert!(
9689            matches!(&repeat.ty, Ty::Array(elem) if **elem == Ty::Int),
9690            "`[7; n]` should have type `[Int]`, got {:?}",
9691            repeat.ty
9692        );
9693    }
9694
9695    #[test]
9696    fn repeat_array_elem_type_follows_the_init_expression() {
9697        // The element type comes from `init`, so a heap filler yields
9698        // `[String]` — and it must satisfy a `[String]` annotation.
9699        let r = check_src(
9700            "fn main() { let n = 2\n let a: [String] = [\"hi\"; n]\n println(\"${a[0]}\") }",
9701        );
9702        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9703        let repeat = exprs_in(&r.module, "main")
9704            .into_iter()
9705            .find(|e| matches!(e.kind, hir::ExprKind::ArrayRepeat { .. }))
9706            .expect("the checker built an `ArrayRepeat`");
9707        assert!(
9708            matches!(&repeat.ty, Ty::Array(elem) if **elem == Ty::String),
9709            "`[\"hi\"; n]` should have type `[String]`, got {:?}",
9710            repeat.ty
9711        );
9712    }
9713
9714    #[test]
9715    fn repeat_array_non_int_length_reports_e0010() {
9716        let r = check_src("fn main() { let a = [7; \"three\"]\n println(\"${a[0]}\") }");
9717        assert!(error_codes(&r).contains(&"E0010"), "{:?}", r.diagnostics);
9718    }
9719
9720    #[test]
9721    fn indexing_non_array_reports_e0014() {
9722        let r = check_src("fn main() { let x = 5\n let y = x[0] }");
9723        assert!(error_codes(&r).contains(&"E0014"), "{:?}", r.diagnostics);
9724    }
9725
9726    #[test]
9727    fn index_set_immutable_reports_e0060() {
9728        let r = check_src("fn main() { let xs = [1, 2]\n xs[0] = 9 }");
9729        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
9730    }
9731
9732    #[test]
9733    fn index_set_through_immutable_field_reports_e0060() {
9734        // Regression: mutating an element reached through a field of an
9735        // immutable binding must still be rejected — the base is `Field`, not
9736        // a bare `Path`, so the single-segment check used to miss it.
9737        let r = check_src(
9738            "record Box { data: [Int] }\n\
9739             fn main() { let b = Box { data: [1, 2, 3] }\n b.data[0] = 99 }",
9740        );
9741        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
9742    }
9743
9744    #[test]
9745    fn index_set_through_immutable_nested_array_reports_e0060() {
9746        // Regression: `grid[0][1] = v` roots at an immutable local through a
9747        // nested `Index`, which the single-segment check used to bypass.
9748        let r = check_src("fn main() { let grid = [[1, 2], [3, 4]]\n grid[0][1] = 99 }");
9749        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
9750    }
9751
9752    #[test]
9753    fn index_set_on_call_result_reports_e0060() {
9754        // A temporary (call result) has no assignable root — mutating an
9755        // element of it is meaningless and must be rejected.
9756        let r = check_src(
9757            "fn make() -> [Int] { [1, 2, 3] }\n\
9758             fn main() { make()[0] = 99 }",
9759        );
9760        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
9761    }
9762
9763    #[test]
9764    fn index_set_through_mutable_field_ok() {
9765        // The mirror of the immutable-field case: a `mut` base makes the
9766        // reachable array storage assignable.
9767        let r = check_src(
9768            "record Box { data: [Int] }\n\
9769             fn main() { let mut b = Box { data: [1, 2, 3] }\n b.data[0] = 99 }",
9770        );
9771        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9772    }
9773
9774    #[test]
9775    fn index_set_nested_mutable_ok() {
9776        let r = check_src("fn main() { let mut grid = [[1, 2], [3, 4]]\n grid[0][1] = 99 }");
9777        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9778    }
9779
9780    #[test]
9781    fn index_set_on_non_array_still_checks_the_rhs() {
9782        // The mirror of `field_assignment_with_unknown_field_still_checks_the_rhs`
9783        // on `check_index_set`'s "not an array" branch: it used to return
9784        // before checking the RHS, dropping an independent mistake there
9785        // alongside (correctly) rejecting the index-assign itself.
9786        let r = check_src("fn main() { let mut n = 1\n n[0] = undefined_fn() }");
9787        let codes = error_codes(&r);
9788        assert!(
9789            codes.contains(&"E0014"),
9790            "expected the not-an-array error: {:?}",
9791            r.diagnostics
9792        );
9793        assert!(
9794            codes.contains(&"E0001"),
9795            "expected `undefined_fn`'s own error to still surface: {:?}",
9796            r.diagnostics
9797        );
9798    }
9799
9800    #[test]
9801    fn field_assignment_typechecks() {
9802        let r = check_src(
9803            "record P { v: Int }\n\
9804             fn main() { let mut p = P { v: 1 }\n p.v = 7\n println(\"${p.v}\") }",
9805        );
9806        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9807        // Absence of diagnostics alone is not enough: `error_expr` is silent and
9808        // `Ty::Error` unifies with anything, so a store that never got built
9809        // would still look clean here. Pin the node, its field index, and its
9810        // unit type.
9811        let sets = field_sets_in(&r.module, "main");
9812        assert_eq!(sets.len(), 1, "exactly one FieldSet in `main`");
9813        let hir::ExprKind::FieldSet { index, value, .. } = &sets[0].kind else {
9814            unreachable!("filtered to FieldSet")
9815        };
9816        assert_eq!(*index, 0, "`v` is field 0, so the store offset is 8*0");
9817        assert_eq!(value.ty, Ty::Int, "the stored value keeps its `Int` type");
9818        assert_eq!(sets[0].ty, Ty::Unit, "a field store is unit-typed");
9819    }
9820
9821    #[test]
9822    fn field_assignment_picks_the_declared_field_index() {
9823        // The store offset is `8 * index`, so writing the *second* field must
9824        // resolve to index 1. A helper that always returned 0 would still pass
9825        // a single-field test.
9826        let r = check_src(
9827            "record P { a: Int, b: String }\n\
9828             fn main() { let mut p = P { a: 1, b: \"x\" }\n p.b = \"y\"\n println(\"${p.b}\") }",
9829        );
9830        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9831        let sets = field_sets_in(&r.module, "main");
9832        assert_eq!(sets.len(), 1, "exactly one FieldSet in `main`");
9833        let hir::ExprKind::FieldSet { index, value, .. } = &sets[0].kind else {
9834            unreachable!("filtered to FieldSet")
9835        };
9836        assert_eq!(*index, 1, "`b` is the second field");
9837        assert_eq!(value.ty, Ty::String);
9838    }
9839
9840    #[test]
9841    fn field_assignment_on_generic_record_substitutes_the_field_type() {
9842        // The shared lookup substitutes the record's type arguments into the
9843        // field's declared type. This is the path `Vec<T>`/`Map<K, V>` depend
9844        // on, so assert the *resolved* type rather than just the absence of
9845        // errors — `Ty::Error` unifies with anything and would hide a failure.
9846        let r = check_src(
9847            "record Box<T> { value: T }\n\
9848             fn main() { let mut b = Box { value: 1 }\n b.value = 7\n println(\"${b.value}\") }",
9849        );
9850        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
9851        let sets = field_sets_in(&r.module, "main");
9852        assert_eq!(sets.len(), 1, "exactly one FieldSet in `main`");
9853        let hir::ExprKind::FieldSet { index, value, .. } = &sets[0].kind else {
9854            unreachable!("filtered to FieldSet")
9855        };
9856        assert_eq!(*index, 0);
9857        assert_eq!(
9858            value.ty,
9859            Ty::Int,
9860            "`T` was instantiated to `Int`, so the stored value is an `Int`"
9861        );
9862    }
9863
9864    #[test]
9865    fn field_assignment_on_generic_record_mismatch_reports_e0010() {
9866        // The mirror of the above: substitution must also *reject* the wrong
9867        // type, rather than leaving the field type as an unconstrained `T`
9868        // that unifies with anything.
9869        let r = check_src(
9870            "record Box<T> { value: T }\n\
9871             fn main() { let mut b = Box { value: 1 }\n b.value = \"s\"\n println(\"${b.value}\") }",
9872        );
9873        assert!(error_codes(&r).contains(&"E0010"), "{:?}", r.diagnostics);
9874    }
9875
9876    #[test]
9877    fn field_assignment_to_immutable_reports_e0060() {
9878        let r = check_src(
9879            "record P { v: Int }\n\
9880             fn main() { let p = P { v: 1 }\n p.v = 7\n println(\"${p.v}\") }",
9881        );
9882        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
9883    }
9884
9885    #[test]
9886    fn field_assignment_type_mismatch_reports_e0010() {
9887        let r = check_src(
9888            "record P { v: Int }\n\
9889             fn main() { let mut p = P { v: 1 }\n p.v = \"s\"\n println(\"${p.v}\") }",
9890        );
9891        assert!(error_codes(&r).contains(&"E0010"), "{:?}", r.diagnostics);
9892    }
9893
9894    #[test]
9895    fn assignment_to_unknown_field_reports_e0014() {
9896        let r = check_src(
9897            "record P { v: Int }\n\
9898             fn main() { let mut p = P { v: 1 }\n p.nope = 7\n println(\"${p.v}\") }",
9899        );
9900        // Same wording as the read path's `unknown_field_access_reports_e0014`
9901        // shape: named as a record, not "on type `P`" — the two used to say
9902        // different things for the identical mistake.
9903        let d = r
9904            .diagnostics
9905            .iter()
9906            .find(|d| d.code == "E0014")
9907            .expect("an E0014 was reported");
9908        assert_eq!(d.message, "no field `nope` on record `P`");
9909    }
9910
9911    #[test]
9912    fn field_assignment_on_non_record_reports_e0014() {
9913        // A mutable binding is not enough — the receiver has to *have* fields.
9914        let r = check_src("fn main() { let mut n = 1\n n.v = 2\n println(\"${n}\") }");
9915        // The write path used to collapse this into the same "no field `v` on
9916        // type `Int`" wording as the wrong-field-name case; it must now match
9917        // the read path's separate not-a-record message.
9918        let d = r
9919            .diagnostics
9920            .iter()
9921            .find(|d| d.code == "E0014")
9922            .expect("an E0014 was reported");
9923        assert_eq!(d.message, "cannot access field `v` on `Int`");
9924    }
9925
9926    #[test]
9927    fn field_assignment_with_unknown_field_still_checks_the_rhs() {
9928        // `check_field_set`'s "no field" branch used to return before checking
9929        // the RHS at all, so an independent mistake there (`undefined_fn` is
9930        // not defined) was silently dropped — only the wrong-field-name E0014
9931        // was ever reported. The array path (`a[i] = undefined_fn()`) does not
9932        // drop it, and the field path must not either.
9933        let r = check_src(
9934            "record P { v: Int }\n\
9935             fn main() { let mut p = P { v: 1 }\n p.nope = undefined_fn() }",
9936        );
9937        let codes = error_codes(&r);
9938        assert!(
9939            codes.contains(&"E0014"),
9940            "expected the unknown-field error: {:?}",
9941            r.diagnostics
9942        );
9943        assert!(
9944            codes.contains(&"E0001"),
9945            "expected `undefined_fn`'s own error to still surface: {:?}",
9946            r.diagnostics
9947        );
9948    }
9949
9950    #[test]
9951    fn field_assignment_through_broken_receiver_does_not_cascade() {
9952        // `p.nope` is already an error on the read path; the write path must not
9953        // add a second E0014 for the unknown field of an error-typed receiver.
9954        // This is the `Ty::Error` early return: the *target* diagnostic is
9955        // deliberately not repeated, since that would be the same mistake
9956        // reported twice. But the RHS is unrelated to the receiver and must
9957        // still be checked, per the invariant on `check_index_set` — so the
9958        // RHS below is `undefined_fn()`, not a literal: a literal produces no
9959        // diagnostic either way and cannot tell "checked but clean" apart
9960        // from "never checked".
9961        let r = check_src(
9962            "record P { v: Int }\n\
9963             fn main() { let mut p = P { v: 1 }\n p.nope.deeper = undefined_fn() }",
9964        );
9965        let codes = error_codes(&r);
9966        let n = codes.iter().filter(|c| **c == "E0014").count();
9967        assert_eq!(n, 1, "expected exactly one E0014, got {:?}", r.diagnostics);
9968        assert!(
9969            codes.contains(&"E0001"),
9970            "expected `undefined_fn`'s own error to still surface: {:?}",
9971            r.diagnostics
9972        );
9973    }
9974
9975    #[test]
9976    fn field_assignment_on_temporary_reports_e0060() {
9977        // A call result has no assignable root, so mutating its field is
9978        // meaningless — rejected at the root exactly as `make()[0] = v` is.
9979        let r = check_src(
9980            "record P { v: Int }\n\
9981             fn make() -> P { P { v: 1 } }\n\
9982             fn main() { make().v = 7 }",
9983        );
9984        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
9985    }
9986
9987    #[test]
9988    fn field_assignment_through_immutable_nested_record_reports_e0060() {
9989        // `outer.inner.v = 7` roots at an immutable local through two `Field`
9990        // projections; `place_root` walks the whole chain.
9991        let r = check_src(
9992            "record Inner { v: Int }\n\
9993             record Outer { inner: Inner }\n\
9994             fn main() { let o = Outer { inner: Inner { v: 1 } }\n o.inner.v = 7 }",
9995        );
9996        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
9997    }
9998
9999    #[test]
10000    fn field_assignment_through_mutable_nested_record_ok() {
10001        // The mirror of the above: a `mut` root makes the reachable record
10002        // storage assignable, however deep the chain.
10003        let r = check_src(
10004            "record Inner { v: Int }\n\
10005             record Outer { inner: Inner }\n\
10006             fn main() {\n\
10007                 let mut o = Outer { inner: Inner { v: 1 } }\n\
10008                 o.inner.v = 7\n\
10009                 println(\"${o.inner.v}\")\n\
10010             }",
10011        );
10012        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10013        assert_eq!(field_sets_in(&r.module, "main").len(), 1);
10014    }
10015
10016    #[test]
10017    fn compound_assignment_to_field_reports_e0900() {
10018        // `+=` on a field would have to read-modify-write; not supported yet,
10019        // mirroring compound assignment to an array element.
10020        let r = check_src(
10021            "record P { v: Int }\n\
10022             fn main() { let mut p = P { v: 1 }\n p.v += 1\n println(\"${p.v}\") }",
10023        );
10024        assert!(error_codes(&r).contains(&"E0900"), "{:?}", r.diagnostics);
10025    }
10026
10027    #[test]
10028    fn compound_assignment_to_field_still_checks_the_rhs() {
10029        // This branch returns before doing anything else at all — no
10030        // mutability check, no receiver check — so it used to drop an
10031        // independent RHS mistake along with (correctly) rejecting the
10032        // compound form itself.
10033        let r = check_src(
10034            "record P { v: Int }\n\
10035             fn main() { let mut p = P { v: 1 }\n p.v += undefined_fn() }",
10036        );
10037        let codes = error_codes(&r);
10038        assert!(codes.contains(&"E0900"), "{:?}", r.diagnostics);
10039        assert!(
10040            codes.contains(&"E0001"),
10041            "expected `undefined_fn`'s own error to still surface: {:?}",
10042            r.diagnostics
10043        );
10044    }
10045
10046    #[test]
10047    fn compound_assignment_to_array_element_still_checks_the_rhs() {
10048        // The array-element analogue of
10049        // `compound_assignment_to_field_still_checks_the_rhs`.
10050        let r = check_src("fn main() { let mut xs = [1, 2]\n xs[0] += undefined_fn() }");
10051        let codes = error_codes(&r);
10052        assert!(codes.contains(&"E0900"), "{:?}", r.diagnostics);
10053        assert!(
10054            codes.contains(&"E0001"),
10055            "expected `undefined_fn`'s own error to still surface: {:?}",
10056            r.diagnostics
10057        );
10058    }
10059
10060    #[test]
10061    fn mut_self_method_on_immutable_receiver_reports_e0060() {
10062        let r = check_src(
10063            "record P { v: Int }\n\
10064             impl P { fn bump(mut self) { self.v = self.v + 1 } }\n\
10065             fn main() { let p = P { v: 1 }\n p.bump()\n println(\"${p.v}\") }",
10066        );
10067        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
10068    }
10069
10070    #[test]
10071    fn mut_self_method_on_mutable_receiver_typechecks() {
10072        let r = check_src(
10073            "record P { v: Int }\n\
10074             impl P { fn bump(mut self) { self.v = self.v + 1 } }\n\
10075             fn main() { let mut p = P { v: 1 }\n p.bump()\n println(\"${p.v}\") }",
10076        );
10077        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10078    }
10079
10080    #[test]
10081    fn plain_self_method_on_immutable_receiver_still_typechecks() {
10082        // Guard: only `mut self` demands a mutable receiver.
10083        let r = check_src(
10084            "record P { v: Int }\n\
10085             impl P { fn get(self) -> Int { self.v } }\n\
10086             fn main() { let p = P { v: 1 }\n println(\"${p.get()}\") }",
10087        );
10088        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10089    }
10090
10091    #[test]
10092    fn mut_self_is_a_mutable_root_inside_the_method_body() {
10093        // The receiver rule is only useful if `mut self` also *permits* the
10094        // mutation it advertises: `place_root` must classify `self` as
10095        // `Mutable` inside the body. Asserting the compiled `FieldSet` rather
10096        // than the absence of diagnostics, because an empty diagnostic list
10097        // would also hold if the body had silently not been compiled at all.
10098        let r = check_src(
10099            "record P { v: Int }\n\
10100             impl P { fn bump(mut self) { self.v = self.v + 1 } }\n\
10101             fn main() { let mut p = P { v: 1 }\n p.bump()\n println(\"${p.v}\") }",
10102        );
10103        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10104        assert_eq!(
10105            // An inherent method's `hir::Function` carries the resolver's
10106            // mangled `Type.method` name.
10107            field_sets_in(&r.module, "P.bump").len(),
10108            1,
10109            "`mut self` body compiled its store"
10110        );
10111    }
10112
10113    #[test]
10114    fn plain_self_method_assigning_a_field_reports_e0060() {
10115        // The mirror of the above, and what makes the `mut` in `mut self` load-
10116        // bearing rather than decorative: a plain `self` receiver is an
10117        // immutable binding like any other, so its fields are read-only.
10118        let r = check_src(
10119            "record P { v: Int }\n\
10120             impl P { fn bump(self) { self.v = self.v + 1 } }\n\
10121             fn main() { let p = P { v: 1 }\n p.bump()\n println(\"${p.v}\") }",
10122        );
10123        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
10124    }
10125
10126    #[test]
10127    fn mut_self_method_called_on_a_field_of_mut_self_typechecks() {
10128        // The shape `std/collections`' `Set` uses: `self.map.insert(…)` from a
10129        // `mut self` method. `place_root` walks the field chain to `self`, so a
10130        // `mut self` root makes the nested receiver mutable too.
10131        let r = check_src(
10132            "record Inner { v: Int }\n\
10133             record Outer { inner: Inner }\n\
10134             impl Inner { fn bump(mut self) { self.v = self.v + 1 } }\n\
10135             impl Outer { fn bump_inner(mut self) { self.inner.bump() } }\n\
10136             fn main() {\n\
10137                 let mut o = Outer { inner: Inner { v: 1 } }\n\
10138                 o.bump_inner()\n\
10139                 println(\"${o.inner.v}\")\n\
10140             }",
10141        );
10142        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10143    }
10144
10145    #[test]
10146    fn mut_self_method_called_on_field_of_immutable_root_reports_e0060() {
10147        // The mirror: the receiver is a field projection, so the whole chain
10148        // must be walked to the root binding — a single-segment check would
10149        // accept `o.inner.bump()` on an immutable `o`.
10150        let r = check_src(
10151            "record Inner { v: Int }\n\
10152             record Outer { inner: Inner }\n\
10153             impl Inner { fn bump(mut self) { self.v = self.v + 1 } }\n\
10154             fn main() {\n\
10155                 let o = Outer { inner: Inner { v: 1 } }\n\
10156                 o.inner.bump()\n\
10157             }",
10158        );
10159        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
10160    }
10161
10162    #[test]
10163    fn mut_self_method_on_temporary_reports_e0060() {
10164        // A call result has no assignable root, so mutating it is meaningless —
10165        // rejected exactly as `make().v = 7` is.
10166        let r = check_src(
10167            "record P { v: Int }\n\
10168             impl P { fn bump(mut self) { self.v = self.v + 1 } }\n\
10169             fn make() -> P { P { v: 1 } }\n\
10170             fn main() { make().bump() }",
10171        );
10172        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
10173    }
10174
10175    #[test]
10176    fn mut_self_method_called_from_a_plain_self_method_reports_e0060() {
10177        // The mistake a std author actually makes: an outer method forgets its
10178        // own `mut` and then delegates to a mutator. `self` is a parameter like
10179        // any other, so a plain `self` receiver is an immutable root and the
10180        // rule catches it at the inner call — not later, at the store.
10181        let r = check_src(
10182            "record Inner { v: Int }\n\
10183             record Outer { inner: Inner }\n\
10184             impl Inner { fn bump(mut self) { self.v = self.v + 1 } }\n\
10185             impl Outer { fn bump_inner(self) { self.inner.bump() } }\n\
10186             fn main() {\n\
10187                 let mut o = Outer { inner: Inner { v: 1 } }\n\
10188                 o.bump_inner()\n\
10189             }",
10190        );
10191        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
10192    }
10193
10194    #[test]
10195    fn misplaced_mut_self_receiver_is_still_a_mutator() {
10196        // `mut_self` must use the same `.any(…)` scan as `has_self`, because
10197        // `method_sig_parts` strips a `self` at *any* position and the parser
10198        // accepts a misplaced receiver. A `params[0]`-shaped predicate would
10199        // classify this method as a non-mutator while the signature machinery
10200        // still treated it as having a receiver — the two disagreeing about the
10201        // same parameter. Everything else about this shape is pre-existing
10202        // garbage (`self` ends up typed `Int`), so only the E0060 is asserted.
10203        let r = check_src(
10204            "record P { v: Int }\n\
10205             impl P { fn odd(x: Int, mut self) -> Int { x } }\n\
10206             fn main() { let p = P { v: 1 }\n println(\"${p.odd(1)}\") }",
10207        );
10208        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
10209    }
10210
10211    #[test]
10212    fn mut_self_method_on_immutable_receiver_suggests_let_mut() {
10213        // The note is the actionable half of the diagnostic; pin it so a
10214        // refactor cannot quietly drop it (`check_field_set` carries the same).
10215        // The message names the callee by its impl-qualified `Def` name, the
10216        // spelling `arity_errors_name_the_callee_uniformly` already pins for the
10217        // other inherent-method diagnostics.
10218        let r = check_src(
10219            "record P { v: Int }\n\
10220             impl P { fn bump(mut self) { self.v = self.v + 1 } }\n\
10221             fn main() { let p = P { v: 1 }\n p.bump()\n println(\"${p.v}\") }",
10222        );
10223        let d = r
10224            .diagnostics
10225            .iter()
10226            .find(|d| d.code == "E0060")
10227            .expect("an E0060 was reported");
10228        assert_eq!(
10229            d.message,
10230            "`P.bump` mutates its receiver, but `p` is immutable"
10231        );
10232        // Spec 3.4b §3.3: the fix says what the note did.
10233        assert!(d.notes.is_empty(), "{:?}", d.notes);
10234        assert_eq!(
10235            d.fixes.iter().map(|f| f.title.as_str()).collect::<Vec<_>>(),
10236            ["make `p` mutable"]
10237        );
10238    }
10239
10240    #[test]
10241    fn immutable_self_root_is_advised_to_use_mut_self_not_let_mut_self() {
10242        // All three mutation forms route through `require_mutable_place`, and
10243        // when the immutable root is a method's own receiver the uniform
10244        // ``declare it as `let mut {name}` `` note would read `let mut self` —
10245        // which Nova cannot parse, so the advice would be unfollowable. A
10246        // receiver's mutability is declared in the signature instead.
10247        //
10248        // `Set::insert` delegating to `Map::insert` is exactly the receiver
10249        // shape here, so this is the note a std author is most likely to be
10250        // handed.
10251        let r = check_src(
10252            "record Inner { v: Int }\n\
10253             impl Inner { fn bump(mut self) { self.v = self.v + 1 } }\n\
10254             record Outer { inner: Inner, data: [Int] }\n\
10255             impl Outer {\n\
10256                 fn call_mutator(self) { self.inner.bump() }\n\
10257                 fn set_elem(self) { self.data[0] = 1 }\n\
10258                 fn set_field(self) { self.inner.v = 1 }\n\
10259             }\n\
10260             fn main() { }",
10261        );
10262        let notes: Vec<&str> = r
10263            .diagnostics
10264            .iter()
10265            .filter(|d| d.code == "E0060")
10266            .flat_map(|d| d.notes.iter().map(|n| n.as_str()))
10267            .collect();
10268        // One per mutation form: the `mut self` call, the element store, and the
10269        // field store.
10270        assert_eq!(notes.len(), 3, "{:?}", r.diagnostics);
10271        for n in &notes {
10272            assert_eq!(
10273                *n, "declare the enclosing method's receiver as `mut self` to allow mutation",
10274                "{notes:?}"
10275            );
10276        }
10277        // The primary messages keep their per-form wording; only the note
10278        // changes for a `self` root.
10279        let messages: Vec<&str> = r
10280            .diagnostics
10281            .iter()
10282            .filter(|d| d.code == "E0060")
10283            .map(|d| d.message.as_str())
10284            .collect();
10285        assert!(
10286            messages.contains(&"`Inner.bump` mutates its receiver, but `self` is immutable"),
10287            "{messages:?}"
10288        );
10289        assert!(
10290            messages.contains(&"cannot assign to an element of immutable `self`"),
10291            "{messages:?}"
10292        );
10293        assert!(
10294            messages.contains(&"cannot assign to a field of immutable `self`"),
10295            "{messages:?}"
10296        );
10297    }
10298
10299    /// **The enforced rule: `mut self` demands a mutable receiver whichever way
10300    /// the method was dispatched.** A trait method declaring `mut self`, called
10301    /// through an immutable binding, is `E0060` — the same answer the *inherent*
10302    /// path gives for the same operation
10303    /// (`mut_self_method_on_immutable_receiver_reports_e0060` a few tests up).
10304    /// One rule, one answer, which was the whole point: ADR 0005 §1 rejected
10305    /// Java/Python receiver semantics precisely because "a language where
10306    /// `v.push(x)` is allowed but `v.items[0] = x` is not has no rule a reader
10307    /// can hold in their head — it has two", and until this landed *trait*
10308    /// dispatch was a third answer to the same question.
10309    ///
10310    /// **This assertion was deliberately inverted, and that is the intended
10311    /// outcome, not a test bent to fit.** It previously read
10312    /// `assert!(r.diagnostics.is_empty())` under the name
10313    /// `…_is_not_enforced_on_immutable_receiver_known_gap`, pinning the
10314    /// permissive behaviour on purpose so that closing the gap could not happen
10315    /// silently. What authorised the flip: ADR 0005 §1's Migration path, whose
10316    /// three steps (the `hir::TraitMethod` flag, the conformance comparison, the
10317    /// call-site check) are now all executed and recorded there as done, and the
10318    /// associated-types plan and design doc §6, which made closing it a hard gate
10319    /// before the first `mut self` trait method — `Iterator::next` — could be
10320    /// declared at all.
10321    ///
10322    /// It keeps its narrow reach: it compiles one program and says nothing about
10323    /// any other. The routes into the same check that this shape does *not*
10324    /// exercise — a generic bound, a supertrait bound, a default body, string
10325    /// interpolation — are pinned individually by the `mut_self_trait_method_*`
10326    /// tests below, because one accepted program could never have proved the
10327    /// check was unbypassable.
10328    #[test]
10329    fn trait_method_mut_self_is_enforced_on_immutable_receiver() {
10330        let r = check_src(
10331            "trait Bump { fn bump(mut self) }\n\
10332             record P { v: Int }\n\
10333             impl Bump for P { fn bump(mut self) { self.v = self.v + 1 } }\n\
10334             fn main() { let p = P { v: 1 }\n p.bump()\n println(\"${p.v}\") }",
10335        );
10336        assert!(
10337            r.diagnostics.iter().any(|d| d.code == "E0060"),
10338            "the mutable-receiver rule is enforced on trait dispatch too \
10339             (ADR 0005 §1, Migration path): `p` is immutable, so `p.bump()` on a \
10340             `mut self` trait method must be E0060: {:?}",
10341            r.diagnostics
10342        );
10343    }
10344
10345    #[test]
10346    fn mut_self_trait_method_on_an_immutable_receiver_reports_e0060() {
10347        let r = check_src(
10348            "trait Bump { fn bump(mut self) }\n\
10349             record C { n: Int }\n\
10350             impl Bump for C { fn bump(mut self) { self.n = self.n + 1 } }\n\
10351             fn main() { let c = C { n: 1 }\n c.bump() }",
10352        );
10353        assert!(
10354            r.diagnostics.iter().any(|d| d.code == "E0060"),
10355            "expected E0060 on a mut-self trait method through an immutable binding: {:?}",
10356            r.diagnostics
10357        );
10358    }
10359
10360    #[test]
10361    fn mut_self_trait_method_on_a_mutable_receiver_is_accepted() {
10362        // The over-rejection guard for the common case: enforcing the rule on
10363        // trait dispatch must not make a legitimately `mut` binding unusable.
10364        let r = check_src(
10365            "trait Bump { fn bump(mut self) }\n\
10366             record C { n: Int }\n\
10367             impl Bump for C { fn bump(mut self) { self.n = self.n + 1 } }\n\
10368             fn main() { let mut c = C { n: 1 }\n c.bump()\n println(\"${c.n}\") }",
10369        );
10370        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10371    }
10372
10373    #[test]
10374    fn a_plain_self_trait_method_is_unaffected_by_the_mut_self_trait_rule() {
10375        // The no-op half, and the mutation-killer for an unconditional check:
10376        // only `mut self` demands anything of the caller, so a reader still
10377        // works on an immutable binding. Mirrors
10378        // `plain_self_method_on_immutable_receiver_still_typechecks` on the
10379        // inherent path.
10380        let r = check_src(
10381            "trait Get { fn get(self) -> Int }\n\
10382             record C { n: Int }\n\
10383             impl Get for C { fn get(self) -> Int { self.n } }\n\
10384             fn main() { let c = C { n: 1 }\n println(\"${c.get()}\") }",
10385        );
10386        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10387    }
10388
10389    #[test]
10390    fn mut_self_trait_method_conformance_disagreement_is_e0072() {
10391        // Both directions: the trait says mut, the impl does not, and vice
10392        // versa. Either way the receiver's mutability requirement would be
10393        // decided by whichever table the caller happened to consult — the call
10394        // site reads the trait's flag, `check_fn_body` reads the impl's `mut`.
10395        //
10396        // The two messages are pinned, not merely the code: a single `E0072`
10397        // assertion holds just as well if the want/got pair is swapped, and a
10398        // swapped pair tells the author to change the wrong side.
10399        for (t, i, got, want) in [
10400            (
10401                "mut self",
10402                "self",
10403                "a plain `self` receiver",
10404                "a `mut self` receiver",
10405            ),
10406            (
10407                "self",
10408                "mut self",
10409                "a `mut self` receiver",
10410                "a plain `self` receiver",
10411            ),
10412        ] {
10413            let src = format!(
10414                "trait Bump {{ fn bump({t}) }}\n\
10415                 record C {{ n: Int }}\n\
10416                 impl Bump for C {{ fn bump({i}) {{ }} }}\n\
10417                 fn main() {{ }}"
10418            );
10419            let r = check_src(&src);
10420            let msgs: Vec<&str> = r
10421                .diagnostics
10422                .iter()
10423                .filter(|d| d.code == "E0072")
10424                .map(|d| d.message.as_str())
10425                .collect();
10426            assert!(
10427                msgs.contains(
10428                    &format!("method `bump` has {got} but trait `Bump` declares {want}").as_str()
10429                ),
10430                "trait `{t}` vs impl `{i}` must be a conformance error naming both sides: {:?}",
10431                r.diagnostics
10432            );
10433        }
10434    }
10435
10436    #[test]
10437    fn mut_self_trait_method_diagnostic_names_the_callee_and_advises_let_mut() {
10438        // The trait-dispatch spelling of the callee is the trait's declared
10439        // method name, unqualified — `arity_errors_name_the_callee_uniformly`
10440        // pins the same choice for `E0016` at this dispatch site, and for the
10441        // same reason: `Self` may still be a generic parameter, so there is no
10442        // impl to qualify with. The actionable note is shared with the other
10443        // three mutation forms via `require_mutable_place`.
10444        let r = check_src(
10445            "trait Bump { fn bump(mut self) }\n\
10446             record C { n: Int }\n\
10447             impl Bump for C { fn bump(mut self) { self.n = self.n + 1 } }\n\
10448             fn main() { let c = C { n: 1 }\n c.bump() }",
10449        );
10450        let d = r
10451            .diagnostics
10452            .iter()
10453            .find(|d| d.code == "E0060")
10454            .expect("an E0060 was reported");
10455        assert_eq!(
10456            d.message,
10457            "`bump` mutates its receiver, but `c` is immutable"
10458        );
10459        // Spec 3.4b §3.3: the fix says what the note did.
10460        assert!(d.notes.is_empty(), "{:?}", d.notes);
10461        assert_eq!(
10462            d.fixes.iter().map(|f| f.title.as_str()).collect::<Vec<_>>(),
10463            ["make `c` mutable"]
10464        );
10465    }
10466
10467    #[test]
10468    fn mut_self_trait_method_on_a_temporary_reports_e0060() {
10469        // ADR 0005 §1 Consequences: a temporary receiver is rejected, because
10470        // the mutation could not be observed by anyone. The trait path must
10471        // agree with the inherent one
10472        // (`mut_self_method_on_temporary_reports_e0060`) — one rule, one answer,
10473        // which is the whole point of closing this gap.
10474        let r = check_src(
10475            "trait Bump { fn bump(mut self) }\n\
10476             record C { n: Int }\n\
10477             impl Bump for C { fn bump(mut self) { self.n = self.n + 1 } }\n\
10478             fn make() -> C { C { n: 1 } }\n\
10479             fn main() { make().bump() }",
10480        );
10481        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
10482    }
10483
10484    #[test]
10485    fn mut_self_trait_method_on_a_field_of_a_mutable_root_is_accepted() {
10486        // `place_root` walks the whole projection chain on the trait path too,
10487        // so a nested receiver under a `mut` root is fine — the shape
10488        // `std/collections` uses, but reached through trait dispatch.
10489        let r = check_src(
10490            "trait Bump { fn bump(mut self) }\n\
10491             record Inner { n: Int }\n\
10492             record Outer { inner: Inner }\n\
10493             impl Bump for Inner { fn bump(mut self) { self.n = self.n + 1 } }\n\
10494             fn main() {\n\
10495                 let mut o = Outer { inner: Inner { n: 1 } }\n\
10496                 o.inner.bump()\n\
10497                 println(\"${o.inner.n}\")\n\
10498             }",
10499        );
10500        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10501    }
10502
10503    #[test]
10504    fn mut_self_trait_method_through_a_generic_bound_reports_e0060() {
10505        // Route 2 of five. A generic receiver is the case ADR 0005 named as the
10506        // reason the gap existed: there is no single impl to read `mut self`
10507        // off, so the flag has to live on `hir::TraitMethod`. `x` is an
10508        // ordinary immutable parameter, so the rule must fire here exactly as
10509        // it does for a `let`.
10510        let r = check_src(
10511            "trait Bump { fn bump(mut self) }\n\
10512             record C { n: Int }\n\
10513             impl Bump for C { fn bump(mut self) { self.n = self.n + 1 } }\n\
10514             fn poke<T: Bump>(x: T) { x.bump() }\n\
10515             fn main() { let c = C { n: 1 }\n poke(c) }",
10516        );
10517        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
10518    }
10519
10520    #[test]
10521    fn mut_self_trait_method_through_a_mut_generic_parameter_is_accepted() {
10522        // The over-rejection mirror of the above, and a prerequisite for the
10523        // generic-projection gate (`fn first<I: Iterator>(…)` calls
10524        // `it.next()`): `mut` on a *parameter* parses and makes the parameter a
10525        // mutable root, so the generic route stays writable.
10526        let r = check_src(
10527            "trait Bump { fn bump(mut self) }\n\
10528             record C { n: Int }\n\
10529             impl Bump for C { fn bump(mut self) { self.n = self.n + 1 } }\n\
10530             fn poke<T: Bump>(mut x: T) { x.bump() }\n\
10531             fn main() { let c = C { n: 1 }\n poke(c) }",
10532        );
10533        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10534    }
10535
10536    #[test]
10537    fn mut_self_trait_method_through_a_supertrait_bound_reports_e0060() {
10538        // Route 3 of five. `collect_traits` puts a trait's *expanded*
10539        // supertraits in the `Self` bound slot, so `x.bump()` under `T: Ext`
10540        // resolves to `Bump`'s method through a different trait's bound list.
10541        // The check must key on the trait the method was resolved *in*, not on
10542        // the bound that was written.
10543        let r = check_src(
10544            "trait Bump { fn bump(mut self) }\n\
10545             trait Ext: Bump { }\n\
10546             record C { n: Int }\n\
10547             impl Bump for C { fn bump(mut self) { self.n = self.n + 1 } }\n\
10548             impl Ext for C { }\n\
10549             fn poke<T: Ext>(x: T) { x.bump() }\n\
10550             fn main() { }",
10551        );
10552        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
10553    }
10554
10555    #[test]
10556    fn mut_self_trait_method_from_a_plain_self_default_body_reports_e0060() {
10557        // Route 4 of five, and the one a trait author hits: a default body
10558        // forgets its own `mut` and delegates to a mutator. Inside a default
10559        // body `self` is typed `Param(0)`, so the delegated call resolves
10560        // through `Self`'s own bound — a second trait-dispatch route into the
10561        // same check.
10562        let r = check_src(
10563            "trait Bump {\n\
10564                 fn bump(mut self)\n\
10565                 fn twice(self) { self.bump() }\n\
10566             }\n\
10567             record C { n: Int }\n\
10568             impl Bump for C { fn bump(mut self) { self.n = self.n + 1 } }\n\
10569             fn main() { }",
10570        );
10571        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
10572    }
10573
10574    #[test]
10575    fn mut_self_trait_method_from_a_mut_self_default_body_is_accepted() {
10576        // The over-rejection mirror: a `mut self` default body is a mutable
10577        // root, so delegating to another `mut self` method is exactly what the
10578        // declaration promises. Without this, closing the gap would make every
10579        // mutating default method unwritable.
10580        let r = check_src(
10581            "trait Bump {\n\
10582                 fn bump(mut self)\n\
10583                 fn twice(mut self) { self.bump() }\n\
10584             }\n\
10585             record C { n: Int }\n\
10586             impl Bump for C { fn bump(mut self) { self.n = self.n + 1 } }\n\
10587             fn main() { }",
10588        );
10589        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10590    }
10591
10592    #[test]
10593    fn mut_self_trait_method_reached_by_string_interpolation_reports_e0060() {
10594        // Route 5 of five, and the one that is invisible from
10595        // `check_method_call`: `"${c}"` bridges to a user `fmt(self) -> String`
10596        // through `try_display`, which calls `emit_trait_call` directly. A check
10597        // installed only in `check_method_call`'s `MethodRes::Trait` arm leaves
10598        // this route accepting a mutation through an immutable binding — so the
10599        // check lives at `emit_trait_call`, the choke point every receiver
10600        // route passes through.
10601        let r = check_src(
10602            "trait Show { fn fmt(mut self) -> String }\n\
10603             record C { n: Int }\n\
10604             impl Show for C { fn fmt(mut self) -> String { \"c\" } }\n\
10605             fn main() { let c = C { n: 1 }\n println(\"${c}\") }",
10606        );
10607        assert!(error_codes(&r).contains(&"E0060"), "{:?}", r.diagnostics);
10608    }
10609
10610    #[test]
10611    fn assignment_to_a_non_place_target_reports_e0900() {
10612        // `arr[i] = v` and `rec.f = v` are both handled before this fallback, so
10613        // its message must name record fields among the supported forms — this
10614        // is the wording that went stale when field assignment landed.
10615        let r = check_src("fn f() -> Int { 1 }\nfn main() { f() = 2 }");
10616        let d = r
10617            .diagnostics
10618            .iter()
10619            .find(|d| d.code == "E0900")
10620            .expect("an E0900 was reported");
10621        assert_eq!(
10622            d.message,
10623            "assignments to anything but a local variable, array element, or record field \
10624             are not supported yet"
10625        );
10626    }
10627
10628    #[test]
10629    fn constants_ok() {
10630        let r = check_src(
10631            "const MAX: Int = 100\n\
10632             const DOUBLE: Int = MAX * 2\n\
10633             const NAME: String = \"nova\"\n\
10634             fn main() { println(\"${MAX} ${DOUBLE} ${NAME}\") }",
10635        );
10636        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10637    }
10638
10639    #[test]
10640    fn constant_type_mismatch_reports_e0010() {
10641        let r = check_src("const X: Int = \"not an int\"\nfn main() { println(\"${X}\") }");
10642        assert!(error_codes(&r).contains(&"E0010"), "{:?}", r.diagnostics);
10643    }
10644
10645    #[test]
10646    fn direct_constant_cycle_reports_e0081() {
10647        let r = check_src("const A: Int = A + 1\nfn main() { println(\"${A}\") }");
10648        assert!(error_codes(&r).contains(&"E0081"), "{:?}", r.diagnostics);
10649    }
10650
10651    #[test]
10652    fn indirect_constant_cycle_reports_e0081() {
10653        let r = check_src("const A: Int = B\nconst B: Int = A\nfn main() { }");
10654        assert!(error_codes(&r).contains(&"E0081"), "{:?}", r.diagnostics);
10655    }
10656
10657    #[test]
10658    fn function_typed_constant_called_directly_ok() {
10659        // Regression: a fn-typed const must be callable with call syntax
10660        // `CONST(args)`, not only via a local (was a spurious E0900).
10661        let r = check_src(
10662            "const DOUBLE: fn(Int) -> Int = |n| n * 2\n\
10663             fn triple(n: Int) -> Int { n * 3 }\n\
10664             const TRIPLE: fn(Int) -> Int = triple\n\
10665             fn main() { println(\"${DOUBLE(3)} ${TRIPLE(4)}\") }",
10666        );
10667        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10668    }
10669
10670    #[test]
10671    fn non_cyclic_constant_chain_ok() {
10672        let r = check_src(
10673            "const A: Int = 1\nconst B: Int = A + 1\nconst C: Int = B + 1\n\
10674             fn main() { println(\"${C}\") }",
10675        );
10676        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10677    }
10678
10679    #[test]
10680    fn if_with_diverging_then_takes_else_type() {
10681        // Regression: `if c { return } else { v }` must type as `v`, not
10682        // `Never` — otherwise a diverging branch in a condition ICE'd codegen.
10683        let r = check_src(
10684            "fn f() -> Int {\n\
10685                 let x = if true { return 0 } else { 5 }\n\
10686                 x + 1\n\
10687             }\n\
10688             fn main() { println(\"${f()}\") }",
10689        );
10690        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10691    }
10692
10693    #[test]
10694    fn break_in_while_condition_ok() {
10695        // Regression: break in a while's own condition is accepted and does
10696        // not crash later stages.
10697        let r = check_src(
10698            "fn main() {\n\
10699                 let mut n = 0\n\
10700                 while (if n > 5 { break } else { true }) { n = n + 1 }\n\
10701                 println(\"${n}\")\n\
10702             }",
10703        );
10704        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10705    }
10706
10707    #[test]
10708    fn return_in_while_condition_ok() {
10709        // Regression: a `Never`-typed while condition (return) must not ICE.
10710        let r = check_src(
10711            "fn f() -> Int { while (if true { return 1 } else { false }) { }\n 0 }\n\
10712             fn main() { println(\"${f()}\") }",
10713        );
10714        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
10715    }
10716
10717    #[test]
10718    fn break_outside_loop_reports_e0080() {
10719        let r = check_src("fn main() { break }");
10720        assert!(error_codes(&r).contains(&"E0080"), "{:?}", r.diagnostics);
10721    }
10722
10723    #[test]
10724    fn continue_outside_loop_reports_e0080() {
10725        let r = check_src("fn main() { continue }");
10726        assert!(error_codes(&r).contains(&"E0080"), "{:?}", r.diagnostics);
10727    }
10728
10729    #[test]
10730    fn break_in_closure_does_not_see_outer_loop() {
10731        // A `break` inside a closure body cannot target the enclosing loop.
10732        let r = check_src(
10733            "fn apply(f: fn(Int) -> Int, x: Int) -> Int { f(x) }\n\
10734             fn main() { while true { let c = |n: Int| { if n > 0 { break }\n n }\n let _ = apply(c, 1) } }",
10735        );
10736        assert!(error_codes(&r).contains(&"E0080"), "{:?}", r.diagnostics);
10737    }
10738
10739    #[test]
10740    fn for_over_non_range_reports_e0900() {
10741        let r = check_src("fn main() { for x in 5 { } }");
10742        assert!(error_codes(&r).contains(&"E0900"), "{:?}", r.diagnostics);
10743    }
10744
10745    #[test]
10746    fn range_outside_for_reports_e0900() {
10747        let r = check_src("fn main() { let r = 0..5 }");
10748        assert!(error_codes(&r).contains(&"E0900"), "{:?}", r.diagnostics);
10749    }
10750
10751    // The iterable is `make_once()` and not the record literal `Once { … }`
10752    // the plan wrote, in this test and the two below: the parser parses the
10753    // `for` iterable in a `no_struct_literal` context (as Rust does), so
10754    // `for x in Once { v: 7 } { … }` takes the record literal's brace as the
10755    // loop body and cannot parse at all. Parenthesising does not help either —
10756    // Nova's paren-grouping inherits the flag rather than resetting it. A call
10757    // and a `let`-bound local are also the two receiver shapes real code uses
10758    // (`v.iter()` is a call), and they are the two `place_root` classifications
10759    // — `NotAPlace` and `ImmutableLocal` — that a desugar handing the *source*
10760    // iterable to the mutable-receiver check would wrongly reject.
10761    #[test]
10762    fn a_for_loop_iterates_an_iterator() {
10763        let r = check_src(
10764            "trait It { type Item\n fn next(mut self) -> Option<Self::Item> }\n\
10765             record Once { v: Int, done: Bool }\n\
10766             impl It for Once { type Item = Int\n\
10767              fn next(mut self) -> Option<Int> { if self.done { None } else { self.done = true\n Some(self.v) } } }\n\
10768             fn make_once() -> Once { Once { v: 7, done: false } }\n\
10769             fn main() { for x in make_once() { println(\"${x}\") } }",
10770        );
10771        assert_eq!(error_codes(&r), Vec::<&str>::new(), "{:?}", r.diagnostics);
10772    }
10773
10774    /// Not in the plan; added because the plan's own mutation (c) — dropping the
10775    /// `None => break` arm — survives every one of its other assertions.
10776    /// Exhaustiveness analysis runs over *source* `match` expressions, not over
10777    /// one the checker synthesized, so a desugar that can never leave the loop
10778    /// type-checks completely clean and only fails at runtime: measured, the
10779    /// program prints its real elements and then dies on the switch's default
10780    /// `Terminator::Trap` (exit 132, `Illegal instruction`) — it does **not**
10781    /// hang. Swapping the `Some` and `None` variant indices likewise survives
10782    /// every diagnostic-based assertion. Both are structural faults, so this
10783    /// reads the structure the checker built rather than the diagnostics it
10784    /// didn't emit.
10785    #[test]
10786    fn a_for_loop_over_an_iterator_breaks_on_none() {
10787        let r = check_src(
10788            "trait It { type Item\n fn next(mut self) -> Option<Self::Item> }\n\
10789             record Once { v: Int, done: Bool }\n\
10790             impl It for Once { type Item = Int\n\
10791              fn next(mut self) -> Option<Int> { if self.done { None } else { self.done = true\n Some(self.v) } } }\n\
10792             fn make_once() -> Once { Once { v: 7, done: false } }\n\
10793             fn main() { for x in make_once() { println(\"${x}\") } }",
10794        );
10795        assert_eq!(error_codes(&r), Vec::<&str>::new(), "{:?}", r.diagnostics);
10796        let arms = exprs_in(&r.module, "main")
10797            .into_iter()
10798            .find_map(|e| match &e.kind {
10799                hir::ExprKind::Match { arms, .. } => Some(arms),
10800                _ => None,
10801            })
10802            .expect("the desugar built a `match` on `next()`");
10803        assert_eq!(arms.len(), 2, "{arms:?}");
10804        // The variant a pattern names, by *name* and binder count. Reading the
10805        // name and not just the binder count matters: `None` and a `Some` written
10806        // with no binders both have zero binders, so a binder-count assertion
10807        // alone lets the two indices be swapped — measured, that mutation
10808        // survives it.
10809        let names = |p: &hir::Pattern| match p {
10810            hir::Pattern::Variant {
10811                sum,
10812                variant,
10813                binders,
10814            } => {
10815                let s = r.module.sum(*sum).expect("the scrutinee's sum");
10816                (s.variants[*variant as usize].name.clone(), binders.len())
10817            }
10818            other => panic!("a variant pattern, not {other:?}"),
10819        };
10820        let (exit, elem): (Vec<&hir::Arm>, Vec<&hir::Arm>) = arms
10821            .iter()
10822            .partition(|a| matches!(a.body.kind, hir::ExprKind::Break));
10823        assert_eq!(exit.len(), 1, "exactly one arm leaves the loop: {arms:?}");
10824        assert_eq!(names(&exit[0].pattern), ("None".to_string(), 0));
10825        assert_eq!(names(&elem[0].pattern), ("Some".to_string(), 1));
10826    }
10827
10828    #[test]
10829    fn a_for_loop_over_a_range_still_works() {
10830        // This task edits the function the range loop lives in, so the range
10831        // path needs its own assertion here rather than relying on the older
10832        // range tests being run.
10833        let r = check_src("fn main() { for i in 0..3 { println(\"${i}\") } }");
10834        assert_eq!(error_codes(&r), Vec::<&str>::new(), "{:?}", r.diagnostics);
10835    }
10836
10837    #[test]
10838    fn a_for_loops_variable_binds_at_the_projected_item_type() {
10839        // `x` must be the normalized `Self::Item` (here `Bool`), not the
10840        // projection and not an inference variable. Bool rather than Int
10841        // deliberately: `mir_ty` collapses Int and Char to the same machine
10842        // type, so a wrong item type among them is invisible downstream.
10843        let r = check_src(
10844            "trait It { type Item\n fn next(mut self) -> Option<Self::Item> }\n\
10845             record OnceB { v: Bool, done: Bool }\n\
10846             impl It for OnceB { type Item = Bool\n\
10847              fn next(mut self) -> Option<Bool> { if self.done { None } else { self.done = true\n Some(self.v) } } }\n\
10848             fn main() { let it = OnceB { v: true, done: false }\n\
10849              for x in it { let y: Int = x\n let _ = y } }",
10850        );
10851        let d = r
10852            .diagnostics
10853            .iter()
10854            .find(|d| d.code == "E0010")
10855            .expect("E0010: the loop variable is Bool, not Int");
10856        assert!(d.message.contains("Bool"), "{}", d.message);
10857    }
10858
10859    #[test]
10860    fn a_for_loops_variable_is_immutable() {
10861        // Same rule the range loop already enforces. Without it the desugar
10862        // could hand out a mutable binding by accident.
10863        let r = check_src(
10864            "trait It { type Item\n fn next(mut self) -> Option<Self::Item> }\n\
10865             record Once { v: Int, done: Bool }\n\
10866             impl It for Once { type Item = Int\n\
10867              fn next(mut self) -> Option<Int> { if self.done { None } else { self.done = true\n Some(self.v) } } }\n\
10868             fn main() { let it = Once { v: 7, done: false }\n\
10869              for x in it { x = 1 } }",
10870        );
10871        assert!(
10872            r.diagnostics.iter().any(|d| d.code == "E0060"),
10873            "{:?}",
10874            r.diagnostics
10875        );
10876    }
10877
10878    #[test]
10879    fn a_for_loop_over_a_non_iterator_names_both_accepted_forms() {
10880        // The existing message says "anything but an integer range", which
10881        // becomes false the moment this task lands. `for x in v` is the mistake
10882        // people will actually make, so the text must mention `.iter()`.
10883        let r = check_src("fn main() { for x in 3 { println(\"${x}\") } }");
10884        let d = r
10885            .diagnostics
10886            .iter()
10887            .find(|d| d.code == "E0900")
10888            .expect("E0900 for a non-range non-iterator");
10889        assert!(
10890            d.message.contains("iter()"),
10891            "points at the fix: {}",
10892            d.message
10893        );
10894    }
10895
10896    /// `check_for_iterator` pushes a scope holding its hidden `__it` local so
10897    /// that `emit_trait_call`'s `place_root` can resolve the receiver as a
10898    /// *path*, then pops it before the body is checked. That pop is the whole
10899    /// hygiene mechanism, and it had **no coverage**: with the single
10900    /// `fcx.scopes.pop()` line deleted, this test is the *only* failure in the
10901    /// whole workspace — measured, not inferred — and this compiles and prints
10902    /// `stole 2`:
10903    ///
10904    /// ```nova
10905    /// for x in v.iter() { match __it.next() { Some(y) => println("stole ${y}"), None => … } }
10906    /// ```
10907    ///
10908    /// Worth pinning specifically because the *range* desugar's version of the
10909    /// same property is structurally safer and yet is the one with a test
10910    /// (`for_loop_does_not_shadow_user_end`): its hidden locals are made with
10911    /// `new_local_unscoped` and never enter a scope at all, so there is nothing
10912    /// to leak. The iterator desugar cannot do that — `place_root` resolves an
10913    /// AST name against the scopes — so it inserts and then relies on a matching
10914    /// pop, which is exactly the shape that one deleted line breaks.
10915    ///
10916    /// Both directions are asserted, because they fail differently. `__it`
10917    /// unreachable from the body is `E0001`; a user's own `__it` staying visible
10918    /// and *unshadowed* is the same leak seen from the other side — under the
10919    /// mutation the body's `__it` resolves to the hidden `VecIter`, so the
10920    /// second half reports `E0013` (no `Display`) rather than compiling clean.
10921    #[test]
10922    fn a_for_loops_hidden_iterator_local_is_not_reachable_from_the_body() {
10923        let decls = "trait It { type Item\n fn next(mut self) -> Option<Self::Item> }\n\
10924                     record Once { v: Int, done: Bool }\n\
10925                     impl It for Once { type Item = Int\n\
10926                      fn next(mut self) -> Option<Int> { if self.done { None } else { self.done = true\n Some(self.v) } } }\n\
10927                     fn make_once() -> Once { Once { v: 7, done: false } }\n";
10928        let stolen = check_src(
10929            &(decls.to_string()
10930                + "fn main() { for x in make_once() {\n\
10931                    match __it.next() { Some(y) => println(\"stole ${y}\"), None => println(\"none\") } } }"),
10932        );
10933        let d = stolen
10934            .diagnostics
10935            .iter()
10936            .find(|d| d.code == "E0001")
10937            .expect("E0001: `__it` is not a name the body can see");
10938        assert!(d.message.contains("__it"), "names it: {}", d.message);
10939
10940        // And it does not shadow a user's own binding of that name either: the
10941        // scope the desugar pushes is gone before the body is checked, so
10942        // `__it` here is still the `Int`.
10943        let shadowed = check_src(
10944            &(decls.to_string()
10945                + "fn main() { let __it = 99\n\
10946                    for x in make_once() { println(\"${x} ${__it}\") }\n\
10947                    println(\"${__it}\") }"),
10948        );
10949        assert_eq!(
10950            error_codes(&shadowed),
10951            Vec::<&str>::new(),
10952            "{:?}",
10953            shadowed.diagnostics
10954        );
10955    }
10956
10957    /// `iterator_next`'s `if sum.variants.len() != 2` guard, which nothing
10958    /// pinned. It is reachable from ordinary source: `Some`/`None` are not
10959    /// reserved, so a user can declare a three-variant sum that still has an
10960    /// `Option`-shaped pair inside it and return that from `next`. Measured —
10961    /// `type Tri = | Some(Int) | None | Third` compiles and `Third` is
10962    /// constructible, so the shape below is writable, not hypothetical.
10963    ///
10964    /// The guard is load-bearing, not defensive. Without it the desugar reads
10965    /// `Some`/`None` off by name and builds a **two**-arm match over a
10966    /// three-variant sum, and exhaustiveness never runs on a synthesized match
10967    /// (see `a_for_loop_over_an_iterator_breaks_on_none`'s doc comment, which
10968    /// measured exactly that for a different mutation): a `Third` would reach
10969    /// the switch's default `Terminator::Trap` at runtime with no diagnostic
10970    /// anywhere. Measured, with the guard deleted: this test is the *only*
10971    /// failure in the whole workspace, and the program below then passes
10972    /// `nova check` with **zero** diagnostics and dies at run time with
10973    /// `Illegal instruction` (exit 132).
10974    ///
10975    /// So this asserts the rejection, and does it by code *and* by message,
10976    /// since `E0900` is this file's code for every unsupported form.
10977    #[test]
10978    fn a_next_returning_a_three_variant_sum_is_not_an_iterator() {
10979        let r = check_src(
10980            "type Tri = | Some(Int) | None | Third\n\
10981             record R { n: Int }\n\
10982             trait It3 { fn next(mut self) -> Tri }\n\
10983             impl It3 for R { fn next(mut self) -> Tri { Third } }\n\
10984             fn main() { let r = R { n: 0 }\n for x in r { println(\"${x}\") } }",
10985        );
10986        let d = r
10987            .diagnostics
10988            .iter()
10989            .find(|d| d.code == "E0900")
10990            .expect("E0900: a three-variant return is not `Option`-shaped");
10991        assert!(
10992            d.message.contains("Iterator"),
10993            "the not-an-iterator message, not some other E0900: {}",
10994            d.message
10995        );
10996    }
10997
10998    #[test]
10999    fn conforming_impl_with_args_ok() {
11000        let r = check_src(
11001            "record P { v: Int }\n\
11002             trait T { fn add(self, x: Int) -> Int }\n\
11003             impl T for P { fn add(self, x: Int) -> Int { self.v + x } }\n\
11004             fn main() { let p = P { v: 1 }\n println(\"${p.add(2)}\") }",
11005        );
11006        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11007    }
11008
11009    // === Generic impls ===
11010
11011    #[test]
11012    fn generic_inherent_impl_ok() {
11013        // A method on `impl<T> Box<T>` returning `T`, used at two element
11014        // types, type-checks.
11015        let r = check_src(
11016            "record Box<T> { value: T }\n\
11017             impl<T> Box<T> { fn get(self) -> T { self.value } }\n\
11018             fn main() {\n\
11019                 let a = Box { value: 1 }\n\
11020                 let b = Box { value: \"s\" }\n\
11021                 println(\"${a.get()} ${b.get()}\")\n\
11022             }",
11023        );
11024        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11025    }
11026
11027    #[test]
11028    fn generic_inherent_impl_return_mismatch_reports_e0010() {
11029        // `get` claims to return `T` but returns an `Int`, so calling it on a
11030        // `Box<String>` and using the result as a String must fail.
11031        let r = check_src(
11032            "record Box<T> { value: T }\n\
11033             impl<T> Box<T> { fn get(self) -> T { 0 } }\n\
11034             fn main() { let b = Box { value: \"s\" }\n let x: String = b.get() }",
11035        );
11036        assert!(error_codes(&r).contains(&"E0010"), "{:?}", r.diagnostics);
11037    }
11038
11039    #[test]
11040    fn generic_trait_impl_ok() {
11041        let r = check_src(
11042            "record Box<T> { value: T }\n\
11043             trait Tag { fn tag(self) -> String }\n\
11044             impl<T> Tag for Box<T> { fn tag(self) -> String { \"b\" } }\n\
11045             fn main() { let b = Box { value: 1 }\n println(b.tag()) }",
11046        );
11047        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11048    }
11049
11050    #[test]
11051    fn conditional_generic_impl_ok() {
11052        // `Wrap<T>: Display` requires `T: Display`; `Int: Display` holds.
11053        let r = check_src(
11054            "trait Display { fn fmt(self) -> String }\n\
11055             record Wrap<T> { inner: T }\n\
11056             impl Display for Int { fn fmt(self) -> String { \"i\" } }\n\
11057             impl<T: Display> Display for Wrap<T> {\n\
11058                 fn fmt(self) -> String { self.inner.fmt() }\n\
11059             }\n\
11060             fn main() { let w = Wrap { inner: 1 }\n println(w.fmt()) }",
11061        );
11062        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11063    }
11064
11065    // Note: an *unsatisfied* conditional-impl bound (`Wrap<Bool>: Display`
11066    // when `Bool` is not `Display`) is reported at monomorphization (E0013),
11067    // not by `check` alone — see the `nova-mir` lowering tests. All trait
11068    // bounds in Nova are verified during monomorphization.
11069
11070    #[test]
11071    fn generic_impl_body_uses_undeclared_capability_reports_e0014() {
11072        // Without a `T: Display` bound the impl body cannot call `fmt` on `T`.
11073        let r = check_src(
11074            "trait Display { fn fmt(self) -> String }\n\
11075             record Wrap<T> { inner: T }\n\
11076             impl<T> Display for Wrap<T> {\n\
11077                 fn fmt(self) -> String { self.inner.fmt() }\n\
11078             }\n\
11079             fn main() { let w = Wrap { inner: 1 }\n println(w.fmt()) }",
11080        );
11081        assert!(error_codes(&r).contains(&"E0014"), "{:?}", r.diagnostics);
11082    }
11083
11084    #[test]
11085    fn generic_impl_wrong_self_param_reports_e0072() {
11086        // The trait says the method returns `Self` (`Box<T>`), but the impl
11087        // declares it returns the bare parameter `T`.
11088        let r = check_src(
11089            "record Box<T> { value: T }\n\
11090             trait Dup { fn dup(self) -> Self }\n\
11091             impl<T> Dup for Box<T> { fn dup(self) -> T { self.value } }\n\
11092             fn main() { let b = Box { value: 1 }\n let c = b.dup() }",
11093        );
11094        assert!(error_codes(&r).contains(&"E0072"), "{:?}", r.diagnostics);
11095    }
11096
11097    #[test]
11098    fn repeated_param_inherent_impl_matches_only_uniform_receiver() {
11099        // `impl<T> Pair<T, T>` must apply to `Pair<Int, Int>` but not to
11100        // `Pair<Int, String>` — sharing the `Pair` head is not enough.
11101        let ok = check_src(
11102            "record Pair<A, B> { first: A, second: B }\n\
11103             impl<T> Pair<T, T> { fn both(self) -> T { self.first } }\n\
11104             fn main() { let p = Pair { first: 1, second: 2 }\n println(\"${p.both()}\") }",
11105        );
11106        assert!(ok.diagnostics.is_empty(), "{:?}", ok.diagnostics);
11107
11108        let bad = check_src(
11109            "record Pair<A, B> { first: A, second: B }\n\
11110             impl<T> Pair<T, T> { fn both(self) -> T { self.first } }\n\
11111             fn main() { let p = Pair { first: 1, second: \"x\" }\n println(\"${p.both()}\") }",
11112        );
11113        assert!(
11114            error_codes(&bad).contains(&"E0014"),
11115            "{:?}",
11116            bad.diagnostics
11117        );
11118    }
11119
11120    #[test]
11121    fn where_clause_on_trait_impl_is_accepted() {
11122        // A `where` clause on a trait impl is an out-of-line bound: this is the
11123        // conditional impl `impl<T: Tag> Tag for Box<T>` and must type-check.
11124        let r = check_src(
11125            "record Box<T> { value: T }\n\
11126             trait Tag { fn tag(self) -> String }\n\
11127             impl<T> Tag for Box<T> where T: Tag { fn tag(self) -> String { \"b\" } }\n\
11128             fn main() { }",
11129        );
11130        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11131    }
11132
11133    #[test]
11134    fn where_clause_on_function_enforces_bound() {
11135        // `where T: Show` is accepted here (Int: Show) and equivalent to the
11136        // inline bound `<T: Show>`.
11137        let r = check_src(
11138            "trait Show { fn show(self) -> String }\n\
11139             impl Show for Int { fn show(self) -> String { \"i\" } }\n\
11140             fn label<T>(x: T) -> String where T: Show { x.show() }\n\
11141             fn main() { println(label(1)) }",
11142        );
11143        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11144    }
11145
11146    #[test]
11147    fn where_clause_on_concrete_type_reports_e0900() {
11148        // A `where` clause may only constrain a type parameter.
11149        let r = check_src(
11150            "trait Show { fn show(self) -> String }\n\
11151             fn f<T>(x: T) -> Int where Int: Show { 0 }\n\
11152             fn main() { }",
11153        );
11154        assert!(error_codes(&r).contains(&"E0900"), "{:?}", r.diagnostics);
11155    }
11156
11157    #[test]
11158    fn where_clause_on_trait_method_reports_e0900() {
11159        let r = check_src(
11160            "trait Foo { fn f(self) -> Int where Self: Foo }\n\
11161             fn main() { }",
11162        );
11163        assert!(error_codes(&r).contains(&"E0900"), "{:?}", r.diagnostics);
11164    }
11165
11166    #[test]
11167    fn record_type_param_bound_no_longer_reports_e0900() {
11168        // Pre-Task-1 (iterator-finishing plan), this was rejected outright:
11169        // `record Keyed<K: Hash2, V>` parsed, but nothing honoured the bound —
11170        // `hir::RecordType` has no `bounds` field and monomorphization only
11171        // discharges *function* bounds — so it used to compile and run with
11172        // `NoHash` (which does not implement `Hash2`), a bound that meant
11173        // nothing. That silent acceptance was worse than an error, so it was
11174        // rejected, exactly as `trait B where Self: A` is.
11175        //
11176        // Since Task 1, the bound is a resolution scope rather than a
11177        // constraint (see the comment in `collect_records`): it exists so a
11178        // field type may name a projection on the bounded parameter, and is
11179        // deliberately not enforced at construction. So this now compiles
11180        // clean, `NoHash`'s missing `Hash2` impl notwithstanding.
11181        // `a_record_bound_is_not_enforced_at_construction` (below, in this
11182        // module) pins the same decision with a projection-shaped bound; this
11183        // test keeps the original two-parameter, real-trait-method shape as a
11184        // second, differently shaped guard on the same decision.
11185        let r = check_src(
11186            "trait Hash2 { fn h(self) -> Int }\n\
11187             record Keyed<K: Hash2, V> { k: K, v: V }\n\
11188             record NoHash { n: Int }\n\
11189             fn main() { let x = Keyed { k: NoHash { n: 1 }, v: 2 }\n println(\"${x.v}\") }",
11190        );
11191        assert_eq!(error_codes(&r), Vec::<&str>::new(), "{:?}", r.diagnostics);
11192    }
11193
11194    #[test]
11195    fn sum_type_param_bound_reports_e0900() {
11196        // The same hole in the sum-type spelling: `hir::SumType` has no
11197        // `bounds` field either, so `type Wrap<T: Hash2>` used to accept a
11198        // payload with no `Hash2` impl.
11199        let r = check_src(
11200            "trait Hash2 { fn h(self) -> Int }\n\
11201             type Wrap<T: Hash2> = | Wrapped(T) | Empty\n\
11202             record NoHash { n: Int }\n\
11203             fn main() { let x = Wrapped(NoHash { n: 1 })\n match x { Wrapped(_) => println(\"w\"), Empty => println(\"e\") } }",
11204        );
11205        assert!(error_codes(&r).contains(&"E0900"), "{:?}", r.diagnostics);
11206    }
11207
11208    #[test]
11209    fn sum_type_param_bound_e0900_reports_every_bounded_param() {
11210        // `reject_type_param_bounds` is still live for sum types (Task 1 only
11211        // dropped the record caller) and its own doc comment promises "one
11212        // diagnostic per bounded parameter, so a second offender is not
11213        // hidden behind the first." Task 1 removed the record caller, so the
11214        // record-side version of this guard could not survive as a count: it
11215        // became *this* test, on the sum-type path that still has the
11216        // behavior, and the empty-diagnostic-list assertion that replaced it
11217        // for records lives in a separate test,
11218        // `multiple_record_type_param_bounds_no_longer_report_e0900` (just
11219        // below). Without this one, no test anywhere in this file would assert
11220        // an `E0900` *count* greater than one.
11221        //
11222        // An earlier version of this comment named a
11223        // `record_type_param_bound_e0900_reports_every_bounded_param` as
11224        // having been "rewritten by Task 1" — no test of that name exists, in
11225        // this file or anywhere else, so the citation pointed a reader at
11226        // nothing. Both surviving tests are named above instead.
11227        let r = check_src(
11228            "trait A { fn a(self) -> Int }\n\
11229             trait B { fn b(self) -> Int }\n\
11230             type Two<K: A, V: B> = | X(K) | Y(V)\n\
11231             fn main() { }",
11232        );
11233        assert_eq!(
11234            error_codes(&r).iter().filter(|c| **c == "E0900").count(),
11235            2,
11236            "{:?}",
11237            r.diagnostics
11238        );
11239    }
11240
11241    #[test]
11242    fn multiple_record_type_param_bounds_no_longer_report_e0900() {
11243        // Pre-Task-1, one E0900 fired *per* bounded parameter, so a
11244        // multi-parameter record did not hide a second offender behind the
11245        // first. Since Task 1 (see `record_type_param_bound_no_longer_reports_e0900`
11246        // just above), the bound is a resolution scope rather than a rejected
11247        // constraint, for every bounded parameter — not just the first — so
11248        // this guards against a partial regression that reintroduced the
11249        // rejection for, say, only a record's first bounded parameter.
11250        let r = check_src(
11251            "trait A { fn a(self) -> Int }\n\
11252             trait B { fn b(self) -> Int }\n\
11253             record Two<K: A, V: B> { k: K, v: V }\n\
11254             fn main() { }",
11255        );
11256        assert_eq!(error_codes(&r), Vec::<&str>::new(), "{:?}", r.diagnostics);
11257    }
11258
11259    #[test]
11260    fn bounds_on_supported_positions_do_not_report_e0900() {
11261        // The E0900 above must fire *only* for a bound on a sum type's own
11262        // parameter (since Task 1 of the iterator-finishing plan, a record's
11263        // own parameter no longer reports it at all — see
11264        // `record_type_param_bound_no_longer_reports_e0900`). Bounds on
11265        // functions, impl blocks, generic trait methods and `where` clauses
11266        // are all supported and are used throughout `std/`, which every
11267        // program compiles — a false positive here would break the whole
11268        // stdlib. An unbounded generic record/sum (how `std` actually writes
11269        // `Vec<T>` / `Map<K, V>`) must stay clean too.
11270        let r = check_src(
11271            "trait Show { fn show(self) -> String }\n\
11272             impl Show for Int { fn show(self) -> String { \"i\" } }\n\
11273             record Vec2<T> { a: T, b: T }\n\
11274             type Maybe<T> = | Just(T) | Nothing\n\
11275             impl<T: Show> Vec2<T> { fn first(self) -> String { self.a.show() } }\n\
11276             trait Conv { fn conv<U: Show>(self, u: U) -> String }\n\
11277             impl Conv for Int { fn conv<U: Show>(self, u: U) -> String { u.show() } }\n\
11278             fn label<T: Show>(x: T) -> String { x.show() }\n\
11279             fn label2<T>(x: T) -> String where T: Show { x.show() }\n\
11280             fn main() {\n\
11281                 println(label(1))\n\
11282                 println(label2(2))\n\
11283                 println(Vec2 { a: 1, b: 2 }.first())\n\
11284                 println(3.conv(4))\n\
11285                 let m = Just(5)\n\
11286                 match m { Just(v) => println(\"${v}\"), Nothing => println(\"n\") }\n\
11287             }",
11288        );
11289        assert!(
11290            !error_codes(&r).contains(&"E0900"),
11291            "false positive: {:?}",
11292            r.diagnostics
11293        );
11294        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11295    }
11296
11297    #[test]
11298    fn a_record_field_may_name_a_projection_on_a_bounded_parameter() {
11299        // The blocker this whole increment exists to remove. Without the bound
11300        // resolving here, a lazy `map` adapter cannot be written at all: its
11301        // field must be typed `fn(I::Item) -> U`.
11302        let r = check_src(
11303            "trait It { type Item\n fn next(mut self) -> Option<Self::Item> }\n\
11304             record M<I: It, U> { it: I, f: fn(I::Item) -> U }\n\
11305             fn main() { }",
11306        );
11307        assert_eq!(error_codes(&r), Vec::<&str>::new(), "{:?}", r.diagnostics);
11308    }
11309
11310    #[test]
11311    fn a_record_bound_resolves_when_the_bounded_parameter_is_not_first() {
11312        // `resolve_bounds` returns one `Vec<DefId>` per generic parameter, in
11313        // declaration order, and `convert_ty` looks up a projection's bound
11314        // list by the same positional index that `generic_scope` assigned —
11315        // that free function is a three-line `generics.iter().enumerate()`, and
11316        // it is named rather than cited by line because the previous version of
11317        // this comment pointed at a line number that had since gone blank.
11318        // Every other test in
11319        // this file puts its bound on parameter index 0 (`M<I: It, U>`,
11320        // `M<I: It>`, `M<I: Sub>`), so a mutation that dropped unbounded
11321        // parameters' empty entries before indexing — shifting every later
11322        // index — would pass the whole suite. `U` (unbounded) is declared
11323        // first here specifically so `I`'s bound sits at index 1.
11324        let r = check_src(
11325            "trait It { type Item\n fn next(mut self) -> Option<Self::Item> }\n\
11326             record M<U, I: It> { it: I, f: fn(I::Item) -> U }\n\
11327             fn main() { }",
11328        );
11329        assert_eq!(error_codes(&r), Vec::<&str>::new(), "{:?}", r.diagnostics);
11330    }
11331
11332    #[test]
11333    fn a_record_bound_naming_an_unknown_trait_is_e0001() {
11334        // Resolution must report, not skip. A silently-dropped bound would put
11335        // this increment straight back into the "accepted and quietly ignored"
11336        // family the spec's §3.2 warns about.
11337        let r = check_src(
11338            "record M<I: NoSuchTrait> { it: I }\n\
11339             fn main() { }",
11340        );
11341        let d = r
11342            .diagnostics
11343            .iter()
11344            .find(|d| d.code == "E0001")
11345            .expect("E0001 for an unresolvable record bound");
11346        assert!(
11347            d.message.contains("NoSuchTrait"),
11348            "names the trait: {}",
11349            d.message
11350        );
11351    }
11352
11353    #[test]
11354    fn a_projection_on_an_unbounded_record_parameter_is_still_e0001() {
11355        // The bound is what makes the projection resolvable, so without one the
11356        // old error must remain. This is the guard against "resolve projections
11357        // against every trait in scope", which would accept nonsense.
11358        let r = check_src(
11359            "trait It { type Item\n fn next(mut self) -> Option<Self::Item> }\n\
11360             record M<I, U> { it: I, f: fn(I::Item) -> U }\n\
11361             fn main() { }",
11362        );
11363        assert!(
11364            r.diagnostics.iter().any(|d| d.code == "E0001"),
11365            "an unbounded parameter has no `Item`: {:?}",
11366            r.diagnostics
11367        );
11368    }
11369
11370    #[test]
11371    fn a_bound_on_a_sum_type_parameter_is_still_e0900() {
11372        // Records only. Nothing in this increment needs a bound on a sum
11373        // parameter, and leaving the rejection in place halves the surface.
11374        let r = check_src(
11375            "trait It { type Item\n fn next(mut self) -> Option<Self::Item> }\n\
11376             type S<I: It> = | A(I) | B\n\
11377             fn main() { }",
11378        );
11379        let d = r
11380            .diagnostics
11381            .iter()
11382            .find(|d| d.code == "E0900")
11383            .expect("E0900 still rejects a bound on a sum type parameter");
11384        assert!(d.message.contains("sum type"), "{}", d.message);
11385    }
11386
11387    #[test]
11388    fn a_record_bound_is_not_enforced_at_construction() {
11389        // The spec's §3.2 decision, pinned so it cannot drift silently in
11390        // either direction. `Int` is not an `It`, and building `M<Int, …>` is
11391        // accepted: the bound is a resolution scope, not a constraint.
11392        //
11393        // What makes that safe is NOT one uniform diagnostic. It is three
11394        // different answers depending on whether the bound reaches a field
11395        // type, and only the first two are diagnostics at all — see ADR 0007
11396        // §1, whose three cases are pinned in `crates/nova-cli/tests/
11397        // run_tests.rs` (they need monomorphization, so they cannot live
11398        // here):
11399        //   `a_wrong_instantiation_of_a_projection_shaped_record_is_e0079_at_
11400        //    construction`               — a field type NAMES the projection
11401        //                                  (std's `MapIter`/`FilterIter`):
11402        //                                  `E0079` at construction.
11403        //   `an_unused_record_bound_is_still_enforced_through_a_bounded_impl_
11404        //    method`                     — no field type does, but a bounded
11405        //                                  impl method is instantiated:
11406        //                                  `E0013`.
11407        //   `a_record_bound_no_field_type_uses_is_silently_accepted_when_
11408        //    never_exercised`            — neither: accepted, runs, prints.
11409        //                                  The residual hole, pinned as
11410        //                                  accepted.
11411        // `M` here is the third shape (`it: I` names no projection) and is
11412        // never used, so it is case 3. Earlier revisions of this comment
11413        // pointed at "the E0014 test below": wrong code, and no such test ever
11414        // existed.
11415        let r = check_src(
11416            "trait It { type Item\n fn next(mut self) -> Option<Self::Item> }\n\
11417             record M<I: It> { it: I }\n\
11418             fn main() { let m = M { it: 3 }\n let _ = m }",
11419        );
11420        assert_eq!(error_codes(&r), Vec::<&str>::new(), "{:?}", r.diagnostics);
11421    }
11422
11423    #[test]
11424    fn a_record_field_initializer_normalizes_a_projection_once_its_parameter_is_concrete() {
11425        // Regression test for a bug found implementing the iterator-finishing
11426        // plan's Task 3 (`MapIter`/`FilterIter` in std/core/lib.nova): building
11427        // a concrete instance of a record whose field type names a projection
11428        // on the record's own bounded parameter. `a_record_field_may_name_a_
11429        // projection_on_a_bounded_parameter`, above, only ever checks the
11430        // *declaration* — its `fn main() {}` is empty and never constructs one.
11431        //
11432        // `check_record_literal` used to compute each field's expected type
11433        // with a raw `field.ty.subst(&type_args)`, with no normalization step.
11434        // Once `it: Counter { n: 0 }` pins `I` to the concrete `Counter`, `f`'s
11435        // declared type substitutes to `fn(Assoc { on: Counter, name: "Item" })
11436        // -> ?U` — an *unnormalized* projection — and unifying that against the
11437        // closure's real type `fn(Int) -> Int` failed structurally, since
11438        // `unify` never normalizes (see `Checker::normalize`'s doc comment,
11439        // "Never called from `unify`"). `|x| x + 1`, not the bare identity
11440        // `|x| x`, is deliberate: a fully generic closure would unify against
11441        // the unnormalized projection just as well as against `Int` (a fresh
11442        // variable binds to either), so nothing would fail and this test would
11443        // pass regardless of the bug. Forcing `x: Int` through `+` is what
11444        // makes the mismatch (`Int` against `Assoc { .. }`) structural, and so
11445        // visible.
11446        //
11447        // Fixed by routing both of `check_record_literal`'s field-type call
11448        // sites through `instantiate` (subst, then apply current bindings and,
11449        // if a projection remains, normalize through the impl table) instead of
11450        // a raw `subst` — the same seam `check_direct_call` and
11451        // `emit_trait_call` already used.
11452        let r = check_src(
11453            "trait It { type Item\n fn next(mut self) -> Option<Self::Item> }\n\
11454             record M<I: It, U> { it: I, f: fn(I::Item) -> U }\n\
11455             record Counter { n: Int }\n\
11456             impl It for Counter { type Item = Int\n fn next(mut self) -> Option<Int> { None } }\n\
11457             fn main() { let m = M { it: Counter { n: 0 }, f: |x| x + 1 }\n let _ = m }",
11458        );
11459        assert_eq!(error_codes(&r), Vec::<&str>::new(), "{:?}", r.diagnostics);
11460    }
11461
11462    #[test]
11463    fn a_record_field_initializer_is_order_sensitive_when_it_names_a_projection() {
11464        // A known, documented limitation of the fix above (see the comment at
11465        // its call site in `check_record_literal`), not a regression: this
11466        // pins *current* behavior so a future fix to field-order independence
11467        // has to touch this test deliberately rather than leave it silently
11468        // wrong.
11469        //
11470        // `check_record_literal` walks `for init in fields` in the *literal's
11471        // written* order. The sibling test just above writes `it` before `f`,
11472        // so by the time `f`'s expected type is computed, `it: Counter { n: 0
11473        // }` has already pinned `I` to `Counter` via `unify`, and `instantiate`
11474        // has something concrete to normalize `I::Item` against. Here the two
11475        // fields are swapped — same record, same values, same closure — so
11476        // `I` is still a free inference variable when `f`'s turn comes:
11477        // `instantiate`'s own `has_assoc()` check finds the projection, but
11478        // `icx.apply` on an unbound variable is a no-op, so there is nothing
11479        // yet to normalize *through*. The result is the exact pre-fix
11480        // symptom: an `E0010` naming an unresolved `Assoc`, cascading into
11481        // `E0011`s for the variables it left unpinned. Measured on *this*
11482        // source: one `E0010` and six `E0011`s. The assertion below
11483        // deliberately checks only the `E0010` — the cascade is a consequence
11484        // of the failure, not the property under test, and pinning its exact
11485        // shape would make this test fail for uninteresting reasons.
11486        //
11487        // **The cascade is a property of this shape, not of the field swap,
11488        // so do not carry it to another one.** `E0011` fires only for the
11489        // variables *nothing later pins*, and here `m` is bound and never
11490        // used, so `I` and `U` both stay free. Swap the same two fields in a
11491        // literal that is subsequently driven and the cascade disappears
11492        // entirely: `tests/runtime/iterator.nova`'s CHAIN block measures
11493        // exactly one `E0010` and zero `E0011`s from this same swap, because
11494        // its `it:` initializer still pins `I` and its `next()` calls pin `U`.
11495        // That fixture's comment briefly claimed a cascade transcribed from
11496        // here; the count was never true there. Same over-generalization ADR
11497        // 0007 §1 exists to record.
11498        //
11499        // This is the same class of hole `instantiate`'s own doc comment
11500        // already admits for a call (`fn f<I: It>(y: I::Item, x: I)` still
11501        // fails) — but wider here, since a function's parameter order is
11502        // fixed by its signature while a record literal's field order is free
11503        // syntax with nothing to read an ordering requirement off of.
11504        let r = check_src(
11505            "trait It { type Item\n fn next(mut self) -> Option<Self::Item> }\n\
11506             record M<I: It, U> { it: I, f: fn(I::Item) -> U }\n\
11507             record Counter { n: Int }\n\
11508             impl It for Counter { type Item = Int\n fn next(mut self) -> Option<Int> { None } }\n\
11509             fn main() { let m = M { f: |x| x + 1, it: Counter { n: 0 } }\n let _ = m }",
11510        );
11511        assert!(
11512            error_codes(&r).contains(&"E0010"),
11513            "documents a known limitation (field order matters when a field \
11514             names a projection); if this now fails, the limitation is fixed \
11515             and this test should be updated to assert a clean compile \
11516             instead: {:?}",
11517            r.diagnostics
11518        );
11519    }
11520
11521    #[test]
11522    fn a_record_bound_resolves_a_projection_through_a_supertrait() {
11523        // `collect_records` calls `expand_bounds` after `resolve_bounds`, so a
11524        // record's bound list carries transitive supertraits exactly like a
11525        // function's or an impl's does (see `convert_ty`'s two-segment path
11526        // case, the `Some(idx)` arm of its `match by_index`: "`expand_bounds`
11527        // has already folded supertraits into every entry here" — named by
11528        // function and arm rather than by line, because the line number this
11529        // comment used to quote was already two dozen lines stale). `Sub: It`
11530        // declares no `Item` itself — only its supertrait `It` does — so
11531        // resolving `I::Item` against a bound of just `Sub` requires that
11532        // fold-in. This is TDD-by-mutation Step 7's second case: skipping
11533        // `expand_bounds` while still passing `&bounds` (rather than `&[]`)
11534        // reproduces the exact failure this test pins.
11535        let r = check_src(
11536            "trait It { type Item\n fn next(mut self) -> Option<Self::Item> }\n\
11537             trait Sub: It { fn extra(self) -> Int }\n\
11538             record M<I: Sub> { f: fn(I::Item) -> Int }\n\
11539             fn main() { }",
11540        );
11541        assert_eq!(error_codes(&r), Vec::<&str>::new(), "{:?}", r.diagnostics);
11542    }
11543
11544    #[test]
11545    fn redundant_same_trait_bounds_are_not_ambiguous() {
11546        // Naming a trait more than once is redundant, not ambiguous: it must not
11547        // read as two providers of the method (a false E0015). Covers inline+where
11548        // accumulation, a duplicated `where` bound, and a duplicated inline bound.
11549        for sig in [
11550            "fn label<T: Show>(x: T) -> String where T: Show { x.show() }",
11551            "fn label<T>(x: T) -> String where T: Show, T: Show { x.show() }",
11552            "fn label<T: Show + Show>(x: T) -> String { x.show() }",
11553        ] {
11554            let src = format!(
11555                "trait Show {{ fn show(self) -> String }}\n\
11556                 impl Show for Int {{ fn show(self) -> String {{ \"i\" }} }}\n\
11557                 {sig}\n\
11558                 fn main() {{ println(label(1)) }}"
11559            );
11560            let r = check_src(&src);
11561            assert!(r.diagnostics.is_empty(), "sig `{sig}`: {:?}", r.diagnostics);
11562        }
11563    }
11564
11565    #[test]
11566    fn generic_sum_in_field_or_payload_typechecks() {
11567        // A generic sum used as a record field or a sum-variant payload must not
11568        // be mis-read as arity 0 (regression: spurious E0012). Covers the prelude
11569        // `Option`/`Result` (collected last) and a forward-referenced record.
11570        let r = check_src(
11571            "record Slot { tag: Option<Int> }\n\
11572             type Wrapper = | W(Result<Int, String>) | Empty\n\
11573             fn main() {\n\
11574                 let s = Slot { tag: Some(1) }\n\
11575                 match s.tag { Some(v) => println(\"${v}\"), None => println(\"n\") }\n\
11576             }",
11577        );
11578        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11579        let r2 = check_src(
11580            "record Slot { b: Box<Int> }\n\
11581             record Box<T> { value: T }\n\
11582             fn main() { }",
11583        );
11584        assert!(r2.diagnostics.is_empty(), "{:?}", r2.diagnostics);
11585    }
11586
11587    #[test]
11588    fn extern_scalar_signature_is_accepted() {
11589        let r = check_src(
11590            "extern \"C\" { fn sqrt(x: Float) -> Float\n fn llabs(x: Int) -> Int }\n\
11591             fn main() { let r = sqrt(2.0)\n println(\"${r}\") }",
11592        );
11593        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11594        assert!(r.module.externs.iter().any(|e| e.symbol == "sqrt"));
11595    }
11596
11597    #[test]
11598    fn extern_non_scalar_param_reports_e0900() {
11599        let r = check_src("extern \"C\" { fn puts(s: String) -> Int }\nfn main() { }");
11600        assert!(error_codes(&r).contains(&"E0900"), "{:?}", r.diagnostics);
11601    }
11602
11603    #[test]
11604    fn extern_unsupported_abi_reports_e0900() {
11605        let r = check_src("extern \"stdcall\" { fn foo() -> Int }\nfn main() { }");
11606        assert!(error_codes(&r).contains(&"E0900"), "{:?}", r.diagnostics);
11607    }
11608
11609    #[test]
11610    fn generic_extern_reports_e0900_without_cascade() {
11611        let r = check_src("extern \"C\" { fn id<T>(x: T) -> T }\nfn main() { }");
11612        let codes = error_codes(&r);
11613        assert!(codes.contains(&"E0900"), "{:?}", r.diagnostics);
11614        // The generic extern short-circuits, so no false "cannot find type `T`".
11615        assert!(!codes.contains(&"E0001"), "cascade: {:?}", r.diagnostics);
11616    }
11617
11618    #[test]
11619    fn extern_reserved_symbol_reports_e0900() {
11620        // An extern may not shadow/alias a compiler-internal symbol (`nova_*`).
11621        let r = check_src("extern \"C\" { fn nova_rt_alloc(x: Int) -> Int }\nfn main() { }");
11622        assert!(error_codes(&r).contains(&"E0900"), "{:?}", r.diagnostics);
11623    }
11624
11625    #[test]
11626    fn unused_impl_param_reports_e0073() {
11627        // `U` is declared but appears nowhere in the self type `Box<T>`.
11628        let r = check_src(
11629            "record Box<T> { value: T }\n\
11630             impl<T, U> Box<T> { fn get(self) -> T { self.value } }\n\
11631             fn main() { let b = Box { value: 1 }\n let x = b.get() }",
11632        );
11633        assert!(error_codes(&r).contains(&"E0073"), "{:?}", r.diagnostics);
11634    }
11635
11636    #[test]
11637    fn overlapping_trait_impls_report_e0074() {
11638        // A generic impl and a specific impl of the same trait for the same
11639        // head both cover `Box<Int>` — a coherence conflict (no specialization).
11640        let r = check_src(
11641            "record Box<T> { value: T }\n\
11642             trait Kind { fn kind(self) -> String }\n\
11643             impl<T> Kind for Box<T> { fn kind(self) -> String { \"g\" } }\n\
11644             impl Kind for Box<Int> { fn kind(self) -> String { \"i\" } }\n\
11645             fn main() { }",
11646        );
11647        assert!(error_codes(&r).contains(&"E0074"), "{:?}", r.diagnostics);
11648    }
11649
11650    #[test]
11651    fn overlapping_inherent_impls_sharing_method_report_e0074() {
11652        let r = check_src(
11653            "record Box<T> { value: T }\n\
11654             impl<T> Box<T> { fn tag(self) -> String { \"g\" } }\n\
11655             impl Box<Int> { fn tag(self) -> String { \"i\" } }\n\
11656             fn main() { }",
11657        );
11658        assert!(error_codes(&r).contains(&"E0074"), "{:?}", r.diagnostics);
11659    }
11660
11661    #[test]
11662    fn non_overlapping_concrete_trait_impls_ok() {
11663        // Two concrete impls of one trait for the same head that share no
11664        // ground instance are allowed, and the call resolves to the fitting
11665        // one regardless of declaration order.
11666        let r = check_src(
11667            "record Pair<A, B> { first: A, second: B }\n\
11668             trait Foo { fn foo(self) -> String }\n\
11669             impl Foo for Pair<Int, Bool> { fn foo(self) -> String { \"b\" } }\n\
11670             impl Foo for Pair<Int, String> { fn foo(self) -> String { \"s\" } }\n\
11671             fn main() { let p = Pair { first: 1, second: \"x\" }\n println(p.foo()) }",
11672        );
11673        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11674    }
11675
11676    #[test]
11677    fn non_overlapping_inherent_impls_ok() {
11678        // Distinct concrete inherent impls on the same head do not conflict.
11679        let r = check_src(
11680            "record Box<T> { value: T }\n\
11681             impl Box<Int> { fn a(self) -> Int { self.value } }\n\
11682             impl Box<Bool> { fn b(self) -> Bool { self.value } }\n\
11683             fn main() {\n\
11684                 let bi = Box { value: 1 }\n\
11685                 let bb = Box { value: true }\n\
11686                 let x = bi.a()\n\
11687                 let y = bb.b()\n\
11688             }",
11689        );
11690        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11691    }
11692
11693    #[test]
11694    fn non_applicable_inherent_method_does_not_shadow_trait_method() {
11695        // A restricted inherent `impl<T> Pair<T, T>` must not block the
11696        // applicable trait method for a non-uniform `Pair<Int, String>`;
11697        // resolution falls through to the trait impl instead of rejecting.
11698        let r = check_src(
11699            "record Pair<A, B> { first: A, second: B }\n\
11700             trait Describe { fn describe(self) -> String }\n\
11701             impl<T> Pair<T, T> { fn describe(self) -> String { \"same\" } }\n\
11702             impl<A, B> Describe for Pair<A, B> { fn describe(self) -> String { \"pair\" } }\n\
11703             fn main() {\n\
11704                 let p = Pair { first: 1, second: \"x\" }\n\
11705                 println(p.describe())\n\
11706             }",
11707        );
11708        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11709    }
11710
11711    #[test]
11712    fn applicable_inherent_method_still_takes_priority() {
11713        // When the inherent impl *does* fit the receiver, it wins over the
11714        // trait method (unchanged precedence).
11715        let r = check_src(
11716            "record Pair<A, B> { first: A, second: B }\n\
11717             trait Describe { fn describe(self) -> String }\n\
11718             impl<T> Pair<T, T> { fn describe(self) -> String { \"same\" } }\n\
11719             impl<A, B> Describe for Pair<A, B> { fn describe(self) -> String { \"pair\" } }\n\
11720             fn main() {\n\
11721                 let q = Pair { first: 1, second: 2 }\n\
11722                 println(q.describe())\n\
11723             }",
11724        );
11725        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11726    }
11727
11728    #[test]
11729    fn non_applicable_trait_impl_is_rejected_on_direct_call() {
11730        // A trait impl on `Pair<T, T>` does not apply to `Pair<Int, String>`;
11731        // a direct call has no other candidate, so it is rejected (E0014).
11732        let r = check_src(
11733            "record Pair<A, B> { first: A, second: B }\n\
11734             trait Same { fn same(self) -> Int }\n\
11735             impl<T> Same for Pair<T, T> { fn same(self) -> Int { 1 } }\n\
11736             fn main() { let p = Pair { first: 1, second: \"x\" }\n let n = p.same() }",
11737        );
11738        assert!(error_codes(&r).contains(&"E0014"), "{:?}", r.diagnostics);
11739    }
11740
11741    #[test]
11742    fn non_applicable_inherent_fmt_does_not_block_display_bridge() {
11743        // A non-applicable inherent `fmt` must not block string interpolation
11744        // from using the applicable `Display` trait impl.
11745        let r = check_src(
11746            "record Pair<A, B> { first: A, second: B }\n\
11747             trait Display { fn fmt(self) -> String }\n\
11748             impl<T> Pair<T, T> { fn fmt(self) -> String { \"same\" } }\n\
11749             impl<A, B> Display for Pair<A, B> { fn fmt(self) -> String { \"pair\" } }\n\
11750             fn main() { let p = Pair { first: 1, second: \"x\" }\n println(\"${p}\") }",
11751        );
11752        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11753    }
11754
11755    #[test]
11756    fn record_literal_in_an_interpolation_reaches_both_interp_paths() {
11757        // Now that a hole's braces balance, a record literal can sit directly
11758        // in one. `check_interp` has two arms and this exercises both from that
11759        // position: `${R { … }.v}` is an `Int`, converted natively without
11760        // consulting any `fmt`, while `${R { … }}` is a nominal type and goes
11761        // through the `Display` bridge.
11762        let r = check_src(
11763            "record R { v: Int }\n\
11764             trait Display { fn fmt(self) -> String }\n\
11765             impl Display for R { fn fmt(self) -> String { \"r=${self.v}\" } }\n\
11766             fn main() { println(\"${R { v: 1 }.v} ${R { v: 2 }}\") }",
11767        );
11768        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11769        // Both pieces really are the two different lowerings, not one path
11770        // twice: the native `ToStr` for the `Int` field, and a call for the
11771        // record. (Plus the `StrLit` for the literal space between them.)
11772        let main = r
11773            .module
11774            .functions
11775            .iter()
11776            .find(|f| f.name == "main")
11777            .expect("`main` compiled to a hir::Function");
11778        let concat = find_str_concat(&main.body).expect("main interpolates");
11779        let kinds: Vec<&str> = concat
11780            .iter()
11781            .map(|p| match &p.kind {
11782                hir::ExprKind::ToStr(_) => "ToStr",
11783                hir::ExprKind::StrLit(_) => "StrLit",
11784                hir::ExprKind::TraitCall { .. } => "TraitCall",
11785                _ => "other",
11786            })
11787            .collect();
11788        assert_eq!(
11789            kinds,
11790            vec!["ToStr", "StrLit", "TraitCall"],
11791            "the Int field converts natively and the record goes through the \
11792             Display bridge"
11793        );
11794        // The record really is built inside the hole, not read from a local.
11795        let record_arg = match &concat[2].kind {
11796            hir::ExprKind::TraitCall {
11797                receiver: Some(r), ..
11798            } => r,
11799            other => panic!("expected a Display TraitCall with a receiver, got {other:?}"),
11800        };
11801        // A record literal lowers to a block that binds its field values to
11802        // temporaries and then builds the record.
11803        let builds_record = match &record_arg.kind {
11804            hir::ExprKind::MakeRecord { .. } => true,
11805            hir::ExprKind::Block {
11806                trailing: Some(t), ..
11807            } => matches!(t.kind, hir::ExprKind::MakeRecord { .. }),
11808            _ => false,
11809        };
11810        assert!(
11811            builds_record,
11812            "receiver should be the record literal itself, got {:?}",
11813            record_arg.kind
11814        );
11815    }
11816
11817    /// The first `StrConcat` inside `e`, searched through the expression forms
11818    /// an interpolation can be nested in here (a block, and `println`'s args).
11819    fn find_str_concat(e: &hir::Expr) -> Option<&Vec<hir::Expr>> {
11820        match &e.kind {
11821            hir::ExprKind::StrConcat(pieces) => Some(pieces),
11822            hir::ExprKind::Call { args, .. } => args.iter().find_map(find_str_concat),
11823            hir::ExprKind::Block { stmts, trailing } => stmts
11824                .iter()
11825                .chain(trailing.as_deref())
11826                .find_map(find_str_concat),
11827            _ => None,
11828        }
11829    }
11830
11831    #[test]
11832    fn selfless_method_params_are_not_shifted() {
11833        // `x: Int` must stay Int; a wrongly prepended `self` shifted it to `P`.
11834        let r = check_src(
11835            "record P { v: Int }\n\
11836             impl P { fn make(x: Int) -> Int { x + 1 } }\n\
11837             fn main() { println(\"ok\") }",
11838        );
11839        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11840    }
11841
11842    #[test]
11843    fn selfless_method_param_types_are_declared_types() {
11844        // `selfless_method_params_are_not_shifted` above only proves there is
11845        // no *diagnostic* for a self-less method's parameters — that covers
11846        // the false-error symptom, not the silent-miscompile one. Pre-fix,
11847        // `impl P { fn f(a: Int, b: Int) -> Int { b } }` type-checked as `ok`
11848        // with `a` silently bound to the self type `P` instead of `Int`: the
11849        // positional zip in `check_fn_body` paired `a` with the wrongly
11850        // prepended self type and `b` with the first declared type (`Int`),
11851        // which happened to be the correct type for `b` — no diagnostic ever
11852        // compared `a` against anything, so nothing caught it. This pins the
11853        // actual bound *types*, not just the absence of errors.
11854        let r = check_src(
11855            "record P { v: Int }\n\
11856             impl P { fn f(a: Int, b: Int) -> Int { b } }\n\
11857             fn main() { println(\"ok\") }",
11858        );
11859        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11860
11861        let f = r
11862            .module
11863            .functions
11864            .iter()
11865            .find(|func| func.name == "P.f")
11866            .expect("impl method `f` compiled to a hir::Function");
11867        // No phantom leading `self` parameter: exactly the two declared params.
11868        assert_eq!(f.params, 2, "expected 2 declared params, none prepended");
11869        assert_eq!(f.locals.len(), 2, "no extra locals beyond the two params");
11870        // And each keeps its *declared* type rather than being shifted by one.
11871        assert_eq!(f.locals[0].ty, Ty::Int, "`a` should be Int, not `P`");
11872        assert_eq!(f.locals[1].ty, Ty::Int, "`b` should be Int");
11873    }
11874
11875    #[test]
11876    fn selfless_trait_impl_method_checks_conformance_without_panicking() {
11877        // A trait method declared without `self` leaves the impl signature with
11878        // no receiver to skip; conformance must compare the declared parameters
11879        // as-is rather than slicing past the end of an empty list.
11880        let r = check_src(
11881            "record P { v: Int }\n\
11882             trait Zero { fn zero() -> Int }\n\
11883             impl Zero for P { fn zero() -> Int { 0 } }\n\
11884             fn main() { println(\"ok\") }",
11885        );
11886        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11887    }
11888
11889    #[test]
11890    fn selfless_method_called_on_instance_reports_e0014() {
11891        // Must be a clean diagnostic, never a codegen ICE.
11892        let r = check_src(
11893            "record P { v: Int }\n\
11894             impl P { fn make() -> P { P { v: 7 } } }\n\
11895             fn main() { let p = P { v: 0 }\n let q = p.make()\n println(\"${q.v}\") }",
11896        );
11897        assert!(error_codes(&r).contains(&"E0014"), "{:?}", r.diagnostics);
11898    }
11899
11900    #[test]
11901    fn associated_function_call_on_concrete_type() {
11902        let r = check_src(
11903            "record P { v: Int }\n\
11904             impl P { fn new() -> P { P { v: 7 } } }\n\
11905             fn main() { let p = P::new()\n println(\"${p.v}\") }",
11906        );
11907        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11908    }
11909
11910    #[test]
11911    fn associated_function_with_args() {
11912        let r = check_src(
11913            "record P { v: Int }\n\
11914             impl P { fn of(x: Int) -> P { P { v: x } } }\n\
11915             fn main() { let p = P::of(5)\n println(\"${p.v}\") }",
11916        );
11917        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11918    }
11919
11920    #[test]
11921    fn associated_function_wrong_arity_reports_e0016() {
11922        let r = check_src(
11923            "record P { v: Int }\n\
11924             impl P { fn of(x: Int) -> P { P { v: x } } }\n\
11925             fn main() { let p = P::of()\n println(\"${p.v}\") }",
11926        );
11927        assert!(error_codes(&r).contains(&"E0016"), "{:?}", r.diagnostics);
11928    }
11929
11930    #[test]
11931    fn unknown_associated_function_still_reports_e0001() {
11932        let r = check_src(
11933            "record P { v: Int }\n\
11934             fn main() { let p = P::nope()\n println(\"x\") }",
11935        );
11936        assert!(error_codes(&r).contains(&"E0001"), "{:?}", r.diagnostics);
11937    }
11938
11939    #[test]
11940    fn generic_type_qualifier_with_unresolved_args_reports_e0011() {
11941        // `Box::zero()` where only a concrete `impl Zero for Box<Int>` exists:
11942        // `find_trait_assoc_fns` cannot select it because the qualifier's type
11943        // argument (from `qualifier_self_ty`) is a fresh, still-unresolved
11944        // inference variable, and `ImplInfo::match_args` compares structurally
11945        // — a documented, deliberate limitation (see `find_trait_assoc_fns`).
11946        // Before this fix this fell through silently to `check_path`, which
11947        // reported "no variant `zero` on type `Box`" — a diagnostic that blames
11948        // the wrong feature, since `zero` is a real associated function on a
11949        // real impl, just not one this call can select.
11950        let r = check_src(
11951            "record Box<T> { value: T }\n\
11952             trait Zero { fn zero() -> Self }\n\
11953             impl Zero for Box<Int> { fn zero() -> Box<Int> { Box { value: 0 } } }\n\
11954             fn main() { let b: Box<Int> = Box::zero()\n println(\"${b.value}\") }",
11955        );
11956        assert!(error_codes(&r).contains(&"E0011"), "{:?}", r.diagnostics);
11957        assert!(
11958            r.diagnostics
11959                .iter()
11960                .any(|d| d.message.contains("type arguments could not be determined")),
11961            "expected the message to name the real reason: {:?}",
11962            r.diagnostics
11963        );
11964        assert!(
11965            !r.diagnostics.iter().any(|d| d.message.contains("variant")),
11966            "should not blame a missing variant: {:?}",
11967            r.diagnostics
11968        );
11969    }
11970
11971    #[test]
11972    fn qualified_variant_call_with_args_still_typechecks() {
11973        // The single largest regression risk of the associated-function-call
11974        // feature: `Type::Variant(args)` and `Type::assoc_fn(args)` share the
11975        // same two-segment branch in `check_call`, and the variant lookup must
11976        // keep running — and returning early — before the associated-function
11977        // lookup is ever tried. No test anywhere exercised the *qualified call*
11978        // form with arguments before this: the existing exhaustiveness /
11979        // reachability tests only ever construct variants via their bare,
11980        // unqualified names (`Circle(r)`, `Some(x)`, ...), never `Type::Variant(args)`.
11981        let r = check_src(
11982            "type Shape = | Circle(Int) | Square(Int)\n\
11983             fn area(s: Shape) -> Int { match s { Circle(r) => r * r, Square(w) => w * w } }\n\
11984             fn main() { println(\"${area(Shape::Circle(5))}\") }",
11985        );
11986        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
11987    }
11988
11989    #[test]
11990    fn associated_function_on_generic_impl_resolves_type_arg() {
11991        // `impl<T> Box<T>` gives an associated function no receiver, so `T`
11992        // cannot be recovered the way `emit_inherent_call` recovers it (by
11993        // unifying the impl's generics against the receiver's type);
11994        // `emit_assoc_call` must instead recover `T` purely from the call's
11995        // own argument. Deliberately no `let` annotation on the call site:
11996        // `check_block`'s `let` handling overwrites the initializer's
11997        // inferred type with the annotation's (`value.ty = annot_ty`)
11998        // whenever unification succeeds — and `Ty::Error` unifies with
11999        // anything — so an annotated call site would silently paper over a
12000        // broken `type_args` substitution rather than exercise it. For the
12001        // same reason, asserting only `diagnostics.is_empty()` would not
12002        // catch that class of bug either, so this also pins the actual
12003        // resolved type of `b`.
12004        let r = check_src(
12005            "record Box<T> { value: T }\n\
12006             impl<T> Box<T> { fn make(v: T) -> Box<T> { Box { value: v } } }\n\
12007             fn main() {\n\
12008                 let b = Box::make(5)\n\
12009                 println(\"${b.value}\")\n\
12010             }",
12011        );
12012        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12013        let box_def = r
12014            .module
12015            .records
12016            .iter()
12017            .find(|rt| rt.name == "Box")
12018            .expect("record Box exists")
12019            .def_id;
12020        let main_fn = r
12021            .module
12022            .functions
12023            .iter()
12024            .find(|f| f.name == "main")
12025            .expect("main compiled to a hir::Function");
12026        let b_ty = &main_fn
12027            .locals
12028            .iter()
12029            .find(|l| l.name == "b")
12030            .expect("`b` is a local in main")
12031            .ty;
12032        assert_eq!(
12033            *b_ty,
12034            Ty::Record {
12035                def_id: box_def,
12036                args: vec![Ty::Int]
12037            },
12038            "expected `b: Box<Int>`, got {b_ty:?}"
12039        );
12040    }
12041
12042    #[test]
12043    fn ambiguous_inherent_associated_function_reports_e0015() {
12044        // Two *disjoint concrete* inherent impls of one generic type both
12045        // declare `tag`. `check_impl_coherence` deliberately allows the pair
12046        // (`self_types_overlap(Box<Int>, Box<Bool>)` is false, so no E0074),
12047        // and `find_assoc_fns` cannot tell them apart either — the qualifier
12048        // `Box` carries no type argument, so neither impl can be selected.
12049        // Before this guard, `Box::tag()` silently took whichever impl was
12050        // declared first: swapping the two `impl` lines below changed the
12051        // program's output from `1` to `2` with no diagnostic at all. That is
12052        // exactly the "dispatch would depend on impl declaration order"
12053        // failure `check_impl_coherence`'s doc comment names.
12054        let r = check_src(
12055            "record Box<T> { value: T }\n\
12056             impl Box<Int> { fn tag() -> Int { 1 } }\n\
12057             impl Box<Bool> { fn tag() -> Int { 2 } }\n\
12058             fn main() { println(\"${Box::tag()}\") }",
12059        );
12060        assert!(error_codes(&r).contains(&"E0015"), "{:?}", r.diagnostics);
12061        // And the pair really is coherent, so E0015 is the *only* thing
12062        // standing between this program and order-dependent dispatch.
12063        assert!(
12064            !error_codes(&r).contains(&"E0074"),
12065            "the two impls do not overlap, so coherence must not fire: {:?}",
12066            r.diagnostics
12067        );
12068        // The reverse declaration order must be rejected identically — the
12069        // whole point is that order stops mattering.
12070        let flipped = check_src(
12071            "record Box<T> { value: T }\n\
12072             impl Box<Bool> { fn tag() -> Int { 2 } }\n\
12073             impl Box<Int> { fn tag() -> Int { 1 } }\n\
12074             fn main() { println(\"${Box::tag()}\") }",
12075        );
12076        assert!(
12077            error_codes(&flipped).contains(&"E0015"),
12078            "{:?}",
12079            flipped.diagnostics
12080        );
12081    }
12082
12083    #[test]
12084    fn single_concrete_inherent_associated_function_still_resolves() {
12085        // The permissive single-candidate case the E0015 guard above must not
12086        // regress: one `impl Box<Int>` and a bare `Box::tag()` qualifier whose
12087        // type argument is still an inference variable. Selection is by
12088        // nominal head alone, so this resolves; asserting the *resolved type*
12089        // of `t` rather than only `diagnostics.is_empty()` because `Ty::Error`
12090        // unifies with anything, which would make an emptiness-only assertion
12091        // silently vacuous.
12092        let r = check_src(
12093            "record Box<T> { value: T }\n\
12094             impl Box<Int> { fn tag() -> Int { 7 } }\n\
12095             fn main() {\n\
12096                 let t = Box::tag()\n\
12097                 println(\"${t}\")\n\
12098             }",
12099        );
12100        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12101        let main_fn = r
12102            .module
12103            .functions
12104            .iter()
12105            .find(|f| f.name == "main")
12106            .expect("main compiled to a hir::Function");
12107        let t_ty = &main_fn
12108            .locals
12109            .iter()
12110            .find(|l| l.name == "t")
12111            .expect("`t` is a local in main")
12112            .ty;
12113        assert_eq!(*t_ty, Ty::Int, "expected `t: Int`, got {t_ty:?}");
12114    }
12115
12116    #[test]
12117    fn trait_associated_function_on_concrete_type() {
12118        let r = check_src(
12119            "trait Zero { fn zero() -> Self }\n\
12120             impl Zero for Int { fn zero() -> Int { 0 } }\n\
12121             fn main() { println(\"${Int::zero()}\") }",
12122        );
12123        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12124    }
12125
12126    #[test]
12127    fn trait_associated_function_through_bound() {
12128        let r = check_src(
12129            "trait Zero { fn zero() -> Self }\n\
12130             impl Zero for Int { fn zero() -> Int { 0 } }\n\
12131             fn make<T: Zero>() -> T { T::zero() }\n\
12132             fn main() { let n: Int = make()\n println(\"${n}\") }",
12133        );
12134        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12135    }
12136
12137    #[test]
12138    fn impl_self_receiver_disagreeing_with_trait_reports_e0072() {
12139        // The trait declares an associated function; the impl gives it a
12140        // receiver. That is a signature mismatch, not a silent difference.
12141        let r = check_src(
12142            "trait Zero { fn zero() -> Self }\n\
12143             record R { v: Int }\n\
12144             impl Zero for R { fn zero(self) -> R { self } }\n\
12145             fn main() { }",
12146        );
12147        assert!(error_codes(&r).contains(&"E0072"), "{:?}", r.diagnostics);
12148    }
12149
12150    #[test]
12151    fn impl_omitting_a_self_receiver_the_trait_declares_reports_e0072() {
12152        // The mirror of `impl_self_receiver_disagreeing_with_trait_reports_e0072`
12153        // and an independent bug: the trait declares `fn zero(self)`, the impl
12154        // omits the receiver. Before `has_self`, conformance compared the impl's
12155        // *declared* parameters (`selfless` ⇒ nothing to skip) against the
12156        // trait's (also none, `self` being implicit) and the return types, so
12157        // both agreed and the impl was accepted. A call then type-checked
12158        // against the trait's signature and lowered with a receiver argument
12159        // into an impl function of arity 0 — Cranelift "mismatched argument
12160        // count: got 1, expected 0", i.e. an ICE from `nova check`-clean source.
12161        let r = check_src(
12162            "record R { v: Int }\n\
12163             trait Zero { fn zero(self) -> Int }\n\
12164             impl Zero for R { fn zero() -> Int { 0 } }\n\
12165             fn main() { }",
12166        );
12167        assert!(error_codes(&r).contains(&"E0072"), "{:?}", r.diagnostics);
12168    }
12169
12170    #[test]
12171    fn trait_associated_function_result_type_is_the_qualifier() {
12172        // `trait_associated_function_on_concrete_type` above asserts only that
12173        // no diagnostic is produced, which is a weak claim: `Ty::Error` unifies
12174        // with anything, so a botched `Self` substitution can be silent. Pin the
12175        // resolved type instead. Deliberately *no* `let` annotation — an
12176        // annotation would have `check_block` overwrite the initializer's
12177        // inferred type with the annotation's, hiding exactly the bug this tests.
12178        let r = check_src(
12179            "trait Zero { fn zero() -> Self }\n\
12180             impl Zero for Int { fn zero() -> Int { 0 } }\n\
12181             fn main() { let n = Int::zero()\n println(\"${n}\") }",
12182        );
12183        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12184        let main_fn = r
12185            .module
12186            .functions
12187            .iter()
12188            .find(|f| f.name == "main")
12189            .expect("main compiled to a hir::Function");
12190        let n_ty = &main_fn
12191            .locals
12192            .iter()
12193            .find(|l| l.name == "n")
12194            .expect("`n` is a local in main")
12195            .ty;
12196        assert_eq!(*n_ty, Ty::Int, "`Int::zero()` should have type `Int`");
12197    }
12198
12199    #[test]
12200    fn trait_associated_function_through_bound_has_param_self_ty() {
12201        // `trait_associated_function_through_bound` above needs its `let n: Int`
12202        // annotation (nothing else can determine `T`), which makes "no
12203        // diagnostics" a weak signal. Pin the shape of the emitted call instead:
12204        // inside `make`, `T::zero()` must be a receiver-less `TraitCall` whose
12205        // `Self` is the *generic parameter* `Param(0)` — that is what lets
12206        // monomorphization resolve it per instantiation — and whose result type
12207        // is that same parameter, not a stray inference variable or `Error`.
12208        let r = check_src(
12209            "trait Zero { fn zero() -> Self }\n\
12210             impl Zero for Int { fn zero() -> Int { 0 } }\n\
12211             fn make<T: Zero>() -> T { T::zero() }\n\
12212             fn main() { let n: Int = make()\n println(\"${n}\") }",
12213        );
12214        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12215        let make_fn = r
12216            .module
12217            .functions
12218            .iter()
12219            .find(|f| f.name == "make")
12220            .expect("`make` compiled to a hir::Function");
12221        let call = child_exprs(&make_fn.body)
12222            .into_iter()
12223            .chain(std::iter::once(&make_fn.body))
12224            .find(|e| matches!(e.kind, hir::ExprKind::TraitCall { .. }))
12225            .expect("`make`'s body contains a TraitCall");
12226        assert_eq!(call.ty, Ty::Param(0), "`T::zero()` should have type `T`");
12227        let hir::ExprKind::TraitCall {
12228            self_ty, receiver, ..
12229        } = &call.kind
12230        else {
12231            unreachable!("filtered above")
12232        };
12233        assert_eq!(*self_ty, Ty::Param(0), "`Self` should be the parameter `T`");
12234        assert!(receiver.is_none(), "an associated function has no receiver");
12235    }
12236
12237    #[test]
12238    fn trait_associated_function_with_default_body_on_concrete_type() {
12239        // A receiver-less trait method may carry a default body, which becomes a
12240        // `Self`-generic function. `collect_traits` must not prepend the `self`
12241        // receiver to that function's signature — the only site exercising the
12242        // `has_self` conditional in the default-body signature loop.
12243        let r = check_src(
12244            "trait Zero { fn zero() -> Int { 5 } }\n\
12245             impl Zero for Int { }\n\
12246             fn main() { println(\"${Int::zero()}\") }",
12247        );
12248        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12249    }
12250
12251    #[test]
12252    fn trait_associated_function_substitutes_self_in_parameters() {
12253        // `Self` in a *parameter* must be substituted with the qualifier too,
12254        // not just in the return type: the expected type of `true` is `Int`.
12255        let r = check_src(
12256            "trait Merge { fn combine(a: Self, b: Self) -> Self }\n\
12257             impl Merge for Int { fn combine(a: Int, b: Int) -> Int { a + b } }\n\
12258             fn main() { println(\"${Int::combine(2, true)}\") }",
12259        );
12260        assert!(error_codes(&r).contains(&"E0010"), "{:?}", r.diagnostics);
12261    }
12262
12263    #[test]
12264    fn ambiguous_trait_associated_function_reports_e0015() {
12265        // Two traits in scope provide `Int::zero`; picking one would make
12266        // dispatch depend on impl declaration order.
12267        let r = check_src(
12268            "trait A { fn zero() -> Int }\n\
12269             trait B { fn zero() -> Int }\n\
12270             impl A for Int { fn zero() -> Int { 1 } }\n\
12271             impl B for Int { fn zero() -> Int { 2 } }\n\
12272             fn main() { println(\"${Int::zero()}\") }",
12273        );
12274        assert!(error_codes(&r).contains(&"E0015"), "{:?}", r.diagnostics);
12275    }
12276
12277    #[test]
12278    fn trait_associated_function_wrong_arity_reports_e0016() {
12279        let r = check_src(
12280            "trait Zero { fn zero() -> Int }\n\
12281             impl Zero for Int { fn zero() -> Int { 1 } }\n\
12282             fn main() { println(\"${Int::zero(9)}\") }",
12283        );
12284        assert!(error_codes(&r).contains(&"E0016"), "{:?}", r.diagnostics);
12285    }
12286
12287    #[test]
12288    fn trait_call_substitution_puts_self_before_the_methods_own_generics() {
12289        // The binding invariant of `emit_trait_call`: the substitution is
12290        // `[Self] ++ method_type_args`, because a trait method's Param space is
12291        // flat — `Self` is Param(0) and the method's own generics follow at
12292        // Param(1..), which is also how `hir::TraitMethod::bounds` is indexed.
12293        // Get the order wrong and a call silently dispatches to a wrongly
12294        // specialized function.
12295        //
12296        // Both shapes are checked because they used to be two functions that had
12297        // to build this vector identically by hand. A swapped order is only
12298        // observable when the method has generics of its own (with `generics ==
12299        // 0` the two orders coincide), so it needs a `<U>` method and a `Self`
12300        // return: under a swap, `u`'s expected type becomes `Self` and the
12301        // `Bool` argument stops type-checking.
12302        for (src, shape) in [
12303            (
12304                "trait Tag { fn tag<U>(self, u: U) -> Self }\n\
12305                 impl Tag for Int { fn tag<U>(self, u: U) -> Int { 1 } }\n\
12306                 fn main() { let n = 5\n let m: Int = n.tag(true)\n println(\"${m}\") }",
12307                "receiver",
12308            ),
12309            (
12310                "trait Make { fn make<U>(u: U) -> Self }\n\
12311                 impl Make for Int { fn make<U>(u: U) -> Int { 1 } }\n\
12312                 fn main() { let m: Int = Int::make(true)\n println(\"${m}\") }",
12313                "qualifier",
12314            ),
12315        ] {
12316            let r = check_src(src);
12317            assert!(
12318                r.diagnostics.is_empty(),
12319                "{shape} dispatch: {:?}",
12320                r.diagnostics
12321            );
12322            let main_fn = r
12323                .module
12324                .functions
12325                .iter()
12326                .find(|f| f.name == "main")
12327                .expect("main compiled to a hir::Function");
12328            let m_ty = &main_fn
12329                .locals
12330                .iter()
12331                .find(|l| l.name == "m")
12332                .expect("`m` is a local in main")
12333                .ty;
12334            // `-> Self` must resolve through Param(0), i.e. to the qualifier /
12335            // receiver type, not to the method's own generic.
12336            assert_eq!(*m_ty, Ty::Int, "{shape} dispatch: `Self` return");
12337        }
12338    }
12339
12340    #[test]
12341    fn every_trait_call_agrees_with_its_callees_has_self() {
12342        // `emit_trait_call` is the sole emitter of `TraitCall`, and it refuses to
12343        // build one whose receiver-ness disagrees with the trait method's
12344        // `has_self` — a mismatch in either direction lowers a `self` argument
12345        // into a callee with no slot for it, or omits one from a callee that has
12346        // it, both of which Cranelift rejects as "mismatched argument count".
12347        // The two guard arms are unreachable from source today (see the emitter's
12348        // comment), so instead of a test that cannot fail, assert the invariant
12349        // they protect over every call a real program produces: any future
12350        // emitter path that got this wrong would break here even if the guard
12351        // itself were deleted.
12352        //
12353        // `check_src` merges the implicit prelude, so `module.functions` also
12354        // carries all of std/core — every `Ord`/`Eq`/`Display` call in it is
12355        // covered for free, on top of the receiver and receiver-less trait calls
12356        // the source below adds.
12357        let r = check_src(
12358            "trait Zero { fn zero() -> Self }\n\
12359             trait Widen { fn widen(self, k: Int) -> Int }\n\
12360             impl Zero for Int { fn zero() -> Int { 0 } }\n\
12361             impl Widen for Int { fn widen(self, k: Int) -> Int { k } }\n\
12362             fn make<T: Zero>() -> T { T::zero() }\n\
12363             fn main() {\n\
12364                 let n: Int = make()\n\
12365                 let m: Int = Int::zero()\n\
12366                 println(\"${n.widen(1)}${m}\")\n\
12367             }",
12368        );
12369        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12370
12371        fn walk<'e>(e: &'e hir::Expr, out: &mut Vec<&'e hir::Expr>) {
12372            out.push(e);
12373            for c in child_exprs(e) {
12374                walk(c, out);
12375            }
12376        }
12377        let mut seen_with_receiver = 0;
12378        let mut seen_without_receiver = 0;
12379        for f in &r.module.functions {
12380            let mut exprs = Vec::new();
12381            walk(&f.body, &mut exprs);
12382            for e in exprs {
12383                let hir::ExprKind::TraitCall {
12384                    trait_id,
12385                    method,
12386                    receiver,
12387                    ..
12388                } = &e.kind
12389                else {
12390                    continue;
12391                };
12392                let tm = r
12393                    .module
12394                    .traits
12395                    .iter()
12396                    .find(|t| t.def_id == *trait_id)
12397                    .and_then(|t| t.methods.get(*method as usize))
12398                    .expect("a TraitCall names an existing trait method");
12399                assert_eq!(
12400                    receiver.is_some(),
12401                    tm.has_self,
12402                    "`{}` (has_self = {}) got receiver.is_some() = {} in `{}`",
12403                    tm.name,
12404                    tm.has_self,
12405                    receiver.is_some(),
12406                    f.name
12407                );
12408                if receiver.is_some() {
12409                    seen_with_receiver += 1;
12410                } else {
12411                    seen_without_receiver += 1;
12412                }
12413            }
12414        }
12415        // Both flavors must actually be present, or the loop above proves nothing.
12416        assert!(seen_with_receiver > 0, "no receiver trait call was emitted");
12417        assert!(
12418            seen_without_receiver > 0,
12419            "no receiver-less trait call was emitted"
12420        );
12421    }
12422
12423    #[test]
12424    fn arity_errors_name_the_callee_uniformly() {
12425        // E0016 is raised from five places — a free function (`check_call`), an
12426        // inherent method and an inherent associated function
12427        // (`emit_inherent_call` / `emit_assoc_call`), and a trait method called
12428        // both ways (`emit_trait_call`) — and the wording had drifted three ways
12429        // across them: one said "method `f` takes …", one "`f` takes …", and the
12430        // inherent-method one "method takes …" with no name at all. The same
12431        // mistake must read the same way whichever dispatch path found the
12432        // callee, so pin the shared phrasing rather than only the code. Two of
12433        // these sites had no arity test at all before this, which is how the
12434        // drift went unnoticed.
12435        //
12436        // What is unified is the phrasing, not the spelling of the name: an impl
12437        // method's `Def` name is impl-qualified (`P.of`), while a trait dispatch
12438        // site only ever has the trait's declared method name — `Self` may still
12439        // be a generic parameter there, so there is no impl to qualify with.
12440        // Each path therefore names the callee with the only name it has.
12441        let cases: [(&str, &str); 5] = [
12442            // Free function.
12443            (
12444                "fn f(a: Int) -> Int { a }\nfn main() { let x = f(1, 2) }",
12445                "`f` takes 1 argument(s) but 2 were supplied",
12446            ),
12447            // Inherent associated function (no receiver).
12448            (
12449                "record P { v: Int }\n\
12450                 impl P { fn of(x: Int) -> P { P { v: x } } }\n\
12451                 fn main() { let p = P::of()\n println(\"${p.v}\") }",
12452                "`P.of` takes 1 argument(s) but 0 were supplied",
12453            ),
12454            // Inherent method (receiver at sig.params[0], so the expected count
12455            // is one less than the signature's length).
12456            (
12457                "record P { v: Int }\n\
12458                 impl P { fn bump(self, k: Int) -> Int { k } }\n\
12459                 fn main() { let p = P { v: 1 }\n println(\"${p.bump()}\") }",
12460                "`P.bump` takes 1 argument(s) but 0 were supplied",
12461            ),
12462            // Trait method through a receiver.
12463            (
12464                "trait Widen { fn widen(self, k: Int) -> Int }\n\
12465                 impl Widen for Int { fn widen(self, k: Int) -> Int { k } }\n\
12466                 fn main() { let n = 5\n println(\"${n.widen()}\") }",
12467                "`widen` takes 1 argument(s) but 0 were supplied",
12468            ),
12469            // Trait associated function through a qualifier — the same emitter as
12470            // the case above, reached with no receiver.
12471            (
12472                "trait Zero { fn zero() -> Int }\n\
12473                 impl Zero for Int { fn zero() -> Int { 1 } }\n\
12474                 fn main() { println(\"${Int::zero(9)}\") }",
12475                "`zero` takes 0 argument(s) but 1 were supplied",
12476            ),
12477        ];
12478        for (src, expected) in cases {
12479            let r = check_src(src);
12480            let msgs: Vec<&str> = r
12481                .diagnostics
12482                .iter()
12483                .filter(|d| d.code == "E0016")
12484                .map(|d| d.message.as_str())
12485                .collect();
12486            assert_eq!(msgs, vec![expected], "for source:\n{src}");
12487        }
12488    }
12489
12490    #[test]
12491    fn generic_param_associated_function_without_a_bound_reports_e0001() {
12492        // `T::zero()` where none of `T`'s bounds declares `zero`. Must be a
12493        // targeted diagnostic, not the misleading "module-qualified paths are
12494        // not supported yet" the pre-existing fall-through produced.
12495        let r = check_src(
12496            "trait Show { fn fmt(self) -> String }\n\
12497             fn make<T: Show>() -> T { T::zero() }\n\
12498             fn main() { println(\"hi\") }",
12499        );
12500        assert!(error_codes(&r).contains(&"E0001"), "{:?}", r.diagnostics);
12501        assert!(
12502            !error_codes(&r).contains(&"E0900"),
12503            "should not blame unsupported module paths: {:?}",
12504            r.diagnostics
12505        );
12506    }
12507
12508    #[test]
12509    fn generic_param_bound_declaring_it_as_a_method_reports_that_reason() {
12510        // `T::fmt(x)` where `T`'s bound *does* declare `fmt` — just not as an
12511        // associated function: as a method with a `self` receiver. The
12512        // sibling test above's final clause ("none of its bounds declares
12513        // one") would be false here, since a bound does declare `fmt`.
12514        let r = check_src(
12515            "trait Show { fn fmt(self) -> String }\n\
12516             fn make<T: Show>(x: T) -> String { T::fmt(x) }\n\
12517             fn main() { }",
12518        );
12519        assert!(error_codes(&r).contains(&"E0001"), "{:?}", r.diagnostics);
12520        assert!(
12521            r.diagnostics
12522                .iter()
12523                .any(|d| d.message.contains("as a method with a `self` receiver")),
12524            "expected the message to name the real reason: {:?}",
12525            r.diagnostics
12526        );
12527        assert!(
12528            !r.diagnostics
12529                .iter()
12530                .any(|d| d.message.contains("none of its bounds declares one")),
12531            "should not claim the bound declares nothing: {:?}",
12532            r.diagnostics
12533        );
12534    }
12535
12536    #[test]
12537    fn inherent_associated_function_on_a_primitive_resolves() {
12538        // The other half of teaching the two-segment path gate about primitive
12539        // type names: `impl Int { … }` records an inherent impl under the `Int`
12540        // head (and an *instance* method on it already dispatched fine), but
12541        // `Int::zero()` could not resolve while lookup was keyed on a type
12542        // `DefId`, which no primitive has. Keeping only the trait half working
12543        // would leave the two kinds of associated function inconsistent.
12544        let r = check_src(
12545            "impl Int { fn three() -> Int { 3 } }\n\
12546             fn main() { let n = Int::three()\n println(\"${n}\") }",
12547        );
12548        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12549        let main_fn = r
12550            .module
12551            .functions
12552            .iter()
12553            .find(|f| f.name == "main")
12554            .expect("main compiled to a hir::Function");
12555        let n_ty = &main_fn
12556            .locals
12557            .iter()
12558            .find(|l| l.name == "n")
12559            .expect("`n` is a local in main")
12560            .ty;
12561        assert_eq!(*n_ty, Ty::Int);
12562    }
12563
12564    #[test]
12565    fn unknown_associated_function_on_a_primitive_reports_e0001() {
12566        // A primitive type name is invisible to `Definitions::resolve_type`, so
12567        // before this task every `Primitive::f()` call — resolvable or not — fell
12568        // through to `check_path` and reported E0900.
12569        let r = check_src("fn main() { println(\"${Int::nope()}\") }");
12570        assert!(error_codes(&r).contains(&"E0001"), "{:?}", r.diagnostics);
12571        assert!(
12572            !error_codes(&r).contains(&"E0900"),
12573            "should not blame unsupported module paths: {:?}",
12574            r.diagnostics
12575        );
12576    }
12577
12578    #[test]
12579    fn receiverless_trait_method_called_on_instance_reports_e0014() {
12580        // The `selfless_method_called_on_instance_reports_e0014` case for a
12581        // *trait* method rather than an inherent one. This impl conforms — the
12582        // trait declares no receiver and neither does the impl — so conformance
12583        // cannot catch it; resolution must. Before `has_self`, `resolve_method_on`
12584        // matched the trait method by name alone, `emit_trait_call` compared the
12585        // argument list against `tm.params` (which never holds `self`) and so saw
12586        // no arity error, and MIR prepended the receiver to a callee of arity 0:
12587        // the same Cranelift ICE, again from source `nova check` accepted.
12588        let r = check_src(
12589            "record P { v: Int }\n\
12590             trait Zero { fn zero() -> Int }\n\
12591             impl Zero for P { fn zero() -> Int { 42 } }\n\
12592             fn main() { let p = P { v: 0 }\n println(\"${p.zero()}\") }",
12593        );
12594        assert!(error_codes(&r).contains(&"E0014"), "{:?}", r.diagnostics);
12595    }
12596
12597    // === Supertraits (`trait B: A`) ===
12598
12599    #[test]
12600    fn impl_of_subtrait_without_supertrait_reports_e0072() {
12601        let r = check_src(
12602            "trait A { fn a(self) -> Int }\n\
12603             trait B: A { fn b(self) -> Int }\n\
12604             record R { v: Int }\n\
12605             impl B for R { fn b(self) -> Int { 2 } }\n\
12606             fn main() { }",
12607        );
12608        assert!(error_codes(&r).contains(&"E0072"), "{:?}", r.diagnostics);
12609    }
12610
12611    #[test]
12612    fn impl_of_subtrait_with_supertrait_typechecks() {
12613        let r = check_src(
12614            "trait A { fn a(self) -> Int }\n\
12615             trait B: A { fn b(self) -> Int }\n\
12616             record R { v: Int }\n\
12617             impl A for R { fn a(self) -> Int { 1 } }\n\
12618             impl B for R { fn b(self) -> Int { 2 } }\n\
12619             fn main() { }",
12620        );
12621        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12622    }
12623
12624    #[test]
12625    fn supertrait_method_callable_through_subtrait_bound() {
12626        // `T: B` implies `T: A`, so `a()` is callable.
12627        let r = check_src(
12628            "trait A { fn a(self) -> Int }\n\
12629             trait B: A { fn b(self) -> Int }\n\
12630             record R { v: Int }\n\
12631             impl A for R { fn a(self) -> Int { 1 } }\n\
12632             impl B for R { fn b(self) -> Int { 2 } }\n\
12633             fn sum<T: B>(x: T) -> Int { x.a() + x.b() }\n\
12634             fn main() { println(\"${sum(R { v: 0 })}\") }",
12635        );
12636        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12637    }
12638
12639    #[test]
12640    fn supertrait_impl_may_be_declared_after_the_subtrait_impl() {
12641        // The declaration-order guard that decides *where* the
12642        // supertrait-satisfaction check may live. Running it inside
12643        // `check_impl_conformance` (called from `collect_impls` *before* the impl
12644        // being checked is pushed) only sees impls from earlier items, so this
12645        // source — `impl B` first, `impl A` second — would report a bogus E0072.
12646        // Nova has no forward-declaration rule for impls; the check therefore has
12647        // to be a post-collection pass, like `check_impl_coherence`.
12648        let r = check_src(
12649            "trait A { fn a(self) -> Int }\n\
12650             trait B: A { fn b(self) -> Int }\n\
12651             record R { v: Int }\n\
12652             impl B for R { fn b(self) -> Int { 2 } }\n\
12653             impl A for R { fn a(self) -> Int { 1 } }\n\
12654             fn main() { }",
12655        );
12656        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12657    }
12658
12659    #[test]
12660    fn subtrait_bound_resolves_the_supertrait_method_to_the_supertrait() {
12661        // `supertrait_method_callable_through_subtrait_bound` only asserts the
12662        // absence of diagnostics, which is weak: `Ty::Error` unifies with
12663        // anything, so a mis-resolved call can be silent. Pin the emitted HIR
12664        // instead — `x.a()` inside `sum` must be a `TraitCall` naming trait `A`
12665        // (not `B`) with `Self` bound to the generic parameter `Param(0)`.
12666        let r = check_src(
12667            "trait A { fn a(self) -> Int }\n\
12668             trait B: A { fn b(self) -> Int }\n\
12669             record R { v: Int }\n\
12670             impl A for R { fn a(self) -> Int { 1 } }\n\
12671             impl B for R { fn b(self) -> Int { 2 } }\n\
12672             fn only_a<T: B>(x: T) -> Int { x.a() }\n\
12673             fn main() { println(\"${only_a(R { v: 0 })}\") }",
12674        );
12675        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12676        let a_id = r
12677            .module
12678            .traits
12679            .iter()
12680            .find(|t| t.name == "A")
12681            .expect("trait A collected")
12682            .def_id;
12683        let only_a = r
12684            .module
12685            .functions
12686            .iter()
12687            .find(|f| f.name == "only_a")
12688            .expect("`only_a` compiled to a hir::Function");
12689        assert_eq!(
12690            only_a.bounds,
12691            vec![vec![
12692                r.module
12693                    .traits
12694                    .iter()
12695                    .find(|t| t.name == "B")
12696                    .expect("trait B collected")
12697                    .def_id,
12698                a_id
12699            ]],
12700            "the bound `T: B` should expand to `[B, A]`"
12701        );
12702        let call = child_exprs(&only_a.body)
12703            .into_iter()
12704            .chain(std::iter::once(&only_a.body))
12705            .find(|e| matches!(e.kind, hir::ExprKind::TraitCall { .. }))
12706            .expect("`only_a`'s body contains a TraitCall");
12707        let hir::ExprKind::TraitCall {
12708            trait_id, self_ty, ..
12709        } = &call.kind
12710        else {
12711            unreachable!("filtered above")
12712        };
12713        assert_eq!(*trait_id, a_id, "`x.a()` should dispatch through trait `A`");
12714        assert_eq!(*self_ty, Ty::Param(0), "`Self` should be the parameter `T`");
12715        assert_eq!(call.ty, Ty::Int);
12716    }
12717
12718    #[test]
12719    fn subtrait_redeclaring_supertrait_method_name_is_ambiguous() {
12720        // `B: A` re-declaring a method name `A` already declares makes a call
12721        // through a `T: B` bound ambiguous: the bound expands to `[B, A]`,
12722        // and both traits now provide `same`, so `resolve_method_on` finds
12723        // two candidate providers. This is a newly reachable shape of the
12724        // pre-existing "ambiguous method call" diagnostic (Rust reports the
12725        // analogous conflict too, as E0034 "multiple applicable items in
12726        // scope"). Pin the behavior, don't try to make it resolve to `B`.
12727        let r = check_src(
12728            "trait A { fn same(self) -> Int }\n\
12729             trait B: A { fn same(self) -> Int }\n\
12730             record R { v: Int }\n\
12731             impl A for R { fn same(self) -> Int { 1 } }\n\
12732             impl B for R { fn same(self) -> Int { 2 } }\n\
12733             fn call_same<T: B>(x: T) -> Int { x.same() }\n\
12734             fn main() { println(\"${call_same(R { v: 0 })}\") }",
12735        );
12736        assert!(error_codes(&r).contains(&"E0015"), "{:?}", r.diagnostics);
12737    }
12738
12739    #[test]
12740    fn supertrait_method_callable_from_a_default_body() {
12741        // A default body's `Self` is bounded by the enclosing trait alone; if that
12742        // bound is not expanded, `self.a()` in `B`'s default body cannot resolve.
12743        let r = check_src(
12744            "trait A { fn a(self) -> Int }\n\
12745             trait B: A { fn b(self) -> Int { self.a() + 1 } }\n\
12746             record R { v: Int }\n\
12747             impl A for R { fn a(self) -> Int { 1 } }\n\
12748             impl B for R { }\n\
12749             fn main() { let x = R { v: 0 }\n println(\"${x.b()}\") }",
12750        );
12751        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12752        let b_body = r
12753            .module
12754            .functions
12755            .iter()
12756            .find(|f| f.name == "B::b$default")
12757            .expect("`B::b`'s default body compiled to a hir::Function");
12758        let a_id = r
12759            .module
12760            .traits
12761            .iter()
12762            .find(|t| t.name == "A")
12763            .expect("trait A collected")
12764            .def_id;
12765        // `child_exprs` is one level deep, and the call sits under a `Binary`
12766        // under the block, so walk the whole tree.
12767        fn calls_trait(e: &hir::Expr, tid: DefId) -> bool {
12768            if matches!(&e.kind, hir::ExprKind::TraitCall { trait_id, .. } if *trait_id == tid) {
12769                return true;
12770            }
12771            child_exprs(e).into_iter().any(|c| calls_trait(c, tid))
12772        }
12773        assert!(
12774            calls_trait(&b_body.body, a_id),
12775            "`self.a()` should resolve to a TraitCall on `A`"
12776        );
12777        assert_eq!(
12778            b_body.bounds,
12779            vec![vec![
12780                r.module
12781                    .traits
12782                    .iter()
12783                    .find(|t| t.name == "B")
12784                    .expect("trait B collected")
12785                    .def_id,
12786                a_id
12787            ]],
12788            "the default body's `Self` should be bounded by `B` and its supertrait `A`"
12789        );
12790    }
12791
12792    #[test]
12793    fn unknown_supertrait_reports_e0001() {
12794        let r = check_src(
12795            "trait B: Nope { fn b(self) -> Int }\n\
12796             fn main() { }",
12797        );
12798        assert!(error_codes(&r).contains(&"E0001"), "{:?}", r.diagnostics);
12799    }
12800
12801    #[test]
12802    fn where_clause_on_trait_declaration_reports_e0900() {
12803        // `trait B where Self: A` is a second spelling of a supertrait
12804        // requirement, distinct from the `trait B: A` shorthand that
12805        // `collect_supertraits` resolves into the graph. Wiring this spelling
12806        // in too is a feature addition, not a fix, so it must be rejected
12807        // outright — otherwise declaring a supertrait this way would once
12808        // again mean nothing (silently accepted, no `A` impl required),
12809        // exactly the bug this feature exists to close for `trait B: A`.
12810        let r = check_src(
12811            "trait A { fn a(self) -> Int }\n\
12812             trait B where Self: A { fn b(self) -> Int }\n\
12813             record R { v: Int }\n\
12814             impl B for R { fn b(self) -> Int { 2 } }\n\
12815             fn main() { }",
12816        );
12817        assert!(error_codes(&r).contains(&"E0900"), "{:?}", r.diagnostics);
12818    }
12819
12820    #[test]
12821    fn cyclic_supertraits_do_not_hang() {
12822        // `trait A: B` / `trait B: A` is nonsense, but the compiler must
12823        // terminate on it rather than loop forever expanding bounds. No
12824        // diagnostic is required — only that this test returns.
12825        let r = check_src(
12826            "trait A: B { fn a(self) -> Int }\n\
12827             trait B: A { fn b(self) -> Int }\n\
12828             record R { v: Int }\n\
12829             impl A for R { fn a(self) -> Int { 1 } }\n\
12830             impl B for R { fn b(self) -> Int { 2 } }\n\
12831             fn both<T: A>(x: T) -> Int { x.a() + x.b() }\n\
12832             fn main() { println(\"${both(R { v: 0 })}\") }",
12833        );
12834        // The cycle is satisfiable (each impl exists), so nothing is reported;
12835        // the point of the test is that we get here at all.
12836        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12837    }
12838
12839    #[test]
12840    fn diamond_supertraits_are_not_ambiguous() {
12841        // `C: A + B` with `A: X` and `B: X` reaches `X` twice. A duplicated trait
12842        // id in the expanded bound set reads as two method providers, which
12843        // `resolve_method_on` reports as a false E0015 "ambiguous method call" —
12844        // the same trap `resolve_bounds`' dedup exists for.
12845        let r = check_src(
12846            "trait X { fn x(self) -> Int }\n\
12847             trait A: X { fn a(self) -> Int }\n\
12848             trait B: X { fn b(self) -> Int }\n\
12849             trait C: A + B { fn c(self) -> Int }\n\
12850             record R { v: Int }\n\
12851             impl X for R { fn x(self) -> Int { 1 } }\n\
12852             impl A for R { fn a(self) -> Int { 2 } }\n\
12853             impl B for R { fn b(self) -> Int { 3 } }\n\
12854             impl C for R { fn c(self) -> Int { 4 } }\n\
12855             fn only_x<T: C>(v: T) -> Int { v.x() }\n\
12856             fn main() { println(\"${only_x(R { v: 0 })}\") }",
12857        );
12858        // A diamond must not read as two providers of `x` (a false E0015);
12859        // `diagnostics.is_empty()` below already covers that and more, so
12860        // there is no separate `E0015`-specific assertion here.
12861        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12862        let only_x = r
12863            .module
12864            .functions
12865            .iter()
12866            .find(|f| f.name == "only_x")
12867            .expect("`only_x` compiled to a hir::Function");
12868        let bounds = only_x.bounds.first().expect("`T` has bounds");
12869        assert_eq!(
12870            bounds.len(),
12871            4,
12872            "expected exactly `[C, A, B, X]` with no repeat, got {bounds:?}"
12873        );
12874    }
12875
12876    #[test]
12877    fn subtrait_bound_expansion_does_not_break_conformance_bound_sets() {
12878        // `check_impl_conformance` compares each method generic's bound set
12879        // against the trait's. Both sides must be expanded consistently, and
12880        // `M` is declared *before* `B` on purpose: an expansion that reads a
12881        // partially-built trait table would leave `M::m`'s `U: B` unexpanded
12882        // while the impl's is expanded, producing a bogus E0072.
12883        let r = check_src(
12884            "trait M { fn m<U: B>(self, u: U) -> Int }\n\
12885             trait A { fn a(self) -> Int }\n\
12886             trait B: A { fn b(self) -> Int }\n\
12887             record R { v: Int }\n\
12888             record S { w: Int }\n\
12889             impl A for S { fn a(self) -> Int { 1 } }\n\
12890             impl B for S { fn b(self) -> Int { 2 } }\n\
12891             impl M for R { fn m<U: B>(self, u: U) -> Int { u.a() + u.b() } }\n\
12892             fn main() {\n\
12893                 let r = R { v: 0 }\n\
12894                 let s = S { w: 0 }\n\
12895                 println(\"${r.m(s)}\")\n\
12896             }",
12897        );
12898        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12899    }
12900
12901    #[test]
12902    fn impl_method_bound_redundantly_spelling_supertrait_typechecks() {
12903        // Deliberate behavior change: the trait declares `U: B`, which expands
12904        // to `[B, A]`. An impl is now free to spell that same set out in full
12905        // (`U: B + A`) instead of just `U: B` — before supertrait expansion
12906        // existed, `same_bound_set` would have compared the trait's
12907        // unexpanded `[B]` against the impl's `[B, A]` and reported E0072 for
12908        // a "mismatched" bound that is in fact redundant (`B + A` is exactly
12909        // `B` when `B: A`). Nothing pinned this previously, so it could
12910        // silently regress.
12911        let r = check_src(
12912            "trait A { fn a(self) -> Int }\n\
12913             trait B: A { fn b(self) -> Int }\n\
12914             trait M { fn m<U: B>(self, u: U) -> Int }\n\
12915             record R { v: Int }\n\
12916             record S { w: Int }\n\
12917             impl A for S { fn a(self) -> Int { 1 } }\n\
12918             impl B for S { fn b(self) -> Int { 2 } }\n\
12919             impl M for R { fn m<U: B + A>(self, u: U) -> Int { u.a() + u.b() } }\n\
12920             fn main() {\n\
12921                 let r = R { v: 0 }\n\
12922                 let s = S { w: 0 }\n\
12923                 println(\"${r.m(s)}\")\n\
12924             }",
12925        );
12926        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12927    }
12928
12929    #[test]
12930    fn supertrait_method_callable_through_an_impl_generic_bound() {
12931        // The impl's own generic bound (`impl<T: B> …`) must be expanded too, so
12932        // a method body can call the supertrait method on `T`.
12933        let r = check_src(
12934            "trait A { fn a(self) -> Int }\n\
12935             trait B: A { fn b(self) -> Int }\n\
12936             record R { v: Int }\n\
12937             record W<T> { inner: T }\n\
12938             impl A for R { fn a(self) -> Int { 1 } }\n\
12939             impl B for R { fn b(self) -> Int { 2 } }\n\
12940             impl<T: B> W<T> { fn total(self) -> Int { self.inner.a() + self.inner.b() } }\n\
12941             fn main() {\n\
12942                 let w = W { inner: R { v: 0 } }\n\
12943                 println(\"${w.total()}\")\n\
12944             }",
12945        );
12946        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12947    }
12948
12949    #[test]
12950    fn generic_impl_of_subtrait_without_a_covering_supertrait_impl_reports_e0072() {
12951        // `impl<T: B> B for W<T>` needs an `A` impl covering *every* `W<T>`;
12952        // an `impl A for W<R>` covers only one instance, so the requirement is
12953        // unmet and must be reported at the impl, not silently at some later
12954        // instantiation.
12955        let r = check_src(
12956            "trait A { fn a(self) -> Int }\n\
12957             trait B: A { fn b(self) -> Int }\n\
12958             record R { v: Int }\n\
12959             record W<T> { inner: T }\n\
12960             impl A for R { fn a(self) -> Int { 1 } }\n\
12961             impl B for R { fn b(self) -> Int { 2 } }\n\
12962             impl A for W<R> { fn a(self) -> Int { 3 } }\n\
12963             impl<T: B> B for W<T> { fn b(self) -> Int { 4 } }\n\
12964             fn main() { }",
12965        );
12966        assert!(error_codes(&r).contains(&"E0072"), "{:?}", r.diagnostics);
12967    }
12968
12969    #[test]
12970    fn generic_impl_of_subtrait_with_a_generic_supertrait_impl_typechecks() {
12971        // The companion of the test above: an `impl<T: B> A for W<T>` covers the
12972        // whole family, so `impl<T: B> B for W<T>` is satisfied.
12973        let r = check_src(
12974            "trait A { fn a(self) -> Int }\n\
12975             trait B: A { fn b(self) -> Int }\n\
12976             record R { v: Int }\n\
12977             record W<T> { inner: T }\n\
12978             impl A for R { fn a(self) -> Int { 1 } }\n\
12979             impl B for R { fn b(self) -> Int { 2 } }\n\
12980             impl<T: B> A for W<T> { fn a(self) -> Int { self.inner.a() } }\n\
12981             impl<T: B> B for W<T> { fn b(self) -> Int { self.inner.b() } }\n\
12982             fn main() {\n\
12983                 let w = W { inner: R { v: 0 } }\n\
12984                 println(\"${w.b()}\")\n\
12985             }",
12986        );
12987        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
12988    }
12989
12990    #[test]
12991    fn hir_trait_def_records_resolved_supertraits() {
12992        let r = check_src(
12993            "trait A { fn a(self) -> Int }\n\
12994             trait B: A { fn b(self) -> Int }\n\
12995             record R { v: Int }\n\
12996             impl A for R { fn a(self) -> Int { 1 } }\n\
12997             impl B for R { fn b(self) -> Int { 2 } }\n\
12998             fn main() { }",
12999        );
13000        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
13001        let a = r
13002            .module
13003            .traits
13004            .iter()
13005            .find(|t| t.name == "A")
13006            .expect("trait A collected");
13007        let b = r
13008            .module
13009            .traits
13010            .iter()
13011            .find(|t| t.name == "B")
13012            .expect("trait B collected");
13013        assert_eq!(b.supertraits, vec![a.def_id]);
13014        assert!(a.supertraits.is_empty());
13015    }
13016
13017    #[test]
13018    fn a_trait_records_its_associated_types_in_order() {
13019        let r = check_src("trait Pair { type A\n type B\n fn get(self) -> Int }\nfn main() { }");
13020        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
13021        let t = r
13022            .module
13023            .traits
13024            .iter()
13025            .find(|t| t.name == "Pair")
13026            .expect("trait Pair collected");
13027        let names: Vec<&str> = t.assoc_types.iter().map(|(n, _)| n.as_str()).collect();
13028        // Order matters: it is declaration order, and two associated types is
13029        // the case that catches an implementation assuming there is only one.
13030        assert_eq!(names, ["A", "B"]);
13031        // Each gets its own DefId, so `display_ty` can name it.
13032        assert_ne!(t.assoc_types[0].1, t.assoc_types[1].1);
13033    }
13034
13035    #[test]
13036    fn a_bound_on_an_associated_type_reports_e0900() {
13037        // Rejected rather than silently dropped — the same rule this project
13038        // applies to record and sum type-parameter bounds, because a bound
13039        // that enforces nothing is worse than no bound.
13040        let r = check_src("trait It { type Item: Display }\nfn main() { }");
13041        let d = r
13042            .diagnostics
13043            .iter()
13044            .find(|d| d.code == "E0900")
13045            .expect("E0900 for a bound on an associated type");
13046        assert!(
13047            d.message.contains("associated type"),
13048            "message should name the construct: {}",
13049            d.message
13050        );
13051    }
13052
13053    #[test]
13054    fn a_projection_on_a_generic_parameter_resolves() {
13055        // `I::Item` where I is a generic parameter bounded by a trait that
13056        // declares `Item`. No impl is needed: the projection stays abstract.
13057        let r = check_src(
13058            "trait It { type Item\n fn get(self) -> Int }\n\
13059             fn first<I: It>(x: I) -> I::Item { panic(\"unreachable\") }\n\
13060             fn main() { }",
13061        );
13062        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
13063    }
13064
13065    #[test]
13066    fn a_module_qualified_type_path_still_reports_e0900() {
13067        // The two-segment path case now has a second meaning; the original
13068        // one must survive with its original message.
13069        let r = check_src("fn f(x: some_mod::Thing) -> Int { 1 }\nfn main() { }");
13070        let d = r
13071            .diagnostics
13072            .iter()
13073            .find(|d| d.code == "E0900")
13074            .expect("E0900 for a module-qualified type path");
13075        assert!(
13076            d.message.contains("module-qualified type paths"),
13077            "original message preserved: {}",
13078            d.message
13079        );
13080    }
13081
13082    #[test]
13083    fn rejected_type_arguments_and_an_undeclared_name_do_not_contradict_each_other() {
13084        // `I::Nope<Int>` combines both problems the projection branch can
13085        // report: type arguments on a projection (rejected, E0012) and an
13086        // undeclared associated-type name (E0001). Both fire — the E0012
13087        // guard runs before resolve_projection, deliberately, so args are
13088        // rejected whether or not the name even resolves — and neither may
13089        // claim something the other disproves: E0012 must not call `Nope`
13090        // "an associated type" in the same breath E0001 says it is not one.
13091        let r = check_src(
13092            "trait It { type Item\n fn get(self) -> Int }\n\
13093             fn f<I: It>(x: I) -> I::Nope<Int> { panic(\"unreachable\") }\n\
13094             fn main() { }",
13095        );
13096        let e0012 = r
13097            .diagnostics
13098            .iter()
13099            .find(|d| d.code == "E0012")
13100            .expect("E0012 for the rejected type arguments");
13101        assert!(
13102            !e0012.message.contains("associated type"),
13103            "must not call `Nope` an associated type before E0001 says it is not one: {}",
13104            e0012.message
13105        );
13106        let e0001 = r
13107            .diagnostics
13108            .iter()
13109            .find(|d| d.code == "E0001")
13110            .expect("E0001 for the undeclared associated type name");
13111        assert!(
13112            e0001.message.contains("no associated type") && e0001.message.contains("Nope"),
13113            "{}",
13114            e0001.message
13115        );
13116    }
13117
13118    #[test]
13119    fn a_projection_naming_an_undeclared_associated_type_is_an_error() {
13120        // Not just "some diagnostic fired": a lazy implementation that
13121        // reused `self.unsupported(span, "module-qualified type paths")` for
13122        // the not-found case (E0900, "... are not supported yet") would also
13123        // satisfy a bare `!diagnostics.is_empty()` — so this pins the actual
13124        // code and that the message names the real construct.
13125        let r = check_src(
13126            "trait It { type Item\n fn get(self) -> Int }\n\
13127             fn f<I: It>(x: I) -> I::Nope { panic(\"unreachable\") }\n\
13128             fn main() { }",
13129        );
13130        let d = r
13131            .diagnostics
13132            .iter()
13133            .find(|d| d.code == "E0001")
13134            .expect("E0001 for a projection naming an undeclared associated type");
13135        assert!(
13136            d.message.contains("associated type"),
13137            "message should name the construct: {}",
13138            d.message
13139        );
13140    }
13141
13142    #[test]
13143    fn two_traits_with_the_same_associated_type_name_get_distinct_defids() {
13144        // The `trait_def == def_id` guard in `collect_traits` (where
13145        // `assoc_type_ids` is filtered) is exactly what a single-trait test
13146        // cannot exercise: two unrelated traits both declaring `Item` must
13147        // not be cross-assigned to each other's associated type.
13148        let r = check_src(
13149            "trait A { type Item\n fn a(self) -> Int }\n\
13150             trait B { type Item\n fn b(self) -> Int }\n\
13151             fn main() { }",
13152        );
13153        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
13154        let a = r
13155            .module
13156            .traits
13157            .iter()
13158            .find(|t| t.name == "A")
13159            .expect("trait A collected");
13160        let b = r
13161            .module
13162            .traits
13163            .iter()
13164            .find(|t| t.name == "B")
13165            .expect("trait B collected");
13166        let a_item = a
13167            .assoc_types
13168            .iter()
13169            .find(|(n, _)| n.as_str() == "Item")
13170            .expect("A::Item")
13171            .1;
13172        let b_item = b
13173            .assoc_types
13174            .iter()
13175            .find(|(n, _)| n.as_str() == "Item")
13176            .expect("B::Item")
13177            .1;
13178        assert_ne!(a_item, b_item, "A::Item and B::Item must not share a DefId");
13179    }
13180
13181    #[test]
13182    fn a_projection_ambiguous_between_two_bounds_is_an_error() {
13183        // `expand_bounds` folds supertraits in *before* `convert_ty` runs, so
13184        // a parameter can legitimately see associated types declared by more
13185        // than one of its bounds — this is the case that makes that
13186        // reachable, not hypothetical. Two unrelated traits, both declaring
13187        // `Item`, both bounding the same parameter: an implementation that
13188        // picked the first match (e.g. the first bounding trait, or always
13189        // `assoc_types[0]`) would silently resolve to the wrong one instead
13190        // of reporting the ambiguity.
13191        let r = check_src(
13192            "trait A { type Item\n fn a(self) -> Int }\n\
13193             trait B { type Item\n fn b(self) -> Int }\n\
13194             fn f<I: A + B>(x: I) -> I::Item { panic(\"unreachable\") }\n\
13195             fn main() { }",
13196        );
13197        assert!(
13198            r.diagnostics.iter().any(|d| d.code == "E0015"),
13199            "expected an ambiguity error: {:?}",
13200            r.diagnostics
13201        );
13202    }
13203
13204    #[test]
13205    fn a_projection_resolves_through_a_transitive_supertrait_bound() {
13206        // `expand_bounds` folds supertraits into a bound list before
13207        // `convert_ty` runs, so a bound on `B` (which requires `A`) also
13208        // carries `A` — `I::Item` must resolve against `A`'s declaration even
13209        // though `I` is only written `I: B`, not `I: A`. Desirable, but a
13210        // consequence of ordering rather than a decision made in
13211        // `resolve_projection` itself (see its comment in `convert_ty`); this
13212        // is the test that would fail if that ordering ever regressed.
13213        let r = check_src(
13214            "trait A { type Item\n fn a(self) -> Int }\n\
13215             trait B: A { fn b(self) -> Int }\n\
13216             fn f<I: B>(x: I) -> I::Item { panic(\"unreachable\") }\n\
13217             fn main() { }",
13218        );
13219        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
13220    }
13221
13222    #[test]
13223    fn a_projection_resolves_to_the_bounding_traits_own_associated_type_not_an_unrelated_one() {
13224        // Two traits both declare `Item`, but only ONE (`Keeper`) bounds `I`
13225        // here. A lookup that ignored *which* trait `I` is actually bounded
13226        // by — matching the name alone, anywhere in the program — could
13227        // silently resolve to `Other::Item` instead of `Keeper::Item`, and a
13228        // test that only asserts "compiles clean" would not notice: both
13229        // outcomes typecheck. So this inspects the compiled `Ty::Assoc`
13230        // itself and pins both halves of it — the projection's `on` (must be
13231        // `Param(0)`, i.e. `I`, never some other parameter or a `Var`) and
13232        // its `assoc` (must be `Keeper`'s `Item`, not `Other`'s or a fresh
13233        // one).
13234        let r = check_src(
13235            "trait Other { type Item\n fn o(self) -> Int }\n\
13236             trait Keeper { type Item\n fn get(self) -> Int }\n\
13237             fn first<I: Keeper>(x: I) -> I::Item { panic(\"unreachable\") }\n\
13238             fn main() { }",
13239        );
13240        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
13241        let keeper = r
13242            .module
13243            .traits
13244            .iter()
13245            .find(|t| t.name == "Keeper")
13246            .expect("trait Keeper collected");
13247        let keeper_item = keeper
13248            .assoc_types
13249            .iter()
13250            .find(|(n, _)| n.as_str() == "Item")
13251            .expect("Keeper::Item")
13252            .1;
13253        let f = r
13254            .module
13255            .functions
13256            .iter()
13257            .find(|f| f.name == "first")
13258            .expect("fn first collected");
13259        match &f.ret_ty {
13260            Ty::Assoc { on, assoc } => {
13261                assert_eq!(
13262                    on.as_ref(),
13263                    &Ty::Param(0),
13264                    "projected on `I` (Param 0), not a Var or some other parameter"
13265                );
13266                assert_eq!(
13267                    *assoc, keeper_item,
13268                    "must resolve to Keeper::Item, not Other::Item"
13269                );
13270            }
13271            other => panic!("expected `first`'s return type to be a Ty::Assoc, got {other:?}"),
13272        }
13273    }
13274
13275    #[test]
13276    fn a_projection_projects_onto_the_correct_parameter_when_there_are_several() {
13277        // `I` is the SECOND generic parameter here (`T` is first, unrelated
13278        // and unbounded). Every other projection test in this file happens
13279        // to project onto a lone parameter at index 0, so a resolver that
13280        // hardcoded `Ty::Param(0)` for the projection's `on` — instead of the
13281        // actual index `base` resolved to — would still pass all of them.
13282        let r = check_src(
13283            "trait It { type Item\n fn get(self) -> Int }\n\
13284             fn f<T, I: It>(x: T, y: I) -> I::Item { panic(\"unreachable\") }\n\
13285             fn main() { }",
13286        );
13287        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
13288        let it = r
13289            .module
13290            .traits
13291            .iter()
13292            .find(|t| t.name == "It")
13293            .expect("trait It collected");
13294        let item = it
13295            .assoc_types
13296            .iter()
13297            .find(|(n, _)| n.as_str() == "Item")
13298            .expect("It::Item")
13299            .1;
13300        let f = r
13301            .module
13302            .functions
13303            .iter()
13304            .find(|f| f.name == "f")
13305            .expect("fn f collected");
13306        assert_eq!(
13307            f.ret_ty,
13308            Ty::Assoc {
13309                on: Box::new(Ty::Param(1)),
13310                assoc: item,
13311            },
13312            "must project onto `I` at Param(1), not `T` at Param(0)"
13313        );
13314    }
13315
13316    #[test]
13317    fn a_projection_resolves_against_its_own_parameters_bounds_not_every_parameters() {
13318        // Two generic parameters, each bounded by a DIFFERENT trait, and the
13319        // projection is onto the FIRST one — the shape the existing
13320        // two-parameter test above does not cover, because there `T` (index
13321        // 0) is unbounded and only `I` (index 1) has any bounds at all. A
13322        // resolver that read the wrong bound list for `idx` — e.g.
13323        // concatenating every parameter's bounds instead of indexing just
13324        // this one's — would still pass every projection test in this file,
13325        // because in every one of them there is only a single non-empty
13326        // bound list for such a mistake to fall into. Here `A: Foo` (declares
13327        // `Item`, not `Other`) and `B: It` (declares `Other`); projecting
13328        // `A::Other` must fail, because `Other` is not on any bound of `A`,
13329        // even though it *is* on a bound of some parameter in scope.
13330        let r = check_src(
13331            "trait Foo { type Item\n fn f(self) -> Int }\n\
13332             trait It { type Other\n fn get(self) -> Int }\n\
13333             fn f<A: Foo, B: It>(x: A, y: B) -> A::Other { panic(\"x\") }\n\
13334             fn main() { }",
13335        );
13336        let d = r
13337            .diagnostics
13338            .iter()
13339            .find(|d| d.code == "E0001")
13340            .expect("E0001: `Other` is not on any bound of `A`");
13341        assert!(
13342            d.message.contains("Other") && d.message.contains("bound of `A`"),
13343            "names both the missing associated type and the parameter: {}",
13344            d.message
13345        );
13346    }
13347
13348    #[test]
13349    fn a_projection_with_type_arguments_reports_e0012() {
13350        // Nova has no generic associated types, so `I::Item<Int>` must not
13351        // silently drop the `<Int>` and resolve exactly as plain `I::Item`
13352        // would — that would let a program compile clean while meaning
13353        // something its source does not actually say.
13354        let r = check_src(
13355            "trait It { type Item\n fn get(self) -> Int }\n\
13356             fn first<I: It>(x: I) -> I::Item<Int> { panic(\"unreachable\") }\n\
13357             fn main() { }",
13358        );
13359        let d = r
13360            .diagnostics
13361            .iter()
13362            .find(|d| d.code == "E0012")
13363            .expect("E0012 for type arguments on a projection");
13364        assert!(
13365            d.message.contains("`I::Item` takes no type arguments"),
13366            "{}",
13367            d.message
13368        );
13369        // Deliberately NOT "associated type `I::Item` takes no type
13370        // arguments": `I::Nope<Int>` reports this same E0012 alongside
13371        // resolve_projection's own E0001 for the undeclared `Nope`, and
13372        // calling something "an associated type" that then turns out not to
13373        // be one would read as self-contradictory.
13374        assert!(
13375            !d.message.contains("associated type"),
13376            "must not call the base an \"associated type\" before it is known to resolve: {}",
13377            d.message
13378        );
13379    }
13380
13381    #[test]
13382    fn rejected_type_arguments_on_a_projection_are_not_themselves_resolved() {
13383        // `args` is rejected wholesale (E0012) rather than converted, exactly
13384        // like the two single-segment branches (generic parameter,
13385        // primitive) that also reject type arguments without recursing into
13386        // them. That is intentional, not an oversight, so this pins it as a
13387        // positive fact rather than merely "some diagnostic fired": `Nope`
13388        // must never surface its own `cannot find type` (E0001) — if it did,
13389        // rejected arguments would secretly still be getting resolved, and a
13390        // future change making that happen should have to update this test
13391        // rather than slip in silently under a bare `!diagnostics.is_empty()`.
13392        let r = check_src(
13393            "trait It { type Item\n fn get(self) -> Int }\n\
13394             fn first<I: It>(x: I) -> I::Item<Nope> { panic(\"unreachable\") }\n\
13395             fn main() { }",
13396        );
13397        assert_eq!(
13398            r.diagnostics.len(),
13399            1,
13400            "expected exactly the one E0012 for the rejected argument: {:?}",
13401            r.diagnostics
13402        );
13403        assert_eq!(r.diagnostics[0].code, "E0012", "{:?}", r.diagnostics);
13404        assert!(
13405            !r.diagnostics.iter().any(|d| d.message.contains("Nope")),
13406            "`Nope` must not be individually resolved or diagnosed: {:?}",
13407            r.diagnostics
13408        );
13409    }
13410
13411    #[test]
13412    fn future_displays_with_its_output_type_in_a_real_diagnostic() {
13413        // display_ty must render the output, not a bare "Future". Two different
13414        // futures printing the same string is the `T{i}` debt this project already
13415        // carries in diagnostics; do not add another instance of it.
13416        //
13417        // Asserted through a real mismatch message rather than by calling
13418        // display_ty directly: `CheckResult` is `{ module, diagnostics }` and
13419        // exposes no `Definitions`, so a direct call would need a second resolve
13420        // in the test. Going through the diagnostic is also the stronger test —
13421        // it is the path a user actually sees.
13422        let r = check_src(
13423            "fn take(x: Future<Int>) -> Int { 1 }\n\
13424             fn f(y: Future<Float>) -> Int { take(y) }\n\
13425             fn main() {}",
13426        );
13427        let msgs: Vec<String> = r.diagnostics.iter().map(|d| d.message.clone()).collect();
13428        assert!(
13429            msgs.iter()
13430                .any(|m| m.contains("Future<Int>") && m.contains("Future<Float>")),
13431            "expected a message naming both futures by their output types, got {msgs:?}"
13432        );
13433    }
13434
13435    #[test]
13436    fn bare_future_without_a_type_argument_is_rejected() {
13437        // `Future` takes exactly one argument. Both the zero-argument and the
13438        // two-argument spellings must be diagnosed, not silently accepted --
13439        // this is the arity path that no existing built-in type name exercises,
13440        // because Int/Float/Bool/Char/String are all nullary.
13441        let r = check_src("fn f(x: Future) -> Int { 1 }\nfn main() {}");
13442        assert!(
13443            r.diagnostics.iter().any(|d| d.code == "E0012"),
13444            "expected E0012, got {:?}",
13445            r.diagnostics.iter().map(|d| &d.code).collect::<Vec<_>>()
13446        );
13447    }
13448
13449    #[test]
13450    fn future_with_two_type_arguments_is_rejected() {
13451        let r = check_src("fn f(x: Future<Int, Bool>) -> Int { 1 }\nfn main() {}");
13452        assert!(r.diagnostics.iter().any(|d| d.code == "E0012"));
13453    }
13454
13455    #[test]
13456    fn future_of_int_and_future_of_float_do_not_unify() {
13457        // The unifier must descend into the output type. An arm that unified any
13458        // two Futures would make `Future<Int>` and `Future<Float>` interchangeable.
13459        let r = check_src(
13460            "fn take(x: Future<Int>) -> Int { 1 }\n\
13461             fn f(y: Future<Float>) -> Int { take(y) }\n\
13462             fn main() {}",
13463        );
13464        assert!(
13465            r.diagnostics.iter().any(|d| d.code == "E0010"),
13466            "expected a type mismatch, got {:?}",
13467            r.diagnostics.iter().map(|d| &d.code).collect::<Vec<_>>()
13468        );
13469    }
13470
13471    #[test]
13472    fn future_of_int_unifies_with_itself() {
13473        // The discriminating half of the test above, and NOT redundant with it:
13474        // an implementation whose `Future` unify arm always FAILED would satisfy
13475        // the mismatch test perfectly. Only this one rejects that.
13476        let r = check_src(
13477            "fn take(x: Future<Int>) -> Int { 1 }\n\
13478             fn f(y: Future<Int>) -> Int { take(y) }\n\
13479             fn main() {}",
13480        );
13481        assert!(
13482            r.diagnostics.is_empty(),
13483            "Future<Int> must unify with Future<Int>, got {:?}",
13484            r.diagnostics
13485                .iter()
13486                .map(|d| d.message.clone())
13487                .collect::<Vec<_>>()
13488        );
13489    }
13490
13491    #[test]
13492    fn future_used_as_a_call_qualifier_is_rejected() {
13493        // Measured, not assumed (there is no reason to expect a specific code
13494        // here a priori): `Future::x()` reports E0900 "module-qualified paths
13495        // are not supported yet" -- the same generic fallback an ordinary
13496        // undeclared-type qualifier gets. It is neither a `resolve_type`-style
13497        // "unknown type" message nor a successful call.
13498        //
13499        // Kills a `"Future" => return Some(_)` mutant in `qualifier_self_ty`
13500        // (e.g. a stray `Some(Ty::Error)`, or miscopying a neighbouring
13501        // `"X" => return Some(Ty::X)` line): that would route into the
13502        // assoc-fn-lookup branch instead, which -- finding nothing for `x`,
13503        // and `resolve_type("Future")` still `None` since nothing declares it
13504        // -- reports the *different* E0001 "no associated function `x` on
13505        // type `Future`" and returns early, never reaching this fallback.
13506        //
13507        // Does NOT kill a mutant that deletes the arm outright: with nothing
13508        // named `Future` declared, `resolve_type` also answers `None`, so
13509        // deletion is a no-op here. Since the built-in type names were
13510        // reserved, that mutant is a no-op everywhere: declaring a type
13511        // named `Future` is itself `E0089`
13512        // (`declaring_a_type_named_for_a_builtin_is_rejected`), so no
13513        // `Definitions` reachable through `resolve()` ever answers `Some`
13514        // for `resolve_type(_, "Future")`.
13515        // `future_qualifier_short_circuits_before_resolve_type` below records
13516        // that the mutant it used to catch is no longer catchable at all.
13517        let r = check_src("fn f() { Future::x() }\nfn main() { }");
13518        assert_eq!(error_codes(&r), ["E0900"], "{:?}", r.diagnostics);
13519    }
13520
13521    #[test]
13522    fn future_qualifier_short_circuits_before_resolve_type() {
13523        // White-box, not a `check_src` integration test: `qualifier_self_ty`'s
13524        // return only becomes *observable* through `check_call` when a
13525        // matching inherent/trait associated function is found for the
13526        // qualifier, which needs a real `impl` block on a type named
13527        // `Future` -- and such a block cannot be declared at all, since its
13528        // header fails `convert_ty`'s arity check first (see
13529        // `bare_future_without_a_type_argument_is_rejected`).
13530        //
13531        // This test used to drive the method past `check()` entirely by
13532        // `resolve()`-ing a program that declared `record Future { v: Int }`,
13533        // which compiled clean and gave `resolve_type(module, "Future")` a
13534        // record to find -- exactly the defect that reserving the built-in
13535        // type names closes. Declaring a type named `Future` is now `E0089`
13536        // (`declaring_a_type_named_for_a_builtin_is_rejected`), and
13537        // `Definitions` has no way to register one other than through that
13538        // now-rejecting path, so `resolve_type(_, "Future")` is `None` for
13539        // every `Definitions` reachable through `resolve()`. The arm-deleted
13540        // mutant this test used to kill is therefore no longer
13541        // distinguishable from correct behaviour by any input -- the same
13542        // conclusion `future_used_as_a_call_qualifier_is_rejected` above
13543        // already reaches for its own case. What remains below is a plain
13544        // pin of the arm's return value against an otherwise-empty
13545        // `Definitions`.
13546        let file_id = FileId::DUMMY;
13547        let src = "fn main() { }";
13548        let (tokens, lex_errors) = lex(src, file_id);
13549        assert!(lex_errors.is_empty(), "lex errors: {lex_errors:?}");
13550        let (ast, parse_errors) = parse(&tokens, file_id);
13551        assert!(parse_errors.is_empty(), "parse errors: {parse_errors:?}");
13552        let ast = ast.expect("no AST");
13553        let resolved = resolve(&ast);
13554        assert!(
13555            resolved.diagnostics.is_empty(),
13556            "resolve errors: {:?}",
13557            resolved.diagnostics
13558        );
13559        // A `Checker` with otherwise-empty tables: `qualifier_self_ty`'s
13560        // `"Future"` arm returns before it would ever touch `fcx`,
13561        // `self.impls`, or any other field a full `check()` pass would
13562        // populate, so this needs only enough of a `Checker` to compile --
13563        // mirroring `check()`'s own construction above.
13564        let file = ast::File { items: Vec::new() };
13565        let checker = Checker {
13566            file: &file,
13567            defs: &resolved.definitions,
13568            cur_module: ModuleId(0),
13569            sigs: FxHashMap::default(),
13570            method_locs: FxHashMap::default(),
13571            selfless: FxHashSet::default(),
13572            mut_self: FxHashSet::default(),
13573            sums: Vec::new(),
13574            records: Vec::new(),
13575            supertraits: FxHashMap::default(),
13576            traits: Vec::new(),
13577            impls: Vec::new(),
13578            impl_self: None,
13579            impl_selves: FxHashMap::default(),
13580            extra_functions: Vec::new(),
13581            next_closure_def: resolved.definitions.defs().len() as u32,
13582            type_arity: FxHashMap::default(),
13583            externs: Vec::new(),
13584            diagnostics: Vec::new(),
13585            probe: None,
13586            probe_result: ProbeResult::default(),
13587            probe_pending: None,
13588            probe_locals_pending: None,
13589            index: None,
13590            type_params: Vec::new(),
13591            sources: None,
13592            bindings: FxHashMap::default(),
13593            qualified_callee: None,
13594        };
13595        let mut fcx = FnCtx {
13596            icx: InferCtx::default(),
13597            locals: Vec::new(),
13598            scopes: Vec::new(),
13599            generics: FxHashMap::default(),
13600            param_bounds: Vec::new(),
13601            ret_ty: Ty::Unit,
13602            loop_depth: 0,
13603            in_async: false,
13604            pending_closures: Vec::new(),
13605        };
13606        assert_eq!(
13607            checker.qualifier_self_ty(&mut fcx, "Future"),
13608            None,
13609            "the reserved qualifier must not resolve to a same-named user type"
13610        );
13611    }
13612
13613    #[test]
13614    fn every_reserved_nullary_names_qualifier_resolves_to_its_own_primitive() {
13615        // Pins `qualifier_self_ty`'s table, which
13616        // `every_reserved_name_really_is_a_builtin_type` does not reach: that
13617        // test only ever annotates a parameter (`fn f(x: {ann}) -> Int`),
13618        // never a qualifier (`Int::zero()`). White-box for the same reason
13619        // `future_qualifier_short_circuits_before_resolve_type` above is: the
13620        // qualifier only becomes observable through `check_call` when a
13621        // matching associated function is found afterward, which needs an
13622        // `impl` this test does not care about constructing.
13623        //
13624        // `Future` is excluded: it is not nullary, and its own arm returns
13625        // `None` rather than a `Ty`, unpinned.
13626        let file_id = FileId::DUMMY;
13627        let src = "fn main() { }";
13628        let (tokens, lex_errors) = lex(src, file_id);
13629        assert!(lex_errors.is_empty(), "lex errors: {lex_errors:?}");
13630        let (ast, parse_errors) = parse(&tokens, file_id);
13631        assert!(parse_errors.is_empty(), "parse errors: {parse_errors:?}");
13632        let ast = ast.expect("no AST");
13633        let resolved = resolve(&ast);
13634        assert!(
13635            resolved.diagnostics.is_empty(),
13636            "resolve errors: {:?}",
13637            resolved.diagnostics
13638        );
13639        let file = ast::File { items: Vec::new() };
13640        let checker = Checker {
13641            file: &file,
13642            defs: &resolved.definitions,
13643            cur_module: ModuleId(0),
13644            sigs: FxHashMap::default(),
13645            method_locs: FxHashMap::default(),
13646            selfless: FxHashSet::default(),
13647            mut_self: FxHashSet::default(),
13648            sums: Vec::new(),
13649            records: Vec::new(),
13650            supertraits: FxHashMap::default(),
13651            traits: Vec::new(),
13652            impls: Vec::new(),
13653            impl_self: None,
13654            impl_selves: FxHashMap::default(),
13655            extra_functions: Vec::new(),
13656            next_closure_def: resolved.definitions.defs().len() as u32,
13657            type_arity: FxHashMap::default(),
13658            externs: Vec::new(),
13659            diagnostics: Vec::new(),
13660            probe: None,
13661            probe_result: ProbeResult::default(),
13662            probe_pending: None,
13663            probe_locals_pending: None,
13664            index: None,
13665            type_params: Vec::new(),
13666            sources: None,
13667            bindings: FxHashMap::default(),
13668            qualified_callee: None,
13669        };
13670        let mut fcx = FnCtx {
13671            icx: InferCtx::default(),
13672            locals: Vec::new(),
13673            scopes: Vec::new(),
13674            generics: FxHashMap::default(),
13675            param_bounds: Vec::new(),
13676            ret_ty: Ty::Unit,
13677            loop_depth: 0,
13678            in_async: false,
13679            pending_closures: Vec::new(),
13680        };
13681        for name in nova_resolver::RESERVED_TYPE_NAMES {
13682            if name == "Future" {
13683                continue;
13684            }
13685            let expected = match name {
13686                "Int" => Ty::Int,
13687                "Float" => Ty::Float,
13688                "Bool" => Ty::Bool,
13689                "Char" => Ty::Char,
13690                "String" => Ty::String,
13691                "Bytes" => Ty::Bytes,
13692                _ => panic!("RESERVED_TYPE_NAMES grew a name this test does not know: {name}"),
13693            };
13694            assert_eq!(
13695                checker.qualifier_self_ty(&mut fcx, name),
13696                Some(expected),
13697                "`{name}::_()`'s qualifier must resolve to the primitive"
13698            );
13699        }
13700    }
13701
13702    #[test]
13703    fn a_user_written_self_type_parameter_is_rejected_in_an_impl() {
13704        // Was `a_user_written_self_type_parameter_makes_self_item_resolve_in_
13705        // an_impl`, which pinned that this program compiled clean. It did, and
13706        // that was the problem: `Self` is an accepted identifier
13707        // (`Token::SelfUpper` parses to the plain string `"Self"`), so a
13708        // user-written `<Self>` landed an ordinary `generic_scope` entry and
13709        // `Self::Item` resolved to `Assoc { on: Param(0) }` — a *second*
13710        // meaning of `Self` in the very scope where `Self` also means the
13711        // impl's own self type. Rejecting the name is what makes that
13712        // unambiguous, so the same program is now an error. Same program on
13713        // purpose: it is the only pin on this shape.
13714        let r = check_src(
13715            "trait It { type Item\n fn get(self) -> Int }\n\
13716             record W<T> { v: T }\n\
13717             impl<Self: It> W<Self> { fn peek(self) -> Self::Item { panic(\"x\") } }\n\
13718             fn main() { }",
13719        );
13720        let d = r
13721            .diagnostics
13722            .iter()
13723            .find(|d| d.code == "E0076")
13724            .expect("E0076 for a type parameter named `Self`");
13725        assert!(
13726            d.message.contains("Self"),
13727            "names the parameter: {}",
13728            d.message
13729        );
13730        // Not E0900: this is not a feature that arrives later, it is a name
13731        // that will never be legal.
13732        assert!(!error_codes(&r).contains(&"E0900"), "{:?}", r.diagnostics);
13733    }
13734
13735    #[test]
13736    fn a_type_parameter_named_self_is_rejected_at_every_generic_declaration() {
13737        // `record W<Self>` is exactly as confusing as `impl<Self>`, so the
13738        // check belongs at every place a generic parameter can be declared —
13739        // all six `generics` fields in the AST, including the two that only
13740        // ever hold a method's own parameters. One shared pass, so a new
13741        // generic-carrying construct cannot quietly opt out.
13742        //
13743        // Each program is checked in isolation, and each must produce at least
13744        // one E0076 that names the parameter.
13745        let cases: &[(&str, &str)] = &[
13746            ("free fn", "fn f<Self>(x: Self) -> Int { 1 }\nfn main() { }"),
13747            ("record", "record W<Self> { v: Self }\nfn main() { }"),
13748            // `ast::Item::Type` covers both a sum type and a plain alias, but
13749            // an alias cannot be tested here: the resolver rejects
13750            // `type A<T> = …` outright with "type aliases are not supported yet
13751            // in the Phase 1 compiler", so it never reaches this pass.
13752            ("sum type", "type S<Self> = | A(Self)\nfn main() { }"),
13753            (
13754                "trait",
13755                "trait Q<Self> { fn q(self) -> Int }\nfn main() { }",
13756            ),
13757            (
13758                "trait method",
13759                "trait Q { fn q<Self>(self, x: Self) -> Int }\nfn main() { }",
13760            ),
13761            (
13762                "impl",
13763                "record W<T> { v: T }\nimpl<Self> W<Self> { fn m(self) -> Int { 1 } }\n\
13764                 fn main() { }",
13765            ),
13766            (
13767                "impl method",
13768                "record W { v: Int }\nimpl W { fn m<Self>(self, x: Self) -> Int { 1 } }\n\
13769                 fn main() { }",
13770            ),
13771        ];
13772        for (label, src) in cases {
13773            let r = check_src(src);
13774            let d = r
13775                .diagnostics
13776                .iter()
13777                .find(|d| d.code == "E0076")
13778                .unwrap_or_else(|| panic!("{label}: expected E0076, got {:?}", r.diagnostics));
13779            assert!(d.message.contains("Self"), "{label}: {}", d.message);
13780        }
13781    }
13782
13783    #[test]
13784    fn the_implicit_self_of_a_trait_body_is_not_a_user_written_parameter() {
13785        // The rejection must not catch the `Self` that `self_generic_scope`
13786        // inserts for a trait's own implicit type — that one is not a declared
13787        // parameter at all, and it is the whole mechanism `Self::Item` inside a
13788        // trait relies on. A control case, because a check that rejected every
13789        // occurrence of the *name* rather than every *declaration* of it would
13790        // still pass the test above.
13791        let r = check_src(
13792            "trait It { type Item\n \
13793             fn get(self) -> Self::Item\n \
13794             fn dup(self) -> Self::Item { self.get() } }\n\
13795             record W { v: Int }\n\
13796             impl It for W { type Item = Int\n fn get(self) -> Self::Item { panic(\"x\") } }\n\
13797             fn main() { }",
13798        );
13799        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
13800        assert!(!error_codes(&r).contains(&"E0076"), "{:?}", r.diagnostics);
13801    }
13802
13803    #[test]
13804    fn a_parameter_named_self_is_rejected_but_still_scoped() {
13805        // The rejected name still enters the generic scope, so the *rest* of
13806        // the declaration resolves normally and the user gets one error rather
13807        // than an E0076 followed by a cascade of "cannot find type `Self`".
13808        let r = check_src("record W<Self> { v: Self }\nfn main() { }");
13809        assert_eq!(error_codes(&r), vec!["E0076"], "{:?}", r.diagnostics);
13810    }
13811
13812    #[test]
13813    fn self_item_in_an_inherent_impl_reports_that_it_has_no_trait() {
13814        // Was `self_item_in_an_impl_with_no_explicit_self_parameter_still_
13815        // reports_e0900`, which pinned that this shape fell through to the
13816        // module-qualified-path branch. `Self` in an impl now means the impl's
13817        // own self type, so it no longer falls through — but an *inherent*
13818        // impl implements no trait, so nothing declares an associated type for
13819        // `Self::Item` to name. That is an E0001 "no such name", not an E0900
13820        // "not supported yet": there is no feature missing here.
13821        //
13822        // Deliberately not `resolve_projection`'s shared empty-candidate
13823        // wording ("on any bound of `Self`"): an inherent impl's `Self` has no
13824        // bounds at all, so that message would describe a lookup that never
13825        // happened.
13826        let r = check_src(
13827            "trait It { type Item\n fn get(self) -> Int }\n\
13828             record K { v: Int }\n\
13829             impl K { fn peek(self) -> Self::Item { panic(\"x\") } }\n\
13830             fn main() { }",
13831        );
13832        let d = r
13833            .diagnostics
13834            .iter()
13835            .find(|d| d.code == "E0001")
13836            .expect("E0001 for Self::Item in an inherent impl");
13837        assert!(d.message.contains("Item"), "names it: {}", d.message);
13838        assert!(d.message.contains("inherent"), "says why: {}", d.message);
13839        assert!(
13840            !d.message.contains("bound"),
13841            "an inherent impl's `Self` has no bounds: {}",
13842            d.message
13843        );
13844        assert!(
13845            !error_codes(&r).contains(&"E0900"),
13846            "no longer a module-qualified path: {:?}",
13847            r.diagnostics
13848        );
13849    }
13850
13851    #[test]
13852    fn self_item_in_a_trait_impl_projects_onto_the_impls_own_self_type() {
13853        // The case Task 6's normalization rests on: `Self::Item` written
13854        // inside `impl<T> It for W<T>` must resolve, and it must project onto
13855        // the impl's SELF TYPE (`W<Param(0)>`) — not onto `Param(0)`, which is
13856        // the impl's first type parameter and a different type entirely. Every
13857        // projection before this task was on a flat `Param(idx)`, so a
13858        // by-index implementation would look right until this compound case.
13859        let r = check_src(
13860            "trait It { type Item\n fn get(self) -> Self::Item }\n\
13861             record W<T> { v: T }\n\
13862             impl<T> It for W<T> { type Item = T\n \
13863             fn get(self) -> Self::Item { panic(\"x\") } }\n\
13864             fn main() { }",
13865        );
13866        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
13867        let it = r
13868            .module
13869            .traits
13870            .iter()
13871            .find(|t| t.name == "It")
13872            .expect("trait It collected");
13873        let item = it.assoc_types[0].1;
13874        let w = r
13875            .module
13876            .records
13877            .iter()
13878            .find(|rec| rec.name == "W")
13879            .expect("record W collected")
13880            .def_id;
13881        // The trait's own declared return type still *is* the projection — this
13882        // is the side nothing normalizes, and it pins the `Param(0)`-is-`Self`
13883        // convention the impl side is checked against.
13884        assert_eq!(
13885            it.methods[0].ret,
13886            Ty::Assoc {
13887                on: Box::new(Ty::Param(0)),
13888                assoc: item
13889            },
13890            "the trait declares `Self::Item`"
13891        );
13892        // The impl method's compiled return type, not just "no diagnostics":
13893        // `Ty::Error` unifies with everything, so an empty diagnostic list
13894        // alone would also hold if the projection had collapsed to an error.
13895        // By the impl method's exact mangled name — `<self type>.<trait>.<method>`,
13896        // with the self type's own arguments folded in by `type_full_name`.
13897        // Matching on a suffix instead picks up `Option::get` from the implicit
13898        // prelude.
13899        let m = r
13900            .module
13901            .functions
13902            .iter()
13903            .find(|f| f.name == "W_T.It.get")
13904            .unwrap_or_else(|| {
13905                panic!(
13906                    "the impl's `get` was compiled; have {:?}",
13907                    r.module
13908                        .functions
13909                        .iter()
13910                        .map(|f| f.name.as_str())
13911                        .filter(|n| n.contains("get"))
13912                        .collect::<Vec<_>>()
13913                )
13914            });
13915        // **Deliberately flipped by Task 5**, which is what this test's own
13916        // header anticipated. It used to assert `ret_ty` was still
13917        // `Assoc { on: W<Param(0)>, assoc: Item }`; the function-return
13918        // normalization seam now resolves that through `type Item = T` to
13919        // `Param(0)`, i.e. `T`.
13920        //
13921        // The flip *strengthens* the original claim rather than weakening it. A
13922        // projection built onto `Param(0)` instead of onto the impl's self type
13923        // — the by-index implementation this test exists to catch, Task 4's
13924        // mutation F — has no `head()` and so cannot normalize at all: it would
13925        // still read `Assoc { .. }` here, and would additionally trip
13926        // conformance. Reaching `Param(0)` therefore proves both that the
13927        // projection was on `W<T>` and that it resolved through this impl's own
13928        // binding.
13929        assert_eq!(
13930            m.ret_ty,
13931            Ty::Param(0),
13932            "`W<T>::Item` normalizes through `type Item = T` to the impl's `T`"
13933        );
13934        // `w` is still the record this projects onto; asserted through the trait
13935        // impl's recorded self type, which normalization does not touch.
13936        let imp = r
13937            .module
13938            .impls
13939            .iter()
13940            .find(|i| i.trait_id.is_some())
13941            .expect("the trait impl was collected");
13942        assert_eq!(
13943            imp.self_ty,
13944            Ty::Record {
13945                def_id: w,
13946                args: vec![Ty::Param(0)]
13947            }
13948        );
13949    }
13950
13951    #[test]
13952    fn self_naming_an_undeclared_associated_type_in_a_trait_impl_is_an_error() {
13953        // The impl's trait is the only candidate: `It` declares `Item`, not
13954        // `Nope`, so this must not silently resolve to something.
13955        let r = check_src(
13956            "trait It { type Item\n fn get(self) -> Int }\n\
13957             record W { v: Int }\n\
13958             impl It for W { type Item = Int\n \
13959             fn get(self) -> Int { 1 }\n \
13960             fn other(self) -> Self::Nope { panic(\"x\") } }\n\
13961             fn main() { }",
13962        );
13963        let d = r
13964            .diagnostics
13965            .iter()
13966            .find(|d| d.code == "E0001" && d.message.contains("Nope"))
13967            .expect("E0001 for Self::Nope in a trait impl");
13968        assert!(d.message.contains("associated type"), "{}", d.message);
13969    }
13970
13971    #[test]
13972    fn self_item_outside_any_impl_or_trait_still_reports_e0900() {
13973        // The two-segment path branch now has a third meaning, and the
13974        // original one must survive it: outside a trait body and outside an
13975        // impl there is no `Self` at all, so `Self::Item` is just a
13976        // module-qualified path.
13977        let r = check_src(
13978            "trait It { type Item\n fn get(self) -> Int }\n\
13979             fn f() -> Self::Item { panic(\"x\") }\n\
13980             fn main() { }",
13981        );
13982        let d = r
13983            .diagnostics
13984            .iter()
13985            .find(|d| d.code == "E0900")
13986            .expect("E0900 for Self::Item with no enclosing impl or trait");
13987        assert!(
13988            d.message.contains("module-qualified type paths"),
13989            "{}",
13990            d.message
13991        );
13992    }
13993
13994    #[test]
13995    fn self_item_resolves_in_an_impl_method_body_annotation_too() {
13996        // `impl_self` has to be in scope for body checking, not only signature
13997        // collection: a `let` annotation goes through the same `convert_ty`,
13998        // from a different pass (`check_method`, not `collect_impls`).
13999        //
14000        // **Deliberately flipped by Task 5**, exactly as this test's own header
14001        // said it would be. The initializer used to be `panic(...)` (type
14002        // `Never`, which unifies with anything), because nothing normalized a
14003        // projection and `let x: Self::Item = 1` was a genuine mismatch; it is
14004        // now a plain `1`, and `x` is typed `Int`. That closes concern 1 of the
14005        // Task 4 report.
14006        //
14007        // `1` is the stronger initializer, not merely the newly-legal one:
14008        // `Never` unified with an unnormalized projection just as happily, so the
14009        // old program could not tell resolving from failing to resolve. `Int`
14010        // against `W::Item` can only pass if the projection was resolved through
14011        // `type Item = Int` — and the mismatching spelling is pinned separately
14012        // by `a_projection_in_a_let_annotation_normalizes`.
14013        let r = check_src(
14014            "trait It { type Item\n fn get(self) -> Self::Item }\n\
14015             record W { v: Int }\n\
14016             impl It for W { type Item = Int\n \
14017             fn get(self) -> Self::Item { let x: Self::Item = 1\n x } }\n\
14018             fn main() { }",
14019        );
14020        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
14021        let m = r
14022            .module
14023            .functions
14024            .iter()
14025            .find(|f| f.name == "W.It.get")
14026            .expect("the impl's `get` was compiled");
14027        // The local's own recorded type, not just an empty diagnostic list:
14028        // `Ty::Error` unifies with everything, so a failed annotation would
14029        // also have produced no diagnostics here.
14030        let x = m
14031            .locals
14032            .iter()
14033            .find(|l| l.name == "x")
14034            .expect("local `x` compiled");
14035        assert_eq!(
14036            x.ty,
14037            Ty::Int,
14038            "the `let` annotation resolved to the projection and normalized to Int"
14039        );
14040    }
14041
14042    #[test]
14043    fn a_required_methods_own_signature_resolves_its_own_traits_self_item() {
14044        // `Iterator::next`'s eventual shape (`fn next(mut self) ->
14045        // Option<Self::Item>`) is exactly this: a REQUIRED method whose own
14046        // return type projects, through `Self`, onto an associated type its
14047        // own enclosing trait declares. At the point this method's signature
14048        // is converted, `collect_traits` has not yet pushed *this trait's
14049        // own* `hir::TraitDef` (it is still being built) — this test is what
14050        // proves `find_assoc_type` must search `self.defs`, not the
14051        // incrementally-built `self.traits`.
14052        let r = check_src("trait It { type Item\n fn get(self) -> Self::Item }\nfn main() { }");
14053        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
14054    }
14055
14056    #[test]
14057    fn a_default_methods_own_signature_resolves_its_own_traits_self_item() {
14058        // Mirrors the required-method case above, but for a method WITH a
14059        // default body: `collect_traits` builds this signature in a second,
14060        // separate pass (the default-method-body pass), with its own
14061        // `Self`-bound construction that is independently susceptible to the
14062        // same ordering hazard.
14063        let r = check_src(
14064            "trait It { type Item\n fn get(self) -> Self::Item { panic(\"unreachable\") } }\n\
14065             fn main() { }",
14066        );
14067        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
14068    }
14069
14070    #[test]
14071    fn an_impl_binds_its_associated_type() {
14072        let r = check_src(
14073            "trait It { type Item\n fn get(self) -> Int }\n\
14074             record W<T> { v: T }\n\
14075             impl<T> It for W<T> { type Item = T\n fn get(self) -> Int { 1 } }\n\
14076             fn main() { }",
14077        );
14078        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
14079        let i = r
14080            .module
14081            .impls
14082            .iter()
14083            .find(|i| i.trait_id.is_some())
14084            .expect("the trait impl was collected");
14085        // Bound to the impl's OWN parameter, which is what makes `subst` the
14086        // thing that carries it — a binding to a primitive would not.
14087        assert_eq!(i.assoc_bindings.len(), 1);
14088        assert_eq!(i.assoc_bindings[0].1, Ty::Param(0));
14089        // And the key is the trait's own associated-type `DefId`, not some
14090        // freshly minted or positional id: a binding keyed by anything else
14091        // could not be looked up by the normalization seams in Tasks 5-7.
14092        let t = r
14093            .module
14094            .traits
14095            .iter()
14096            .find(|t| t.name == "It")
14097            .expect("trait It collected");
14098        assert_eq!(i.assoc_bindings[0].0, t.assoc_types[0].1);
14099    }
14100
14101    #[test]
14102    fn an_impl_missing_an_associated_type_reports_e0070() {
14103        let r = check_src(
14104            "trait It { type Item\n fn get(self) -> Int }\n\
14105             record W { v: Int }\n\
14106             impl It for W { fn get(self) -> Int { 1 } }\n\
14107             fn main() { }",
14108        );
14109        let d = r
14110            .diagnostics
14111            .iter()
14112            .find(|d| d.code == "E0070")
14113            .expect("E0070 for a missing associated type");
14114        assert!(
14115            d.message.contains("Item"),
14116            "names the missing type: {}",
14117            d.message
14118        );
14119        // The impl provides every method the trait requires, so the message
14120        // must not read as a missing *method* — the shared E0070 site says
14121        // "method(s)" for that case and has to say something else for this one.
14122        assert!(
14123            !d.message.contains("method"),
14124            "a missing associated type is not a missing method: {}",
14125            d.message
14126        );
14127    }
14128
14129    #[test]
14130    fn an_impl_binding_an_undeclared_associated_type_reports_e0071() {
14131        let r = check_src(
14132            "trait It { type Item\n fn get(self) -> Int }\n\
14133             record W { v: Int }\n\
14134             impl It for W { type Item = Int\n type Extra = Bool\n fn get(self) -> Int { 1 } }\n\
14135             fn main() { }",
14136        );
14137        let d = r
14138            .diagnostics
14139            .iter()
14140            .find(|d| d.code == "E0071")
14141            .expect("E0071 for an undeclared associated type");
14142        assert!(d.message.contains("Extra"), "names it: {}", d.message);
14143        // `Item` IS declared, so only the one offender may be reported.
14144        assert!(
14145            !d.message.contains("Item"),
14146            "must not implicate the legitimate binding: {}",
14147            d.message
14148        );
14149        assert_eq!(
14150            r.diagnostics.iter().filter(|d| d.code == "E0071").count(),
14151            1,
14152            "exactly one E0071: {:?}",
14153            r.diagnostics
14154        );
14155    }
14156
14157    #[test]
14158    fn binding_the_same_associated_type_twice_is_rejected() {
14159        // Both bindings resolve to the same `DefId`, so keeping both would
14160        // leave which one normalization reads up to list order.
14161        let r = check_src(
14162            "trait It { type Item\n fn get(self) -> Int }\n\
14163             record W { v: Int }\n\
14164             impl It for W { type Item = Int\n type Item = Bool\n fn get(self) -> Int { 1 } }\n\
14165             fn main() { }",
14166        );
14167        let d = r
14168            .diagnostics
14169            .iter()
14170            .find(|d| d.code == "E0403")
14171            .expect("E0403 for a repeated associated-type binding");
14172        assert!(d.message.contains("Item"), "{}", d.message);
14173        // The first binding survives, so the set is complete and no E0070 is
14174        // due — a duplicate is one error, not two.
14175        assert!(!error_codes(&r).contains(&"E0070"), "{:?}", r.diagnostics);
14176        let i = r
14177            .module
14178            .impls
14179            .iter()
14180            .find(|i| i.trait_id.is_some())
14181            .expect("the trait impl was collected");
14182        assert_eq!(i.assoc_bindings.len(), 1, "{:?}", i.assoc_bindings);
14183        assert_eq!(i.assoc_bindings[0].1, Ty::Int, "the FIRST binding is kept");
14184    }
14185
14186    #[test]
14187    fn an_inherent_impl_cannot_bind_an_associated_type() {
14188        // `check_impl_conformance` never runs for an inherent impl, so without
14189        // its own check here the binding would be silently dropped.
14190        let r = check_src(
14191            "record W { v: Int }\n\
14192             impl W { type Item = Int\n fn get(self) -> Int { 1 } }\n\
14193             fn main() { }",
14194        );
14195        let d = r
14196            .diagnostics
14197            .iter()
14198            .find(|d| d.code == "E0071")
14199            .expect("E0071 for a binding on an inherent impl");
14200        assert!(d.message.contains("Item"), "{}", d.message);
14201        assert!(
14202            d.message.contains("inherent"),
14203            "says why, not just that: {}",
14204            d.message
14205        );
14206    }
14207
14208    #[test]
14209    fn an_impl_level_const_is_rejected_rather_than_silently_dropped() {
14210        // `ast::ImplBlock::consts` had no reader anywhere in the workspace, so
14211        // an impl-level `const` parsed, ran, and vanished: this program printed
14212        // its method's result with no diagnostic, and `K::LIMIT` then reported
14213        // `no variant 'LIMIT' on type 'K'` — which is not what the user wrote.
14214        // Accepting and discarding a declaration is worse than refusing it, so
14215        // it is `E0900` until associated constants are actually implemented.
14216        let r = check_src(
14217            "record K { v: Int }\n\
14218             impl K { const LIMIT: Int = 99\n fn get(self) -> Int { self.v } }\n\
14219             fn main() { }",
14220        );
14221        let d = r
14222            .diagnostics
14223            .iter()
14224            .find(|d| d.code == "E0900")
14225            .expect("E0900 for an impl-level const");
14226        assert!(
14227            d.message.contains("LIMIT"),
14228            "names the constant: {}",
14229            d.message
14230        );
14231        assert!(
14232            d.message.contains("associated constant"),
14233            "names the construct, so the message is searchable: {}",
14234            d.message
14235        );
14236    }
14237
14238    #[test]
14239    fn a_top_level_const_still_works_beside_an_impl() {
14240        // The rejection above must not reach ordinary `const`s. Without this,
14241        // narrowing it to impl bodies could not be distinguished from banning
14242        // constants outright — every existing const test uses a file with no
14243        // `impl` in it at all.
14244        let r = check_src(
14245            "const LIMIT: Int = 99\n\
14246             record K { v: Int }\n\
14247             impl K { fn get(self) -> Int { LIMIT } }\n\
14248             fn main() { }",
14249        );
14250        assert_eq!(error_codes(&r), Vec::<&str>::new(), "{:?}", r.diagnostics);
14251    }
14252
14253    #[test]
14254    fn conformance_compares_the_associated_type_sets_not_their_sizes() {
14255        // Two declared, two bound, but one of each is wrong: `A` is missing
14256        // and `C` is undeclared. A count comparison sees 2 == 2 and reports
14257        // nothing; only a set comparison catches both. This is the case a
14258        // single-associated-type test cannot distinguish.
14259        let r = check_src(
14260            "trait Pair { type A\n type B\n fn get(self) -> Int }\n\
14261             record W { v: Int }\n\
14262             impl Pair for W { type B = Int\n type C = Bool\n fn get(self) -> Int { 1 } }\n\
14263             fn main() { }",
14264        );
14265        let missing = r
14266            .diagnostics
14267            .iter()
14268            .find(|d| d.code == "E0070")
14269            .expect("E0070 for the missing `A`");
14270        assert!(missing.message.contains('A'), "{}", missing.message);
14271        assert!(
14272            !missing.message.contains('B'),
14273            "`B` is bound, so it is not missing: {}",
14274            missing.message
14275        );
14276        let extra = r
14277            .diagnostics
14278            .iter()
14279            .find(|d| d.code == "E0071")
14280            .expect("E0071 for the undeclared `C`");
14281        assert!(extra.message.contains('C'), "{}", extra.message);
14282    }
14283
14284    #[test]
14285    fn a_self_referential_associated_type_binding_is_rejected() {
14286        // `type Item = Self::Item` describes a type in terms of itself. It has
14287        // to be rejected *here*, at the declaration, because `normalize` must
14288        // re-normalize its own result for the legitimate `A = Self::B` /
14289        // `B = Int` chain to resolve — and that is exactly the walk that would
14290        // never terminate on this input.
14291        let r = check_src(
14292            "trait It { type Item\n fn get(self) -> Self::Item }\n\
14293             record W { v: Int }\n\
14294             impl It for W { type Item = Self::Item\n\
14295             fn get(self) -> Self::Item { 1 } }\n\
14296             fn main() { }",
14297        );
14298        let d = r
14299            .diagnostics
14300            .iter()
14301            .find(|d| d.code == "E0077")
14302            .expect("E0077 for a self-referential associated-type binding");
14303        assert!(
14304            d.message.contains("Item"),
14305            "names the offending type: {}",
14306            d.message
14307        );
14308        // A cycle is one mistake, so the binding is not also reported missing.
14309        assert!(!error_codes(&r).contains(&"E0070"), "{:?}", r.diagnostics);
14310    }
14311
14312    #[test]
14313    fn a_mutually_recursive_pair_of_associated_type_bindings_is_rejected() {
14314        // Neither binding is self-referential on its own, so a check that only
14315        // looked at each binding in isolation would accept this. The cycle
14316        // closes only by following `A -> B -> A`.
14317        let r = check_src(
14318            "trait Two { type A\n type B\n fn get(self) -> Int }\n\
14319             record W { v: Int }\n\
14320             impl Two for W { type A = Self::B\n type B = Self::A\n\
14321             fn get(self) -> Int { 1 } }\n\
14322             fn main() { }",
14323        );
14324        let msgs: Vec<&str> = r
14325            .diagnostics
14326            .iter()
14327            .filter(|d| d.code == "E0077")
14328            .map(|d| d.message.as_str())
14329            .collect();
14330        assert_eq!(msgs.len(), 2, "one per offending type: {:?}", r.diagnostics);
14331        assert!(
14332            msgs.iter().any(|m| m.contains('A')) && msgs.iter().any(|m| m.contains('B')),
14333            "both members of the cycle are named: {msgs:?}"
14334        );
14335    }
14336
14337    #[test]
14338    fn a_cyclic_associated_type_binding_nested_in_a_compound_type_is_rejected() {
14339        // `Item = [Item]` is an infinitely large type exactly as `Item = Item`
14340        // is. A cycle walk that only inspected the top level of each bound type
14341        // accepts this and hands `normalize` a projection that grows a layer per
14342        // step, so the compiler diverges on a five-line program.
14343        let r = check_src(
14344            "trait It { type Item\n fn get(self) -> Int }\n\
14345             record W { v: Int }\n\
14346             impl It for W { type Item = [Self::Item]\n fn get(self) -> Int { 1 } }\n\
14347             fn main() { }",
14348        );
14349        assert_eq!(error_codes(&r), ["E0077"], "{:?}", r.diagnostics);
14350    }
14351
14352    #[test]
14353    fn a_cyclic_associated_type_binding_nested_in_a_future_is_rejected() {
14354        // The same shape as the test above, `Item = [Self::Item]`, but through
14355        // `collect_self_projections`'s `Ty::Future` arm instead of its
14356        // `Ty::Array` arm. Kills a mutant that drops the payload there (e.g.
14357        // matching `Ty::Future(_)` and doing nothing, the way the pre-existing
14358        // primitive arms correctly do for types with no self-projections to
14359        // find): with no edge recorded back to `Item`, `reaches_self` would
14360        // say there is no cycle, and the compiler would fall through to
14361        // `normalize_ty`'s re-normalization instead of reporting it here --
14362        // which is precisely the wrong-diagnostic failure mode this function's
14363        // own doc comment warns about ("a compiler-limit message is the wrong
14364        // thing to show a user who wrote a two-line cycle").
14365        let r = check_src(
14366            "trait It { type Item\n fn get(self) -> Int }\n\
14367             record W { v: Int }\n\
14368             impl It for W { type Item = Future<Self::Item>\n fn get(self) -> Int { 1 } }\n\
14369             fn main() { }",
14370        );
14371        assert_eq!(error_codes(&r), ["E0077"], "{:?}", r.diagnostics);
14372    }
14373
14374    /// Task 11 Step 4. A signature comparison whose either side is already
14375    /// poisoned reports nothing, so `{error}` never reaches a user.
14376    ///
14377    /// `Ty` derives `PartialEq` with no `Ty::Error` absorption, so at this seam a
14378    /// poisoned side *forces* a mismatch — the opposite of `unify`, where
14379    /// `Ty::Error` unifies with anything and the poison quietly stops. Every one
14380    /// of the three programs below already has a diagnostic explaining the real
14381    /// problem, and each used to get a second `E0072` naming `{error}`, which is
14382    /// meaningless to a user and points at a method that is not the mistake.
14383    ///
14384    /// Three rows, not one, because the poison arrives by two different routes
14385    /// and at two different depths:
14386    ///  * a cyclic binding poisoned by `E0077`, on the **trait** side;
14387    ///  * an unresolvable binding poisoned by `E0001`, also on the trait side;
14388    ///  * the same, but the projection is **wrapped** in `Option`, so the
14389    ///    normalized type is `Option<{error}>` and not `Ty::Error` itself. This
14390    ///    row is why the guard is `has_error()` and not `== Ty::Error`: a
14391    ///    top-level check leaves this one leaking `Option<{error}>`. Measured, on
14392    ///    `25db453`.
14393    ///
14394    /// A fourth row puts the projection in a **parameter** rather than the return
14395    /// type, because the comparison has two guarded sites and they are separate
14396    /// code: measured, removing only the parameter-side guard left all 602 tests
14397    /// green when the first three rows were the whole test.
14398    ///
14399    /// The last row is the control that this is suppression of a *second* error
14400    /// and not of the check: two concrete types that genuinely disagree, with no
14401    /// poison anywhere, still report `E0072`. Without it, deleting the comparison
14402    /// outright would pass.
14403    #[test]
14404    fn signatures_are_not_compared_when_either_side_is_already_poisoned() {
14405        // (trait's `get` signature tail, impl body, the complete code set)
14406        let rows: [(&str, &str, &[&str]); 5] = [
14407            (
14408                ") -> Self::Item",
14409                "type Item = Self::Item\n fn get(self) -> Int { 1 }",
14410                &["E0077"],
14411            ),
14412            (
14413                ") -> Self::Item",
14414                "type Item = Nope\n fn get(self) -> Bool { true }",
14415                &["E0001"],
14416            ),
14417            (
14418                ") -> Option<Self::Item>",
14419                "type Item = Nope\n fn get(self) -> Option<Bool> { None }",
14420                &["E0001"],
14421            ),
14422            (
14423                ", x: Self::Item) -> Int",
14424                "type Item = Nope\n fn get(self, x: Bool) -> Int { 1 }",
14425                &["E0001"],
14426            ),
14427            (
14428                ") -> Int",
14429                "type Item = Int\n fn get(self) -> Bool { true }",
14430                &["E0072"],
14431            ),
14432        ];
14433        for (trait_tail, impl_body, want) in rows {
14434            let r = check_src(&format!(
14435                "trait It {{ type Item\n fn get(self{trait_tail} }}\n\
14436                 record W {{ v: Int }}\n\
14437                 impl It for W {{ {impl_body} }}\n\
14438                 fn main() {{ }}"
14439            ));
14440            assert_eq!(
14441                error_codes(&r),
14442                want,
14443                "impl `{impl_body}` against `fn get(self{trait_tail}`: {:?}",
14444                r.diagnostics
14445            );
14446            // Belt and braces on the whole row set: no message may render the
14447            // `Ty::Error` sentinel, whatever code carries it.
14448            for d in &r.diagnostics {
14449                assert!(
14450                    !d.message.contains("{error}"),
14451                    "no user-facing message may render `Ty::Error`: {}",
14452                    d.message
14453                );
14454            }
14455        }
14456    }
14457
14458    /// Task 11 Step 1. A projection in an impl's **self type** is rejected.
14459    ///
14460    /// Two independent defects compound in that position, both measured on
14461    /// `25db453` where the whole file below reported `ok`. The impl can never be
14462    /// selected, because `Ty::match_pattern` recovers an impl's type arguments by
14463    /// matching its self type against a ground type and cannot invert `T::Item`
14464    /// to find `T`. And it is invisible to coherence, because
14465    /// `hir::self_types_overlap`'s helpers do not understand `Assoc` — so it does
14466    /// not conflict with an impl that *does* apply to the same type. Dead code
14467    /// that also defeats overlap checking is worse than either alone.
14468    ///
14469    /// The `E0074` control is what makes the coherence half of that claim
14470    /// falsifiable: the same file with `impl<T> Tr for W<T>` in place of the
14471    /// projection *does* conflict. Without it, "no `E0074`" would be consistent
14472    /// with these two impls simply not overlapping.
14473    #[test]
14474    fn a_projection_in_an_impls_self_type_is_rejected() {
14475        let prelude = "trait It { type Item\n fn g(self) -> Int }\n\
14476                       trait Tr { fn h(self) -> Int }\n\
14477                       record W<T> { v: T }\n";
14478        let overlapping = "impl<T: It> Tr for W<T::Item> { fn h(self) -> Int { 1 } }\n\
14479                           impl Tr for W<Int> { fn h(self) -> Int { 2 } }\n";
14480        let r = check_src(&format!("{prelude}{overlapping}fn main() {{ }}"));
14481        assert_eq!(error_codes(&r), ["E0900"], "{:?}", r.diagnostics);
14482        assert!(
14483            r.diagnostics[0].message.contains("impl's self type"),
14484            "the message must name the position: {}",
14485            r.diagnostics[0].message
14486        );
14487        // Alone, too: the impl is unselectable whether or not anything overlaps
14488        // it, so overlap is not the reason for the rejection and a test that only
14489        // used the pair would leave the single impl accepted.
14490        let alone = check_src(&format!(
14491            "{prelude}impl<T: It> Tr for W<T::Item> {{ fn h(self) -> Int {{ 1 }} }}\n\
14492             fn main() {{ }}"
14493        ));
14494        assert_eq!(error_codes(&alone), ["E0900"], "{:?}", alone.diagnostics);
14495        // A *bare* projection as the self type, which used to report
14496        // `E0010: impl blocks are only supported on named types` — misleading,
14497        // because `T::Item` may well resolve to a named type. Checked before the
14498        // head check for exactly that reason.
14499        let bare = check_src(&format!(
14500            "{prelude}impl<T: It> Tr for T::Item {{ fn h(self) -> Int {{ 1 }} }}\n\
14501             fn main() {{ }}"
14502        ));
14503        assert_eq!(error_codes(&bare), ["E0900"], "{:?}", bare.diagnostics);
14504        // The control. With a plain `W<T>` the same pair *is* an overlap, so the
14505        // projection was genuinely hiding one rather than the two impls being
14506        // disjoint.
14507        let control = check_src(&format!(
14508            "{prelude}impl<T> Tr for W<T> {{ fn h(self) -> Int {{ 1 }} }}\n\
14509             impl Tr for W<Int> {{ fn h(self) -> Int {{ 2 }} }}\n\
14510             fn main() {{ }}"
14511        ));
14512        assert_eq!(
14513            error_codes(&control),
14514            ["E0074"],
14515            "{:?}",
14516            control.diagnostics
14517        );
14518    }
14519
14520    /// The other half of Step 1, and the assertion that keeps the rejection from
14521    /// quietly becoming "projections are banned from impls".
14522    ///
14523    /// Every position a projection *is* legal in, in one program: an impl's
14524    /// binding right-hand side both on the impl's own parameter (`type Item =
14525    /// T::Item`) and on `Self` (`type Other = Self::Item`); an impl method's
14526    /// return type, bare and wrapped in `Option`; a `let` annotation inside an
14527    /// impl method body; a trait method's own declaration; and a free generic
14528    /// function's return type. Only the self type is refused.
14529    #[test]
14530    fn a_projection_is_still_accepted_in_every_other_position_of_an_impl() {
14531        let r = check_src(
14532            "trait It { type Item\n type Other\n\
14533             fn get(self) -> Self::Item\n\
14534             fn wrapped(self) -> Option<Self::Item> }\n\
14535             record W<T> { v: T }\n\
14536             impl<T: It> It for W<T> { type Item = T::Item\n\
14537             type Other = Self::Item\n\
14538             fn get(self) -> Self::Item { self.v.get() }\n\
14539             fn wrapped(self) -> Option<Self::Item> { let x: Self::Item = self.get()\n\
14540             Some(x) } }\n\
14541             record C { n: Int }\n\
14542             impl It for C { type Item = Int\n type Other = Int\n\
14543             fn get(self) -> Int { self.n }\n\
14544             fn wrapped(self) -> Option<Int> { Some(self.n) } }\n\
14545             fn first<I: It>(x: I) -> I::Item { x.get() }\n\
14546             fn main() { let c = C { n: 4 }\n println(\"${first(c)}\")\n\
14547             let w = W { v: C { n: 7 } }\n println(\"${first(w)}\") }",
14548        );
14549        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
14550    }
14551
14552    /// Task 11 Step 2. A trait may not declare the same name twice — as an
14553    /// associated type or as a method.
14554    ///
14555    /// Both were accepted on `25db453`, and both are the same defect: the
14556    /// consumers take the **first** match by name, so the second declaration is
14557    /// silently dead. For an associated type, `find_assoc_type` scans and takes
14558    /// the first, so nothing binds the second and conformance does not notice
14559    /// because it matches bindings by *name*. For a method,
14560    /// `trait_method_index` and `check_impl_method_signatures` likewise take the
14561    /// first, so a trait can declare two contradictory signatures and the impl is
14562    /// checked against whichever happens to be written first.
14563    ///
14564    /// The plan asked for the associated-type half and said to *check* whether
14565    /// the method half had the same hole rather than assume it. It does; both are
14566    /// fixed here, with the same `E0403` a duplicate generic parameter and a
14567    /// duplicate associated-type binding in an impl already report.
14568    ///
14569    /// `fn g(self)` beside `fn g()` is rejected too, and that is the case worth
14570    /// naming: the two lookups partition the method list by receiver, so allowing
14571    /// it would make `g` resolve to a different declaration depending on the call
14572    /// syntax.
14573    ///
14574    /// One diagnostic each, asserted as the complete list: the duplicate is
14575    /// skipped before its signature is converted, so it does not also report its
14576    /// own errors for a mistake that is the duplication.
14577    #[test]
14578    fn a_trait_may_not_declare_one_name_twice() {
14579        let assoc = check_src(
14580            "trait It { type Item\n type Item\n fn g(self) -> Int }\n\
14581             record W { v: Int }\n\
14582             impl It for W { type Item = Int\n fn g(self) -> Int { 1 } }\n\
14583             fn main() { }",
14584        );
14585        assert_eq!(error_codes(&assoc), ["E0403"], "{:?}", assoc.diagnostics);
14586        assert!(
14587            assoc.diagnostics[0].message.contains("`Item`")
14588                && assoc.diagnostics[0].message.contains("associated type"),
14589            "the message must name the type: {}",
14590            assoc.diagnostics[0].message
14591        );
14592        let method = check_src(
14593            "trait It { fn g(self) -> Int\n fn g(self) -> Bool }\n\
14594             record W { v: Int }\n\
14595             impl It for W { fn g(self) -> Int { 1 } }\n\
14596             fn main() { }",
14597        );
14598        assert_eq!(error_codes(&method), ["E0403"], "{:?}", method.diagnostics);
14599        assert!(
14600            method.diagnostics[0].message.contains("`g`")
14601                && method.diagnostics[0].message.contains("method"),
14602            "the message must name the method: {}",
14603            method.diagnostics[0].message
14604        );
14605        // A receiver-ful method beside a receiver-less associated function of the
14606        // same name: still one name, still rejected.
14607        let mixed = check_src(
14608            "trait It { fn g(self) -> Int\n fn g() -> Int }\n\
14609             record W { v: Int }\n\
14610             impl It for W { fn g(self) -> Int { 1 } }\n\
14611             fn main() { }",
14612        );
14613        assert_eq!(error_codes(&mixed), ["E0403"], "{:?}", mixed.diagnostics);
14614        // Control: distinct names are untouched.
14615        let ok = check_src(
14616            "trait It { type A\n type B\n fn g(self) -> Self::A\n fn h(self) -> Self::B }\n\
14617             record W { v: Int }\n\
14618             impl It for W { type A = Int\n type B = Bool\n\
14619             fn g(self) -> Int { 1 }\n fn h(self) -> Bool { true } }\n\
14620             fn main() { }",
14621        );
14622        assert!(ok.diagnostics.is_empty(), "{:?}", ok.diagnostics);
14623        // And the survivors keep the right `DefId`s. `assoc_type_ids` is drained
14624        // *positionally* — one id per `TraitItem::AssocType`, in source order —
14625        // so a rejection that `continue`d without calling `next()` would hand `B`
14626        // the id minted for the discarded second `A`, and `TraitDef.assoc_types`
14627        // would carry a `(name, DefId)` pair whose two halves disagree.
14628        //
14629        // The observable consequence is a cross-table one, which is why this is
14630        // asserted here rather than left to the diagnostic list: `ImplInfo
14631        // .assoc_bindings` is keyed by `find_assoc_type`, which searches
14632        // `self.defs` by name and is therefore *immune* to the misalignment,
14633        // while `TraitDef.assoc_types` is not. So the two tables key the same
14634        // associated type under two different ids and nothing else notices —
14635        // `check_impl_conformance` compares by name, so it reports nothing, and
14636        // the only reader of the id half is `mono.rs`'s `type_name`, which would
14637        // render the projection as `W::?` in a diagnostic that needs a name.
14638        // Measured: with the `next()` moved inside the `if let`, the whole
14639        // 599-test suite stays green and only this assertion fails.
14640        let realigned = check_src(
14641            "trait It { type A\n type A\n type B\n\
14642             fn g(self) -> Self::B }\n\
14643             record W { v: Int }\n\
14644             impl It for W { type A = Int\n type B = Bool\n\
14645             fn g(self) -> Bool { true } }\n\
14646             fn main() { }",
14647        );
14648        assert_eq!(
14649            error_codes(&realigned),
14650            ["E0403"],
14651            "the duplicate is the only error — `B` must still resolve to `Bool`: {:?}",
14652            realigned.diagnostics
14653        );
14654        let t = realigned
14655            .module
14656            .traits
14657            .iter()
14658            .find(|t| t.name == "It")
14659            .expect("trait It collected");
14660        assert_eq!(
14661            t.assoc_types
14662                .iter()
14663                .map(|(n, _)| n.as_str())
14664                .collect::<Vec<_>>(),
14665            ["A", "B"],
14666            "the duplicate is dropped, the two survivors keep their order"
14667        );
14668        let i = realigned
14669            .module
14670            .impls
14671            .iter()
14672            .find(|i| i.trait_id.is_some())
14673            .expect("the trait impl was collected");
14674        for (name, id) in &t.assoc_types {
14675            assert!(
14676                i.assoc_bindings.iter().any(|(d, _)| d == id),
14677                "`{name}` in the trait table is keyed by an id no binding uses, so the \
14678                 positional drain fell out of step: {:?} vs {:?}",
14679                t.assoc_types,
14680                i.assoc_bindings
14681            );
14682        }
14683    }
14684
14685    /// Task 11 Step 5. An impl may echo an associated type its trait inherits
14686    /// from a **supertrait**, exactly as the trait's own method signature may.
14687    ///
14688    /// The two sides used to disagree. `collect_traits` seeds `sig_bounds[0]`
14689    /// with the trait and then calls `expand_bounds`, so inside
14690    /// `trait Ext: Base { fn peek(self) -> Self::Elem }` the projection resolves
14691    /// against `Base`. `convert_ty`'s impl branch passed `vec![tid]`
14692    /// *unexpanded*, and `find_assoc_type` matches `trait_def == trait_id`
14693    /// exactly, so the echoed spelling in `impl Ext for W` was
14694    /// `E0001: no associated type `Elem` on any bound of `Self`` — measured on
14695    /// `25db453` — while the identical signature one declaration above was fine.
14696    /// Design doc §5.1 pins "either spelling is accepted", and this was the one
14697    /// place the echo was not.
14698    ///
14699    /// Two negative controls, because "resolve it against more traits" has two
14700    /// ways to over-reach. A name no bound declares must still be `E0001`, and a
14701    /// name **both** `Ext` and `Base` declare must still be `E0015` — for that
14702    /// second one the trait side already reported `E0015` before this change (so
14703    /// the program was already rejected, measured), and the impl now agrees
14704    /// instead of quietly picking `Ext`'s. Two reports for one root cause is the
14705    /// pre-existing lack of diagnostic dedup, asserted by count rather than
14706    /// hidden behind a `contains`.
14707    #[test]
14708    fn an_impl_may_echo_an_associated_type_inherited_from_a_supertrait() {
14709        let prelude = "trait Base { type Elem\n fn base(self) -> Int }\n";
14710        let w = "record W { v: Int }\n\
14711                 impl Base for W { type Elem = Int\n fn base(self) -> Int { 1 } }\n";
14712        // The echo: `Ext` declares no associated type of its own, so `Self::Elem`
14713        // can only come from `Base`.
14714        let r = check_src(&format!(
14715            "{prelude}trait Ext: Base {{ fn peek(self) -> Self::Elem }}\n\
14716             {w}impl Ext for W {{ fn peek(self) -> Self::Elem {{ 5 }} }}\n\
14717             fn main() {{ }}"
14718        ));
14719        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
14720        // And it really resolved to `Int` rather than to `Ty::Error`, which would
14721        // unify with anything and make the assertion above vacuous.
14722        let wrong = check_src(&format!(
14723            "{prelude}trait Ext: Base {{ fn peek(self) -> Self::Elem }}\n\
14724             {w}impl Ext for W {{ fn peek(self) -> Bool {{ true }} }}\n\
14725             fn main() {{ }}"
14726        ));
14727        assert_eq!(error_codes(&wrong), ["E0072"], "{:?}", wrong.diagnostics);
14728        // Control 1: a name no bound of `Self` declares is still unresolved, on
14729        // both sides.
14730        let absent = check_src(&format!(
14731            "{prelude}trait Ext: Base {{ fn peek(self) -> Self::Nope }}\n\
14732             {w}impl Ext for W {{ fn peek(self) -> Self::Nope {{ 5 }} }}\n\
14733             fn main() {{ }}"
14734        ));
14735        assert_eq!(
14736            error_codes(&absent),
14737            ["E0001", "E0001"],
14738            "{:?}",
14739            absent.diagnostics
14740        );
14741        // Control 2: declared by the trait *and* its supertrait is ambiguous, and
14742        // now says so on both sides. Pre-fix this reported one `E0015` (the trait
14743        // side), so the program was already rejected; the impl no longer resolves
14744        // it silently to `Ext`'s.
14745        let ambiguous = check_src(&format!(
14746            "{prelude}trait Ext: Base {{ type Elem\n fn peek(self) -> Self::Elem }}\n\
14747             {w}impl Ext for W {{ type Elem = Int\n fn peek(self) -> Self::Elem {{ 5 }} }}\n\
14748             fn main() {{ }}"
14749        ));
14750        assert_eq!(
14751            error_codes(&ambiguous),
14752            ["E0015", "E0015"],
14753            "{:?}",
14754            ambiguous.diagnostics
14755        );
14756    }
14757
14758    #[test]
14759    fn a_binding_projecting_onto_an_impl_parameter_is_not_a_cycle() {
14760        // The control case. `type Item = T::Item` is a projection in a binding
14761        // and must be accepted: it names the *argument's* associated type, and
14762        // each normalization step strips a `W<…>` layer, so it bottoms out.
14763        // A cycle check that flags any `Assoc` in a bound type kills this.
14764        let r = check_src(
14765            "trait It { type Item\n fn get(self) -> Int }\n\
14766             record W<T> { v: T }\n\
14767             impl<T: It> It for W<T> { type Item = T::Item\n fn get(self) -> Int { 1 } }\n\
14768             fn main() { }",
14769        );
14770        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
14771    }
14772
14773    #[test]
14774    fn a_chain_of_associated_type_bindings_is_accepted() {
14775        // `A = Self::B` with `B = Int` is legal and is the reason `normalize`
14776        // has to re-normalize its own result. The cycle check must not reject a
14777        // reference that bottoms out.
14778        let r = check_src(
14779            "trait Two { type A\n type B\n fn get(self) -> Int }\n\
14780             record W { v: Int }\n\
14781             impl Two for W { type A = Self::B\n type B = Int\n\
14782             fn get(self) -> Int { 1 } }\n\
14783             fn main() { }",
14784        );
14785        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
14786    }
14787
14788    /// A hang is not assertable: this test's value is that it *completes*.
14789    /// Every cyclic shape reachable from source goes through here, and the
14790    /// diagnostic count is bounded so a regression to merely-quadratic
14791    /// reporting is caught too (the model is Task 4's termination tests).
14792    #[test]
14793    fn the_compiler_terminates_on_every_cyclic_binding_shape() {
14794        let shapes = [
14795            "impl It for W { type Item = Self::Item\n fn get(self) -> Self::Item { 1 } }",
14796            "impl It for W { type Item = [Self::Item]\n fn get(self) -> Int { 1 } }",
14797            "impl It for W { type Item = [[Self::Item]]\n fn get(self) -> Int { 1 } }",
14798        ];
14799        for shape in shapes {
14800            let src = format!(
14801                "trait It {{ type Item\n fn get(self) -> Self::Item }}\n\
14802                 record W {{ v: Int }}\n\
14803                 {shape}\n\
14804                 fn use_it(w: W) -> Int {{ 1 }}\n\
14805                 fn main() {{ println(\"${{use_it(W {{ v: 1 }})}}\") }}"
14806            );
14807            let r = check_src(&src);
14808            assert!(
14809                r.diagnostics.len() < 10,
14810                "bounded diagnostics for {shape}: {:?}",
14811                r.diagnostics
14812            );
14813            assert!(
14814                error_codes(&r).contains(&"E0077"),
14815                "the cycle is reported for {shape}: {:?}",
14816                r.diagnostics
14817            );
14818        }
14819    }
14820
14821    /// The plan's Step 1 test, with one change recorded here rather than only in
14822    /// the report: it writes the impl's return type as `Self::Item`, not as `T`.
14823    ///
14824    /// The `T` spelling reports an *extra* `E0072` ("method `get_item` returns
14825    /// `T0` but trait `It` declares `W<T0>::Item`"), because
14826    /// `check_impl_conformance` still compares the two signatures raw — that is
14827    /// the second normalization seam, and it is Task 6's, where both spellings
14828    /// are pinned by `an_impl_may_echo_the_projection_or_write_the_concrete_type`.
14829    /// Written the `T` way, this test could not pass at the end of Task 5 for a
14830    /// reason that has nothing to do with `normalize`.
14831    #[test]
14832    fn a_projection_on_a_concrete_type_normalizes_at_a_use_site() {
14833        // `w.get_item()` returns `Self::Item`; with Self = W<Int> that is Int,
14834        // so assigning it to an Int must typecheck with no annotation.
14835        let r = check_src(
14836            "trait It { type Item\n fn get_item(self) -> Self::Item }\n\
14837             record W<T> { v: T }\n\
14838             impl<T> It for W<T> { type Item = T\n\
14839             fn get_item(self) -> Self::Item { self.v } }\n\
14840             fn main() { let w = W { v: 7 }\n let n: Int = w.get_item()\n\
14841             println(\"${n} ${w.get_item()}\") }",
14842        );
14843        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
14844        // Not vacuous on the *value* either: the call must be typed `Int`, so a
14845        // `normalize` that returned `Ty::Error` (which unifies with anything)
14846        // cannot hide behind the empty diagnostic list above.
14847        //
14848        // Read off the **unannotated** second call, deliberately. `Stmt::Let`
14849        // does `value.ty = annot_ty` *after* the unify, so the initializer of
14850        // `let n: Int = …` carries the annotation's type by construction — an
14851        // assertion there equals `Int` whatever `normalize` returned. Measured:
14852        // this exact test passed against a `normalize` that answered `Ty::Error`
14853        // for the second impl when it was written against the annotated call.
14854        let call_tys: Vec<&Ty> = exprs_in(&r.module, "main")
14855            .into_iter()
14856            .filter(|e| matches!(e.kind, hir::ExprKind::TraitCall { .. }))
14857            .map(|e| &e.ty)
14858            .collect();
14859        assert_eq!(call_tys.len(), 2, "two calls: {call_tys:?}");
14860        assert_eq!(
14861            *call_tys[1],
14862            Ty::Int,
14863            "the unannotated call is typed Int, not an error type"
14864        );
14865    }
14866
14867    #[test]
14868    fn a_projection_normalizes_to_the_wrong_type_is_an_error() {
14869        // The negative direction: Self::Item is Int here, so binding it to a
14870        // Bool must fail. Without this, a `normalize` that returned Ty::Error
14871        // or Ty::Never for everything would pass the positive test.
14872        let r = check_src(
14873            "trait It { type Item\n fn get_item(self) -> Self::Item }\n\
14874             record W<T> { v: T }\n\
14875             impl<T> It for W<T> { type Item = T\n\
14876             fn get_item(self) -> Self::Item { self.v } }\n\
14877             fn main() { let w = W { v: 7 }\n let b: Bool = w.get_item()\n println(\"${b}\") }",
14878        );
14879        assert!(
14880            r.diagnostics.iter().any(|d| d.code == "E0010"),
14881            "expected a type mismatch: {:?}",
14882            r.diagnostics
14883        );
14884    }
14885
14886    #[test]
14887    fn normalization_picks_the_impl_that_matches_the_receiver() {
14888        // Two impls of one trait, on different self types, binding `Item` to
14889        // different types. A single-impl test cannot tell "looks the impl up"
14890        // apart from "takes the only impl in the table".
14891        let prelude = "trait It { type Item\n fn get_item(self) -> Self::Item }\n\
14892             record W<T> { v: T }\n\
14893             record K { k: Bool }\n\
14894             impl<T> It for W<T> { type Item = T\n\
14895             fn get_item(self) -> Self::Item { self.v } }\n\
14896             impl It for K { type Item = Bool\n\
14897             fn get_item(self) -> Self::Item { self.k } }\n";
14898        // The calls are left **unannotated**, on purpose: `Stmt::Let` overwrites
14899        // an annotated initializer's type with the annotation, so `let n: Int =
14900        // w.get_item()` records `Int` on the call node no matter what `normalize`
14901        // returned. The `let`s below only pin the mismatch direction; the types
14902        // are read off the interpolated calls.
14903        let r = check_src(&format!(
14904            "{prelude}fn main() {{ let w = W {{ v: 7 }}\n let k = K {{ k: true }}\n\
14905             println(\"${{w.get_item()}} ${{k.get_item()}}\") }}"
14906        ));
14907        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
14908        // The two calls' own types, in source order. An empty diagnostic list is
14909        // not enough: an implementation that picks the wrong impl and fails to
14910        // recover its arguments yields `Ty::Error`, which unifies with both `Int`
14911        // and `Bool` and so satisfies the assertion above for the wrong reason.
14912        // Measured — this is what let a "take any impl that binds `Item`"
14913        // mutation survive before these two lines existed.
14914        let call_tys: Vec<&Ty> = exprs_in(&r.module, "main")
14915            .into_iter()
14916            .filter(|e| matches!(e.kind, hir::ExprKind::TraitCall { .. }))
14917            .map(|e| &e.ty)
14918            .collect();
14919        assert_eq!(call_tys, [&Ty::Int, &Ty::Bool], "one type each, in order");
14920        // And the crossed pairing must fail, or `Item` is resolving to something
14921        // that fits both.
14922        let bad = check_src(&format!(
14923            "{prelude}fn main() {{ let w = W {{ v: 7 }}\n\
14924             let b: Bool = w.get_item()\n println(\"${{b}}\") }}"
14925        ));
14926        assert!(
14927            bad.diagnostics.iter().any(|d| d.code == "E0010"),
14928            "W<Int>::Item is Int, not Bool: {:?}",
14929            bad.diagnostics
14930        );
14931    }
14932
14933    /// A trait method may declare a *parameter* as `Self::Item`, and the caller
14934    /// passes a concrete value for it. The plan named only the method-call
14935    /// *return* type; the parameter is the mirror at the same seam, and without
14936    /// it `w.put(9)` reads "argument has type `Int` but `W<Int>::Item` was
14937    /// expected".
14938    #[test]
14939    fn a_projection_in_a_trait_method_parameter_normalizes_at_the_call() {
14940        let prelude = "trait It { type Item\n fn put(self, x: Self::Item) -> Int }\n\
14941             record W<T> { v: T }\n\
14942             impl<T> It for W<T> { type Item = T\n\
14943             fn put(self, x: Self::Item) -> Int { 1 } }\n";
14944        let r = check_src(&format!(
14945            "{prelude}fn main() {{ println(\"${{(W {{ v: 7 }}).put(9)}}\") }}"
14946        ));
14947        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
14948        // And the parameter still constrains: `W<Int>::Item` is `Int`.
14949        let bad = check_src(&format!(
14950            "{prelude}fn main() {{ println(\"${{(W {{ v: 7 }}).put(true)}}\") }}"
14951        ));
14952        assert!(
14953            bad.diagnostics.iter().any(|d| d.code == "E0010"),
14954            "a Bool argument where Int was expected: {:?}",
14955            bad.diagnostics
14956        );
14957    }
14958
14959    /// An *impl* method whose parameter is declared `Self::Item` binds a local of
14960    /// that type, so `check_fn_body` has to normalize `sig.params` as well as
14961    /// `sig.ret` — otherwise `x + 1` reports "mismatched operand types:
14962    /// `K::Item` vs `Int`" inside a body the signature seam already accepted.
14963    #[test]
14964    fn a_projection_in_an_impl_method_parameter_normalizes_in_the_body() {
14965        let r = check_src(
14966            "trait It { type Item\n fn put(self, x: Self::Item) -> Int }\n\
14967             record K { k: Int }\n\
14968             impl It for K { type Item = Int\n\
14969             fn put(self, x: Self::Item) -> Int { x + 1 } }\n\
14970             fn main() { println(\"${(K { k: 0 }).put(9)}\") }",
14971        );
14972        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
14973        // The local's recorded type, not just the absence of diagnostics: an
14974        // annotation that collapsed to `Ty::Error` would also add nothing here.
14975        let m = r
14976            .module
14977            .functions
14978            .iter()
14979            .find(|f| f.name == "K.It.put")
14980            .expect("the impl's `put` was compiled");
14981        let x = m
14982            .locals
14983            .iter()
14984            .find(|l| l.name == "x")
14985            .expect("local `x` compiled");
14986        assert_eq!(x.ty, Ty::Int, "`K::Item` normalized to Int");
14987    }
14988
14989    /// A generic *free* function whose signature projects onto its own type
14990    /// parameter. Neither the plan's three seams nor Task 7's monomorphization
14991    /// covers this: the instantiation is fully concrete here in typeck, and it is
14992    /// the exact shape of Task 7's own Step 1 test
14993    /// (`fn unwrap_item<I: It>(x: I) -> I::Item`), which cannot even be called
14994    /// without it.
14995    #[test]
14996    fn a_projection_in_a_generic_functions_signature_normalizes_at_the_call() {
14997        let prelude = "trait It { type Item\n fn get_item(self) -> Self::Item }\n\
14998             record W<T> { v: T }\n\
14999             record K { k: Bool }\n\
15000             impl<T> It for W<T> { type Item = T\n\
15001             fn get_item(self) -> Self::Item { self.v } }\n\
15002             impl It for K { type Item = Bool\n\
15003             fn get_item(self) -> Self::Item { self.k } }\n\
15004             fn first<I: It>(x: I) -> I::Item { x.get_item() }\n\
15005             fn take<I: It>(x: I, y: I::Item) -> Int { 1 }\n";
15006        // Two instantiations of the same generic function, so "resolves per
15007        // instantiation" is distinguishable from "resolves once".
15008        let r = check_src(&format!(
15009            "{prelude}fn main() {{ let n: Int = first(W {{ v: 7 }})\n\
15010             let b: Bool = first(K {{ k: true }})\n\
15011             let t: Int = take(W {{ v: 7 }}, 9)\n\
15012             println(\"${{n}} ${{b}} ${{t}}\") }}"
15013        ));
15014        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
15015        let bad = check_src(&format!(
15016            "{prelude}fn main() {{ let b: Bool = first(W {{ v: 7 }})\n println(\"${{b}}\") }}"
15017        ));
15018        assert!(
15019            bad.diagnostics.iter().any(|d| d.code == "E0010"),
15020            "`W<Int>::Item` is Int, not Bool: {:?}",
15021            bad.diagnostics
15022        );
15023        let bad_arg = check_src(&format!(
15024            "{prelude}fn main() {{ println(\"${{take(W {{ v: 7 }}, true)}}\") }}"
15025        ));
15026        assert!(
15027            bad_arg.diagnostics.iter().any(|d| d.code == "E0010"),
15028            "`I::Item` is Int for `W<Int>`, so a Bool argument is wrong: {:?}",
15029            bad_arg.diagnostics
15030        );
15031    }
15032
15033    /// The design doc §4.2 says `Assoc { on: Var(_) }` cannot arise. It can, in
15034    /// one shape: a generic call whose projection-typed parameter comes *before*
15035    /// the parameter that determines the type, so nothing has solved the variable
15036    /// when the projection is instantiated.
15037    ///
15038    /// Pinned as a **known gap**, not as desired behaviour. Fixing it needs
15039    /// deferred obligations, which §4.1 rules out for this increment. The value
15040    /// of the test is that closing it later is a visible decision with a test to
15041    /// change, and that the failure mode stays an ordinary type mismatch.
15042    #[test]
15043    fn known_gap_a_projection_parameter_before_its_determining_parameter() {
15044        let r = check_src(
15045            "trait It { type Item\n fn get_item(self) -> Self::Item }\n\
15046             record W<T> { v: T }\n\
15047             impl<T> It for W<T> { type Item = T\n\
15048             fn get_item(self) -> Self::Item { self.v } }\n\
15049             fn take<I: It>(y: I::Item, x: I) -> Int { 1 }\n\
15050             fn main() { println(\"${take(9, W { v: 7 })}\") }",
15051        );
15052        let d = r
15053            .diagnostics
15054            .iter()
15055            .find(|d| d.code == "E0010")
15056            .expect("the known gap still reports a plain type mismatch");
15057        // Names the unresolved projection rather than claiming anything false.
15058        assert!(
15059            d.message.contains("::Item"),
15060            "the message names the projection: {}",
15061            d.message
15062        );
15063        // Reversing the parameters is the workaround, and it must work — the gap
15064        // is about argument order, not about the shape being unsupported.
15065        let reversed = check_src(
15066            "trait It { type Item\n fn get_item(self) -> Self::Item }\n\
15067             record W<T> { v: T }\n\
15068             impl<T> It for W<T> { type Item = T\n\
15069             fn get_item(self) -> Self::Item { self.v } }\n\
15070             fn take<I: It>(x: I, y: I::Item) -> Int { 1 }\n\
15071             fn main() { println(\"${take(W { v: 7 }, 9)}\") }",
15072        );
15073        assert!(
15074            reversed.diagnostics.is_empty(),
15075            "{:?}",
15076            reversed.diagnostics
15077        );
15078    }
15079
15080    #[test]
15081    fn a_projection_nested_inside_a_compound_type_normalizes() {
15082        // `[Self::Item]`, not `Self::Item`. A `normalize` that handles a
15083        // top-level projection but does not recurse into `Array`/`Record`/
15084        // `Sum`/`Fn` passes every test whose projection is the whole type.
15085        let r = check_src(
15086            "trait It { type Item\n fn items(self) -> [Self::Item] }\n\
15087             record W<T> { v: T }\n\
15088             impl<T> It for W<T> { type Item = T\n\
15089             fn items(self) -> [Self::Item] { [self.v] } }\n\
15090             fn main() { let w = W { v: 7 }\n let a: [Int] = w.items()\n\
15091             println(\"${a.len()}\") }",
15092        );
15093        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
15094        let bad = check_src(
15095            "trait It { type Item\n fn items(self) -> [Self::Item] }\n\
15096             record W<T> { v: T }\n\
15097             impl<T> It for W<T> { type Item = T\n\
15098             fn items(self) -> [Self::Item] { [self.v] } }\n\
15099             fn main() { let a: [Bool] = W { v: 7 }.items()\n println(\"${a.len()}\") }",
15100        );
15101        assert!(
15102            bad.diagnostics.iter().any(|d| d.code == "E0010"),
15103            "`[W<Int>::Item]` is `[Int]`, not `[Bool]`: {:?}",
15104            bad.diagnostics
15105        );
15106    }
15107
15108    #[test]
15109    fn a_projection_in_a_let_annotation_normalizes() {
15110        // The `let`-annotation seam. `value.ty = annot_ty` happens *after* the
15111        // unify, so an unnormalized projection written here becomes the
15112        // binding's type and propagates to every later use of it.
15113        let r = check_src(
15114            "trait It { type Item\n fn get_item(self) -> Self::Item }\n\
15115             record W<T> { v: T }\n\
15116             impl<T> It for W<T> { type Item = T\n\
15117             fn get_item(self) -> Self::Item { let n: Self::Item = self.v\n n } }\n\
15118             fn main() { let w = W { v: 7 }\n println(\"${w.get_item()}\") }",
15119        );
15120        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
15121        // And the annotation still constrains: `Self::Item` is `T` here, so
15122        // initializing it from a `Bool` must fail.
15123        let bad = check_src(
15124            "trait It { type Item\n fn get_item(self) -> Self::Item }\n\
15125             record W<T> { v: T }\n\
15126             impl<T> It for W<T> { type Item = T\n\
15127             fn get_item(self) -> Self::Item { let n: Self::Item = true\n self.v } }\n\
15128             fn main() { }",
15129        );
15130        assert!(
15131            bad.diagnostics.iter().any(|d| d.code == "E0010"),
15132            "expected a mismatch on the annotated let: {:?}",
15133            bad.diagnostics
15134        );
15135    }
15136
15137    #[test]
15138    fn a_method_body_checks_against_its_normalized_return_type() {
15139        // The function-return seam, on a *non-generic* impl so the expected type
15140        // is a concrete `Int` rather than a parameter: the body must be accepted
15141        // when it matches and rejected when it does not. The negative half is
15142        // what distinguishes normalizing from discarding.
15143        let ok = check_src(
15144            "trait It { type Item\n fn get_item(self) -> Self::Item }\n\
15145             record K { k: Int }\n\
15146             impl It for K { type Item = Int\n fn get_item(self) -> Self::Item { 1 } }\n\
15147             fn main() { }",
15148        );
15149        assert!(ok.diagnostics.is_empty(), "{:?}", ok.diagnostics);
15150        let bad = check_src(
15151            "trait It { type Item\n fn get_item(self) -> Self::Item }\n\
15152             record K { k: Int }\n\
15153             impl It for K { type Item = Int\n fn get_item(self) -> Self::Item { true } }\n\
15154             fn main() { }",
15155        );
15156        assert!(
15157            bad.diagnostics.iter().any(|d| d.code == "E0010"),
15158            "`K::Item` is `Int`, so a `Bool` body is wrong: {:?}",
15159            bad.diagnostics
15160        );
15161    }
15162
15163    #[test]
15164    fn a_chain_of_associated_type_bindings_normalizes_all_the_way() {
15165        // `A = Self::B` and `B = Int`: one resolution step yields `Self::B`, not
15166        // `Int`. A `normalize` that does not re-normalize its own result fails
15167        // here and passes every other positive test in this file.
15168        let prelude = "trait Two { type A\n type B\n fn get_a(self) -> Self::A }\n\
15169             record W { v: Int }\n\
15170             impl Two for W { type A = Self::B\n type B = Int\n\
15171             fn get_a(self) -> Self::A { self.v } }\n";
15172        let r = check_src(&format!(
15173            "{prelude}fn main() {{ let n: Int = W {{ v: 1 }}.get_a()\n println(\"${{n}}\") }}"
15174        ));
15175        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
15176        let bad = check_src(&format!(
15177            "{prelude}fn main() {{ let b: Bool = W {{ v: 1 }}.get_a()\n println(\"${{b}}\") }}"
15178        ));
15179        assert!(
15180            bad.diagnostics.iter().any(|d| d.code == "E0010"),
15181            "the chain ends at Int, not Bool: {:?}",
15182            bad.diagnostics
15183        );
15184    }
15185
15186    /// `E0077` poisons the binding to `Ty::Error`, so a *use* of the cyclic
15187    /// projection adds nothing: not a second `E0010`, and not `normalize`'s
15188    /// `E0078` depth-limit report. One mistake, one diagnostic.
15189    ///
15190    /// **The impl's return type is `Int`, deliberately, and this test was wrong
15191    /// before it was.** It used to spell `Self::Item` on the impl side too, so
15192    /// *both* sides of `check_impl_method_signatures`' comparison normalized to
15193    /// `Ty::Error` and compared **equal** — the test passed by accident of its
15194    /// own spelling, not because the property it is named for held. Changing that
15195    /// one token to a concrete type made it fail with
15196    /// `E0072: method `get` returns `Int` but trait `It` declares `{error}``,
15197    /// which is the cascade the name denies. Fixed at the comparison (see the
15198    /// `has_error` guard there and the poisoning comment on
15199    /// `check_assoc_binding_cycles`), and the spelling is now the one that can
15200    /// see it.
15201    #[test]
15202    fn a_reported_binding_cycle_is_poisoned_and_does_not_cascade() {
15203        let r = check_src(
15204            "trait It { type Item\n fn get(self) -> Self::Item }\n\
15205             record W { v: Int }\n\
15206             impl It for W { type Item = Self::Item\n\
15207             fn get(self) -> Int { 1 } }\n\
15208             fn main() { println(\"${W { v: 1 }.get()}\") }",
15209        );
15210        assert_eq!(error_codes(&r), ["E0077"], "{:?}", r.diagnostics);
15211    }
15212
15213    /// A trait with `n+1` associated types `A0..An`, a method taking `Self::A0`,
15214    /// and an impl binding each `Ak` from `link(k)`. Terminates at `An = Int`, so
15215    /// there is no cycle and `E0077` never fires — this is the shape that makes
15216    /// `E0078` reachable from ordinary source.
15217    ///
15218    /// Accepted, when it is accepted: the method takes `Self::A0` as a
15219    /// *parameter* and returns `Int`, so conformance compares two spellings of the
15220    /// same projection and the body is just `1`. An `E0078` here is therefore the
15221    /// only diagnostic, never a knock-on of some other rejection.
15222    fn assoc_chain_src(n: u32, link: impl Fn(u32) -> String) -> String {
15223        let decls: String = (0..=n).map(|k| format!(" type A{k}\n")).collect();
15224        let binds: String = (0..n)
15225            .map(|k| format!(" type A{} = {}\n", k, link(k)))
15226            .collect();
15227        format!(
15228            "record Pair<A, B> {{ a: A\n b: B }}\n\
15229             trait Chain {{\n{decls} fn put(self, y: Self::A0) -> Int }}\n\
15230             record W {{ v: Int }}\n\
15231             impl Chain for W {{\n{binds} type A{n} = Int\n\
15232             fn put(self, y: Self::A0) -> Int {{ 1 }} }}\n\
15233             fn main() {{ }}"
15234        )
15235    }
15236
15237    #[test]
15238    fn a_binding_chain_that_resolves_to_an_enormous_type_is_a_diagnostic() {
15239        // `type A(k) = Pair<Self::A(k+1), Self::A(k+1)>`. No cycle and only 16
15240        // links, so neither `E0077` nor the depth limit sees anything wrong — but
15241        // the resolved type is `2^16` nodes. Before the step allowance this
15242        // *compiled*, slowly: measured through `nova check`, 16 links took 716 ms,
15243        // 20 took 12.9 s, and 24 (a 60-line file) had not finished after two
15244        // minutes. Now 41 ms and an error.
15245        //
15246        // The failure has to be a diagnostic, and it has to be *this* code: a hang
15247        // is worse than any error, and `Ty::Error` without a diagnostic would be
15248        // worse still, because it unifies with anything and would turn the hang
15249        // into a silently wrong type.
15250        let branching =
15251            assoc_chain_src(16, |k| format!("Pair<Self::A{}, Self::A{}>", k + 1, k + 1));
15252        let r = check_src(&branching);
15253        // Three reports, all `E0078`, for one root cause. That is **pre-existing
15254        // duplication**, not a consequence of this fix: `normalize` has three call
15255        // sites this program reaches — conformance normalizes the trait side and
15256        // the impl side of the same signature (two, same span), and
15257        // `check_fn_body` normalizes `sig.params` (one, the body's span) — and
15258        // nothing in this codebase deduplicates diagnostics. Verified by rebuilding
15259        // the base commit and counting: `nova check` printed three `E0078`s there
15260        // too, and the number of `self.normalize(` call sites is unchanged at 8.
15261        //
15262        // Asserted rather than tolerated, because before this seam was reachable
15263        // nobody could see it. Asserting only "contains E0078" would hide both the
15264        // duplication and any *other* code creeping in.
15265        assert_eq!(
15266            error_codes(&r),
15267            ["E0078", "E0078", "E0078"],
15268            "{:?}",
15269            r.diagnostics
15270        );
15271        // And that it reports the *size* limit, not the depth one. The chain here
15272        // is 16 links, comfortably inside the depth limit of 64, so a message
15273        // blaming depth would be actively misleading — it would tell the user to
15274        // shorten a chain that is already short. Added after a mutation that
15275        // reported `NormalizeLimit::Depth` for every overflow survived this test
15276        // with only the code asserted.
15277        assert!(
15278            r.diagnostics[0]
15279                .message
15280                .contains("resolves to more than 10000 type nodes"),
15281            "a wide chain is a size report, not a depth report: {}",
15282            r.diagnostics[0].message
15283        );
15284
15285        // The control: the same shape, small enough to resolve. Without this, the
15286        // test above would also pass if all branching were rejected outright.
15287        let small = assoc_chain_src(4, |k| format!("Pair<Self::A{}, Self::A{}>", k + 1, k + 1));
15288        let ok = check_src(&small);
15289        assert!(ok.diagnostics.is_empty(), "{:?}", ok.diagnostics);
15290    }
15291
15292    #[test]
15293    fn a_binding_chain_longer_than_the_depth_limit_is_a_diagnostic() {
15294        // A plain linear chain, which is neither a cycle nor large — it resolves
15295        // to `Int`. It is only *long*. This is the case that proves `E0078` is
15296        // reachable from source and that the comment claiming otherwise ("a
15297        // compiler defect") was wrong: 63 links check clean, 64 report.
15298        //
15299        // Both boundaries are asserted because a limit test that only checks the
15300        // rejecting side cannot tell the limit from a blanket refusal.
15301        let ok = check_src(&assoc_chain_src(63, |k| format!("Self::A{}", k + 1)));
15302        assert!(ok.diagnostics.is_empty(), "{:?}", ok.diagnostics);
15303        let r = check_src(&assoc_chain_src(64, |k| format!("Self::A{}", k + 1)));
15304        // Three, for the pre-existing reason recorded on the test above.
15305        assert_eq!(
15306            error_codes(&r),
15307            ["E0078", "E0078", "E0078"],
15308            "{:?}",
15309            r.diagnostics
15310        );
15311        // And that the two limits are told apart in the message. A single
15312        // catch-all wording would make this chain read as if it were too big.
15313        assert!(
15314            r.diagnostics[0].message.contains("more than 64 deep"),
15315            "a long chain is a depth report, not a size report: {}",
15316            r.diagnostics[0].message
15317        );
15318    }
15319
15320    // === Normalization seam 2: impl conformance (design doc §4.1, risk 2) ===
15321    //
15322    // The dangerous direction here is **over-acceptance**. This seam makes two
15323    // types that used to compare unequal compare equal, so a suite that only
15324    // checks "the good impl is accepted" cannot tell the real fix from deleting
15325    // the comparison. Every positive test below is therefore paired with the
15326    // negative the same code path must still reject.
15327
15328    #[test]
15329    fn an_impl_may_echo_the_projection_or_write_the_concrete_type() {
15330        // Both spellings must be accepted (design doc §5.1).
15331        for ret in ["T", "Self::Item"] {
15332            let src = format!(
15333                "trait It {{ type Item\n fn get_item(self) -> Self::Item }}\n\
15334                 record W<T> {{ v: T }}\n\
15335                 impl<T> It for W<T> {{ type Item = T\n fn get_item(self) -> {ret} {{ self.v }} }}\n\
15336                 fn main() {{ }}"
15337            );
15338            let r = check_src(&src);
15339            assert!(r.diagnostics.is_empty(), "ret = {ret}: {:?}", r.diagnostics);
15340        }
15341    }
15342
15343    #[test]
15344    fn a_genuinely_wrong_impl_signature_still_reports_e0072() {
15345        // The risk is that normalizing to make the two spellings agree also
15346        // makes everything agree. Self::Item is T here, so returning Bool is
15347        // wrong and must still be caught.
15348        let r = check_src(
15349            "trait It { type Item\n fn get_item(self) -> Self::Item }\n\
15350             record W<T> { v: T }\n\
15351             impl<T> It for W<T> { type Item = T\n fn get_item(self) -> Bool { true } }\n\
15352             fn main() { }",
15353        );
15354        // Exactly one, and exactly the conformance error — not a `contains` that
15355        // would also hold if the fix had added a second, cascading diagnostic.
15356        assert_eq!(error_codes(&r), ["E0072"], "{:?}", r.diagnostics);
15357        // And the message names the **normalized** trait type. Before this seam
15358        // it read "declares `W<T0>::Item`" — the projection the user never
15359        // wrote. A fix that normalized only the impl side would still report
15360        // E0072 here, so the code alone cannot tell the two apart.
15361        let msg = &r.diagnostics[0].message;
15362        assert!(
15363            msg.contains("returns `Bool`") && msg.contains("declares `T0`"),
15364            "the diagnostic reports the normalized trait type: {msg}"
15365        );
15366    }
15367
15368    #[test]
15369    fn conformance_normalizes_parameter_types_not_only_the_return_type() {
15370        // Normalizing only the return type is the plausible half-fix: it passes
15371        // every return-position test in this file and leaves this one reporting a
15372        // bogus E0072 on parameter 1.
15373        let ok = check_src(
15374            "trait It { type Item\n fn put(self, x: Self::Item) -> Int }\n\
15375             record W<T> { v: T }\n\
15376             impl<T> It for W<T> { type Item = T\n fn put(self, x: T) -> Int { 1 } }\n\
15377             fn main() { }",
15378        );
15379        assert!(ok.diagnostics.is_empty(), "{:?}", ok.diagnostics);
15380        // The negative: `Self::Item` is `T`, so `x: Bool` is wrong and must still
15381        // be rejected. `x` is deliberately unused, which produces no diagnostic
15382        // of its own — measured, so the exact-sequence assertion is safe.
15383        let bad = check_src(
15384            "trait It { type Item\n fn put(self, x: Self::Item) -> Int }\n\
15385             record W<T> { v: T }\n\
15386             impl<T> It for W<T> { type Item = T\n fn put(self, x: Bool) -> Int { 1 } }\n\
15387             fn main() { }",
15388        );
15389        assert_eq!(error_codes(&bad), ["E0072"], "{:?}", bad.diagnostics);
15390        let msg = &bad.diagnostics[0].message;
15391        assert!(
15392            msg.contains("parameter 1") && msg.contains("declares `T0`"),
15393            "the parameter diagnostic reports the normalized trait type: {msg}"
15394        );
15395    }
15396
15397    #[test]
15398    fn conformance_normalizes_inside_a_compound_type() {
15399        // `[Self::Item]` against `[T]`. A comparison that normalized only a
15400        // top-level projection passes every other positive test here and fails
15401        // this one — the same boundary
15402        // `a_projection_nested_inside_a_compound_type_normalizes` pins for seam
15403        // 1, at seam 2.
15404        let ok = check_src(
15405            "trait It { type Item\n fn items(self) -> [Self::Item] }\n\
15406             record W<T> { v: T }\n\
15407             impl<T> It for W<T> { type Item = T\n fn items(self) -> [T] { [self.v] } }\n\
15408             fn main() { }",
15409        );
15410        assert!(ok.diagnostics.is_empty(), "{:?}", ok.diagnostics);
15411        // `[Bool]` is still wrong, so the compound case is not waved through
15412        // wholesale.
15413        let bad = check_src(
15414            "trait It { type Item\n fn items(self) -> [Self::Item] }\n\
15415             record W<T> { v: T }\n\
15416             impl<T> It for W<T> { type Item = T\n fn items(self) -> [Bool] { [true] } }\n\
15417             fn main() { }",
15418        );
15419        assert_eq!(error_codes(&bad), ["E0072"], "{:?}", bad.diagnostics);
15420    }
15421
15422    #[test]
15423    fn conformance_still_rejects_a_wrong_arity_and_a_wrong_receiver() {
15424        // Neither of these compares a projection at all — the arity check counts
15425        // parameters and the receiver check compares two bools — but both sit on
15426        // the code path this seam moved out of `check_impl_conformance`, and both
15427        // `continue` past the type comparison. They are the checks that can
15428        // silently stop running while every projection-shaped test stays green.
15429        let arity = check_src(
15430            "trait It { type Item\n fn put(self, x: Self::Item) -> Int }\n\
15431             record W<T> { v: T }\n\
15432             impl<T> It for W<T> { type Item = T\n fn put(self) -> Int { 1 } }\n\
15433             fn main() { }",
15434        );
15435        assert_eq!(error_codes(&arity), ["E0072"], "{:?}", arity.diagnostics);
15436        // Specifically the arity message, not a per-parameter mismatch: the
15437        // count is compared before substitution and normalization precisely so a
15438        // wrong arity reads as one.
15439        assert!(
15440            arity.diagnostics[0].message.contains("0 parameter(s)"),
15441            "{}",
15442            arity.diagnostics[0].message
15443        );
15444        // A trait method declared with `self`, implemented as an associated
15445        // function. The return types agree (`T` on both sides once normalized),
15446        // so the receiver is the only disagreement and nothing else can catch it.
15447        let recv = check_src(
15448            "trait It { type Item\n fn get_item(self) -> Self::Item }\n\
15449             record W<T> { v: T }\n\
15450             impl<T> It for W<T> { type Item = T\n\
15451             fn get_item() -> T { panic(\"no\") } }\n\
15452             fn main() { }",
15453        );
15454        assert_eq!(error_codes(&recv), ["E0072"], "{:?}", recv.diagnostics);
15455        assert!(
15456            recv.diagnostics[0].message.contains("`self` receiver"),
15457            "{}",
15458            recv.diagnostics[0].message
15459        );
15460    }
15461
15462    #[test]
15463    fn conformance_normalizes_through_the_matching_impl_not_the_only_one() {
15464        // Two impls of one trait with **different** bindings, each writing its
15465        // own concrete type. A normalizer that takes the first impl, or the only
15466        // impl, or ignores the self type answers `T` where `[T]` is wanted and
15467        // reports E0072 on one of them — which a single-impl test cannot see.
15468        let prelude = "trait It { type Item\n fn peek(self) -> Self::Item }\n\
15469             record W<T> { v: T }\n\
15470             record K<T> { k: T }\n";
15471        let ok = check_src(&format!(
15472            "{prelude}\
15473             impl<T> It for W<T> {{ type Item = T\n fn peek(self) -> T {{ self.v }} }}\n\
15474             impl<T> It for K<T> {{ type Item = [T]\n fn peek(self) -> [T] {{ [self.k] }} }}\n\
15475             fn main() {{ }}"
15476        ));
15477        assert!(ok.diagnostics.is_empty(), "{:?}", ok.diagnostics);
15478        // Swap the two return types and *both* impls must be rejected. Without
15479        // this half, the positive above would also hold for a normalizer that
15480        // answered "whichever binding fits" rather than "this impl's binding".
15481        let swapped = check_src(&format!(
15482            "{prelude}\
15483             impl<T> It for W<T> {{ type Item = T\n fn peek(self) -> [T] {{ [self.v] }} }}\n\
15484             impl<T> It for K<T> {{ type Item = [T]\n fn peek(self) -> T {{ self.k }} }}\n\
15485             fn main() {{ }}"
15486        ));
15487        assert_eq!(
15488            error_codes(&swapped),
15489            ["E0072", "E0072"],
15490            "{:?}",
15491            swapped.diagnostics
15492        );
15493    }
15494
15495    #[test]
15496    fn conformance_resolves_a_projection_bound_by_a_later_declared_impl() {
15497        // The discriminating test for why this seam is a post-collection pass
15498        // rather than a `normalize` inside `check_impl_conformance` with the
15499        // `ImplInfo` push hoisted above the call.
15500        //
15501        // A supertrait's associated type is reachable from a subtrait method's
15502        // signature: `collect_traits` puts the trait *plus its expanded
15503        // supertraits* in `sig_bounds[0]`, so `Self::Elem` inside
15504        // `trait Ext: Base` is `Base`'s `Elem`. Substituting `Self` at
15505        // conformance yields `Assoc { on: W }` — a projection with a head whose
15506        // binding lives in a **different** impl, `impl Base for W`, which the
15507        // user may write either side of `impl Ext for W`.
15508        //
15509        // Hoisting the push fixes only the impl's own bindings: the "sub first"
15510        // ordering below would still fail, because normalization consults the
15511        // whole table and would see only impls from earlier items. Nova has no
15512        // declaration-order rule for impls — the same reason
15513        // `check_supertrait_impls` was moved out of conformance — so both
15514        // orderings must pass.
15515        //
15516        // **Two shapes, added in Task 11 Step 6.** The original test only had
15517        // the primitive-on-a-non-generic-record row, which resolves the
15518        // projection through a **ground** self type: `Assoc { on: W }`, whose
15519        // binding is `Int`. Nothing there can tell whether the impl's own type
15520        // arguments reach the binding at all, because `Int.subst(_)` is `Int`
15521        // for every argument list.
15522        //
15523        // The second row is the one with teeth: the supertrait impl binds
15524        // `Elem = A` — its *first* parameter — and the subtrait impl is
15525        // partially concrete, `impl<T> Ext for W<Int, T>`. So conformance
15526        // resolves `Assoc { on: W<Int, Param(0)> }`, `match_args` recovers
15527        // `[Int, Param(0)]`, and `Elem` is `Int` **only if** those arguments are
15528        // substituted into the binding. Measured: dropping the `subst` in
15529        // `hir::normalize_ty`'s `Assoc` arm leaves the primitive row passing and
15530        // fails this one.
15531        //
15532        // The plan asked for `record W<T>` with `type Elem = T`. That is not
15533        // enough, and the reason is worth recording: `match_args` on
15534        // `W<Param(0)>` recovers `[Param(0)]`, so `Param(0).subst([Param(0)])` is
15535        // the *identity* — the substitution runs but cannot be observed, and the
15536        // subst-dropping mutation survives it. A parameter of the impl only
15537        // "survives substitution" observably when the argument it maps to is
15538        // something else.
15539        for (shape, decl, ext_ok, ext_bad, base) in [
15540            (
15541                "a primitive on a non-generic record",
15542                "record W { v: Int }\n",
15543                "impl Ext for W { fn peek(self) -> Int { self.v } }\n",
15544                "impl Ext for W { fn peek(self) -> Bool { true } }\n",
15545                "impl Base for W { type Elem = Int }\n",
15546            ),
15547            (
15548                "the impl's own arguments substituted into a supertrait's binding",
15549                "record W<A, B> { a: A\n b: B }\n",
15550                "impl<T> Ext for W<Int, T> { fn peek(self) -> Int { self.a } }\n",
15551                "impl<T> Ext for W<Int, T> { fn peek(self) -> Bool { true } }\n",
15552                "impl<A, B> Base for W<A, B> { type Elem = A }\n",
15553            ),
15554        ] {
15555            let src = |impls: String| {
15556                format!(
15557                    "trait Base {{ type Elem }}\n\
15558                     trait Ext: Base {{ fn peek(self) -> Self::Elem }}\n\
15559                     {decl}{impls}fn main() {{ }}"
15560                )
15561            };
15562            for (order, impls) in [
15563                ("sub first", format!("{ext_ok}{base}")),
15564                ("super first", format!("{base}{ext_ok}")),
15565            ] {
15566                let r = check_src(&src(impls));
15567                assert!(
15568                    r.diagnostics.is_empty(),
15569                    "{shape}, {order}: {:?}",
15570                    r.diagnostics
15571                );
15572            }
15573            // Not vacuous in either order: `Elem` is the element type, so a
15574            // `Bool` return is still wrong however the two impls are ordered.
15575            // Without this half, a pass that skipped the comparison whenever a
15576            // projection was involved would satisfy the loop above.
15577            for (order, impls) in [
15578                ("sub first", format!("{ext_bad}{base}")),
15579                ("super first", format!("{base}{ext_bad}")),
15580            ] {
15581                let r = check_src(&src(impls));
15582                assert_eq!(
15583                    error_codes(&r),
15584                    ["E0072"],
15585                    "{shape}, {order}: {:?}",
15586                    r.diagnostics
15587                );
15588            }
15589        }
15590    }
15591
15592    /// Task 11 Step 6, second half. The **selfless** branch of the conformance
15593    /// comparison, with a projection in it.
15594    ///
15595    /// A trait method declared without a receiver leaves the impl signature with
15596    /// no `self` to skip, so `check_impl_method_signatures` compares
15597    /// `impl_sig.params` as-is instead of `params[1..]`. Its nearest existing
15598    /// test, `selfless_trait_impl_method_checks_conformance_without_panicking`,
15599    /// cannot see the branch at all: with `fn zero() -> Int` both arms produce an
15600    /// empty parameter list, so always slicing `[1..]` is an equivalent mutant
15601    /// there. A *parameter* is what makes the branch observable.
15602    ///
15603    /// Measured, so the claim is exact rather than "this branch was untested":
15604    /// under the always-slice mutation the whole workspace reports **two**
15605    /// failures — this test and
15606    /// `trait_call_substitution_puts_self_before_the_methods_own_generics`, whose
15607    /// `fn make<U>(u: U) -> Self` row also puts a parameter on a receiverless
15608    /// trait method. So the branch was partly pinned; what had no test is the
15609    /// branch **with a projection on it**, where the parameter list the arm
15610    /// selects is also the list normalization runs over.
15611    ///
15612    /// The impl echoes `Self::Out` in both positions rather than writing `Int`,
15613    /// so this is also the §5.1 "either spelling" row for an associated function
15614    /// — the one place the echo was never exercised.
15615    #[test]
15616    fn conformance_normalizes_a_selfless_methods_projection() {
15617        let prelude = "trait Zero { type Out\n\
15618                       fn zero() -> Self::Out\n\
15619                       fn of(x: Self::Out) -> Self::Out }\n\
15620                       record P { v: Int }\n";
15621        // Echoed on the impl side, in a parameter and in the return type.
15622        let echoed = check_src(&format!(
15623            "{prelude}impl Zero for P {{ type Out = Int\n\
15624             fn zero() -> Int {{ 0 }}\n\
15625             fn of(x: Self::Out) -> Self::Out {{ x }} }}\n\
15626             fn main() {{ println(\"${{P::of(P::zero())}}\") }}"
15627        ));
15628        assert!(echoed.diagnostics.is_empty(), "{:?}", echoed.diagnostics);
15629        // And the concrete spelling of the same signature.
15630        let concrete = check_src(&format!(
15631            "{prelude}impl Zero for P {{ type Out = Int\n\
15632             fn zero() -> Int {{ 0 }}\n\
15633             fn of(x: Int) -> Int {{ x }} }}\n\
15634             fn main() {{ println(\"${{P::of(P::zero())}}\") }}"
15635        ));
15636        assert!(
15637            concrete.diagnostics.is_empty(),
15638            "{:?}",
15639            concrete.diagnostics
15640        );
15641        // Not vacuous: the *parameter* is what the branch decides, so a wrong
15642        // parameter type is the assertion that matters. `Out` is `Int`, so a
15643        // `Bool` parameter is a mismatch and must be reported as one — not as an
15644        // arity error, which is what slicing an already-receiverless list
15645        // produces.
15646        let wrong = check_src(&format!(
15647            "{prelude}impl Zero for P {{ type Out = Int\n\
15648             fn zero() -> Int {{ 0 }}\n\
15649             fn of(x: Bool) -> Int {{ 1 }} }}\n\
15650             fn main() {{ }}"
15651        ));
15652        assert_eq!(error_codes(&wrong), ["E0072"], "{:?}", wrong.diagnostics);
15653        assert!(
15654            wrong.diagnostics[0].message.contains("parameter 1"),
15655            "the parameter is compared, not sliced away: {}",
15656            wrong.diagnostics[0].message
15657        );
15658    }
15659
15660    #[test]
15661    fn duplicate_supertrait_is_deduplicated() {
15662        // `trait B: A + A` must not record `A` twice — a repeat reads as two
15663        // providers of `a` and yields a false E0015 at the call site.
15664        let r = check_src(
15665            "trait A { fn a(self) -> Int }\n\
15666             trait B: A + A { fn b(self) -> Int }\n\
15667             record R { v: Int }\n\
15668             impl A for R { fn a(self) -> Int { 1 } }\n\
15669             impl B for R { fn b(self) -> Int { 2 } }\n\
15670             fn only_a<T: B>(x: T) -> Int { x.a() }\n\
15671             fn main() { println(\"${only_a(R { v: 0 })}\") }",
15672        );
15673        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
15674        let b = r
15675            .module
15676            .traits
15677            .iter()
15678            .find(|t| t.name == "B")
15679            .expect("trait B collected");
15680        assert_eq!(b.supertraits.len(), 1, "supertraits: {:?}", b.supertraits);
15681    }
15682
15683    #[test]
15684    fn std_core_parses_and_typechecks_clean() {
15685        // The implicit std/core module must itself be error-free; a program that
15686        // uses nothing from it must produce no diagnostics.
15687        let r = check_src("fn main() { println(\"hi\") }");
15688        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
15689    }
15690
15691    #[test]
15692    fn std_core_option_is_available_without_import() {
15693        let r = check_src(
15694            "fn main() {\n\
15695                 let o = Some(3)\n\
15696                 match o { Some(v) => println(\"${v}\"), None => println(\"none\") }\n\
15697             }",
15698        );
15699        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
15700    }
15701
15702    #[test]
15703    fn std_core_option_result_methods_typecheck() {
15704        let r = check_src(
15705            "fn dbl(n: Int) -> Int { n * 2 }\n\
15706             fn main() {\n\
15707                 let a = Some(21).map(dbl).unwrap_or(0)\n\
15708                 let b = Some(1).is_some()\n\
15709                 let c: Result<Int, String> = Some(2).ok_or(\"none\")\n\
15710                 let d = c.map(dbl).unwrap_or(0)\n\
15711                 println(\"${a} ${b} ${d}\")\n\
15712             }",
15713        );
15714        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
15715    }
15716
15717    #[test]
15718    fn std_core_traits_and_primitive_impls_typecheck() {
15719        let r = check_src(
15720            "fn show_all<T: Display>(x: T) -> String { x.fmt() }\n\
15721             fn main() {\n\
15722                 println(show_all(3))\n\
15723                 println(\"${(1).eq(1)}\")\n\
15724                 let o = (\"a\").cmp(\"b\")\n\
15725                 match o { Less => println(\"less\"), Equal => println(\"eq\"), \
15726                           Greater => println(\"gt\") }\n\
15727             }",
15728        );
15729        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
15730    }
15731
15732    #[test]
15733    fn vec_methods_typecheck() {
15734        let r = check_src(
15735            "fn main() {\n\
15736                 let mut v = Vec::new()\n\
15737                 v.push(1)\n\
15738                 v.push(2)\n\
15739                 println(\"${v.len()}\")\n\
15740                 match v.get(0) { Some(x) => println(\"${x}\"), None => println(\"none\") }\n\
15741                 match v.pop() { Some(x) => println(\"${x}\"), None => println(\"none\") }\n\
15742                 v.set(0, 9)\n\
15743                 v.clear()\n\
15744                 println(\"${v.len()}\")\n\
15745             }",
15746        );
15747        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
15748    }
15749
15750    #[test]
15751    fn map_methods_typecheck() {
15752        let r = check_src(
15753            "fn main() {\n\
15754                 let mut m = Map::new()\n\
15755                 let prev = m.insert(1, \"one\")\n\
15756                 println(\"${m.len()} ${m.contains_key(1)}\")\n\
15757                 match m.get(1) { Some(s) => println(s), None => println(\"none\") }\n\
15758                 match m.remove(1) { Some(s) => println(s), None => println(\"none\") }\n\
15759                 println(\"${m.len()}\")\n\
15760             }",
15761        );
15762        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
15763        // Not vacuous. `Ty::Error` unifies with anything, so an empty
15764        // diagnostic list alone would still hold if every one of these calls had
15765        // collapsed to an error expression — and `Map::new()`'s `K`/`V` are
15766        // fixed only by inference from the later calls, so a wrong signature
15767        // surfaces as an error type rather than as a diagnostic. Pin what the
15768        // checker actually built. `Ty::Unit` drops the `println`s, which are also
15769        // `Call`s; what remains is every `Map` method in the program, in source
15770        // order. `Option`'s and `Map`'s `DefId`s are matched structurally rather
15771        // than by number, but the type *arguments* are exact: `Map<Int, String>`
15772        // from `new`, and `Option<String>` — not `Option<_>` — from the three
15773        // that return one.
15774        let map_ty = |t: &Ty| matches!(t, Ty::Record { args, .. } if args.as_slice() == [Ty::Int, Ty::String]);
15775        let opt_str = |t: &Ty| matches!(t, Ty::Sum { args, .. } if args.as_slice() == [Ty::String]);
15776        let calls: Vec<Ty> = exprs_in(&r.module, "main")
15777            .into_iter()
15778            .filter(|e| matches!(e.kind, hir::ExprKind::Call { .. }) && e.ty != Ty::Unit)
15779            .map(|e| e.ty.clone())
15780            .collect();
15781        assert_eq!(calls.len(), 7, "seven non-unit `Map` calls: {calls:?}");
15782        assert!(
15783            map_ty(&calls[0]),
15784            "`Map::new()` is `Map<Int, String>`: {calls:?}"
15785        );
15786        assert!(
15787            opt_str(&calls[1]),
15788            "`insert` returns `Option<String>`: {calls:?}"
15789        );
15790        assert_eq!(calls[2], Ty::Int, "`len` returns `Int`: {calls:?}");
15791        assert_eq!(
15792            calls[3],
15793            Ty::Bool,
15794            "`contains_key` returns `Bool`: {calls:?}"
15795        );
15796        assert!(
15797            opt_str(&calls[4]),
15798            "`get` returns `Option<String>`: {calls:?}"
15799        );
15800        assert!(
15801            opt_str(&calls[5]),
15802            "`remove` returns `Option<String>`: {calls:?}"
15803        );
15804        assert_eq!(calls[6], Ty::Int, "`len` returns `Int`: {calls:?}");
15805    }
15806
15807    // `Map`'s key contract is `K: Hash + Eq`, and a key satisfying neither must
15808    // be rejected. That test is *not* here: like every trait bound in Nova
15809    // (`12-TYPESYSTEM.md` §5.4) it is discharged at monomorphization, so `check`
15810    // alone reports nothing at all for `Map<Unhashable, Int>` — the same reason
15811    // the conditional-impl note above gives. It lives in `nova-mir`'s
15812    // `map_key_without_hash_reports_e0013`, whose helper runs
15813    // `check` + `lower_module`, which is exactly what `nova check` runs
15814    // (`nova_driver::check_file`).
15815
15816    #[test]
15817    fn set_methods_typecheck() {
15818        let r = check_src(
15819            "fn main() {\n\
15820                 let mut s = Set::new()\n\
15821                 println(\"${s.insert(1)} ${s.insert(1)}\")\n\
15822                 println(\"${s.len()} ${s.contains(1)} ${s.contains(2)}\")\n\
15823                 println(\"${s.remove(1)} ${s.len()}\")\n\
15824             }",
15825        );
15826        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
15827        // Not vacuous, as `map_methods_typecheck` above notes: `Ty::Error`
15828        // unifies with anything, and `Set::new()`'s `T` is fixed only by
15829        // inference from the later calls, so a wrong signature would surface
15830        // as an error type rather than as a diagnostic. Pin what the checker
15831        // actually built. `Ty::Unit` drops the `println`s, which are also
15832        // `Call`s; what remains is every `Set` method call, in source order.
15833        // `Set`'s own `DefId` is looked up rather than assumed, because `Vec`
15834        // is also a single-type-argument record and `args == [Ty::Int]` alone
15835        // would not tell the two apart.
15836        let set_def_id = r
15837            .module
15838            .records
15839            .iter()
15840            .find(|rt| rt.name == "Set")
15841            .expect("Set is defined")
15842            .def_id;
15843        let set_int = |t: &Ty| matches!(t, Ty::Record { def_id, args } if *def_id == set_def_id && args.as_slice() == [Ty::Int]);
15844        let calls: Vec<Ty> = exprs_in(&r.module, "main")
15845            .into_iter()
15846            .filter(|e| matches!(e.kind, hir::ExprKind::Call { .. }) && e.ty != Ty::Unit)
15847            .map(|e| e.ty.clone())
15848            .collect();
15849        assert_eq!(calls.len(), 8, "eight non-unit `Set` calls: {calls:?}");
15850        assert!(set_int(&calls[0]), "`Set::new()` is `Set<Int>`: {calls:?}");
15851        assert_eq!(
15852            calls[1],
15853            Ty::Bool,
15854            "first `insert` returns `Bool`: {calls:?}"
15855        );
15856        assert_eq!(
15857            calls[2],
15858            Ty::Bool,
15859            "second `insert` returns `Bool`: {calls:?}"
15860        );
15861        assert_eq!(calls[3], Ty::Int, "`len` returns `Int`: {calls:?}");
15862        assert_eq!(
15863            calls[4],
15864            Ty::Bool,
15865            "first `contains` returns `Bool`: {calls:?}"
15866        );
15867        assert_eq!(
15868            calls[5],
15869            Ty::Bool,
15870            "second `contains` returns `Bool`: {calls:?}"
15871        );
15872        assert_eq!(calls[6], Ty::Bool, "`remove` returns `Bool`: {calls:?}");
15873        assert_eq!(calls[7], Ty::Int, "final `len` returns `Int`: {calls:?}");
15874    }
15875
15876    /// Written as an exhaustive `match` rather than a list of `assert_eq!`s so
15877    /// that adding a `Builtin` without stating its expected signature does not
15878    /// compile. The previous hand-written list silently missed `StrLenChars`.
15879    #[test]
15880    fn builtin_signatures_are_what_the_std_call_sites_use() {
15881        // The site each signature has to satisfy, so a mismatch names the
15882        // caller it would break rather than only the types.
15883        fn expected(b: Builtin) -> ((Vec<Ty>, Ty), &'static str) {
15884            match b {
15885                Builtin::Println | Builtin::Print | Builtin::EPrint | Builtin::EPrintln => (
15886                    (vec![Ty::String], Ty::Unit),
15887                    "`println(s)` / `print(s)` / `eprint(s)` / `eprintln(s)`",
15888                ),
15889                Builtin::Panic => ((vec![Ty::String], Ty::Never), "`panic(msg)` diverges"),
15890                Builtin::StrCmp => (
15891                    (vec![Ty::String, Ty::String], Ty::Int),
15892                    "`str_cmp(self, other)` in `impl Ord for String`",
15893                ),
15894                Builtin::StrHash => (
15895                    (vec![Ty::String], Ty::Int),
15896                    "`str_hash(self)` in `impl Hash for String`",
15897                ),
15898                Builtin::CharToInt => (
15899                    (vec![Ty::Char], Ty::Int),
15900                    "`char_to_int(self)` in `impl Hash for Char`",
15901                ),
15902                Builtin::StrLenChars => (
15903                    (vec![Ty::String], Ty::Int),
15904                    "`str_len_chars(self)` in `String::len`",
15905                ),
15906                Builtin::StrChars => (
15907                    (vec![Ty::String], Ty::Array(Box::new(Ty::Char))),
15908                    "`str_chars(self)` in `String::chars`",
15909                ),
15910                Builtin::StrFromChars => (
15911                    (vec![Ty::Array(Box::new(Ty::Char))], Ty::String),
15912                    "`str_from_chars(cs)` in `chars_to_string`",
15913                ),
15914                Builtin::StrJoin => (
15915                    (
15916                        vec![Ty::String, Ty::Array(Box::new(Ty::String))],
15917                        Ty::String,
15918                    ),
15919                    "`str_join(self, parts)` in `String::join`",
15920                ),
15921                Builtin::StrIndexOf => (
15922                    (vec![Ty::String, Ty::String], Ty::Int),
15923                    "`str_index_of(self, needle)` in `String::index_of`",
15924                ),
15925                Builtin::JsonQuote => (
15926                    (vec![Ty::String], Ty::String),
15927                    "`json_quote(s)` in `std/json`'s `quote`",
15928                ),
15929                Builtin::StrToUpper => (
15930                    (vec![Ty::String], Ty::String),
15931                    "`str_to_upper(self)` in `String::to_upper`",
15932                ),
15933                Builtin::StrToLower => (
15934                    (vec![Ty::String], Ty::String),
15935                    "`str_to_lower(self)` in `String::to_lower`",
15936                ),
15937                Builtin::FloatFixed => (
15938                    (vec![Ty::Float, Ty::Int], Ty::String),
15939                    "`float_fixed(self, places)` in `Float::fixed`",
15940                ),
15941                Builtin::StrToFloat => (
15942                    (vec![Ty::String], Ty::Float),
15943                    "`str_to_float(span)` in `std/json`'s `scan_number`",
15944                ),
15945                Builtin::TestSelector => (
15946                    (vec![], Ty::Int),
15947                    "the synthesized `main`'s dispatch, reading `NOVA_TEST_INDEX`",
15948                ),
15949                Builtin::TaskSpawn => (
15950                    (vec![Ty::Future(Box::new(Ty::Param(0)))], Ty::Int),
15951                    "`task_spawn(fut)` in `std/task`'s `spawn<T>`",
15952                ),
15953                Builtin::TaskIsDone => (
15954                    (vec![Ty::Future(Box::new(Ty::Param(0)))], Ty::Bool),
15955                    "no call site in `std/task` since Task 3's `join` -- kept \
15956                     as part of the executor's Nova-facing surface",
15957                ),
15958                Builtin::TaskRelease => (
15959                    (vec![Ty::Future(Box::new(Ty::Param(0)))], Ty::Unit),
15960                    "`task_release(self.fut)` in `JoinHandle<T>::join`",
15961                ),
15962                Builtin::TaskDrive => (
15963                    (vec![Ty::Future(Box::new(Ty::Param(0)))], Ty::Unit),
15964                    "`task_drive(fut)` in `std/task`'s `block_on<T>`",
15965                ),
15966                Builtin::TaskOutput => (
15967                    (vec![Ty::Future(Box::new(Ty::Param(0)))], Ty::Param(0)),
15968                    "`task_output(fut)` in `std/task`'s `block_on<T>` and `JoinHandle<T>::join`",
15969                ),
15970                Builtin::TaskYieldFuture => (
15971                    (vec![], Ty::Future(Box::new(Ty::Unit))),
15972                    "`task_yield_future().await` in `std/task`'s `yield_now`",
15973                ),
15974                Builtin::TaskSleepFutureNanos => (
15975                    (vec![Ty::Int], Ty::Future(Box::new(Ty::Unit))),
15976                    "`task_sleep_future_nanos(nanos).await` in `std/time`'s `sleep`",
15977                ),
15978                Builtin::TaskJoinFuture => (
15979                    (
15980                        vec![Ty::Future(Box::new(Ty::Param(0)))],
15981                        Ty::Future(Box::new(Ty::Unit)),
15982                    ),
15983                    "`task_join_future(self.fut).await` in `JoinHandle<T>::join`",
15984                ),
15985                Builtin::TaskTimeoutFuture => (
15986                    (
15987                        vec![Ty::Int, Ty::Future(Box::new(Ty::Param(0)))],
15988                        Ty::Future(Box::new(Ty::Int)),
15989                    ),
15990                    "`task_timeout_future(nanos, fut).await` in `std/time`'s `timeout`",
15991                ),
15992                Builtin::FsReadToString => (
15993                    (vec![Ty::String], Ty::Int),
15994                    "`fs_read_to_string(path)` in `std/fs`'s `read_to_string`",
15995                ),
15996                Builtin::FsWriteString => (
15997                    (vec![Ty::String, Ty::String], Ty::Int),
15998                    "`fs_write_string(path, content)` in `std/fs`'s `write_string`",
15999                ),
16000                Builtin::FsTakeString => (
16001                    (vec![], Ty::String),
16002                    "`fs_take_string()` in `std/fs`'s `read_to_string`",
16003                ),
16004                Builtin::FsLastErrorMessage => (
16005                    (vec![], Ty::String),
16006                    "`fs_last_error_message()` in every fallible `std/fs` wrapper: \
16007                     `read_to_string`, `write_string`, `create_dir`, \
16008                     `create_dir_all`, `remove_file`, `remove_dir_all`, `read_dir`",
16009                ),
16010                Builtin::FsTempDir => (
16011                    (vec![], Ty::String),
16012                    "`fs_temp_dir()` in `std/fs`'s `temp_dir`",
16013                ),
16014                Builtin::FsExists => (
16015                    (vec![Ty::String], Ty::Bool),
16016                    "`fs_exists(path)` in `std/fs`'s `exists`",
16017                ),
16018                Builtin::FsCreateDir => (
16019                    (vec![Ty::String], Ty::Int),
16020                    "`fs_create_dir(path)` in `std/fs`'s `create_dir`",
16021                ),
16022                Builtin::FsCreateDirAll => (
16023                    (vec![Ty::String], Ty::Int),
16024                    "`fs_create_dir_all(path)` in `std/fs`'s `create_dir_all`",
16025                ),
16026                Builtin::FsRemoveFile => (
16027                    (vec![Ty::String], Ty::Int),
16028                    "`fs_remove_file(path)` in `std/fs`'s `remove_file`",
16029                ),
16030                Builtin::FsRemoveDirAll => (
16031                    (vec![Ty::String], Ty::Int),
16032                    "`fs_remove_dir_all(path)` in `std/fs`'s `remove_dir_all`",
16033                ),
16034                Builtin::FsReadDir => (
16035                    (vec![Ty::String], Ty::Int),
16036                    "`fs_read_dir(path)` in `std/fs`'s `read_dir`",
16037                ),
16038                Builtin::FsTakeStringArray => (
16039                    (vec![], Ty::Array(Box::new(Ty::String))),
16040                    "`fs_take_string_array()` in `std/fs`'s `read_dir`",
16041                ),
16042                Builtin::FsKind => (
16043                    (vec![Ty::String], Ty::Int),
16044                    "`fs_kind(path)` in `std/fs`'s `read_dir`",
16045                ),
16046                Builtin::FsRead => (
16047                    (vec![Ty::String], Ty::Int),
16048                    "`fs_read(path)` in `std/fs`'s `read`",
16049                ),
16050                Builtin::FsTakeBytes => (
16051                    (vec![], Ty::Bytes),
16052                    "`fs_take_bytes()` in `std/fs`'s `read`",
16053                ),
16054                Builtin::FsWrite => (
16055                    (vec![Ty::String, Ty::Bytes], Ty::Int),
16056                    "`fs_write(path, content)` in `std/fs`'s `write`",
16057                ),
16058                Builtin::FileOpen => (
16059                    (
16060                        vec![
16061                            Ty::String,
16062                            Ty::Bool,
16063                            Ty::Bool,
16064                            Ty::Bool,
16065                            Ty::Bool,
16066                            Ty::Bool,
16067                            Ty::Bool,
16068                        ],
16069                        Ty::Int,
16070                    ),
16071                    "`file_open(path, read, write, append, truncate, create, \
16072                     create_new)` in `std/fs`'s `open`",
16073                ),
16074                Builtin::FileClose => (
16075                    (vec![Ty::Int], Ty::Int),
16076                    "`file_close(self.fd)` in `std/fs`'s `File::close`",
16077                ),
16078                Builtin::FileRead => (
16079                    (vec![Ty::Int, Ty::Int], Ty::Int),
16080                    "`file_read(f.fd, max)` in `std/fs`'s `read_file`, which \
16081                     backs `File`'s `Read::read`",
16082                ),
16083                Builtin::FileWrite => (
16084                    (vec![Ty::Int, Ty::Bytes], Ty::Int),
16085                    "`file_write(f.fd, buf)` in `std/fs`'s `write_file`, which \
16086                     backs `File`'s `Write::write`",
16087                ),
16088                Builtin::FileFlush => (
16089                    (vec![Ty::Int], Ty::Int),
16090                    "`file_flush(f.fd)` in `std/fs`'s `flush_file`, which backs \
16091                     `File`'s `Write::flush`",
16092                ),
16093                Builtin::IoStdinRead => (
16094                    (vec![Ty::Int], Ty::Int),
16095                    "`io_stdin_read(max)` in `std/io`'s `read_stdin`, which \
16096                     backs `Stdin`'s `Read::read`",
16097                ),
16098                Builtin::IoStdoutWrite => (
16099                    (vec![Ty::Bytes], Ty::Int),
16100                    "`io_stdout_write(buf)` in `std/io`'s `write_stdout`, \
16101                     which backs `Stdout`'s `Write::write`",
16102                ),
16103                Builtin::IoStderrWrite => (
16104                    (vec![Ty::Bytes], Ty::Int),
16105                    "`io_stderr_write(buf)` in `std/io`'s `write_stderr`, \
16106                     which backs `Stderr`'s `Write::write`",
16107                ),
16108                Builtin::IoStdoutFlush => (
16109                    (vec![], Ty::Int),
16110                    "`io_stdout_flush()` in `std/io`'s `flush_stdout`, which \
16111                     backs `Stdout`'s `Write::flush`",
16112                ),
16113                Builtin::IoStderrFlush => (
16114                    (vec![], Ty::Int),
16115                    "`io_stderr_flush()` in `std/io`'s `flush_stderr`, which \
16116                     backs `Stderr`'s `Write::flush`",
16117                ),
16118                Builtin::NetConnect => (
16119                    (vec![Ty::String], Ty::Future(Box::new(Ty::Int))),
16120                    "`net_connect(addr).await` in `std/net`'s `connect`",
16121                ),
16122                Builtin::NetClose => (
16123                    (vec![Ty::Int], Ty::Int),
16124                    "`net_close(self.fd)` in `std/net`'s `TcpStream::close`",
16125                ),
16126                Builtin::NetRead => (
16127                    (vec![Ty::Int, Ty::Int], Ty::Future(Box::new(Ty::Int))),
16128                    "`net_read(s.fd, max).await` in `std/net`'s `read_stream`, \
16129                     which backs `TcpStream`'s `Read::read`",
16130                ),
16131                Builtin::NetWrite => (
16132                    (vec![Ty::Int, Ty::Bytes], Ty::Future(Box::new(Ty::Int))),
16133                    "`net_write(s.fd, buf).await` in `std/net`'s `write_stream`, \
16134                     which backs `TcpStream`'s `Write::write`",
16135                ),
16136                Builtin::NetReadTimeout => (
16137                    (
16138                        vec![Ty::Int, Ty::Int, Ty::Int],
16139                        Ty::Future(Box::new(Ty::Int)),
16140                    ),
16141                    "`net_read_timeout(self.fd, max, ms).await` in `std/net`'s \
16142                     `TcpStream::read_timeout`",
16143                ),
16144                Builtin::NetListen => (
16145                    (vec![Ty::String], Ty::Int),
16146                    "`net_listen(addr)` -- with no `.await`, since it cannot \
16147                     suspend -- in `std/net`'s `bind`",
16148                ),
16149                Builtin::NetLocalPort => (
16150                    (vec![Ty::Int], Ty::Int),
16151                    "`net_local_port(self.fd)` -- with no `.await`, since it \
16152                     cannot suspend -- in `std/net`'s `TcpListener::local_port`",
16153                ),
16154                Builtin::NetAccept => (
16155                    (vec![Ty::Int], Ty::Future(Box::new(Ty::Int))),
16156                    "`net_accept(self.fd).await` in `std/net`'s \
16157                     `TcpListener::accept`",
16158                ),
16159                Builtin::BytesLen => (
16160                    (vec![Ty::Bytes], Ty::Int),
16161                    "`bytes_len(self)` in `Bytes::len`",
16162                ),
16163                Builtin::BytesFromString => (
16164                    (vec![Ty::String], Ty::Bytes),
16165                    "`bytes_from_string_intrinsic(s)` in the free function `bytes_from_string`",
16166                ),
16167                Builtin::BytesIsUtf8 => (
16168                    (vec![Ty::Bytes], Ty::Bool),
16169                    "`bytes_is_utf8(self)` in `Bytes::to_string`",
16170                ),
16171                Builtin::BytesToStringUnchecked => (
16172                    (vec![Ty::Bytes], Ty::String),
16173                    "`bytes_to_string_unchecked(self)` in `Bytes::to_string`",
16174                ),
16175                Builtin::BytesAt => (
16176                    (vec![Ty::Bytes, Ty::Int], Ty::Int),
16177                    "`bytes_at(self, i)` in `Bytes::byte_at`",
16178                ),
16179                Builtin::BytesSlice => (
16180                    (vec![Ty::Bytes, Ty::Int, Ty::Int], Ty::Bytes),
16181                    "`bytes_slice(self, start, end)` in `Bytes::slice`",
16182                ),
16183                Builtin::BytesConcat => (
16184                    (vec![Ty::Bytes, Ty::Bytes], Ty::Bytes),
16185                    "`bytes_concat(self, other)` in `Bytes::concat`",
16186                ),
16187                Builtin::BytesToInts => (
16188                    (vec![Ty::Bytes], Ty::Array(Box::new(Ty::Int))),
16189                    "`bytes_to_ints(self)` in `Bytes::to_ints`",
16190                ),
16191                Builtin::BytesFromInts => (
16192                    (vec![Ty::Array(Box::new(Ty::Int))], Ty::Bytes),
16193                    "`bytes_from_ints_intrinsic(ints)` in the free function `bytes_from_ints`",
16194                ),
16195                Builtin::BytesEq => (
16196                    (vec![Ty::Bytes, Ty::Bytes], Ty::Bool),
16197                    "`bytes_eq(self, other)` in `impl Eq for Bytes`",
16198                ),
16199                Builtin::IntHashSeed => (
16200                    (vec![], Ty::Int),
16201                    "`int_hash_seed()` in `std/core`'s `Int`, `Bool` and `Char` `Hash` impls",
16202                ),
16203                Builtin::HttpParseRequest => (
16204                    (vec![Ty::Bytes], Ty::Array(Box::new(Ty::Int))),
16205                    "`http_parse_request(buf)` in `std/http`'s `parse_offsets`",
16206                ),
16207                Builtin::CryptoHash => (
16208                    (vec![Ty::Int, Ty::Bytes, Ty::Bytes, Ty::Bytes], Ty::Int),
16209                    "`crypto_hash(op, key, data, tag)` in `std/crypto`",
16210                ),
16211                Builtin::CryptoRandomBytes => (
16212                    (vec![Ty::Int], Ty::Int),
16213                    "`crypto_random_bytes(n)` in `std/crypto`",
16214                ),
16215                Builtin::CryptoRandomInt => (
16216                    (vec![Ty::Int, Ty::Int], Ty::Int),
16217                    "`crypto_random_int(min, max)` in `std/crypto`",
16218                ),
16219                Builtin::TimeNowNanos => (
16220                    (vec![], Ty::Int),
16221                    "`time_now_nanos()` in `std/time`'s `Instant::now`",
16222                ),
16223                Builtin::TimeNowEpochNanos => (
16224                    (vec![], Ty::Int),
16225                    "`time_now_epoch_nanos()` in `std/time`'s `SystemTime::now`",
16226                ),
16227                Builtin::LogConfigLevel => (
16228                    (vec![], Ty::Int),
16229                    "`log_config_level()` in the free function `emit`",
16230                ),
16231                Builtin::LogConfigToStderr => (
16232                    (vec![], Ty::Int),
16233                    "`log_config_to_stderr()` in the free function `emit`",
16234                ),
16235                Builtin::LogSetConfig => (
16236                    (vec![Ty::Int, Ty::Int], Ty::Unit),
16237                    "`log_set_config(level, to_stderr)` in `std/log`'s `Log::init_with`",
16238                ),
16239                Builtin::ShutdownRequested => (
16240                    (vec![], Ty::Bool),
16241                    "`shutdown_requested()` in `std/http`'s `Server::listen` and `serve`",
16242                ),
16243                Builtin::ProcessArgCount => (
16244                    (vec![], Ty::Int),
16245                    "`process_arg_count()` in `std/process`'s `args`",
16246                ),
16247                Builtin::ProcessArg => (
16248                    (vec![Ty::Int], Ty::String),
16249                    "`process_arg(i)` in `std/process`'s `args`",
16250                ),
16251                Builtin::ProcessExit => (
16252                    (vec![Ty::Int], Ty::Unit),
16253                    "`process_exit(code)` in `std/process`'s `exit`",
16254                ),
16255            }
16256        }
16257        for b in Builtin::ALL {
16258            let (sig, site) = expected(b);
16259            assert_eq!(builtin_signature(b), sig, "{}: {site}", b.name());
16260        }
16261    }
16262
16263    /// The five stream builtins are visible in an std module and typed as
16264    /// status words.
16265    ///
16266    /// `STD_ONLY` is seeded into every std module's scope, not per-module
16267    /// (see `Builtin::STD_ONLY`'s own doc), so `std/io` reaches these without
16268    /// new plumbing -- and so does any other std module, which is why their
16269    /// names carry the `io_` prefix rather than relying on scope to
16270    /// disambiguate.
16271    #[test]
16272    fn the_stream_builtins_are_std_only_and_return_status_words() {
16273        for name in [
16274            "io_stdin_read",
16275            "io_stdout_write",
16276            "io_stderr_write",
16277            "io_stdout_flush",
16278            "io_stderr_flush",
16279        ] {
16280            assert!(
16281                nova_resolver::Builtin::STD_ONLY
16282                    .iter()
16283                    .any(|b| b.name() == name),
16284                "{name} must be an STD_ONLY builtin"
16285            );
16286        }
16287    }
16288
16289    /// The five file builtins are `STD_ONLY` and return status words.
16290    ///
16291    /// `STD_ONLY` is seeded into every std module's scope rather than per-module
16292    /// (see `Builtin::STD_ONLY`'s own doc), which is why these carry a `file_`
16293    /// prefix rather than relying on scope to disambiguate them.
16294    #[test]
16295    fn the_file_builtins_are_std_only_and_return_status_words() {
16296        for name in [
16297            "file_open",
16298            "file_close",
16299            "file_read",
16300            "file_write",
16301            "file_flush",
16302        ] {
16303            assert!(
16304                nova_resolver::Builtin::STD_ONLY
16305                    .iter()
16306                    .any(|b| b.name() == name),
16307                "{name} must be an STD_ONLY builtin"
16308            );
16309        }
16310    }
16311
16312    /// `shutdown_requested` is `STD_ONLY` and typed `() -> Bool`: only
16313    /// `std/http`'s `Server` may read the shutdown flag, and reading it is
16314    /// what installs the signal handler (docs/adr/0022).
16315    #[test]
16316    fn shutdown_requested_is_std_only_and_returns_bool() {
16317        let b = nova_resolver::Builtin::STD_ONLY
16318            .iter()
16319            .copied()
16320            .find(|b| b.name() == "shutdown_requested")
16321            .expect("shutdown_requested must be an STD_ONLY builtin");
16322        assert_eq!(builtin_signature(b), (vec![], Ty::Bool));
16323    }
16324
16325    /// The three `std/process` builtins are `STD_ONLY` and typed as
16326    /// `std/process/lib.nova` calls them (docs/adr/0023).
16327    #[test]
16328    fn the_process_builtins_are_std_only_with_their_signatures() {
16329        for (name, sig) in [
16330            ("process_arg_count", (vec![], Ty::Int)),
16331            ("process_arg", (vec![Ty::Int], Ty::String)),
16332            ("process_exit", (vec![Ty::Int], Ty::Unit)),
16333        ] {
16334            let b = nova_resolver::Builtin::STD_ONLY
16335                .iter()
16336                .copied()
16337                .find(|b| b.name() == name)
16338                .unwrap_or_else(|| panic!("{name} must be an STD_ONLY builtin"));
16339            assert_eq!(builtin_signature(b), sig, "{name}");
16340        }
16341    }
16342
16343    /// The shared arity/argument path all builtins now go through, exercised
16344    /// via the one family a *user* program can call. Before these, nothing in
16345    /// the suite reached `check_builtin_call`'s error branches at all.
16346    #[test]
16347    fn builtin_call_with_wrong_arity_reports_e0016() {
16348        let r = check_src("fn main() { println(\"a\", \"b\") }");
16349        assert!(error_codes(&r).contains(&"E0016"), "{:?}", r.diagnostics);
16350    }
16351
16352    #[test]
16353    fn builtin_call_with_a_non_string_argument_reports_e0010() {
16354        let r = check_src("fn main() { println(7) }");
16355        assert!(error_codes(&r).contains(&"E0010"), "{:?}", r.diagnostics);
16356        // The print family keeps its interpolation hint; the std-only builtins
16357        // deliberately have none.
16358        assert!(
16359            r.diagnostics
16360                .iter()
16361                .any(|d| d.message.contains("use string interpolation")),
16362            "{:?}",
16363            r.diagnostics
16364        );
16365    }
16366
16367    #[test]
16368    fn hash_impls_typecheck_for_primitives() {
16369        let r = check_src(
16370            "fn h<T: Hash>(x: T) -> Int { x.hash() }\n\
16371             fn main() {\n\
16372                 println(\"${h(7)} ${h(true)} ${h('c')} ${h(\"s\")}\")\n\
16373             }",
16374        );
16375        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
16376        // Not vacuous: `Ty::Error` unifies with anything, so an empty
16377        // diagnostic list alone would still hold if `x.hash()` had collapsed
16378        // to an error expression. Pin what the checker actually built — a
16379        // `TraitCall` dispatching through `T`'s bound, typed `Int`.
16380        let call = exprs_in(&r.module, "h")
16381            .into_iter()
16382            .find(|e| matches!(e.kind, hir::ExprKind::TraitCall { .. }))
16383            .expect("`x.hash()` is a trait-method call");
16384        assert_eq!(
16385            call.ty,
16386            Ty::Int,
16387            "`hash()` must be typed `Int`, not an error type"
16388        );
16389    }
16390
16391    /// The companion the generic test above cannot be: inside `h`, `self_ty` is
16392    /// `Param(0)` and the bound is only discharged at monomorphization, so
16393    /// `hash_impls_typecheck_for_primitives` would still pass with `trait Hash`
16394    /// declared and *no* impls at all. Calling `.hash()` on each concrete
16395    /// primitive resolves against the impl itself (`E0014 no method 'hash' on
16396    /// type 'Int'` otherwise), which is what pins all four impls existing —
16397    /// including `Hash for Char`, whose `Char`→`Int` conversion needed the
16398    /// `char_to_int` builtin.
16399    #[test]
16400    fn hash_dispatches_on_each_concrete_primitive() {
16401        let r = check_src(
16402            "fn main() {\n\
16403                 println(\"${(7).hash()} ${true.hash()} ${'c'.hash()} ${(\"s\").hash()}\")\n\
16404             }",
16405        );
16406        assert!(r.diagnostics.is_empty(), "{:?}", r.diagnostics);
16407        let tys: Vec<&Ty> = exprs_in(&r.module, "main")
16408            .into_iter()
16409            .filter(|e| matches!(e.kind, hir::ExprKind::TraitCall { .. }))
16410            .map(|e| &e.ty)
16411            .collect();
16412        assert_eq!(tys.len(), 4, "four `.hash()` calls: {tys:?}");
16413        assert!(
16414            tys.iter().all(|t| **t == Ty::Int),
16415            "every `hash()` is typed `Int`, not an error type: {tys:?}"
16416        );
16417    }
16418
16419    /// `Hash for Float` is deliberately absent (ADR 0005 §2): NaN never equals
16420    /// itself, so a NaN key would be unreachable in any hash map, and
16421    /// `0.0`/`-0.0` compare equal but would hash differently. Pinned as a
16422    /// diagnostic so re-adding it is a deliberate act with a test to change,
16423    /// not an accident.
16424    #[test]
16425    fn float_has_no_hash_impl() {
16426        let r = check_src("fn main() { println(\"${(1.5).hash()}\") }");
16427        assert!(
16428            error_codes(&r).contains(&"E0014"),
16429            "expected E0014 no method `hash` on `Float`: {:?}",
16430            r.diagnostics
16431        );
16432    }
16433
16434    // `str_cmp_builtin_wrong_arity_reports_e0016` and
16435    // `str_cmp_builtin_rejects_non_string_argument` used to live here,
16436    // calling `str_cmp(...)` directly from a *user* module to exercise
16437    // `check_builtin_call`'s `Builtin::StrCmp` arity/type-error branches.
16438    // They were removed by the Fix 1 review pass (nova-resolver's
16439    // `Builtin::STD_ONLY`): `str_cmp` is no longer seeded into user
16440    // module scopes, so `str_cmp(...)` written in a user module no longer
16441    // resolves at all — it now fails name resolution with `E0001 cannot find
16442    // function 'str_cmp'`, not the `E0016`/`E0010` these tests asserted.
16443    // `std/core` is the only remaining caller, and its one call site
16444    // (`str_cmp(self, other)` in `impl Ord for String`, above) always passes
16445    // exactly two `String` arguments, so the arity/type-error branches are
16446    // now defensive-only — unreachable from any Nova program, user or
16447    // std/core. Exercising them would require bypassing name resolution
16448    // entirely to call `check_builtin_call` with hand-built `Checker`/`FnCtx`
16449    // state, which is disproportionate test-harness surgery for dead code
16450    // paths, so the tests were dropped rather than repointed. The success
16451    // path they didn't cover anyway (`("a").cmp("b")`, i.e. `Ord for String`
16452    // dispatching through the builtin) remains covered by
16453    // `std_core_traits_and_primitive_impls_typecheck` above, unaffected by
16454    // Fix 1 since it calls `.cmp()` as a method, never `str_cmp` by name.
16455    //
16456    // `Builtin::StrHash` and `Builtin::CharToInt` (ADR 0005 §2) joined the
16457    // same std-only list and are unreachable for the same reason. Rather than
16458    // add two more untestable copies of the arity/type logic,
16459    // `check_builtin_call` was collapsed onto one shared path driven by
16460    // `builtin_signature`: the two error branches are now exercised by
16461    // `println`, which a user program *can* misuse
16462    // (`builtin_call_with_wrong_arity_reports_e0016` /
16463    // `builtin_call_with_a_non_string_argument_reports_e0010`), and the part
16464    // that is genuinely per-builtin — the signature itself — is unit-tested
16465    // directly by `builtin_signatures_are_what_the_std_call_sites_use`.
16466
16467    // ---- `@test` attribute validation and collection (Task 2) --------------
16468    //
16469    // Task 1 made `@name(args)` parse and be stored on items, deliberately
16470    // without validating them. These tests are the first thing to ever read
16471    // `Attribute` at all: an unknown name is `E0082`, `@test` on anything but
16472    // a function is `E0083`, a `@test` function with parameters, generics, or
16473    // a non-`Unit` return is `E0084`, and an unknown `@test(...)` argument is
16474    // `E0085`. A misspelled `@tset` must be a hard error rather than silently
16475    // parsed and ignored — that would compile a function that looks like a
16476    // test and never runs as one, which is the worst instance of this
16477    // project's most-repeated defect ("parses, then enforces nothing").
16478
16479    #[test]
16480    fn an_unknown_attribute_is_e0082_and_names_it() {
16481        let r = check_src("@tset\nfn t() { }\nfn main() { }");
16482        let d = r
16483            .diagnostics
16484            .iter()
16485            .find(|d| d.code == "E0082")
16486            .expect("E0082 for an unknown attribute");
16487        assert!(
16488            d.message.contains("tset"),
16489            "names the attribute: {}",
16490            d.message
16491        );
16492        assert!(
16493            d.message.contains("test"),
16494            "lists the known set: {}",
16495            d.message
16496        );
16497    }
16498
16499    #[test]
16500    fn test_on_a_non_function_is_e0083() {
16501        let r = check_src("@test\nrecord R { n: Int }\nfn main() { }");
16502        assert!(error_codes(&r).contains(&"E0083"), "{:?}", r.diagnostics);
16503    }
16504
16505    #[test]
16506    fn test_on_a_function_with_parameters_is_e0084() {
16507        let r = check_src("@test\nfn t(x: Int) { }\nfn main() { }");
16508        let d = r
16509            .diagnostics
16510            .iter()
16511            .find(|d| d.code == "E0084")
16512            .expect("E0084 for a @test function with parameters");
16513        // "have parameters" (not just "parameters") so this can't pass
16514        // against a message that named "generic parameters" instead —
16515        // "generic parameters" contains "parameters" as a substring but not
16516        // "have parameters", since "have" is followed by "generic " there,
16517        // not directly by "parameters".
16518        assert!(
16519            d.message.contains("have parameters"),
16520            "names parameters specifically, not a generic \"bad signature\": {}",
16521            d.message
16522        );
16523    }
16524
16525    #[test]
16526    fn test_on_a_function_returning_a_value_is_e0084() {
16527        let r = check_src("@test\nfn t() -> Int { 1 }\nfn main() { }");
16528        let d = r
16529            .diagnostics
16530            .iter()
16531            .find(|d| d.code == "E0084")
16532            .expect("E0084 for a @test function returning a value");
16533        assert!(
16534            d.message.contains("non-Unit return type"),
16535            "names the return type specifically, not a generic \"bad signature\": {}",
16536            d.message
16537        );
16538    }
16539
16540    #[test]
16541    fn test_on_a_generic_function_is_e0084() {
16542        // A test takes no arguments, so nothing could ever fix its type
16543        // parameter — monomorphization would have no instance to emit.
16544        let r = check_src("@test\nfn t<T>() { }\nfn main() { }");
16545        let d = r
16546            .diagnostics
16547            .iter()
16548            .find(|d| d.code == "E0084")
16549            .expect("E0084 for a generic @test function");
16550        assert!(
16551            d.message.contains("generic parameters"),
16552            "names generic parameters specifically, not a generic \"bad signature\": {}",
16553            d.message
16554        );
16555    }
16556
16557    /// An `async` `@test` is rejected, not run.
16558    ///
16559    /// The runner calls a `@test` function directly and discards its result, so
16560    /// an `async` one hands back a future that nothing polls: were the shape
16561    /// accepted, the call would allocate a state object, the body would not run,
16562    /// and a guaranteed-failing assertion inside it would report `ok` — the same
16563    /// failure `nova-mir`'s `mono.rs` describes for an `async` entry point.
16564    ///
16565    /// Rejected rather than shimmed because
16566    /// `docs/superpowers/specs/2026-08-07-phase-2-3a-async-core-design.md` §10
16567    /// specifies `@test fn t() { block_on(f()) }` as the way async code is
16568    /// tested, with no change to the runner; the defect was that `@test async
16569    /// fn` was silently accepted instead of refused. `nova-cli`'s
16570    /// `nova_test_runs_an_async_body_via_block_on_and_pins_a_wrong_answer`
16571    /// is the other half — that the supported shape really does run and really
16572    /// does fail on a wrong answer.
16573    #[test]
16574    fn test_on_an_async_function_is_e0084() {
16575        let r = check_src("@test\nasync fn t() { }\nfn main() { }");
16576        let d = r
16577            .diagnostics
16578            .iter()
16579            .find(|d| d.code == "E0084")
16580            .expect("E0084 for an async @test function");
16581        assert!(
16582            d.message.contains("an `async` body"),
16583            "names async specifically, not a generic \"bad signature\": {}",
16584            d.message
16585        );
16586        // The note has to be actionable on its own: a user who reads it must be
16587        // able to write the working form without opening the docs, so it is
16588        // required to name `block_on` *and* to spell the replacement as source.
16589        let notes = d.notes.join(" ");
16590        assert!(
16591            notes.contains("block_on"),
16592            "the note names the working alternative: {notes}"
16593        );
16594        assert!(
16595            notes.contains("@test fn t()"),
16596            "the note spells the replacement out as source: {notes}"
16597        );
16598    }
16599
16600    #[test]
16601    fn an_unknown_test_argument_is_e0085() {
16602        let r = check_src("@test(shuold_panic)\nfn t() { }\nfn main() { }");
16603        let d = r
16604            .diagnostics
16605            .iter()
16606            .find(|d| d.code == "E0085")
16607            .expect("E0085 for an unknown @test argument");
16608        assert!(
16609            d.message.contains("shuold_panic"),
16610            "names the offending value: {}",
16611            d.message
16612        );
16613        assert!(
16614            d.message.contains("should_panic"),
16615            "names the accepted arg: {}",
16616            d.message
16617        );
16618    }
16619
16620    #[test]
16621    fn a_well_formed_test_is_accepted() {
16622        let r = check_src("@test\nfn t() { }\n@test(should_panic)\nfn u() { }\nfn main() { }");
16623        assert_eq!(error_codes(&r), Vec::<&str>::new(), "{:?}", r.diagnostics);
16624    }
16625
16626    #[test]
16627    fn collected_tests_are_in_source_order_with_should_panic_attached() {
16628        // The runner addresses tests BY INDEX across separate processes
16629        // (spec §4, risk 2). If collection order is not source order, or is
16630        // not stable between the enumeration run and a test run, `nova test`
16631        // silently runs one test and reports another's name. That failure is
16632        // invisible to every other test in this file, so it is pinned here.
16633        //
16634        // Three tests, not two: with two, a reversed order and a swapped
16635        // should_panic flag are indistinguishable. The middle one carries the
16636        // flag so a reversal moves it.
16637        let r = collect_tests_of(
16638            "@test\nfn alpha() { }\n\
16639             @test(should_panic)\nfn beta() { }\n\
16640             @test\nfn gamma() { }\n\
16641             fn main() { }",
16642        );
16643        let names: Vec<&str> = r.iter().map(|t| t.name.as_str()).collect();
16644        assert_eq!(names, ["alpha", "beta", "gamma"]);
16645        assert_eq!(
16646            r.iter().map(|t| t.should_panic).collect::<Vec<_>>(),
16647            [false, true, false]
16648        );
16649    }
16650
16651    // ---- fix round 1: import/module/extern carry attrs too -----------------
16652    //
16653    // Escalated after independent measurement: `@tset` before a function was
16654    // E0082, but before an `import` it was masked by an unrelated E0001
16655    // ("cannot find module"), and before an `extern` block it produced NO
16656    // diagnostic at all — an unknown attribute compiled clean. The root cause
16657    // was in `nova-ast`/`nova-parser`, not just `nova-resolver`: `Import`,
16658    // `Module` and `ExternBlock` had no `attrs` field, so `try_parse_item`'s
16659    // freshly-parsed `Vec<Attribute>` had nowhere to go for those three item
16660    // kinds and was silently dropped before the resolver ever ran. Fixed by
16661    // giving all three an `attrs` field (matching the other six exactly in
16662    // placement and type) and extending `validate_attrs_reject_test` to walk
16663    // them, rather than special-casing the parser to reject `@` there — every
16664    // item may carry attributes; validation decides what is legal, uniformly.
16665
16666    #[test]
16667    fn an_unknown_attribute_before_an_import_is_e0082_and_names_it() {
16668        let r = check_src("@tset\nimport core\nfn main() { }");
16669        let d = r
16670            .diagnostics
16671            .iter()
16672            .find(|d| d.code == "E0082")
16673            .expect("E0082 for an unknown attribute before an import");
16674        assert!(
16675            d.message.contains("tset"),
16676            "names the attribute: {}",
16677            d.message
16678        );
16679        assert!(
16680            d.message.contains("test"),
16681            "lists the known set: {}",
16682            d.message
16683        );
16684    }
16685
16686    #[test]
16687    fn an_unknown_attribute_before_a_module_is_e0082_and_names_it() {
16688        let r = check_src("@tset\nmodule foo;\nfn main() { }");
16689        let d = r
16690            .diagnostics
16691            .iter()
16692            .find(|d| d.code == "E0082")
16693            .expect("E0082 for an unknown attribute before a module declaration");
16694        assert!(
16695            d.message.contains("tset"),
16696            "names the attribute: {}",
16697            d.message
16698        );
16699        assert!(
16700            d.message.contains("test"),
16701            "lists the known set: {}",
16702            d.message
16703        );
16704    }
16705
16706    #[test]
16707    fn an_unknown_attribute_before_an_extern_block_is_e0082_and_names_it() {
16708        let r = check_src("@tset\nextern { }\nfn main() { }");
16709        let d = r
16710            .diagnostics
16711            .iter()
16712            .find(|d| d.code == "E0082")
16713            .expect("E0082 for an unknown attribute before an extern block");
16714        assert!(
16715            d.message.contains("tset"),
16716            "names the attribute: {}",
16717            d.message
16718        );
16719        assert!(
16720            d.message.contains("test"),
16721            "lists the known set: {}",
16722            d.message
16723        );
16724    }
16725
16726    #[test]
16727    fn test_before_an_import_is_e0083() {
16728        let r = check_src("@test\nimport core\nfn main() { }");
16729        let d = r
16730            .diagnostics
16731            .iter()
16732            .find(|d| d.code == "E0083")
16733            .expect("E0083 for @test before an import");
16734        assert!(
16735            d.message.contains("import"),
16736            "names the item kind: {}",
16737            d.message
16738        );
16739    }
16740
16741    #[test]
16742    fn test_before_a_module_is_e0083() {
16743        let r = check_src("@test\nmodule foo;\nfn main() { }");
16744        let d = r
16745            .diagnostics
16746            .iter()
16747            .find(|d| d.code == "E0083")
16748            .expect("E0083 for @test before a module declaration");
16749        assert!(
16750            d.message.contains("module"),
16751            "names the item kind: {}",
16752            d.message
16753        );
16754    }
16755
16756    #[test]
16757    fn test_before_an_extern_block_is_e0083() {
16758        let r = check_src("@test\nextern { }\nfn main() { }");
16759        let d = r
16760            .diagnostics
16761            .iter()
16762            .find(|d| d.code == "E0083")
16763            .expect("E0083 for @test before an extern block");
16764        assert!(
16765            d.message.contains("extern"),
16766            "names the item kind: {}",
16767            d.message
16768        );
16769    }
16770
16771    // ---- fix round 2: a function with two @test attributes is one test ----
16772    //
16773    // `@test` twice on one function is syntactically legal (the parser puts
16774    // no uniqueness constraint on attribute names — see
16775    // `multiple_arguments_and_multiple_attributes_parse` in nova-parser for
16776    // proof a function can carry multiple attributes at all). Without
16777    // deduplication in `validate_test_function`, each well-formed `@test`
16778    // independently pushes a `TestFn`, so the same def_id/name would be
16779    // collected twice and `nova test` would list and run one function as two
16780    // different tests.
16781
16782    #[test]
16783    fn a_function_with_two_test_attributes_is_collected_once() {
16784        let r = collect_tests_of("@test\n@test\nfn t() { }\nfn main() { }");
16785        assert_eq!(r.len(), 1, "{:?}", r);
16786        assert_eq!(r[0].name, "t");
16787    }
16788
16789    // ---- reserved built-in type names ----
16790
16791    #[test]
16792    fn reserved_type_names_is_exactly_the_seven_expected_names() {
16793        // Both tests below iterate `RESERVED_TYPE_NAMES` to build their own
16794        // cases, so neither one can notice a name silently missing from it --
16795        // they would just stop checking that name, not fail. This list is
16796        // written out independently of the constant it checks against, so a
16797        // name dropped (or swapped for a duplicate of another, keeping the
16798        // length unchanged) shows up as a content mismatch here, and a name
16799        // dropped without a length change fails to even compile, since two
16800        // differently-sized arrays are different types.
16801        let mut expected = ["Int", "Float", "Bool", "Char", "String", "Future", "Bytes"];
16802        let mut actual = nova_resolver::RESERVED_TYPE_NAMES;
16803        expected.sort_unstable();
16804        actual.sort_unstable();
16805        assert_eq!(actual, expected);
16806    }
16807
16808    #[test]
16809    fn a_rejected_declarations_own_body_is_never_checked() {
16810        // Pins skip-not-register for BOTH declaration forms:
16811        // `reject_reserved_type_name` returns before `push_def`, so the
16812        // rejected item never enters `defs` and is never visited by
16813        // `collect_records`/`collect_sums`/`collect_type_arities` (which
16814        // iterate `defs()` directly, independent of the name lookup that
16815        // rejected it). An ordinary field or payload type (`v: Int`, or a
16816        // variant with no payload) can't discriminate this -- it would
16817        // convert cleanly whether or not the item were processed. One naming
16818        // an unresolvable type can: if a future change made rejection
16819        // register the def anyway, `collect_records`/`collect_sums` would
16820        // convert it and add a second diagnostic for `Nope` alongside
16821        // `E0089`. `check_src` runs the type checker unconditionally (unlike
16822        // the driver, which stops after a resolver error), so this is the
16823        // layer where such a regression would actually surface. The record
16824        // and sum arms are two separate call sites in `collect_item`
16825        // (`reject_reserved_type_name` is called independently from each),
16826        // so one passing is no evidence about the other -- both are checked.
16827        for src in [
16828            "record Bool { v: Nope }\nfn main() { }",
16829            "type Bool = | A(Nope) | B\nfn main() { }",
16830        ] {
16831            let r = check_src(src);
16832            assert_eq!(
16833                r.diagnostics.len(),
16834                1,
16835                "expected only E0089 for {src:?}, got {:?}",
16836                r.diagnostics
16837                    .iter()
16838                    .map(|d| (&d.code, &d.message))
16839                    .collect::<Vec<_>>()
16840            );
16841            assert_eq!(r.diagnostics[0].code, "E0089");
16842        }
16843    }
16844
16845    #[test]
16846    fn two_declarations_of_the_same_reserved_name_each_raise_e0089_not_e0002() {
16847        // Pins a claim `reject_reserved_type_name`'s own doc makes but that
16848        // nothing exercised: skip-not-register means a second declaration
16849        // under an already-rejected reserved name is not a *duplicate* --
16850        // `insert_type`, the `E0002` source two same-file type declarations
16851        // under one name would otherwise reach, is never reached for either
16852        // one here, since both return from `reject_reserved_type_name`
16853        // before `push_def`. So this is two independent `E0089`s, not an
16854        // `E0089` followed by an `E0002`. A refactor that reordered the check
16855        // after `push_def`/`insert_type` would turn the second occurrence
16856        // into `E0002` silently; this fails if it does.
16857        let r = check_src("record Bool { v: Int }\nrecord Bool { w: Int }\nfn main() { }");
16858        let codes: Vec<&str> = r.diagnostics.iter().map(|d| d.code.as_str()).collect();
16859        assert_eq!(
16860            codes,
16861            ["E0089", "E0089"],
16862            "got {:?}",
16863            r.diagnostics
16864                .iter()
16865                .map(|d| d.message.clone())
16866                .collect::<Vec<_>>()
16867        );
16868    }
16869
16870    #[test]
16871    fn declaring_a_type_named_for_a_builtin_is_rejected() {
16872        // Every name in `RESERVED_TYPE_NAMES`, in both declaration forms --
16873        // the loop below iterates the array itself, so this cannot go stale
16874        // as the table grows. `convert_ty` resolves the name
16875        // to the built-in, or to a shadowing generic parameter, wherever it
16876        // runs -- never to a declaration under this name -- so a type declared
16877        // under one of these names could never be referred to in a type
16878        // annotation. Rejecting the declaration puts that fact where the user
16879        // can act on it, rather than only once it is named in a signature.
16880        //
16881        // This also forecloses construction and pattern matching, which used to
16882        // work (a record literal resolves through `resolve_type` directly; a
16883        // sum type's variants live in the value namespace, both independent of
16884        // `convert_ty`) -- a real, accepted breaking change for that narrower
16885        // usage. An `impl` header's self type is a `convert_ty` site too. See
16886        // CHANGELOG.md for exactly what breaks, and
16887        // `a_rejected_declarations_own_body_is_never_checked` for the
16888        // no-cascade guarantee this rejection still provides.
16889        for name in nova_resolver::RESERVED_TYPE_NAMES {
16890            for src in [
16891                format!("record {name} {{ v: Bool }}\nfn main() {{ }}"),
16892                format!("type {name} = | A | B\nfn main() {{ }}"),
16893            ] {
16894                let r = check_src(&src);
16895                // Exactly one diagnostic, not merely "E0089 is present somewhere":
16896                // skip-not-register means none of these declarations -- every
16897                // reserved name, in both declaration forms -- should ever cascade,
16898                // and `.find()` alone would not notice if one did.
16899                assert_eq!(
16900                    r.diagnostics.len(),
16901                    1,
16902                    "expected only E0089 for `{name}` in {src:?}, got {:?}",
16903                    r.diagnostics
16904                        .iter()
16905                        .map(|d| (&d.code, &d.message))
16906                        .collect::<Vec<_>>()
16907                );
16908                let d = &r.diagnostics[0];
16909                assert_eq!(d.code, "E0089", "for `{name}` in {src:?}: {:?}", d.message);
16910                // Both halves of the message matter. The name identifies which
16911                // built-in was shadowed; the second half is the fact a user cannot
16912                // discover from the declaration alone, and a code-only assertion
16913                // would survive deleting it.
16914                assert!(
16915                    d.message.contains(name),
16916                    "E0089 must name the built-in it collides with; got {:?}",
16917                    d.message
16918                );
16919                assert!(
16920                    d.message.contains("built-in") || d.message.contains("builtin"),
16921                    "E0089 must say the name belongs to a built-in type; got {:?}",
16922                    d.message
16923                );
16924            }
16925        }
16926    }
16927
16928    #[test]
16929    fn every_reserved_name_really_is_a_builtin_type() {
16930        // The drift guard, in the direction that can be caught. If a name is
16931        // removed from `convert_ty`'s table while staying in the reserved list,
16932        // this fails -- annotating with it would become `E0001 cannot find type`.
16933        //
16934        // The other direction (a seventh built-in added without reserving it) is
16935        // NOT caught here and cannot be from a fixed list; the mitigation is the
16936        // pointer comment at the table itself (Step 4).
16937        for name in nova_resolver::RESERVED_TYPE_NAMES {
16938            let ann = if name == "Future" {
16939                "Future<Int>"
16940            } else {
16941                name
16942            };
16943            let r = check_src(&format!("fn f(x: {ann}) -> Int {{ 1 }}\nfn main() {{ }}"));
16944            assert!(
16945                !r.diagnostics.iter().any(|d| d.code == "E0001"),
16946                "`{name}` is in RESERVED_TYPE_NAMES but is not a built-in type name: {:?}",
16947                r.diagnostics
16948                    .iter()
16949                    .map(|d| d.message.clone())
16950                    .collect::<Vec<_>>()
16951            );
16952        }
16953    }
16954
16955    #[test]
16956    fn bytes_is_nameable_in_representative_positions() {
16957        // This test asserts only that `Bytes` converts in a signature, a
16958        // `let` annotation, a field type and an array element type -- one
16959        // representative per broad category, not an exhaustive enumeration
16960        // of `convert_ty`'s call sites (there are far more than these
16961        // four). `main`'s `let` initializes with `panic(...)` rather than a
16962        // real `Bytes` value -- not because one cannot be built
16963        // (`bytes_from_string("x")` works today, with `$std.bytes`
16964        // glob-imported into every other module since Task 2, no `import`
16965        // needed) but because this test does not need one: `panic`
16966        // diverges (`Ty::Never`), and `Never` unifies with anything, so the
16967        // annotation still converts and unifies without needing a way to
16968        // construct one.
16969        let r = check_src(
16970            "record Holder { b: Bytes }\n\
16971             fn ident(x: Bytes) -> Bytes { x }\n\
16972             fn takes_array(xs: [Bytes]) -> Int { xs.len() }\n\
16973             fn main() { let x: Bytes = panic(\"unreachable\") }",
16974        );
16975        assert!(
16976            r.diagnostics.is_empty(),
16977            "`Bytes` must convert in each representative type position, got {:?}",
16978            r.diagnostics
16979                .iter()
16980                .map(|d| (&d.code, &d.message))
16981                .collect::<Vec<_>>()
16982        );
16983    }
16984
16985    #[test]
16986    fn bytes_eq_bytes_is_e0013_not_a_silent_pointer_compare() {
16987        // Pins the decision recorded in `Builtin::BytesEq`'s doc comment
16988        // (`nova-resolver`): `Ty::Bytes` is deliberately absent from
16989        // `binary_result_ty`'s `Eq | Ne` arm, so `b1 == b2` does not type
16990        // check today -- `Bytes` equality is reached only through `impl Eq
16991        // for Bytes`'s `eq` method. This is not a hypothetical seam: if
16992        // `Ty::Bytes` is ever added to that arm (the obvious symmetry with
16993        // `Ty::String`), `nova_mir::lower::lower_binary`'s `matches!(lhs.ty,
16994        // Ty::String)` guard (`lower.rs`) would not also match `Ty::Bytes`,
16995        // so `==` would silently fall through to a pointer-identity
16996        // comparison on the two `MirTy::Ptr` temps instead of a
16997        // byte-for-byte one -- `bytes_from_string("x") ==
16998        // bytes_from_string("x")` would silently become `false`. `Ty::Array`/
16999        // `Record`/`Sum` share this same latent exposure and are unchanged;
17000        // `Bytes` is the member under active pressure to grow a `==` arm,
17001        // being the only non-primitive `STD_MODULES` type shipping its own
17002        // `impl Eq`. This test is what would catch the typeck half of that
17003        // regressing; `lower.rs`'s own comment beside the `matches!` names
17004        // the MIR half that would also need to change.
17005        let r = check_src(
17006            "fn main() { let a = bytes_from_string(\"x\")\n let b = bytes_from_string(\"x\")\n let _ = a == b }",
17007        );
17008        assert!(
17009            r.diagnostics.iter().any(|d| d.code == "E0013"),
17010            "`Bytes == Bytes` must be E0013 until `binary_result_ty` gains a `Ty::Bytes` arm; got {:?}",
17011            r.diagnostics
17012                .iter()
17013                .map(|d| (&d.code, &d.message))
17014                .collect::<Vec<_>>()
17015        );
17016    }
17017
17018    #[test]
17019    fn a_generic_parameter_named_for_a_builtin_still_works() {
17020        // NON-GOAL, pinned. `convert_ty` resolves generics BEFORE the built-in
17021        // table, so this shadowing is coherent rather than broken: the parameter
17022        // genuinely means the parameter. Compiling is not enough to show that --
17023        // an `Int` argument round-trips whether `x: Int` names the parameter or
17024        // the primitive it shadows, since both are the same value either way.
17025        // This crate has no evaluator, only the type checker, so the
17026        // discriminating half of the claim -- that the annotation names a
17027        // parameter free to become a *different* type, not just that it
17028        // compiles for one -- is pinned at the runtime layer instead: see
17029        // `shadow_builtin` in `tests/runtime/generics.nova`, called at both
17030        // `Int` and `String`. If `Int` had instead fallen through to the
17031        // primitive, the `String` call would be a compile-time type error,
17032        // not a different runtime value.
17033        let r = check_src("fn f<Int>(x: Int) -> Int { x }\nfn main() { }");
17034        assert!(
17035            r.diagnostics.is_empty(),
17036            "a generic parameter may shadow a built-in type name, got {:?}",
17037            r.diagnostics
17038                .iter()
17039                .map(|d| d.message.clone())
17040                .collect::<Vec<_>>()
17041        );
17042    }
17043
17044    #[test]
17045    fn a_trait_named_for_a_builtin_still_works() {
17046        // NON-GOAL, pinned. Traits are a separate namespace: `trait Int` does not
17047        // shadow the type, and the return annotation below resolves to the
17048        // primitive.
17049        let r = check_src("trait Int { fn m(self) -> Int }\nfn main() { }");
17050        assert!(
17051            r.diagnostics.is_empty(),
17052            "a trait may be named for a built-in type, got {:?}",
17053            r.diagnostics
17054                .iter()
17055                .map(|d| d.message.clone())
17056                .collect::<Vec<_>>()
17057        );
17058    }
17059
17060    #[test]
17061    fn a_value_named_for_a_builtin_still_works() {
17062        // NON-GOAL, pinned. `reject_reserved_type_name` runs only from the
17063        // sum-type and record arms of `collect_item`, never from `insert_value`
17064        // -- a function, a const, and a local binding all live in the value
17065        // namespace, which this check does not touch. Nothing before this
17066        // stopped `nova-resolver` from being extended to reject these too, and
17067        // this is the test that would fail if a later tidy-up quietly widened
17068        // the rule that way.
17069        let r = check_src(
17070            "fn Int() -> Int { 1 }\nconst String: Int = 2\n\
17071             fn main() { let Bool = 3\nprintln(\"${Int()} ${String} ${Bool}\") }",
17072        );
17073        assert!(
17074            r.diagnostics.is_empty(),
17075            "a function, const, or local binding may be named for a built-in type, got {:?}",
17076            r.diagnostics
17077                .iter()
17078                .map(|d| d.message.clone())
17079                .collect::<Vec<_>>()
17080        );
17081    }
17082
17083    #[test]
17084    fn an_unknown_type_name_is_still_e0001_not_e0089() {
17085        // The new check must not swallow the not-found path.
17086        let r = check_src("fn f(x: Nope) -> Int { 1 }\nfn main() { }");
17087        assert!(r.diagnostics.iter().any(|d| d.code == "E0001"));
17088        assert!(!r.diagnostics.iter().any(|d| d.code == "E0089"));
17089    }
17090
17091    #[test]
17092    fn an_ordinary_type_declaration_is_unaffected() {
17093        // Kills a check that fires on every type name rather than the reserved
17094        // ones. Weak on its own -- the whole suite would fail -- but it states the
17095        // boundary at the point the reader is looking at.
17096        let r = check_src("record Wrap { v: Int }\ntype Two = | A | B\nfn main() { }");
17097        assert!(!r.diagnostics.iter().any(|d| d.code == "E0089"));
17098    }
17099
17100    // === Phase 3.2: the probe (spec
17101    // docs/superpowers/specs/2026-10-08-phase-3-2-lsp-core-design.md §4.1) ===
17102
17103    /// Check `marked` with the probe where `<|>` stands, after removing the
17104    /// marker. The module and each std module get their own `FileId`, so the
17105    /// probe's offset cannot also land in std's code. Parse errors are
17106    /// allowed, as the language server allows them.
17107    fn probe_src(marked: &str) -> (CheckResult, Definitions) {
17108        let offset = marked.find("<|>").expect("a `<|>` marks the probe") as u32;
17109        let src = marked.replacen("<|>", "", 1);
17110        let mut db = nova_diagnostics::FileDb::new();
17111        let file = db.add("probe.nova", src.as_str());
17112        let std_files: Vec<FileId> = nova_resolver::STD_MODULES
17113            .iter()
17114            .map(|&(name, source)| db.add(name, source))
17115            .collect();
17116        let (tokens, _) = lex(&src, file);
17117        let parsed = nova_parser::parse_recovering(&tokens, file);
17118        let module = nova_resolver::ModuleSource::new("main", &parsed.file);
17119        let resolved = nova_resolver::resolve_program(&[module], &std_files, None);
17120        let options = CheckOptions {
17121            probe: Some(ProbePoint { file, offset }),
17122            index: false,
17123            sources: None,
17124        };
17125        let checked = check_with(&resolved.file, &resolved.definitions, &options);
17126        (checked, resolved.definitions)
17127    }
17128
17129    #[test]
17130    fn a_missing_member_name_adds_no_type_error() {
17131        let (r, _) = probe_src("fn main() {\n    let s = \"a\"\n    s.<|>\n}\n");
17132        assert!(error_codes(&r).is_empty(), "{:?}", r.diagnostics);
17133    }
17134
17135    #[test]
17136    fn the_probe_records_the_receiver_after_a_dot() {
17137        let (r, _) = probe_src("fn main() {\n    let s = \"a\"\n    s.<|>\n}\n");
17138        assert_eq!(r.probe.receiver, Some(Ty::String));
17139    }
17140
17141    #[test]
17142    fn the_probe_reaches_a_name_on_the_next_line() {
17143        // Newlines do not end a statement, so `v.` then `println("x")` is the
17144        // call `v.println("x")`. The cursor is still after the `.`.
17145        let (r, _) = probe_src("fn main() {\n    let v = 1\n    v.<|>\n    println(\"x\")\n}\n");
17146        assert_eq!(r.probe.receiver, Some(Ty::Int));
17147    }
17148
17149    #[test]
17150    fn a_chain_records_the_innermost_receiver() {
17151        let (r, defs) = probe_src(
17152            "record Q { n: Int }\nrecord P { q: Q }\n\
17153             fn main() {\n    let p = P { q: Q { n: 1 } }\n    let m = p.q<|>.n\n}\n",
17154        );
17155        let receiver = r.probe.receiver.expect("a receiver");
17156        assert_eq!(display_ty(&receiver, &defs), "P");
17157    }
17158
17159    #[test]
17160    fn the_probe_at_the_end_of_a_receiver_records_names_not_its_members() {
17161        // `items|.len()`, the cursor at the end of a receiver being edited:
17162        // the name there is the receiver's, not the member's (spec §4.1).
17163        let (r, _) = probe_src("fn main() {\n    let items = [1]\n    let n = items<|>.len()\n}\n");
17164        assert_eq!(r.probe.receiver, None);
17165        assert!(
17166            r.probe.locals.iter().any(|(n, _)| n == "items"),
17167            "{:?}",
17168            r.probe.locals
17169        );
17170    }
17171
17172    #[test]
17173    fn without_a_probe_nothing_is_recorded() {
17174        let r = check_src("fn main() {\n    let s = \"a\"\n    let n = s.len()\n}\n");
17175        assert_eq!(r.probe, ProbeResult::default());
17176    }
17177
17178    fn member_names(r: &CheckResult) -> Vec<&str> {
17179        r.probe.members.iter().map(|m| m.name.as_str()).collect()
17180    }
17181
17182    #[test]
17183    fn members_of_a_record_in_its_own_module_include_private_fields() {
17184        let (r, _) = probe_src(
17185            "record P { x: Int, pub y: Int }\n\
17186             fn main() {\n    let p = P { x: 1, y: 2 }\n    p.<|>\n}\n",
17187        );
17188        assert_eq!(member_names(&r), ["x", "y"]);
17189        assert!(r.probe.members.iter().all(|m| m.kind == MemberKind::Field));
17190        assert!(r.probe.members.iter().all(|m| m.decl.is_some()));
17191    }
17192
17193    #[test]
17194    fn members_of_a_std_vec_hide_its_internals_and_its_associated_functions() {
17195        let (r, _) = probe_src("fn main() {\n    let v: Vec<Int> = Vec::new()\n    v.<|>\n}\n");
17196        let names = member_names(&r);
17197        for m in ["push", "len", "get"] {
17198            assert!(names.contains(&m), "{m} missing from {names:?}");
17199        }
17200        // `data` is a std field without `pub`; `new` takes no `self`.
17201        assert!(!names.contains(&"data"), "{names:?}");
17202        assert!(!names.contains(&"new"), "{names:?}");
17203        let push = r.probe.members.iter().find(|m| m.name == "push").unwrap();
17204        assert_eq!(push.kind, MemberKind::Method);
17205        assert!(push.decl.is_some());
17206    }
17207
17208    #[test]
17209    fn members_of_a_string_and_of_an_array() {
17210        let (s, _) = probe_src("fn main() {\n    let n = \"a\".<|>\n}\n");
17211        assert!(member_names(&s).contains(&"len"), "{:?}", member_names(&s));
17212        let (a, _) = probe_src("fn main() {\n    let n = [1, 2].<|>\n}\n");
17213        assert_eq!(member_names(&a), ["len"]);
17214        assert_eq!(a.probe.members[0].decl, None);
17215    }
17216
17217    #[test]
17218    fn members_of_a_bounded_type_parameter_are_its_bounds_methods() {
17219        let (r, _) = probe_src("fn f<T: Display>(t: T) -> String {\n    t.<|>\n}\nfn main() {}\n");
17220        assert_eq!(member_names(&r), ["fmt"]);
17221    }
17222
17223    #[test]
17224    fn members_include_the_methods_of_traits_the_type_implements() {
17225        let (r, _) = probe_src(
17226            "record P { x: Int }\n\
17227             impl Display for P {\n    fn fmt(self) -> String { \"p\" }\n}\n\
17228             fn main() {\n    let p = P { x: 1 }\n    p.<|>\n}\n",
17229        );
17230        let names = member_names(&r);
17231        assert!(names.contains(&"x") && names.contains(&"fmt"), "{names:?}");
17232    }
17233
17234    #[test]
17235    fn the_probe_records_locals_on_an_empty_line() {
17236        let (r, _) = probe_src("fn main() {\n    let a = 1\n    let b = \"s\"\n    <|>\n}\n");
17237        assert_eq!(
17238            r.probe.locals,
17239            vec![("a".to_string(), Ty::Int), ("b".to_string(), Ty::String)]
17240        );
17241    }
17242
17243    #[test]
17244    fn the_probe_records_locals_at_a_name_innermost_first_each_once() {
17245        let (r, _) = probe_src(
17246            "fn main(n: Int) {\n    let a = 1\n    if true {\n        let a = \"s\"\n        a<|>\n    }\n}\n",
17247        );
17248        assert_eq!(
17249            r.probe.locals,
17250            vec![("a".to_string(), Ty::String), ("n".to_string(), Ty::Int)]
17251        );
17252    }
17253}