diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 24df9b100ce95..806825405a7ea 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -527,6 +527,16 @@ impl NoArgsAttributeParser for RustcLintUntrackedQueryInformationParser { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintUntrackedQueryInformation; } +pub(crate) struct RustcLowPriorityImplParser; + +impl NoArgsAttributeParser for RustcLowPriorityImplParser { + const PATH: &[Symbol] = &[sym::rustc_low_priority_impl]; + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowList(&[Allow(Target::Impl { of_trait: true })]); + const STABILITY: AttributeStability = unstable!(rustc_attrs); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLowPriorityImpl; +} + pub(crate) struct RustcSimdMonomorphizeLaneLimitParser; impl SingleAttributeParser for RustcSimdMonomorphizeLaneLimitParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 1595d2f80ddc7..013aabe5f1dc3 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -306,6 +306,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 6a9cfeff49339..223ced7601822 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -255,6 +255,7 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ sym::may_dangle, sym::rustc_never_type_options, + sym::rustc_low_priority_impl, // ========================================================================== // Internal attributes: Runtime related: diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 17d00863d99d5..abc3b0436b0f4 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1520,6 +1520,8 @@ pub enum AttributeKind { /// Represents `#[rustc_lint_untracked_query_information]` RustcLintUntrackedQueryInformation, + RustcLowPriorityImpl, + /// Represents `#[rustc_macro_transparency]`. RustcMacroTransparency(Transparency), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index f5a6eeec07406..6a97d29f63db7 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -156,6 +156,7 @@ impl AttributeKind { RustcLintOptTy => Yes, RustcLintQueryInstability => Yes, RustcLintUntrackedQueryInformation => Yes, + RustcLowPriorityImpl => Yes, RustcMacroTransparency(..) => Yes, RustcMain => No, RustcMir(..) => Yes, diff --git a/compiler/rustc_hir_typeck/src/diagnostics.rs b/compiler/rustc_hir_typeck/src/diagnostics.rs index a5c39bd1584a1..662a5c8c62ac8 100644 --- a/compiler/rustc_hir_typeck/src/diagnostics.rs +++ b/compiler/rustc_hir_typeck/src/diagnostics.rs @@ -15,6 +15,7 @@ use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; use rustc_span::edition::{Edition, LATEST_STABLE_EDITION}; use rustc_span::{Ident, Span, Spanned, Symbol}; +use rustc_trait_selection::diagnostics::SourceKindSubdiag; use crate::FnCtxt; @@ -1320,3 +1321,14 @@ pub(crate) struct FloatLiteralF32Fallback { )] pub span: Option, } + +#[derive(Diagnostic)] +#[help("specify the types explicitly")] +#[note("in the future, the requirement `{$obligation}` will fail")] +#[diag("dependency on trait impl fallback")] +pub(crate) struct DependencyOnTraitImplFallback<'tcx, 'a> { + pub obligation_span: Span, + pub obligation: ty::Predicate<'tcx>, + #[subdiagnostic] + pub subdiagnostic: Option>, +} diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 0e34a6120b609..c5f4802f16cf9 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -81,6 +81,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// - Unconstrained floats are replaced with `f64`, except when there is a trait predicate /// `f32: From<{float}>`, in which case `f32` is used as the fallback instead. /// + /// - Non-numberics may get constrained if there are obligations which have multiple applicable + /// impls, all bot one of which are low priority. + /// /// - Non-numerics may get replaced with `()` or `!`, depending on how they /// were categorized by [`Self::calculate_diverging_fallback`], crate's /// edition, and the setting of `#![rustc_never_type_options(fallback = ...)]`. @@ -97,30 +100,28 @@ impl<'tcx> FnCtxt<'_, 'tcx> { diverging_fallback_ty: Ty<'tcx>, fallback_to_f32: &UnordSet, ) -> bool { - // Careful: we do NOT shallow-resolve `ty`. We know that `ty` - // is an unsolved variable, and we determine its fallback - // based solely on how it was created, not what other type - // variables it may have been unified with since then. - // - // The reason this matters is that other attempts at fallback - // may (in principle) conflict with this fallback, and we wish - // to generate a type error in that case. (However, this - // actually isn't true right now, because we're only using the - // builtin fallback rules. This would be true if we were using - // user-supplied fallbacks. But it's still useful to write the - // code to detect bugs.) + // Resolve is needed because both low priority impl fallback and diverging fallback might + // apply to the same variable. We want low priority impl fallback to take precedence, so it + // happens first. // - // (Note though that if we have a general type variable `?T` - // that is then unified with an integer type variable `?I` - // that ultimately never gets resolved to a special integral - // type, `?T` is not considered unsolved, but `?I` is. The - // same is true for float variables.) - let fallback = match ty.kind() { + // Yet, there might be a case where multiple related type variables require fallback, such + // that low priority impl fallback applies to the first, resolving both of them. In such + // cases low priority impl fallback wouldn't apply to the second, allowing diverging + // fallback to trigger. To prevent such cases, resolve the variables in `ty` first, to make + // sure it is still unresolved. + let fallback = match self.resolve_vars_if_possible(ty).kind() { _ if let Some(e) = self.tainted_by_errors() => Ty::new_error(self.tcx, e), ty::Infer(ty::IntVar(_)) => self.tcx.types.i32, ty::Infer(ty::FloatVar(vid)) if fallback_to_f32.contains(vid) => self.tcx.types.f32, ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64, - _ if diverging_fallback.contains(&ty) => { + + ty::Infer(ty::TyVar(_)) + if let Ok(_) = self.try_low_priority_impl_fallback_and_fcw(DUMMY_SP, ty) => + { + return true; + } + + ty::Infer(_) if diverging_fallback.contains(&ty) => { self.diverging_fallback_has_occurred.set(true); diverging_fallback_ty } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 9a1b1f8300957..cf9926b6fe785 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1,6 +1,7 @@ use std::collections::hash_map::Entry; use std::slice; +use itertools::Itertools; use rustc_abi::FieldIdx; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ @@ -21,6 +22,8 @@ use rustc_hir_analysis::hir_ty_lowering::{ }; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; +use rustc_infer::traits::ObligationCause; +use rustc_infer::traits::util::Elaboratable; use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, @@ -32,10 +35,16 @@ use rustc_middle::ty::{ }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; -use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; -use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; +use rustc_span::{DUMMY_SP, Span}; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; +use rustc_trait_selection::error_reporting::infer::need_type_info::{ + TypeAnnotationNeeded, find_infer_source, +}; +use rustc_trait_selection::error_reporting::traits::ambiguity::{ + CandidateSource, compute_applicable_impls_for_diagnostics, +}; use rustc_trait_selection::traits::{ self, NormalizeExt, ObligationCauseCode, StructurallyNormalizeExt, }; @@ -1531,20 +1540,160 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[cold] pub(crate) fn type_must_be_known_at_this_point(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - let guar = self.tainted_by_errors().unwrap_or_else(|| { - self.err_ctxt() - .emit_inference_failure_err( - self.body_def_id, - sp, + self.try_fallback_and_fcw_or_error(sp, ty) + } + + #[cold] + pub(crate) fn try_fallback_and_fcw_or_error(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + self.try_low_priority_impl_fallback_and_fcw(sp, ty).unwrap_or_else(|()| { + let guar = self.tainted_by_errors().unwrap_or_else(|| { + self.err_ctxt() + .emit_inference_failure_err( + self.body_def_id, + sp, + ty.into(), + TypeAnnotationNeeded::E0282, + true, + ) + .emit() + }); + let err = Ty::new_error(self.tcx, guar); + self.demand_suptype(sp, err, ty); + err + }) + } + + /// Tries to apply low priority impl fallback and emit a FCW if fallback has been applied. + /// + /// Returns `Ok(new_ty)` if fallback happened and `ty` was unified with `new_ty`. + /// + /// Panics if called while in a probe. + pub(crate) fn try_low_priority_impl_fallback_and_fcw( + &self, + sp: Span, + ty: Ty<'tcx>, + ) -> Result, ()> { + assert!(!self.fulfillment_cx.borrow().is_in_probe(&self.infcx)); + + // On new solver `obligations_referencing_infer_var` calls `resolve_vars_if_possible`, + // which can lead to inference progress. Use a probe so that this doesn't leak... + let obligations = + self.infcx.probe(|_| self.obligations_referencing_infer_var(ty.ty_vid().unwrap())); + + let fallback_opportunity = { + obligations + .into_iter() + .filter_map(|obligation| { + let clause = obligation.predicate().as_trait_clause()?; + let trait_obligation = obligation.with(self.tcx, clause); + + // `compute_applicable_impls_for_diagnostics` has a lot of side effects, put it in a probe. + let impls = self.infcx.probe(|_| { + compute_applicable_impls_for_diagnostics( + &self.infcx, + &trait_obligation, + false, + ) + }); + + // Below, we check that there is exactly one low priority impl. In combination + // with that, this checks that there is also at least one low priority impl. + if impls.len() <= 1 { + return None; + } + + // Exactly one candidate is not low priority + impls + .into_iter() + .filter(|candidate| !candidate.is_low_priority(self.tcx)) + .exactly_one() + .ok() + // ... and it's an impl rather than a param env candidate + .and_then(|candidate| match candidate { + CandidateSource::DefId(impl_def_id) => Some(impl_def_id), + CandidateSource::ParamEnv(_) => None, + }) + .map(|imp| (imp, obligation, trait_obligation)) + }) + .next() + }; + + let Some((impl_def_id, obligation, trait_obligation)) = fallback_opportunity else { + return Err(()); + }; + + self.infcx.enter_forall(trait_obligation.predicate, |placeholder_obligation| { + let obligation_trait_ref = + self.normalize(DUMMY_SP, Unnormalized::new_wip(placeholder_obligation.trait_ref)); + + let impl_args = self.infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = self + .tcx + .impl_trait_ref(impl_def_id) + .instantiate(self.tcx, impl_args) + .skip_norm_wip(); + let impl_trait_ref = self.normalize(DUMMY_SP, Unnormalized::new_wip(impl_trait_ref)); + + let cause = ObligationCause::dummy(); + + let extract_inference_diagnostics_data = + self.infcx.err_ctxt().extract_inference_diagnostics_data( ty.into(), - TypeAnnotationNeeded::E0282, - true, - ) - .emit() + ty::print::RegionHighlightMode::default(), + ); + + let source = + self.tcx.hir_node_by_def_id(self.body_def_id).body_id().and_then(|body_id| { + find_infer_source( + self.tcx, + &self.infcx, + &self.typeck_results.borrow(), + ty.into(), + body_id, + ) + }); + + _ = self + .at(&cause, self.param_env) + .eq(DefineOpaqueTypes::Yes, obligation_trait_ref, impl_trait_ref) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + .map(|()| { + let (hir_id, span) = source + .as_ref() + .map(|source| (source.hir_id, source.span)) + .unwrap_or_else(|| { + ( + self.tcx.local_def_id_to_hir_id(self.body_def_id), + trait_obligation.cause.span, + ) + }); + + let subdiagnostic = source.and_then(|source| { + source.kind.suggestion( + self.tcx, + &self.infcx, + self.body_def_id, + ty.into(), + &extract_inference_diagnostics_data, + &self.typeck_results.borrow(), + span, + ) + }); + + self.tcx.emit_node_span_lint( + lint::builtin::TRAIT_IMPL_FALLBACK, + hir_id, + span, + diagnostics::DependencyOnTraitImplFallback { + obligation_span: trait_obligation.cause.span, + obligation: obligation.predicate, + subdiagnostic, + }, + ) + }); }); - let err = Ty::new_error(self.tcx, guar); - self.demand_suptype(sp, err, ty); - err + + Ok(self.structurally_resolve_type(sp, ty)) } pub(crate) fn structurally_resolve_const( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index cfaa60231379e..f38098dae385d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -49,6 +49,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Returns a list of all obligations whose self type has been unified + /// with the unconstrained type `self_ty`. + #[instrument(skip(self), level = "debug")] + pub(crate) fn obligations_referencing_infer_var( + &self, + infer: ty::TyVid, + ) -> PredicateObligations<'tcx> { + if self.next_trait_solver() { + self.obligations_referencing_infer_var_next(infer) + } else { + let ty_var_root = self.root_var(infer); + let mut obligations = self.fulfillment_cx.borrow().pending_obligations(); + trace!("pending_obligations = {:#?}", obligations); + obligations.retain(|obligation| { + self.predicate_references_infer_var(obligation.predicate, ty_var_root) + }); + obligations + } + } + #[instrument(level = "debug", skip(self), ret)] fn predicate_has_self_ty( &self, @@ -83,6 +103,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self), ret)] + fn predicate_references_infer_var( + &self, + predicate: ty::Predicate<'tcx>, + expected_vid: ty::TyVid, + ) -> bool { + match predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => data + .trait_ref + .args + .iter() + .filter_map(|arg| arg.as_type()) + .any(|t| self.type_matches_expected_vid(t, expected_vid, UseSubtyping::Yes)), + ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { + if data.projection_term.kind.is_trait_projection() { + data.projection_term + .args + .iter() + .filter_map(|arg| arg.as_type()) + .any(|t| self.type_matches_expected_vid(t, expected_vid, UseSubtyping::Yes)) + } else { + false + } + } + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..)) + | ty::PredicateKind::DynCompatible(..) + | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) + | ty::PredicateKind::Ambiguous => false, + } + } + #[instrument(level = "debug", skip(self), ret)] fn type_matches_expected_vid( &self, @@ -145,6 +205,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { obligations_for_self_ty } + pub(crate) fn obligations_referencing_infer_var_next( + &self, + infer: ty::TyVid, + ) -> PredicateObligations<'tcx> { + // We only look at obligations which may reference the self type. + // This lookup uses the `sub_root` instead of the inference variable + // itself as that's slightly nicer to implement. It shouldn't really + // matter. + // + // This is really impactful when typechecking functions with a lot of + // stalled obligations, e.g. in the `wg-grammar` benchmark. + let sub_root_var = self.sub_unification_table_root_var(infer); + let obligations = self + .fulfillment_cx + .borrow() + .pending_obligations_potentially_referencing_sub_root(&self.infcx, sub_root_var); + debug!(?obligations); + let mut obligations_referencing_infer_var = PredicateObligations::new(); + for obligation in obligations { + let mut visitor = NestedObligationsReferencingInferVar { + fcx: self, + infer, + obligations_referencing_infer_var: &mut obligations_referencing_infer_var, + root_cause: &obligation.cause, + }; + + let goal = obligation.as_goal(); + self.visit_proof_tree(goal, &mut visitor); + } + + obligations_referencing_infer_var.retain_mut(|obligation| { + obligation.predicate = self.resolve_vars_if_possible(obligation.predicate); + !obligation.predicate.has_placeholders() + }); + obligations_referencing_infer_var + } + /// Only needed for the `From<{float}>` for `f32` type fallback. #[instrument(skip(self), level = "debug")] pub(crate) fn from_float_for_f32_root_vids(&self) -> UnordSet { @@ -209,6 +306,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } +struct NestedObligationsReferencingInferVar<'a, 'tcx> { + fcx: &'a FnCtxt<'a, 'tcx>, + infer: ty::TyVid, + root_cause: &'a ObligationCause<'tcx>, + obligations_referencing_infer_var: &'a mut PredicateObligations<'tcx>, +} + +impl<'tcx> ProofTreeVisitor<'tcx> for NestedObligationsReferencingInferVar<'_, 'tcx> { + fn span(&self) -> Span { + self.root_cause.span + } + + fn config(&self) -> InspectConfig { + // Using an intentionally low depth to minimize the chance of future + // breaking changes in case we adapt the approach later on. This also + // avoids any hangs for exponentially growing proof trees. + InspectConfig { max_depth: 5 } + } + + fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) { + // No need to walk into goal subtrees that certainly hold, since they + // wouldn't then be stalled on an infer var. + if inspect_goal.result() == Ok(Certainty::Yes) { + return; + } + + // We don't care about any pending goals which don't actually + // use the self type. + if !inspect_goal + .orig_values() + .iter() + .filter_map(|arg| arg.as_type()) + .any(|ty| self.fcx.type_matches_expected_vid(ty, self.infer, UseSubtyping::Yes)) + { + debug!(goal = ?inspect_goal.goal(), "goal does not mention self type"); + return; + } + + let tcx = self.fcx.tcx; + let goal = inspect_goal.goal(); + if self.fcx.predicate_references_infer_var(goal.predicate, self.infer) { + self.obligations_referencing_infer_var.push(traits::Obligation::new( + tcx, + self.root_cause.clone(), + goal.param_env, + goal.predicate, + )); + } + + // If there's a unique way to prove a given goal, recurse into + // that candidate. This means that for `impl Trait for () {}` + // and a `(): Trait` goal we recurse into the impl and look at + // the nested `?0: FnOnce(u32)` goal. + if let Some(candidate) = inspect_goal.unique_applicable_candidate() { + candidate.visit_nested_no_probe(self) + } + } +} + struct NestedObligationsForSelfTy<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, self_ty: ty::TyVid, diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index 38fc991fcfeb6..59e55e46a739e 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -127,6 +127,8 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { &mut self, infcx: &InferCtxt<'tcx>, ) -> PredicateObligations<'tcx>; + + fn is_in_probe(&self, infcx: &InferCtxt<'_>) -> bool; } pub trait FromSolverError<'tcx, E>: Debug + 'tcx { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index d38b1cf47bd6f..2510a5e6e876e 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4270,7 +4270,7 @@ declare_lint! { /// --> src/lib.rs:3:10 /// | /// 3 | #[derive(Trait)] - /// | ^^^^^ + /// | ^^^^^ /// ``` pub AMBIGUOUS_DERIVE_HELPERS, Warn, @@ -5578,3 +5578,51 @@ declare_lint! { "usage of `unsafe` code and other potentially unsound constructs", @eval_always = true } + +declare_lint! { + /// The `trait_impl_fallback` lint detects code depending on the compiler + /// to choose a particular trait implementation when multiple apply. + /// + /// ### Example + /// + /// ``` + /// #![feature(rustc_attrs)] + /// + /// struct X; + /// struct Y; + /// + /// #[rustc_low_priority_impl] + /// impl From for X { + /// fn from(Y: Y) -> X { + /// X + /// } + /// } + /// + /// fn main() { + /// let _: X = From::from(loop {} as _); + /// } + /// ``` + /// + /// ### Explanation + /// + /// When there is only one applicable implementation of a trait, `rustc` + /// uses it. While convenient, this leads to adding trait implementations + /// being a breaking change (as it can lead to the number of applicable + /// implementations to go from 1 to 2). + /// + /// To allow evolution of the standard library, `rustc` provides an + /// attribute to mark the newly added implementation as "low priority". + /// The compiler then chooses the old implementation over the "low + /// priority" ones, if there is exactly 1 normal priority applicabble + /// implementation. + /// + /// This is a [future-incompatible] lint, in the future we will remove the + /// low priority annotations, breaking code which depends on them. + pub TRAIT_IMPL_FALLBACK, + Warn, + "code depending on fallback to an older implementation of a trait", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError #0), + report_in_deps: true, + }; +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 6949090781cf6..6f297c120d79a 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -803,6 +803,7 @@ macro_rules! declare_lint_pass { }; } +/// Helper macro to create [`FutureIncompatibilityReason`]. #[macro_export] macro_rules! fcw { (FutureReleaseError # $issue_number: literal) => { diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 5bc56eae7755d..4b4bc4d45d7d6 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -9,7 +9,7 @@ use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::solve::inspect::ProbeKind; use rustc_type_ir::{ self as ty, Binder, FallibleTypeFolder, Interner, Movability, Mutability, TypeFoldable, - TypeSuperFoldable, Unnormalized, Upcast as _, elaborate, + TypeSuperFoldable, TypeVisitableExt, Unnormalized, Upcast as _, elaborate, }; use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::instrument; @@ -279,13 +279,14 @@ where } } -// Returns a binder of the tupled inputs types and output type from a builtin callable type. +/// Returns a binder of the tupled inputs types and output type from a builtin callable type. pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable( cx: I, self_ty: I::Ty, goal_kind: ty::ClosureKind, ) -> Result>, NoSolution> { match self_ty.kind() { + _ if self_ty.references_error() => Err(NoSolution), // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed. ty::FnDef(def_id, args) => { let sig = cx.fn_sig(def_id); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 7ae6005e9bc98..26786f718b0a4 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -364,6 +364,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::RustcLintOptTy => (), AttributeKind::RustcLintQueryInstability => (), AttributeKind::RustcLintUntrackedQueryInformation => (), + AttributeKind::RustcLowPriorityImpl => (), AttributeKind::RustcMacroTransparency(_) => (), AttributeKind::RustcMain => (), AttributeKind::RustcMir(_) => (), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index db2804a3f83bf..8c1f509c57ed5 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1812,6 +1812,7 @@ symbols! { rustc_lint_opt_ty, rustc_lint_query_instability, rustc_lint_untracked_query_information, + rustc_low_priority_impl, rustc_macro_transparency, rustc_main, rustc_mir, diff --git a/compiler/rustc_trait_selection/src/diagnostics.rs b/compiler/rustc_trait_selection/src/diagnostics.rs index ba89b0987659e..37fbcf8d3d0f3 100644 --- a/compiler/rustc_trait_selection/src/diagnostics.rs +++ b/compiler/rustc_trait_selection/src/diagnostics.rs @@ -188,9 +188,7 @@ pub(crate) struct AnnotationRequired<'a> { #[subdiagnostic] pub bad_label: Option>, #[subdiagnostic] - pub infer_subdiags: Vec>, - #[subdiagnostic] - pub multi_suggestions: Vec>, + pub subdiagnostic: Option>, } // Copy of `AnnotationRequired` for E0283 @@ -210,9 +208,7 @@ pub(crate) struct AmbiguousImpl<'a> { #[subdiagnostic] pub bad_label: Option>, #[subdiagnostic] - pub infer_subdiags: Vec>, - #[subdiagnostic] - pub multi_suggestions: Vec>, + pub subdiagnostic: Option>, } // Copy of `AnnotationRequired` for E0284 @@ -232,9 +228,7 @@ pub(crate) struct AmbiguousReturn<'a> { #[subdiagnostic] pub bad_label: Option>, #[subdiagnostic] - pub infer_subdiags: Vec>, - #[subdiagnostic] - pub multi_suggestions: Vec>, + pub subdiagnostic: Option>, } // Used when a better one isn't available @@ -265,7 +259,7 @@ pub(crate) struct InferenceBadError<'a> { } #[derive(Subdiagnostic)] -pub(crate) enum SourceKindSubdiag<'a> { +pub enum SourceKindSubdiag<'a> { #[suggestion( "{$kind -> [with_pattern] consider giving `{$name}` an explicit type @@ -294,7 +288,7 @@ pub(crate) enum SourceKindSubdiag<'a> { x_kind: &'static str, prefix_kind: UnderspecifiedArgKind, prefix: &'a str, - arg_name: String, + arg_name: &'a str, }, #[label( "cannot infer {$is_type -> @@ -308,7 +302,7 @@ pub(crate) enum SourceKindSubdiag<'a> { *[false] parameter {$param_name} }" )] - GenericLabel { + Generic { #[primary_span] span: Span, is_type: bool, @@ -316,48 +310,9 @@ pub(crate) enum SourceKindSubdiag<'a> { parent_exists: bool, parent_prefix: String, parent_name: String, + #[subdiagnostic] + suggestion: Option, }, - #[suggestion( - "consider specifying the generic {$arg_count -> - [one] argument - *[other] arguments - }", - style = "verbose", - code = "::<{args}>", - applicability = "has-placeholders" - )] - GenericSuggestion { - #[primary_span] - span: Span, - arg_count: usize, - args: String, - }, - #[suggestion( - "consider specifying a concrete type for the type parameter `{$param}`", - style = "verbose", - code = "::", - applicability = "has-placeholders" - )] - GenericTypeSuggestion { - #[primary_span] - span: Span, - param: String, - }, - #[suggestion( - "consider specifying a const for the const parameter `{$param}`", - style = "verbose", - code = "::", - applicability = "has-placeholders" - )] - ConstGenericSuggestion { - #[primary_span] - span: Span, - param: String, - }, -} - -#[derive(Subdiagnostic)] -pub(crate) enum SourceKindMultiSuggestion<'a> { #[multipart_suggestion( "try using a fully qualified path to specify the expected types", style = "verbose", @@ -386,7 +341,7 @@ pub(crate) enum SourceKindMultiSuggestion<'a> { }, } -impl<'a> SourceKindMultiSuggestion<'a> { +impl<'a> SourceKindSubdiag<'a> { pub(crate) fn new_fully_qualified( span: Span, def_path: String, @@ -419,6 +374,50 @@ impl<'a> SourceKindMultiSuggestion<'a> { } } +/// Suggestion to specify generic parameter(s) via `::<>`. +#[derive(Subdiagnostic)] +pub enum SpecifyGenericParamsSuggestion { + #[suggestion( + "consider specifying the generic {$arg_count -> + [one] argument + *[other] arguments + }", + style = "verbose", + code = "::<{args}>", + applicability = "has-placeholders" + )] + GenericSuggestion { + #[primary_span] + span: Span, + arg_count: usize, + args: String, + }, + // FIXME: there is really no reason for `Generic{Type,Const}Suggestion` to be separate + // variants, all of these should be unified. + #[suggestion( + "consider specifying a concrete type for the type parameter `{$param}`", + style = "verbose", + code = "::", + applicability = "has-placeholders" + )] + GenericTypeSuggestion { + #[primary_span] + span: Span, + param: String, + }, + #[suggestion( + "consider specifying a const for the const parameter `{$param}`", + style = "verbose", + code = "::", + applicability = "has-placeholders" + )] + ConstGenericSuggestion { + #[primary_span] + span: Span, + param: String, + }, +} + pub(crate) enum RegionOriginNote<'a> { Plain { span: Span, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 624aef89911f7..13b565f71d29e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -8,7 +8,8 @@ use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ - self as hir, Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource, PatKind, + self as hir, Body, BodyId, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource, + PatKind, }; use rustc_middle::bug; use rustc_middle::hir::nested_filter; @@ -24,12 +25,29 @@ use tracing::{debug, instrument, warn}; use super::nice_region_error::placeholder_error::Highlighted; use crate::diagnostics::{ - AmbiguousImpl, AmbiguousReturn, AnnotationRequired, InferenceBadError, - SourceKindMultiSuggestion, SourceKindSubdiag, + AmbiguousImpl, AmbiguousReturn, AnnotationRequired, InferenceBadError, SourceKindSubdiag, + SpecifyGenericParamsSuggestion, }; use crate::error_reporting::TypeErrCtxt; use crate::infer::{InferCtxt, TyOrConstInferVar}; +pub fn find_infer_source<'tcx>( + tcx: TyCtxt<'tcx>, + infcx: &'_ InferCtxt<'tcx>, + typeck_results: &'_ TypeckResults<'tcx>, + target: Term<'tcx>, + body: BodyId, +) -> Option> { + let mut visitor = FindInferSourceVisitor::new(tcx, infcx, typeck_results, target, None); + + let body = tcx.hir_body(body); + let expr = body.value; + + visitor.visit_expr(expr); + + visitor.infer_source +} + pub enum TypeAnnotationNeeded { /// ```compile_fail,E0282 /// let x; @@ -448,8 +466,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let source_kind = "other"; let source_name = ""; let failure_span = None; - let infer_subdiags = Vec::new(); - let multi_suggestions = Vec::new(); + let subdiagnostic = None; let bad_label = Some(arg_data.make_bad_error(span)); match error_code { TypeAnnotationNeeded::E0282 => self.dcx().create_err(AnnotationRequired { @@ -457,8 +474,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { source_kind, source_name, failure_span, - infer_subdiags, - multi_suggestions, + subdiagnostic, bad_label, }), TypeAnnotationNeeded::E0283 => self.dcx().create_err(AmbiguousImpl { @@ -466,8 +482,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { source_kind, source_name, failure_span, - infer_subdiags, - multi_suggestions, + subdiagnostic, bad_label, }), TypeAnnotationNeeded::E0284 => self.dcx().create_err(AmbiguousReturn { @@ -475,8 +490,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { source_kind, source_name, failure_span, - infer_subdiags, - multi_suggestions, + subdiagnostic, bad_label, }), } @@ -521,7 +535,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return self.bad_inference_failure_err(failure_span, arg_data, error_code); }; - let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, term, ty); + // FIXME: use find_infer_source(tcx, infcx, typeck_results, target, body) + let mut local_visitor = + FindInferSourceVisitor::new(self.tcx, self.infcx, typeck_results, term, ty); if let Some(body) = self.tcx.hir_maybe_body_owned_by(self.tcx.typeck_root_def_id_local(body_def_id)) { @@ -529,7 +545,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { local_visitor.visit_expr(expr); } - let Some(InferSource { span, kind }) = local_visitor.infer_source else { + let Some(InferSource { span, kind, hir_id: _ }) = local_visitor.infer_source else { let silence = if let DefKind::AssocFn = self.tcx.def_kind(body_def_id) && let parent = self.tcx.local_parent(body_def_id) && self.tcx.is_automatically_derived(parent.to_def_id()) @@ -562,189 +578,23 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { None }; - let mut infer_subdiags = Vec::new(); - let mut multi_suggestions = Vec::new(); - match kind { - InferSourceKind::LetBinding { insert_span, pattern_name, ty, def_id } => { - infer_subdiags.push(SourceKindSubdiag::LetLike { - span: insert_span, - name: pattern_name.map(|name| name.to_string()).unwrap_or_else(String::new), - x_kind: arg_data.where_x_is_kind(self.infcx, ty), - prefix_kind: arg_data.kind.clone(), - prefix: arg_data.kind.try_get_prefix().unwrap_or_default(), - arg_name: arg_data.name, - kind: if pattern_name.is_some() { "with_pattern" } else { "other" }, - type_name: ty_to_string(self, ty, def_id), - }); - } - InferSourceKind::ClosureArg { insert_span, ty, .. } => { - infer_subdiags.push(SourceKindSubdiag::LetLike { - span: insert_span, - name: String::new(), - x_kind: arg_data.where_x_is_kind(self.infcx, ty), - prefix_kind: arg_data.kind.clone(), - prefix: arg_data.kind.try_get_prefix().unwrap_or_default(), - arg_name: arg_data.name, - kind: "closure", - type_name: ty_to_string(self, ty, None), - }); - } - InferSourceKind::GenericArg { - insert_span, - argument_index, - generics_def_id, - def_id: _, - generic_args, - have_turbofish, - hir_id, - } => { - let generics = self.tcx.generics_of(generics_def_id); - let is_type = term.as_type().is_some(); - - let (parent_exists, parent_prefix, parent_name) = - InferenceDiagnosticsParentData::for_parent_def_id(self.tcx, generics_def_id) - .map_or((false, String::new(), String::new()), |parent| { - (true, parent.prefix.to_string(), parent.name) - }); - - let param = &generics.own_params[argument_index]; - let param_name = param.name.to_string(); - - infer_subdiags.push(SourceKindSubdiag::GenericLabel { - span, - is_type, - param_name: param_name.clone(), - parent_exists, - parent_prefix, - parent_name, - }); - - let mut used_fallback = false; - let args = if self.tcx.get_diagnostic_item(sym::iterator_collect_fn) - == Some(generics_def_id) - { - if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(hir_id) - && let hir::ExprKind::Call(expr, _args) = expr.kind - && let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind - && let Res::Def(DefKind::AssocFn, def_id) = path.res - && let Some(try_trait) = self.tcx.lang_items().try_trait() - && try_trait == self.tcx.parent(def_id) - && let DefKind::Fn | DefKind::AssocFn = - self.tcx.def_kind(body_def_id.to_def_id()) - && let ret = self - .tcx - .fn_sig(body_def_id.to_def_id()) - .instantiate_identity() - .skip_binder() - .output() - && let ty::Adt(adt, _args) = ret.kind() - && let Some(sym::Option | sym::Result) = - self.tcx.get_diagnostic_name(adt.did()) - { - if let Some(sym::Option) = self.tcx.get_diagnostic_name(adt.did()) { - "Option<_>".to_string() - } else { - "Result<_, _>".to_string() - } - } else { - "Vec<_>".to_string() - } - } else { - let mut p = fmt_printer(self, Namespace::TypeNS); - p.comma_sep(generic_args.iter().copied().map(|arg| { - if arg.is_suggestable(self.tcx, true) { - used_fallback = true; - return arg; - } - match arg.kind() { - GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), - GenericArgKind::Type(_) => self.next_ty_var(DUMMY_SP).into(), - GenericArgKind::Const(_) => self.next_const_var(DUMMY_SP).into(), - } - })) - .unwrap(); - p.into_buffer() - }; - - if !have_turbofish { - if generic_args.len() == 1 && used_fallback { - match param.kind { - GenericParamDefKind::Type { .. } => { - infer_subdiags.push(SourceKindSubdiag::GenericTypeSuggestion { - span: insert_span, - param: param_name, - }); - } - GenericParamDefKind::Const { .. } => { - infer_subdiags.push(SourceKindSubdiag::ConstGenericSuggestion { - span: insert_span, - param: param_name, - }); - } - GenericParamDefKind::Lifetime => { - bug!("unexpected lifetime") - } - } - } else { - infer_subdiags.push(SourceKindSubdiag::GenericSuggestion { - span: insert_span, - arg_count: generic_args.len(), - args, - }); - } - } - } - InferSourceKind::FullyQualifiedMethodCall { receiver, successor, args, def_id } => { - let placeholder = Some(self.next_ty_var(DUMMY_SP)); - if let Some(args) = args.make_suggestable(self.infcx.tcx, true, placeholder) { - let mut p = fmt_printer(self, Namespace::ValueNS); - p.print_def_path(def_id, args).unwrap(); - let def_path = p.into_buffer(); - - // We only care about whether we have to add `&` or `&mut ` for now. - // This is the case if the last adjustment is a borrow and the - // first adjustment was not a builtin deref. - let adjustment = match typeck_results.expr_adjustments(receiver) { - [ - Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: _ }, - .., - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ }, - ] => "", - [ - .., - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mut_)), target: _ }, - ] => hir::Mutability::from(*mut_).ref_prefix_str(), - _ => "", - }; + let subdiagnostic = kind.suggestion( + self.tcx, + self.infcx, + body_def_id, + term, + &arg_data, + typeck_results, + span, + ); - multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified( - receiver.span, - def_path, - adjustment, - successor, - )); - } - } - InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => { - let placeholder = Some(self.next_ty_var(DUMMY_SP)); - if let Some(ty) = ty.make_suggestable(self.infcx.tcx, true, placeholder) { - let ty_info = ty_to_string(self, ty, None); - multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return( - ty_info, - data, - should_wrap_expr, - )); - } - } - } let mut err = match error_code { TypeAnnotationNeeded::E0282 => self.dcx().create_err(AnnotationRequired { span, source_kind, source_name: &name, failure_span, - infer_subdiags, - multi_suggestions, + subdiagnostic, bad_label: None, }), TypeAnnotationNeeded::E0283 => self.dcx().create_err(AmbiguousImpl { @@ -752,8 +602,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { source_kind, source_name: &name, failure_span, - infer_subdiags, - multi_suggestions, + subdiagnostic, bad_label: None, }), TypeAnnotationNeeded::E0284 => self.dcx().create_err(AmbiguousReturn { @@ -761,8 +610,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { source_kind, source_name: &name, failure_span, - infer_subdiags, - multi_suggestions, + subdiagnostic, bad_label: None, }), }; @@ -776,13 +624,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } #[derive(Debug)] -struct InferSource<'tcx> { - span: Span, - kind: InferSourceKind<'tcx>, +pub struct InferSource<'tcx> { + pub span: Span, + pub hir_id: HirId, + pub kind: InferSourceKind<'tcx>, } #[derive(Debug)] -enum InferSourceKind<'tcx> { +pub enum InferSourceKind<'tcx> { LetBinding { insert_span: Span, pattern_name: Option, @@ -867,6 +716,188 @@ impl<'tcx> InferSourceKind<'tcx> { } } } + + pub fn suggestion<'local>( + &self, + tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, + body_def_id: LocalDefId, + term: Term<'tcx>, + arg_data: &'local InferenceDiagnosticsData, + typeck_results: &TypeckResults<'tcx>, + span: Span, + ) -> Option> + where + 'tcx: 'local, + { + let subdiag = match *self { + InferSourceKind::LetBinding { insert_span, pattern_name, ty, def_id } => { + SourceKindSubdiag::LetLike { + span: insert_span, + name: pattern_name.map(|name| name.to_string()).unwrap_or_else(String::new), + x_kind: arg_data.where_x_is_kind(infcx, ty), + prefix_kind: arg_data.kind.clone(), + prefix: arg_data.kind.try_get_prefix().unwrap_or_default(), + arg_name: &arg_data.name, + kind: if pattern_name.is_some() { "with_pattern" } else { "other" }, + type_name: ty_to_string(infcx, ty, def_id), + } + } + InferSourceKind::ClosureArg { insert_span, ty, .. } => SourceKindSubdiag::LetLike { + span: insert_span, + name: String::new(), + x_kind: arg_data.where_x_is_kind(infcx, ty), + prefix_kind: arg_data.kind.clone(), + prefix: arg_data.kind.try_get_prefix().unwrap_or_default(), + arg_name: &arg_data.name, + kind: "closure", + type_name: ty_to_string(infcx, ty, None), + }, + InferSourceKind::GenericArg { + insert_span, + argument_index, + generics_def_id, + def_id: _, + generic_args, + have_turbofish, + hir_id, + } => { + let generics = tcx.generics_of(generics_def_id); + let is_type = term.as_type().is_some(); + + let (parent_exists, parent_prefix, parent_name) = + InferenceDiagnosticsParentData::for_parent_def_id(tcx, generics_def_id) + .map_or((false, String::new(), String::new()), |parent| { + (true, parent.prefix.to_string(), parent.name) + }); + + let param = &generics.own_params[argument_index]; + let param_name = param.name.to_string(); + + let mut used_fallback = false; + let args = if tcx.get_diagnostic_item(sym::iterator_collect_fn) + == Some(generics_def_id) + { + if let hir::Node::Expr(expr) = tcx.parent_hir_node(hir_id) + && let hir::ExprKind::Call(expr, _args) = expr.kind + && let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind + && let Res::Def(DefKind::AssocFn, def_id) = path.res + && let Some(try_trait) = tcx.lang_items().try_trait() + && try_trait == tcx.parent(def_id) + && let DefKind::Fn | DefKind::AssocFn = + tcx.def_kind(body_def_id.to_def_id()) + && let ret = tcx + .fn_sig(body_def_id.to_def_id()) + .instantiate_identity() + .skip_binder() + .output() + && let ty::Adt(adt, _args) = ret.kind() + && let Some(sym::Option | sym::Result) = tcx.get_diagnostic_name(adt.did()) + { + if let Some(sym::Option) = tcx.get_diagnostic_name(adt.did()) { + "Option<_>".to_string() + } else { + "Result<_, _>".to_string() + } + } else { + "Vec<_>".to_string() + } + } else { + let mut p = fmt_printer(infcx, Namespace::TypeNS); + p.comma_sep(generic_args.iter().copied().map(|arg| { + if arg.is_suggestable(tcx, true) { + used_fallback = true; + return arg; + } + match arg.kind() { + GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), + GenericArgKind::Type(_) => infcx.next_ty_var(DUMMY_SP).into(), + GenericArgKind::Const(_) => infcx.next_const_var(DUMMY_SP).into(), + } + })) + .unwrap(); + p.into_buffer() + }; + + let suggestion = if have_turbofish { + None + } else if generic_args.len() == 1 && used_fallback { + match param.kind { + GenericParamDefKind::Type { .. } => { + Some(SpecifyGenericParamsSuggestion::GenericTypeSuggestion { + span: insert_span, + param: param_name.clone(), + }) + } + GenericParamDefKind::Const { .. } => { + Some(SpecifyGenericParamsSuggestion::ConstGenericSuggestion { + span: insert_span, + param: param_name.clone(), + }) + } + GenericParamDefKind::Lifetime => { + bug!("unexpected lifetime") + } + } + } else { + Some(SpecifyGenericParamsSuggestion::GenericSuggestion { + span: insert_span, + arg_count: generic_args.len(), + args, + }) + }; + + SourceKindSubdiag::Generic { + span, + is_type, + param_name, + parent_exists, + parent_prefix, + parent_name, + suggestion, + } + } + InferSourceKind::FullyQualifiedMethodCall { receiver, successor, args, def_id } => { + let placeholder = Some(infcx.next_ty_var(DUMMY_SP)); + let args = args.make_suggestable(tcx, true, placeholder)?; + + let mut p = fmt_printer(infcx, Namespace::ValueNS); + p.print_def_path(def_id, args).unwrap(); + let def_path = p.into_buffer(); + + // We only care about whether we have to add `&` or `&mut ` for now. + // This is the case if the last adjustment is a borrow and the + // first adjustment was not a builtin deref. + let adjustment = match typeck_results.expr_adjustments(receiver) { + [ + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: _ }, + .., + Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ }, + ] => "", + [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mut_)), target: _ }] => { + hir::Mutability::from(*mut_).ref_prefix_str() + } + _ => "", + }; + + SourceKindSubdiag::new_fully_qualified( + receiver.span, + def_path, + adjustment, + successor, + ) + } + InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => { + let placeholder = Some(infcx.next_ty_var(DUMMY_SP)); + + let ty = ty.make_suggestable(tcx, true, placeholder)?; + let ty_info = ty_to_string(infcx, ty, None); + SourceKindSubdiag::new_closure_return(ty_info, data, should_wrap_expr) + } + }; + + Some(subdiag) + } } #[derive(Debug)] @@ -886,7 +917,8 @@ struct InsertableGenericArgs<'tcx> { /// While doing so, the currently best spot is stored in `infer_source`. /// For details on how we rank spots, see [Self::source_cost] struct FindInferSourceVisitor<'a, 'tcx> { - tecx: &'a TypeErrCtxt<'a, 'tcx>, + tcx: TyCtxt<'tcx>, + infcx: &'a InferCtxt<'tcx>, typeck_results: &'a TypeckResults<'tcx>, target: Term<'tcx>, @@ -899,13 +931,16 @@ struct FindInferSourceVisitor<'a, 'tcx> { impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { fn new( - tecx: &'a TypeErrCtxt<'a, 'tcx>, + tcx: TyCtxt<'tcx>, + infcx: &'a InferCtxt<'tcx>, + typeck_results: &'a TypeckResults<'tcx>, target: Term<'tcx>, ty: Option>, ) -> Self { FindInferSourceVisitor { - tecx, + tcx, + infcx, typeck_results, target, @@ -957,7 +992,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { } // The sources are listed in order of preference here. - let tcx = self.tecx.tcx; + let tcx = self.tcx; let ctx = CostCtxt { tcx }; match source.kind { InferSourceKind::LetBinding { ty, .. } => ctx.ty_cost(ty), @@ -1008,12 +1043,12 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { fn node_args_opt(&self, hir_id: HirId) -> Option> { let args = self.typeck_results.node_args_opt(hir_id); - self.tecx.resolve_vars_if_possible(args) + self.infcx.resolve_vars_if_possible(args) } fn opt_node_type(&self, hir_id: HirId) -> Option> { let ty = self.typeck_results.node_type_opt(hir_id); - self.tecx.resolve_vars_if_possible(ty) + self.infcx.resolve_vars_if_possible(ty) } // Check whether this generic argument is the inference variable we @@ -1028,8 +1063,8 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { use ty::{Infer, TyVar}; match (inner_ty.kind(), target_ty.kind()) { (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => { - self.tecx.sub_unification_table_root_var(a_vid) - == self.tecx.sub_unification_table_root_var(b_vid) + self.infcx.sub_unification_table_root_var(a_vid) + == self.infcx.sub_unification_table_root_var(b_vid) } _ => false, } @@ -1039,7 +1074,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { ( ty::ConstKind::Infer(ty::InferConst::Var(a_vid)), ty::ConstKind::Infer(ty::InferConst::Var(b_vid)), - ) => self.tecx.root_const_var(a_vid) == self.tecx.root_const_var(b_vid), + ) => self.infcx.root_const_var(a_vid) == self.infcx.root_const_var(b_vid), _ => false, } } @@ -1094,7 +1129,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { &self, expr: &'tcx hir::Expr<'tcx>, ) -> Box> + 'a> { - let tcx = self.tecx.tcx; + let tcx = self.tcx; match expr.kind { hir::ExprKind::Path(ref path) => { if let Some(args) = self.node_args_opt(expr.hir_id) { @@ -1160,7 +1195,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { path: &'tcx hir::Path<'tcx>, args: GenericArgsRef<'tcx>, ) -> impl Iterator> + 'tcx { - let tcx = self.tecx.tcx; + let tcx = self.tcx; let have_turbofish = path.segments.iter().any(|segment| { segment.args.is_some_and(|args| args.args.iter().any(|arg| arg.is_ty_or_const())) }); @@ -1214,7 +1249,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { args: GenericArgsRef<'tcx>, qpath: &'tcx hir::QPath<'tcx>, ) -> Box> + 'a> { - let tcx = self.tecx.tcx; + let tcx = self.tcx; match qpath { hir::QPath::Resolved(_self_ty, path) => { Box::new(self.resolved_path_inferred_arg_iter(path, args)) @@ -1286,7 +1321,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.tecx.tcx + self.tcx } fn visit_local(&mut self, local: &'tcx LetStmt<'tcx>) { @@ -1330,6 +1365,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { { self.update_infer_source(InferSource { span: local.pat.span, + hir_id: local.hir_id, kind: InferSourceKind::LetBinding { insert_span: local.pat.span.shrink_to_hi(), pattern_name: local.pat.simple_ident(), @@ -1360,6 +1396,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { if self.generic_arg_contains_target(param_ty.into()) { self.update_infer_source(InferSource { span: param.pat.span, + hir_id: body.id().hir_id, kind: InferSourceKind::ClosureArg { insert_span: param.pat.span.shrink_to_hi(), ty: param_ty, @@ -1373,7 +1410,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - let tcx = self.tecx.tcx; + let tcx = self.tcx; match expr.kind { // When encountering `func(arg)` first look into `arg` and then `func`, // as `arg` is "more specific". @@ -1401,7 +1438,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { .iter() .position(|&arg| self.generic_arg_contains_target(arg)) { - let args = self.tecx.resolve_vars_if_possible(args); + let args = self.infcx.resolve_vars_if_possible(args); let generic_args = &generics.own_args_no_defaults(tcx, args)[generics.own_counts().lifetimes..]; let span = match expr.kind { @@ -1425,6 +1462,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { self.update_infer_source(InferSource { span, + hir_id: expr.hir_id, kind: InferSourceKind::GenericArg { insert_span, argument_index, @@ -1446,7 +1484,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { { let output = args.as_closure().sig().output().skip_binder(); if self.generic_arg_contains_target(output.into()) { - let body = self.tecx.tcx.hir_body(body); + let body = self.tcx.hir_body(body); let should_wrap_expr = if matches!(body.value.kind, ExprKind::Block(..)) { None } else { @@ -1454,6 +1492,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { }; self.update_infer_source(InferSource { span: fn_decl_span, + hir_id: expr.hir_id, kind: InferSourceKind::ClosureReturn { ty: output, data: &fn_decl.output, @@ -1474,7 +1513,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { && let Some(args) = self.node_args_opt(expr.hir_id) && args.iter().any(|arg| self.generic_arg_contains_target(arg)) && let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) - && self.tecx.tcx.trait_of_assoc(def_id).is_some() + && self.tcx.trait_of_assoc(def_id).is_some() && !has_impl_trait(def_id) // FIXME(fn_delegation): In delegation item argument spans are equal to last path // segment. This leads to ICE's when emitting `multipart_suggestion`. @@ -1482,9 +1521,10 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { { let successor = method_args.get(0).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo())); - let args = self.tecx.resolve_vars_if_possible(args); + let args = self.infcx.resolve_vars_if_possible(args); self.update_infer_source(InferSource { span: path.ident.span, + hir_id: expr.hir_id, kind: InferSourceKind::FullyQualifiedMethodCall { receiver, successor, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 40bae03a649db..b63f1b28055cc 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -1,11 +1,10 @@ use std::ops::ControlFlow; use rustc_errors::{Applicability, Diag, E0283, E0284, E0790, MultiSpan, struct_span_code_err}; -use rustc_hir as hir; -use rustc_hir::LangItem; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_hir::intravisit::Visitor as _; +use rustc_hir::{self as hir, LangItem, find_attr}; use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt}; use rustc_infer::traits::util::elaborate; use rustc_infer::traits::{ @@ -23,12 +22,19 @@ use crate::error_reporting::traits::{FindExprBySpan, to_pretty_impl_header}; use crate::traits::ObligationCtxt; use crate::traits::query::evaluate_obligation::InferCtxtExt; -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub enum CandidateSource { DefId(DefId), ParamEnv(Span), } +impl CandidateSource { + #[instrument(target = "meow", level = "debug", skip(tcx), ret)] + pub fn is_low_priority(self, tcx: TyCtxt<'_>) -> bool { + matches!(self, Self::DefId(def_id) if find_attr!(tcx, def_id, RustcLowPriorityImpl)) + } +} + pub fn compute_applicable_impls_for_diagnostics<'tcx>( infcx: &InferCtxt<'tcx>, obligation: &PolyTraitObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 4b80d22d6bb49..68a09655ee520 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -349,6 +349,10 @@ where .map(|(o, _)| o) .collect() } + + fn is_in_probe(&self, infcx: &InferCtxt<'_>) -> bool { + self.usable_in_snapshot != infcx.num_open_snapshots() + } } pub enum NextSolverError<'tcx> { diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index aa963b1bca749..456c7d5d1e687 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -273,6 +273,10 @@ where fn pending_obligations(&self) -> PredicateObligations<'tcx> { self.predicates.map_pending_obligations(|o| o.obligation.clone()) } + + fn is_in_probe(&self, infcx: &InferCtxt<'_>) -> bool { + self.usable_in_snapshot != infcx.num_open_snapshots() + } } struct FulfillProcessor<'a, 'tcx> { diff --git a/tests/ui/traits/low_priority_impls/from.implbreaking-current.stderr b/tests/ui/traits/low_priority_impls/from.implbreaking-current.stderr new file mode 100644 index 0000000000000..df4ed1164d453 --- /dev/null +++ b/tests/ui/traits/low_priority_impls/from.implbreaking-current.stderr @@ -0,0 +1,24 @@ +error[E0283]: type annotations needed + --> $DIR/from.rs:46:16 + | +LL | let _: X = Meow::f(<_>::default()); + | ^^^^^^^ -------------- type must be known at this point + | | + | cannot infer type of the type parameter `T` declared on the trait `Meow` + | +note: multiple `impl`s satisfying `X: Meow<_>` found + --> $DIR/from.rs:31:1 + | +LL | impl Meow for T { + | ^^^^^^^^^^^^^^^^^^^^^ +... +LL | impl Meow for X { + | ^^^^^^^^^^^^^^^^^^ +help: consider specifying a concrete type for the type parameter `T` + | +LL | let _: X = Meow::::f(<_>::default()); + | ++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/low_priority_impls/from.implbreaking-next.stderr b/tests/ui/traits/low_priority_impls/from.implbreaking-next.stderr new file mode 100644 index 0000000000000..df4ed1164d453 --- /dev/null +++ b/tests/ui/traits/low_priority_impls/from.implbreaking-next.stderr @@ -0,0 +1,24 @@ +error[E0283]: type annotations needed + --> $DIR/from.rs:46:16 + | +LL | let _: X = Meow::f(<_>::default()); + | ^^^^^^^ -------------- type must be known at this point + | | + | cannot infer type of the type parameter `T` declared on the trait `Meow` + | +note: multiple `impl`s satisfying `X: Meow<_>` found + --> $DIR/from.rs:31:1 + | +LL | impl Meow for T { + | ^^^^^^^^^^^^^^^^^^^^^ +... +LL | impl Meow for X { + | ^^^^^^^^^^^^^^^^^^ +help: consider specifying a concrete type for the type parameter `T` + | +LL | let _: X = Meow::::f(<_>::default()); + | ++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/low_priority_impls/from.lowprio-current.stderr b/tests/ui/traits/low_priority_impls/from.lowprio-current.stderr new file mode 100644 index 0000000000000..400a2b4f6e23e --- /dev/null +++ b/tests/ui/traits/low_priority_impls/from.lowprio-current.stderr @@ -0,0 +1,36 @@ + WARN rustc_trait_selection::error_reporting::infer::need_type_info resolved ty var in error message +warning: dependency on trait impl fallback + --> $DIR/from.rs:46:16 + | +LL | let _: X = Meow::f(<_>::default()); + | ^^^^^^^ cannot infer type of the type parameter `T` declared on the trait `Meow` + | + = help: specify the types explicitly + = note: in the future, the requirement `X: Meow<_>` will fail + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #0 + = note: `#[warn(trait_impl_fallback)]` on by default +help: consider specifying a concrete type for the type parameter `T` + | +LL | let _: X = Meow::::f(<_>::default()); + | ++++++++++++++ + +warning: 1 warning emitted + +Future incompatibility report: Future breakage diagnostic: +warning: dependency on trait impl fallback + --> $DIR/from.rs:46:16 + | +LL | let _: X = Meow::f(<_>::default()); + | ^^^^^^^ cannot infer type of the type parameter `T` declared on the trait `Meow` + | + = help: specify the types explicitly + = note: in the future, the requirement `X: Meow<_>` will fail + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #0 + = note: `#[warn(trait_impl_fallback)]` on by default +help: consider specifying a concrete type for the type parameter `T` + | +LL | let _: X = Meow::::f(<_>::default()); + | ++++++++++++++ + diff --git a/tests/ui/traits/low_priority_impls/from.lowprio-next.stderr b/tests/ui/traits/low_priority_impls/from.lowprio-next.stderr new file mode 100644 index 0000000000000..400a2b4f6e23e --- /dev/null +++ b/tests/ui/traits/low_priority_impls/from.lowprio-next.stderr @@ -0,0 +1,36 @@ + WARN rustc_trait_selection::error_reporting::infer::need_type_info resolved ty var in error message +warning: dependency on trait impl fallback + --> $DIR/from.rs:46:16 + | +LL | let _: X = Meow::f(<_>::default()); + | ^^^^^^^ cannot infer type of the type parameter `T` declared on the trait `Meow` + | + = help: specify the types explicitly + = note: in the future, the requirement `X: Meow<_>` will fail + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #0 + = note: `#[warn(trait_impl_fallback)]` on by default +help: consider specifying a concrete type for the type parameter `T` + | +LL | let _: X = Meow::::f(<_>::default()); + | ++++++++++++++ + +warning: 1 warning emitted + +Future incompatibility report: Future breakage diagnostic: +warning: dependency on trait impl fallback + --> $DIR/from.rs:46:16 + | +LL | let _: X = Meow::f(<_>::default()); + | ^^^^^^^ cannot infer type of the type parameter `T` declared on the trait `Meow` + | + = help: specify the types explicitly + = note: in the future, the requirement `X: Meow<_>` will fail + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #0 + = note: `#[warn(trait_impl_fallback)]` on by default +help: consider specifying a concrete type for the type parameter `T` + | +LL | let _: X = Meow::::f(<_>::default()); + | ++++++++++++++ + diff --git a/tests/ui/traits/low_priority_impls/from.rs b/tests/ui/traits/low_priority_impls/from.rs new file mode 100644 index 0000000000000..cc5de589d520b --- /dev/null +++ b/tests/ui/traits/low_priority_impls/from.rs @@ -0,0 +1,53 @@ +// This test tests the basic usage of `#[rustc_low_priority_impl]`: +// - There was only a single applicable impl (identity `Meow` impl) +// - Adding a second impl (`Meow for X`) breaks uses of `Meow` depending on "1 impl "rule"" +// - Marking the second impl as low priority fixes the issue, but introduces a FCW +// +// This test has revisions of [noimpl, implbreaking, lowprio] x [current, next]. +// +// ignore-tidy-linelength +//@ revisions: noimpl-current implbreaking-current lowprio-current noimpl-next implbreaking-next lowprio-next +// +//@[noimpl-next] compile-flags: -Znext-solver +//@[implbreaking-next] compile-flags: -Znext-solver +//@[lowprio-next] compile-flags: -Znext-solver +// +//@[noimpl-current] check-pass +//@[lowprio-current] check-pass +//@[noimpl-next] check-pass +//@[lowprio-next] check-pass + +#![feature(rustc_attrs)] + +#[derive(Default)] +struct X; +#[derive(Default)] +struct Y; + +trait Meow { + fn f(x: T) -> Self; +} + +impl Meow for T { + fn f(x: T) -> T { + x + } +} + +#[cfg(not(any(noimpl_current, noimpl_next)))] +#[cfg_attr(any(lowprio_current, lowprio_next), rustc_low_priority_impl)] +impl Meow for X { + fn f(Y: Y) -> X { + X + } +} + +fn main() { + let _: X = Meow::f(<_>::default()); + //[implbreaking-current]~^ error: type annotations needed [E0283] + //[implbreaking-next]~^^ error: type annotations needed [E0283] + //[lowprio-current]~^^^ warn: dependency on trait impl fallback [trait_impl_fallback] + //[lowprio-current]~^^^^ warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //[lowprio-next]~^^^^^ warn: dependency on trait impl fallback [trait_impl_fallback] + //[lowprio-next]~^^^^^^ warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +} diff --git a/tests/ui/traits/low_priority_impls/never.current.stderr b/tests/ui/traits/low_priority_impls/never.current.stderr new file mode 100644 index 0000000000000..f64a3bd8937ae --- /dev/null +++ b/tests/ui/traits/low_priority_impls/never.current.stderr @@ -0,0 +1,36 @@ + WARN rustc_trait_selection::error_reporting::infer::need_type_info resolved ty var in error message +warning: dependency on trait impl fallback + --> $DIR/never.rs:33:16 + | +LL | let _: X = Meow::f(loop {}); + | ^^^^^^^ cannot infer type of the type parameter `T` declared on the trait `Meow` + | + = help: specify the types explicitly + = note: in the future, the requirement `X: Meow<_>` will fail + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #0 + = note: `#[warn(trait_impl_fallback)]` on by default +help: consider specifying a concrete type for the type parameter `T` + | +LL | let _: X = Meow::::f(loop {}); + | ++++++++++++++ + +warning: 1 warning emitted + +Future incompatibility report: Future breakage diagnostic: +warning: dependency on trait impl fallback + --> $DIR/never.rs:33:16 + | +LL | let _: X = Meow::f(loop {}); + | ^^^^^^^ cannot infer type of the type parameter `T` declared on the trait `Meow` + | + = help: specify the types explicitly + = note: in the future, the requirement `X: Meow<_>` will fail + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #0 + = note: `#[warn(trait_impl_fallback)]` on by default +help: consider specifying a concrete type for the type parameter `T` + | +LL | let _: X = Meow::::f(loop {}); + | ++++++++++++++ + diff --git a/tests/ui/traits/low_priority_impls/never.next.stderr b/tests/ui/traits/low_priority_impls/never.next.stderr new file mode 100644 index 0000000000000..f64a3bd8937ae --- /dev/null +++ b/tests/ui/traits/low_priority_impls/never.next.stderr @@ -0,0 +1,36 @@ + WARN rustc_trait_selection::error_reporting::infer::need_type_info resolved ty var in error message +warning: dependency on trait impl fallback + --> $DIR/never.rs:33:16 + | +LL | let _: X = Meow::f(loop {}); + | ^^^^^^^ cannot infer type of the type parameter `T` declared on the trait `Meow` + | + = help: specify the types explicitly + = note: in the future, the requirement `X: Meow<_>` will fail + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #0 + = note: `#[warn(trait_impl_fallback)]` on by default +help: consider specifying a concrete type for the type parameter `T` + | +LL | let _: X = Meow::::f(loop {}); + | ++++++++++++++ + +warning: 1 warning emitted + +Future incompatibility report: Future breakage diagnostic: +warning: dependency on trait impl fallback + --> $DIR/never.rs:33:16 + | +LL | let _: X = Meow::f(loop {}); + | ^^^^^^^ cannot infer type of the type parameter `T` declared on the trait `Meow` + | + = help: specify the types explicitly + = note: in the future, the requirement `X: Meow<_>` will fail + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #0 + = note: `#[warn(trait_impl_fallback)]` on by default +help: consider specifying a concrete type for the type parameter `T` + | +LL | let _: X = Meow::::f(loop {}); + | ++++++++++++++ + diff --git a/tests/ui/traits/low_priority_impls/never.rs b/tests/ui/traits/low_priority_impls/never.rs new file mode 100644 index 0000000000000..a870f89057144 --- /dev/null +++ b/tests/ui/traits/low_priority_impls/never.rs @@ -0,0 +1,36 @@ +// This test checks that low priority impls take precedence over never type fallback +// +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +// +//@ check-pass + +#![feature(rustc_attrs)] + +#[derive(Default)] +struct X; +#[derive(Default)] +struct Y; + +trait Meow { + fn f(x: T) -> Self; +} + +impl Meow for T { + fn f(x: T) -> T { + x + } +} + +#[rustc_low_priority_impl] +impl Meow for X { + fn f(Y: Y) -> X { + X + } +} + +fn main() { + let _: X = Meow::f(loop {}); + //~^ warn: dependency on trait impl fallback [trait_impl_fallback] + //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +}