diff --git a/crates/hir-def/src/attrs.rs b/crates/hir-def/src/attrs.rs index 5cf5a9b6be84..7f76d02a7ab5 100644 --- a/crates/hir-def/src/attrs.rs +++ b/crates/hir-def/src/attrs.rs @@ -263,6 +263,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) + } + _ => {} + }, _ => {} }, } @@ -335,6 +341,8 @@ bitflags::bitflags! { const MACRO_STYLE_PARENTHESES = 1 << 48; const PREFER_UNDERSCORE_IMPORT = 1 << 49; + + const DIAGNOSTIC_DO_NOT_RECOMMEND = 1 << 50; } } 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 beb126717314..b058ad6d9ab8 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 /// ::new_…(arg) /// ``` - fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId { + fn make_argument( + &mut self, + arg_ptr: Option>, + 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 271484da7b9a..366857f23316 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>, } #[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, fmt_snippet: Option, 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 71da560b15f1..735b30732d5e 100644 --- a/crates/hir-expand/src/files.rs +++ b/crates/hir-expand/src/files.rs @@ -128,6 +128,16 @@ impl ErasedAstId { } } +impl InFileWrapper> { + #[inline] + pub fn upcast(self) -> InFileWrapper> + where + N: Into, + { + self.map(|it| it.upcast()) + } +} + impl InFileWrapper { 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 2c2400a14a7f..a8ed4126abea 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 99dddf0bf4cd..a3c080fc220c 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs @@ -177,7 +177,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 { @@ -331,7 +331,7 @@ impl<'db> ExprValidator<'db> { let &Statement::Let { pat, initializer, else_branch: None, .. } = stmt else { continue; }; - if self.infer.type_mismatch_for_pat(pat).is_some() { + if self.infer.pat_has_type_mismatch(pat) { continue; } let Some(initializer) = initializer else { continue }; @@ -628,13 +628,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 ca3723f8ef29..f70caa0507ea 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) } @@ -2269,6 +2269,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 5cccba15848b..5ac5e387f8f0 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, }; @@ -455,13 +456,13 @@ pub enum InferenceDiagnostic { at_point: Span, top_term: Option, }, -} - -/// 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. @@ -681,7 +682,6 @@ pub struct InferenceResult { pub(crate) type_of_type_placeholder: FxHashMap, pub(crate) type_of_opaque: FxHashMap, - pub(crate) type_mismatches: Option>>, /// 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`. @@ -689,6 +689,8 @@ pub struct InferenceResult { pub(crate) has_errors: bool, /// During inference this field is empty and [`InferenceContext::diagnostics`] is filled instead. diagnostics: ThinVec, + // FIXME: Remove this, change it to be in `InferenceContext`: + nodes_with_type_mismatches: Option>>, /// Interned `Error` type to return references to. // FIXME: Remove this. @@ -982,12 +984,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(), @@ -1038,26 +1040,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 { - 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 { - 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)> { self.type_of_type_placeholder.iter().map(|(&type_ref, ty)| (type_ref, ty.as_ref())) @@ -1404,7 +1402,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: _, @@ -1414,6 +1411,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); @@ -1437,12 +1435,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); @@ -1930,24 +1925,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( @@ -1957,7 +1968,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) @@ -1971,14 +1982,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) } @@ -2012,11 +2020,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)); } } @@ -2081,7 +2089,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); } @@ -2181,7 +2189,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); @@ -2294,7 +2302,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 @@ -2326,12 +2334,13 @@ impl<'body, 'db> InferenceContext<'body, 'db> { fn resolve_variant_on_alias( &mut self, + node: ExprOrPatId, ty: Ty<'db>, unresolved: Option, path: &ModPath, ) -> (Ty<'db>, Option) { 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 { @@ -2544,10 +2553,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 a6c7b2dbb9c3..0ba3b3dd056d 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 237c9177f879..30f82495e3d0 100644 --- a/crates/hir-ty/src/infer/callee.rs +++ b/crates/hir-ty/src/infer/callee.rs @@ -45,9 +45,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() { @@ -97,7 +99,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 } @@ -110,7 +112,8 @@ impl<'db> InferenceContext<'_, 'db> { error_reported: &mut bool, ) -> Option> { 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() { @@ -244,8 +247,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); @@ -257,6 +260,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, MethodCallee<'db>)> { @@ -313,7 +317,7 @@ impl<'db> InferenceContext<'_, 'db> { // one which may apply. So if we treat opaques as inference variables // `Box: 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, @@ -471,8 +475,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() @@ -545,7 +550,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 d23a32d81b64..6cc3726103ac 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>, ()> { - 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 f90f13591946..218d8e2f3e0b 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], - decl_output: Option, + decl_inputs: &[PatId], + decl_input_tys: &[Option], + decl_output_ty: Option, expected_sig: Option>, ) -> 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], - decl_output: Option, + decl_inputs: &[PatId], + decl_input_tys: &[Option], + decl_output_ty: Option, 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], - decl_output: Option, + decl_inputs: &[PatId], + decl_input_tys: &[Option], + decl_output_ty: Option, 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 79bdc6cea104..0622559f1e94 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 16b84217b860..2e55d53a6529 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::{AdtDef, IntoKind, Ty as _}, -}; +use macros::{TypeFoldable, TypeVisitable}; +use rustc_type_ir::inherent::{AdtDef, 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, } -impl<'db> TypeVisitable> for Place { - fn visit_with>>(&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> for Place { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - 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::>()?; - Ok(Self { base_ty, base, projections }) - } - - fn fold_with>>(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 ddff23e84043..e5767c2d46a8 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`. - 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 327975d76650..0494214a294d 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, Vec, 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 5900027a9876..0127fd6cdbcd 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), @@ -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 02bce22d6d4c..178b3fcbf5ae 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 ac209adef870..6f96a1d07244 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); @@ -320,6 +320,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { && derefed_tys.iter().any(|adjust| adjust.kind == PatAdjust::OverloadedDeref) { 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()), @@ -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, ()>>, ) -> 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 `::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>, ) -> 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 @@ -1683,10 +1695,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 c020c9812b82..4df38e96ee00 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 63841af682ed..968d793615ed 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>`. 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 4342375621d1..6a34c5b8e5b6 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>, + trait_errors: Vec>, } 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) { + 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(&mut self, value_ref: &mut T) where T: TypeFoldable>, @@ -543,6 +548,8 @@ pub(super) mod resolve_completely { pub(crate) fn resolve_diagnostics(mut self) -> (ThinVec, 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> + TypeSuperFoldable> + 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 4433dd6425ed..ca32a404a0ac 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, TypeAliasId, TypeOrConstParamId, TypeParamId, - hir::{ExprId, ExprOrPatId, PatId}, + hir::{BindingId, ExprId, ExprOrPatId, PatId}, resolver::TypeNs, type_ref::{Rawness, TypeRefId}, }; @@ -549,7 +550,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; @@ -674,11 +675,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 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 2c6c7ed9a554..86477f2c0b6b 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 a29e3db18d25..821d737cf976 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` // 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> + 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 8a28b1672454..f40eed9c1c05 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 7dcc00858e25..3a070e25be87 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -34,7 +34,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, @@ -111,7 +111,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), @@ -198,12 +197,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) => { @@ -2399,7 +2392,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 3645f8096cfd..84cfbf27671a 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 fa90e3d8a004..2df9a5259ddf 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>` 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 ba9cd39d448f..1fb4cb2b43e7 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> 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 4784edf60f5a..f63200a2e08c 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> { 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> { 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 000000000000..d10f70274cd8 --- /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>, TypeError<'db>), // always comes from a SubtypePredicate + ConstEquate(ExpectedFound>, 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, + }, +} + +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) -> >::Result, + ) -> >::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> { + 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> { + 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> { + 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> { + 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> { + 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> { + 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>; + + 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 Trait for for<..> fn(..A) -> R` was written + // instead of `impl 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>, + cause: ObligationCause, + _idx: usize, + _parent_trait_pred: PolyTraitPredicate<'db>, +) -> ObligationCause { + cause +} + +fn derive_host_cause<'db>( + _interner: DbInterner<'db>, + _candidate_kind: inspect::ProbeKind>, + 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 f038c47a8bcf..0bb980c90609 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 0443dbd8142b..f3af697febcb 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> for LatticeOp<'_, 'db> { preds: impl IntoIterator, 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>>) { 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 1edf256d012f..4584b3579645 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>(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> 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> 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> { 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, 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 566f72fbd8e6..fdb1fa3d0515 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 aa6f27c4c2d2..152b58baeb6d 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> 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>>( 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 8658d03a9e3e..fba3712ed53a 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>; pub type ExistentialTraitRef<'db> = ty::ExistentialTraitRef>; pub type ExistentialProjection<'db> = ty::ExistentialProjection>; pub type TraitPredicate<'db> = ty::TraitPredicate>; +pub type HostEffectPredicate<'db> = ty::HostEffectPredicate>; pub type ClauseKind<'db> = ty::ClauseKind>; pub type PredicateKind<'db> = ty::PredicateKind>; pub type NormalizesTo<'db> = ty::NormalizesTo>; diff --git a/crates/hir-ty/src/next_solver/structural_normalize.rs b/crates/hir-ty/src/next_solver/structural_normalize.rs index 7a70bae97cfe..769d2ae141f0 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 858233cb2c90..4d52b6f9ce14 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> { 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 000000000000..e4e76fa67bac --- /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, + }, +} + +#[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 { + 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 { + 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 7e4d3a83549e..1ef8adb4d985 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 83767c42ea7c..2fa70cd3a8c0 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, Ty<'_>)> = Vec::new(); - let mut mismatches: Vec<(InFile, &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::>(); + let mut mismatches: Vec<(InFile, (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 1b63a4a2c0bb..a5f349e59338 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 93..97 'self': Foo 108..125 '{ ... }': usize 118..119 'N': usize diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index afafede6b929..c32b649d3bab 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> for SpanAst { + fn from(value: Either) -> 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 { + 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>; + macro_rules! diagnostics { ($AnyDiagnostic:ident <$db:lifetime> -> $($diag:ident $(<$lt:lifetime>)?,)*) => { #[derive(Debug)] @@ -111,6 +157,7 @@ diagnostics![AnyDiagnostic<'db> -> MissingLifetime, ElidedLifetimesInPath, TypeMustBeKnown<'db>, + UnimplementedTrait<'db>, ]; #[derive(Debug)] @@ -454,7 +501,7 @@ pub struct ElidedLifetimesInPath { #[derive(Debug)] pub struct TypeMustBeKnown<'db> { - pub at_point: InFile>>>, + pub at_point: SpanSyntax, pub top_term: Option, String>>, } @@ -492,6 +539,13 @@ pub struct InvalidLhsOfAssignment { pub lhs: InFile>>, } +#[derive(Debug)] +pub struct UnimplementedTrait<'db> { + pub span: SpanSyntax, + pub trait_predicate: crate::TraitPredicate<'db>, + pub root_trait_predicate: Option>, +} + impl<'db> AnyDiagnostic<'db> { pub(crate) fn body_validation_diagnostic( db: &'db dyn HirDatabase, @@ -623,9 +677,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> { let expr_syntax = |expr| { source_map @@ -649,6 +704,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 { @@ -824,14 +891,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, @@ -847,6 +907,39 @@ impl<'db> AnyDiagnostic<'db> { }); TypeMustBeKnown { at_point, top_term }.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> { + 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 139f078eef6b..880c9d9ae68a 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 df9f743224a4..84eafc97c08b 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 -> 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); @@ -6933,6 +6914,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 405d8df6854d..7dd2d534037c 100644 --- a/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -387,7 +387,7 @@ struct Bar; impl Foo for Bar {} -fn to_raw(_: *mut T) -> *mut () { +fn to_raw(_: *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 b900a8f5cc10..95bcf731869e 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 3351f5dc1cfb..f54485677c4d 100644 --- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -1059,7 +1059,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 2ec41d052849..09263941d582 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 a845e0b59aca..b5f4c06de5a7 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(x: T) -> T { @@ -938,12 +942,13 @@ fn div(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; @@ -965,12 +970,13 @@ fn div(x: i32, y: i32) -> MyResult { 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) -> (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 5363f4a5cec5..b9f608567959 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 f81d34377da4..768aa153f3fc 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 000000000000..441a6ac876be --- /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 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 300a6e6c7fd5..6871471acae6 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -71,6 +71,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 unreachable_label; pub(crate) mod unresolved_assoc_item; pub(crate) mod unresolved_extern_crate; @@ -487,6 +488,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::GenericDefaultRefersToSelf(d) => handlers::generic_default_refers_to_self::generic_default_refers_to_self(&ctx, &d), AnyDiagnostic::InvalidLhsOfAssignment(d) => handlers::invalid_lhs_of_assignment::invalid_lhs_of_assignment(&ctx, &d), AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&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 fc49542e3ccd..4b9535ca061b 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, + 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 a2b317be5884..3eb7867a3ab3 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 bf9a66bf3fc2..a5f587aed26d 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}, @@ -907,6 +908,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::>() + }); for (expr_id, _) in body.exprs() { let ty = inference_result.expr_ty(expr_id); num_exprs += 1; @@ -963,9 +976,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!( @@ -975,24 +989,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) ); } } @@ -1066,9 +1081,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 {}", @@ -1077,24 +1093,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 e9ab06616033..29775590ea8c 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 { 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 Iterator for $range { + type Item = Idx; + fn next(&mut self) -> Option { 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 Debug for &T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + T::fmt(&**self, f) + } + } + impl 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::*;