//! Trait solving error diagnosis and reporting.
//!
//! This code isn't used by rust-analyzer (it should, but then it'll probably be better to re-port it from rustc).
//! It's only there because without it, debugging trait solver errors is a nightmare.
use std::{fmt::Debug, ops::ControlFlow};
use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt};
use rustc_type_ir::{
AliasRelationDirection, AliasTermKind, HostEffectPredicate, Interner, PredicatePolarity,
error::ExpectedFound,
inherent::{IntoKind, Span as _},
lang_items::SolverTraitLangItem,
solve::{Certainty, GoalSource, MaybeCause, NoSolution},
};
use tracing::{instrument, trace};
use crate::next_solver::{
AliasTerm, Binder, ClauseKind, Const, ConstKind, DbInterner, PolyTraitPredicate, PredicateKind,
SolverContext, Span, Term, TraitPredicate, Ty, TyKind, TypeError,
fulfill::NextSolverError,
infer::{
InferCtxt,
select::SelectionError,
traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations},
},
inspect::{self, ProofTreeVisitor},
normalize::deeply_normalize_for_diagnostics,
};
#[derive(Debug)]
pub struct FulfillmentError<'db> {
pub obligation: PredicateObligation<'db>,
pub code: FulfillmentErrorCode<'db>,
/// Diagnostics only: the 'root' obligation which resulted in
/// the failure to process `obligation`. This is the obligation
/// that was initially passed to `register_predicate_obligation`
pub root_obligation: PredicateObligation<'db>,
}
impl<'db> FulfillmentError<'db> {
pub fn new(
obligation: PredicateObligation<'db>,
code: FulfillmentErrorCode<'db>,
root_obligation: PredicateObligation<'db>,
) -> FulfillmentError<'db> {
FulfillmentError { obligation, code, root_obligation }
}
pub fn is_true_error(&self) -> bool {
match self.code {
FulfillmentErrorCode::Select(_)
| FulfillmentErrorCode::Project(_)
| FulfillmentErrorCode::Subtype(_, _)
| FulfillmentErrorCode::ConstEquate(_, _) => true,
FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => {
false
}
}
}
}
#[derive(Debug, Clone)]
pub enum FulfillmentErrorCode<'db> {
/// Inherently impossible to fulfill; this trait is implemented if and only
/// if it is already implemented.
Cycle(PredicateObligations<'db>),
Select(SelectionError<'db>),
Project(MismatchedProjectionTypes<'db>),
Subtype(ExpectedFound<Ty<'db>>, TypeError<'db>), // always comes from a SubtypePredicate
ConstEquate(ExpectedFound<Const<'db>>, TypeError<'db>),
Ambiguity {
/// Overflow is only `Some(suggest_recursion_limit)` when using the next generation
/// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by
/// emitting a fatal error instead.
overflow: Option<bool>,
},
}
#[derive(Debug, Clone)]
pub struct MismatchedProjectionTypes<'db> {
pub err: TypeError<'db>,
}
pub(super) fn fulfillment_error_for_no_solution<'db>(
infcx: &InferCtxt<'db>,
root_obligation: PredicateObligation<'db>,
) -> FulfillmentError<'db> {
let obligation = find_best_leaf_obligation(infcx, &root_obligation, false);
let code = match obligation.predicate.kind().skip_binder() {
PredicateKind::Clause(ClauseKind::Projection(_)) => {
FulfillmentErrorCode::Project(
// FIXME: This could be a `Sorts` if the term is a type
MismatchedProjectionTypes { err: TypeError::Mismatch },
)
}
PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, expected_ty)) => {
let ct_ty = match ct.kind() {
ConstKind::Unevaluated(uv) => {
infcx.interner.type_of(uv.def.into()).instantiate(infcx.interner, uv.args)
}
ConstKind::Param(param_ct) => param_ct.find_const_ty_from_env(obligation.param_env),
ConstKind::Value(cv) => cv.ty,
kind => panic!(
"ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
),
};
FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType {
ct,
ct_ty,
expected_ty,
})
}
PredicateKind::NormalizesTo(..) => {
FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
}
PredicateKind::AliasRelate(_, _, _) => {
FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
}
PredicateKind::Subtype(pred) => {
let (a, b) = infcx.enter_forall_and_leak_universe(
obligation.predicate.kind().rebind((pred.a, pred.b)),
);
let expected_found = ExpectedFound::new(a, b);
FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
}
PredicateKind::Coerce(pred) => {
let (a, b) = infcx.enter_forall_and_leak_universe(
obligation.predicate.kind().rebind((pred.a, pred.b)),
);
let expected_found = ExpectedFound::new(b, a);
FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
}
PredicateKind::Clause(_) | PredicateKind::DynCompatible(_) | PredicateKind::Ambiguous => {
FulfillmentErrorCode::Select(SelectionError::Unimplemented)
}
PredicateKind::ConstEquate(..) => {
panic!("unexpected goal: {obligation:?}")
}
};
FulfillmentError { obligation, code, root_obligation }
}
pub(super) fn fulfillment_error_for_stalled<'db>(
infcx: &InferCtxt<'db>,
root_obligation: PredicateObligation<'db>,
) -> FulfillmentError<'db> {
let (code, refine_obligation) = infcx.probe(|_| {
match <&SolverContext<'db>>::from(infcx).evaluate_root_goal(
root_obligation.as_goal(),
Span::dummy(),
None,
) {
Ok(GoalEvaluation {
certainty: Certainty::Maybe { cause: MaybeCause::Ambiguity, .. },
..
}) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true),
Ok(GoalEvaluation {
certainty:
Certainty::Maybe {
cause:
MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ },
..
},
..
}) => (
FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
// Don't look into overflows because we treat overflows weirdly anyways.
// We discard the inference constraints from overflowing goals, so
// recomputing the goal again during `find_best_leaf_obligation` may apply
// inference guidance that makes other goals go from ambig -> pass, for example.
//
// FIXME: We should probably just look into overflows here.
false,
),
Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => {
panic!(
"did not expect successful goal when collecting ambiguity errors for `{:?}`",
infcx.resolve_vars_if_possible(root_obligation.predicate),
)
}
Err(_) => {
panic!(
"did not expect selection error when collecting ambiguity errors for `{:?}`",
infcx.resolve_vars_if_possible(root_obligation.predicate),
)
}
}
});
FulfillmentError {
obligation: if refine_obligation {
find_best_leaf_obligation(infcx, &root_obligation, true)
} else {
root_obligation.clone()
},
code,
root_obligation,
}
}
pub(super) fn fulfillment_error_for_overflow<'db>(
infcx: &InferCtxt<'db>,
root_obligation: PredicateObligation<'db>,
) -> FulfillmentError<'db> {
FulfillmentError {
obligation: find_best_leaf_obligation(infcx, &root_obligation, true),
code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
root_obligation,
}
}
#[instrument(level = "debug", skip(infcx), ret)]
fn find_best_leaf_obligation<'db>(
infcx: &InferCtxt<'db>,
obligation: &PredicateObligation<'db>,
consider_ambiguities: bool,
) -> PredicateObligation<'db> {
let obligation = infcx.resolve_vars_if_possible(obligation.clone());
// FIXME: we use a probe here as the `BestObligation` visitor does not
// check whether it uses candidates which get shadowed by where-bounds.
//
// We should probably fix the visitor to not do so instead, as this also
// means the leaf obligation may be incorrect.
let obligation = infcx
.fudge_inference_if_ok(|| {
infcx
.visit_proof_tree(
obligation.as_goal(),
&mut BestObligation { obligation: obligation.clone(), consider_ambiguities },
)
.break_value()
.ok_or(())
})
.unwrap_or(obligation);
deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation)
}
struct BestObligation<'db> {
obligation: PredicateObligation<'db>,
consider_ambiguities: bool,
}
impl<'db> BestObligation<'db> {
fn with_derived_obligation(
&mut self,
derived_obligation: PredicateObligation<'db>,
and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'db>>::Result,
) -> <Self as ProofTreeVisitor<'db>>::Result {
let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation);
let res = and_then(self);
self.obligation = old_obligation;
res
}
/// Filter out the candidates that aren't interesting to visit for the
/// purposes of reporting errors. For ambiguities, we only consider
/// candidates that may hold. For errors, we only consider candidates that
/// *don't* hold and which have impl-where clauses that also don't hold.
fn non_trivial_candidates<'a>(
&self,
goal: &'a inspect::InspectGoal<'a, 'db>,
) -> Vec<inspect::InspectCandidate<'a, 'db>> {
let mut candidates = goal.candidates();
match self.consider_ambiguities {
true => {
// If we have an ambiguous obligation, we must consider *all* candidates
// that hold, or else we may guide inference causing other goals to go
// from ambig -> pass/fail.
candidates.retain(|candidate| candidate.result().is_ok());
}
false => {
// We always handle rigid alias candidates separately as we may not add them for
// aliases whose trait bound doesn't hold.
candidates.retain(|c| !matches!(c.kind(), inspect::ProbeKind::RigidAlias { .. }));
// If we have >1 candidate, one may still be due to "boring" reasons, like
// an alias-relate that failed to hold when deeply evaluated. We really
// don't care about reasons like this.
if candidates.len() > 1 {
candidates.retain(|candidate| {
goal.infcx().probe(|_| {
candidate.instantiate_nested_goals().iter().any(|nested_goal| {
matches!(
nested_goal.source(),
GoalSource::ImplWhereBound
| GoalSource::AliasBoundConstCondition
| GoalSource::AliasWellFormed
) && nested_goal.result().is_err()
})
})
});
}
}
}
candidates
}
/// HACK: We walk the nested obligations for a well-formed arg manually,
/// since there's nontrivial logic in `wf.rs` to set up an obligation cause.
/// Ideally we'd be able to track this better.
fn visit_well_formed_goal(
&mut self,
candidate: &inspect::InspectCandidate<'_, 'db>,
term: Term<'db>,
) -> ControlFlow<PredicateObligation<'db>> {
let infcx = candidate.goal().infcx();
let param_env = candidate.goal().goal().param_env;
for obligation in wf::unnormalized_obligations(infcx, param_env, term).into_iter().flatten()
{
let nested_goal = candidate
.instantiate_proof_tree_for_nested_goal(GoalSource::Misc, obligation.as_goal());
// Skip nested goals that aren't the *reason* for our goal's failure.
match (self.consider_ambiguities, nested_goal.result()) {
(true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }))
| (false, Err(_)) => {}
_ => continue,
}
self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
}
ControlFlow::Break(self.obligation.clone())
}
/// If a normalization of an associated item or a trait goal fails without trying any
/// candidates it's likely that normalizing its self type failed. We manually detect
/// such cases here.
fn detect_error_in_self_ty_normalization(
&mut self,
goal: &inspect::InspectGoal<'_, 'db>,
self_ty: Ty<'db>,
) -> ControlFlow<PredicateObligation<'db>> {
assert!(!self.consider_ambiguities);
let interner = goal.infcx().interner;
if let TyKind::Alias(..) = self_ty.kind() {
let infer_term = goal.infcx().next_ty_var();
let pred = PredicateKind::AliasRelate(
self_ty.into(),
infer_term.into(),
AliasRelationDirection::Equate,
);
let obligation = Obligation::new(
interner,
self.obligation.cause.clone(),
goal.goal().param_env,
pred,
);
self.with_derived_obligation(obligation, |this| {
goal.infcx().visit_proof_tree_at_depth(
goal.goal().with(interner, pred),
goal.depth() + 1,
this,
)
})
} else {
ControlFlow::Continue(())
}
}
/// When a higher-ranked projection goal fails, check that the corresponding
/// higher-ranked trait goal holds or not. This is because the process of
/// instantiating and then re-canonicalizing the binder of the projection goal
/// forces us to be unable to see that the leak check failed in the nested
/// `NormalizesTo` goal, so we don't fall back to the rigid projection check
/// that should catch when a projection goal fails due to an unsatisfied trait
/// goal.
fn detect_trait_error_in_higher_ranked_projection(
&mut self,
goal: &inspect::InspectGoal<'_, 'db>,
) -> ControlFlow<PredicateObligation<'db>> {
let interner = goal.infcx().interner;
if let Some(projection_clause) = goal.goal().predicate.as_projection_clause()
&& !projection_clause.bound_vars().is_empty()
{
let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(interner));
let obligation = Obligation::new(
interner,
self.obligation.cause.clone(),
goal.goal().param_env,
deeply_normalize_for_diagnostics(goal.infcx(), goal.goal().param_env, pred),
);
self.with_derived_obligation(obligation, |this| {
goal.infcx().visit_proof_tree_at_depth(
goal.goal().with(interner, pred),
goal.depth() + 1,
this,
)
})
} else {
ControlFlow::Continue(())
}
}
/// It is likely that `NormalizesTo` failed without any applicable candidates
/// because the alias is not well-formed.
///
/// As we only enter `RigidAlias` candidates if the trait bound of the associated type
/// holds, we discard these candidates in `non_trivial_candidates` and always manually
/// check this here.
fn detect_non_well_formed_assoc_item(
&mut self,
goal: &inspect::InspectGoal<'_, 'db>,
alias: AliasTerm<'db>,
) -> ControlFlow<PredicateObligation<'db>> {
let interner = goal.infcx().interner;
let obligation = Obligation::new(
interner,
self.obligation.cause.clone(),
goal.goal().param_env,
alias.trait_ref(interner),
);
self.with_derived_obligation(obligation, |this| {
goal.infcx().visit_proof_tree_at_depth(
goal.goal().with(interner, alias.trait_ref(interner)),
goal.depth() + 1,
this,
)
})
}
/// If we have no candidates, then it's likely that there is a
/// non-well-formed alias in the goal.
fn detect_error_from_empty_candidates(
&mut self,
goal: &inspect::InspectGoal<'_, 'db>,
) -> ControlFlow<PredicateObligation<'db>> {
let interner = goal.infcx().interner;
let pred_kind = goal.goal().predicate.kind();
match pred_kind.no_bound_vars() {
Some(PredicateKind::Clause(ClauseKind::Trait(pred))) => {
self.detect_error_in_self_ty_normalization(goal, pred.self_ty())?;
}
Some(PredicateKind::NormalizesTo(pred)) => {
if let AliasTermKind::ProjectionTy | AliasTermKind::ProjectionConst =
pred.alias.kind(interner)
{
self.detect_error_in_self_ty_normalization(goal, pred.alias.self_ty())?;
self.detect_non_well_formed_assoc_item(goal, pred.alias)?;
}
}
Some(_) | None => {}
}
ControlFlow::Break(self.obligation.clone())
}
}
impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> {
type Result = ControlFlow<PredicateObligation<'db>>;
#[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'db>) -> Self::Result {
let interner = goal.infcx().interner;
// Skip goals that aren't the *reason* for our goal's failure.
match (self.consider_ambiguities, goal.result()) {
(true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) | (false, Err(_)) => {
}
_ => return ControlFlow::Continue(()),
}
let pred = goal.goal().predicate;
let candidates = self.non_trivial_candidates(goal);
let candidate = match candidates.as_slice() {
[candidate] => candidate,
[] => return self.detect_error_from_empty_candidates(goal),
_ => return ControlFlow::Break(self.obligation.clone()),
};
// // Don't walk into impls that have `do_not_recommend`.
// if let inspect::ProbeKind::TraitCandidate {
// source: CandidateSource::Impl(impl_def_id),
// result: _,
// } = candidate.kind()
// && interner.do_not_recommend_impl(impl_def_id)
// {
// trace!("#[do_not_recommend] -> exit");
// return ControlFlow::Break(self.obligation.clone());
// }
// FIXME: Also, what about considering >1 layer up the stack? May be necessary
// for normalizes-to.
let child_mode = match pred.kind().skip_binder() {
PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => {
ChildMode::Trait(pred.kind().rebind(trait_pred))
}
PredicateKind::Clause(ClauseKind::HostEffect(host_pred)) => {
ChildMode::Host(pred.kind().rebind(host_pred))
}
PredicateKind::NormalizesTo(normalizes_to)
if matches!(
normalizes_to.alias.kind(interner),
AliasTermKind::ProjectionTy | AliasTermKind::ProjectionConst
) =>
{
ChildMode::Trait(pred.kind().rebind(TraitPredicate {
trait_ref: normalizes_to.alias.trait_ref(interner),
polarity: PredicatePolarity::Positive,
}))
}
PredicateKind::Clause(ClauseKind::WellFormed(term)) => {
return self.visit_well_formed_goal(candidate, term);
}
_ => ChildMode::PassThrough,
};
let nested_goals = candidate.instantiate_nested_goals();
// If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as
// an actual candidate, instead we should treat them as if the impl was never considered to
// have potentially applied. As if `impl<A, R> Trait for for<..> fn(..A) -> R` was written
// instead of `impl<T: FnPtr> Trait for T`.
//
// We do this as a separate loop so that we do not choose to tell the user about some nested
// goal before we encounter a `T: FnPtr` nested goal.
for nested_goal in &nested_goals {
if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
&& interner
.is_trait_lang_item(poly_trait_pred.def_id(), SolverTraitLangItem::FnPtrTrait)
&& let Err(NoSolution) = nested_goal.result()
{
return ControlFlow::Break(self.obligation.clone());
}
}
for nested_goal in nested_goals {
trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
let nested_pred = nested_goal.goal().predicate;
let make_obligation = || Obligation {
cause: ObligationCause::dummy(),
param_env: nested_goal.goal().param_env,
predicate: nested_pred,
recursion_depth: self.obligation.recursion_depth + 1,
};
let obligation = match (child_mode, nested_goal.source()) {
(
ChildMode::Trait(_) | ChildMode::Host(_),
GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_),
) => {
continue;
}
(ChildMode::Trait(_parent_trait_pred), GoalSource::ImplWhereBound) => {
make_obligation()
}
(
ChildMode::Host(_parent_host_pred),
GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition,
) => make_obligation(),
(ChildMode::PassThrough, _)
| (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
make_obligation()
}
};
self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
}
// alias-relate may fail because the lhs or rhs can't be normalized,
// and therefore is treated as rigid.
if let Some(PredicateKind::AliasRelate(lhs, rhs, _)) = pred.kind().no_bound_vars() {
goal.infcx().visit_proof_tree_at_depth(
goal.goal().with(interner, ClauseKind::WellFormed(lhs)),
goal.depth() + 1,
self,
)?;
goal.infcx().visit_proof_tree_at_depth(
goal.goal().with(interner, ClauseKind::WellFormed(rhs)),
goal.depth() + 1,
self,
)?;
}
self.detect_trait_error_in_higher_ranked_projection(goal)?;
ControlFlow::Break(self.obligation.clone())
}
}
#[derive(Debug, Copy, Clone)]
enum ChildMode<'db> {
// Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
// and skip all `GoalSource::Misc`, which represent useless obligations
// such as alias-eq which may not hold.
Trait(PolyTraitPredicate<'db>),
// Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
// and skip all `GoalSource::Misc`, which represent useless obligations
// such as alias-eq which may not hold.
Host(Binder<'db, HostEffectPredicate<DbInterner<'db>>>),
// Skip trying to derive an `ObligationCause` from this obligation, and
// report *all* sub-obligations as if they came directly from the parent
// obligation.
PassThrough,
}
impl<'db> NextSolverError<'db> {
pub fn to_debuggable_error(&self, infcx: &InferCtxt<'db>) -> FulfillmentError<'db> {
match self {
NextSolverError::TrueError(obligation) => {
fulfillment_error_for_no_solution(infcx, obligation.clone())
}
NextSolverError::Ambiguity(obligation) => {
fulfillment_error_for_stalled(infcx, obligation.clone())
}
NextSolverError::Overflow(obligation) => {
fulfillment_error_for_overflow(infcx, obligation.clone())
}
}
}
}
mod wf {
use hir_def::{GeneralConstId, ItemContainerId};
use rustc_type_ir::inherent::{
AdtDef, BoundExistentialPredicates, GenericArgs as _, IntoKind, SliceLike, Term as _,
Ty as _,
};
use rustc_type_ir::lang_items::SolverTraitLangItem;
use rustc_type_ir::{
Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
};
use tracing::{debug, instrument};
use crate::next_solver::infer::InferCtxt;
use crate::next_solver::infer::traits::{Obligation, ObligationCause, PredicateObligations};
use crate::next_solver::{
Binder, ClauseKind, Const, ConstKind, Ctor, DbInterner, ExistentialPredicate, GenericArgs,
ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty, TyKind,
};
/// Compute the predicates that are required for a type to be well-formed.
///
/// This is only intended to be used in the new solver, since it does not
/// take into account recursion depth or proper error-reporting spans.
pub(crate) fn unnormalized_obligations<'db>(
infcx: &InferCtxt<'db>,
param_env: ParamEnv<'db>,
term: Term<'db>,
) -> Option<PredicateObligations<'db>> {
debug_assert_eq!(term, infcx.resolve_vars_if_possible(term));
// However, if `arg` IS an unresolved inference variable, returns `None`,
// because we are not able to make any progress at all. This is to prevent
// cycles where we say "?0 is WF if ?0 is WF".
if term.is_infer() {
return None;
}
let mut wf =
WfPredicates { infcx, param_env, out: PredicateObligations::new(), recursion_depth: 0 };
wf.add_wf_preds_for_term(term);
Some(wf.out)
}
struct WfPredicates<'a, 'db> {
infcx: &'a InferCtxt<'db>,
param_env: ParamEnv<'db>,
out: PredicateObligations<'db>,
recursion_depth: usize,
}
impl<'a, 'db> WfPredicates<'a, 'db> {
fn interner(&self) -> DbInterner<'db> {
self.infcx.interner
}
fn require_sized(&mut self, subty: Ty<'db>) {
if !subty.has_escaping_bound_vars() {
let cause = ObligationCause::new();
let trait_ref = TraitRef::new(
self.interner(),
self.interner().require_trait_lang_item(SolverTraitLangItem::Sized),
[subty],
);
self.out.push(Obligation::with_depth(
self.interner(),
cause,
self.recursion_depth,
self.param_env,
Binder::dummy(trait_ref),
));
}
}
/// Pushes all the predicates needed to validate that `term` is WF into `out`.
#[instrument(level = "debug", skip(self))]
fn add_wf_preds_for_term(&mut self, term: Term<'db>) {
term.visit_with(self);
debug!(?self.out);
}
#[instrument(level = "debug", skip(self))]
fn nominal_obligations(
&mut self,
def_id: SolverDefId,
args: GenericArgs<'db>,
) -> PredicateObligations<'db> {
// PERF: `Sized`'s predicates include `MetaSized`, but both are compiler implemented marker
// traits, so `MetaSized` will always be WF if `Sized` is WF and vice-versa. Determining
// the nominal obligations of `Sized` would in-effect just elaborate `MetaSized` and make
// the compiler do a bunch of work needlessly.
if let SolverDefId::TraitId(def_id) = def_id
&& self.interner().is_trait_lang_item(def_id.into(), SolverTraitLangItem::Sized)
{
return Default::default();
}
self.interner()
.predicates_of(def_id)
.iter_instantiated(self.interner(), args)
.map(|pred| {
let cause = ObligationCause::new();
Obligation::with_depth(
self.interner(),
cause,
self.recursion_depth,
self.param_env,
pred,
)
})
.filter(|pred| !pred.has_escaping_bound_vars())
.collect()
}
fn add_wf_preds_for_dyn_ty(
&mut self,
_ty: Ty<'db>,
data: &[Binder<'db, ExistentialPredicate<'db>>],
region: Region<'db>,
) {
// Imagine a type like this:
//
// trait Foo { }
// trait Bar<'c> : 'c { }
//
// &'b (Foo+'c+Bar<'d>)
// ^
//
// In this case, the following relationships must hold:
//
// 'b <= 'c
// 'd <= 'c
//
// The first conditions is due to the normal region pointer
// rules, which say that a reference cannot outlive its
// referent.
//
// The final condition may be a bit surprising. In particular,
// you may expect that it would have been `'c <= 'd`, since
// usually lifetimes of outer things are conservative
// approximations for inner things. However, it works somewhat
// differently with trait objects: here the idea is that if the
// user specifies a region bound (`'c`, in this case) it is the
// "master bound" that *implies* that bounds from other traits are
// all met. (Remember that *all bounds* in a type like
// `Foo+Bar+Zed` must be met, not just one, hence if we write
// `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and
// 'y.)
//
// Note: in fact we only permit builtin traits, not `Bar<'d>`, I
// am looking forward to the future here.
if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() {
let implicit_bounds = object_region_bounds(self.interner(), data);
let explicit_bound = region;
self.out.reserve(implicit_bounds.len());
for implicit_bound in implicit_bounds {
let cause = ObligationCause::new();
let outlives = Binder::dummy(rustc_type_ir::OutlivesPredicate(
explicit_bound,
implicit_bound,
));
self.out.push(Obligation::with_depth(
self.interner(),
cause,
self.recursion_depth,
self.param_env,
outlives,
));
}
// We don't add any wf predicates corresponding to the trait ref's generic arguments
// which allows code like this to compile:
// ```rust
// trait Trait<T: Sized> {}
// fn foo(_: &dyn Trait<[u32]>) {}
// ```
}
}
}
impl<'a, 'db> TypeVisitor<DbInterner<'db>> for WfPredicates<'a, 'db> {
type Result = ();
fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind());
let tcx = self.interner();
match t.kind() {
TyKind::Bool
| TyKind::Char
| TyKind::Int(..)
| TyKind::Uint(..)
| TyKind::Float(..)
| TyKind::Error(_)
| TyKind::Str
| TyKind::CoroutineWitness(..)
| TyKind::Never
| TyKind::Param(_)
| TyKind::Bound(..)
| TyKind::Placeholder(..)
| TyKind::Foreign(..) => {
// WfScalar, WfParameter, etc
}
// Can only infer to `TyKind::Int(_) | TyKind::Uint(_)`.
TyKind::Infer(rustc_type_ir::IntVar(_)) => {}
// Can only infer to `TyKind::Float(_)`.
TyKind::Infer(rustc_type_ir::FloatVar(_)) => {}
TyKind::Slice(subty) => {
self.require_sized(subty);
}
TyKind::Array(subty, len) => {
self.require_sized(subty);
// Note that the len being WF is implicitly checked while visiting.
// Here we just check that it's of type usize.
let cause = ObligationCause::new();
self.out.push(Obligation::with_depth(
tcx,
cause,
self.recursion_depth,
self.param_env,
Binder::dummy(PredicateKind::Clause(ClauseKind::ConstArgHasType(
len,
Ty::new_unit(self.interner()),
))),
));
}
TyKind::Pat(base_ty, _pat) => {
self.require_sized(base_ty);
}
TyKind::Tuple(tys) => {
if let Some((_last, rest)) = tys.split_last() {
for &elem in rest {
self.require_sized(elem);
}
}
}
TyKind::RawPtr(_, _) => {
// Simple cases that are WF if their type args are WF.
}
TyKind::Alias(
rustc_type_ir::Projection | rustc_type_ir::Opaque | rustc_type_ir::Free,
data,
) => {
let obligations = self.nominal_obligations(data.def_id, data.args);
self.out.extend(obligations);
}
TyKind::Alias(rustc_type_ir::Inherent, _data) => {
return;
}
TyKind::Adt(def, args) => {
// WfNominalType
let obligations = self.nominal_obligations(def.def_id().0.into(), args);
self.out.extend(obligations);
}
TyKind::FnDef(did, args) => {
// HACK: Check the return type of function definitions for
// well-formedness to mostly fix #84533. This is still not
// perfect and there may be ways to abuse the fact that we
// ignore requirements with escaping bound vars. That's a
// more general issue however.
let fn_sig = tcx.fn_sig(did).instantiate(tcx, args);
fn_sig.output().skip_binder().visit_with(self);
let did = match did.0 {
hir_def::CallableDefId::FunctionId(id) => id.into(),
hir_def::CallableDefId::StructId(id) => SolverDefId::Ctor(Ctor::Struct(id)),
hir_def::CallableDefId::EnumVariantId(id) => {
SolverDefId::Ctor(Ctor::Enum(id))
}
};
let obligations = self.nominal_obligations(did, args);
self.out.extend(obligations);
}
TyKind::Ref(r, rty, _) => {
// WfReference
if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
let cause = ObligationCause::new();
self.out.push(Obligation::with_depth(
tcx,
cause,
self.recursion_depth,
self.param_env,
Binder::dummy(PredicateKind::Clause(ClauseKind::TypeOutlives(
rustc_type_ir::OutlivesPredicate(rty, r),
))),
));
}
}
TyKind::Coroutine(did, args, ..) => {
// Walk ALL the types in the coroutine: this will
// include the upvar types as well as the yield
// type. Note that this is mildly distinct from
// the closure case, where we have to be careful
// about the signature of the closure. We don't
// have the problem of implied bounds here since
// coroutines don't take arguments.
let obligations = self.nominal_obligations(did.0.into(), args);
self.out.extend(obligations);
}
TyKind::Closure(did, args) => {
// Note that we cannot skip the generic types
// types. Normally, within the fn
// body where they are created, the generics will
// always be WF, and outside of that fn body we
// are not directly inspecting closure types
// anyway, except via auto trait matching (which
// only inspects the upvar types).
// But when a closure is part of a type-alias-impl-trait
// then the function that created the defining site may
// have had more bounds available than the type alias
// specifies. This may cause us to have a closure in the
// hidden type that is not actually well formed and
// can cause compiler crashes when the user abuses unsafe
// code to procure such a closure.
// See tests/ui/type-alias-impl-trait/wf_check_closures.rs
let obligations = self.nominal_obligations(did.0.into(), args);
self.out.extend(obligations);
// Only check the upvar types for WF, not the rest
// of the types within. This is needed because we
// capture the signature and it may not be WF
// without the implied bounds. Consider a closure
// like `|x: &'a T|` -- it may be that `T: 'a` is
// not known to hold in the creator's context (and
// indeed the closure may not be invoked by its
// creator, but rather turned to someone who *can*
// verify that).
//
// The special treatment of closures here really
// ought not to be necessary either; the problem
// is related to #25860 -- there is no way for us
// to express a fn type complete with the implied
// bounds that it is assuming. I think in reality
// the WF rules around fn are a bit messed up, and
// that is the rot problem: `fn(&'a T)` should
// probably always be WF, because it should be
// shorthand for something like `where(T: 'a) {
// fn(&'a T) }`, as discussed in #25860.
let upvars = args.as_closure().tupled_upvars_ty();
return upvars.visit_with(self);
}
TyKind::CoroutineClosure(did, args) => {
// See the above comments. The same apply to coroutine-closures.
let obligations = self.nominal_obligations(did.0.into(), args);
self.out.extend(obligations);
let upvars = args.as_coroutine_closure().tupled_upvars_ty();
return upvars.visit_with(self);
}
TyKind::FnPtr(..) => {
// Let the visitor iterate into the argument/return
// types appearing in the fn signature.
}
TyKind::UnsafeBinder(_ty) => {}
TyKind::Dynamic(data, r) => {
// WfObject
//
// Here, we defer WF checking due to higher-ranked
// regions. This is perhaps not ideal.
self.add_wf_preds_for_dyn_ty(t, data.as_slice(), r);
// FIXME(#27579) RFC also considers adding trait
// obligations that don't refer to Self and
// checking those
if let Some(principal) = data.principal_def_id() {
self.out.push(Obligation::with_depth(
tcx,
ObligationCause::new(),
self.recursion_depth,
self.param_env,
Binder::dummy(PredicateKind::DynCompatible(principal)),
));
}
}
// Inference variables are the complicated case, since we don't
// know what type they are. We do two things:
//
// 1. Check if they have been resolved, and if so proceed with
// THAT type.
// 2. If not, we've at least simplified things (e.g., we went
// from `Vec?0>: WF` to `?0: WF`), so we can
// register a pending obligation and keep
// moving. (Goal is that an "inductive hypothesis"
// is satisfied to ensure termination.)
// See also the comment on `fn obligations`, describing cycle
// prevention, which happens before this can be reached.
TyKind::Infer(_) => {
let cause = ObligationCause::new();
self.out.push(Obligation::with_depth(
tcx,
cause,
self.recursion_depth,
self.param_env,
Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(t.into()))),
));
}
}
t.super_visit_with(self)
}
fn visit_const(&mut self, c: Const<'db>) -> Self::Result {
let tcx = self.interner();
match c.kind() {
ConstKind::Unevaluated(uv) => {
if !c.has_escaping_bound_vars() {
let predicate =
Binder::dummy(PredicateKind::Clause(ClauseKind::ConstEvaluatable(c)));
let cause = ObligationCause::new();
self.out.push(Obligation::with_depth(
tcx,
cause,
self.recursion_depth,
self.param_env,
predicate,
));
if let GeneralConstId::ConstId(uv_def) = uv.def.0
&& let ItemContainerId::ImplId(impl_) =
uv_def.loc(self.interner().db).container
&& self.interner().db.impl_signature(impl_).target_trait.is_none()
{
return; // Subtree is handled by above function
} else {
let obligations = self.nominal_obligations(uv.def.into(), uv.args);
self.out.extend(obligations);
}
}
}
ConstKind::Infer(_) => {
let cause = ObligationCause::new();
self.out.push(Obligation::with_depth(
tcx,
cause,
self.recursion_depth,
self.param_env,
Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(c.into()))),
));
}
ConstKind::Expr(_) => {
// FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the
// trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary
// as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated`
// which means that the `DefId` would have been typeck'd elsewhere. However in
// the future we may allow directly lowering to `ConstKind::Expr` in which case
// we would not be proving bounds we should.
let predicate =
Binder::dummy(PredicateKind::Clause(ClauseKind::ConstEvaluatable(c)));
let cause = ObligationCause::new();
self.out.push(Obligation::with_depth(
tcx,
cause,
self.recursion_depth,
self.param_env,
predicate,
));
}
ConstKind::Error(_)
| ConstKind::Param(_)
| ConstKind::Bound(..)
| ConstKind::Placeholder(..) => {
// These variants are trivially WF, so nothing to do here.
}
ConstKind::Value(..) => {
// FIXME: Enforce that values are structurally-matchable.
}
}
c.super_visit_with(self)
}
fn visit_predicate(&mut self, _p: Predicate<'db>) -> Self::Result {
panic!("predicate should not be checked for well-formedness");
}
}
/// Given an object type like `SomeTrait + Send`, computes the lifetime
/// bounds that must hold on the elided self type. These are derived
/// from the declarations of `SomeTrait`, `Send`, and friends -- if
/// they declare `trait SomeTrait : 'static`, for example, then
/// `'static` would appear in the list.
///
/// N.B., in some cases, particularly around higher-ranked bounds,
/// this function returns a kind of conservative approximation.
/// That is, all regions returned by this function are definitely
/// required, but there may be other region bounds that are not
/// returned, as well as requirements like `for<'a> T: 'a`.
///
/// Requires that trait definitions have been processed so that we can
/// elaborate predicates and walk supertraits.
pub(crate) fn object_region_bounds<'db>(
interner: DbInterner<'db>,
existential_predicates: &[Binder<'db, ExistentialPredicate<'db>>],
) -> Vec<Region<'db>> {
let erased_self_ty = Ty::new_unit(interner);
let predicates = existential_predicates
.iter()
.map(|predicate| predicate.with_self_ty(interner, erased_self_ty));
rustc_type_ir::elaborate::elaborate(interner, predicates)
.filter_map(|pred| {
debug!(?pred);
match pred.kind().skip_binder() {
ClauseKind::TypeOutlives(rustc_type_ir::OutlivesPredicate(ref t, ref r)) => {
// Search for a bound of the form `erased_self_ty
// : 'a`, but be wary of something like `for<'a>
// erased_self_ty : 'a` (we interpret a
// higher-ranked bound like that as 'static,
// though at present the code in `fulfill.rs`
// considers such bounds to be unsatisfiable, so
// it's kind of a moot point since you could never
// construct such an object, but this seems
// correct even if that code changes).
if t == &erased_self_ty && !r.has_escaping_bound_vars() {
Some(*r)
} else {
None
}
}
ClauseKind::Trait(_)
| ClauseKind::HostEffect(..)
| ClauseKind::RegionOutlives(_)
| ClauseKind::Projection(_)
| ClauseKind::ConstArgHasType(_, _)
| ClauseKind::WellFormed(_)
| ClauseKind::UnstableFeature(_)
| ClauseKind::ConstEvaluatable(_) => None,
}
})
.collect()
}
}