Rollup merge of #135900 - compiler-errors:derive-wf, r=lcnr
Manually walk into WF obligations in `BestObligation` proof tree visitor When we encounter a `WellFormed` obligation in the `BestObligation` proof tree visitor, ignore the proof tree and call `wf::unnormalized_obligations` to derive well-formed obligations with the correct cause codes. This is to avoid having to replicate the somewhat delicate logic that `wf.rs` does to set up its obligation causes... Don't see a better way to do this. vibes?? r? lcnr
This commit is contained in:
commit
3c4b9122ec
20 changed files with 654 additions and 584 deletions
|
@ -1,7 +1,7 @@
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
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::query_response::make_query_region_constraints;
|
||||||
use rustc_infer::infer::canonical::{
|
use rustc_infer::infer::canonical::{
|
||||||
Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarValues,
|
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>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
arg: ty::GenericArg<'tcx>,
|
arg: ty::GenericArg<'tcx>,
|
||||||
) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> {
|
) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> {
|
||||||
crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg).map(|obligations| {
|
crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg, DUMMY_SP, CRATE_DEF_ID)
|
||||||
obligations.into_iter().map(|obligation| obligation.into()).collect()
|
.map(|obligations| {
|
||||||
})
|
obligations.into_iter().map(|obligation| obligation.into()).collect()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
|
fn clone_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
|
||||||
|
|
|
@ -1,25 +1,21 @@
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::ControlFlow;
|
|
||||||
|
|
||||||
use rustc_data_structures::thinvec::ExtractIf;
|
use rustc_data_structures::thinvec::ExtractIf;
|
||||||
use rustc_infer::infer::InferCtxt;
|
use rustc_infer::infer::InferCtxt;
|
||||||
use rustc_infer::traits::query::NoSolution;
|
use rustc_infer::traits::query::NoSolution;
|
||||||
use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause};
|
|
||||||
use rustc_infer::traits::{
|
use rustc_infer::traits::{
|
||||||
self, FromSolverError, MismatchedProjectionTypes, Obligation, ObligationCause,
|
FromSolverError, PredicateObligation, PredicateObligations, TraitEngine,
|
||||||
ObligationCauseCode, PredicateObligation, PredicateObligations, SelectionError, 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 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::Certainty;
|
||||||
use super::delegate::SolverDelegate;
|
use super::delegate::SolverDelegate;
|
||||||
use super::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
|
use crate::traits::{FulfillmentError, ScrubbedTraitError};
|
||||||
use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError};
|
|
||||||
|
mod derive_errors;
|
||||||
|
|
||||||
/// A trait engine using the new trait solver.
|
/// 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) -> <Self as ProofTreeVisitor<'tcx>>::Result,
|
|
||||||
) -> <Self as ProofTreeVisitor<'tcx>>::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<inspect::InspectCandidate<'a, 'tcx>> {
|
|
||||||
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<PredicateObligation<'tcx>>;
|
|
||||||
|
|
||||||
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::<Vec<_>>());
|
|
||||||
|
|
||||||
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<A, R> Trait for for<..> fn(..A) -> R` was written
|
|
||||||
// instead of `impl<T: FnPtr> Trait for T`.
|
|
||||||
//
|
|
||||||
// We do this as a separate loop so that we do not choose to tell the user about some nested
|
|
||||||
// goal before we encounter a `T: FnPtr` nested goal.
|
|
||||||
for nested_goal in &nested_goals {
|
|
||||||
if let Some(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<TyCtxt<'tcx>>,
|
|
||||||
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<TyCtxt<'tcx>>,
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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) -> <Self as ProofTreeVisitor<'tcx>>::Result,
|
||||||
|
) -> <Self as ProofTreeVisitor<'tcx>>::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<inspect::InspectCandidate<'a, 'tcx>> {
|
||||||
|
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<PredicateObligation<'tcx>> {
|
||||||
|
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<PredicateObligation<'tcx>>;
|
||||||
|
|
||||||
|
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::<Vec<_>>());
|
||||||
|
|
||||||
|
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<A, R> Trait for for<..> fn(..A) -> R` was written
|
||||||
|
// instead of `impl<T: FnPtr> Trait for T`.
|
||||||
|
//
|
||||||
|
// We do this as a separate loop so that we do not choose to tell the user about some nested
|
||||||
|
// goal before we encounter a `T: FnPtr` nested goal.
|
||||||
|
for nested_goal in &nested_goals {
|
||||||
|
if let Some(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<TyCtxt<'tcx>>,
|
||||||
|
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<TyCtxt<'tcx>>,
|
||||||
|
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
|
||||||
|
}
|
|
@ -194,47 +194,57 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
|
||||||
|
|
||||||
let goals = instantiated_goals
|
let goals = instantiated_goals
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(source, goal)| match goal.predicate.kind().no_bound_vars() {
|
.map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal, span))
|
||||||
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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
(goals, opt_impl_args)
|
(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
|
/// Visit all nested goals of this candidate, rolling back
|
||||||
/// all inference constraints.
|
/// all inference constraints.
|
||||||
pub fn visit_nested_in_probe<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result {
|
pub fn visit_nested_in_probe<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result {
|
||||||
|
|
|
@ -76,6 +76,7 @@ use crate::infer::{InferCtxt, TyCtxtInferExt};
|
||||||
use crate::regions::InferCtxtRegionExt;
|
use crate::regions::InferCtxtRegionExt;
|
||||||
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct FulfillmentError<'tcx> {
|
pub struct FulfillmentError<'tcx> {
|
||||||
pub obligation: PredicateObligation<'tcx>,
|
pub obligation: PredicateObligation<'tcx>,
|
||||||
pub code: FulfillmentErrorCode<'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)]
|
#[derive(Clone)]
|
||||||
pub enum FulfillmentErrorCode<'tcx> {
|
pub enum FulfillmentErrorCode<'tcx> {
|
||||||
/// Inherently impossible to fulfill; this trait is implemented if and only
|
/// Inherently impossible to fulfill; this trait is implemented if and only
|
||||||
|
|
|
@ -5,8 +5,8 @@ use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds;
|
||||||
use rustc_middle::infer::canonical::CanonicalQueryResponse;
|
use rustc_middle::infer::canonical::CanonicalQueryResponse;
|
||||||
use rustc_middle::traits::ObligationCause;
|
use rustc_middle::traits::ObligationCause;
|
||||||
use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt};
|
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::def_id::CRATE_DEF_ID;
|
||||||
|
use rustc_span::{DUMMY_SP, Span};
|
||||||
use rustc_type_ir::outlives::{Component, push_outlives_components};
|
use rustc_type_ir::outlives::{Component, push_outlives_components};
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use tracing::debug;
|
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.
|
// From the full set of obligations, just filter down to the region relationships.
|
||||||
for obligation in
|
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());
|
assert!(!obligation.has_escaping_bound_vars());
|
||||||
let Some(pred) = obligation.predicate.kind().no_bound_vars() else {
|
let Some(pred) = obligation.predicate.kind().no_bound_vars() else {
|
||||||
|
|
|
@ -8,8 +8,8 @@ use rustc_middle::ty::{
|
||||||
self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable,
|
self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable,
|
||||||
TypeVisitable, TypeVisitableExt, TypeVisitor,
|
TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||||
};
|
};
|
||||||
use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
|
use rustc_span::Span;
|
||||||
use rustc_span::{DUMMY_SP, Span};
|
use rustc_span::def_id::{DefId, LocalDefId};
|
||||||
use tracing::{debug, instrument, trace};
|
use tracing::{debug, instrument, trace};
|
||||||
|
|
||||||
use crate::infer::InferCtxt;
|
use crate::infer::InferCtxt;
|
||||||
|
@ -89,6 +89,8 @@ pub fn unnormalized_obligations<'tcx>(
|
||||||
infcx: &InferCtxt<'tcx>,
|
infcx: &InferCtxt<'tcx>,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
arg: GenericArg<'tcx>,
|
arg: GenericArg<'tcx>,
|
||||||
|
span: Span,
|
||||||
|
body_id: LocalDefId,
|
||||||
) -> Option<PredicateObligations<'tcx>> {
|
) -> Option<PredicateObligations<'tcx>> {
|
||||||
debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
|
debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
|
||||||
|
|
||||||
|
@ -106,8 +108,8 @@ pub fn unnormalized_obligations<'tcx>(
|
||||||
let mut wf = WfPredicates {
|
let mut wf = WfPredicates {
|
||||||
infcx,
|
infcx,
|
||||||
param_env,
|
param_env,
|
||||||
body_id: CRATE_DEF_ID,
|
body_id,
|
||||||
span: DUMMY_SP,
|
span,
|
||||||
out: PredicateObligations::new(),
|
out: PredicateObligations::new(),
|
||||||
recursion_depth: 0,
|
recursion_depth: 0,
|
||||||
item: None,
|
item: None,
|
||||||
|
|
|
@ -5,6 +5,8 @@ LL | fn main() -> Foo::Bar::<Vec<[u32]>> {}
|
||||||
| ^^^^^^^^^^ doesn't have a size known at compile-time
|
| ^^^^^^^^^^ doesn't have a size known at compile-time
|
||||||
|
|
|
|
||||||
= help: the trait `Sized` is not implemented for `[u32]`
|
= 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
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
|
|
@ -6,35 +6,29 @@ LL | #![feature(const_trait_impl, generic_const_exprs)]
|
||||||
|
|
|
|
||||||
= help: remove one of these features
|
= help: remove one of these features
|
||||||
|
|
||||||
error[E0284]: type annotations needed: cannot normalize `<&T as ConstName>::{constant#0}`
|
error[E0284]: type annotations needed: cannot satisfy `the constant `name_len::<T>()` can be evaluated`
|
||||||
--> $DIR/issue-88119.rs:19:49
|
--> $DIR/issue-88119.rs:21:5
|
||||||
|
|
|
|
||||||
LL | impl<T: ?Sized + ConstName> const ConstName for &T
|
|
||||||
| ^^ cannot normalize `<&T as ConstName>::{constant#0}`
|
|
||||||
|
|
|
||||||
note: required for `&T` to implement `~const ConstName`
|
|
||||||
--> $DIR/issue-88119.rs:19:35
|
|
||||||
|
|
|
||||||
LL | impl<T: ?Sized + ConstName> const ConstName for &T
|
|
||||||
| ^^^^^^^^^ ^^
|
|
||||||
LL | where
|
|
||||||
LL | [(); name_len::<T>()]:,
|
LL | [(); name_len::<T>()]:,
|
||||||
| --------------------- unsatisfied trait bound introduced here
|
| ^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `name_len::<T>()` can be evaluated`
|
||||||
|
|
|
||||||
|
note: required by a bound in `<&T as ConstName>`
|
||||||
|
--> $DIR/issue-88119.rs:21:10
|
||||||
|
|
|
||||||
|
LL | [(); name_len::<T>()]:,
|
||||||
|
| ^^^^^^^^^^^^^^^ required by this bound in `<&T as ConstName>`
|
||||||
|
|
||||||
error[E0284]: type annotations needed: cannot normalize `<&mut T as ConstName>::{constant#0}`
|
error[E0284]: type annotations needed: cannot satisfy `the constant `name_len::<T>()` can be evaluated`
|
||||||
--> $DIR/issue-88119.rs:26:49
|
--> $DIR/issue-88119.rs:28:5
|
||||||
|
|
|
|
||||||
LL | impl<T: ?Sized + ConstName> const ConstName for &mut T
|
|
||||||
| ^^^^^^ cannot normalize `<&mut T as ConstName>::{constant#0}`
|
|
||||||
|
|
|
||||||
note: required for `&mut T` to implement `~const ConstName`
|
|
||||||
--> $DIR/issue-88119.rs:26:35
|
|
||||||
|
|
|
||||||
LL | impl<T: ?Sized + ConstName> const ConstName for &mut T
|
|
||||||
| ^^^^^^^^^ ^^^^^^
|
|
||||||
LL | where
|
|
||||||
LL | [(); name_len::<T>()]:,
|
LL | [(); name_len::<T>()]:,
|
||||||
| --------------------- unsatisfied trait bound introduced here
|
| ^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `name_len::<T>()` can be evaluated`
|
||||||
|
|
|
||||||
|
note: required by a bound in `<&mut T as ConstName>`
|
||||||
|
--> $DIR/issue-88119.rs:28:10
|
||||||
|
|
|
||||||
|
LL | [(); name_len::<T>()]:,
|
||||||
|
| ^^^^^^^^^^^^^^^ required by this bound in `<&mut T as ConstName>`
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
|
|
@ -16,23 +16,13 @@ LL | where
|
||||||
LL | T: AsExpression<Self::SqlType>,
|
LL | T: AsExpression<Self::SqlType>,
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check`
|
||||||
|
|
||||||
error[E0277]: the trait bound `&str: AsExpression<Integer>` is not satisfied
|
|
||||||
--> $DIR/as_expression.rs:55:15
|
|
||||||
|
|
|
||||||
LL | SelectInt.check("bar");
|
|
||||||
| ^^^^^ the trait `AsExpression<Integer>` is not implemented for `&str`
|
|
||||||
|
|
|
||||||
= help: the trait `AsExpression<Integer>` is not implemented for `&str`
|
|
||||||
but trait `AsExpression<Text>` is implemented for it
|
|
||||||
= help: for that trait implementation, expected `Text`, found `Integer`
|
|
||||||
|
|
||||||
error[E0271]: type mismatch resolving `<SelectInt as Expression>::SqlType == Text`
|
error[E0271]: type mismatch resolving `<SelectInt as Expression>::SqlType == Text`
|
||||||
--> $DIR/as_expression.rs:55:5
|
--> $DIR/as_expression.rs:55:5
|
||||||
|
|
|
|
||||||
LL | SelectInt.check("bar");
|
LL | SelectInt.check("bar");
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Integer`
|
| ^^^^^^^^^^^^^^^^^^^^^^ 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.
|
Some errors have detailed explanations: E0271, E0277.
|
||||||
For more information about an error, try `rustc --explain E0271`.
|
For more information about an error, try `rustc --explain E0271`.
|
||||||
|
|
|
@ -53,7 +53,7 @@ impl<T> Foo for T where T: Expression {}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
SelectInt.check("bar");
|
SelectInt.check("bar");
|
||||||
//~^ ERROR the trait bound `&str: AsExpression<Integer>` is not satisfied
|
//[current]~^ ERROR the trait bound `&str: AsExpression<Integer>` is not satisfied
|
||||||
//[next]~| the trait bound `&str: AsExpression<<SelectInt as Expression>::SqlType>` is not satisfied
|
//[next]~^^ the trait bound `&str: AsExpression<<SelectInt as Expression>::SqlType>` is not satisfied
|
||||||
//[next]~| type mismatch
|
//[next]~| type mismatch
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,11 @@ help: this trait has no implementations, consider adding one
|
||||||
|
|
|
|
||||||
LL | trait Foo {}
|
LL | trait Foo {}
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^
|
||||||
|
note: required by a bound in `A`
|
||||||
|
--> $DIR/alias-bounds-when-not-wf.rs:8:11
|
||||||
|
|
|
||||||
|
LL | type A<T: Foo> = T;
|
||||||
|
| ^^^ required by this bound in `A`
|
||||||
|
|
||||||
error[E0277]: the trait bound `usize: Foo` is not satisfied
|
error[E0277]: the trait bound `usize: Foo` is not satisfied
|
||||||
--> $DIR/alias-bounds-when-not-wf.rs:16:10
|
--> $DIR/alias-bounds-when-not-wf.rs:16:10
|
||||||
|
|
|
@ -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
|
--> $DIR/const-region-infer-to-static-in-binder.rs:4:10
|
||||||
|
|
|
|
||||||
LL | struct X<const FN: fn() = { || {} }>;
|
LL | struct X<const FN: fn() = { || {} }>;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `X::{constant#0}`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `{ || {} }` can be evaluated`
|
||||||
|
|
||||||
error: using function pointers as const generic parameters is forbidden
|
error: using function pointers as const generic parameters is forbidden
|
||||||
--> $DIR/const-region-infer-to-static-in-binder.rs:4:20
|
--> $DIR/const-region-infer-to-static-in-binder.rs:4:20
|
||||||
|
|
|
@ -10,11 +10,11 @@ LL | #![feature(specialization)]
|
||||||
|
|
||||||
error: cannot normalize `<T as Default>::Id: '_`
|
error: cannot normalize `<T as Default>::Id: '_`
|
||||||
|
|
||||||
error[E0284]: type annotations needed: cannot normalize `<T as Default>::Id`
|
error[E0282]: type annotations needed
|
||||||
--> $DIR/specialization-transmute.rs:15:23
|
--> $DIR/specialization-transmute.rs:15:23
|
||||||
|
|
|
|
||||||
LL | fn intu(&self) -> &Self::Id {
|
LL | fn intu(&self) -> &Self::Id {
|
||||||
| ^^^^^^^^^ cannot normalize `<T as Default>::Id`
|
| ^^^^^^^^^ cannot infer type for reference `&<T as Default>::Id`
|
||||||
|
|
||||||
error[E0284]: type annotations needed: cannot satisfy `<T as Default>::Id normalizes-to T`
|
error[E0284]: type annotations needed: cannot satisfy `<T as Default>::Id normalizes-to T`
|
||||||
--> $DIR/specialization-transmute.rs:17:9
|
--> $DIR/specialization-transmute.rs:17:9
|
||||||
|
@ -36,4 +36,5 @@ LL | fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U {
|
||||||
|
|
||||||
error: aborting due to 4 previous errors; 1 warning emitted
|
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`.
|
||||||
|
|
|
@ -9,6 +9,8 @@ error: the constant `N` is not of type `usize`
|
||||||
|
|
|
|
||||||
LL | fn func<const N: u32>() -> [(); N];
|
LL | fn func<const N: u32>() -> [(); N];
|
||||||
| ^^^^^^^ expected `usize`, found `u32`
|
| ^^^^^^^ expected `usize`, found `u32`
|
||||||
|
|
|
||||||
|
= note: the length of array `[(); N]` must be type `usize`
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ LL | union U2 {
|
||||||
LL | a: PartialEqNotEq,
|
LL | a: PartialEqNotEq,
|
||||||
| ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `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)
|
= 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)]`
|
help: consider annotating `PartialEqNotEq` with `#[derive(Eq)]`
|
||||||
|
|
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ LL | const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = ();
|
||||||
| ^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
| ^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
||||||
|
|
|
|
||||||
= help: the trait `Sized` is not implemented for `[[[[[u8]]]]]`
|
= 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
|
error[E0277]: the size for values of type `[[[[[u8]]]]]` cannot be known at compilation time
|
||||||
--> $DIR/wf-normalization-sized.rs:19:11
|
--> $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
|
| ^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
||||||
|
|
|
|
||||||
= help: the trait `Sized` is not implemented for `[[[[[u8]]]]]`
|
= 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`
|
= 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
|
error[E0277]: the size for values of type `str` cannot be known at compilation time
|
||||||
|
@ -22,6 +24,8 @@ LL | const _: <Vec<str> as WellUnformed>::RequestNormalize = ();
|
||||||
| ^^^^^^^^ doesn't have a size known at compile-time
|
| ^^^^^^^^ doesn't have a size known at compile-time
|
||||||
|
|
|
|
||||||
= help: the trait `Sized` is not implemented for `str`
|
= 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
|
error[E0277]: the size for values of type `str` cannot be known at compilation time
|
||||||
--> $DIR/wf-normalization-sized.rs:22:11
|
--> $DIR/wf-normalization-sized.rs:22:11
|
||||||
|
@ -30,6 +34,8 @@ LL | const _: <Vec<str> as WellUnformed>::RequestNormalize = ();
|
||||||
| ^^^^^^^^ doesn't have a size known at compile-time
|
| ^^^^^^^^ doesn't have a size known at compile-time
|
||||||
|
|
|
|
||||||
= help: the trait `Sized` is not implemented for `str`
|
= 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`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: aborting due to 4 previous errors
|
||||||
|
|
|
@ -4,6 +4,11 @@ error[E0277]: the trait bound `Self: Eq` is not satisfied
|
||||||
LL | fn bar(&self, x: &Bar<Self>);
|
LL | fn bar(&self, x: &Bar<Self>);
|
||||||
| ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
|
| ^^^^^^^^^ 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<T: Eq + ?Sized> {
|
||||||
|
| ^^ required by this bound in `Bar`
|
||||||
help: consider further restricting `Self`
|
help: consider further restricting `Self`
|
||||||
|
|
|
|
||||||
LL | fn bar(&self, x: &Bar<Self>) where Self: Eq;
|
LL | fn bar(&self, x: &Bar<Self>) where Self: Eq;
|
||||||
|
|
|
@ -4,6 +4,11 @@ error[E0277]: the trait bound `Self: Eq` is not satisfied
|
||||||
LL | fn bar(&self) -> &Bar<Self>;
|
LL | fn bar(&self) -> &Bar<Self>;
|
||||||
| ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
|
| ^^^^^^^^^ 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<T: Eq + ?Sized> {
|
||||||
|
| ^^ required by this bound in `Bar`
|
||||||
help: consider further restricting `Self`
|
help: consider further restricting `Self`
|
||||||
|
|
|
|
||||||
LL | fn bar(&self) -> &Bar<Self> where Self: Eq;
|
LL | fn bar(&self) -> &Bar<Self> where Self: Eq;
|
||||||
|
|
|
@ -4,6 +4,11 @@ error[E0277]: the trait bound `Self: Eq` is not satisfied
|
||||||
LL | Bar<Self>: Copy;
|
LL | Bar<Self>: Copy;
|
||||||
| ^^^^ the trait `Eq` is not implemented for `Self`
|
| ^^^^ 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<T: Eq + ?Sized> {
|
||||||
|
| ^^ required by this bound in `Bar`
|
||||||
help: consider further restricting `Self`
|
help: consider further restricting `Self`
|
||||||
|
|
|
|
||||||
LL | Bar<Self>: Copy, Self: Eq;
|
LL | Bar<Self>: Copy, Self: Eq;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue