Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/unify.rs')
| -rw-r--r-- | crates/hir-ty/src/infer/unify.rs | 540 |
1 files changed, 313 insertions, 227 deletions
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index b0f916b8c0..f9ad76b0c1 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -3,35 +3,35 @@ use std::fmt; use base_db::Crate; -use hir_def::{AdtId, ExpressionStoreOwnerId, GenericParamId, TraitId}; +use hir_def::{ExpressionStoreOwnerId, GenericParamId, TraitId}; use rustc_hash::FxHashSet; use rustc_type_ir::{ - TyVid, TypeFoldable, TypeVisitableExt, UpcastFrom, + TyVid, TypeFoldable, TypeVisitableExt, inherent::{Const as _, GenericArg as _, IntoKind, Ty as _}, solve::Certainty, }; use smallvec::SmallVec; +use thin_vec::ThinVec; use crate::{ + InferenceDiagnostic, Span, db::HirDatabase, next_solver::{ - Canonical, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Goal, - ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty, TyKind, - TypingMode, + Canonical, ClauseKind, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, + GenericArgs, ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty, + TyKind, TypingMode, fulfill::{FulfillmentCtxt, NextSolverError}, infer::{ - DbInternerInferExt, InferCtxt, InferOk, InferResult, - at::{At, ToTrace}, + DbInternerInferExt, InferCtxt, InferOk, + at::At, snapshot::CombinedSnapshot, traits::{Obligation, ObligationCause, PredicateObligation}, }, inspect::{InspectConfig, InspectGoal, ProofTreeVisitor}, obligation_ctxt::ObligationCtxt, }, - traits::{ - NextTraitSolveResult, ParamEnvAndCrate, next_trait_solve_canonical_in_ctxt, - next_trait_solve_in_ctxt, - }, + solver_errors::SolverDiagnostic, + traits::ParamEnvAndCrate, }; struct NestedObligationsForSelfTy<'a, 'db> { @@ -44,6 +44,10 @@ struct NestedObligationsForSelfTy<'a, 'db> { impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> { type Result = (); + fn span(&self) -> Span { + self.root_cause.span() + } + fn config(&self) -> InspectConfig { // Using an intentionally low depth to minimize the chance of future // breaking changes in case we adapt the approach later on. This also @@ -63,7 +67,7 @@ impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> { if self.ctx.predicate_has_self_ty(goal.predicate, self.self_ty) { self.obligations_for_self_ty.push(Obligation::new( db, - self.root_cause.clone(), + *self.root_cause, goal.param_env, goal.predicate, )); @@ -115,7 +119,7 @@ fn could_unify_impl<'db>( let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); let cause = ObligationCause::dummy(); let at = infcx.at(&cause, env.param_env); - let ((ty1_with_vars, ty2_with_vars), _) = infcx.instantiate_canonical(tys); + let ((ty1_with_vars, ty2_with_vars), _) = infcx.instantiate_canonical(Span::Dummy, tys); let mut ctxt = ObligationCtxt::new(&infcx); let can_unify = at .eq(ty1_with_vars, ty2_with_vars) @@ -124,18 +128,13 @@ fn could_unify_impl<'db>( can_unify && select(&mut ctxt).is_empty() } -#[derive(Clone)] pub(crate) struct InferenceTable<'db> { pub(crate) db: &'db dyn HirDatabase, pub(crate) param_env: ParamEnv<'db>, pub(crate) infer_ctxt: InferCtxt<'db>, pub(super) fulfillment_cx: FulfillmentCtxt<'db>, pub(super) diverging_type_vars: FxHashSet<Ty<'db>>, -} - -pub(crate) struct InferenceTableSnapshot<'db> { - ctxt_snapshot: CombinedSnapshot, - obligations: FulfillmentCtxt<'db>, + pub(super) trait_errors: Vec<NextSolverError<'db>>, } impl<'db> InferenceTable<'db> { @@ -145,14 +144,10 @@ impl<'db> InferenceTable<'db> { db: &'db dyn HirDatabase, trait_env: ParamEnv<'db>, krate: Crate, - owner: Option<ExpressionStoreOwnerId>, + owner: ExpressionStoreOwnerId, ) -> Self { let interner = DbInterner::new_with(db, krate); - let typing_mode = match owner { - Some(owner) => TypingMode::typeck_for_body(interner, owner.into()), - // IDE things wants to reveal opaque types. - None => TypingMode::PostAnalysis, - }; + let typing_mode = TypingMode::typeck_for_body(interner, owner.into()); let infer_ctxt = interner.infer_ctxt().build(typing_mode); InferenceTable { db, @@ -160,6 +155,7 @@ impl<'db> InferenceTable<'db> { fulfillment_cx: FulfillmentCtxt::new(&infer_ctxt), infer_ctxt, diverging_type_vars: FxHashSet::default(), + trait_errors: Vec::new(), } } @@ -172,6 +168,10 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.type_is_copy_modulo_regions(self.param_env, ty) } + pub(crate) fn type_is_sized_modulo_regions(&self, ty: Ty<'db>) -> bool { + self.infer_ctxt.type_is_sized_modulo_regions(self.param_env, ty) + } + pub(crate) fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'db>) -> bool { self.infer_ctxt.type_is_use_cloned_modulo_regions(self.param_env, ty) } @@ -253,29 +253,12 @@ impl<'db> InferenceTable<'db> { self.diverging_type_vars.insert(ty); } - pub(crate) fn canonicalize<T>(&mut self, t: T) -> rustc_type_ir::Canonical<DbInterner<'db>, T> - where - T: TypeFoldable<DbInterner<'db>>, - { - // try to resolve obligations before canonicalizing, since this might - // result in new knowledge about variables - self.select_obligations_where_possible(); - self.infer_ctxt.canonicalize_response(t) - } - - pub(crate) fn normalize_alias_ty(&mut self, alias: Ty<'db>) -> Ty<'db> { - self.infer_ctxt - .at(&ObligationCause::new(), self.param_env) - .structurally_normalize_ty(alias, &mut self.fulfillment_cx) - .unwrap_or(alias) - } - - pub(crate) fn next_ty_var(&self) -> Ty<'db> { - self.infer_ctxt.next_ty_var() + pub(crate) fn next_ty_var(&self, span: Span) -> Ty<'db> { + self.infer_ctxt.next_ty_var(span) } - pub(crate) fn next_const_var(&self) -> Const<'db> { - self.infer_ctxt.next_const_var() + pub(crate) fn next_const_var(&self, span: Span) -> Const<'db> { + self.infer_ctxt.next_const_var(span) } pub(crate) fn next_int_var(&self) -> Ty<'db> { @@ -286,42 +269,18 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.next_float_var() } - pub(crate) fn new_maybe_never_var(&mut self) -> Ty<'db> { - let var = self.next_ty_var(); + pub(crate) fn new_maybe_never_var(&mut self, span: Span) -> Ty<'db> { + let var = self.next_ty_var(span); self.set_diverging(var); var } - pub(crate) fn next_region_var(&self) -> Region<'db> { - self.infer_ctxt.next_region_var() - } - - pub(crate) fn next_var_for_param(&self, id: GenericParamId) -> GenericArg<'db> { - self.infer_ctxt.next_var_for_param(id) - } - - pub(crate) fn resolve_completely<T>(&mut self, value: T) -> T - where - T: TypeFoldable<DbInterner<'db>>, - { - let value = self.infer_ctxt.resolve_vars_if_possible(value); - - let mut goals = vec![]; - - // FIXME(next-solver): Handle `goals`. - - value.fold_with(&mut resolve_completely::Resolver::new(self, true, &mut goals)) + pub(crate) fn next_region_var(&self, span: Span) -> Region<'db> { + self.infer_ctxt.next_region_var(span) } - /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. - pub(crate) fn unify<T: ToTrace<'db>>(&mut self, ty1: T, ty2: T) -> bool { - self.try_unify(ty1, ty2).map(|infer_ok| self.register_infer_ok(infer_ok)).is_ok() - } - - /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the - /// caller needs to deal with them. - pub(crate) fn try_unify<T: ToTrace<'db>>(&mut self, t1: T, t2: T) -> InferResult<'db, ()> { - self.at(&ObligationCause::new()).eq(t1, t2) + pub(crate) fn var_for_def(&self, id: GenericParamId, span: Span) -> GenericArg<'db> { + self.infer_ctxt.var_for_def(id, span) } pub(crate) fn at<'a>(&'a self, cause: &'a ObligationCause) -> At<'a, 'db> { @@ -332,6 +291,10 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.shallow_resolve(ty) } + pub(crate) fn resolve_vars_if_possible<T: TypeFoldable<DbInterner<'db>>>(&self, t: T) -> T { + self.infer_ctxt.resolve_vars_if_possible(t) + } + pub(crate) fn resolve_vars_with_obligations<T>(&mut self, t: T) -> T where T: rustc_type_ir::TypeFoldable<DbInterner<'db>>, @@ -351,8 +314,8 @@ impl<'db> InferenceTable<'db> { } /// Create a `GenericArgs` full of infer vars for `def`. - pub(crate) fn fresh_args_for_item(&self, def: SolverDefId) -> GenericArgs<'db> { - self.infer_ctxt.fresh_args_for_item(def) + pub(crate) fn fresh_args_for_item(&self, span: Span, def: SolverDefId) -> GenericArgs<'db> { + self.infer_ctxt.fresh_args_for_item(span, def) } /// Try to resolve `ty` to a structural type, normalizing aliases. @@ -360,36 +323,55 @@ impl<'db> InferenceTable<'db> { /// In case there is still ambiguity, the returned type may be an inference /// variable. This is different from `structurally_resolve_type` which errors /// in this case. - pub(crate) fn try_structurally_resolve_type(&mut self, ty: Ty<'db>) -> Ty<'db> { + pub(crate) fn try_structurally_resolve_type(&mut self, span: Span, ty: Ty<'db>) -> Ty<'db> { if let TyKind::Alias(..) = ty.kind() { let result = self .infer_ctxt - .at(&ObligationCause::misc(), self.param_env) + .at(&ObligationCause::new(span), self.param_env) .structurally_normalize_ty(ty, &mut self.fulfillment_cx); match result { Ok(normalized_ty) => normalized_ty, - Err(_errors) => Ty::new_error(self.interner(), ErrorGuaranteed), + Err(errors) => { + self.trait_errors.extend(errors); + Ty::new_error(self.interner(), ErrorGuaranteed) + } } } else { self.resolve_vars_with_obligations(ty) } } - pub(crate) fn structurally_resolve_type(&mut self, ty: Ty<'db>) -> Ty<'db> { - self.try_structurally_resolve_type(ty) - // FIXME: Err if it still contain infer vars. + pub(crate) fn try_structurally_resolve_const( + &mut self, + sp: Span, + ct: Const<'db>, + ) -> Const<'db> { + let ct = self.resolve_vars_with_obligations(ct); + + if let ConstKind::Unevaluated(..) = ct.kind() { + let result = self + .infer_ctxt + .at(&ObligationCause::new(sp), self.param_env) + .structurally_normalize_const(ct, &mut self.fulfillment_cx); + match result { + Ok(normalized_ct) => normalized_ct, + Err(errors) => { + self.trait_errors.extend(errors); + Const::new_error(self.interner(), ErrorGuaranteed) + } + } + } else { + ct + } } - pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot<'db> { - let ctxt_snapshot = self.infer_ctxt.start_snapshot(); - let obligations = self.fulfillment_cx.clone(); - InferenceTableSnapshot { ctxt_snapshot, obligations } + pub(crate) fn snapshot(&mut self) -> CombinedSnapshot { + self.infer_ctxt.start_snapshot() } #[tracing::instrument(skip_all)] - pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot<'db>) { - self.infer_ctxt.rollback_to(snapshot.ctxt_snapshot); - self.fulfillment_cx = snapshot.obligations; + pub(crate) fn rollback_to(&mut self, snapshot: CombinedSnapshot) { + self.infer_ctxt.rollback_to(snapshot); } pub(crate) fn commit_if_ok<T, E>( @@ -399,51 +381,12 @@ impl<'db> InferenceTable<'db> { let snapshot = self.snapshot(); let result = f(self); match result { - Ok(_) => {} - Err(_) => { - self.rollback_to(snapshot); - } + Ok(_) => self.infer_ctxt.commit_from(snapshot), + Err(_) => self.rollback_to(snapshot), } result } - /// Checks an obligation without registering it. Useful mostly to check - /// whether a trait *might* be implemented before deciding to 'lock in' the - /// choice (during e.g. method resolution or deref). - #[tracing::instrument(level = "debug", skip(self))] - pub(crate) fn try_obligation(&mut self, predicate: Predicate<'db>) -> NextTraitSolveResult { - let goal = Goal { param_env: self.param_env, predicate }; - let canonicalized = self.canonicalize(goal); - - next_trait_solve_canonical_in_ctxt(&self.infer_ctxt, canonicalized) - } - - pub(crate) fn register_obligation(&mut self, predicate: Predicate<'db>) { - let goal = Goal { param_env: self.param_env, predicate }; - self.register_obligation_in_env(goal) - } - - #[tracing::instrument(level = "debug", skip(self))] - fn register_obligation_in_env(&mut self, goal: Goal<'db, Predicate<'db>>) { - let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); - tracing::debug!(?result); - match result { - Ok((_, Certainty::Yes)) => {} - Err(rustc_type_ir::solve::NoSolution) => {} - Ok((_, Certainty::Maybe { .. })) => { - self.fulfillment_cx.register_predicate_obligation( - &self.infer_ctxt, - Obligation::new( - self.interner(), - ObligationCause::new(), - goal.param_env, - goal.predicate, - ), - ); - } - } - } - pub(crate) fn register_bound(&mut self, ty: Ty<'db>, def_id: TraitId, cause: ObligationCause) { if !ty.references_non_lt_error() { let trait_ref = TraitRef::new(self.interner(), def_id.into(), [ty]); @@ -463,7 +406,8 @@ impl<'db> InferenceTable<'db> { } pub(crate) fn select_obligations_where_possible(&mut self) { - self.fulfillment_cx.try_evaluate_obligations(&self.infer_ctxt); + let errors = self.fulfillment_cx.try_evaluate_obligations(&self.infer_ctxt); + self.trait_errors.extend(errors); } pub(super) fn register_predicate(&mut self, obligation: PredicateObligation<'db>) { @@ -478,9 +422,7 @@ impl<'db> InferenceTable<'db> { where I: IntoIterator<Item = PredicateObligation<'db>>, { - obligations.into_iter().for_each(|obligation| { - self.register_predicate(obligation); - }); + self.fulfillment_cx.register_predicate_obligations(&self.infer_ctxt, obligations); } /// checking later, during regionck, that `arg` is well-formed. @@ -494,9 +436,9 @@ impl<'db> InferenceTable<'db> { } /// Registers obligations that all `args` are well-formed. - pub(crate) fn add_wf_bounds(&mut self, args: GenericArgs<'db>) { + pub(crate) fn add_wf_bounds(&mut self, span: Span, args: GenericArgs<'db>) { for term in args.iter().filter_map(|it| it.as_term()) { - self.register_wf_obligation(term, ObligationCause::new()); + self.register_wf_obligation(term, ObligationCause::new(span)); } } @@ -507,15 +449,9 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.insert_type_vars(ty) } - /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. - pub(super) fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> { - if ty.is_ty_error() { self.next_ty_var() } else { ty } - } - /// Whenever you lower a user-written type, you should call this. pub(crate) fn process_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { self.process_remote_user_written_ty(ty) - // FIXME: Register a well-formed obligation. } /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation, @@ -525,77 +461,17 @@ impl<'db> InferenceTable<'db> { // See https://github.com/rust-lang/rust/blob/cdb45c87e2cd43495379f7e867e3cc15dcee9f93/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs#L487-L495: // Even though the new solver only lazily normalizes usually, here we eagerly normalize so that not everything needs // to normalize before inspecting the `TyKind`. - // FIXME(next-solver): We should not deeply normalize here, only shallowly. - self.try_structurally_resolve_type(ty) - } - - /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it. - pub(super) fn insert_const_vars_shallow(&mut self, c: Const<'db>) -> Const<'db> { - if c.is_ct_error() { self.next_const_var() } else { c } - } - - /// Check if given type is `Sized` or not - pub(crate) fn is_sized(&mut self, ty: Ty<'db>) -> bool { - fn short_circuit_trivial_tys(ty: Ty<'_>) -> Option<bool> { - match ty.kind() { - TyKind::Bool - | TyKind::Char - | TyKind::Int(_) - | TyKind::Uint(_) - | TyKind::Float(_) - | TyKind::Ref(..) - | TyKind::RawPtr(..) - | TyKind::Never - | TyKind::FnDef(..) - | TyKind::Array(..) - | TyKind::FnPtr(..) => Some(true), - TyKind::Slice(..) | TyKind::Str | TyKind::Dynamic(..) => Some(false), - _ => None, - } - } - - let mut ty = ty; - ty = self.try_structurally_resolve_type(ty); - if let Some(sized) = short_circuit_trivial_tys(ty) { - return sized; - } - - { - let mut structs = SmallVec::<[_; 8]>::new(); - // Must use a loop here and not recursion because otherwise users will conduct completely - // artificial examples of structs that have themselves as the tail field and complain r-a crashes. - while let Some((AdtId::StructId(id), subst)) = ty.as_adt() { - let struct_data = id.fields(self.db); - if let Some((last_field, _)) = struct_data.fields().iter().next_back() { - let last_field_ty = self.db.field_types(id.into())[last_field] - .get() - .instantiate(self.interner(), subst); - if structs.contains(&ty) { - // A struct recursively contains itself as a tail field somewhere. - return true; // Don't overload the users with too many errors. - } - structs.push(ty); - // Structs can have DST as its last field and such cases are not handled - // as unsized by the chalk, so we do this manually. - ty = last_field_ty; - ty = self.try_structurally_resolve_type(ty); - if let Some(sized) = short_circuit_trivial_tys(ty) { - return sized; - } - } else { - break; - }; - } - } + self.try_structurally_resolve_type(Span::Dummy, ty) + } - let Some(sized) = self.interner().lang_items().Sized else { - return false; - }; - let sized_pred = Predicate::upcast_from( - TraitRef::new(self.interner(), sized.into(), [ty]), - self.interner(), - ); - self.try_obligation(sized_pred).certain() + fn emit_trait_errors(&mut self, diagnostics: &mut ThinVec<InferenceDiagnostic>) { + diagnostics.extend(std::mem::take(&mut self.trait_errors).into_iter().filter_map( + |error| { + let error = error.into_fulfillment_error(&self.infer_ctxt); + SolverDiagnostic::from_fulfillment_error(&error) + .map(InferenceDiagnostic::SolverDiagnostic) + }, + )); } } @@ -608,20 +484,229 @@ impl fmt::Debug for InferenceTable<'_> { } } -mod resolve_completely { - use rustc_type_ir::{DebruijnIndex, Flags, TypeFolder, TypeSuperFoldable}; +pub(super) mod resolve_completely { + use rustc_hash::FxHashSet; + use rustc_type_ir::{ + DebruijnIndex, Flags, InferConst, InferTy, TypeFlags, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitableExt, inherent::IntoKind, + }; + use stdx::never; + use thin_vec::ThinVec; use crate::{ + InferenceDiagnostic, Span, infer::unify::InferenceTable, next_solver::{ - Const, DbInterner, Goal, Predicate, Region, Term, Ty, + Const, ConstKind, DbInterner, DefaultAny, GenericArg, Goal, Predicate, Region, Term, + TermKind, Ty, TyKind, infer::{resolve::ReplaceInferWithError, traits::ObligationCause}, normalize::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals, }, }; + pub(crate) struct WriteBackCtxt<'db> { + table: InferenceTable<'db>, + diagnostics: ThinVec<InferenceDiagnostic>, + has_errors: bool, + spans_emitted_type_must_be_known_for: FxHashSet<Span>, + types: &'db DefaultAny<'db>, + } + + impl<'db> WriteBackCtxt<'db> { + pub(crate) fn new( + table: InferenceTable<'db>, + diagnostics: ThinVec<InferenceDiagnostic>, + vars_emitted_type_must_be_known_for: FxHashSet<Term<'db>>, + ) -> Self { + let spans_emitted_type_must_be_known_for = vars_emitted_type_must_be_known_for + .into_iter() + .filter_map(|term| match term.kind() { + TermKind::Ty(ty) => match ty.kind() { + TyKind::Infer(InferTy::TyVar(vid)) => { + Some(table.infer_ctxt.type_var_span(vid)) + } + _ => None, + }, + TermKind::Const(ct) => match ct.kind() { + ConstKind::Infer(InferConst::Var(vid)) => { + table.infer_ctxt.const_var_span(vid) + } + _ => None, + }, + }) + .collect(); + + Self { + types: table.interner().default_types(), + table, + diagnostics, + has_errors: false, + spans_emitted_type_must_be_known_for, + } + } + + pub(crate) fn resolve_completely<T>(&mut self, value_ref: &mut T) + where + T: TypeFoldable<DbInterner<'db>>, + { + self.resolve_completely_with_default(value_ref, value_ref.clone()); + } + + pub(crate) fn resolve_completely_with_default<T>(&mut self, value_ref: &mut T, default: T) + where + T: TypeFoldable<DbInterner<'db>>, + { + let value = std::mem::replace(value_ref, default); + + let value = self.table.resolve_vars_if_possible(value); + + let mut goals = vec![]; + + // FIXME(next-solver): Handle `goals`. + + *value_ref = value.fold_with(&mut Resolver::new(self, true, &mut goals)); + } + + pub(crate) fn resolve_diagnostics(mut self) -> (ThinVec<InferenceDiagnostic>, bool) { + let has_errors = self.has_errors; + + self.table.emit_trait_errors(&mut self.diagnostics); + + // Ignore diagnostics made from resolving diagnostics. + let mut diagnostics = std::mem::take(&mut self.diagnostics); + diagnostics.retain_mut(|diagnostic| { + self.resolve_completely(diagnostic); + + if let InferenceDiagnostic::ExpectedFunction { found: ty, .. } + | InferenceDiagnostic::ExpectedArrayOrSlicePat { found: ty, .. } + | InferenceDiagnostic::UnresolvedField { receiver: ty, .. } + | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic + && ty.as_ref().references_non_lt_error() + { + false + } else { + true + } + }); + diagnostics.shrink_to_fit(); + + (diagnostics, has_errors) + } + } + + struct DiagnoseInferVars<'a, 'db> { + ctx: &'a mut WriteBackCtxt<'db>, + top_term: Term<'db>, + } + + impl<'db> DiagnoseInferVars<'_, 'db> { + const TYPE_FLAGS: TypeFlags = TypeFlags::HAS_INFER.union(TypeFlags::HAS_NON_REGION_ERROR); + + fn err_on_span(&mut self, span: Span) { + if !self.ctx.spans_emitted_type_must_be_known_for.insert(span) { + // Suppress duplicate diagnostics. + return; + } + + if span.is_dummy() { + return; + } + + // We have to be careful not to insert infer vars here, as we won't resolve this new diagnostic. + let top_term = self.top_term.fold_with(&mut ReplaceInferWithError::new(self.cx())); + self.ctx.diagnostics.push(InferenceDiagnostic::TypeMustBeKnown { + at_point: span, + top_term: Some(GenericArg::from(top_term).store()), + }); + } + } + + impl<'db> TypeFolder<DbInterner<'db>> for DiagnoseInferVars<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.ctx.table.interner() + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + if !t.has_type_flags(Self::TYPE_FLAGS) { + return t; + } + + match t.kind() { + TyKind::Error(_) => { + self.ctx.has_errors = true; + t + } + TyKind::Infer(infer_ty) => match infer_ty { + InferTy::TyVar(vid) => { + self.err_on_span(self.ctx.table.infer_ctxt.type_var_span(vid)); + self.ctx.has_errors = true; + self.ctx.types.types.error + } + InferTy::IntVar(_) => { + never!("fallback should have resolved all int vars"); + self.ctx.types.types.i32 + } + InferTy::FloatVar(_) => { + never!("fallback should have resolved all float vars"); + self.ctx.types.types.f64 + } + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => { + never!("should not have fresh infer vars outside of caching"); + self.ctx.has_errors = true; + self.ctx.types.types.error + } + }, + _ => t.super_fold_with(self), + } + } + + fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { + if !c.has_type_flags(Self::TYPE_FLAGS) { + return c; + } + + match c.kind() { + ConstKind::Error(_) => { + self.ctx.has_errors = true; + c + } + ConstKind::Infer(infer_ct) => match infer_ct { + InferConst::Var(vid) => { + if let Some(span) = self.ctx.table.infer_ctxt.const_var_span(vid) { + self.err_on_span(span); + } + self.ctx.has_errors = true; + self.ctx.types.consts.error + } + InferConst::Fresh(_) => { + never!("should not have fresh infer vars outside of caching"); + self.ctx.has_errors = true; + self.ctx.types.consts.error + } + }, + _ => c.super_fold_with(self), + } + } + + fn fold_predicate(&mut self, p: Predicate<'db>) -> Predicate<'db> { + if !p.has_type_flags(Self::TYPE_FLAGS) { + return p; + } + p.super_fold_with(self) + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + if r.is_var() { + // For now, we don't error on regions. + self.ctx.types.regions.error + } else { + r + } + } + } + pub(super) struct Resolver<'a, 'db> { - ctx: &'a mut InferenceTable<'db>, + ctx: &'a mut WriteBackCtxt<'db>, /// Whether we should normalize, disabled when resolving predicates. should_normalize: bool, nested_goals: &'a mut Vec<Goal<'db, Predicate<'db>>>, @@ -629,7 +714,7 @@ mod resolve_completely { impl<'a, 'db> Resolver<'a, 'db> { pub(super) fn new( - ctx: &'a mut InferenceTable<'db>, + ctx: &'a mut WriteBackCtxt<'db>, should_normalize: bool, nested_goals: &'a mut Vec<Goal<'db, Predicate<'db>>>, ) -> Resolver<'a, 'db> { @@ -645,8 +730,9 @@ mod resolve_completely { T: Into<Term<'db>> + TypeSuperFoldable<DbInterner<'db>> + Copy, { let value = if self.should_normalize { - let cause = ObligationCause::new(); - let at = self.ctx.at(&cause); + // FIXME: This should not use a dummy span. + let cause = ObligationCause::new(Span::Dummy); + let at = self.ctx.table.at(&cause); let universes = vec![None; outer_exclusive_binder(value).as_usize()]; match deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( at, value, universes, @@ -655,8 +741,8 @@ mod resolve_completely { self.nested_goals.extend(goals); value } - Err(_errors) => { - // FIXME: Report the error. + Err(errors) => { + self.ctx.table.trait_errors.extend(errors); value } } @@ -664,17 +750,17 @@ mod resolve_completely { value }; - value.fold_with(&mut ReplaceInferWithError::new(self.ctx.interner())) + value.fold_with(&mut DiagnoseInferVars { ctx: self.ctx, top_term: value.into() }) } } - impl<'cx, 'db> TypeFolder<DbInterner<'db>> for Resolver<'cx, 'db> { + impl<'db> TypeFolder<DbInterner<'db>> for Resolver<'_, 'db> { fn cx(&self) -> DbInterner<'db> { - self.ctx.interner() + self.ctx.table.interner() } fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { - if r.is_var() { Region::error(self.ctx.interner()) } else { r } + if r.is_var() { self.ctx.types.regions.error } else { r } } fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { |