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(¶ms) {
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 ¬es {
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}