diff --git a/RELEASES.md b/RELEASES.md
index e0175db7ec881..1dd3fbea6136f 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -16,8 +16,7 @@ Version 1.84.1 (2025-01-30)
Version 1.84.0 (2025-01-09)
==========================
-
+
Language
--------
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 0664a882c1d50..9f5f2533e085b 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -747,7 +747,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
- let res = self.adjust_nan(a.min(b), &[a, b]);
+ let res = if a == b {
+ // They are definitely not NaN (those are never equal), but they could be `+0` and `-0`.
+ // Let the machine decide which one to return.
+ M::equal_float_min_max(self, a, b)
+ } else {
+ self.adjust_nan(a.min(b), &[a, b])
+ };
self.write_scalar(res, dest)?;
interp_ok(())
}
@@ -762,7 +768,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
- let res = self.adjust_nan(a.max(b), &[a, b]);
+ let res = if a == b {
+ // They are definitely not NaN (those are never equal), but they could be `+0` and `-0`.
+ // Let the machine decide which one to return.
+ M::equal_float_min_max(self, a, b)
+ } else {
+ self.adjust_nan(a.max(b), &[a, b])
+ };
self.write_scalar(res, dest)?;
interp_ok(())
}
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index 36e5a2ff750ae..8f6b15b8df012 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -278,6 +278,12 @@ pub trait Machine<'tcx>: Sized {
F2::NAN
}
+ /// Determines the result of `min`/`max` on floats when the arguments are equal.
+ fn equal_float_min_max(_ecx: &InterpCx<'tcx, Self>, a: F, _b: F) -> F {
+ // By default, we pick the left argument.
+ a
+ }
+
/// Called before a basic block terminator is executed.
#[inline]
fn before_terminator(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index e84ae65f90322..9277d71234f42 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -253,6 +253,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return;
}
+ let mut expr_ty = self.typeck_results.borrow().expr_ty_adjusted(expr);
+
for a in &adj {
match a.kind {
Adjust::NeverToAny => {
@@ -266,7 +268,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
None,
expr.span,
overloaded_deref.method_call(self.tcx),
- self.tcx.mk_args(&[a.target.into()]),
+ self.tcx.mk_args(&[expr_ty.into()]),
);
}
Adjust::Deref(None) => {
@@ -283,6 +285,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// No effects to enforce here.
}
}
+
+ expr_ty = a.target;
}
let autoborrow_mut = adj.iter().any(|adj| {
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 73da8855e10c2..60f7616a5fbad 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -1522,6 +1522,14 @@ impl<'tcx> Liveness<'_, 'tcx> {
}
fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) {
+ if let Some(intrinsic) =
+ self.ir.tcx.intrinsic(self.ir.tcx.hir().body_owner_def_id(body.id()))
+ {
+ if intrinsic.must_be_overridden {
+ return;
+ }
+ }
+
for p in body.params {
self.check_unused_vars_in_pat(
p.pat,
diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs
index acd00d9f74f56..abb794934324e 100644
--- a/compiler/rustc_trait_selection/src/solve/delegate.rs
+++ b/compiler/rustc_trait_selection/src/solve/delegate.rs
@@ -1,7 +1,7 @@
use std::ops::Deref;
use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
use rustc_infer::infer::canonical::{
Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarValues,
@@ -98,9 +98,10 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
param_env: ty::ParamEnv<'tcx>,
arg: ty::GenericArg<'tcx>,
) -> Option>>> {
- crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg).map(|obligations| {
- obligations.into_iter().map(|obligation| obligation.into()).collect()
- })
+ crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg, DUMMY_SP, CRATE_DEF_ID)
+ .map(|obligations| {
+ obligations.into_iter().map(|obligation| obligation.into()).collect()
+ })
}
fn clone_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index c8ae977b5ad60..0db44eda8470e 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -1,25 +1,21 @@
use std::marker::PhantomData;
use std::mem;
-use std::ops::ControlFlow;
use rustc_data_structures::thinvec::ExtractIf;
use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause};
use rustc_infer::traits::{
- self, FromSolverError, MismatchedProjectionTypes, Obligation, ObligationCause,
- ObligationCauseCode, PredicateObligation, PredicateObligations, SelectionError, TraitEngine,
+ FromSolverError, PredicateObligation, PredicateObligations, TraitEngine,
};
-use rustc_middle::ty::error::{ExpectedFound, TypeError};
-use rustc_middle::ty::{self, TyCtxt};
-use rustc_middle::{bug, span_bug};
use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _};
-use tracing::{instrument, trace};
+use tracing::instrument;
+use self::derive_errors::*;
use super::Certainty;
use super::delegate::SolverDelegate;
-use super::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
-use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError};
+use crate::traits::{FulfillmentError, ScrubbedTraitError};
+
+mod derive_errors;
/// A trait engine using the new trait solver.
///
@@ -244,483 +240,3 @@ impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'
}
}
}
-
-fn fulfillment_error_for_no_solution<'tcx>(
- infcx: &InferCtxt<'tcx>,
- root_obligation: PredicateObligation<'tcx>,
-) -> FulfillmentError<'tcx> {
- let obligation = find_best_leaf_obligation(infcx, &root_obligation, false);
-
- let code = match obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
- FulfillmentErrorCode::Project(
- // FIXME: This could be a `Sorts` if the term is a type
- MismatchedProjectionTypes { err: TypeError::Mismatch },
- )
- }
- ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => {
- let ct_ty = match ct.kind() {
- ty::ConstKind::Unevaluated(uv) => {
- infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
- }
- ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env),
- ty::ConstKind::Value(cv) => cv.ty,
- kind => span_bug!(
- obligation.cause.span,
- "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
- ),
- };
- FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType {
- ct,
- ct_ty,
- expected_ty,
- })
- }
- ty::PredicateKind::NormalizesTo(..) => {
- FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
- }
- ty::PredicateKind::AliasRelate(_, _, _) => {
- FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
- }
- ty::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))
- }
- ty::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))
- }
- ty::PredicateKind::Clause(_)
- | ty::PredicateKind::DynCompatible(_)
- | ty::PredicateKind::Ambiguous => {
- FulfillmentErrorCode::Select(SelectionError::Unimplemented)
- }
- ty::PredicateKind::ConstEquate(..) => {
- bug!("unexpected goal: {obligation:?}")
- }
- };
-
- FulfillmentError { obligation, code, root_obligation }
-}
-
-fn fulfillment_error_for_stalled<'tcx>(
- infcx: &InferCtxt<'tcx>,
- root_obligation: PredicateObligation<'tcx>,
-) -> FulfillmentError<'tcx> {
- let (code, refine_obligation) = infcx.probe(|_| {
- match <&SolverDelegate<'tcx>>::from(infcx)
- .evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::No)
- .0
- {
- Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => {
- (FulfillmentErrorCode::Ambiguity { overflow: None }, true)
- }
- Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => (
- 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((_, Certainty::Yes)) => {
- bug!("did not expect successful goal when collecting ambiguity errors")
- }
- Err(_) => {
- bug!("did not expect selection error when collecting ambiguity errors")
- }
- }
- });
-
- 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<'tcx>(
- infcx: &InferCtxt<'tcx>,
- root_obligation: PredicateObligation<'tcx>,
-) -> FulfillmentError<'tcx> {
- FulfillmentError {
- obligation: find_best_leaf_obligation(infcx, &root_obligation, true),
- code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
- root_obligation,
- }
-}
-
-fn find_best_leaf_obligation<'tcx>(
- infcx: &InferCtxt<'tcx>,
- obligation: &PredicateObligation<'tcx>,
- consider_ambiguities: bool,
-) -> PredicateObligation<'tcx> {
- 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.
- infcx
- .fudge_inference_if_ok(|| {
- infcx
- .visit_proof_tree(obligation.clone().into(), &mut BestObligation {
- obligation: obligation.clone(),
- consider_ambiguities,
- })
- .break_value()
- .ok_or(())
- })
- .unwrap_or(obligation)
-}
-
-struct BestObligation<'tcx> {
- obligation: PredicateObligation<'tcx>,
- consider_ambiguities: bool,
-}
-
-impl<'tcx> BestObligation<'tcx> {
- fn with_derived_obligation(
- &mut self,
- derived_obligation: PredicateObligation<'tcx>,
- 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, 'tcx>,
- ) -> 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 => {
- // 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::InstantiateHigherRanked
- | GoalSource::AliasWellFormed
- ) && match self.consider_ambiguities {
- true => {
- matches!(
- nested_goal.result(),
- Ok(Certainty::Maybe(MaybeCause::Ambiguity))
- )
- }
- false => matches!(nested_goal.result(), Err(_)),
- }
- },
- )
- })
- });
- }
-
- // Prefer a non-rigid candidate if there is one.
- if candidates.len() > 1 {
- candidates.retain(|candidate| {
- !matches!(candidate.kind(), inspect::ProbeKind::RigidAlias { .. })
- });
- }
- }
- }
-
- candidates
- }
-}
-
-impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
- type Result = ControlFlow>;
-
- fn span(&self) -> rustc_span::Span {
- self.obligation.cause.span
- }
-
- #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
- fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
- let candidates = self.non_trivial_candidates(goal);
- trace!(candidates = ?candidates.iter().map(|c| c.kind()).collect::>());
-
- let [candidate] = candidates.as_slice() else {
- 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()
- && goal.infcx().tcx.do_not_recommend_impl(impl_def_id)
- {
- return ControlFlow::Break(self.obligation.clone());
- }
-
- let tcx = goal.infcx().tcx;
- // FIXME: Also, what about considering >1 layer up the stack? May be necessary
- // for normalizes-to.
- let pred_kind = goal.goal().predicate.kind();
- let child_mode = match pred_kind.skip_binder() {
- ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
- ChildMode::Trait(pred_kind.rebind(pred))
- }
- ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => {
- ChildMode::Host(pred_kind.rebind(pred))
- }
- ty::PredicateKind::NormalizesTo(normalizes_to)
- if matches!(
- normalizes_to.alias.kind(tcx),
- ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst
- ) =>
- {
- ChildMode::Trait(pred_kind.rebind(ty::TraitPredicate {
- trait_ref: normalizes_to.alias.trait_ref(tcx),
- polarity: ty::PredicatePolarity::Positive,
- }))
- }
- _ => 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(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait()
- && let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
- && poly_trait_pred.def_id() == fn_ptr_trait
- && 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 make_obligation = |cause| Obligation {
- cause,
- param_env: nested_goal.goal().param_env,
- predicate: nested_goal.goal().predicate,
- recursion_depth: self.obligation.recursion_depth + 1,
- };
-
- let obligation;
- match (child_mode, nested_goal.source()) {
- (ChildMode::Trait(_) | ChildMode::Host(_), GoalSource::Misc) => {
- continue;
- }
- (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
- obligation = make_obligation(derive_cause(
- tcx,
- candidate.kind(),
- self.obligation.cause.clone(),
- 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(
- tcx,
- candidate.kind(),
- self.obligation.cause.clone(),
- impl_where_bound_count,
- parent_host_pred,
- ));
- impl_where_bound_count += 1;
- }
- // Skip over a higher-ranked predicate.
- (_, GoalSource::InstantiateHigherRanked) => {
- obligation = self.obligation.clone();
- }
- (ChildMode::PassThrough, _)
- | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
- obligation = make_obligation(self.obligation.cause.clone());
- }
- }
-
- // Skip nested goals that aren't the *reason* for our goal's failure.
- match self.consider_ambiguities {
- true if matches!(
- nested_goal.result(),
- Ok(Certainty::Maybe(MaybeCause::Ambiguity))
- ) => {}
- false if matches!(nested_goal.result(), Err(_)) => {}
- _ => continue,
- }
-
- 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(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() {
- if let Some(obligation) = goal
- .infcx()
- .visit_proof_tree_at_depth(
- goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())),
- goal.depth() + 1,
- self,
- )
- .break_value()
- {
- return ControlFlow::Break(obligation);
- } else if let Some(obligation) = goal
- .infcx()
- .visit_proof_tree_at_depth(
- goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())),
- goal.depth() + 1,
- self,
- )
- .break_value()
- {
- return ControlFlow::Break(obligation);
- }
- }
-
- ControlFlow::Break(self.obligation.clone())
- }
-}
-
-#[derive(Debug, Copy, Clone)]
-enum ChildMode<'tcx> {
- // 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(ty::PolyTraitPredicate<'tcx>),
- // 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(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>),
- // 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<'tcx>(
- tcx: TyCtxt<'tcx>,
- candidate_kind: inspect::ProbeKind>,
- mut cause: ObligationCause<'tcx>,
- idx: usize,
- parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
-) -> ObligationCause<'tcx> {
- match candidate_kind {
- inspect::ProbeKind::TraitCandidate {
- source: CandidateSource::Impl(impl_def_id),
- result: _,
- } => {
- if let Some((_, span)) =
- tcx.predicates_of(impl_def_id).instantiate_identity(tcx).iter().nth(idx)
- {
- cause = cause.derived_cause(parent_trait_pred, |derived| {
- ObligationCauseCode::ImplDerived(Box::new(traits::ImplDerivedCause {
- derived,
- impl_or_alias_def_id: impl_def_id,
- impl_def_predicate_index: Some(idx),
- span,
- }))
- })
- }
- }
- inspect::ProbeKind::TraitCandidate {
- source: CandidateSource::BuiltinImpl(..),
- result: _,
- } => {
- cause = cause.derived_cause(parent_trait_pred, ObligationCauseCode::BuiltinDerived);
- }
- _ => {}
- };
- cause
-}
-
-fn derive_host_cause<'tcx>(
- tcx: TyCtxt<'tcx>,
- candidate_kind: inspect::ProbeKind>,
- mut cause: ObligationCause<'tcx>,
- idx: usize,
- parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
-) -> ObligationCause<'tcx> {
- match candidate_kind {
- inspect::ProbeKind::TraitCandidate {
- source: CandidateSource::Impl(impl_def_id),
- result: _,
- } => {
- if let Some((_, span)) = tcx
- .predicates_of(impl_def_id)
- .instantiate_identity(tcx)
- .into_iter()
- .chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map(
- |(trait_ref, span)| {
- (
- trait_ref.to_host_effect_clause(
- tcx,
- parent_host_pred.skip_binder().constness,
- ),
- span,
- )
- },
- ))
- .nth(idx)
- {
- cause =
- cause.derived_host_cause(parent_host_pred, |derived| {
- ObligationCauseCode::ImplDerivedHost(Box::new(
- traits::ImplDerivedHostCause { derived, impl_def_id, span },
- ))
- })
- }
- }
- inspect::ProbeKind::TraitCandidate {
- source: CandidateSource::BuiltinImpl(..),
- result: _,
- } => {
- cause =
- cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost);
- }
- _ => {}
- };
- cause
-}
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
new file mode 100644
index 0000000000000..c64bc19835ba6
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
@@ -0,0 +1,527 @@
+use std::ops::ControlFlow;
+
+use rustc_infer::infer::InferCtxt;
+use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause};
+use rustc_infer::traits::{
+ self, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode,
+ PredicateObligation, SelectionError,
+};
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::{bug, span_bug};
+use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _};
+use rustc_type_ir::solve::{Goal, NoSolution};
+use tracing::{instrument, trace};
+
+use crate::solve::Certainty;
+use crate::solve::delegate::SolverDelegate;
+use crate::solve::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
+use crate::traits::{FulfillmentError, FulfillmentErrorCode, wf};
+
+pub(super) fn fulfillment_error_for_no_solution<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ root_obligation: PredicateObligation<'tcx>,
+) -> FulfillmentError<'tcx> {
+ let obligation = find_best_leaf_obligation(infcx, &root_obligation, false);
+
+ let code = match obligation.predicate.kind().skip_binder() {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
+ FulfillmentErrorCode::Project(
+ // FIXME: This could be a `Sorts` if the term is a type
+ MismatchedProjectionTypes { err: TypeError::Mismatch },
+ )
+ }
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => {
+ let ct_ty = match ct.kind() {
+ ty::ConstKind::Unevaluated(uv) => {
+ infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
+ }
+ ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env),
+ ty::ConstKind::Value(cv) => cv.ty,
+ kind => span_bug!(
+ obligation.cause.span,
+ "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
+ ),
+ };
+ FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType {
+ ct,
+ ct_ty,
+ expected_ty,
+ })
+ }
+ ty::PredicateKind::NormalizesTo(..) => {
+ FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
+ }
+ ty::PredicateKind::AliasRelate(_, _, _) => {
+ FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
+ }
+ ty::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))
+ }
+ ty::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))
+ }
+ ty::PredicateKind::Clause(_)
+ | ty::PredicateKind::DynCompatible(_)
+ | ty::PredicateKind::Ambiguous => {
+ FulfillmentErrorCode::Select(SelectionError::Unimplemented)
+ }
+ ty::PredicateKind::ConstEquate(..) => {
+ bug!("unexpected goal: {obligation:?}")
+ }
+ };
+
+ FulfillmentError { obligation, code, root_obligation }
+}
+
+pub(super) fn fulfillment_error_for_stalled<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ root_obligation: PredicateObligation<'tcx>,
+) -> FulfillmentError<'tcx> {
+ let (code, refine_obligation) = infcx.probe(|_| {
+ match <&SolverDelegate<'tcx>>::from(infcx)
+ .evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::No)
+ .0
+ {
+ Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => {
+ (FulfillmentErrorCode::Ambiguity { overflow: None }, true)
+ }
+ Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => (
+ 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((_, Certainty::Yes)) => {
+ bug!("did not expect successful goal when collecting ambiguity errors")
+ }
+ Err(_) => {
+ bug!("did not expect selection error when collecting ambiguity errors")
+ }
+ }
+ });
+
+ FulfillmentError {
+ obligation: if refine_obligation {
+ find_best_leaf_obligation(infcx, &root_obligation, true)
+ } else {
+ root_obligation.clone()
+ },
+ code,
+ root_obligation,
+ }
+}
+
+pub(super) fn fulfillment_error_for_overflow<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ root_obligation: PredicateObligation<'tcx>,
+) -> FulfillmentError<'tcx> {
+ FulfillmentError {
+ obligation: find_best_leaf_obligation(infcx, &root_obligation, true),
+ code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
+ root_obligation,
+ }
+}
+
+fn find_best_leaf_obligation<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ obligation: &PredicateObligation<'tcx>,
+ consider_ambiguities: bool,
+) -> PredicateObligation<'tcx> {
+ 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.
+ infcx
+ .fudge_inference_if_ok(|| {
+ infcx
+ .visit_proof_tree(obligation.clone().into(), &mut BestObligation {
+ obligation: obligation.clone(),
+ consider_ambiguities,
+ })
+ .break_value()
+ .ok_or(())
+ })
+ .unwrap_or(obligation)
+}
+
+struct BestObligation<'tcx> {
+ obligation: PredicateObligation<'tcx>,
+ consider_ambiguities: bool,
+}
+
+impl<'tcx> BestObligation<'tcx> {
+ fn with_derived_obligation(
+ &mut self,
+ derived_obligation: PredicateObligation<'tcx>,
+ 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, 'tcx>,
+ ) -> 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 => {
+ // 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::InstantiateHigherRanked
+ | GoalSource::AliasWellFormed
+ ) && match (self.consider_ambiguities, nested_goal.result()) {
+ (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity)))
+ | (false, Err(_)) => true,
+ _ => false,
+ }
+ },
+ )
+ })
+ });
+ }
+
+ // Prefer a non-rigid candidate if there is one.
+ if candidates.len() > 1 {
+ candidates.retain(|candidate| {
+ !matches!(candidate.kind(), inspect::ProbeKind::RigidAlias { .. })
+ });
+ }
+ }
+ }
+
+ 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<'_, 'tcx>,
+ arg: ty::GenericArg<'tcx>,
+ ) -> ControlFlow> {
+ 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, arg, self.span(), body_id)
+ .into_iter()
+ .flatten()
+ {
+ let nested_goal = candidate.instantiate_proof_tree_for_nested_goal(
+ GoalSource::Misc,
+ Goal::new(infcx.tcx, obligation.param_env, obligation.predicate),
+ 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(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
+ _ => continue,
+ }
+
+ self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
+ }
+
+ ControlFlow::Break(self.obligation.clone())
+ }
+}
+
+impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
+ type Result = ControlFlow>;
+
+ fn span(&self) -> rustc_span::Span {
+ self.obligation.cause.span
+ }
+
+ #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
+ fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
+ let candidates = self.non_trivial_candidates(goal);
+ trace!(candidates = ?candidates.iter().map(|c| c.kind()).collect::>());
+
+ let [candidate] = candidates.as_slice() else {
+ 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()
+ && goal.infcx().tcx.do_not_recommend_impl(impl_def_id)
+ {
+ return ControlFlow::Break(self.obligation.clone());
+ }
+
+ let tcx = goal.infcx().tcx;
+ // FIXME: Also, what about considering >1 layer up the stack? May be necessary
+ // for normalizes-to.
+ let pred_kind = goal.goal().predicate.kind();
+ let child_mode = match pred_kind.skip_binder() {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
+ ChildMode::Trait(pred_kind.rebind(pred))
+ }
+ ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => {
+ ChildMode::Host(pred_kind.rebind(pred))
+ }
+ ty::PredicateKind::NormalizesTo(normalizes_to)
+ if matches!(
+ normalizes_to.alias.kind(tcx),
+ ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst
+ ) =>
+ {
+ ChildMode::Trait(pred_kind.rebind(ty::TraitPredicate {
+ trait_ref: normalizes_to.alias.trait_ref(tcx),
+ polarity: ty::PredicatePolarity::Positive,
+ }))
+ }
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
+ return self.visit_well_formed_goal(candidate, arg);
+ }
+ _ => 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(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait()
+ && let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
+ && poly_trait_pred.def_id() == fn_ptr_trait
+ && 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 make_obligation = |cause| Obligation {
+ cause,
+ param_env: nested_goal.goal().param_env,
+ predicate: nested_goal.goal().predicate,
+ recursion_depth: self.obligation.recursion_depth + 1,
+ };
+
+ let obligation;
+ match (child_mode, nested_goal.source()) {
+ (ChildMode::Trait(_) | ChildMode::Host(_), GoalSource::Misc) => {
+ continue;
+ }
+ (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
+ obligation = make_obligation(derive_cause(
+ tcx,
+ candidate.kind(),
+ self.obligation.cause.clone(),
+ 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(
+ tcx,
+ candidate.kind(),
+ self.obligation.cause.clone(),
+ impl_where_bound_count,
+ parent_host_pred,
+ ));
+ impl_where_bound_count += 1;
+ }
+ // Skip over a higher-ranked predicate.
+ (_, GoalSource::InstantiateHigherRanked) => {
+ obligation = self.obligation.clone();
+ }
+ (ChildMode::PassThrough, _)
+ | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
+ obligation = make_obligation(self.obligation.cause.clone());
+ }
+ }
+
+ // Skip nested goals that aren't the *reason* for our goal's failure.
+ match (self.consider_ambiguities, nested_goal.result()) {
+ (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
+ _ => continue,
+ }
+
+ 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(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() {
+ if let Some(obligation) = goal
+ .infcx()
+ .visit_proof_tree_at_depth(
+ goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())),
+ goal.depth() + 1,
+ self,
+ )
+ .break_value()
+ {
+ return ControlFlow::Break(obligation);
+ } else if let Some(obligation) = goal
+ .infcx()
+ .visit_proof_tree_at_depth(
+ goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())),
+ goal.depth() + 1,
+ self,
+ )
+ .break_value()
+ {
+ return ControlFlow::Break(obligation);
+ }
+ }
+
+ ControlFlow::Break(self.obligation.clone())
+ }
+}
+
+#[derive(Debug, Copy, Clone)]
+enum ChildMode<'tcx> {
+ // 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(ty::PolyTraitPredicate<'tcx>),
+ // 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(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>),
+ // 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<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ candidate_kind: inspect::ProbeKind>,
+ mut cause: ObligationCause<'tcx>,
+ idx: usize,
+ parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
+) -> ObligationCause<'tcx> {
+ match candidate_kind {
+ inspect::ProbeKind::TraitCandidate {
+ source: CandidateSource::Impl(impl_def_id),
+ result: _,
+ } => {
+ if let Some((_, span)) =
+ tcx.predicates_of(impl_def_id).instantiate_identity(tcx).iter().nth(idx)
+ {
+ cause = cause.derived_cause(parent_trait_pred, |derived| {
+ ObligationCauseCode::ImplDerived(Box::new(traits::ImplDerivedCause {
+ derived,
+ impl_or_alias_def_id: impl_def_id,
+ impl_def_predicate_index: Some(idx),
+ span,
+ }))
+ })
+ }
+ }
+ inspect::ProbeKind::TraitCandidate {
+ source: CandidateSource::BuiltinImpl(..),
+ result: _,
+ } => {
+ cause = cause.derived_cause(parent_trait_pred, ObligationCauseCode::BuiltinDerived);
+ }
+ _ => {}
+ };
+ cause
+}
+
+fn derive_host_cause<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ candidate_kind: inspect::ProbeKind>,
+ mut cause: ObligationCause<'tcx>,
+ idx: usize,
+ parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
+) -> ObligationCause<'tcx> {
+ match candidate_kind {
+ inspect::ProbeKind::TraitCandidate {
+ source: CandidateSource::Impl(impl_def_id),
+ result: _,
+ } => {
+ if let Some((_, span)) = tcx
+ .predicates_of(impl_def_id)
+ .instantiate_identity(tcx)
+ .into_iter()
+ .chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map(
+ |(trait_ref, span)| {
+ (
+ trait_ref.to_host_effect_clause(
+ tcx,
+ parent_host_pred.skip_binder().constness,
+ ),
+ span,
+ )
+ },
+ ))
+ .nth(idx)
+ {
+ cause =
+ cause.derived_host_cause(parent_host_pred, |derived| {
+ ObligationCauseCode::ImplDerivedHost(Box::new(
+ traits::ImplDerivedHostCause { derived, impl_def_id, span },
+ ))
+ })
+ }
+ }
+ inspect::ProbeKind::TraitCandidate {
+ source: CandidateSource::BuiltinImpl(..),
+ result: _,
+ } => {
+ cause =
+ cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost);
+ }
+ _ => {}
+ };
+ cause
+}
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index e735020a63e66..9ba48cd588fa1 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -194,47 +194,57 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
let goals = instantiated_goals
.into_iter()
- .map(|(source, goal)| match goal.predicate.kind().no_bound_vars() {
- Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
- let unconstrained_term = match term.unpack() {
- ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(),
- ty::TermKind::Const(_) => infcx.next_const_var(span).into(),
- };
- let goal =
- goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
- // We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the
- // expected term. This means that candidates which only fail due to nested goals
- // and which normalize to a different term then the final result could ICE: when
- // building their proof tree, the expected term was unconstrained, but when
- // instantiating the candidate it is already constrained to the result of another
- // candidate.
- let proof_tree = infcx
- .probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1);
- InspectGoal::new(
- infcx,
- self.goal.depth + 1,
- proof_tree.unwrap(),
- Some(NormalizesToTermHack { term, unconstrained_term }),
- source,
- )
- }
- _ => {
- // We're using a probe here as evaluating a goal could constrain
- // inference variables by choosing one candidate. If we then recurse
- // into another candidate who ends up with different inference
- // constraints, we get an ICE if we already applied the constraints
- // from the chosen candidate.
- let proof_tree = infcx
- .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1)
- .unwrap();
- InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
- }
- })
+ .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal, span))
.collect();
(goals, opt_impl_args)
}
+ pub fn instantiate_proof_tree_for_nested_goal(
+ &self,
+ source: GoalSource,
+ goal: Goal<'tcx, ty::Predicate<'tcx>>,
+ span: Span,
+ ) -> InspectGoal<'a, 'tcx> {
+ let infcx = self.goal.infcx;
+ match goal.predicate.kind().no_bound_vars() {
+ Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
+ let unconstrained_term = match term.unpack() {
+ ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(),
+ ty::TermKind::Const(_) => infcx.next_const_var(span).into(),
+ };
+ let goal =
+ goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
+ // We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the
+ // expected term. This means that candidates which only fail due to nested goals
+ // and which normalize to a different term then the final result could ICE: when
+ // building their proof tree, the expected term was unconstrained, but when
+ // instantiating the candidate it is already constrained to the result of another
+ // candidate.
+ let proof_tree =
+ infcx.probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1);
+ InspectGoal::new(
+ infcx,
+ self.goal.depth + 1,
+ proof_tree.unwrap(),
+ Some(NormalizesToTermHack { term, unconstrained_term }),
+ source,
+ )
+ }
+ _ => {
+ // We're using a probe here as evaluating a goal could constrain
+ // inference variables by choosing one candidate. If we then recurse
+ // into another candidate who ends up with different inference
+ // constraints, we get an ICE if we already applied the constraints
+ // from the chosen candidate.
+ let proof_tree = infcx
+ .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1)
+ .unwrap();
+ InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
+ }
+ }
+ }
+
/// Visit all nested goals of this candidate, rolling back
/// all inference constraints.
pub fn visit_nested_in_probe>(&self, visitor: &mut V) -> V::Result {
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 6b5ebade6aedb..d4a9664e28280 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -76,6 +76,7 @@ use crate::infer::{InferCtxt, TyCtxtInferExt};
use crate::regions::InferCtxtRegionExt;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
+#[derive(Debug)]
pub struct FulfillmentError<'tcx> {
pub obligation: PredicateObligation<'tcx>,
pub code: FulfillmentErrorCode<'tcx>,
@@ -107,12 +108,6 @@ impl<'tcx> FulfillmentError<'tcx> {
}
}
-impl<'tcx> Debug for FulfillmentError<'tcx> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "FulfillmentError({:?},{:?})", self.obligation, self.code)
- }
-}
-
#[derive(Clone)]
pub enum FulfillmentErrorCode<'tcx> {
/// Inherently impossible to fulfill; this trait is implemented if and only
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
index 1339739ce7f5e..ec0b790339691 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
@@ -5,8 +5,8 @@ use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds;
use rustc_middle::infer::canonical::CanonicalQueryResponse;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt};
-use rustc_span::Span;
use rustc_span::def_id::CRATE_DEF_ID;
+use rustc_span::{DUMMY_SP, Span};
use rustc_type_ir::outlives::{Component, push_outlives_components};
use smallvec::{SmallVec, smallvec};
use tracing::debug;
@@ -92,7 +92,9 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
// From the full set of obligations, just filter down to the region relationships.
for obligation in
- wf::unnormalized_obligations(ocx.infcx, param_env, arg).into_iter().flatten()
+ wf::unnormalized_obligations(ocx.infcx, param_env, arg, DUMMY_SP, CRATE_DEF_ID)
+ .into_iter()
+ .flatten()
{
assert!(!obligation.has_escaping_bound_vars());
let Some(pred) = obligation.predicate.kind().no_bound_vars() else {
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 20b675bcb76b7..37d49cc2f555f 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -8,8 +8,8 @@ use rustc_middle::ty::{
self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable,
TypeVisitable, TypeVisitableExt, TypeVisitor,
};
-use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::Span;
+use rustc_span::def_id::{DefId, LocalDefId};
use tracing::{debug, instrument, trace};
use crate::infer::InferCtxt;
@@ -89,6 +89,8 @@ pub fn unnormalized_obligations<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
arg: GenericArg<'tcx>,
+ span: Span,
+ body_id: LocalDefId,
) -> Option> {
debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
@@ -106,8 +108,8 @@ pub fn unnormalized_obligations<'tcx>(
let mut wf = WfPredicates {
infcx,
param_env,
- body_id: CRATE_DEF_ID,
- span: DUMMY_SP,
+ body_id,
+ span,
out: PredicateObligations::new(),
recursion_depth: 0,
item: None,
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index a793fc2aa2e55..18ada14d101b5 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -1091,7 +1091,195 @@ pub trait FnPtr: Copy + Clone {
fn addr(self) -> *const ();
}
-/// Derive macro generating impls of traits related to smart pointers.
+/// Derive macro that makes a smart pointer usable with trait objects.
+///
+/// # What this macro does
+///
+/// This macro is intended to be used with user-defined pointer types, and makes it possible to
+/// perform coercions on the pointee of the user-defined pointer. There are two aspects to this:
+///
+/// ## Unsizing coercions of the pointee
+///
+/// By using the macro, the following example will compile:
+/// ```
+/// #![feature(derive_coerce_pointee)]
+/// use std::marker::CoercePointee;
+/// use std::ops::Deref;
+///
+/// #[derive(CoercePointee)]
+/// #[repr(transparent)]
+/// struct MySmartPointer(Box);
+///
+/// impl Deref for MySmartPointer {
+/// type Target = T;
+/// fn deref(&self) -> &T {
+/// &self.0
+/// }
+/// }
+///
+/// trait MyTrait {}
+///
+/// impl MyTrait for i32 {}
+///
+/// fn main() {
+/// let ptr: MySmartPointer = MySmartPointer(Box::new(4));
+///
+/// // This coercion would be an error without the derive.
+/// let ptr: MySmartPointer = ptr;
+/// }
+/// ```
+/// Without the `#[derive(CoercePointee)]` macro, this example would fail with the following error:
+/// ```text
+/// error[E0308]: mismatched types
+/// --> src/main.rs:11:44
+/// |
+/// 11 | let ptr: MySmartPointer = ptr;
+/// | --------------------------- ^^^ expected `MySmartPointer`, found `MySmartPointer`
+/// | |
+/// | expected due to this
+/// |
+/// = note: expected struct `MySmartPointer`
+/// found struct `MySmartPointer`
+/// = help: `i32` implements `MyTrait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well
+/// ```
+///
+/// ## Dyn compatibility
+///
+/// This macro allows you to dispatch on the user-defined pointer type. That is, traits using the
+/// type as a receiver are dyn-compatible. For example, this compiles:
+///
+/// ```
+/// #![feature(arbitrary_self_types, derive_coerce_pointee)]
+/// use std::marker::CoercePointee;
+/// use std::ops::Deref;
+///
+/// #[derive(CoercePointee)]
+/// #[repr(transparent)]
+/// struct MySmartPointer(Box);
+///
+/// impl Deref for MySmartPointer {
+/// type Target = T;
+/// fn deref(&self) -> &T {
+/// &self.0
+/// }
+/// }
+///
+/// // You can always define this trait. (as long as you have #![feature(arbitrary_self_types)])
+/// trait MyTrait {
+/// fn func(self: MySmartPointer);
+/// }
+///
+/// // But using `dyn MyTrait` requires #[derive(CoercePointee)].
+/// fn call_func(value: MySmartPointer) {
+/// value.func();
+/// }
+/// ```
+/// If you remove the `#[derive(CoercePointee)]` annotation from the struct, then the above example
+/// will fail with this error message:
+/// ```text
+/// error[E0038]: the trait `MyTrait` is not dyn compatible
+/// --> src/lib.rs:21:36
+/// |
+/// 17 | fn func(self: MySmartPointer);
+/// | -------------------- help: consider changing method `func`'s `self` parameter to be `&self`: `&Self`
+/// ...
+/// 21 | fn call_func(value: MySmartPointer) {
+/// | ^^^^^^^^^^^ `MyTrait` is not dyn compatible
+/// |
+/// note: for a trait to be dyn compatible it needs to allow building a vtable
+/// for more information, visit
+/// --> src/lib.rs:17:19
+/// |
+/// 16 | trait MyTrait {
+/// | ------- this trait is not dyn compatible...
+/// 17 | fn func(self: MySmartPointer);
+/// | ^^^^^^^^^^^^^^^^^^^^ ...because method `func`'s `self` parameter cannot be dispatched on
+/// ```
+///
+/// # Requirements for using the macro
+///
+/// This macro can only be used if:
+/// * The type is a `#[repr(transparent)]` struct.
+/// * The type of its non-zero-sized field must either be a standard library pointer type
+/// (reference, raw pointer, `NonNull`, `Box`, `Rc`, `Arc`, etc.) or another user-defined type
+/// also using the `#[derive(CoercePointee)]` macro.
+/// * Zero-sized fields must not mention any generic parameters unless the zero-sized field has
+/// type [`PhantomData`].
+///
+/// ## Multiple type parameters
+///
+/// If the type has multiple type parameters, then you must explicitly specify which one should be
+/// used for dynamic dispatch. For example:
+/// ```
+/// # #![feature(derive_coerce_pointee)]
+/// # use std::marker::{CoercePointee, PhantomData};
+/// #[derive(CoercePointee)]
+/// #[repr(transparent)]
+/// struct MySmartPointer<#[pointee] T: ?Sized, U> {
+/// ptr: Box,
+/// _phantom: PhantomData,
+/// }
+/// ```
+/// Specifying `#[pointee]` when the struct has only one type parameter is allowed, but not required.
+///
+/// # Examples
+///
+/// A custom implementation of the `Rc` type:
+/// ```
+/// #![feature(derive_coerce_pointee)]
+/// use std::marker::CoercePointee;
+/// use std::ops::Deref;
+/// use std::ptr::NonNull;
+///
+/// #[derive(CoercePointee)]
+/// #[repr(transparent)]
+/// pub struct Rc {
+/// inner: NonNull>,
+/// }
+///
+/// struct RcInner {
+/// refcount: usize,
+/// value: T,
+/// }
+///
+/// impl Deref for Rc {
+/// type Target = T;
+/// fn deref(&self) -> &T {
+/// let ptr = self.inner.as_ptr();
+/// unsafe { &(*ptr).value }
+/// }
+/// }
+///
+/// impl Rc {
+/// pub fn new(value: T) -> Self {
+/// let inner = Box::new(RcInner {
+/// refcount: 1,
+/// value,
+/// });
+/// Self {
+/// inner: NonNull::from(Box::leak(inner)),
+/// }
+/// }
+/// }
+///
+/// impl Clone for Rc {
+/// fn clone(&self) -> Self {
+/// // A real implementation would handle overflow here.
+/// unsafe { (*self.inner.as_ptr()).refcount += 1 };
+/// Self { inner: self.inner }
+/// }
+/// }
+///
+/// impl Drop for Rc {
+/// fn drop(&mut self) {
+/// let ptr = self.inner.as_ptr();
+/// unsafe { (*ptr).refcount -= 1 };
+/// if unsafe { (*ptr).refcount } == 0 {
+/// drop(unsafe { Box::from_raw(ptr) });
+/// }
+/// }
+/// }
+/// ```
#[rustc_builtin_macro(CoercePointee, attributes(pointee))]
#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)]
#[unstable(feature = "derive_coerce_pointee", issue = "123430")]
diff --git a/library/core/src/slice/sort/stable/drift.rs b/library/core/src/slice/sort/stable/drift.rs
index 644e75a4581e9..cf1df1e91a50d 100644
--- a/library/core/src/slice/sort/stable/drift.rs
+++ b/library/core/src/slice/sort/stable/drift.rs
@@ -10,8 +10,8 @@ use crate::{cmp, intrinsics};
/// Sorts `v` based on comparison function `is_less`. If `eager_sort` is true,
/// it will only do small-sorts and physical merges, ensuring O(N * log(N))
-/// worst-case complexity. `scratch.len()` must be at least `max(v.len() / 2,
-/// MIN_SMALL_SORT_SCRATCH_LEN)` otherwise the implementation may abort.
+/// worst-case complexity. `scratch.len()` must be at least
+/// `max(v.len() - v.len() / 2, SMALL_SORT_GENERAL_SCRATCH_LEN)` otherwise the implementation may abort.
/// Fully ascending and descending inputs will be sorted with exactly N - 1
/// comparisons.
///
diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs
index 7adcc83b818d1..3ff2e71fd05bc 100644
--- a/library/core/src/slice/sort/stable/mod.rs
+++ b/library/core/src/slice/sort/stable/mod.rs
@@ -41,6 +41,8 @@ pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less
cfg_if! {
if #[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))] {
+ // Unlike driftsort, mergesort only requires len / 2,
+ // not len - len / 2.
let alloc_len = len / 2;
cfg_if! {
@@ -91,16 +93,26 @@ fn driftsort_main bool, BufT: BufGuard>(v: &mut [T], i
// By allocating n elements of memory we can ensure the entire input can
// be sorted using stable quicksort, which allows better performance on
// random and low-cardinality distributions. However, we still want to
- // reduce our memory usage to n / 2 for large inputs. We do this by scaling
- // our allocation as max(n / 2, min(n, 8MB)), ensuring we scale like n for
- // small inputs and n / 2 for large inputs, without a sudden drop off. We
- // also need to ensure our alloc >= MIN_SMALL_SORT_SCRATCH_LEN, as the
+ // reduce our memory usage to n - n / 2 for large inputs. We do this by scaling
+ // our allocation as max(n - n / 2, min(n, 8MB)), ensuring we scale like n for
+ // small inputs and n - n / 2 for large inputs, without a sudden drop off. We
+ // also need to ensure our alloc >= SMALL_SORT_GENERAL_SCRATCH_LEN, as the
// small-sort always needs this much memory.
+ //
+ // driftsort will produce unsorted runs of up to min_good_run_len, which
+ // is at most len - len / 2.
+ // Unsorted runs need to be processed by quicksort, which requires as much
+ // scratch space as the run length, therefore the scratch space must be at
+ // least len - len / 2.
+ // If min_good_run_len is ever modified, this code must be updated to allocate
+ // the correct scratch size for it.
const MAX_FULL_ALLOC_BYTES: usize = 8_000_000; // 8MB
let max_full_alloc = MAX_FULL_ALLOC_BYTES / mem::size_of::();
let len = v.len();
- let alloc_len =
- cmp::max(cmp::max(len / 2, cmp::min(len, max_full_alloc)), SMALL_SORT_GENERAL_SCRATCH_LEN);
+ let alloc_len = cmp::max(
+ cmp::max(len - len / 2, cmp::min(len, max_full_alloc)),
+ SMALL_SORT_GENERAL_SCRATCH_LEN,
+ );
// For small inputs 4KiB of stack storage suffices, which allows us to avoid
// calling the (de-)allocator. Benchmarks showed this was quite beneficial.
diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs
index 0c8308bfce00e..630c6ff907703 100644
--- a/library/core/src/slice/sort/stable/quicksort.rs
+++ b/library/core/src/slice/sort/stable/quicksort.rs
@@ -7,6 +7,8 @@ use crate::slice::sort::shared::smallsort::StableSmallSortTypeImpl;
use crate::{intrinsics, ptr};
/// Sorts `v` recursively using quicksort.
+/// `scratch.len()` must be at least `max(v.len() - v.len() / 2, SMALL_SORT_GENERAL_SCRATCH_LEN)`
+/// otherwise the implementation may abort.
///
/// `limit` when initialized with `c*log(v.len())` for some c ensures we do not
/// overflow the stack or go quadratic.
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 845ba484326f0..3727b5f4cae4a 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -11,6 +11,7 @@ use std::{fmt, process};
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use rustc_abi::{Align, ExternAbi, Size};
+use rustc_apfloat::{Float, FloatConvert};
use rustc_attr_parsing::InlineAttr;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
#[allow(unused)]
@@ -1129,20 +1130,24 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
}
#[inline(always)]
- fn generate_nan<
- F1: rustc_apfloat::Float + rustc_apfloat::FloatConvert,
- F2: rustc_apfloat::Float,
- >(
+ fn generate_nan, F2: Float>(
ecx: &InterpCx<'tcx, Self>,
inputs: &[F1],
) -> F2 {
ecx.generate_nan(inputs)
}
+ #[inline(always)]
+ fn equal_float_min_max(ecx: &MiriInterpCx<'tcx>, a: F, b: F) -> F {
+ ecx.equal_float_min_max(a, b)
+ }
+
+ #[inline(always)]
fn ub_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> {
interp_ok(ecx.tcx.sess.ub_checks())
}
+ #[inline(always)]
fn thread_local_static_pointer(
ecx: &mut MiriInterpCx<'tcx>,
def_id: DefId,
diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs
index 0017a3991b53b..43c628d66d590 100644
--- a/src/tools/miri/src/operator.rs
+++ b/src/tools/miri/src/operator.rs
@@ -115,4 +115,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
nan
}
}
+
+ fn equal_float_min_max(&self, a: F, b: F) -> F {
+ let this = self.eval_context_ref();
+ // Return one side non-deterministically.
+ let mut rand = this.machine.rng.borrow_mut();
+ if rand.gen() { a } else { b }
+ }
}
diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs
index 4de315e358975..2f4f64b1aa800 100644
--- a/src/tools/miri/tests/pass/float.rs
+++ b/src/tools/miri/tests/pass/float.rs
@@ -31,6 +31,7 @@ fn main() {
test_fast();
test_algebraic();
test_fmuladd();
+ test_min_max_nondet();
}
trait Float: Copy + PartialEq + Debug {
@@ -1211,3 +1212,30 @@ fn test_fmuladd() {
test_operations_f32(0.1, 0.2, 0.3);
test_operations_f64(1.1, 1.2, 1.3);
}
+
+/// `min` and `max` on equal arguments are non-deterministic.
+fn test_min_max_nondet() {
+ /// Ensure that if we call the closure often enough, we see both `true` and `false.`
+ #[track_caller]
+ fn ensure_both(f: impl Fn() -> bool) {
+ let rounds = 16;
+ let first = f();
+ for _ in 1..rounds {
+ if f() != first {
+ // We saw two different values!
+ return;
+ }
+ }
+ // We saw the same thing N times.
+ panic!("expected non-determinism, got {rounds} times the same result: {first:?}");
+ }
+
+ ensure_both(|| f16::min(0.0, -0.0).is_sign_positive());
+ ensure_both(|| f16::max(0.0, -0.0).is_sign_positive());
+ ensure_both(|| f32::min(0.0, -0.0).is_sign_positive());
+ ensure_both(|| f32::max(0.0, -0.0).is_sign_positive());
+ ensure_both(|| f64::min(0.0, -0.0).is_sign_positive());
+ ensure_both(|| f64::max(0.0, -0.0).is_sign_positive());
+ ensure_both(|| f128::min(0.0, -0.0).is_sign_positive());
+ ensure_both(|| f128::max(0.0, -0.0).is_sign_positive());
+}
diff --git a/tests/crashes/135210.rs b/tests/crashes/135210.rs
deleted file mode 100644
index acb61e21090d2..0000000000000
--- a/tests/crashes/135210.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-//@ known-bug: #135210
-
-#![feature(const_trait_impl)]
-const _: fn(&String) = |s| {
- &*s as &str;
-};
-
-fn main() {}
diff --git a/tests/ui/array-slice-vec/driftsort-off-by-one-issue-136103.rs b/tests/ui/array-slice-vec/driftsort-off-by-one-issue-136103.rs
new file mode 100644
index 0000000000000..42197ff102d72
--- /dev/null
+++ b/tests/ui/array-slice-vec/driftsort-off-by-one-issue-136103.rs
@@ -0,0 +1,10 @@
+//@ run-pass
+// Ensures that driftsort doesn't crash under specific slice
+// length and memory size.
+// Based on the example given in https://github.com/rust-lang/rust/issues/136103.
+fn main() {
+ let n = 127;
+ let mut objs: Vec<_> =
+ (0..n).map(|i| [(i % 2) as u8; 125001]).collect();
+ objs.sort();
+}
diff --git a/tests/ui/associated-inherent-types/bugs/wf-check-skipped.next.stderr b/tests/ui/associated-inherent-types/bugs/wf-check-skipped.next.stderr
index bf53089675d5b..81ace4ebb6daf 100644
--- a/tests/ui/associated-inherent-types/bugs/wf-check-skipped.next.stderr
+++ b/tests/ui/associated-inherent-types/bugs/wf-check-skipped.next.stderr
@@ -5,6 +5,8 @@ LL | fn main() -> Foo::Bar::> {}
| ^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u32]`
+note: required by an implicit `Sized` bound in `Vec`
+ --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
error: aborting due to 1 previous error
diff --git a/tests/ui/const-generics/issues/issue-88119.stderr b/tests/ui/const-generics/issues/issue-88119.stderr
index f219c90849a26..c497f1b6d0bdb 100644
--- a/tests/ui/const-generics/issues/issue-88119.stderr
+++ b/tests/ui/const-generics/issues/issue-88119.stderr
@@ -6,35 +6,29 @@ LL | #![feature(const_trait_impl, generic_const_exprs)]
|
= help: remove one of these features
-error[E0284]: type annotations needed: cannot normalize `<&T as ConstName>::{constant#0}`
- --> $DIR/issue-88119.rs:19:49
+error[E0284]: type annotations needed: cannot satisfy `the constant `name_len::()` can be evaluated`
+ --> $DIR/issue-88119.rs:21:5
|
-LL | impl const ConstName for &T
- | ^^ cannot normalize `<&T as ConstName>::{constant#0}`
+LL | [(); name_len::()]:,
+ | ^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `name_len::()` can be evaluated`
|
-note: required for `&T` to implement `~const ConstName`
- --> $DIR/issue-88119.rs:19:35
+note: required by a bound in `<&T as ConstName>`
+ --> $DIR/issue-88119.rs:21:10
|
-LL | impl const ConstName for &T
- | ^^^^^^^^^ ^^
-LL | where
LL | [(); name_len::()]:,
- | --------------------- unsatisfied trait bound introduced here
+ | ^^^^^^^^^^^^^^^ required by this bound in `<&T as ConstName>`
-error[E0284]: type annotations needed: cannot normalize `<&mut T as ConstName>::{constant#0}`
- --> $DIR/issue-88119.rs:26:49
+error[E0284]: type annotations needed: cannot satisfy `the constant `name_len::()` can be evaluated`
+ --> $DIR/issue-88119.rs:28:5
|
-LL | impl const ConstName for &mut T
- | ^^^^^^ cannot normalize `<&mut T as ConstName>::{constant#0}`
+LL | [(); name_len::()]:,
+ | ^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `name_len::()` can be evaluated`
|
-note: required for `&mut T` to implement `~const ConstName`
- --> $DIR/issue-88119.rs:26:35
+note: required by a bound in `<&mut T as ConstName>`
+ --> $DIR/issue-88119.rs:28:10
|
-LL | impl const ConstName for &mut T
- | ^^^^^^^^^ ^^^^^^
-LL | where
LL | [(); name_len::()]:,
- | --------------------- unsatisfied trait bound introduced here
+ | ^^^^^^^^^^^^^^^ required by this bound in `<&mut T as ConstName>`
error: aborting due to 3 previous errors
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr
index 1b76669ccb0d7..4f685c508c721 100644
--- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr
@@ -16,23 +16,13 @@ LL | where
LL | T: AsExpression,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check`
-error[E0277]: the trait bound `&str: AsExpression` is not satisfied
- --> $DIR/as_expression.rs:55:15
- |
-LL | SelectInt.check("bar");
- | ^^^^^ the trait `AsExpression` is not implemented for `&str`
- |
- = help: the trait `AsExpression` is not implemented for `&str`
- but trait `AsExpression` is implemented for it
- = help: for that trait implementation, expected `Text`, found `Integer`
-
error[E0271]: type mismatch resolving `::SqlType == Text`
--> $DIR/as_expression.rs:55:5
|
LL | SelectInt.check("bar");
| ^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Integer`
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
Some errors have detailed explanations: E0271, E0277.
For more information about an error, try `rustc --explain E0271`.
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs
index 583b3c4675a87..48c1ed2b02d71 100644
--- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs
@@ -53,7 +53,7 @@ impl Foo for T where T: Expression {}
fn main() {
SelectInt.check("bar");
- //~^ ERROR the trait bound `&str: AsExpression` is not satisfied
- //[next]~| the trait bound `&str: AsExpression<::SqlType>` is not satisfied
+ //[current]~^ ERROR the trait bound `&str: AsExpression` is not satisfied
+ //[next]~^^ the trait bound `&str: AsExpression<::SqlType>` is not satisfied
//[next]~| type mismatch
}
diff --git a/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr b/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr
index 1cfc2a6d94495..a95670ced8678 100644
--- a/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr
+++ b/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr
@@ -18,6 +18,11 @@ help: this trait has no implementations, consider adding one
|
LL | trait Foo {}
| ^^^^^^^^^
+note: required by a bound in `A`
+ --> $DIR/alias-bounds-when-not-wf.rs:8:11
+ |
+LL | type A = T;
+ | ^^^ required by this bound in `A`
error[E0277]: the trait bound `usize: Foo` is not satisfied
--> $DIR/alias-bounds-when-not-wf.rs:16:10
diff --git a/tests/ui/liveness/liveness-unused.rs b/tests/ui/liveness/liveness-unused.rs
index ba635e6638c8c..49e7044aeda1f 100644
--- a/tests/ui/liveness/liveness-unused.rs
+++ b/tests/ui/liveness/liveness-unused.rs
@@ -2,6 +2,7 @@
#![deny(unused_variables)]
#![deny(unused_assignments)]
#![allow(dead_code, non_camel_case_types, trivial_numeric_casts, dropping_copy_types)]
+#![feature(intrinsics)]
use std::ops::AddAssign;
@@ -137,5 +138,10 @@ fn f7() {
drop(a);
}
+// unused params warnings are not needed for intrinsic functions without bodies
+#[rustc_intrinsic]
+unsafe fn simd_shuffle(a: T, b: T, i: I) -> U;
+
+
fn main() {
}
diff --git a/tests/ui/liveness/liveness-unused.stderr b/tests/ui/liveness/liveness-unused.stderr
index f6c478ddbc72c..a69fc10dff271 100644
--- a/tests/ui/liveness/liveness-unused.stderr
+++ b/tests/ui/liveness/liveness-unused.stderr
@@ -1,5 +1,5 @@
warning: unreachable statement
- --> $DIR/liveness-unused.rs:92:9
+ --> $DIR/liveness-unused.rs:93:9
|
LL | continue;
| -------- any code following this expression is unreachable
@@ -14,7 +14,7 @@ LL | #![warn(unused)]
= note: `#[warn(unreachable_code)]` implied by `#[warn(unused)]`
error: unused variable: `x`
- --> $DIR/liveness-unused.rs:8:7
+ --> $DIR/liveness-unused.rs:9:7
|
LL | fn f1(x: isize) {
| ^ help: if this is intentional, prefix it with an underscore: `_x`
@@ -26,25 +26,25 @@ LL | #![deny(unused_variables)]
| ^^^^^^^^^^^^^^^^
error: unused variable: `x`
- --> $DIR/liveness-unused.rs:12:8
+ --> $DIR/liveness-unused.rs:13:8
|
LL | fn f1b(x: &mut isize) {
| ^ help: if this is intentional, prefix it with an underscore: `_x`
error: unused variable: `x`
- --> $DIR/liveness-unused.rs:20:9
+ --> $DIR/liveness-unused.rs:21:9
|
LL | let x: isize;
| ^ help: if this is intentional, prefix it with an underscore: `_x`
error: unused variable: `x`
- --> $DIR/liveness-unused.rs:25:9
+ --> $DIR/liveness-unused.rs:26:9
|
LL | let x = 3;
| ^ help: if this is intentional, prefix it with an underscore: `_x`
error: variable `x` is assigned to, but never used
- --> $DIR/liveness-unused.rs:30:13
+ --> $DIR/liveness-unused.rs:31:13
|
LL | let mut x = 3;
| ^
@@ -52,7 +52,7 @@ LL | let mut x = 3;
= note: consider using `_x` instead
error: value assigned to `x` is never read
- --> $DIR/liveness-unused.rs:32:5
+ --> $DIR/liveness-unused.rs:33:5
|
LL | x += 4;
| ^
@@ -65,7 +65,7 @@ LL | #![deny(unused_assignments)]
| ^^^^^^^^^^^^^^^^^^
error: variable `z` is assigned to, but never used
- --> $DIR/liveness-unused.rs:37:13
+ --> $DIR/liveness-unused.rs:38:13
|
LL | let mut z = 3;
| ^
@@ -73,31 +73,31 @@ LL | let mut z = 3;
= note: consider using `_z` instead
error: unused variable: `i`
- --> $DIR/liveness-unused.rs:59:12
+ --> $DIR/liveness-unused.rs:60:12
|
LL | Some(i) => {
| ^ help: if this is intentional, prefix it with an underscore: `_i`
error: unused variable: `x`
- --> $DIR/liveness-unused.rs:79:9
+ --> $DIR/liveness-unused.rs:80:9
|
LL | for x in 1..10 { }
| ^ help: if this is intentional, prefix it with an underscore: `_x`
error: unused variable: `x`
- --> $DIR/liveness-unused.rs:84:10
+ --> $DIR/liveness-unused.rs:85:10
|
LL | for (x, _) in [1, 2, 3].iter().enumerate() { }
| ^ help: if this is intentional, prefix it with an underscore: `_x`
error: unused variable: `x`
- --> $DIR/liveness-unused.rs:89:13
+ --> $DIR/liveness-unused.rs:90:13
|
LL | for (_, x) in [1, 2, 3].iter().enumerate() {
| ^ help: if this is intentional, prefix it with an underscore: `_x`
error: variable `x` is assigned to, but never used
- --> $DIR/liveness-unused.rs:112:9
+ --> $DIR/liveness-unused.rs:113:9
|
LL | let x;
| ^
@@ -105,7 +105,7 @@ LL | let x;
= note: consider using `_x` instead
error: value assigned to `x` is never read
- --> $DIR/liveness-unused.rs:116:9
+ --> $DIR/liveness-unused.rs:117:9
|
LL | x = 0;
| ^
diff --git a/tests/ui/traits/const-traits/enforce-deref-on-adjust.rs b/tests/ui/traits/const-traits/enforce-deref-on-adjust.rs
new file mode 100644
index 0000000000000..d5240b7e18ddb
--- /dev/null
+++ b/tests/ui/traits/const-traits/enforce-deref-on-adjust.rs
@@ -0,0 +1,28 @@
+//@ check-pass
+
+#![feature(const_deref)]
+#![feature(const_trait_impl)]
+
+use std::ops::Deref;
+
+struct Wrap(T);
+struct Foo;
+
+impl Foo {
+ const fn call(&self) {}
+}
+
+impl const Deref for Wrap {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+const fn foo() {
+ let x = Wrap(Foo);
+ x.call();
+}
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr
index 377dfc8b52916..425f2d59222e0 100644
--- a/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr
+++ b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr
@@ -1,8 +1,8 @@
-error[E0284]: type annotations needed: cannot normalize `X::{constant#0}`
+error[E0284]: type annotations needed: cannot satisfy `the constant `{ || {} }` can be evaluated`
--> $DIR/const-region-infer-to-static-in-binder.rs:4:10
|
LL | struct X;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `X::{constant#0}`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `{ || {} }` can be evaluated`
error: using function pointers as const generic parameters is forbidden
--> $DIR/const-region-infer-to-static-in-binder.rs:4:20
diff --git a/tests/ui/traits/next-solver/specialization-transmute.stderr b/tests/ui/traits/next-solver/specialization-transmute.stderr
index b96bfab927d2d..2d0c503bc958d 100644
--- a/tests/ui/traits/next-solver/specialization-transmute.stderr
+++ b/tests/ui/traits/next-solver/specialization-transmute.stderr
@@ -10,11 +10,11 @@ LL | #![feature(specialization)]
error: cannot normalize `::Id: '_`
-error[E0284]: type annotations needed: cannot normalize `::Id`
+error[E0282]: type annotations needed
--> $DIR/specialization-transmute.rs:15:23
|
LL | fn intu(&self) -> &Self::Id {
- | ^^^^^^^^^ cannot normalize `::Id`
+ | ^^^^^^^^^ cannot infer type for reference `&::Id`
error[E0284]: type annotations needed: cannot satisfy `::Id normalizes-to T`
--> $DIR/specialization-transmute.rs:17:9
@@ -36,4 +36,5 @@ LL | fn transmute, U: Copy>(t: T) -> U {
error: aborting due to 4 previous errors; 1 warning emitted
-For more information about this error, try `rustc --explain E0284`.
+Some errors have detailed explanations: E0282, E0284.
+For more information about an error, try `rustc --explain E0282`.
diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr
index 1bcc0dbaf6726..92ad83c330000 100644
--- a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr
+++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr
@@ -9,6 +9,8 @@ error: the constant `N` is not of type `usize`
|
LL | fn func() -> [(); N];
| ^^^^^^^ expected `usize`, found `u32`
+ |
+ = note: the length of array `[(); N]` must be type `usize`
error: aborting due to 2 previous errors
diff --git a/tests/ui/union/union-derive-eq.next.stderr b/tests/ui/union/union-derive-eq.next.stderr
index 3952b1f12840f..151ceebe1ba67 100644
--- a/tests/ui/union/union-derive-eq.next.stderr
+++ b/tests/ui/union/union-derive-eq.next.stderr
@@ -7,6 +7,8 @@ LL | union U2 {
LL | a: PartialEqNotEq,
| ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `PartialEqNotEq`
|
+note: required by a bound in `AssertParamIsEq`
+ --> $SRC_DIR/core/src/cmp.rs:LL:COL
= note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider annotating `PartialEqNotEq` with `#[derive(Eq)]`
|
diff --git a/tests/ui/wf/wf-normalization-sized.next.stderr b/tests/ui/wf/wf-normalization-sized.next.stderr
index 1e898fb7b78ab..83b56bb6b19ad 100644
--- a/tests/ui/wf/wf-normalization-sized.next.stderr
+++ b/tests/ui/wf/wf-normalization-sized.next.stderr
@@ -5,6 +5,7 @@ LL | const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = ();
| ^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[[[[[u8]]]]]`
+ = note: slice and array elements must have `Sized` type
error[E0277]: the size for values of type `[[[[[u8]]]]]` cannot be known at compilation time
--> $DIR/wf-normalization-sized.rs:19:11
@@ -13,6 +14,7 @@ LL | const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = ();
| ^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[[[[[u8]]]]]`
+ = note: slice and array elements must have `Sized` type
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0277]: the size for values of type `str` cannot be known at compilation time
@@ -22,6 +24,8 @@ LL | const _: as WellUnformed>::RequestNormalize = ();
| ^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `str`
+note: required by an implicit `Sized` bound in `Vec`
+ --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
error[E0277]: the size for values of type `str` cannot be known at compilation time
--> $DIR/wf-normalization-sized.rs:22:11
@@ -30,6 +34,8 @@ LL | const _: as WellUnformed>::RequestNormalize = ();
| ^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `str`
+note: required by an implicit `Sized` bound in `Vec`
+ --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: aborting due to 4 previous errors
diff --git a/tests/ui/wf/wf-trait-fn-arg.next.stderr b/tests/ui/wf/wf-trait-fn-arg.next.stderr
index c55dc5c8a121a..d5dd36fad6dd6 100644
--- a/tests/ui/wf/wf-trait-fn-arg.next.stderr
+++ b/tests/ui/wf/wf-trait-fn-arg.next.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `Self: Eq` is not satisfied
LL | fn bar(&self, x: &Bar);
| ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
|
+note: required by a bound in `Bar`
+ --> $DIR/wf-trait-fn-arg.rs:11:15
+ |
+LL | struct Bar {
+ | ^^ required by this bound in `Bar`
help: consider further restricting `Self`
|
LL | fn bar(&self, x: &Bar) where Self: Eq;
diff --git a/tests/ui/wf/wf-trait-fn-ret.next.stderr b/tests/ui/wf/wf-trait-fn-ret.next.stderr
index b3dca17672d31..0ad786c2fd566 100644
--- a/tests/ui/wf/wf-trait-fn-ret.next.stderr
+++ b/tests/ui/wf/wf-trait-fn-ret.next.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `Self: Eq` is not satisfied
LL | fn bar(&self) -> &Bar;
| ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
|
+note: required by a bound in `Bar`
+ --> $DIR/wf-trait-fn-ret.rs:10:15
+ |
+LL | struct Bar {
+ | ^^ required by this bound in `Bar`
help: consider further restricting `Self`
|
LL | fn bar(&self) -> &Bar where Self: Eq;
diff --git a/tests/ui/wf/wf-trait-fn-where-clause.next.stderr b/tests/ui/wf/wf-trait-fn-where-clause.next.stderr
index 8c8a5fa3e7041..db5454d0f3c22 100644
--- a/tests/ui/wf/wf-trait-fn-where-clause.next.stderr
+++ b/tests/ui/wf/wf-trait-fn-where-clause.next.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `Self: Eq` is not satisfied
LL | Bar: Copy;
| ^^^^ the trait `Eq` is not implemented for `Self`
|
+note: required by a bound in `Bar`
+ --> $DIR/wf-trait-fn-where-clause.rs:10:15
+ |
+LL | struct Bar {
+ | ^^ required by this bound in `Bar`
help: consider further restricting `Self`
|
LL | Bar: Copy, Self: Eq;