Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22186 from ChayimFriedman2/trait-errors
feat: Diagnose trait errors 🎉
Chayim Refael Friedman 2 weeks ago
parent 7546435 · parent 26ae4ac · commit 1102c0b
-rw-r--r--crates/hir-def/src/attrs.rs8
-rw-r--r--crates/hir-def/src/expr_store/lower/format_args.rs48
-rw-r--r--crates/hir-def/src/hir/format_args.rs9
-rw-r--r--crates/hir-expand/src/files.rs10
-rw-r--r--crates/hir-ty/src/autoderef.rs24
-rw-r--r--crates/hir-ty/src/diagnostics/expr.rs14
-rw-r--r--crates/hir-ty/src/display.rs25
-rw-r--r--crates/hir-ty/src/infer.rs121
-rw-r--r--crates/hir-ty/src/infer/autoderef.rs14
-rw-r--r--crates/hir-ty/src/infer/callee.rs25
-rw-r--r--crates/hir-ty/src/infer/cast.rs31
-rw-r--r--crates/hir-ty/src/infer/closure.rs71
-rw-r--r--crates/hir-ty/src/infer/closure/analysis.rs16
-rw-r--r--crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs69
-rw-r--r--crates/hir-ty/src/infer/coerce.rs44
-rw-r--r--crates/hir-ty/src/infer/expr.rs108
-rw-r--r--crates/hir-ty/src/infer/op.rs8
-rw-r--r--crates/hir-ty/src/infer/opaques.rs4
-rw-r--r--crates/hir-ty/src/infer/pat.rs53
-rw-r--r--crates/hir-ty/src/infer/path.rs7
-rw-r--r--crates/hir-ty/src/infer/place_op.rs9
-rw-r--r--crates/hir-ty/src/infer/unify.rs74
-rw-r--r--crates/hir-ty/src/lib.rs8
-rw-r--r--crates/hir-ty/src/method_resolution.rs6
-rw-r--r--crates/hir-ty/src/method_resolution/confirm.rs40
-rw-r--r--crates/hir-ty/src/method_resolution/probe.rs34
-rw-r--r--crates/hir-ty/src/mir/lower.rs11
-rw-r--r--crates/hir-ty/src/next_solver/binder.rs26
-rw-r--r--crates/hir-ty/src/next_solver/consts.rs39
-rw-r--r--crates/hir-ty/src/next_solver/fulfill.rs2
-rw-r--r--crates/hir-ty/src/next_solver/infer/at.rs26
-rw-r--r--crates/hir-ty/src/next_solver/infer/errors.rs704
-rw-r--r--crates/hir-ty/src/next_solver/infer/mod.rs7
-rw-r--r--crates/hir-ty/src/next_solver/infer/relate/lattice.rs9
-rw-r--r--crates/hir-ty/src/next_solver/infer/traits.rs43
-rw-r--r--crates/hir-ty/src/next_solver/inspect.rs6
-rw-r--r--crates/hir-ty/src/next_solver/normalize.rs3
-rw-r--r--crates/hir-ty/src/next_solver/predicate.rs1
-rw-r--r--crates/hir-ty/src/next_solver/structural_normalize.rs2
-rw-r--r--crates/hir-ty/src/next_solver/util.rs2
-rw-r--r--crates/hir-ty/src/solver_errors.rs90
-rw-r--r--crates/hir-ty/src/specialization.rs2
-rw-r--r--crates/hir-ty/src/tests.rs43
-rw-r--r--crates/hir-ty/src/tests/regression.rs1
-rw-r--r--crates/hir/src/diagnostics.rs117
-rw-r--r--crates/hir/src/display.rs10
-rw-r--r--crates/hir/src/lib.rs64
-rw-r--r--crates/ide-diagnostics/src/handlers/invalid_cast.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs1
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_unsafe.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/remove_trailing_return.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/type_mismatch.rs25
-rw-r--r--crates/ide-diagnostics/src/handlers/type_must_be_known.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/undeclared_label.rs8
-rw-r--r--crates/ide-diagnostics/src/handlers/unimplemented_trait.rs53
-rw-r--r--crates/ide-diagnostics/src/lib.rs2
-rw-r--r--crates/ide-diagnostics/src/tests.rs4
-rw-r--r--crates/ide/src/references.rs3
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs45
-rw-r--r--crates/test-utils/src/minicore.rs56
60 files changed, 1748 insertions, 547 deletions
diff --git a/crates/hir-def/src/attrs.rs b/crates/hir-def/src/attrs.rs
index 7757d537e6..352c98a76a 100644
--- a/crates/hir-def/src/attrs.rs
+++ b/crates/hir-def/src/attrs.rs
@@ -265,6 +265,12 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow<
}
_ => {}
},
+ "diagnostic" => match &*second_segment {
+ "do_not_recommend" => {
+ attr_flags.insert(AttrFlags::DIAGNOSTIC_DO_NOT_RECOMMEND)
+ }
+ _ => {}
+ },
_ => {}
},
}
@@ -339,6 +345,8 @@ bitflags::bitflags! {
const PREFER_UNDERSCORE_IMPORT = 1 << 49;
const IS_MUST_USE = 1 << 50;
+
+ const DIAGNOSTIC_DO_NOT_RECOMMEND = 1 << 51;
}
}
diff --git a/crates/hir-def/src/expr_store/lower/format_args.rs b/crates/hir-def/src/expr_store/lower/format_args.rs
index beb1267173..b058ad6d9a 100644
--- a/crates/hir-def/src/expr_store/lower/format_args.rs
+++ b/crates/hir-def/src/expr_store/lower/format_args.rs
@@ -30,12 +30,14 @@ impl<'db> ExprCollector<'db> {
) -> ExprId {
let mut args = FormatArgumentsCollector::default();
f.args().for_each(|arg| {
+ let expr = arg.expr();
args.add(FormatArgument {
kind: match arg.arg_name() {
Some(name) => FormatArgumentKind::Named(Name::new_root(name.name().text())),
None => FormatArgumentKind::Normal,
},
- expr: self.collect_expr_opt(arg.expr()),
+ syntax: expr.as_ref().map(AstPtr::new),
+ expr: self.collect_expr_opt(expr),
});
});
let template = f.template();
@@ -53,8 +55,10 @@ impl<'db> ExprCollector<'db> {
Some(((s, is_direct_literal), template)) => {
let call_ctx = SyntaxContext::root(self.def_map.edition());
let hygiene = self.hygiene_id_for(s.syntax().text_range());
+ let template_ptr = AstPtr::new(&template);
let fmt = format_args::parse(
&s,
+ template_ptr,
fmt_snippet,
args,
is_direct_literal,
@@ -65,10 +69,7 @@ impl<'db> ExprCollector<'db> {
.template_map
.get_or_insert_with(Default::default)
.implicit_capture_to_source
- .insert(
- expr_id,
- self.expander.in_file((AstPtr::new(&template), range)),
- );
+ .insert(expr_id, self.expander.in_file((template_ptr, range)));
}
if !hygiene.is_root() {
self.store.ident_hygiene.insert(expr_id.into(), hygiene);
@@ -340,7 +341,8 @@ impl<'db> ExprCollector<'db> {
expr: args_ident_expr,
name: Name::new_tuple_field(arg_index),
});
- self.make_argument(arg, ty)
+ let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax);
+ self.make_argument(arg_ptr, arg, ty)
})
.collect();
let args =
@@ -557,7 +559,8 @@ impl<'db> ExprCollector<'db> {
rawness: Rawness::Ref,
mutability: Mutability::Shared,
});
- self.make_argument(arg, ty)
+ let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax);
+ self.make_argument(arg_ptr, arg, ty)
})
.collect();
let array =
@@ -649,7 +652,8 @@ impl<'db> ExprCollector<'db> {
rawness: Rawness::Ref,
mutability: Mutability::Shared,
});
- self.make_argument(ref_arg, ty)
+ let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax);
+ self.make_argument(arg_ptr, ref_arg, ty)
})
.collect();
let args =
@@ -716,7 +720,8 @@ impl<'db> ExprCollector<'db> {
expr: args_ident_expr,
name: Name::new_tuple_field(arg_index),
});
- self.make_argument(arg, ty)
+ let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax);
+ self.make_argument(arg_ptr, arg, ty)
})
.collect();
let array =
@@ -976,11 +981,16 @@ impl<'db> ExprCollector<'db> {
/// ```text
/// <core::fmt::Argument>::new_…(arg)
/// ```
- fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId {
+ fn make_argument(
+ &mut self,
+ arg_ptr: Option<AstPtr<ast::Expr>>,
+ arg: ExprId,
+ ty: ArgumentType,
+ ) -> ExprId {
use ArgumentType::*;
use FormatTrait::*;
- let new_fn = self.ty_rel_lang_path_desugared_expr(
+ let new_fn = self.ty_rel_lang_path(
self.lang_items().FormatArgument,
match ty {
Format(Display) => sym::new_display,
@@ -995,6 +1005,22 @@ impl<'db> ExprCollector<'db> {
Usize => sym::from_usize,
},
);
+ let new_fn = match new_fn {
+ Some(new_fn) => {
+ let new_fn = self.store.exprs.alloc(Expr::Path(new_fn));
+ if let Some(arg_ptr) = arg_ptr {
+ // Trait errors (the argument does not implement the expected fmt trait) will show
+ // on this path, so to not end up with synthetic syntax we insert this mapping. We
+ // don't want to insert the other way's mapping in order to not override the source
+ // for the argument.
+ self.store
+ .expr_map_back
+ .insert(new_fn, self.expander.in_file(arg_ptr.wrap_left()));
+ }
+ new_fn
+ }
+ None => self.missing_expr(),
+ };
self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) })
}
diff --git a/crates/hir-def/src/hir/format_args.rs b/crates/hir-def/src/hir/format_args.rs
index 271484da7b..366857f233 100644
--- a/crates/hir-def/src/hir/format_args.rs
+++ b/crates/hir-def/src/hir/format_args.rs
@@ -7,7 +7,7 @@ use rustc_parse_format as parse;
use span::SyntaxContext;
use stdx::TupleExt;
use syntax::{
- TextRange,
+ AstPtr, TextRange,
ast::{self, IsString},
};
@@ -146,6 +146,7 @@ pub enum FormatCount {
pub struct FormatArgument {
pub kind: FormatArgumentKind,
pub expr: ExprId,
+ pub syntax: Option<AstPtr<ast::Expr>>,
}
#[derive(Clone, PartialEq, Eq, Debug)]
@@ -171,6 +172,7 @@ use PositionUsedAs::*;
#[allow(clippy::unnecessary_lazy_evaluations)]
pub(crate) fn parse(
s: &ast::String,
+ string_ptr: AstPtr<ast::Expr>,
fmt_snippet: Option<String>,
mut args: FormatArgumentsCollector,
is_direct_literal: bool,
@@ -273,6 +275,11 @@ pub(crate) fn parse(
// FIXME: This is problematic, we might want to synthesize a dummy
// expression proper and/or desugar these.
expr: synth(name, span),
+ // FIXME: This will lead us to show failed trait bounds (e.g. `T: Display`)
+ // on the whole template string for implicit arguments instead of just their name.
+ // Fixing this is hard since we don't have an `AstPtr` for the arg, and it's
+ // only part of an expression.
+ syntax: Some(string_ptr),
}))
}
}
diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs
index 09a5c9326e..d64090f913 100644
--- a/crates/hir-expand/src/files.rs
+++ b/crates/hir-expand/src/files.rs
@@ -128,6 +128,16 @@ impl ErasedAstId {
}
}
+impl<FileKind, N: AstNode> InFileWrapper<FileKind, AstPtr<N>> {
+ #[inline]
+ pub fn upcast<M: AstNode>(self) -> InFileWrapper<FileKind, AstPtr<M>>
+ where
+ N: Into<M>,
+ {
+ self.map(|it| it.upcast())
+ }
+}
+
impl<FileKind, T> InFileWrapper<FileKind, T> {
pub fn new(file_id: FileKind, value: T) -> Self {
Self { file_id, value }
diff --git a/crates/hir-ty/src/autoderef.rs b/crates/hir-ty/src/autoderef.rs
index 2c2400a14a..a8ed4126ab 100644
--- a/crates/hir-ty/src/autoderef.rs
+++ b/crates/hir-ty/src/autoderef.rs
@@ -40,7 +40,7 @@ pub fn autoderef<'db>(
let interner = DbInterner::new_with(db, env.krate);
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
let (ty, _) = infcx.instantiate_canonical(Span::Dummy, &ty);
- let autoderef = Autoderef::new(&infcx, env.param_env, ty);
+ let autoderef = Autoderef::new(&infcx, env.param_env, ty, Span::Dummy);
let mut v = Vec::new();
for (ty, _steps) in autoderef {
// `ty` may contain unresolved inference variables. Since there's no chance they would be
@@ -155,6 +155,7 @@ pub(crate) struct GeneralAutoderef<'db, Ctx, Steps = Vec<(Ty<'db>, AutoderefKind
// Configurations:
include_raw_pointers: bool,
use_receiver_trait: bool,
+ span: Span,
}
pub(crate) type Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> =
@@ -200,7 +201,7 @@ where
// autoderef expect this type to have been structurally normalized.
if let TyKind::Alias(..) = ty.kind() {
let (normalized_ty, obligations) =
- structurally_normalize_ty(self.infcx(), self.param_env(), ty)?;
+ structurally_normalize_ty(self.infcx(), self.param_env(), ty, self.span)?;
self.state.obligations.extend(obligations);
(AutoderefKind::Builtin, normalized_ty)
} else {
@@ -232,8 +233,9 @@ impl<'a, 'db> Autoderef<'a, 'db> {
infcx: &'a InferCtxt<'db>,
param_env: ParamEnv<'db>,
base_ty: Ty<'db>,
+ span: Span,
) -> Self {
- Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty)
+ Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty, span)
}
}
@@ -242,8 +244,9 @@ impl<'a, 'b, 'db> InferenceContextAutoderef<'a, 'b, 'db> {
pub(crate) fn new_from_inference_context(
ctx: &'a mut InferenceContext<'b, 'db>,
base_ty: Ty<'db>,
+ span: Span,
) -> Self {
- Self::new_impl(InferenceContextAutoderefCtx(ctx), base_ty)
+ Self::new_impl(InferenceContextAutoderefCtx(ctx), base_ty, span)
}
#[inline]
@@ -258,8 +261,9 @@ impl<'a, 'db> Autoderef<'a, 'db, usize> {
infcx: &'a InferCtxt<'db>,
param_env: ParamEnv<'db>,
base_ty: Ty<'db>,
+ span: Span,
) -> Self {
- Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty)
+ Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty, span)
}
}
@@ -269,7 +273,7 @@ where
Steps: TrackAutoderefSteps<'db>,
{
#[inline]
- fn new_impl(ctx: Ctx, base_ty: Ty<'db>) -> Self {
+ fn new_impl(ctx: Ctx, base_ty: Ty<'db>, span: Span) -> Self {
GeneralAutoderef {
state: AutoderefSnapshot {
steps: Steps::default(),
@@ -282,6 +286,7 @@ where
traits: None,
include_raw_pointers: false,
use_receiver_trait: false,
+ span,
}
}
@@ -338,7 +343,7 @@ where
let trait_ref = TraitRef::new(interner, trait_.into(), [ty]);
let obligation =
- Obligation::new(interner, ObligationCause::new(), self.param_env(), trait_ref);
+ Obligation::new(interner, ObligationCause::new(self.span), self.param_env(), trait_ref);
// We detect whether the self type implements `Deref` before trying to
// structurally normalize. We use `predicate_may_hold_opaque_types_jank`
// to support not-yet-defined opaque types. It will succeed for `impl Deref`
@@ -352,6 +357,7 @@ where
self.infcx(),
self.param_env(),
Ty::new_projection(interner, trait_target.into(), [ty]),
+ self.span,
)?;
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
self.state.obligations.extend(obligations);
@@ -403,9 +409,11 @@ fn structurally_normalize_ty<'db>(
infcx: &InferCtxt<'db>,
param_env: ParamEnv<'db>,
ty: Ty<'db>,
+ span: Span,
) -> Option<(Ty<'db>, PredicateObligations<'db>)> {
let mut ocx = ObligationCtxt::new(infcx);
- let Ok(normalized_ty) = ocx.structurally_normalize_ty(&ObligationCause::misc(), param_env, ty)
+ let Ok(normalized_ty) =
+ ocx.structurally_normalize_ty(&ObligationCause::new(span), param_env, ty)
else {
// We shouldn't have errors here in the old solver, except for
// evaluate/fulfill mismatches, but that's not a reason for an ICE.
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index be29926a38..768b185ae7 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -182,7 +182,7 @@ impl<'db> ExprValidator<'db> {
}
// Check that the number of arguments matches the number of parameters.
- if self.infer.expr_type_mismatches().next().is_some() {
+ if self.infer.exprs_have_type_mismatches() {
// FIXME: Due to shortcomings in the current type system implementation, only emit
// this diagnostic if there are no type mismatches in the containing function.
} else if let Expr::MethodCall { receiver, .. } = expr {
@@ -355,7 +355,7 @@ impl<'db> ExprValidator<'db> {
pat: PatId,
initializer: Option<ExprId>,
) -> Option<BodyValidationDiagnostic> {
- if self.infer.type_mismatch_for_pat(pat).is_some() {
+ if self.infer.pat_has_type_mismatch(pat) {
return None;
}
let initializer = initializer?;
@@ -683,13 +683,13 @@ pub fn record_pattern_missing_fields(
fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResult) -> bool {
fn walk(pat: PatId, body: &Body, infer: &InferenceResult, has_type_mismatches: &mut bool) {
- match infer.type_mismatch_for_pat(pat) {
- Some(_) => *has_type_mismatches = true,
- None if *has_type_mismatches => (),
- None => {
+ match infer.pat_has_type_mismatch(pat) {
+ true => *has_type_mismatches = true,
+ false if *has_type_mismatches => (),
+ false => {
let pat = &body[pat];
if let Pat::ConstBlock(expr) | Pat::Lit(expr) = *pat {
- *has_type_mismatches |= infer.type_mismatch_for_expr(expr).is_some();
+ *has_type_mismatches |= infer.expr_has_type_mismatch(expr);
if *has_type_mismatches {
return;
}
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index c300388c5e..fda572a02b 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -59,8 +59,8 @@ use crate::{
next_solver::{
AliasTy, Allocation, Clause, ClauseKind, Const, ConstKind, DbInterner,
ExistentialPredicate, FnSig, GenericArg, GenericArgKind, GenericArgs, ParamEnv, PolyFnSig,
- Region, SolverDefId, StoredEarlyBinder, StoredTy, Term, TermKind, TraitRef, Ty, TyKind,
- TypingMode, ValTree,
+ Region, SolverDefId, StoredEarlyBinder, StoredTy, Term, TermKind, TraitPredicate, TraitRef,
+ Ty, TyKind, TypingMode, ValTree,
abi::Safety,
infer::{DbInternerInferExt, traits::ObligationCause},
},
@@ -771,7 +771,7 @@ fn render_const_scalar<'db>(
) -> Result {
let param_env = ParamEnv::empty();
let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis);
- let ty = infcx.at(&ObligationCause::new(), param_env).deeply_normalize(ty).unwrap_or(ty);
+ let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty);
render_const_scalar_inner(f, b, memory_map, ty, param_env)
}
@@ -1056,7 +1056,7 @@ fn render_const_scalar_from_valtree<'db>(
) -> Result {
let param_env = ParamEnv::empty();
let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis);
- let ty = infcx.at(&ObligationCause::new(), param_env).deeply_normalize(ty).unwrap_or(ty);
+ let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty);
render_const_scalar_from_valtree_inner(f, ty, valtree, param_env)
}
@@ -2275,6 +2275,23 @@ impl<'db> HirDisplay<'db> for TraitRef<'db> {
}
}
+impl<'db> HirDisplay<'db> for TraitPredicate<'db> {
+ fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
+ self.self_ty().hir_fmt(f)?;
+ f.write_str(": ")?;
+ match self.polarity {
+ rustc_type_ir::PredicatePolarity::Positive => {}
+ rustc_type_ir::PredicatePolarity::Negative => f.write_char('!')?,
+ }
+ let trait_ = self.def_id().0;
+ f.start_location_link(trait_.into());
+ write!(f, "{}", TraitSignature::of(f.db, trait_).name.display(f.db, f.edition()))?;
+ f.end_location_link();
+ let substs = &self.trait_ref.args[1..];
+ hir_fmt_generic_args(f, substs, None, None)
+ }
+}
+
impl<'db> HirDisplay<'db> for Region<'db> {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
match self.kind() {
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 0e307feaa8..c61badf179 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -89,6 +89,7 @@ use crate::{
abi::Safety,
infer::{InferCtxt, ObligationInspector, traits::ObligationCause},
},
+ solver_errors::SolverDiagnostic,
utils::TargetFeatureIsSafeInTarget,
};
@@ -469,13 +470,13 @@ pub enum InferenceDiagnostic {
#[type_visitable(ignore)]
expr: ExprId,
},
-}
-
-/// A mismatch between an expected and an inferred type.
-#[derive(Clone, PartialEq, Eq, Debug, Hash, TypeVisitable, TypeFoldable)]
-pub struct TypeMismatch {
- pub expected: StoredTy,
- pub actual: StoredTy,
+ TypeMismatch {
+ #[type_visitable(ignore)]
+ node: ExprOrPatId,
+ expected: StoredTy,
+ found: StoredTy,
+ },
+ SolverDiagnostic(SolverDiagnostic),
}
/// Represents coercing a value to a different type of value.
@@ -695,7 +696,6 @@ pub struct InferenceResult {
pub(crate) type_of_type_placeholder: FxHashMap<TypeRefId, StoredTy>,
pub(crate) type_of_opaque: FxHashMap<InternedOpaqueTyId, StoredTy>,
- pub(crate) type_mismatches: Option<Box<FxHashMap<ExprOrPatId, TypeMismatch>>>,
/// Whether there are any type-mismatching errors in the result.
// FIXME: This isn't as useful as initially thought due to us falling back placeholders to
// `TyKind::Error`.
@@ -703,6 +703,8 @@ pub struct InferenceResult {
pub(crate) has_errors: bool,
/// During inference this field is empty and [`InferenceContext::diagnostics`] is filled instead.
diagnostics: ThinVec<InferenceDiagnostic>,
+ // FIXME: Remove this, change it to be in `InferenceContext`:
+ nodes_with_type_mismatches: Option<Box<FxHashSet<ExprOrPatId>>>,
/// Interned `Error` type to return references to.
// FIXME: Remove this.
@@ -996,12 +998,12 @@ impl InferenceResult {
assoc_resolutions: Default::default(),
tuple_field_access_types: Default::default(),
diagnostics: Default::default(),
+ nodes_with_type_mismatches: Default::default(),
type_of_expr: Default::default(),
type_of_pat: Default::default(),
type_of_binding: Default::default(),
type_of_type_placeholder: Default::default(),
type_of_opaque: Default::default(),
- type_mismatches: Default::default(),
skipped_ref_pats: Default::default(),
has_errors: Default::default(),
error_ty: error_ty.store(),
@@ -1052,26 +1054,22 @@ impl InferenceResult {
ExprOrPatId::PatId(id) => self.assoc_resolutions_for_pat(id),
}
}
- pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> {
- self.type_mismatches.as_deref()?.get(&expr.into())
+ pub fn expr_or_pat_has_type_mismatch(&self, node: ExprOrPatId) -> bool {
+ self.nodes_with_type_mismatches.as_ref().is_some_and(|it| it.contains(&node))
}
- pub fn type_mismatch_for_pat(&self, pat: PatId) -> Option<&TypeMismatch> {
- self.type_mismatches.as_deref()?.get(&pat.into())
+ pub fn expr_has_type_mismatch(&self, expr: ExprId) -> bool {
+ self.expr_or_pat_has_type_mismatch(expr.into())
}
- pub fn type_mismatches(&self) -> impl Iterator<Item = (ExprOrPatId, &TypeMismatch)> {
- self.type_mismatches
- .as_deref()
- .into_iter()
- .flatten()
- .map(|(expr_or_pat, mismatch)| (*expr_or_pat, mismatch))
- }
- pub fn expr_type_mismatches(&self) -> impl Iterator<Item = (ExprId, &TypeMismatch)> {
- self.type_mismatches.as_deref().into_iter().flatten().filter_map(
- |(expr_or_pat, mismatch)| match *expr_or_pat {
- ExprOrPatId::ExprId(expr) => Some((expr, mismatch)),
- _ => None,
- },
- )
+ pub fn pat_has_type_mismatch(&self, pat: PatId) -> bool {
+ self.expr_or_pat_has_type_mismatch(pat.into())
+ }
+ pub fn exprs_have_type_mismatches(&self) -> bool {
+ self.nodes_with_type_mismatches
+ .as_ref()
+ .is_some_and(|it| it.iter().any(|node| node.is_expr()))
+ }
+ pub fn has_type_mismatches(&self) -> bool {
+ self.nodes_with_type_mismatches.is_some()
}
pub fn placeholder_types<'db>(&self) -> impl Iterator<Item = (TypeRefId, Ty<'db>)> {
self.type_of_type_placeholder.iter().map(|(&type_ref, ty)| (type_ref, ty.as_ref()))
@@ -1418,7 +1416,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
type_of_type_placeholder,
type_of_opaque,
skipped_ref_pats,
- type_mismatches,
closures_data,
has_errors,
error_ty: _,
@@ -1428,6 +1425,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
tuple_field_access_types,
coercion_casts: _,
diagnostics: result_diagnostics,
+ nodes_with_type_mismatches,
} = &mut result;
let mut resolver =
WriteBackCtxt::new(table, diagnostics, vars_emitted_type_must_be_known_for);
@@ -1451,12 +1449,9 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
type_of_type_placeholder.shrink_to_fit();
type_of_opaque.shrink_to_fit();
- if let Some(type_mismatches) = type_mismatches {
+ if let Some(nodes_with_type_mismatches) = nodes_with_type_mismatches {
*has_errors = true;
- for mismatch in type_mismatches.values_mut() {
- resolver.resolve_type_mismatch(mismatch);
- }
- type_mismatches.shrink_to_fit();
+ nodes_with_type_mismatches.shrink_to_fit();
}
for (_, subst) in method_resolutions.values_mut() {
resolver.resolve_completely(subst);
@@ -1944,24 +1939,40 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
}
pub(crate) fn structurally_resolve_type(&mut self, node: ExprOrPatId, ty: Ty<'db>) -> Ty<'db> {
- let result = self.table.try_structurally_resolve_type(ty);
+ let result = self.table.try_structurally_resolve_type(node.into(), ty);
if result.is_ty_var() { self.type_must_be_known_at_this_point(node, ty) } else { result }
}
+ pub(crate) fn emit_type_mismatch(
+ &mut self,
+ node: ExprOrPatId,
+ expected: Ty<'db>,
+ found: Ty<'db>,
+ ) {
+ if self.result.nodes_with_type_mismatches.get_or_insert_default().insert(node) {
+ self.diagnostics.push(InferenceDiagnostic::TypeMismatch {
+ node,
+ expected: expected.store(),
+ found: found.store(),
+ });
+ }
+ }
+
fn demand_eqtype(
&mut self,
id: ExprOrPatId,
expected: Ty<'db>,
actual: Ty<'db>,
) -> Result<(), ()> {
- let result = self.demand_eqtype_fixme_no_diag(expected, actual);
+ let result = self
+ .table
+ .at(&ObligationCause::new(id))
+ .eq(expected, actual)
+ .map(|infer_ok| self.table.register_infer_ok(infer_ok));
if result.is_err() {
- self.result
- .type_mismatches
- .get_or_insert_default()
- .insert(id, TypeMismatch { expected: expected.store(), actual: actual.store() });
+ self.emit_type_mismatch(id, expected, actual);
}
- result
+ result.map_err(drop)
}
fn demand_eqtype_fixme_no_diag(
@@ -1971,7 +1982,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
) -> Result<(), ()> {
let result = self
.table
- .at(&ObligationCause::new())
+ .at(&ObligationCause::dummy())
.eq(expected, actual)
.map(|infer_ok| self.table.register_infer_ok(infer_ok));
result.map_err(drop)
@@ -1985,14 +1996,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
) -> Result<(), ()> {
let result = self
.table
- .at(&ObligationCause::new())
+ .at(&ObligationCause::new(id))
.sup(expected, actual)
.map(|infer_ok| self.table.register_infer_ok(infer_ok));
if result.is_err() {
- self.result
- .type_mismatches
- .get_or_insert_default()
- .insert(id, TypeMismatch { expected: expected.store(), actual: actual.store() });
+ self.emit_type_mismatch(id, expected, actual);
}
result.map_err(drop)
}
@@ -2026,11 +2034,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.types.types.error
}
- pub(crate) fn require_type_is_sized(&mut self, ty: Ty<'db>) {
+ pub(crate) fn require_type_is_sized(&mut self, ty: Ty<'db>, span: Span) {
if !ty.references_non_lt_error()
&& let Some(sized_trait) = self.lang_items.Sized
{
- self.table.register_bound(ty, sized_trait, ObligationCause::new());
+ self.table.register_bound(ty, sized_trait, ObligationCause::new(span));
}
}
@@ -2095,7 +2103,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
args,
),
);
- ty = self.table.try_structurally_resolve_type(alias);
+ ty = self.table.try_structurally_resolve_type(node.into(), alias);
segments = segments.skip(1);
}
@@ -2195,7 +2203,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
never!("resolver should always resolve lang item paths");
return (self.err_ty(), None);
};
- return self.resolve_variant_on_alias(ty, None, mod_path);
+ return self.resolve_variant_on_alias(node, ty, None, mod_path);
};
let mut remaining_segments = path.segments().skip(remaining_idx);
@@ -2308,7 +2316,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
let ty = self.db.ty(it.into()).instantiate(interner, args);
let ty = self.insert_type_vars(ty, Span::Dummy);
- self.resolve_variant_on_alias(ty, unresolved, mod_path)
+ self.resolve_variant_on_alias(node, ty, unresolved, mod_path)
}
TypeNs::AdtSelfType(_) => {
// FIXME this could happen in array size expressions, once we're checking them
@@ -2340,12 +2348,13 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
fn resolve_variant_on_alias(
&mut self,
+ node: ExprOrPatId,
ty: Ty<'db>,
unresolved: Option<usize>,
path: &ModPath,
) -> (Ty<'db>, Option<VariantId>) {
let remaining = unresolved.map(|it| path.segments()[it..].len()).filter(|it| it > &0);
- let ty = self.table.try_structurally_resolve_type(ty);
+ let ty = self.table.try_structurally_resolve_type(node.into(), ty);
match remaining {
None => {
let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id {
@@ -2558,10 +2567,14 @@ impl<'db> Expectation<'db> {
/// an expected type. Otherwise, we might write parts of the type
/// when checking the 'then' block which are incompatible with the
/// 'else' branch.
- fn adjust_for_branches(&self, table: &mut unify::InferenceTable<'db>) -> Expectation<'db> {
+ fn adjust_for_branches(
+ &self,
+ table: &mut unify::InferenceTable<'db>,
+ span: Span,
+ ) -> Expectation<'db> {
match *self {
Expectation::HasType(ety) => {
- let ety = table.try_structurally_resolve_type(ety);
+ let ety = table.try_structurally_resolve_type(span, ety);
if ety.is_ty_var() { Expectation::None } else { Expectation::HasType(ety) }
}
Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety),
diff --git a/crates/hir-ty/src/infer/autoderef.rs b/crates/hir-ty/src/infer/autoderef.rs
index a6c7b2dbb9..0ba3b3dd05 100644
--- a/crates/hir-ty/src/infer/autoderef.rs
+++ b/crates/hir-ty/src/infer/autoderef.rs
@@ -5,7 +5,7 @@ use std::iter;
use rustc_ast_ir::Mutability;
use crate::{
- Adjust, Adjustment, OverloadedDeref,
+ Adjust, Adjustment, OverloadedDeref, Span,
autoderef::{Autoderef, AutoderefCtx, AutoderefKind, GeneralAutoderef},
infer::unify::InferenceTable,
next_solver::{
@@ -15,12 +15,16 @@ use crate::{
};
impl<'db> InferenceTable<'db> {
- pub(crate) fn autoderef(&self, base_ty: Ty<'db>) -> Autoderef<'_, 'db, usize> {
- Autoderef::new(&self.infer_ctxt, self.param_env, base_ty)
+ pub(crate) fn autoderef(&self, base_ty: Ty<'db>, span: Span) -> Autoderef<'_, 'db, usize> {
+ Autoderef::new(&self.infer_ctxt, self.param_env, base_ty, span)
}
- pub(crate) fn autoderef_with_tracking(&self, base_ty: Ty<'db>) -> Autoderef<'_, 'db> {
- Autoderef::new_with_tracking(&self.infer_ctxt, self.param_env, base_ty)
+ pub(crate) fn autoderef_with_tracking(
+ &self,
+ base_ty: Ty<'db>,
+ span: Span,
+ ) -> Autoderef<'_, 'db> {
+ Autoderef::new_with_tracking(&self.infer_ctxt, self.param_env, base_ty, span)
}
}
diff --git a/crates/hir-ty/src/infer/callee.rs b/crates/hir-ty/src/infer/callee.rs
index 9fe566b8fe..9c134cb75f 100644
--- a/crates/hir-ty/src/infer/callee.rs
+++ b/crates/hir-ty/src/infer/callee.rs
@@ -43,9 +43,11 @@ impl<'db> InferenceContext<'_, 'db> {
) -> Ty<'db> {
let original_callee_ty = self.infer_expr_no_expect(callee_expr, ExprIsRead::Yes);
- let expr_ty = self.table.try_structurally_resolve_type(original_callee_ty);
+ let expr_ty =
+ self.table.try_structurally_resolve_type(callee_expr.into(), original_callee_ty);
- let mut autoderef = GeneralAutoderef::new_from_inference_context(self, expr_ty);
+ let mut autoderef =
+ GeneralAutoderef::new_from_inference_context(self, expr_ty, callee_expr.into());
let mut result = None;
let mut error_reported = false;
while result.is_none() && autoderef.next().is_some() {
@@ -95,7 +97,7 @@ impl<'db> InferenceContext<'_, 'db> {
};
// we must check that return type of called functions is WF:
- self.table.register_wf_obligation(output.into(), ObligationCause::new());
+ self.table.register_wf_obligation(output.into(), ObligationCause::new(call_expr));
output
}
@@ -108,7 +110,8 @@ impl<'db> InferenceContext<'_, 'db> {
error_reported: &mut bool,
) -> Option<CallStep<'db>> {
let final_ty = autoderef.final_ty();
- let adjusted_ty = autoderef.ctx().table.try_structurally_resolve_type(final_ty);
+ let adjusted_ty =
+ autoderef.ctx().table.try_structurally_resolve_type(callee_expr.into(), final_ty);
// If the callee is a function pointer or a closure, then we're all set.
match adjusted_ty.kind() {
@@ -242,8 +245,8 @@ impl<'db> InferenceContext<'_, 'db> {
// is implemented, and use this information for diagnostic.
autoderef
.ctx()
- .try_overloaded_call_traits(adjusted_ty, Some(arg_exprs))
- .or_else(|| autoderef.ctx().try_overloaded_call_traits(adjusted_ty, None))
+ .try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs))
+ .or_else(|| autoderef.ctx().try_overloaded_call_traits(call_expr, adjusted_ty, None))
.map(|(autoref, method)| {
let adjustments = autoderef.adjust_steps_as_infer_ok();
let mut adjustments = autoderef.ctx().table.register_infer_ok(adjustments);
@@ -255,6 +258,7 @@ impl<'db> InferenceContext<'_, 'db> {
fn try_overloaded_call_traits(
&mut self,
+ call_expr: ExprId,
adjusted_ty: Ty<'db>,
opt_arg_exprs: Option<&[ExprId]>,
) -> Option<(Option<Adjustment>, MethodCallee<'db>)> {
@@ -311,7 +315,7 @@ impl<'db> InferenceContext<'_, 'db> {
// one which may apply. So if we treat opaques as inference variables
// `Box<impl FnOnce()>: Fn` is considered ambiguous and chosen.
if let Some(ok) = self.table.lookup_method_for_operator(
- ObligationCause::new(),
+ ObligationCause::new(call_expr),
method_name,
trait_def_id,
adjusted_ty,
@@ -464,8 +468,9 @@ impl<'db> InferenceContext<'_, 'db> {
&& let Some(ty) = fn_sig.inputs().last().copied()
&& let Some(tuple_trait) = self.lang_items.Tuple
{
- self.table.register_bound(ty, tuple_trait, ObligationCause::new());
- self.require_type_is_sized(ty);
+ let span = arg_exprs.last().copied().unwrap_or(call_expr);
+ self.table.register_bound(ty, tuple_trait, ObligationCause::new(span));
+ self.require_type_is_sized(ty, span.into());
}
fn_sig.output()
@@ -538,7 +543,7 @@ impl<'a, 'db> DeferredCallResolution<'db> {
assert!(ctx.infcx().closure_kind(self.closure_ty).is_some());
// We may now know enough to figure out fn vs fnmut etc.
- match ctx.try_overloaded_call_traits(self.closure_ty, None) {
+ match ctx.try_overloaded_call_traits(self.call_expr, self.closure_ty, None) {
Some((autoref, method_callee)) => {
// One problem is that when we get here, we are going
// to have a newly instantiated function signature
diff --git a/crates/hir-ty/src/infer/cast.rs b/crates/hir-ty/src/infer/cast.rs
index daf954c217..1dade4dfd7 100644
--- a/crates/hir-ty/src/infer/cast.rs
+++ b/crates/hir-ty/src/infer/cast.rs
@@ -125,8 +125,9 @@ impl<'db> CastCheck<'db> {
&mut self,
ctx: &mut InferenceContext<'_, 'db>,
) -> Result<(), InferenceDiagnostic> {
- self.expr_ty = ctx.table.try_structurally_resolve_type(self.expr_ty);
- self.cast_ty = ctx.table.try_structurally_resolve_type(self.cast_ty);
+ self.expr_ty =
+ ctx.table.try_structurally_resolve_type(self.source_expr.into(), self.expr_ty);
+ self.cast_ty = ctx.table.try_structurally_resolve_type(self.expr.into(), self.cast_ty);
// This should always come first so that we apply the coercion, which impacts infer vars.
if ctx
@@ -199,7 +200,8 @@ impl<'db> CastCheck<'db> {
},
// array-ptr-cast
CastTy::Ptr(t, m) => {
- let t = ctx.table.try_structurally_resolve_type(t);
+ let t =
+ ctx.table.try_structurally_resolve_type(self.expr.into(), t);
if !ctx.table.type_is_sized_modulo_regions(t) {
return Err(CastError::IllegalCast);
}
@@ -262,8 +264,8 @@ impl<'db> CastCheck<'db> {
t_cast: Ty<'db>,
m_cast: Mutability,
) -> Result<(), CastError> {
- let t_expr = ctx.table.try_structurally_resolve_type(t_expr);
- let t_cast = ctx.table.try_structurally_resolve_type(t_cast);
+ let t_expr = ctx.table.try_structurally_resolve_type(self.expr.into(), t_expr);
+ let t_cast = ctx.table.try_structurally_resolve_type(self.expr.into(), t_cast);
if m_expr >= m_cast
&& let TyKind::Array(ety, _) = t_expr.kind()
@@ -306,8 +308,8 @@ impl<'db> CastCheck<'db> {
src: Ty<'db>,
dst: Ty<'db>,
) -> Result<(), CastError> {
- let src_kind = pointer_kind(src, ctx).map_err(|_| CastError::Unknown)?;
- let dst_kind = pointer_kind(dst, ctx).map_err(|_| CastError::Unknown)?;
+ let src_kind = pointer_kind(self.expr, src, ctx).map_err(|_| CastError::Unknown)?;
+ let dst_kind = pointer_kind(self.expr, dst, ctx).map_err(|_| CastError::Unknown)?;
match (src_kind, dst_kind) {
(Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()),
@@ -372,7 +374,7 @@ impl<'db> CastCheck<'db> {
// This is `fcx.demand_eqtype`, but inlined to give a better error.
if ctx
.table
- .at(&ObligationCause::dummy())
+ .at(&ObligationCause::new(self.expr))
.eq(src_obj, dst_obj)
.map(|infer_ok| ctx.table.register_infer_ok(infer_ok))
.is_err()
@@ -457,7 +459,7 @@ impl<'db> CastCheck<'db> {
ctx: &mut InferenceContext<'_, 'db>,
expr_ty: Ty<'db>,
) -> Result<(), CastError> {
- match pointer_kind(expr_ty, ctx).map_err(|_| CastError::Unknown)? {
+ match pointer_kind(self.expr, expr_ty, ctx).map_err(|_| CastError::Unknown)? {
// None => Err(CastError::UnknownExprPtrKind),
None => Ok(()),
Some(PointerKind::Error) => Ok(()),
@@ -471,7 +473,7 @@ impl<'db> CastCheck<'db> {
ctx: &mut InferenceContext<'_, 'db>,
cast_ty: Ty<'db>,
) -> Result<(), CastError> {
- match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? {
+ match pointer_kind(self.expr, cast_ty, ctx).map_err(|_| CastError::Unknown)? {
// None => Err(CastError::UnknownCastPtrKind),
None => Ok(()),
Some(PointerKind::Error) => Ok(()),
@@ -487,7 +489,7 @@ impl<'db> CastCheck<'db> {
ctx: &mut InferenceContext<'_, 'db>,
cast_ty: Ty<'db>,
) -> Result<(), CastError> {
- match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? {
+ match pointer_kind(self.expr, cast_ty, ctx).map_err(|_| CastError::Unknown)? {
// None => Err(CastError::UnknownCastPtrKind),
None => Ok(()),
Some(PointerKind::Error) => Ok(()),
@@ -516,10 +518,11 @@ enum PointerKind<'db> {
}
fn pointer_kind<'db>(
+ expr: ExprId,
ty: Ty<'db>,
ctx: &mut InferenceContext<'_, 'db>,
) -> Result<Option<PointerKind<'db>>, ()> {
- let ty = ctx.table.try_structurally_resolve_type(ty);
+ let ty = ctx.table.try_structurally_resolve_type(expr.into(), ty);
if ctx.table.type_is_sized_modulo_regions(ty) {
return Ok(Some(PointerKind::Thin));
@@ -540,14 +543,14 @@ fn pointer_kind<'db>(
let last_field_ty = ctx.db.field_types(id.into())[last_field]
.get()
.instantiate(ctx.interner(), subst);
- pointer_kind(last_field_ty, ctx)
+ pointer_kind(expr, last_field_ty, ctx)
} else {
Ok(Some(PointerKind::Thin))
}
}
TyKind::Tuple(subst) => match subst.iter().next_back() {
None => Ok(Some(PointerKind::Thin)),
- Some(ty) => pointer_kind(ty, ctx),
+ Some(ty) => pointer_kind(expr, ty, ctx),
},
TyKind::Foreign(_) => Ok(Some(PointerKind::Thin)),
TyKind::Alias(..) => Ok(Some(PointerKind::OfAlias)),
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index f90f135919..218d8e2f3e 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -18,7 +18,7 @@ use rustc_type_ir::{
use tracing::debug;
use crate::{
- FnAbi,
+ FnAbi, Span,
db::{InternedClosure, InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId},
infer::{BreakableKind, Diverges, coerce::CoerceMany, pat::PatOrigin},
next_solver::{
@@ -80,14 +80,14 @@ impl<'db> InferenceContext<'_, 'db> {
// type, and see if can glean a closure kind from there.
let (expected_sig, expected_kind) = match expected.to_option(&mut self.table) {
Some(ty) => {
- let ty = self.table.try_structurally_resolve_type(ty);
+ let ty = self.table.try_structurally_resolve_type(closure_expr.into(), ty);
self.deduce_closure_signature(closure_expr, ty, closure_kind)
}
None => (None, None),
};
let ClosureSignatures { bound_sig, mut liberated_sig } =
- self.sig_of_closure(closure_expr, arg_types, ret_type, expected_sig);
+ self.sig_of_closure(closure_expr, args, arg_types, ret_type, expected_sig);
debug!(?bound_sig, ?liberated_sig);
@@ -143,7 +143,7 @@ impl<'db> InferenceContext<'_, 'db> {
ClosureKind::OldCoroutine(_)
| ClosureKind::Coroutine { kind: CoroutineKind::Gen, .. } => {
let yield_ty = self.table.next_ty_var(closure_expr.into());
- self.require_type_is_sized(yield_ty);
+ self.require_type_is_sized(yield_ty, closure_expr.into());
yield_ty
}
ClosureKind::Coroutine { kind: CoroutineKind::Async, .. } => {
@@ -151,7 +151,7 @@ impl<'db> InferenceContext<'_, 'db> {
}
ClosureKind::Coroutine { kind: CoroutineKind::AsyncGen, .. } => {
let yield_ty = self.table.next_ty_var(closure_expr.into());
- self.require_type_is_sized(yield_ty);
+ self.require_type_is_sized(yield_ty, closure_expr.into());
self.poll_option_ty(yield_ty)
}
_ => unreachable!(),
@@ -475,7 +475,7 @@ impl<'db> InferenceContext<'_, 'db> {
_ = self
.table
.infer_ctxt
- .at(&ObligationCause::new(), self.table.param_env)
+ .at(&ObligationCause::new(closure_expr), self.table.param_env)
.eq(inferred_fnptr_sig, generalized_fnptr_sig)
.map(|infer_ok| self.table.register_infer_ok(infer_ok));
@@ -681,14 +681,21 @@ impl<'db> InferenceContext<'_, 'db> {
fn sig_of_closure(
&mut self,
closure_expr: ExprId,
- decl_inputs: &[Option<TypeRefId>],
- decl_output: Option<TypeRefId>,
+ decl_inputs: &[PatId],
+ decl_input_tys: &[Option<TypeRefId>],
+ decl_output_ty: Option<TypeRefId>,
expected_sig: Option<PolyFnSig<'db>>,
) -> ClosureSignatures<'db> {
if let Some(e) = expected_sig {
- self.sig_of_closure_with_expectation(closure_expr, decl_inputs, decl_output, e)
+ self.sig_of_closure_with_expectation(
+ closure_expr,
+ decl_inputs,
+ decl_input_tys,
+ decl_output_ty,
+ e,
+ )
} else {
- self.sig_of_closure_no_expectation(closure_expr, decl_inputs, decl_output)
+ self.sig_of_closure_no_expectation(closure_expr, decl_input_tys, decl_output_ty)
}
}
@@ -755,18 +762,25 @@ impl<'db> InferenceContext<'_, 'db> {
fn sig_of_closure_with_expectation(
&mut self,
closure_expr: ExprId,
- decl_inputs: &[Option<TypeRefId>],
- decl_output: Option<TypeRefId>,
+ decl_inputs: &[PatId],
+ decl_input_tys: &[Option<TypeRefId>],
+ decl_output_ty: Option<TypeRefId>,
expected_sig: PolyFnSig<'db>,
) -> ClosureSignatures<'db> {
// Watch out for some surprises and just ignore the
// expectation if things don't see to match up with what we
// expect.
if expected_sig.c_variadic() {
- return self.sig_of_closure_no_expectation(closure_expr, decl_inputs, decl_output);
- } else if expected_sig.skip_binder().inputs_and_output.len() != decl_inputs.len() + 1 {
- return self
- .sig_of_closure_with_mismatched_number_of_arguments(decl_inputs, decl_output);
+ return self.sig_of_closure_no_expectation(
+ closure_expr,
+ decl_input_tys,
+ decl_output_ty,
+ );
+ } else if expected_sig.skip_binder().inputs_and_output.len() != decl_input_tys.len() + 1 {
+ return self.sig_of_closure_with_mismatched_number_of_arguments(
+ decl_input_tys,
+ decl_output_ty,
+ );
}
// Create a `PolyFnSig`. Note the oddity that late bound
@@ -798,11 +812,14 @@ impl<'db> InferenceContext<'_, 'db> {
match self.merge_supplied_sig_with_expectation(
closure_expr,
decl_inputs,
- decl_output,
+ decl_input_tys,
+ decl_output_ty,
closure_sigs,
) {
Ok(infer_ok) => self.table.register_infer_ok(infer_ok),
- Err(_) => self.sig_of_closure_no_expectation(closure_expr, decl_inputs, decl_output),
+ Err(_) => {
+ self.sig_of_closure_no_expectation(closure_expr, decl_input_tys, decl_output_ty)
+ }
}
}
@@ -822,15 +839,17 @@ impl<'db> InferenceContext<'_, 'db> {
fn merge_supplied_sig_with_expectation(
&mut self,
closure_expr: ExprId,
- decl_inputs: &[Option<TypeRefId>],
- decl_output: Option<TypeRefId>,
+ decl_inputs: &[PatId],
+ decl_input_tys: &[Option<TypeRefId>],
+ decl_output_ty: Option<TypeRefId>,
mut expected_sigs: ClosureSignatures<'db>,
) -> InferResult<'db, ClosureSignatures<'db>> {
// Get the signature S that the user gave.
//
// (See comment on `sig_of_closure_with_expectation` for the
// meaning of these letters.)
- let supplied_sig = self.supplied_sig_of_closure(closure_expr, decl_inputs, decl_output);
+ let supplied_sig =
+ self.supplied_sig_of_closure(closure_expr, decl_input_tys, decl_output_ty);
debug!(?supplied_sig);
@@ -858,19 +877,21 @@ impl<'db> InferenceContext<'_, 'db> {
// The liberated version of this signature should be a subtype
// of the liberated form of the expectation.
- for (supplied_ty, expected_ty) in iter::zip(
- supplied_sig.inputs().iter().copied(),
+ for ((decl_input, supplied_ty), expected_ty) in iter::zip(
+ iter::zip(decl_inputs, supplied_sig.inputs().iter().copied()),
expected_sigs.liberated_sig.inputs().iter().copied(),
) {
// Check that E' = S'.
- let cause = ObligationCause::new();
+ let cause = ObligationCause::new(*decl_input);
let InferOk { value: (), obligations } =
table.infer_ctxt.at(&cause, table.param_env).eq(expected_ty, supplied_ty)?;
all_obligations.extend(obligations);
}
let supplied_output_ty = supplied_sig.output();
- let cause = ObligationCause::new();
+ let cause = ObligationCause::new(
+ decl_output_ty.map(Span::TypeRefId).unwrap_or(closure_expr.into()),
+ );
let InferOk { value: (), obligations } =
table
.infer_ctxt
diff --git a/crates/hir-ty/src/infer/closure/analysis.rs b/crates/hir-ty/src/infer/closure/analysis.rs
index eb4eb8cce3..ce7931bb3c 100644
--- a/crates/hir-ty/src/infer/closure/analysis.rs
+++ b/crates/hir-ty/src/infer/closure/analysis.rs
@@ -52,7 +52,7 @@ use span::Edition;
use tracing::{debug, instrument};
use crate::{
- FnAbi,
+ FnAbi, Span,
infer::{
CaptureInfo, CaptureSourceStack, CapturedPlace, InferenceContext, UpvarCapture,
closure::analysis::expr_use_visitor::{
@@ -920,12 +920,12 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
self.result.closures_data.insert(closure_def_id, closure_data);
}
- fn normalize_capture_place(&self, place: Place) -> Place {
+ fn normalize_capture_place(&self, span: Span, place: Place) -> Place {
let mut place = self.infcx().resolve_vars_if_possible(place);
// In the new solver, types in HIR `Place`s can contain unnormalized aliases,
// which can ICE later (e.g. when projecting fields for diagnostics).
- let cause = ObligationCause::misc();
+ let cause = ObligationCause::new(span);
let at = self.table.at(&cause);
match normalize::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
at,
@@ -1011,7 +1011,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// Normalize eagerly when inserting into `capture_information`, so all downstream
// capture analysis can assume a normalized `Place`.
- self.normalize_capture_place(place)
+ self.normalize_capture_place(var_hir_id.into(), place)
}
/// A captured place is mutable if
@@ -1215,7 +1215,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind {
let mut dummy_capture_info =
CaptureInfo { sources: SmallVec::new(), capture_kind: dummy_capture_kind };
- let place = ctx.normalize_capture_place(place_with_id.place.clone());
+ let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone());
let place = restrict_capture_precision(place, &mut dummy_capture_info);
@@ -1231,7 +1231,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind {
};
assert_eq!(self.closure_def_id, upvar_closure);
- let place = ctx.normalize_capture_place(place_with_id.place.clone());
+ let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone());
self.capture_information.push((
place,
@@ -1246,7 +1246,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind {
};
assert_eq!(self.closure_def_id, upvar_closure);
- let place = ctx.normalize_capture_place(place_with_id.place.clone());
+ let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone());
self.capture_information.push((
place,
@@ -1271,7 +1271,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind {
let mut capture_info =
CaptureInfo { sources: place_with_id.origins.iter().cloned().collect(), capture_kind };
- let place = ctx.normalize_capture_place(place_with_id.place.clone());
+ let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone());
// We only want repr packed restriction to be applied to reading references into a packed
// struct, and not when the data is being moved. Therefore we call this method here instead
diff --git a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
index b234e69197..44dc1a63d1 100644
--- a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
+++ b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
@@ -14,22 +14,19 @@ use hir_def::{
},
resolver::ValueNs,
};
-use rustc_ast_ir::{try_visit, visit::VisitorResult};
-use rustc_type_ir::{
- FallibleTypeFolder, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor,
- inherent::{IntoKind, Ty as _},
-};
+use macros::{TypeFoldable, TypeVisitable};
+use rustc_type_ir::inherent::{IntoKind, Ty as _};
use smallvec::{SmallVec, smallvec};
use syntax::ast::{BinaryOp, UnaryOp};
use tracing::{debug, instrument, trace};
use crate::{
- Adjust, Adjustment, AutoBorrow,
+ Adjust, Adjustment, AutoBorrow, Span,
infer::{
ByRef, CaptureSourceStack, InferenceContext, UpvarCapture, closure::analysis::BorrowKind,
},
method_resolution::CandidateId,
- next_solver::{DbInterner, ErrorGuaranteed, StoredTy, Ty, TyKind},
+ next_solver::{ErrorGuaranteed, StoredTy, Ty, TyKind},
upvars::UpvarsRef,
utils::EnumerateAndAdjustIterator,
};
@@ -71,12 +68,13 @@ pub enum PlaceBase {
Upvar { closure: ExprId, var_id: BindingId },
}
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
pub struct Projection {
/// Type after the projection is applied.
pub ty: StoredTy,
/// Defines the kind of access made by the projection.
+ #[type_visitable(ignore)]
pub kind: ProjectionKind,
}
@@ -84,61 +82,17 @@ pub struct Projection {
/// always correspond to a syntactic place expression. For example, when
/// processing a pattern, a `Place` can be used to refer to the sub-value
/// currently being inspected.
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
pub struct Place {
/// The type of the `PlaceBase`
pub base_ty: StoredTy,
/// The "outermost" place that holds this value.
+ #[type_visitable(ignore)]
pub base: PlaceBase,
/// How this place is derived from the base place.
pub projections: Vec<Projection>,
}
-impl<'db> TypeVisitable<DbInterner<'db>> for Place {
- fn visit_with<V: TypeVisitor<DbInterner<'db>>>(&self, visitor: &mut V) -> V::Result {
- let Self { base_ty, base: _, projections } = self;
- try_visit!(base_ty.as_ref().visit_with(visitor));
- for proj in projections {
- let Projection { ty, kind: _ } = proj;
- try_visit!(ty.as_ref().visit_with(visitor));
- }
- V::Result::output()
- }
-}
-
-impl<'db> TypeFoldable<DbInterner<'db>> for Place {
- fn try_fold_with<F: FallibleTypeFolder<DbInterner<'db>>>(
- self,
- folder: &mut F,
- ) -> Result<Self, F::Error> {
- let Self { base_ty, base, projections } = self;
- let base_ty = base_ty.as_ref().try_fold_with(folder)?.store();
- let projections = projections
- .into_iter()
- .map(|proj| {
- let Projection { ty, kind } = proj;
- let ty = ty.as_ref().try_fold_with(folder)?.store();
- Ok(Projection { ty, kind })
- })
- .collect::<Result<_, _>>()?;
- Ok(Self { base_ty, base, projections })
- }
-
- fn fold_with<F: TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
- let Self { base_ty, base, projections } = self;
- let base_ty = base_ty.as_ref().fold_with(folder).store();
- let projections = projections
- .into_iter()
- .map(|proj| {
- let Projection { ty, kind } = proj;
- let ty = ty.as_ref().fold_with(folder).store();
- Projection { ty, kind }
- })
- .collect();
- Self { base_ty, base, projections }
- }
-}
-
impl Place {
/// Returns an iterator of the types that have to be dereferenced to access
/// the `Place`.
@@ -216,6 +170,13 @@ impl PlaceWithOrigin {
origin_stack.push(origin);
}
}
+
+ pub(crate) fn span(&self) -> Span {
+ match self.origins.first() {
+ Some(origin) => origin.final_source().into(),
+ None => Span::Dummy,
+ }
+ }
}
/// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists.
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index ddff23e840..e5767c2d46 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -55,9 +55,7 @@ use crate::{
Adjust, Adjustment, AutoBorrow, ParamEnvAndCrate, PointerCast, Span, TargetFeatures,
autoderef::Autoderef,
db::{HirDatabase, InternedClosure, InternedClosureId},
- infer::{
- AllowTwoPhase, AutoBorrowMutability, InferenceContext, TypeMismatch, expr::ExprIsRead,
- },
+ infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext, expr::ExprIsRead},
next_solver::{
Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, CallableIdWrapper,
Canonical, CoercePredicate, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs,
@@ -324,7 +322,7 @@ where
if source_ty != target_ty {
obligations.push(Obligation::new(
self.interner(),
- self.cause.clone(),
+ self.cause,
self.param_env(),
Binder::dummy(PredicateKind::Coerce(CoercePredicate {
a: source_ty,
@@ -377,7 +375,8 @@ where
let mut first_error = None;
let mut r_borrow_var = None;
- let mut autoderef = Autoderef::new_with_tracking(self.infcx(), self.param_env(), a);
+ let mut autoderef =
+ Autoderef::new_with_tracking(self.infcx(), self.param_env(), a, self.cause.span());
let mut found = None;
for (referent_ty, autoderefs) in autoderef.by_ref() {
@@ -669,7 +668,7 @@ where
)?;
// Create an obligation for `Source: CoerceUnsized<Target>`.
- let cause = self.cause.clone();
+ let cause = self.cause;
let pred = TraitRef::new(
self.interner(),
coerce_unsized_did.into(),
@@ -897,11 +896,11 @@ impl<'db> InferenceContext<'_, 'db> {
allow_two_phase: AllowTwoPhase,
expr_is_read: ExprIsRead,
) -> RelateResult<'db, Ty<'db>> {
- let source = self.table.try_structurally_resolve_type(expr_ty);
- target = self.table.try_structurally_resolve_type(target);
+ let source = self.table.try_structurally_resolve_type(expr.into(), expr_ty);
+ target = self.table.try_structurally_resolve_type(expr.into(), target);
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
- let cause = ObligationCause::with_span(expr.into());
+ let cause = ObligationCause::new(expr);
let coerce_never = self.expr_guaranteed_to_constitute_read_for_never(expr, expr_is_read);
let mut coerce = Coerce {
delegate: InferenceCoercionDelegate(self),
@@ -930,8 +929,8 @@ impl<'db> InferenceContext<'_, 'db> {
new: ExprId,
new_ty: Ty<'db>,
) -> RelateResult<'db, Ty<'db>> {
- let prev_ty = self.table.try_structurally_resolve_type(prev_ty);
- let new_ty = self.table.try_structurally_resolve_type(new_ty);
+ let prev_ty = self.table.try_structurally_resolve_type(new.into(), prev_ty);
+ let new_ty = self.table.try_structurally_resolve_type(new.into(), new_ty);
debug!(
"coercion::try_find_coercion_lub({:?}, {:?}, exprs={:?} exprs)",
prev_ty,
@@ -977,7 +976,7 @@ impl<'db> InferenceContext<'_, 'db> {
// We need to eagerly handle nested obligations due to lazy norm.
let mut ocx = ObligationCtxt::new(&table.infer_ctxt);
let value = ocx.lub(
- &ObligationCause::with_span(new.into()),
+ &ObligationCause::new(new),
table.param_env,
prev_ty,
new_ty,
@@ -1029,7 +1028,7 @@ impl<'db> InferenceContext<'_, 'db> {
let sig = self
.table
.infer_ctxt
- .at(&ObligationCause::with_span(new.into()), self.table.param_env)
+ .at(&ObligationCause::new(new), self.table.param_env)
.lub(a_sig, b_sig)
.map(|ok| self.table.register_infer_ok(ok))?;
@@ -1075,7 +1074,7 @@ impl<'db> InferenceContext<'_, 'db> {
// operate on values and not places, so a never coercion is valid.
let mut coerce = Coerce {
delegate: InferenceCoercionDelegate(self),
- cause: ObligationCause::with_span(new.into()),
+ cause: ObligationCause::new(new),
allow_two_phase: AllowTwoPhase::No,
coerce_never: true,
use_lub: true,
@@ -1111,7 +1110,7 @@ impl<'db> InferenceContext<'_, 'db> {
.commit_if_ok(|table| {
table
.infer_ctxt
- .at(&ObligationCause::with_span(new.into()), table.param_env)
+ .at(&ObligationCause::new(new), table.param_env)
.lub(prev_ty, new_ty)
})
.unwrap_err())
@@ -1392,14 +1391,11 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> {
self.final_ty = Some(icx.types.types.error);
- icx.result.type_mismatches.get_or_insert_default().insert(
- expression.into(),
- if label_expression_as_expected {
- TypeMismatch { expected: found.store(), actual: expected.store() }
- } else {
- TypeMismatch { expected: expected.store(), actual: found.store() }
- },
- );
+ if label_expression_as_expected {
+ icx.emit_type_mismatch(expression.into(), found, expected);
+ } else {
+ icx.emit_type_mismatch(expression.into(), expected, found);
+ }
}
}
@@ -1459,7 +1455,7 @@ fn coerce<'db>(
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
let ((ty1_with_vars, ty2_with_vars), vars) = infcx.instantiate_canonical(Span::Dummy, tys);
- let cause = ObligationCause::new();
+ let cause = ObligationCause::dummy();
// FIXME: Target features.
let target_features = TargetFeatures::default();
let mut coerce = Coerce {
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index c59c919c51..680800244b 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -42,7 +42,7 @@ use crate::{
};
use super::{
- BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch,
+ BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic,
cast::CastCheck, find_breakable,
};
@@ -117,10 +117,7 @@ impl<'db> InferenceContext<'_, 'db> {
match self.coerce(expr, ty, target, AllowTwoPhase::No, is_read) {
Ok(res) => res,
Err(_) => {
- self.result.type_mismatches.get_or_insert_default().insert(
- expr.into(),
- TypeMismatch { expected: target.store(), actual: ty.store() },
- );
+ self.emit_type_mismatch(expr.into(), target, ty);
target
}
}
@@ -327,7 +324,7 @@ impl<'db> InferenceContext<'_, 'db> {
let ty = match expr {
Expr::Missing => self.err_ty(),
&Expr::If { condition, then_branch, else_branch } => {
- let expected = &expected.adjust_for_branches(&mut self.table);
+ let expected = &expected.adjust_for_branches(&mut self.table, tgt_expr.into());
self.infer_expr_coerce_never(
condition,
&Expectation::HasType(self.types.types.bool),
@@ -346,14 +343,20 @@ impl<'db> InferenceContext<'_, 'db> {
expected.coercion_target_type(&mut self.table, then_branch.into()),
&coercion_sites,
);
- coerce.coerce(self, &ObligationCause::new(), then_branch, then_ty, ExprIsRead::Yes);
+ coerce.coerce(
+ self,
+ &ObligationCause::new(then_branch),
+ then_branch,
+ then_ty,
+ ExprIsRead::Yes,
+ );
match else_branch {
Some(else_branch) => {
let else_ty = self.infer_expr_inner(else_branch, expected, ExprIsRead::Yes);
let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
coerce.coerce(
self,
- &ObligationCause::new(),
+ &ObligationCause::new(else_branch),
else_branch,
else_ty,
ExprIsRead::Yes,
@@ -364,7 +367,7 @@ impl<'db> InferenceContext<'_, 'db> {
coerce.coerce_forced_unit(
self,
tgt_expr,
- &ObligationCause::new(),
+ &ObligationCause::new(tgt_expr),
true,
ExprIsRead::Yes,
);
@@ -461,7 +464,7 @@ impl<'db> InferenceContext<'_, 'db> {
self.infer_top_pat(arm.pat, input_ty, PatOrigin::MatchArm);
}
- let expected = expected.adjust_for_branches(&mut self.table);
+ let expected = expected.adjust_for_branches(&mut self.table, tgt_expr.into());
let result_ty = match &expected {
// We don't coerce to `()` so that if the match expression is a
// statement it's branches can have any consistent type.
@@ -485,7 +488,7 @@ impl<'db> InferenceContext<'_, 'db> {
all_arms_diverge &= self.diverges;
coerce.coerce(
self,
- &ObligationCause::new(),
+ &ObligationCause::new(arm.expr),
arm.expr,
arm_ty,
ExprIsRead::Yes,
@@ -536,10 +539,11 @@ impl<'db> InferenceContext<'_, 'db> {
match find_breakable(&mut self.breakables, label) {
Some(ctxt) => match ctxt.coerce.take() {
Some(mut coerce) => {
+ let expr = expr.unwrap_or(tgt_expr);
coerce.coerce(
self,
- &ObligationCause::new(),
- expr.unwrap_or(tgt_expr),
+ &ObligationCause::new(expr),
+ expr,
val_ty,
ExprIsRead::Yes,
);
@@ -598,7 +602,7 @@ impl<'db> InferenceContext<'_, 'db> {
self.infer_record_expr(tgt_expr, expected, path, fields, *spread)
}
Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected),
- Expr::Await { expr } => self.infer_await_expr(*expr),
+ Expr::Await { expr } => self.infer_await_expr(tgt_expr, *expr),
Expr::Cast { expr, type_ref } => {
let cast_ty = self.make_body_ty(*type_ref);
let expr_ty =
@@ -751,7 +755,7 @@ impl<'db> InferenceContext<'_, 'db> {
Expr::Tuple { exprs, .. } => {
let mut tys = match expected
.only_has_type(&mut self.table)
- .map(|t| self.table.try_structurally_resolve_type(t).kind())
+ .map(|t| self.table.try_structurally_resolve_type(tgt_expr.into(), t).kind())
{
Some(TyKind::Tuple(substs)) => substs
.iter()
@@ -964,14 +968,14 @@ impl<'db> InferenceContext<'_, 'db> {
ty
}
- fn infer_await_expr(&mut self, awaitee: ExprId) -> Ty<'db> {
+ fn infer_await_expr(&mut self, expr: ExprId, awaitee: ExprId) -> Ty<'db> {
let awaitee_ty = self.infer_expr_no_expect(awaitee, ExprIsRead::Yes);
let (Some(into_future), Some(into_future_output)) =
(self.lang_items.IntoFuture, self.lang_items.IntoFutureOutput)
else {
return self.types.types.error;
};
- self.table.register_bound(awaitee_ty, into_future, ObligationCause::new());
+ self.table.register_bound(awaitee_ty, into_future, ObligationCause::new(expr));
// Do not eagerly normalize.
Ty::new_projection(self.interner(), into_future_output.into(), [awaitee_ty])
}
@@ -1002,7 +1006,7 @@ impl<'db> InferenceContext<'_, 'db> {
self.check_record_expr_fields(adt_ty, expected, expr, variant, fields, base_expr);
- self.require_type_is_sized(adt_ty);
+ self.require_type_is_sized(adt_ty, expr.into());
adt_ty
}
@@ -1017,12 +1021,12 @@ impl<'db> InferenceContext<'_, 'db> {
) {
let interner = self.interner();
- let adt_ty = self.table.try_structurally_resolve_type(adt_ty);
+ let adt_ty = self.table.try_structurally_resolve_type(expr.into(), adt_ty);
let adt_ty_hint = expected.only_has_type(&mut self.table).and_then(|expected| {
self.infcx()
.fudge_inference_if_ok(|| {
let mut ocx = ObligationCtxt::new(self.infcx());
- ocx.sup(&ObligationCause::new(), self.table.param_env, expected, adt_ty)?;
+ ocx.sup(&ObligationCause::new(expr), self.table.param_env, expected, adt_ty)?;
if !ocx.try_evaluate_obligations().is_empty() {
return Err(TypeError::Mismatch);
}
@@ -1084,7 +1088,7 @@ impl<'db> InferenceContext<'_, 'db> {
// Check that the expected field type is WF. Otherwise, we emit no use-site error
// in the case of coercions for non-WF fields, which leads to incorrect error
// tainting. See issue #126272.
- self.table.register_wf_obligation(field_type.into(), ObligationCause::new());
+ self.table.register_wf_obligation(field_type.into(), ObligationCause::new(field.expr));
// Make sure to give a type to the field even if there's
// an error, so we can continue type-checking.
@@ -1131,7 +1135,7 @@ impl<'db> InferenceContext<'_, 'db> {
if remaining_fields.remove(&field.name).is_some() {
let target_ty =
variant_field_tys[field_idx].get().instantiate(interner, args);
- let cause = ObligationCause::new();
+ let cause = ObligationCause::new(expr);
match self.table.at(&cause).sup(target_ty, fru_ty) {
Ok(InferOk { obligations, value: () }) => {
self.table.register_predicates(obligations)
@@ -1337,7 +1341,7 @@ impl<'db> InferenceContext<'_, 'db> {
) -> Ty<'db> {
let elem_ty = match expected
.to_option(&mut self.table)
- .map(|t| self.table.try_structurally_resolve_type(t).kind())
+ .map(|t| self.table.try_structurally_resolve_type(expr.into(), t).kind())
{
Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st,
_ => self.table.next_ty_var(expr.into()),
@@ -1356,7 +1360,7 @@ impl<'db> InferenceContext<'_, 'db> {
let cur_elem_ty = self.infer_expr_inner(expr, &expected, ExprIsRead::Yes);
coerce.coerce(
self,
- &ObligationCause::new(),
+ &ObligationCause::new(expr),
expr,
cur_elem_ty,
ExprIsRead::Yes,
@@ -1402,7 +1406,13 @@ impl<'db> InferenceContext<'_, 'db> {
let return_expr_ty =
self.infer_expr_inner(expr, &Expectation::HasType(ret_ty), ExprIsRead::Yes);
let mut coerce_many = self.return_coercion.take().unwrap();
- coerce_many.coerce(self, &ObligationCause::new(), expr, return_expr_ty, ExprIsRead::Yes);
+ coerce_many.coerce(
+ self,
+ &ObligationCause::new(expr),
+ expr,
+ return_expr_ty,
+ ExprIsRead::Yes,
+ );
self.return_coercion = Some(coerce_many);
}
@@ -1416,7 +1426,7 @@ impl<'db> InferenceContext<'_, 'db> {
coerce.coerce_forced_unit(
self,
ret,
- &ObligationCause::new(),
+ &ObligationCause::new(ret),
true,
ExprIsRead::Yes,
);
@@ -1579,13 +1589,7 @@ impl<'db> InferenceContext<'_, 'db> {
)
.is_err()
{
- this.result.type_mismatches.get_or_insert_default().insert(
- expr.into(),
- TypeMismatch {
- expected: t.store(),
- actual: this.types.types.unit.store(),
- },
- );
+ this.emit_type_mismatch(expr.into(), t, this.types.types.unit);
}
t
} else {
@@ -1600,11 +1604,12 @@ impl<'db> InferenceContext<'_, 'db> {
fn lookup_field(
&mut self,
+ field_expr: ExprId,
receiver_ty: Ty<'db>,
name: &Name,
) -> Option<(Ty<'db>, Either<FieldId, TupleFieldId>, Vec<Adjustment>, bool)> {
let interner = self.interner();
- let mut autoderef = self.table.autoderef_with_tracking(receiver_ty);
+ let mut autoderef = self.table.autoderef_with_tracking(receiver_ty, field_expr.into());
let mut private_field = None;
let res = autoderef.by_ref().find_map(|(derefed_ty, _)| {
let (field_id, parameters) = match derefed_ty.kind() {
@@ -1692,7 +1697,7 @@ impl<'db> InferenceContext<'_, 'db> {
return self.err_ty();
}
- match self.lookup_field(receiver_ty, name) {
+ match self.lookup_field(tgt_expr, receiver_ty, name) {
Some((ty, field_id, adjustments, is_public)) => {
self.write_expr_adj(receiver, adjustments.into_boxed_slice());
self.result.field_resolutions.insert(tgt_expr, field_id);
@@ -1761,7 +1766,7 @@ impl<'db> InferenceContext<'_, 'db> {
CallableDefId::StructId(it) => it.into(),
CallableDefId::EnumVariantId(it) => it.loc(self.db).parent.into(),
};
- self.add_required_obligations_for_value_path(def_id, args);
+ self.add_required_obligations_for_value_path(tgt_expr.into(), def_id, args);
}
self.check_call_arguments(
@@ -1787,7 +1792,7 @@ impl<'db> InferenceContext<'_, 'db> {
expected: &Expectation<'db>,
) -> Ty<'db> {
let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes);
- let receiver_ty = self.table.try_structurally_resolve_type(receiver_ty);
+ let receiver_ty = self.table.try_structurally_resolve_type(receiver.into(), receiver_ty);
let resolved = self.lookup_method_including_private(
receiver_ty,
@@ -1809,15 +1814,15 @@ impl<'db> InferenceContext<'_, 'db> {
// Failed to resolve, report diagnostic and try to resolve as call to field access or
// assoc function
Err(_) => {
- let field_with_same_name_exists = match self.lookup_field(receiver_ty, method_name)
- {
- Some((ty, field_id, adjustments, _public)) => {
- self.write_expr_adj(receiver, adjustments.into_boxed_slice());
- self.result.field_resolutions.insert(tgt_expr, field_id);
- Some(ty)
- }
- None => None,
- };
+ let field_with_same_name_exists =
+ match self.lookup_field(tgt_expr, receiver_ty, method_name) {
+ Some((ty, field_id, adjustments, _public)) => {
+ self.write_expr_adj(receiver, adjustments.into_boxed_slice());
+ self.result.field_resolutions.insert(tgt_expr, field_id);
+ Some(ty)
+ }
+ None => None,
+ };
let assoc_func_with_same_name =
self.with_method_resolution(tgt_expr.into(), receiver.into(), |ctx| {
@@ -1961,13 +1966,13 @@ impl<'db> InferenceContext<'_, 'db> {
// return type (likely containing type variables if the function
// is polymorphic) and the expected return type.
// No argument expectations are produced if unification fails.
- let origin = ObligationCause::new();
+ let origin = ObligationCause::new(call_expr);
ocx.sup(&origin, self.table.param_env, expected_output, formal_output)?;
for &ty in &formal_input_tys {
ocx.register_obligation(Obligation::new(
self.interner(),
- ObligationCause::new(),
+ ObligationCause::new(call_expr),
self.table.param_env,
ClauseKind::WellFormed(ty.into()),
));
@@ -2088,7 +2093,7 @@ impl<'db> InferenceContext<'_, 'db> {
let formal_ty_error = this
.table
.infer_ctxt
- .at(&ObligationCause::new(), this.table.param_env)
+ .at(&ObligationCause::new(provided_arg), this.table.param_env)
.eq(formal_input_ty, coerced_ty);
// If neither check failed, the types are compatible
@@ -2147,10 +2152,7 @@ impl<'db> InferenceContext<'_, 'db> {
&& args_count_matches
{
// Don't report type mismatches if there is a mismatch in args count.
- self.result.type_mismatches.get_or_insert_default().insert(
- (*arg).into(),
- TypeMismatch { expected: expected.store(), actual: found.store() },
- );
+ self.emit_type_mismatch((*arg).into(), expected, found);
}
}
}
diff --git a/crates/hir-ty/src/infer/op.rs b/crates/hir-ty/src/infer/op.rs
index 5900027a98..0127fd6cdb 100644
--- a/crates/hir-ty/src/infer/op.rs
+++ b/crates/hir-ty/src/infer/op.rs
@@ -194,6 +194,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// particularly for things like `String + &String`.
let rhs_ty_var = self.table.next_ty_var(rhs_expr.into());
let result = self.lookup_op_method(
+ expr,
lhs_ty,
Some((rhs_expr, rhs_ty_var)),
self.lang_item_for_bin_op(op),
@@ -265,7 +266,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
operand_ty: Ty<'db>,
op: UnaryOp,
) -> Ty<'db> {
- match self.lookup_op_method(operand_ty, None, self.lang_item_for_unop(op)) {
+ match self.lookup_op_method(ex, operand_ty, None, self.lang_item_for_unop(op)) {
Ok(method) => {
self.write_method_resolution(ex, method.def_id, method.args);
method.sig.output()
@@ -279,6 +280,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
fn lookup_op_method(
&mut self,
+ expr: ExprId,
lhs_ty: Ty<'db>,
opt_rhs: Option<(ExprId, Ty<'db>)>,
(opname, trait_did): (Symbol, Option<TraitId>),
@@ -294,14 +296,14 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
);
let opt_rhs_ty = opt_rhs.map(|it| it.1);
- let cause = ObligationCause::new();
+ let cause = ObligationCause::new(expr);
// We don't consider any other candidates if this lookup fails
// so we can freely treat opaque types as inference variables here
// to allow more code to compile.
let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
let method = self.table.lookup_method_for_operator(
- cause.clone(),
+ cause,
opname,
trait_did,
lhs_ty,
diff --git a/crates/hir-ty/src/infer/opaques.rs b/crates/hir-ty/src/infer/opaques.rs
index 02bce22d6d..178b3fcbf5 100644
--- a/crates/hir-ty/src/infer/opaques.rs
+++ b/crates/hir-ty/src/infer/opaques.rs
@@ -4,6 +4,7 @@ use rustc_type_ir::{TypeVisitableExt, fold_regions};
use tracing::{debug, instrument};
use crate::{
+ Span,
infer::InferenceContext,
next_solver::{
EarlyBinder, OpaqueTypeKey, SolverDefId, TypingMode,
@@ -135,7 +136,8 @@ impl<'db> InferenceContext<'_, 'db> {
return UsageKind::UnconstrainedHiddenType(hidden_type);
}
- let cause = ObligationCause::new();
+ // FIXME: This should not use a dummy span.
+ let cause = ObligationCause::new(Span::Dummy);
let at = self.table.infer_ctxt.at(&cause, self.table.param_env);
let hidden_type = match at.deeply_normalize(hidden_type) {
Ok(hidden_type) => hidden_type,
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index 0b2c7dd745..b9ca34559c 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -29,7 +29,7 @@ use crate::{
BindingMode, InferenceDiagnostic, Span,
infer::{
AllowTwoPhase, ByRef, Expectation, InferenceContext, PatAdjust, PatAdjustment,
- TypeMismatch, expr::ExprIsRead,
+ expr::ExprIsRead,
},
next_solver::{
Const, TraitRef, Ty, TyKind, Tys,
@@ -309,7 +309,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
Pat::TupleStruct { path, .. } => Some(self.resolve_tuple_struct_pat(pat_id, path)),
_ => None,
};
- let adjust_mode = self.calc_adjust_mode(pat, opt_path_res);
+ let adjust_mode = self.calc_adjust_mode(pat_id, pat, opt_path_res);
let ty = self.infer_pat_inner(pat_id, opt_path_res, adjust_mode, expected, pat_info);
let ty = self.insert_type_vars_shallow(ty);
self.write_pat_ty(pat_id, ty);
@@ -321,6 +321,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
{
let infer_ok = self.register_deref_mut_bounds_if_needed(
pat_id,
+ pat_id,
derefed_tys.iter().filter_map(|adjust| match adjust.kind {
PatAdjust::OverloadedDeref => Some(adjust.source.as_ref()),
PatAdjust::BuiltinDeref => None,
@@ -393,7 +394,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
let expected = if let AdjustMode::Peel { .. } = adjust_mode
&& pat_info.pat_origin.default_binding_modes()
{
- self.table.try_structurally_resolve_type(expected)
+ self.table.try_structurally_resolve_type(pat.into(), expected)
} else {
expected
};
@@ -432,7 +433,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// The scrutinee is a smart pointer; implicitly dereference it. This adds a
// requirement that `expected: DerefPure`.
- let inner_ty = self.deref_pat_target(expected);
+ let inner_ty = self.deref_pat_target(pat, expected);
// Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any
// `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`.
@@ -604,6 +605,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
/// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`.
fn calc_adjust_mode(
&mut self,
+ pat_id: PatId,
pat: &Pat,
opt_path_res: Option<Result<ResolvedPat<'db>, ()>>,
) -> AdjustMode {
@@ -640,7 +642,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
let mut peeled_ty = lit_ty;
let mut pat_ref_layers = 0;
while let TyKind::Ref(_, inner_ty, mutbl) =
- self.table.try_structurally_resolve_type(peeled_ty).kind()
+ self.table.try_structurally_resolve_type(pat_id.into(), peeled_ty).kind()
{
// We rely on references at the head of constants being immutable.
debug_assert!(mutbl.is_not());
@@ -763,7 +765,10 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
match expected.kind() {
// Allow `b"...": &[u8]`
TyKind::Ref(_, inner_ty, _)
- if self.table.try_structurally_resolve_type(inner_ty).is_slice() =>
+ if self
+ .table
+ .try_structurally_resolve_type(expr.into(), inner_ty)
+ .is_slice() =>
{
trace!(?expr, "polymorphic byte string lit");
pat_ty = self.types.types.static_u8_slice;
@@ -788,7 +793,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// string literal patterns to have type `str`. This is accounted for when lowering to MIR.
if self.features.deref_patterns
&& matches!(literal, Literal::String(_))
- && self.table.try_structurally_resolve_type(expected).is_str()
+ && self.table.try_structurally_resolve_type(expr.into(), expected).is_str()
{
pat_ty = self.types.types.str;
}
@@ -825,7 +830,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// be peeled to `str` while ty here is still `&str`, if we don't
// err early here, a rather confusing unification error will be
// emitted instead).
- let ty = self.table.try_structurally_resolve_type(ty);
+ let ty = self.table.try_structurally_resolve_type(expr.into(), ty);
let fail =
!(ty.is_numeric() || ty.is_char() || ty.is_ty_var() || ty.references_error());
Some((fail, ty, expr))
@@ -1118,7 +1123,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
let pat_ty = Ty::new(interner, TyKind::Tuple(element_tys));
if self.demand_eqtype(pat.into(), expected, pat_ty).is_err() {
let expected = if let TyKind::Tuple(tys) =
- self.table.try_structurally_resolve_type(expected).kind()
+ self.table.try_structurally_resolve_type(Span::Dummy, expected).kind()
{
for (expected_var, found) in iter::zip(element_tys, tys) {
// Constrain the infer var so that the type mismatch error message, which contains it,
@@ -1265,15 +1270,21 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
box_ty
}
- fn _infer_deref_pat(&mut self, inner: PatId, expected: Ty<'db>, pat_info: PatInfo) -> Ty<'db> {
- let target_ty = self.deref_pat_target(expected);
+ fn _infer_deref_pat(
+ &mut self,
+ pat: PatId,
+ inner: PatId,
+ expected: Ty<'db>,
+ pat_info: PatInfo,
+ ) -> Ty<'db> {
+ let target_ty = self.deref_pat_target(pat, expected);
self.infer_pat(inner, target_ty, pat_info);
- let infer_ok = self.register_deref_mut_bounds_if_needed(inner, [expected]);
+ let infer_ok = self.register_deref_mut_bounds_if_needed(pat, inner, [expected]);
self.table.register_infer_ok(infer_ok);
expected
}
- fn deref_pat_target(&mut self, source_ty: Ty<'db>) -> Ty<'db> {
+ fn deref_pat_target(&mut self, pat: PatId, source_ty: Ty<'db>) -> Ty<'db> {
let (Some(deref_pure), Some(deref_target)) =
(self.lang_items.DerefPure, self.lang_items.DerefTarget)
else {
@@ -1281,10 +1292,10 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
};
// Register a `DerefPure` bound, which is required by all `deref!()` pats.
let interner = self.interner();
- self.table.register_bound(source_ty, deref_pure, ObligationCause::new());
+ self.table.register_bound(source_ty, deref_pure, ObligationCause::new(pat));
// The expected type for the deref pat's inner pattern is `<expected as Deref>::Target`.
let target_ty = Ty::new_projection(interner, deref_target.into(), [source_ty]);
- self.table.try_structurally_resolve_type(target_ty)
+ self.table.try_structurally_resolve_type(pat.into(), target_ty)
}
/// Check if the interior of a deref pattern (either explicit or implicit) has any `ref mut`
@@ -1293,6 +1304,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
/// account for `ref mut` binding modes inherited from implicitly dereferencing `&mut` refs.
fn register_deref_mut_bounds_if_needed(
&self,
+ pat: PatId,
inner: PatId,
derefed_tys: impl IntoIterator<Item = Ty<'db>>,
) -> InferOk<'db, ()> {
@@ -1303,7 +1315,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
for mutably_derefed_ty in derefed_tys {
infer_ok.obligations.push(Obligation::new(
interner,
- ObligationCause::new(),
+ ObligationCause::new(pat),
self.table.param_env,
TraitRef::new(interner, deref_mut.into(), [mutably_derefed_ty]),
));
@@ -1350,7 +1362,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not();
}
- expected = self.table.try_structurally_resolve_type(expected);
+ expected = self.table.try_structurally_resolve_type(pat.into(), expected);
// Determine whether we're consuming an inherited reference and resetting the default
// binding mode, based on edition and enabled experimental features.
if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
@@ -1574,7 +1586,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
expected: Ty<'db>,
pat_info: PatInfo,
) -> Ty<'db> {
- let expected = self.table.try_structurally_resolve_type(expected);
+ let expected = self.table.try_structurally_resolve_type(pat.into(), expected);
// If the pattern is irrefutable and `expected` is an infer ty, we try to equate it
// to an array if the given pattern allows it. See issue #76342
@@ -1693,10 +1705,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
match self.coerce(expr, expected, lhs_ty, AllowTwoPhase::No, expr_is_read) {
Ok(ty) => ty,
Err(_) => {
- self.result.type_mismatches.get_or_insert_default().insert(
- expr.into(),
- TypeMismatch { expected: expected.store(), actual: lhs_ty.store() },
- );
+ self.emit_type_mismatch(expr.into(), expected, lhs_ty);
// `rhs_ty` is returned so no further type mismatches are
// reported because of this mismatch.
expected
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index c020c9812b..4df38e96ee 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -40,7 +40,7 @@ impl<'db> InferenceContext<'_, 'db> {
};
let args = self.insert_type_vars(substs, id.into());
- self.add_required_obligations_for_value_path(generic_def, args);
+ self.add_required_obligations_for_value_path(id, generic_def, args);
let ty = self.db.value_ty(value_def)?.instantiate(self.interner(), args);
let ty = self.process_remote_user_written_ty(ty);
@@ -226,6 +226,7 @@ impl<'db> InferenceContext<'_, 'db> {
pub(super) fn add_required_obligations_for_value_path(
&mut self,
+ node: ExprOrPatId,
def: GenericDefId,
subst: GenericArgs<'db>,
) {
@@ -234,7 +235,7 @@ impl<'db> InferenceContext<'_, 'db> {
let param_env = self.table.param_env;
self.table.register_predicates(clauses_as_obligations(
predicates.iter_instantiated(interner, subst.as_slice()),
- ObligationCause::new(),
+ ObligationCause::new(node),
param_env,
));
}
@@ -348,7 +349,7 @@ impl<'db> InferenceContext<'_, 'db> {
name: &Name,
id: ExprOrPatId,
) -> Option<(ValueNs, GenericArgs<'db>)> {
- let ty = self.table.try_structurally_resolve_type(ty);
+ let ty = self.table.try_structurally_resolve_type(id.into(), ty);
let (enum_id, subst) = match ty.as_adt() {
Some((AdtId::EnumId(e), subst)) => (e, subst),
_ => return None,
diff --git a/crates/hir-ty/src/infer/place_op.rs b/crates/hir-ty/src/infer/place_op.rs
index 63841af682..968d793615 100644
--- a/crates/hir-ty/src/infer/place_op.rs
+++ b/crates/hir-ty/src/infer/place_op.rs
@@ -90,7 +90,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// autoderef that normal method probing does. They could likely be
// consolidated.
- let mut autoderef = InferenceContextAutoderef::new_from_inference_context(self, base_ty);
+ let mut autoderef =
+ InferenceContextAutoderef::new_from_inference_context(self, base_ty, base_expr.into());
let mut result = None;
while result.is_none() && autoderef.next().is_some() {
result = Self::try_index_step(expr, base_expr, index_expr, &mut autoderef, idx_ty);
@@ -126,7 +127,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
let ctx = autoderef.ctx();
ctx.table.register_predicate(Obligation::new(
ctx.interner(),
- ObligationCause::new(),
+ ObligationCause::new(base_expr),
ctx.table.param_env,
ClauseKind::ConstArgHasType(ct, ctx.types.types.usize),
));
@@ -206,7 +207,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// opaque types as rigid here to support `impl Deref<Target = impl Index<usize>>`.
let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
self.table.lookup_method_for_operator(
- ObligationCause::with_span(expr.into()),
+ ObligationCause::new(expr),
imm_op,
imm_tr,
base_ty,
@@ -239,7 +240,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// of the opaque.
let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
table.lookup_method_for_operator(
- ObligationCause::with_span(expr.into()),
+ ObligationCause::new(expr),
mut_op,
mut_tr,
base_ty,
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index 4342375621..6a34c5b8e5 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -11,9 +11,10 @@ use rustc_type_ir::{
solve::Certainty,
};
use smallvec::SmallVec;
+use thin_vec::ThinVec;
use crate::{
- Span,
+ InferenceDiagnostic, Span,
db::HirDatabase,
next_solver::{
Canonical, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
@@ -29,6 +30,7 @@ use crate::{
inspect::{InspectConfig, InspectGoal, ProofTreeVisitor},
obligation_ctxt::ObligationCtxt,
},
+ solver_errors::SolverDiagnostic,
traits::ParamEnvAndCrate,
};
@@ -65,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,
));
@@ -133,6 +135,7 @@ pub(crate) struct InferenceTable<'db> {
pub(crate) infer_ctxt: InferCtxt<'db>,
pub(super) fulfillment_cx: FulfillmentCtxt<'db>,
pub(super) diverging_type_vars: FxHashSet<Ty<'db>>,
+ trait_errors: Vec<NextSolverError<'db>>,
}
impl<'db> InferenceTable<'db> {
@@ -153,6 +156,7 @@ impl<'db> InferenceTable<'db> {
fulfillment_cx: FulfillmentCtxt::new(&infer_ctxt),
infer_ctxt,
diverging_type_vars: FxHashSet::default(),
+ trait_errors: Vec::new(),
}
}
@@ -320,15 +324,18 @@ 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)
@@ -376,7 +383,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>) {
@@ -396,22 +404,18 @@ impl<'db> InferenceTable<'db> {
/// checking later, during regionck, that `arg` is well-formed.
pub(crate) fn register_wf_obligation(&mut self, term: Term<'db>, cause: ObligationCause) {
- let _ = (term, cause);
- // FIXME: We don't currently register an obligation here because we don't implement
- // wf checking anyway and this function is currently often passed dummy spans, which could
- // prevent reporting "type annotation needed" errors.
- // self.register_predicate(Obligation::new(
- // self.interner(),
- // cause,
- // self.param_env,
- // ClauseKind::WellFormed(term),
- // ));
+ self.register_predicate(Obligation::new(
+ self.interner(),
+ cause,
+ self.param_env,
+ ClauseKind::WellFormed(term),
+ ));
}
/// 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));
}
}
@@ -425,7 +429,7 @@ impl<'db> InferenceTable<'db> {
/// Whenever you lower a user-written type, you should call this.
pub(crate) fn process_user_written_ty(&mut self, span: Span, ty: Ty<'db>) -> Ty<'db> {
let ty = self.insert_type_vars(ty, span);
- self.try_structurally_resolve_type(ty)
+ self.try_structurally_resolve_type(span, ty)
}
/// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation,
@@ -435,8 +439,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)
+ self.try_structurally_resolve_type(Span::Dummy, ty)
+ }
+
+ 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)
+ },
+ ));
}
}
@@ -460,7 +473,7 @@ pub(super) mod resolve_completely {
use crate::{
InferenceDiagnostic, Span,
- infer::{TypeMismatch, unify::InferenceTable},
+ infer::unify::InferenceTable,
next_solver::{
Const, ConstKind, DbInterner, DefaultAny, GenericArg, Goal, Predicate, Region, Term,
TermKind, Ty, TyKind,
@@ -510,14 +523,6 @@ pub(super) mod resolve_completely {
}
}
- pub(crate) fn resolve_type_mismatch(&mut self, value_ref: &mut TypeMismatch) {
- // Ignore diagnostics from type mismatches, which are diagnostics themselves.
- // FIXME: We should make type mismatches just regular diagnostics.
- let prev_diagnostics_len = self.diagnostics.len();
- self.resolve_completely(value_ref);
- self.diagnostics.truncate(prev_diagnostics_len);
- }
-
pub(crate) fn resolve_completely<T>(&mut self, value_ref: &mut T)
where
T: TypeFoldable<DbInterner<'db>>,
@@ -543,6 +548,8 @@ pub(super) mod resolve_completely {
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| {
@@ -700,7 +707,8 @@ pub(super) mod resolve_completely {
T: Into<Term<'db>> + TypeSuperFoldable<DbInterner<'db>> + Copy,
{
let value = if self.should_normalize {
- let cause = ObligationCause::new();
+ // 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(
@@ -710,8 +718,8 @@ pub(super) 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
}
}
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 133447dff3..5cb2a3d804 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -50,6 +50,7 @@ pub mod layout;
pub mod method_resolution;
pub mod mir;
pub mod primitive;
+pub mod solver_errors;
pub mod traits;
pub mod upvars;
@@ -63,7 +64,7 @@ use std::{hash::Hash, ops::ControlFlow};
use hir_def::{
CallableDefId, ExpressionStoreOwnerId, GenericDefId, LifetimeParamId, TypeAliasId,
TypeOrConstParamId, TypeParamId,
- hir::{ExprId, ExprOrPatId, PatId},
+ hir::{BindingId, ExprId, ExprOrPatId, PatId},
resolver::TypeNs,
type_ref::{Rawness, TypeRefId},
};
@@ -553,7 +554,7 @@ pub fn callable_sig_from_fn_trait<'db>(
let args = GenericArgs::new_from_slice(&[self_ty.into(), tupled_args.into()]);
let trait_id = trait_.get_id(lang_items)?;
let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args);
- let obligation = Obligation::new(interner, cause.clone(), param_env, trait_ref);
+ let obligation = Obligation::new(interner, cause, param_env, trait_ref);
ocx.register_obligation(obligation);
if !ocx.try_evaluate_obligations().is_empty() {
return None;
@@ -678,11 +679,12 @@ pub fn known_const_to_ast<'db>(
pub enum Span {
ExprId(ExprId),
PatId(PatId),
+ BindingId(BindingId),
TypeRefId(TypeRefId),
/// An unimportant location. Errors on this will be suppressed.
Dummy,
}
-impl_from!(ExprId, PatId, TypeRefId for Span);
+impl_from!(ExprId, PatId, BindingId, TypeRefId for Span);
impl From<ExprOrPatId> for Span {
fn from(value: ExprOrPatId) -> Self {
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 2c6c7ed9a5..86477f2c0b 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -256,7 +256,7 @@ impl<'db> InferenceTable<'db> {
let obligation = Obligation::new(
self.interner(),
- cause.clone(),
+ cause,
self.param_env,
TraitRef::new_from_args(self.interner(), trait_def_id.into(), args),
);
@@ -316,7 +316,7 @@ impl<'db> InferenceTable<'db> {
let bounds = GenericPredicates::query_all(self.db, method_item.into());
let bounds = clauses_as_obligations(
bounds.iter_instantiated(interner, args.as_slice()),
- ObligationCause::new(),
+ cause,
self.param_env,
);
@@ -330,7 +330,7 @@ impl<'db> InferenceTable<'db> {
for ty in fn_sig.inputs_and_output {
obligations.push(Obligation::new(
interner,
- obligation.cause.clone(),
+ obligation.cause,
self.param_env,
Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(ty.into()))),
));
diff --git a/crates/hir-ty/src/method_resolution/confirm.rs b/crates/hir-ty/src/method_resolution/confirm.rs
index a29e3db18d..821d737cf9 100644
--- a/crates/hir-ty/src/method_resolution/confirm.rs
+++ b/crates/hir-ty/src/method_resolution/confirm.rs
@@ -18,7 +18,7 @@ use crate::{
Adjust, Adjustment, AutoBorrow, IncorrectGenericsLenKind, InferenceDiagnostic,
LifetimeElisionKind, PointerCast, Span,
db::HirDatabase,
- infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext, TypeMismatch},
+ infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext},
lower::{
GenericPredicates,
path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings},
@@ -39,7 +39,7 @@ use crate::{
struct ConfirmContext<'a, 'b, 'db> {
ctx: &'a mut InferenceContext<'b, 'db>,
candidate: FunctionId,
- expr: ExprId,
+ call_expr: ExprId,
}
#[derive(Debug)]
@@ -74,9 +74,9 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
fn new(
ctx: &'a mut InferenceContext<'b, 'db>,
candidate: FunctionId,
- expr: ExprId,
+ call_expr: ExprId,
) -> ConfirmContext<'a, 'b, 'db> {
- ConfirmContext { ctx, candidate, expr }
+ ConfirmContext { ctx, candidate, call_expr }
}
#[inline]
@@ -181,7 +181,8 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
) -> (Ty<'db>, Box<[Adjustment]>) {
// Commit the autoderefs by calling `autoderef` again, but this
// time writing the results into the various typeck results.
- let mut autoderef = self.ctx.table.autoderef_with_tracking(unadjusted_self_ty);
+ let mut autoderef =
+ self.ctx.table.autoderef_with_tracking(unadjusted_self_ty, self.call_expr.into());
let Some((mut target, n)) = autoderef.nth(pick.autoderefs) else {
return (Ty::new_error(self.interner(), ErrorGuaranteed), Box::new([]));
};
@@ -191,7 +192,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
self.ctx.table.register_infer_ok(autoderef.adjust_steps_as_infer_ok());
match pick.autoref_or_ptr_adjustment {
Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => {
- let region = self.infcx().next_region_var(self.expr.into());
+ let region = self.infcx().next_region_var(self.call_expr.into());
// Type we're wrapping in a reference, used later for unsizing
let base_ty = target;
@@ -255,7 +256,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
) -> GenericArgs<'db> {
match pick.kind {
probe::InherentImplPick(impl_def_id) => {
- self.infcx().fresh_args_for_item(self.expr.into(), impl_def_id.into())
+ self.infcx().fresh_args_for_item(self.call_expr.into(), impl_def_id.into())
}
probe::ObjectPick(trait_def_id) => {
@@ -297,7 +298,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
// the process we will unify the transformed-self-type
// of the method with the actual type in order to
// unify some of these variables.
- self.infcx().fresh_args_for_item(self.expr.into(), trait_def_id.into())
+ self.infcx().fresh_args_for_item(self.call_expr.into(), trait_def_id.into())
}
probe::WhereClausePick(poly_trait_ref) => {
@@ -317,7 +318,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
// yield an object-type (e.g., `&Object` or `Box<Object>`
// etc).
- let mut autoderef = self.ctx.table.autoderef(self_ty);
+ let mut autoderef = self.ctx.table.autoderef(self_ty, self.call_expr.into());
// We don't need to gate this behind arbitrary self types
// per se, but it does make things a bit more gated.
@@ -466,7 +467,11 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
LifetimeElisionKind::Infer,
false,
None,
- &mut LowererCtx { ctx: self.ctx, expr: self.expr, parent_args: parent_args.as_slice() },
+ &mut LowererCtx {
+ ctx: self.ctx,
+ expr: self.call_expr,
+ parent_args: parent_args.as_slice(),
+ },
)
}
@@ -480,17 +485,14 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
"unify_receivers: self_ty={:?} method_self_ty={:?} pick={:?}",
self_ty, method_self_ty, pick
);
- let cause = ObligationCause::new();
+ let cause = ObligationCause::new(self.call_expr);
match self.ctx.table.at(&cause).sup(method_self_ty, self_ty) {
Ok(infer_ok) => {
self.ctx.table.register_infer_ok(infer_ok);
}
Err(_) => {
if self.ctx.features.arbitrary_self_types {
- self.ctx.result.type_mismatches.get_or_insert_default().insert(
- self.expr.into(),
- TypeMismatch { expected: method_self_ty.store(), actual: self_ty.store() },
- );
+ self.ctx.emit_type_mismatch(self.call_expr.into(), method_self_ty, self_ty);
}
}
}
@@ -513,7 +515,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
let method_predicates = clauses_as_obligations(
GenericPredicates::query_all(self.db(), def_id.into())
.iter_instantiated(self.interner(), all_args),
- ObligationCause::new(),
+ ObligationCause::new(self.call_expr),
self.ctx.table.param_env,
);
@@ -539,13 +541,13 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
// this is a projection from a trait reference, so we have to
// make sure that the trait reference inputs are well-formed.
- self.ctx.table.add_wf_bounds(all_args);
+ self.ctx.table.add_wf_bounds(self.call_expr.into(), all_args);
// the function type must also be well-formed (this is not
// implied by the args being well-formed because of inherent
// impls and late-bound regions - see issue #28609).
for ty in sig.inputs_and_output {
- self.ctx.table.register_wf_obligation(ty.into(), ObligationCause::new());
+ self.ctx.table.register_wf_obligation(ty.into(), ObligationCause::new(self.call_expr));
}
}
@@ -614,7 +616,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
T: TypeFoldable<DbInterner<'db>> + Copy,
{
self.infcx().instantiate_binder_with_fresh_vars(
- self.expr.into(),
+ self.call_expr.into(),
BoundRegionConversionTime::FnCall,
value,
)
diff --git a/crates/hir-ty/src/method_resolution/probe.rs b/crates/hir-ty/src/method_resolution/probe.rs
index 6eeec6cb41..34cd941e91 100644
--- a/crates/hir-ty/src/method_resolution/probe.rs
+++ b/crates/hir-ty/src/method_resolution/probe.rs
@@ -339,7 +339,7 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> {
let ty = self
.infcx
.instantiate_query_response_and_region_obligations(
- &ObligationCause::new(),
+ &ObligationCause::new(self.receiver_span),
self.param_env,
&orig_values,
ty,
@@ -403,7 +403,8 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> {
// converted to, in order to find out which of those methods might actually
// be callable.
let mut autoderef_via_deref =
- Autoderef::new(infcx, self.param_env, self_ty).include_raw_pointers();
+ Autoderef::new(infcx, self.param_env, self_ty, self.receiver_span)
+ .include_raw_pointers();
let mut reached_raw_pointer = false;
let arbitrary_self_types_enabled =
@@ -412,9 +413,10 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> {
let reachable_via_deref =
autoderef_via_deref.by_ref().map(|_| true).chain(std::iter::repeat(false));
- let mut autoderef_via_receiver = Autoderef::new(infcx, self.param_env, self_ty)
- .include_raw_pointers()
- .use_receiver_trait();
+ let mut autoderef_via_receiver =
+ Autoderef::new(infcx, self.param_env, self_ty, self.receiver_span)
+ .include_raw_pointers()
+ .use_receiver_trait();
let steps = autoderef_via_receiver
.by_ref()
.zip(reachable_via_deref)
@@ -1270,7 +1272,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
let InferOk { value: self_ty, obligations: instantiate_self_ty_obligations } = self
.infcx()
.instantiate_query_response_and_region_obligations(
- &ObligationCause::new(),
+ &ObligationCause::new(self.ctx.receiver_span),
self.param_env(),
self.orig_steps_var_values,
&step.self_ty,
@@ -1497,8 +1499,12 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
&self,
trait_ref: TraitRef<'db>,
) -> SelectionResult<'db, Selection<'db>> {
- let obligation =
- Obligation::new(self.interner(), ObligationCause::new(), self.param_env(), trait_ref);
+ let obligation = Obligation::new(
+ self.interner(),
+ ObligationCause::new(self.ctx.call_span),
+ self.param_env(),
+ trait_ref,
+ );
self.infcx().select(&obligation)
}
@@ -1525,7 +1531,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
// up with the `self` parameter of the method.
let _ = self
.infcx()
- .at(&ObligationCause::dummy(), self.param_env())
+ .at(&ObligationCause::new(self.ctx.call_span), self.param_env())
.sup(xform_self_ty, self_ty);
match self.select_trait_candidate(trait_ref) {
Ok(Some(ImplSource::UserDefined(ref impl_data))) => {
@@ -1556,7 +1562,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
) -> ProbeResult {
self.infcx().probe(|_| {
let mut result = ProbeResult::Match;
- let cause = &ObligationCause::new();
+ let cause = &ObligationCause::new(self.ctx.call_span);
let mut ocx = ObligationCtxt::new(self.infcx());
// Subtle: we're not *really* instantiating the current self type while
@@ -1600,7 +1606,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
let impl_bounds = GenericPredicates::query_all(self.db(), impl_def_id.into());
let impl_bounds = clauses_as_obligations(
impl_bounds.iter_instantiated(self.interner(), impl_args.as_slice()),
- ObligationCause::new(),
+ ObligationCause::new(self.ctx.call_span),
self.param_env(),
);
// Convert the bounds into obligations.
@@ -1660,7 +1666,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
}
let obligation = Obligation::new(
self.interner(),
- cause.clone(),
+ *cause,
self.param_env(),
Binder::dummy(trait_ref),
);
@@ -1734,7 +1740,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
if let Some(xform_ret_ty) = xform_ret_ty {
ocx.register_obligation(Obligation::new(
self.interner(),
- cause.clone(),
+ *cause,
self.param_env(),
ClauseKind::WellFormed(xform_ret_ty.into()),
));
@@ -1805,7 +1811,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
// reachable. In this case we don't care about opaque
// types there.
let Ok(ok) = self.infcx().instantiate_query_response_and_region_obligations(
- &ObligationCause::new(),
+ &ObligationCause::new(self.ctx.receiver_span),
self.param_env(),
self.orig_steps_var_values,
&step.self_ty,
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index b2a7eaa674..8f8f557716 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -33,7 +33,7 @@ use crate::{
display::{DisplayTarget, HirDisplay, hir_display_with_store},
generics::generics,
infer::{
- CaptureSourceStack, CapturedPlace, TypeMismatch, UpvarCapture,
+ CaptureSourceStack, CapturedPlace, UpvarCapture,
cast::CastTy,
closure::analysis::expr_use_visitor::{
Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
@@ -110,7 +110,6 @@ pub enum MirLowerError {
UnresolvedField,
UnsizedTemporary(StoredTy),
MissingFunctionDefinition(DefWithBodyId, ExprId),
- TypeMismatch(TypeMismatch),
HasErrors,
/// This should never happen. Type mismatch should catch everything.
TypeError(&'static str),
@@ -197,12 +196,6 @@ impl MirLowerError {
)?;
}
MirLowerError::HasErrors => writeln!(f, "Type inference result contains errors")?,
- MirLowerError::TypeMismatch(e) => writeln!(
- f,
- "Type mismatch: Expected {}, found {}",
- e.expected.as_ref().display(db, display_target),
- e.actual.as_ref().display(db, display_target),
- )?,
MirLowerError::GenericArgNotProvided(id, subst) => {
let param_name = match *id {
GenericParamId::TypeParamId(id) => {
@@ -2402,7 +2395,7 @@ pub fn lower_to_mir_with_store<'db>(
self_param: Option<(BindingId, Ty<'db>)>,
is_root: bool,
) -> Result<'db, MirBody> {
- if infer.type_mismatches().next().is_some() || infer.is_erroneous() {
+ if infer.has_type_mismatches() || infer.is_erroneous() {
return Err(MirLowerError::HasErrors);
}
let mut ctx = MirLowerCtx::new(db, owner, store, infer);
diff --git a/crates/hir-ty/src/next_solver/binder.rs b/crates/hir-ty/src/next_solver/binder.rs
index 3645f8096c..84cfbf2767 100644
--- a/crates/hir-ty/src/next_solver/binder.rs
+++ b/crates/hir-ty/src/next_solver/binder.rs
@@ -1,8 +1,11 @@
+use hir_def::TraitId;
+use macros::{TypeFoldable, TypeVisitable};
+
use crate::{
FnAbi,
next_solver::{
- Binder, Clauses, EarlyBinder, FnSig, PolyFnSig, StoredBoundVarKinds, StoredClauses,
- StoredTy, StoredTys, Ty, abi::Safety,
+ Binder, Clauses, DbInterner, EarlyBinder, FnSig, PolyFnSig, StoredBoundVarKinds,
+ StoredClauses, StoredGenericArgs, StoredTy, StoredTys, TraitRef, Ty, abi::Safety,
},
};
@@ -81,3 +84,22 @@ impl StoredPolyFnSig {
)
}
}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
+pub struct StoredTraitRef {
+ #[type_visitable(ignore)]
+ def_id: TraitId,
+ args: StoredGenericArgs,
+}
+
+impl StoredTraitRef {
+ #[inline]
+ pub fn new(trait_ref: TraitRef<'_>) -> Self {
+ Self { def_id: trait_ref.def_id.0, args: trait_ref.args.store() }
+ }
+
+ #[inline]
+ pub fn get<'db>(&'db self, interner: DbInterner<'db>) -> TraitRef<'db> {
+ TraitRef::new_from_args(interner, self.def_id.into(), self.args.as_ref())
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/consts.rs b/crates/hir-ty/src/next_solver/consts.rs
index fa90e3d8a0..2df9a5259d 100644
--- a/crates/hir-ty/src/next_solver/consts.rs
+++ b/crates/hir-ty/src/next_solver/consts.rs
@@ -11,13 +11,14 @@ use rustc_ast_ir::visit::VisitorResult;
use rustc_type_ir::{
BoundVar, BoundVarIndexKind, ConstVid, DebruijnIndex, FlagComputation, Flags,
GenericTypeVisitable, InferConst, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable,
- TypeVisitable, WithCachedTypeInfo, inherent::IntoKind, relate::Relate,
+ TypeVisitable, TypeVisitableExt, WithCachedTypeInfo, inherent::IntoKind, relate::Relate,
};
use crate::{
ParamEnvAndCrate,
next_solver::{
- AllocationData, impl_foldable_for_interned_slice, impl_stored_interned, interned_slice,
+ AllocationData, ClauseKind, ParamEnv, impl_foldable_for_interned_slice,
+ impl_stored_interned, interned_slice,
},
};
@@ -146,6 +147,40 @@ impl std::fmt::Debug for ParamConst {
}
}
+impl ParamConst {
+ pub fn find_const_ty_from_env<'db>(self, env: ParamEnv<'db>) -> Ty<'db> {
+ let mut candidates = env.clauses.iter().filter_map(|clause| {
+ // `ConstArgHasType` are never desugared to be higher ranked.
+ match clause.kind().skip_binder() {
+ ClauseKind::ConstArgHasType(param_ct, ty) => {
+ assert!(!(param_ct, ty).has_escaping_bound_vars());
+
+ match param_ct.kind() {
+ ConstKind::Param(param_ct) if param_ct.index == self.index => Some(ty),
+ _ => None,
+ }
+ }
+ _ => None,
+ }
+ });
+
+ // N.B. it may be tempting to fix ICEs by making this function return
+ // `Option<Ty<'db>>` instead of `Ty<'db>`; however, this is generally
+ // considered to be a bandaid solution, since it hides more important
+ // underlying issues with how we construct generics and predicates of
+ // items. It's advised to fix the underlying issue rather than trying
+ // to modify this function.
+ let ty = candidates.next().unwrap_or_else(|| {
+ panic!("cannot find `{self:?}` in param-env: {env:#?}");
+ });
+ assert!(
+ candidates.next().is_none(),
+ "did not expect duplicate `ConstParamHasTy` for `{self:?}` in param-env: {env:#?}"
+ );
+ ty
+ }
+}
+
#[derive(
Copy, Clone, Debug, Hash, PartialEq, Eq, TypeVisitable, TypeFoldable, GenericTypeVisitable,
)]
diff --git a/crates/hir-ty/src/next_solver/fulfill.rs b/crates/hir-ty/src/next_solver/fulfill.rs
index ba9cd39d44..1fb4cb2b43 100644
--- a/crates/hir-ty/src/next_solver/fulfill.rs
+++ b/crates/hir-ty/src/next_solver/fulfill.rs
@@ -330,7 +330,7 @@ impl<'db> TypeVisitor<DbInterner<'db>> for StalledOnCoroutines<'_, 'db> {
}
}
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub enum NextSolverError<'db> {
TrueError(PredicateObligation<'db>),
Ambiguity(PredicateObligation<'db>),
diff --git a/crates/hir-ty/src/next_solver/infer/at.rs b/crates/hir-ty/src/next_solver/infer/at.rs
index 4784edf60f..f63200a2e0 100644
--- a/crates/hir-ty/src/next_solver/infer/at.rs
+++ b/crates/hir-ty/src/next_solver/infer/at.rs
@@ -199,7 +199,7 @@ impl<'a, 'db> At<'a, 'db> {
.map(|goal| {
Obligation::new(
self.infcx.interner,
- self.cause.clone(),
+ *self.cause,
goal.param_env,
goal.predicate,
)
@@ -212,7 +212,7 @@ impl<'a, 'db> At<'a, 'db> {
impl<'db> ToTrace<'db> for Ty<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
}
}
@@ -220,14 +220,14 @@ impl<'db> ToTrace<'db> for Ty<'db> {
impl<'db> ToTrace<'db> for Region<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
- TypeTrace { cause: cause.clone(), values: ValuePairs::Regions(ExpectedFound::new(a, b)) }
+ TypeTrace { cause: *cause, values: ValuePairs::Regions(ExpectedFound::new(a, b)) }
}
}
impl<'db> ToTrace<'db> for Const<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
}
}
@@ -236,7 +236,7 @@ impl<'db> ToTrace<'db> for Const<'db> {
impl<'db> ToTrace<'db> for GenericArg<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: match (a.kind(), b.kind()) {
(GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => {
ValuePairs::Regions(ExpectedFound::new(a, b))
@@ -255,20 +255,20 @@ impl<'db> ToTrace<'db> for GenericArg<'db> {
impl<'db> ToTrace<'db> for Term<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
- TypeTrace { cause: cause.clone(), values: ValuePairs::Terms(ExpectedFound::new(a, b)) }
+ TypeTrace { cause: *cause, values: ValuePairs::Terms(ExpectedFound::new(a, b)) }
}
}
impl<'db> ToTrace<'db> for TraitRef<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
- TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) }
+ TypeTrace { cause: *cause, values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) }
}
}
impl<'db> ToTrace<'db> for AliasTy<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::Aliases(ExpectedFound::new(a.into(), b.into())),
}
}
@@ -276,14 +276,14 @@ impl<'db> ToTrace<'db> for AliasTy<'db> {
impl<'db> ToTrace<'db> for AliasTerm<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
- TypeTrace { cause: cause.clone(), values: ValuePairs::Aliases(ExpectedFound::new(a, b)) }
+ TypeTrace { cause: *cause, values: ValuePairs::Aliases(ExpectedFound::new(a, b)) }
}
}
impl<'db> ToTrace<'db> for FnSig<DbInterner<'db>> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::PolySigs(ExpectedFound::new(Binder::dummy(a), Binder::dummy(b))),
}
}
@@ -291,14 +291,14 @@ impl<'db> ToTrace<'db> for FnSig<DbInterner<'db>> {
impl<'db> ToTrace<'db> for PolyFnSig<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
- TypeTrace { cause: cause.clone(), values: ValuePairs::PolySigs(ExpectedFound::new(a, b)) }
+ TypeTrace { cause: *cause, values: ValuePairs::PolySigs(ExpectedFound::new(a, b)) }
}
}
impl<'db> ToTrace<'db> for PolyExistentialTraitRef<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(a, b)),
}
}
@@ -307,7 +307,7 @@ impl<'db> ToTrace<'db> for PolyExistentialTraitRef<'db> {
impl<'db> ToTrace<'db> for PolyExistentialProjection<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::ExistentialProjection(ExpectedFound::new(a, b)),
}
}
diff --git a/crates/hir-ty/src/next_solver/infer/errors.rs b/crates/hir-ty/src/next_solver/infer/errors.rs
new file mode 100644
index 0000000000..d10f70274c
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/errors.rs
@@ -0,0 +1,704 @@
+use std::{fmt, ops::ControlFlow};
+
+use hir_def::{GeneralConstId, attrs::AttrFlags};
+use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt};
+use rustc_type_ir::{
+ AliasRelationDirection, AliasTermKind, PredicatePolarity,
+ error::ExpectedFound,
+ inherent::{IntoKind as _, Ty as _},
+ solve::{CandidateSource, Certainty, GoalSource, MaybeCause, NoSolution},
+};
+use tracing::{instrument, trace};
+
+use crate::{
+ Span,
+ next_solver::{
+ AliasTerm, AnyImplId, Binder, ClauseKind, Const, ConstKind, DbInterner, EarlyBinder,
+ ErrorGuaranteed, HostEffectPredicate, PolyTraitPredicate, PredicateKind, SolverContext,
+ Term, TraitPredicate, Ty, TyKind, TypeError,
+ fulfill::NextSolverError,
+ infer::{
+ InferCtxt,
+ select::SelectionError,
+ traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations},
+ },
+ inspect::{self, ProofTreeVisitor},
+ normalize::deeply_normalize_for_diagnostics,
+ },
+};
+
+#[derive(Debug)]
+pub struct FulfillmentError<'db> {
+ pub obligation: PredicateObligation<'db>,
+ pub code: FulfillmentErrorCode<'db>,
+ /// Diagnostics only: the 'root' obligation which resulted in
+ /// the failure to process `obligation`. This is the obligation
+ /// that was initially passed to `register_predicate_obligation`
+ pub root_obligation: PredicateObligation<'db>,
+}
+
+impl<'db> FulfillmentError<'db> {
+ pub fn new(
+ obligation: PredicateObligation<'db>,
+ code: FulfillmentErrorCode<'db>,
+ root_obligation: PredicateObligation<'db>,
+ ) -> FulfillmentError<'db> {
+ FulfillmentError { obligation, code, root_obligation }
+ }
+
+ pub fn is_true_error(&self) -> bool {
+ match self.code {
+ FulfillmentErrorCode::Select(_)
+ | FulfillmentErrorCode::Project(_)
+ | FulfillmentErrorCode::Subtype(_, _)
+ | FulfillmentErrorCode::ConstEquate(_, _) => true,
+ FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => {
+ false
+ }
+ }
+ }
+}
+
+#[derive(Clone)]
+pub enum FulfillmentErrorCode<'db> {
+ /// Inherently impossible to fulfill; this trait is implemented if and only
+ /// if it is already implemented.
+ Cycle(PredicateObligations<'db>),
+ Select(SelectionError<'db>),
+ Project(MismatchedProjectionTypes<'db>),
+ Subtype(ExpectedFound<Ty<'db>>, TypeError<'db>), // always comes from a SubtypePredicate
+ ConstEquate(ExpectedFound<Const<'db>>, TypeError<'db>),
+ Ambiguity {
+ /// Overflow is only `Some(suggest_recursion_limit)` when using the next generation
+ /// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by
+ /// emitting a fatal error instead.
+ overflow: Option<bool>,
+ },
+}
+
+impl<'db> fmt::Debug for FulfillmentErrorCode<'db> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ FulfillmentErrorCode::Select(ref e) => write!(f, "{e:?}"),
+ FulfillmentErrorCode::Project(ref e) => write!(f, "{e:?}"),
+ FulfillmentErrorCode::Subtype(ref a, ref b) => {
+ write!(f, "CodeSubtypeError({a:?}, {b:?})")
+ }
+ FulfillmentErrorCode::ConstEquate(ref a, ref b) => {
+ write!(f, "CodeConstEquateError({a:?}, {b:?})")
+ }
+ FulfillmentErrorCode::Ambiguity { overflow: None } => write!(f, "Ambiguity"),
+ FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => {
+ write!(f, "Overflow({suggest_increasing_limit})")
+ }
+ FulfillmentErrorCode::Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"),
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct MismatchedProjectionTypes<'db> {
+ pub err: TypeError<'db>,
+}
+
+impl<'db> fmt::Debug for MismatchedProjectionTypes<'db> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "MismatchedProjectionTypes({:?})", self.err)
+ }
+}
+
+impl<'db> NextSolverError<'db> {
+ pub fn into_fulfillment_error(self, infcx: &InferCtxt<'db>) -> FulfillmentError<'db> {
+ match self {
+ NextSolverError::TrueError(obligation) => {
+ fulfillment_error_for_no_solution(infcx, obligation)
+ }
+ NextSolverError::Ambiguity(obligation) => {
+ fulfillment_error_for_stalled(infcx, obligation)
+ }
+ NextSolverError::Overflow(obligation) => {
+ fulfillment_error_for_overflow(infcx, obligation)
+ }
+ }
+ }
+}
+
+fn fulfillment_error_for_no_solution<'db>(
+ infcx: &InferCtxt<'db>,
+ root_obligation: PredicateObligation<'db>,
+) -> FulfillmentError<'db> {
+ let interner = infcx.interner;
+ let db = interner.db;
+ let obligation = find_best_leaf_obligation(infcx, &root_obligation, false);
+
+ let code = match obligation.predicate.kind().skip_binder() {
+ PredicateKind::Clause(ClauseKind::Projection(_)) => {
+ FulfillmentErrorCode::Project(
+ // FIXME: This could be a `Sorts` if the term is a type
+ MismatchedProjectionTypes { err: TypeError::Mismatch },
+ )
+ }
+ PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, expected_ty)) => {
+ let ct_ty = match ct.kind() {
+ ConstKind::Unevaluated(uv) => {
+ let ct_ty = match uv.def.0 {
+ GeneralConstId::ConstId(konst) => db.value_ty(konst.into()).unwrap(),
+ GeneralConstId::StaticId(statik) => db.value_ty(statik.into()).unwrap(),
+ // FIXME: Return the type of the const here.
+ GeneralConstId::AnonConstId(_) => {
+ EarlyBinder::bind(Ty::new_error(interner, ErrorGuaranteed))
+ }
+ };
+ ct_ty.instantiate(interner, uv.args)
+ }
+ ConstKind::Param(param_ct) => param_ct.find_const_ty_from_env(obligation.param_env),
+ ConstKind::Value(cv) => cv.ty,
+ kind => panic!(
+ "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
+ ),
+ };
+ FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType {
+ ct,
+ ct_ty,
+ expected_ty,
+ })
+ }
+ PredicateKind::NormalizesTo(..) => {
+ FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
+ }
+ PredicateKind::AliasRelate(_, _, _) => {
+ FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
+ }
+ PredicateKind::Subtype(pred) => {
+ let (a, b) = infcx.enter_forall_and_leak_universe(
+ obligation.predicate.kind().rebind((pred.a, pred.b)),
+ );
+ let expected_found = ExpectedFound::new(a, b);
+ FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
+ }
+ PredicateKind::Coerce(pred) => {
+ let (a, b) = infcx.enter_forall_and_leak_universe(
+ obligation.predicate.kind().rebind((pred.a, pred.b)),
+ );
+ let expected_found = ExpectedFound::new(b, a);
+ FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
+ }
+ PredicateKind::Clause(_) | PredicateKind::DynCompatible(_) | PredicateKind::Ambiguous => {
+ FulfillmentErrorCode::Select(SelectionError::Unimplemented)
+ }
+ PredicateKind::ConstEquate(..) => {
+ panic!("unexpected goal: {obligation:?}")
+ }
+ };
+
+ FulfillmentError { obligation, code, root_obligation }
+}
+
+fn fulfillment_error_for_stalled<'db>(
+ infcx: &InferCtxt<'db>,
+ root_obligation: PredicateObligation<'db>,
+) -> FulfillmentError<'db> {
+ let (code, refine_obligation) = infcx.probe(|_| {
+ match <&SolverContext<'db>>::from(infcx).evaluate_root_goal(
+ root_obligation.as_goal(),
+ root_obligation.cause.span(),
+ None,
+ ) {
+ Ok(GoalEvaluation {
+ certainty: Certainty::Maybe { cause: MaybeCause::Ambiguity, .. },
+ ..
+ }) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true),
+ Ok(GoalEvaluation {
+ certainty:
+ Certainty::Maybe {
+ cause:
+ MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ },
+ ..
+ },
+ ..
+ }) => (
+ FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
+ // Don't look into overflows because we treat overflows weirdly anyways.
+ // We discard the inference constraints from overflowing goals, so
+ // recomputing the goal again during `find_best_leaf_obligation` may apply
+ // inference guidance that makes other goals go from ambig -> pass, for example.
+ //
+ // FIXME: We should probably just look into overflows here.
+ false,
+ ),
+ Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => {
+ panic!(
+ "did not expect successful goal when collecting ambiguity errors for `{:?}`",
+ infcx.resolve_vars_if_possible(root_obligation.predicate),
+ )
+ }
+ Err(_) => {
+ panic!(
+ "did not expect selection error when collecting ambiguity errors for `{:?}`",
+ infcx.resolve_vars_if_possible(root_obligation.predicate),
+ )
+ }
+ }
+ });
+
+ FulfillmentError {
+ obligation: if refine_obligation {
+ find_best_leaf_obligation(infcx, &root_obligation, true)
+ } else {
+ root_obligation.clone()
+ },
+ code,
+ root_obligation,
+ }
+}
+
+fn fulfillment_error_for_overflow<'db>(
+ infcx: &InferCtxt<'db>,
+ root_obligation: PredicateObligation<'db>,
+) -> FulfillmentError<'db> {
+ FulfillmentError {
+ obligation: find_best_leaf_obligation(infcx, &root_obligation, true),
+ code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
+ root_obligation,
+ }
+}
+
+#[instrument(level = "debug", skip(infcx), ret)]
+fn find_best_leaf_obligation<'db>(
+ infcx: &InferCtxt<'db>,
+ obligation: &PredicateObligation<'db>,
+ consider_ambiguities: bool,
+) -> PredicateObligation<'db> {
+ let obligation = infcx.resolve_vars_if_possible(obligation.clone());
+ // FIXME: we use a probe here as the `BestObligation` visitor does not
+ // check whether it uses candidates which get shadowed by where-bounds.
+ //
+ // We should probably fix the visitor to not do so instead, as this also
+ // means the leaf obligation may be incorrect.
+ let obligation = infcx
+ .fudge_inference_if_ok(|| {
+ infcx
+ .visit_proof_tree(
+ obligation.as_goal(),
+ &mut BestObligation { obligation: obligation.clone(), consider_ambiguities },
+ )
+ .break_value()
+ .ok_or(())
+ // walk around the fact that the cause in `Obligation` is ignored by folders so that
+ // we can properly fudge the infer vars in cause code.
+ .map(|o| (o.cause, o))
+ })
+ .map(|(cause, o)| PredicateObligation { cause, ..o })
+ .unwrap_or(obligation);
+ deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation)
+}
+
+struct BestObligation<'db> {
+ obligation: PredicateObligation<'db>,
+ consider_ambiguities: bool,
+}
+
+impl<'db> BestObligation<'db> {
+ fn with_derived_obligation(
+ &mut self,
+ derived_obligation: PredicateObligation<'db>,
+ and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'db>>::Result,
+ ) -> <Self as ProofTreeVisitor<'db>>::Result {
+ let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation);
+ let res = and_then(self);
+ self.obligation = old_obligation;
+ res
+ }
+
+ /// Filter out the candidates that aren't interesting to visit for the
+ /// purposes of reporting errors. For ambiguities, we only consider
+ /// candidates that may hold. For errors, we only consider candidates that
+ /// *don't* hold and which have impl-where clauses that also don't hold.
+ fn non_trivial_candidates<'a>(
+ &self,
+ goal: &'a inspect::InspectGoal<'a, 'db>,
+ ) -> Vec<inspect::InspectCandidate<'a, 'db>> {
+ let mut candidates = goal.candidates();
+ match self.consider_ambiguities {
+ true => {
+ // If we have an ambiguous obligation, we must consider *all* candidates
+ // that hold, or else we may guide inference causing other goals to go
+ // from ambig -> pass/fail.
+ candidates.retain(|candidate| candidate.result().is_ok());
+ }
+ false => {
+ // We always handle rigid alias candidates separately as we may not add them for
+ // aliases whose trait bound doesn't hold.
+ candidates.retain(|c| !matches!(c.kind(), inspect::ProbeKind::RigidAlias { .. }));
+ // If we have >1 candidate, one may still be due to "boring" reasons, like
+ // an alias-relate that failed to hold when deeply evaluated. We really
+ // don't care about reasons like this.
+ if candidates.len() > 1 {
+ candidates.retain(|candidate| {
+ goal.infcx().probe(|_| {
+ candidate.instantiate_nested_goals(self.span()).iter().any(
+ |nested_goal| {
+ matches!(
+ nested_goal.source(),
+ GoalSource::ImplWhereBound
+ | GoalSource::AliasBoundConstCondition
+ | GoalSource::AliasWellFormed
+ ) && nested_goal.result().is_err()
+ },
+ )
+ })
+ });
+ }
+ }
+ }
+
+ candidates
+ }
+
+ /// HACK: We walk the nested obligations for a well-formed arg manually,
+ /// since there's nontrivial logic in `wf.rs` to set up an obligation cause.
+ /// Ideally we'd be able to track this better.
+ fn visit_well_formed_goal(
+ &mut self,
+ candidate: &inspect::InspectCandidate<'_, 'db>,
+ term: Term<'db>,
+ ) -> ControlFlow<PredicateObligation<'db>> {
+ let _ = (candidate, term);
+ // FIXME: rustc does this, but we don't process WF obligations yet:
+ // let infcx = candidate.goal().infcx();
+ // let param_env = candidate.goal().goal().param_env;
+ // let body_id = self.obligation.cause.body_id;
+
+ // for obligation in wf::unnormalized_obligations(infcx, param_env, term, self.span(), body_id)
+ // .into_iter()
+ // .flatten()
+ // {
+ // let nested_goal = candidate.instantiate_proof_tree_for_nested_goal(
+ // GoalSource::Misc,
+ // obligation.as_goal(),
+ // self.span(),
+ // );
+ // // Skip nested goals that aren't the *reason* for our goal's failure.
+ // match (self.consider_ambiguities, nested_goal.result()) {
+ // (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }))
+ // | (false, Err(_)) => {}
+ // _ => continue,
+ // }
+
+ // self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
+ // }
+
+ ControlFlow::Break(self.obligation.clone())
+ }
+
+ /// If a normalization of an associated item or a trait goal fails without trying any
+ /// candidates it's likely that normalizing its self type failed. We manually detect
+ /// such cases here.
+ fn detect_error_in_self_ty_normalization(
+ &mut self,
+ goal: &inspect::InspectGoal<'_, 'db>,
+ self_ty: Ty<'db>,
+ ) -> ControlFlow<PredicateObligation<'db>> {
+ assert!(!self.consider_ambiguities);
+ let interner = goal.infcx().interner;
+ if let TyKind::Alias(..) = self_ty.kind() {
+ let infer_term = goal.infcx().next_ty_var(self.obligation.cause.span());
+ let pred = PredicateKind::AliasRelate(
+ self_ty.into(),
+ infer_term.into(),
+ AliasRelationDirection::Equate,
+ );
+ let obligation =
+ Obligation::new(interner, self.obligation.cause, goal.goal().param_env, pred);
+ self.with_derived_obligation(obligation, |this| {
+ goal.infcx().visit_proof_tree_at_depth(
+ goal.goal().with(interner, pred),
+ goal.depth() + 1,
+ this,
+ )
+ })
+ } else {
+ ControlFlow::Continue(())
+ }
+ }
+
+ /// When a higher-ranked projection goal fails, check that the corresponding
+ /// higher-ranked trait goal holds or not. This is because the process of
+ /// instantiating and then re-canonicalizing the binder of the projection goal
+ /// forces us to be unable to see that the leak check failed in the nested
+ /// `NormalizesTo` goal, so we don't fall back to the rigid projection check
+ /// that should catch when a projection goal fails due to an unsatisfied trait
+ /// goal.
+ fn detect_trait_error_in_higher_ranked_projection(
+ &mut self,
+ goal: &inspect::InspectGoal<'_, 'db>,
+ ) -> ControlFlow<PredicateObligation<'db>> {
+ let interner = goal.infcx().interner;
+ if let Some(projection_clause) = goal.goal().predicate.as_projection_clause()
+ && !projection_clause.bound_vars().is_empty()
+ {
+ let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(interner));
+ let obligation = Obligation::new(
+ interner,
+ self.obligation.cause,
+ goal.goal().param_env,
+ deeply_normalize_for_diagnostics(goal.infcx(), goal.goal().param_env, pred),
+ );
+ self.with_derived_obligation(obligation, |this| {
+ goal.infcx().visit_proof_tree_at_depth(
+ goal.goal().with(interner, pred),
+ goal.depth() + 1,
+ this,
+ )
+ })
+ } else {
+ ControlFlow::Continue(())
+ }
+ }
+
+ /// It is likely that `NormalizesTo` failed without any applicable candidates
+ /// because the alias is not well-formed.
+ ///
+ /// As we only enter `RigidAlias` candidates if the trait bound of the associated type
+ /// holds, we discard these candidates in `non_trivial_candidates` and always manually
+ /// check this here.
+ fn detect_non_well_formed_assoc_item(
+ &mut self,
+ goal: &inspect::InspectGoal<'_, 'db>,
+ alias: AliasTerm<'db>,
+ ) -> ControlFlow<PredicateObligation<'db>> {
+ let interner = goal.infcx().interner;
+ let obligation = Obligation::new(
+ interner,
+ self.obligation.cause,
+ goal.goal().param_env,
+ alias.trait_ref(interner),
+ );
+ self.with_derived_obligation(obligation, |this| {
+ goal.infcx().visit_proof_tree_at_depth(
+ goal.goal().with(interner, alias.trait_ref(interner)),
+ goal.depth() + 1,
+ this,
+ )
+ })
+ }
+
+ /// If we have no candidates, then it's likely that there is a
+ /// non-well-formed alias in the goal.
+ fn detect_error_from_empty_candidates(
+ &mut self,
+ goal: &inspect::InspectGoal<'_, 'db>,
+ ) -> ControlFlow<PredicateObligation<'db>> {
+ let interner = goal.infcx().interner;
+ let pred_kind = goal.goal().predicate.kind();
+
+ match pred_kind.no_bound_vars() {
+ Some(PredicateKind::Clause(ClauseKind::Trait(pred))) => {
+ self.detect_error_in_self_ty_normalization(goal, pred.self_ty())?;
+ }
+ Some(PredicateKind::NormalizesTo(pred))
+ if let AliasTermKind::ProjectionTy | AliasTermKind::ProjectionConst =
+ pred.alias.kind(interner) =>
+ {
+ self.detect_error_in_self_ty_normalization(goal, pred.alias.self_ty())?;
+ self.detect_non_well_formed_assoc_item(goal, pred.alias)?;
+ }
+ Some(_) | None => {}
+ }
+
+ ControlFlow::Break(self.obligation.clone())
+ }
+}
+
+impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> {
+ type Result = ControlFlow<PredicateObligation<'db>>;
+
+ fn span(&self) -> Span {
+ self.obligation.cause.span()
+ }
+
+ #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
+ fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'db>) -> Self::Result {
+ let interner = goal.infcx().interner;
+ // Skip goals that aren't the *reason* for our goal's failure.
+ match (self.consider_ambiguities, goal.result()) {
+ (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) | (false, Err(_)) => {
+ }
+ _ => return ControlFlow::Continue(()),
+ }
+
+ let pred = goal.goal().predicate;
+
+ let candidates = self.non_trivial_candidates(goal);
+ let candidate = match candidates.as_slice() {
+ [candidate] => candidate,
+ [] => return self.detect_error_from_empty_candidates(goal),
+ _ => return ControlFlow::Break(self.obligation.clone()),
+ };
+
+ // Don't walk into impls that have `do_not_recommend`.
+ if let inspect::ProbeKind::TraitCandidate {
+ source: CandidateSource::Impl(impl_def_id),
+ result: _,
+ } = candidate.kind()
+ && let AnyImplId::ImplId(impl_def_id) = impl_def_id
+ && AttrFlags::query(interner.db, impl_def_id.into())
+ .contains(AttrFlags::DIAGNOSTIC_DO_NOT_RECOMMEND)
+ {
+ trace!("#[diagnostic::do_not_recommend] -> exit");
+ return ControlFlow::Break(self.obligation.clone());
+ }
+
+ // FIXME: Also, what about considering >1 layer up the stack? May be necessary
+ // for normalizes-to.
+ let child_mode = match pred.kind().skip_binder() {
+ PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => {
+ ChildMode::Trait(pred.kind().rebind(trait_pred))
+ }
+ PredicateKind::Clause(ClauseKind::HostEffect(host_pred)) => {
+ ChildMode::Host(pred.kind().rebind(host_pred))
+ }
+ PredicateKind::NormalizesTo(normalizes_to)
+ if matches!(
+ normalizes_to.alias.kind(interner),
+ AliasTermKind::ProjectionTy | AliasTermKind::ProjectionConst
+ ) =>
+ {
+ ChildMode::Trait(pred.kind().rebind(TraitPredicate {
+ trait_ref: normalizes_to.alias.trait_ref(interner),
+ polarity: PredicatePolarity::Positive,
+ }))
+ }
+ PredicateKind::Clause(ClauseKind::WellFormed(term)) => {
+ return self.visit_well_formed_goal(candidate, term);
+ }
+ _ => ChildMode::PassThrough,
+ };
+
+ let nested_goals = candidate.instantiate_nested_goals(self.span());
+
+ // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as
+ // an actual candidate, instead we should treat them as if the impl was never considered to
+ // have potentially applied. As if `impl<A, R> Trait for for<..> fn(..A) -> R` was written
+ // instead of `impl<T: FnPtr> Trait for T`.
+ //
+ // We do this as a separate loop so that we do not choose to tell the user about some nested
+ // goal before we encounter a `T: FnPtr` nested goal.
+ for nested_goal in &nested_goals {
+ if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
+ && Some(poly_trait_pred.def_id().0) == interner.lang_items().FnPtrTrait
+ && let Err(NoSolution) = nested_goal.result()
+ {
+ return ControlFlow::Break(self.obligation.clone());
+ }
+ }
+
+ let mut impl_where_bound_count = 0;
+ for nested_goal in nested_goals {
+ trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
+
+ let nested_pred = nested_goal.goal().predicate;
+
+ let make_obligation = |cause| Obligation {
+ cause,
+ param_env: nested_goal.goal().param_env,
+ predicate: nested_pred,
+ recursion_depth: self.obligation.recursion_depth + 1,
+ };
+
+ let obligation;
+ match (child_mode, nested_goal.source()) {
+ (
+ ChildMode::Trait(_) | ChildMode::Host(_),
+ GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_),
+ ) => {
+ continue;
+ }
+ (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
+ obligation = make_obligation(derive_cause(
+ interner,
+ candidate.kind(),
+ self.obligation.cause,
+ impl_where_bound_count,
+ parent_trait_pred,
+ ));
+ impl_where_bound_count += 1;
+ }
+ (
+ ChildMode::Host(parent_host_pred),
+ GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition,
+ ) => {
+ obligation = make_obligation(derive_host_cause(
+ interner,
+ candidate.kind(),
+ self.obligation.cause,
+ impl_where_bound_count,
+ parent_host_pred,
+ ));
+ impl_where_bound_count += 1;
+ }
+ (ChildMode::PassThrough, _)
+ | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
+ obligation = make_obligation(self.obligation.cause);
+ }
+ }
+
+ self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
+ }
+
+ // alias-relate may fail because the lhs or rhs can't be normalized,
+ // and therefore is treated as rigid.
+ if let Some(PredicateKind::AliasRelate(lhs, rhs, _)) = pred.kind().no_bound_vars() {
+ goal.infcx().visit_proof_tree_at_depth(
+ goal.goal().with(interner, ClauseKind::WellFormed(lhs)),
+ goal.depth() + 1,
+ self,
+ )?;
+ goal.infcx().visit_proof_tree_at_depth(
+ goal.goal().with(interner, ClauseKind::WellFormed(rhs)),
+ goal.depth() + 1,
+ self,
+ )?;
+ }
+
+ self.detect_trait_error_in_higher_ranked_projection(goal)?;
+
+ ControlFlow::Break(self.obligation.clone())
+ }
+}
+
+#[derive(Debug, Copy, Clone)]
+enum ChildMode<'db> {
+ // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
+ // and skip all `GoalSource::Misc`, which represent useless obligations
+ // such as alias-eq which may not hold.
+ Trait(PolyTraitPredicate<'db>),
+ // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
+ // and skip all `GoalSource::Misc`, which represent useless obligations
+ // such as alias-eq which may not hold.
+ Host(Binder<'db, HostEffectPredicate<'db>>),
+ // Skip trying to derive an `ObligationCause` from this obligation, and
+ // report *all* sub-obligations as if they came directly from the parent
+ // obligation.
+ PassThrough,
+}
+
+fn derive_cause<'db>(
+ _interner: DbInterner<'db>,
+ _candidate_kind: inspect::ProbeKind<DbInterner<'db>>,
+ cause: ObligationCause,
+ _idx: usize,
+ _parent_trait_pred: PolyTraitPredicate<'db>,
+) -> ObligationCause {
+ cause
+}
+
+fn derive_host_cause<'db>(
+ _interner: DbInterner<'db>,
+ _candidate_kind: inspect::ProbeKind<DbInterner<'db>>,
+ cause: ObligationCause,
+ _idx: usize,
+ _parent_host_pred: Binder<'db, HostEffectPredicate<'db>>,
+) -> ObligationCause {
+ cause
+}
diff --git a/crates/hir-ty/src/next_solver/infer/mod.rs b/crates/hir-ty/src/next_solver/infer/mod.rs
index f038c47a8b..0bb980c906 100644
--- a/crates/hir-ty/src/next_solver/infer/mod.rs
+++ b/crates/hir-ty/src/next_solver/infer/mod.rs
@@ -53,6 +53,7 @@ use super::{
pub mod at;
pub mod canonical;
mod context;
+pub mod errors;
pub mod opaque_types;
mod outlives;
pub mod region_constraints;
@@ -1352,7 +1353,7 @@ impl TyOrConstInferVar {
impl<'db> TypeTrace<'db> {
pub fn types(cause: &ObligationCause, a: Ty<'db>, b: Ty<'db>) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
}
}
@@ -1362,12 +1363,12 @@ impl<'db> TypeTrace<'db> {
a: TraitRef<'db>,
b: TraitRef<'db>,
) -> TypeTrace<'db> {
- TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) }
+ TypeTrace { cause: *cause, values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) }
}
pub fn consts(cause: &ObligationCause, a: Const<'db>, b: Const<'db>) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
}
}
diff --git a/crates/hir-ty/src/next_solver/infer/relate/lattice.rs b/crates/hir-ty/src/next_solver/infer/relate/lattice.rs
index 0443dbd814..f3af697feb 100644
--- a/crates/hir-ty/src/next_solver/infer/relate/lattice.rs
+++ b/crates/hir-ty/src/next_solver/infer/relate/lattice.rs
@@ -258,18 +258,13 @@ impl<'db> PredicateEmittingRelation<InferCtxt<'db>> for LatticeOp<'_, 'db> {
preds: impl IntoIterator<Item: Upcast<DbInterner<'db>, Predicate<'db>>>,
) {
self.obligations.extend(preds.into_iter().map(|pred| {
- Obligation::new(self.infcx.interner, self.trace.cause.clone(), self.param_env, pred)
+ Obligation::new(self.infcx.interner, self.trace.cause, self.param_env, pred)
}))
}
fn register_goals(&mut self, goals: impl IntoIterator<Item = Goal<'db, Predicate<'db>>>) {
self.obligations.extend(goals.into_iter().map(|goal| {
- Obligation::new(
- self.infcx.interner,
- self.trace.cause.clone(),
- goal.param_env,
- goal.predicate,
- )
+ Obligation::new(self.infcx.interner, self.trace.cause, goal.param_env, goal.predicate)
}))
}
diff --git a/crates/hir-ty/src/next_solver/infer/traits.rs b/crates/hir-ty/src/next_solver/infer/traits.rs
index 1edf256d01..4584b35796 100644
--- a/crates/hir-ty/src/next_solver/infer/traits.rs
+++ b/crates/hir-ty/src/next_solver/infer/traits.rs
@@ -27,37 +27,21 @@ use crate::{
use super::InferCtxt;
/// The reason why we incurred this obligation; used for error reporting.
-///
-/// Non-misc `ObligationCauseCode`s are stored on the heap. This gives the
-/// best trade-off between keeping the type small (which makes copies cheaper)
-/// while not doing too many heap allocations.
-///
-/// We do not want to intern this as there are a lot of obligation causes which
-/// only live for a short period of time.
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeVisitable, TypeFoldable)]
pub struct ObligationCause {
+ #[type_visitable(ignore)]
span: Span,
}
impl ObligationCause {
#[inline]
- pub fn new() -> ObligationCause {
- ObligationCause { span: Span::Dummy }
- }
-
- #[inline]
- pub fn with_span(span: Span) -> ObligationCause {
- ObligationCause { span }
+ pub fn new<S: Into<Span>>(span: S) -> ObligationCause {
+ ObligationCause { span: span.into() }
}
#[inline]
pub fn dummy() -> ObligationCause {
- ObligationCause::new()
- }
-
- #[inline]
- pub fn misc() -> ObligationCause {
- ObligationCause::new()
+ ObligationCause::new(Span::Dummy)
}
#[inline]
@@ -66,13 +50,6 @@ impl ObligationCause {
}
}
-impl Default for ObligationCause {
- #[inline]
- fn default() -> Self {
- Self::new()
- }
-}
-
/// An `Obligation` represents some trait reference (e.g., `i32: Eq`) for
/// which the "impl_source" must be found. The process of finding an "impl_source" is
/// called "resolving" the `Obligation`. This process consists of
@@ -118,7 +95,7 @@ impl<'db> Elaboratable<DbInterner<'db>> for PredicateObligation<'db> {
fn child(&self, clause: Clause<'db>) -> Self {
Obligation {
- cause: self.cause.clone(),
+ cause: self.cause,
param_env: self.param_env,
recursion_depth: 0,
predicate: clause.as_predicate(),
@@ -128,11 +105,11 @@ impl<'db> Elaboratable<DbInterner<'db>> for PredicateObligation<'db> {
fn child_with_derived_cause(
&self,
clause: Clause<'db>,
- _span: Span,
+ span: Span,
_parent_trait_pred: PolyTraitPredicate<'db>,
_index: usize,
) -> Self {
- let cause = ObligationCause::new();
+ let cause = ObligationCause::new(span);
Obligation {
cause,
param_env: self.param_env,
@@ -186,7 +163,7 @@ impl<'db> PredicateObligation<'db> {
/// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`.
pub fn flip_polarity(&self, _interner: DbInterner<'db>) -> Option<PredicateObligation<'db>> {
Some(PredicateObligation {
- cause: self.cause.clone(),
+ cause: self.cause,
param_env: self.param_env,
predicate: self.predicate.flip_polarity()?,
recursion_depth: self.recursion_depth,
@@ -228,7 +205,7 @@ impl<'db, O> Obligation<'db, O> {
tcx: DbInterner<'db>,
value: impl Upcast<DbInterner<'db>, P>,
) -> Obligation<'db, P> {
- Obligation::with_depth(tcx, self.cause.clone(), self.recursion_depth, self.param_env, value)
+ Obligation::with_depth(tcx, self.cause, self.recursion_depth, self.param_env, value)
}
}
diff --git a/crates/hir-ty/src/next_solver/inspect.rs b/crates/hir-ty/src/next_solver/inspect.rs
index 566f72fbd8..fdb1fa3d05 100644
--- a/crates/hir-ty/src/next_solver/inspect.rs
+++ b/crates/hir-ty/src/next_solver/inspect.rs
@@ -24,6 +24,8 @@ use crate::{
},
};
+pub(crate) use rustc_next_trait_solver::solve::inspect::*;
+
pub(crate) struct InspectConfig {
pub(crate) max_depth: usize,
}
@@ -319,6 +321,10 @@ impl<'a, 'db> InspectGoal<'a, 'db> {
self.result
}
+ pub(crate) fn source(&self) -> GoalSource {
+ self.source
+ }
+
pub(crate) fn depth(&self) -> usize {
self.depth
}
diff --git a/crates/hir-ty/src/next_solver/normalize.rs b/crates/hir-ty/src/next_solver/normalize.rs
index aa6f27c4c2..152b58baeb 100644
--- a/crates/hir-ty/src/next_solver/normalize.rs
+++ b/crates/hir-ty/src/next_solver/normalize.rs
@@ -102,7 +102,7 @@ impl<'db> NormalizationFolder<'_, 'db> {
let infer_term = infcx.next_term_var_of_kind(alias_term, self.at.cause.span());
let obligation = Obligation::new(
interner,
- self.at.cause.clone(),
+ *self.at.cause,
self.at.param_env,
PredicateKind::AliasRelate(alias_term, infer_term, AliasRelationDirection::Equate),
);
@@ -229,7 +229,6 @@ impl<'db> FallibleTypeFolder<DbInterner<'db>> for NormalizationFolder<'_, 'db> {
}
// Deeply normalize a value and return it
-#[expect(dead_code, reason = "rustc has this")]
pub(crate) fn deeply_normalize_for_diagnostics<'db, T: TypeFoldable<DbInterner<'db>>>(
infcx: &InferCtxt<'db>,
param_env: ParamEnv<'db>,
diff --git a/crates/hir-ty/src/next_solver/predicate.rs b/crates/hir-ty/src/next_solver/predicate.rs
index 2abd9bdd94..e16428cd2e 100644
--- a/crates/hir-ty/src/next_solver/predicate.rs
+++ b/crates/hir-ty/src/next_solver/predicate.rs
@@ -31,6 +31,7 @@ pub type ExistentialPredicate<'db> = ty::ExistentialPredicate<DbInterner<'db>>;
pub type ExistentialTraitRef<'db> = ty::ExistentialTraitRef<DbInterner<'db>>;
pub type ExistentialProjection<'db> = ty::ExistentialProjection<DbInterner<'db>>;
pub type TraitPredicate<'db> = ty::TraitPredicate<DbInterner<'db>>;
+pub type HostEffectPredicate<'db> = ty::HostEffectPredicate<DbInterner<'db>>;
pub type ClauseKind<'db> = ty::ClauseKind<DbInterner<'db>>;
pub type PredicateKind<'db> = ty::PredicateKind<DbInterner<'db>>;
pub type NormalizesTo<'db> = ty::NormalizesTo<DbInterner<'db>>;
diff --git a/crates/hir-ty/src/next_solver/structural_normalize.rs b/crates/hir-ty/src/next_solver/structural_normalize.rs
index 7a70bae97c..769d2ae141 100644
--- a/crates/hir-ty/src/next_solver/structural_normalize.rs
+++ b/crates/hir-ty/src/next_solver/structural_normalize.rs
@@ -41,7 +41,7 @@ impl<'db> At<'_, 'db> {
// (or a not-yet-defined opaque in scope).
let obligation = Obligation::new(
self.infcx.interner,
- self.cause.clone(),
+ *self.cause,
self.param_env,
PredicateKind::AliasRelate(term, new_infer, AliasRelationDirection::Equate),
);
diff --git a/crates/hir-ty/src/next_solver/util.rs b/crates/hir-ty/src/next_solver/util.rs
index 3c5126cc4a..43b4503204 100644
--- a/crates/hir-ty/src/next_solver/util.rs
+++ b/crates/hir-ty/src/next_solver/util.rs
@@ -717,7 +717,7 @@ pub(crate) fn clauses_as_obligations<'db>(
param_env: ParamEnv<'db>,
) -> impl Iterator<Item = PredicateObligation<'db>> {
clauses.into_iter().map(move |clause| Obligation {
- cause: cause.clone(),
+ cause,
param_env,
predicate: clause.as_predicate(),
recursion_depth: 0,
diff --git a/crates/hir-ty/src/solver_errors.rs b/crates/hir-ty/src/solver_errors.rs
new file mode 100644
index 0000000000..e4e76fa67b
--- /dev/null
+++ b/crates/hir-ty/src/solver_errors.rs
@@ -0,0 +1,90 @@
+//! Handling of trait solver errors and converting them to errors `hir` can pass to `ide-diagnostics`.
+//!
+//! Note that we also have [`crate::next_solver::infer::errors`], which takes the raw [`NextSolverError`],
+//! and converts it into [`FulfillmentError`] that contains more details.
+//!
+//! [`NextSolverError`]: crate::next_solver::fulfill::NextSolverError
+
+use macros::{TypeFoldable, TypeVisitable};
+use rustc_type_ir::{PredicatePolarity, inherent::IntoKind};
+
+use crate::{
+ Span,
+ next_solver::{
+ ClauseKind, DbInterner, PredicateKind, StoredTraitRef, TraitPredicate,
+ infer::{
+ errors::{FulfillmentError, FulfillmentErrorCode},
+ select::SelectionError,
+ },
+ },
+};
+
+#[derive(Debug, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)]
+pub struct SolverDiagnostic {
+ pub span: Span,
+ pub kind: SolverDiagnosticKind,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)]
+pub enum SolverDiagnosticKind {
+ TraitUnimplemented {
+ trait_predicate: StoredTraitPredicate,
+ root_trait_predicate: Option<StoredTraitPredicate>,
+ },
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)]
+pub struct StoredTraitPredicate {
+ pub trait_ref: StoredTraitRef,
+ pub polarity: PredicatePolarity,
+}
+
+impl StoredTraitPredicate {
+ #[inline]
+ pub fn get<'db>(&'db self, interner: DbInterner<'db>) -> TraitPredicate<'db> {
+ TraitPredicate { polarity: self.polarity, trait_ref: self.trait_ref.get(interner) }
+ }
+}
+
+impl SolverDiagnostic {
+ pub fn from_fulfillment_error(error: &FulfillmentError<'_>) -> Option<Self> {
+ let span = error.obligation.cause.span();
+ if span.is_dummy() {
+ return None;
+ }
+
+ // FIXME: Handle more error kinds.
+ let kind = match &error.code {
+ FulfillmentErrorCode::Select(SelectionError::Unimplemented) => {
+ match error.obligation.predicate.kind().skip_binder() {
+ PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => {
+ handle_trait_unimplemented(error, trait_pred)?
+ }
+ _ => return None,
+ }
+ }
+ _ => return None,
+ };
+ Some(SolverDiagnostic { span, kind })
+ }
+}
+
+fn handle_trait_unimplemented<'db>(
+ error: &FulfillmentError<'db>,
+ trait_pred: TraitPredicate<'db>,
+) -> Option<SolverDiagnosticKind> {
+ let trait_predicate = StoredTraitPredicate {
+ trait_ref: StoredTraitRef::new(trait_pred.trait_ref),
+ polarity: trait_pred.polarity,
+ };
+
+ let root_trait_predicate = match error.root_obligation.predicate.kind().skip_binder() {
+ PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => Some(StoredTraitPredicate {
+ trait_ref: StoredTraitRef::new(trait_pred.trait_ref),
+ polarity: trait_pred.polarity,
+ }),
+ _ => None,
+ };
+
+ Some(SolverDiagnosticKind::TraitUnimplemented { trait_predicate, root_trait_predicate })
+}
diff --git a/crates/hir-ty/src/specialization.rs b/crates/hir-ty/src/specialization.rs
index 7e4d3a8354..1ef8adb4d9 100644
--- a/crates/hir-ty/src/specialization.rs
+++ b/crates/hir-ty/src/specialization.rs
@@ -110,7 +110,7 @@ fn specializes_query(
ocx.register_obligations(clauses_as_obligations(
GenericPredicates::query_all(db, parent_impl_def_id.into())
.iter_instantiated(interner, parent_args.as_slice()),
- cause.clone(),
+ *cause,
param_env,
));
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs
index 83767c42ea..2fa70cd3a8 100644
--- a/crates/hir-ty/src/tests.rs
+++ b/crates/hir-ty/src/tests.rs
@@ -36,9 +36,9 @@ use syntax::{
use test_fixture::WithFixture;
use crate::{
- InferenceResult,
+ InferenceDiagnostic, InferenceResult,
display::{DisplayTarget, HirDisplay},
- infer::{Adjustment, TypeMismatch},
+ infer::Adjustment,
next_solver::Ty,
setup_tracing,
test_db::TestDB,
@@ -195,7 +195,14 @@ fn check_impl(
}
}
- for (expr_or_pat, mismatch) in inference_result.type_mismatches() {
+ let type_mismatches =
+ inference_result.diagnostics().iter().filter_map(|diag| match diag {
+ InferenceDiagnostic::TypeMismatch { node, expected, found } => {
+ Some((*node, expected.as_ref(), found.as_ref()))
+ }
+ _ => None,
+ });
+ for (expr_or_pat, expected, actual) in type_mismatches {
let Some(node) = (match expr_or_pat {
hir_def::hir::ExprOrPatId::ExprId(expr) => {
expr_node(body_source_map, expr, &db)
@@ -207,8 +214,8 @@ fn check_impl(
let range = node.as_ref().original_file_range_rooted(&db);
let actual = format!(
"expected {}, got {}",
- mismatch.expected.as_ref().display_test(&db, display_target),
- mismatch.actual.as_ref().display_test(&db, display_target)
+ expected.display_test(&db, display_target),
+ actual.display_test(&db, display_target)
);
match mismatches.remove(&range) {
Some(annotation) => assert_eq!(actual, annotation),
@@ -326,7 +333,17 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
krate: Crate| {
let display_target = DisplayTarget::from_crate(&db, krate);
let mut types: Vec<(InFile<SyntaxNode>, Ty<'_>)> = Vec::new();
- let mut mismatches: Vec<(InFile<SyntaxNode>, &TypeMismatch)> = Vec::new();
+ let type_mismatch_for_node = inference_result
+ .diagnostics()
+ .iter()
+ .filter_map(|diag| match diag {
+ InferenceDiagnostic::TypeMismatch { node, expected, found } => {
+ Some((*node, (expected.as_ref(), found.as_ref())))
+ }
+ _ => None,
+ })
+ .collect::<FxHashMap<_, _>>();
+ let mut mismatches: Vec<(InFile<SyntaxNode>, (Ty<'_>, Ty<'_>))> = Vec::new();
if let Some((binding_id, syntax_ptr)) = self_param {
let ty = &inference_result.type_of_binding[binding_id];
@@ -349,8 +366,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
Err(SyntheticSyntax) => continue,
};
types.push((node.clone(), ty.as_ref()));
- if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat) {
- mismatches.push((node, mismatch));
+ if let Some(mismatch) = type_mismatch_for_node.get(&pat.into()) {
+ mismatches.push((node, *mismatch));
}
}
@@ -363,8 +380,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
Err(SyntheticSyntax) => continue,
};
types.push((node.clone(), ty.as_ref()));
- if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) {
- mismatches.push((node, mismatch));
+ if let Some(mismatch) = type_mismatch_for_node.get(&expr.into()) {
+ mismatches.push((node, *mismatch));
}
}
@@ -395,7 +412,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
let range = node.value.text_range();
(range.start(), range.end())
});
- for (src_ptr, mismatch) in &mismatches {
+ for (src_ptr, (expected, actual)) in &mismatches {
let range = src_ptr.value.text_range();
let macro_prefix = if src_ptr.file_id != file_id { "!" } else { "" };
format_to!(
@@ -403,8 +420,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
"{}{:?}: expected {}, got {}\n",
macro_prefix,
range,
- mismatch.expected.as_ref().display_test(&db, display_target),
- mismatch.actual.as_ref().display_test(&db, display_target),
+ expected.display_test(&db, display_target),
+ actual.display_test(&db, display_target),
);
}
}
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 1b63a4a2c0..a5f349e593 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -2363,7 +2363,6 @@ fn test() {
}
"#,
expect![[r#"
- 46..49 'Foo': Foo<N>
93..97 'self': Foo<N>
108..125 '{ ... }': usize
118..119 'N': usize
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 168854de74..fd9ca8c402 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -15,12 +15,15 @@ use hir_def::{
};
use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name};
use hir_ty::{
- CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathGenericsSource,
- PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
+ CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, ParamEnvAndCrate,
+ PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
db::HirDatabase,
diagnostics::{BodyValidationDiagnostic, UnsafetyReason},
display::{DisplayTarget, HirDisplay},
+ next_solver::DbInterner,
+ solver_errors::SolverDiagnosticKind,
};
+use stdx::{impl_from, never};
use syntax::{
AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange,
ast::{self, HasGenericArgs},
@@ -36,6 +39,49 @@ pub use hir_ty::{
diagnostics::{CaseType, IncorrectCase},
};
+#[derive(Debug, Clone)]
+pub enum SpanAst {
+ Expr(ast::Expr),
+ Pat(ast::Pat),
+ Type(ast::Type),
+}
+const _: () = {
+ use syntax::ast::*;
+ impl_from!(Expr, Pat, Type for SpanAst);
+};
+
+impl From<Either<ast::Expr, ast::Pat>> for SpanAst {
+ fn from(value: Either<ast::Expr, ast::Pat>) -> Self {
+ match value {
+ Either::Left(it) => it.into(),
+ Either::Right(it) => it.into(),
+ }
+ }
+}
+
+impl ast::AstNode for SpanAst {
+ fn can_cast(kind: syntax::SyntaxKind) -> bool {
+ ast::Expr::can_cast(kind) || ast::Pat::can_cast(kind) || ast::Type::can_cast(kind)
+ }
+
+ fn cast(syntax: syntax::SyntaxNode) -> Option<Self> {
+ ast::Expr::cast(syntax.clone())
+ .map(SpanAst::Expr)
+ .or_else(|| ast::Pat::cast(syntax.clone()).map(SpanAst::Pat))
+ .or_else(|| ast::Type::cast(syntax).map(SpanAst::Type))
+ }
+
+ fn syntax(&self) -> &syntax::SyntaxNode {
+ match self {
+ SpanAst::Expr(it) => it.syntax(),
+ SpanAst::Pat(it) => it.syntax(),
+ SpanAst::Type(it) => it.syntax(),
+ }
+ }
+}
+
+pub type SpanSyntax = InFile<AstPtr<SpanAst>>;
+
macro_rules! diagnostics {
($AnyDiagnostic:ident <$db:lifetime> -> $($diag:ident $(<$lt:lifetime>)?,)*) => {
#[derive(Debug)]
@@ -115,6 +161,7 @@ diagnostics![AnyDiagnostic<'db> ->
ElidedLifetimesInPath,
TypeMustBeKnown<'db>,
UnionExprMustHaveExactlyOneField,
+ UnimplementedTrait<'db>,
];
#[derive(Debug)]
@@ -471,7 +518,7 @@ pub struct ElidedLifetimesInPath {
#[derive(Debug)]
pub struct TypeMustBeKnown<'db> {
- pub at_point: InFile<AstPtr<Either<ast::Type, Either<ast::Expr, ast::Pat>>>>,
+ pub at_point: SpanSyntax,
pub top_term: Option<Either<Type<'db>, String>>,
}
@@ -519,6 +566,13 @@ pub struct PatternArgInExternFn {
pub node: InFile<AstPtr<ast::Pat>>,
}
+#[derive(Debug)]
+pub struct UnimplementedTrait<'db> {
+ pub span: SpanSyntax,
+ pub trait_predicate: crate::TraitPredicate<'db>,
+ pub root_trait_predicate: Option<crate::TraitPredicate<'db>>,
+}
+
impl<'db> AnyDiagnostic<'db> {
pub(crate) fn body_validation_diagnostic(
db: &'db dyn HirDatabase,
@@ -655,9 +709,10 @@ impl<'db> AnyDiagnostic<'db> {
pub(crate) fn inference_diagnostic(
db: &'db dyn HirDatabase,
def: DefWithBodyId,
- d: &InferenceDiagnostic,
+ d: &'db InferenceDiagnostic,
source_map: &hir_def::expr_store::BodySourceMap,
sig_map: &hir_def::expr_store::ExpressionStoreSourceMap,
+ env: ParamEnvAndCrate<'db>,
) -> Option<AnyDiagnostic<'db>> {
let expr_syntax = |expr| {
source_map
@@ -681,6 +736,18 @@ impl<'db> AnyDiagnostic<'db> {
ExprOrPatId::ExprId(expr) => expr_syntax(expr),
ExprOrPatId::PatId(pat) => pat_syntax(pat),
};
+ let span_syntax = |span| match span {
+ hir_ty::Span::ExprId(idx) => expr_syntax(idx).map(|it| it.upcast()),
+ hir_ty::Span::PatId(idx) => pat_syntax(idx).map(|it| it.upcast()),
+ hir_ty::Span::TypeRefId(idx) => type_syntax(idx).map(|it| it.upcast()),
+ hir_ty::Span::BindingId(idx) => {
+ pat_syntax(source_map.patterns_for_binding(idx)[0]).map(|it| it.upcast())
+ }
+ hir_ty::Span::Dummy => {
+ never!("should never create a diagnostic for dummy spans");
+ None
+ }
+ };
Some(match d {
&InferenceDiagnostic::NoSuchField { field: expr, private, variant } => {
let expr_or_pat = match expr {
@@ -860,14 +927,7 @@ impl<'db> AnyDiagnostic<'db> {
InvalidLhsOfAssignment { lhs }.into()
}
&InferenceDiagnostic::TypeMustBeKnown { at_point, ref top_term } => {
- let at_point = match at_point {
- hir_ty::Span::ExprId(idx) => expr_syntax(idx)?.map(|it| it.wrap_right()),
- hir_ty::Span::PatId(idx) => pat_syntax(idx)?.map(|it| it.wrap_right()),
- hir_ty::Span::TypeRefId(idx) => type_syntax(idx)?.map(|it| it.wrap_left()),
- hir_ty::Span::Dummy => unreachable!(
- "should never create TypeMustBeKnown diagnostic for dummy spans"
- ),
- };
+ let at_point = span_syntax(at_point)?;
let top_term = top_term.as_ref().map(|top_term| match top_term.as_ref().kind() {
rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(Type {
ty,
@@ -887,6 +947,39 @@ impl<'db> AnyDiagnostic<'db> {
let expr = expr_syntax(expr)?;
UnionExprMustHaveExactlyOneField { expr }.into()
}
+ InferenceDiagnostic::TypeMismatch { node, expected, found } => {
+ let expr_or_pat = expr_or_pat_syntax(*node)?;
+ TypeMismatch {
+ expr_or_pat,
+ expected: Type { env, ty: expected.as_ref() },
+ actual: Type { env, ty: found.as_ref() },
+ }
+ .into()
+ }
+ InferenceDiagnostic::SolverDiagnostic(d) => {
+ let span = span_syntax(d.span)?;
+ Self::solver_diagnostic(db, &d.kind, span, env)?
+ }
+ })
+ }
+
+ fn solver_diagnostic(
+ db: &'db dyn HirDatabase,
+ d: &'db SolverDiagnosticKind,
+ span: SpanSyntax,
+ env: ParamEnvAndCrate<'db>,
+ ) -> Option<AnyDiagnostic<'db>> {
+ let interner = DbInterner::new_no_crate(db);
+ Some(match d {
+ SolverDiagnosticKind::TraitUnimplemented { trait_predicate, root_trait_predicate } => {
+ let trait_predicate =
+ crate::TraitPredicate { inner: trait_predicate.get(interner), env };
+ let root_trait_predicate =
+ root_trait_predicate.as_ref().map(|root_trait_predicate| {
+ crate::TraitPredicate { inner: root_trait_predicate.get(interner), env }
+ });
+ UnimplementedTrait { span, trait_predicate, root_trait_predicate }.into()
+ }
})
}
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 139f078eef..880c9d9ae6 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -30,8 +30,8 @@ use rustc_type_ir::inherent::IntoKind;
use crate::{
Adt, AnyFunctionId, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum,
EnumVariant, ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl,
- LifetimeParam, Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitRef,
- TupleField, Type, TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union,
+ LifetimeParam, Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitPredicate,
+ TraitRef, TupleField, Type, TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union,
};
fn write_builtin_derive_impl_method<'db>(
@@ -853,6 +853,12 @@ impl<'db> HirDisplay<'db> for TraitRef<'db> {
}
}
+impl<'db> HirDisplay<'db> for TraitPredicate<'db> {
+ fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
+ self.inner.hir_fmt(f)
+ }
+}
+
impl<'db> HirDisplay<'db> for Trait {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
// FIXME(trait-alias) needs special handling to print the equal sign
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index b74f594ebe..7ab9bca697 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2121,6 +2121,7 @@ impl DefWithBody {
return;
};
let krate = self.module(db).id.krate(db);
+ let env = body_param_env_from_has_crate(db, id);
let (body, source_map) = Body::with_source_map(db, id);
let sig_source_map = match self {
@@ -2144,34 +2145,14 @@ impl DefWithBody {
let infer = InferenceResult::of(db, id);
for d in infer.diagnostics() {
- acc.extend(AnyDiagnostic::inference_diagnostic(db, id, d, source_map, sig_source_map));
- }
-
- for (pat_or_expr, mismatch) in infer.type_mismatches() {
- let expr_or_pat = match pat_or_expr {
- ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left),
- ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right),
- };
- let expr_or_pat = match expr_or_pat {
- Ok(Either::Left(expr)) => expr,
- Ok(Either::Right(InFile { file_id, value: pat })) => {
- // cast from Either<Pat, SelfParam> -> Either<_, Pat>
- let Some(ptr) = AstPtr::try_from_raw(pat.syntax_node_ptr()) else {
- continue;
- };
- InFile { file_id, value: ptr }
- }
- Err(SyntheticSyntax) => continue,
- };
-
- acc.push(
- TypeMismatch {
- expr_or_pat,
- expected: Type::new(db, id, mismatch.expected.as_ref()),
- actual: Type::new(db, id, mismatch.actual.as_ref()),
- }
- .into(),
- );
+ acc.extend(AnyDiagnostic::inference_diagnostic(
+ db,
+ id,
+ d,
+ source_map,
+ sig_source_map,
+ env,
+ ));
}
let missing_unsafe = hir_ty::diagnostics::missing_unsafe(db, id);
@@ -6935,6 +6916,33 @@ pub trait HasVisibility {
}
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum PredicatePolarity {
+ /// `T: Trait`
+ Positive,
+ /// `T: !Trait`
+ Negative,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct TraitPredicate<'db> {
+ inner: hir_ty::next_solver::TraitPredicate<'db>,
+ env: ParamEnvAndCrate<'db>,
+}
+
+impl<'db> TraitPredicate<'db> {
+ pub fn polarity(&self) -> PredicatePolarity {
+ match self.inner.polarity {
+ rustc_type_ir::PredicatePolarity::Positive => PredicatePolarity::Positive,
+ rustc_type_ir::PredicatePolarity::Negative => PredicatePolarity::Negative,
+ }
+ }
+
+ pub fn trait_ref(&self) -> TraitRef<'db> {
+ TraitRef { env: self.env, trait_ref: self.inner.trait_ref }
+ }
+}
+
/// Trait for obtaining the defining crate of an item.
pub trait HasCrate {
fn krate(&self, db: &dyn HirDatabase) -> Crate;
diff --git a/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/crates/ide-diagnostics/src/handlers/invalid_cast.rs
index ba7556cd8b..c004ee31ae 100644
--- a/crates/ide-diagnostics/src/handlers/invalid_cast.rs
+++ b/crates/ide-diagnostics/src/handlers/invalid_cast.rs
@@ -390,7 +390,7 @@ struct Bar;
impl Foo for Bar {}
-fn to_raw<T>(_: *mut T) -> *mut () {
+fn to_raw<T: ?Sized>(_: *mut T) -> *mut () {
loop {}
}
diff --git a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
index 179f9dcec5..f6293e35d0 100644
--- a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
+++ b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
@@ -205,6 +205,7 @@ trait Foo { fn method(&self, _arg: usize) {} }
fn f() {
let x;
+ // ^ error: type annotations needed
x.method();
}
"#,
diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 38cf548cc6..aacbe72313 100644
--- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -1062,7 +1062,7 @@ impl FooTrait for S2 {
fn no_false_positive_on_format_args_since_1_89_0() {
check_diagnostics(
r#"
-//- minicore: fmt
+//- minicore: fmt, builtin_impls
fn test() {
let foo = 10;
let bar = true;
diff --git a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
index fb1470b69f..b5a47e508e 100644
--- a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
+++ b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
@@ -333,7 +333,7 @@ fn foo(x: usize) -> u8 {
}
}
"#,
- std::iter::once("remove-unnecessary-else".to_owned()),
+ &["remove-unnecessary-else"],
);
check_fix(
r#"
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index d469405d61..250c692d16 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -338,7 +338,8 @@ fn str_ref_to_owned(
#[cfg(test)]
mod tests {
use crate::tests::{
- check_diagnostics, check_diagnostics_with_disabled, check_fix, check_has_fix, check_no_fix,
+ check_diagnostics, check_diagnostics_with_disabled, check_fix, check_fix_with_disabled,
+ check_has_fix, check_no_fix,
};
#[test]
@@ -755,7 +756,7 @@ fn foo() -> Result<(), ()> {
#[test]
fn wrapped_unit_as_return_expr() {
- check_fix(
+ check_fix_with_disabled(
r#"
//- minicore: result
fn foo(b: bool) -> Result<(), String> {
@@ -773,6 +774,7 @@ fn foo(b: bool) -> Result<(), String> {
Err("oh dear".to_owned())
}"#,
+ &["E0599"],
);
}
@@ -822,7 +824,7 @@ fn foo() -> SomeOtherEnum { 0$0 }
#[test]
fn unwrap_return_type() {
- check_fix(
+ check_fix_with_disabled(
r#"
//- minicore: option, result
fn div(x: i32, y: i32) -> i32 {
@@ -840,6 +842,7 @@ fn div(x: i32, y: i32) -> i32 {
x / y
}
"#,
+ &["E0282"],
);
}
@@ -897,7 +900,7 @@ fn div(x: i32, y: i32) -> i32 {
#[test]
fn unwrap_return_type_option_tail_unit() {
- check_fix(
+ check_fix_with_disabled(
r#"
//- minicore: option, result
fn div(x: i32, y: i32) {
@@ -915,12 +918,13 @@ fn div(x: i32, y: i32) {
}
}
"#,
+ &["E0282"],
);
}
#[test]
fn unwrap_return_type_handles_generic_functions() {
- check_fix(
+ check_fix_with_disabled(
r#"
//- minicore: option, result
fn div<T>(x: T) -> T {
@@ -938,12 +942,13 @@ fn div<T>(x: T) -> T {
x
}
"#,
+ &["E0282"],
);
}
#[test]
fn unwrap_return_type_handles_type_aliases() {
- check_fix(
+ check_fix_with_disabled(
r#"
//- minicore: option, result
type MyResult<T> = T;
@@ -965,12 +970,13 @@ fn div(x: i32, y: i32) -> MyResult<i32> {
x / y
}
"#,
+ &["E0282"],
);
}
#[test]
fn unwrap_tail_expr() {
- check_fix(
+ check_fix_with_disabled(
r#"
//- minicore: result
fn foo() -> () {
@@ -983,12 +989,13 @@ fn foo() -> () {
println!("Hello, world!");
}
"#,
+ &["E0282"],
);
}
#[test]
fn unwrap_to_empty_block() {
- check_fix(
+ check_fix_with_disabled(
r#"
//- minicore: result
fn foo() -> () {
@@ -998,6 +1005,7 @@ fn foo() -> () {
r#"
fn foo() -> () {}
"#,
+ &["E0282"],
);
}
@@ -1341,6 +1349,7 @@ pub fn foo<T: Foo>(_: T) -> (T::Out,) { loop { } }
fn main() {
let _x = foo(2);
// ^^ error: type annotations needed
+ // ^^^ error: the trait bound `i32: Foo` is not satisfied
}
"#,
);
diff --git a/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs
index ad86df407a..30d8165e0a 100644
--- a/crates/ide-diagnostics/src/handlers/type_must_be_known.rs
+++ b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs
@@ -1,5 +1,5 @@
use either::Either;
-use hir::HirDisplay;
+use hir::{HirDisplay, SpanAst};
use stdx::format_to;
use syntax::{AstNode, SyntaxNodePtr, ast};
@@ -17,7 +17,7 @@ pub(crate) fn type_must_be_known<'db>(
// Do some adjustments to the node: FIXME: We should probably do that at the emitting site.
let node = ctx.sema.to_node(d.at_point);
- if let Either::Right(Either::Left(expr)) = &node
+ if let SpanAst::Expr(expr) = &node
&& let Some(Either::Left(top_ty)) = &d.top_term
&& let Some(expr_ty) = ctx.sema.type_of_expr(expr)
&& expr_ty.original == *top_ty
diff --git a/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/crates/ide-diagnostics/src/handlers/undeclared_label.rs
index 1bab4f453f..7efc8a7136 100644
--- a/crates/ide-diagnostics/src/handlers/undeclared_label.rs
+++ b/crates/ide-diagnostics/src/handlers/undeclared_label.rs
@@ -86,16 +86,18 @@ fn foo() {
check_diagnostics(
r#"
//- minicore: option, try
-fn foo() {
+fn foo() -> Option<()> {
None?;
+ None
}
"#,
);
check_diagnostics(
r#"
//- minicore: option, try, future
-async fn foo() {
+async fn foo() -> Option<()> {
None?;
+ None
}
"#,
);
@@ -103,7 +105,7 @@ async fn foo() {
r#"
//- minicore: option, try, future, fn
async fn foo() {
- || None?;
+ || { None?; Some(()) };
}
"#,
);
diff --git a/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs b/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs
new file mode 100644
index 0000000000..4326aec458
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs
@@ -0,0 +1,53 @@
+use hir::HirDisplay;
+
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: type-must-be-known
+//
+// This diagnostic is triggered when rust-analyzer cannot infer some type.
+pub(crate) fn unimplemented_trait<'db>(
+ ctx: &DiagnosticsContext<'_, 'db>,
+ d: &hir::UnimplementedTrait<'db>,
+) -> Diagnostic {
+ let message = match &d.root_trait_predicate {
+ Some(root_predicate) if *root_predicate != d.trait_predicate => format!(
+ "the trait bound `{}` is not satisfied\n\
+ required by the bound `{}`\n",
+ d.trait_predicate.display(ctx.db(), ctx.display_target),
+ root_predicate.display(ctx.db(), ctx.display_target),
+ ),
+ _ => format!(
+ "the trait bound `{}` is not satisfied",
+ d.trait_predicate.display(ctx.db(), ctx.display_target),
+ ),
+ };
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0277"),
+ message,
+ d.span.map(Into::into),
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn smoke_test() {
+ check_diagnostics(
+ r#"
+trait Trait {}
+impl<T: Trait, const N: usize> Trait for [T; N] {}
+fn foo(_v: impl Trait) {}
+fn bar() {
+ foo(1);
+ // ^^^ error: the trait bound `i32: Trait` is not satisfied
+ foo([1]);
+ // ^^^ error: the trait bound `i32: Trait` is not satisfied
+ // | required by the bound `[i32; 1]: Trait`
+}
+ "#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 3f803a8c11..5882a8fdc2 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -73,6 +73,7 @@ mod handlers {
pub(crate) mod typed_hole;
pub(crate) mod undeclared_label;
pub(crate) mod unimplemented_builtin_macro;
+ pub(crate) mod unimplemented_trait;
pub(crate) mod union_expr_must_have_exactly_one_field;
pub(crate) mod unreachable_label;
pub(crate) mod unresolved_assoc_item;
@@ -495,6 +496,7 @@ pub fn semantic_diagnostics(
AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&ctx, &d),
AnyDiagnostic::PatternArgInExternFn(d) => handlers::pattern_arg_in_extern_fn::pattern_arg_in_extern_fn(&ctx, &d),
AnyDiagnostic::UnionExprMustHaveExactlyOneField(d) => handlers::union_expr_must_have_exactly_one_field::union_expr_must_have_exactly_one_field(&ctx, &d),
+ AnyDiagnostic::UnimplementedTrait(d) => handlers::unimplemented_trait::unimplemented_trait(&ctx, &d),
};
res.push(d)
}
diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs
index fc49542e3c..4b9535ca06 100644
--- a/crates/ide-diagnostics/src/tests.rs
+++ b/crates/ide-diagnostics/src/tests.rs
@@ -57,11 +57,11 @@ fn check_nth_fix(
pub(crate) fn check_fix_with_disabled(
#[rust_analyzer::rust_fixture] ra_fixture_before: &str,
#[rust_analyzer::rust_fixture] ra_fixture_after: &str,
- disabled: impl Iterator<Item = String>,
+ disabled: &[&str],
) {
let mut config = DiagnosticsConfig::test_sample();
config.expr_fill_default = ExprFillDefaultMode::Default;
- config.disabled.extend(disabled);
+ config.disabled.extend(disabled.iter().map(|&disabled| disabled.to_owned()));
check_nth_fix_with_config(config, 0, ra_fixture_before, ra_fixture_after)
}
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index a2b317be58..3eb7867a3a 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -580,6 +580,7 @@ pub fn also_calls_foo() {
"#,
false,
false,
+ // FIXME: The ranges here are volatile when minicore changes, that's not good.
expect![[r#"
foo Function FileId(1) 0..15 7..10
@@ -599,7 +600,7 @@ fn main() {
false,
false,
expect![[r#"
- Some Variant FileId(1) 6022..6054 6047..6051
+ Some Variant FileId(1) 6737..6769 6762..6766
FileId(0) 46..50
"#]],
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 0314600405..d06690f203 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -2,6 +2,7 @@
//! errors.
use std::{
+ cell::LazyCell,
env, fmt,
ops::AddAssign,
panic::{AssertUnwindSafe, catch_unwind},
@@ -908,6 +909,18 @@ impl flags::AnalysisStats {
// region:expressions
let (previous_exprs, previous_unknown, previous_partially_unknown) =
(num_exprs, num_exprs_unknown, num_exprs_partially_unknown);
+ let type_mismatch_for_node = LazyCell::new(|| {
+ inference_result
+ .diagnostics()
+ .iter()
+ .filter_map(|diag| match diag {
+ hir_ty::InferenceDiagnostic::TypeMismatch { node, expected, found } => {
+ Some((*node, (expected.as_ref(), found.as_ref())))
+ }
+ _ => None,
+ })
+ .collect::<FxHashMap<_, _>>()
+ });
for (expr_id, _) in body.exprs() {
let ty = inference_result.expr_ty(expr_id);
num_exprs += 1;
@@ -964,9 +977,10 @@ impl flags::AnalysisStats {
ty.display(db, display_target)
);
}
- if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
+ if inference_result.expr_has_type_mismatch(expr_id) {
num_expr_type_mismatches += 1;
if verbosity.is_verbose() {
+ let (expected, actual) = type_mismatch_for_node[&expr_id.into()];
if let Some((path, start, end)) = expr_syntax_range(db, vfs, sm(), expr_id)
{
bar.println(format!(
@@ -976,24 +990,25 @@ impl flags::AnalysisStats {
start.col,
end.line + 1,
end.col,
- mismatch.expected.as_ref().display(db, display_target),
- mismatch.actual.as_ref().display(db, display_target)
+ expected.display(db, display_target),
+ actual.display(db, display_target)
));
} else {
bar.println(format!(
"{}: Expected {}, got {}",
name.display(db, Edition::LATEST),
- mismatch.expected.as_ref().display(db, display_target),
- mismatch.actual.as_ref().display(db, display_target)
+ expected.display(db, display_target),
+ actual.display(db, display_target)
));
}
}
if self.output == Some(OutputFormat::Csv) {
+ let (expected, actual) = type_mismatch_for_node[&expr_id.into()];
println!(
r#"{},mismatch,"{}","{}""#,
location_csv_expr(db, vfs, sm(), expr_id),
- mismatch.expected.as_ref().display(db, display_target),
- mismatch.actual.as_ref().display(db, display_target)
+ expected.display(db, display_target),
+ actual.display(db, display_target)
);
}
}
@@ -1067,9 +1082,10 @@ impl flags::AnalysisStats {
ty.display(db, display_target)
);
}
- if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat_id) {
+ if inference_result.pat_has_type_mismatch(pat_id) {
num_pat_type_mismatches += 1;
if verbosity.is_verbose() {
+ let (expected, actual) = type_mismatch_for_node[&pat_id.into()];
if let Some((path, start, end)) = pat_syntax_range(db, vfs, sm(), pat_id) {
bar.println(format!(
"{} {}:{}-{}:{}: Expected {}, got {}",
@@ -1078,24 +1094,25 @@ impl flags::AnalysisStats {
start.col,
end.line + 1,
end.col,
- mismatch.expected.as_ref().display(db, display_target),
- mismatch.actual.as_ref().display(db, display_target)
+ expected.display(db, display_target),
+ actual.display(db, display_target)
));
} else {
bar.println(format!(
"{}: Expected {}, got {}",
name.display(db, Edition::LATEST),
- mismatch.expected.as_ref().display(db, display_target),
- mismatch.actual.as_ref().display(db, display_target)
+ expected.display(db, display_target),
+ actual.display(db, display_target)
));
}
}
if self.output == Some(OutputFormat::Csv) {
+ let (expected, actual) = type_mismatch_for_node[&pat_id.into()];
println!(
r#"{},mismatch,"{}","{}""#,
location_csv_pat(db, vfs, sm(), pat_id),
- mismatch.expected.as_ref().display(db, display_target),
- mismatch.actual.as_ref().display(db, display_target)
+ expected.display(db, display_target),
+ actual.display(db, display_target)
);
}
}
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index e9ab066160..29775590ea 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -738,6 +738,30 @@ pub mod ops {
pub struct RangeToInclusive<Idx> {
pub end: Idx,
}
+
+ // region:iterator
+ pub trait Step {}
+ macro_rules! impl_step {
+ ( $( $ty:ty ),* $(,)? ) => {
+ $(
+ impl Step for $ty {}
+ )*
+ };
+ }
+ impl_step!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize);
+
+ macro_rules! impl_iterator {
+ ( $( $range:ident ),* $(,)? ) => {
+ $(
+ impl<Idx: Step> Iterator for $range<Idx> {
+ type Item = Idx;
+ fn next(&mut self) -> Option<Self::Item> { loop {} }
+ }
+ )*
+ };
+ }
+ impl_iterator!(Range, RangeFrom, RangeTo, RangeInclusive, RangeToInclusive);
+ // endregion:iterator
}
pub use self::range::{Range, RangeFrom, RangeFull, RangeTo};
pub use self::range::{RangeInclusive, RangeToInclusive};
@@ -1292,6 +1316,38 @@ pub mod fmt {
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
}
+ impl<T: ?Sized + Debug> Debug for &T {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ T::fmt(&**self, f)
+ }
+ }
+ impl<T: ?Sized + Display> Display for &T {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ T::fmt(&**self, f)
+ }
+ }
+
+ macro_rules! impl_fmt_traits {
+ ( $($ty:ty),* $(,)? ) => {
+ $(
+ impl Debug for $ty {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result { loop {} }
+ }
+ impl Display for $ty {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result { loop {} }
+ }
+ )*
+ }
+ }
+
+ impl_fmt_traits!(str);
+
+ // region:builtin_impls
+ impl_fmt_traits!(
+ i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64, bool, char,
+ );
+ // endregion:builtin_impls
+
mod rt {
use super::*;