about summary refs log tree commit diff
path: root/compiler/rustc_trait_selection/src
diff options
context:
space:
mode:
authorStuart Cook <Zalathar@users.noreply.github.com>2024-12-24 14:05:21 +1100
committerGitHub <noreply@github.com>2024-12-24 14:05:21 +1100
commitc2f44cd32c08323849ee459c504b0430cfcb02f9 (patch)
treeb8312bfbd6b0b23549f4cedc02b3efe65b7d8994 /compiler/rustc_trait_selection/src
parent65fe42a5f46a17ac63b21684b7ec8327f1a4c86b (diff)
parent535bc781f81d26883783b726770da94e4918c711 (diff)
downloadrust-c2f44cd32c08323849ee459c504b0430cfcb02f9.tar.gz
rust-c2f44cd32c08323849ee459c504b0430cfcb02f9.zip
Rollup merge of #134638 - compiler-errors:fx-item-bounds, r=lcnr
Fix effect predicates from item bounds in old solver

r? lcnr
Diffstat (limited to 'compiler/rustc_trait_selection/src')
-rw-r--r--compiler/rustc_trait_selection/src/traits/effects.rs165
1 files changed, 137 insertions, 28 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs
index b17a489a857..91484ef99db 100644
--- a/compiler/rustc_trait_selection/src/traits/effects.rs
+++ b/compiler/rustc_trait_selection/src/traits/effects.rs
@@ -1,13 +1,15 @@
 use rustc_hir as hir;
-use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt};
+use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes};
 use rustc_infer::traits::{ImplSource, Obligation, PredicateObligation};
 use rustc_middle::span_bug;
 use rustc_middle::ty::fast_reject::DeepRejectCtxt;
 use rustc_middle::ty::{self, TypingMode};
+use rustc_type_ir::elaborate::elaborate;
 use rustc_type_ir::solve::NoSolution;
-use thin_vec::ThinVec;
+use thin_vec::{ThinVec, thin_vec};
 
 use super::SelectionContext;
+use super::normalize::normalize_with_depth_to;
 
 pub type HostEffectObligation<'tcx> = Obligation<'tcx, ty::HostEffectPredicate<'tcx>>;
 
@@ -38,6 +40,12 @@ pub fn evaluate_host_effect_obligation<'tcx>(
         Err(EvaluationFailure::NoSolution) => {}
     }
 
+    match evaluate_host_effect_from_item_bounds(selcx, obligation) {
+        Ok(result) => return Ok(result),
+        Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
+        Err(EvaluationFailure::NoSolution) => {}
+    }
+
     match evaluate_host_effect_from_selection_candiate(selcx, obligation) {
         Ok(result) => return Ok(result),
         Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
@@ -48,24 +56,45 @@ pub fn evaluate_host_effect_obligation<'tcx>(
 }
 
 fn match_candidate<'tcx>(
-    infcx: &InferCtxt<'tcx>,
+    selcx: &mut SelectionContext<'_, 'tcx>,
     obligation: &HostEffectObligation<'tcx>,
     candidate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
+    candidate_is_unnormalized: bool,
+    more_nested: impl FnOnce(&mut SelectionContext<'_, 'tcx>, &mut ThinVec<PredicateObligation<'tcx>>),
 ) -> Result<ThinVec<PredicateObligation<'tcx>>, NoSolution> {
     if !candidate.skip_binder().constness.satisfies(obligation.predicate.constness) {
         return Err(NoSolution);
     }
 
-    let candidate = infcx.instantiate_binder_with_fresh_vars(
+    let mut candidate = selcx.infcx.instantiate_binder_with_fresh_vars(
         obligation.cause.span,
         BoundRegionConversionTime::HigherRankedType,
         candidate,
     );
 
-    let mut nested = infcx
-        .at(&obligation.cause, obligation.param_env)
-        .eq(DefineOpaqueTypes::Yes, obligation.predicate.trait_ref, candidate.trait_ref)?
-        .into_obligations();
+    let mut nested = thin_vec![];
+
+    // Unlike param-env bounds, item bounds may not be normalized.
+    if candidate_is_unnormalized {
+        candidate = normalize_with_depth_to(
+            selcx,
+            obligation.param_env,
+            obligation.cause.clone(),
+            obligation.recursion_depth,
+            candidate,
+            &mut nested,
+        );
+    }
+
+    nested.extend(
+        selcx
+            .infcx
+            .at(&obligation.cause, obligation.param_env)
+            .eq(DefineOpaqueTypes::Yes, obligation.predicate.trait_ref, candidate.trait_ref)?
+            .into_obligations(),
+    );
+
+    more_nested(selcx, &mut nested);
 
     for nested in &mut nested {
         nested.set_depth_from_parent(obligation.recursion_depth);
@@ -82,36 +111,116 @@ fn evaluate_host_effect_from_bounds<'tcx>(
     let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
     let mut candidate = None;
 
-    for predicate in obligation.param_env.caller_bounds() {
-        let bound_predicate = predicate.kind();
-        if let ty::ClauseKind::HostEffect(data) = predicate.kind().skip_binder() {
-            let data = bound_predicate.rebind(data);
-            if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
-                continue;
+    for clause in obligation.param_env.caller_bounds() {
+        let bound_clause = clause.kind();
+        let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else {
+            continue;
+        };
+        let data = bound_clause.rebind(data);
+        if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
+            continue;
+        }
+
+        if !drcx
+            .args_may_unify(obligation.predicate.trait_ref.args, data.skip_binder().trait_ref.args)
+        {
+            continue;
+        }
+
+        let is_match =
+            infcx.probe(|_| match_candidate(selcx, obligation, data, false, |_, _| {}).is_ok());
+
+        if is_match {
+            if candidate.is_some() {
+                return Err(EvaluationFailure::Ambiguous);
+            } else {
+                candidate = Some(data);
             }
+        }
+    }
 
-            if !drcx.args_may_unify(
-                obligation.predicate.trait_ref.args,
-                data.skip_binder().trait_ref.args,
+    if let Some(data) = candidate {
+        Ok(match_candidate(selcx, obligation, data, false, |_, _| {})
+            .expect("candidate matched before, so it should match again"))
+    } else {
+        Err(EvaluationFailure::NoSolution)
+    }
+}
+
+fn evaluate_host_effect_from_item_bounds<'tcx>(
+    selcx: &mut SelectionContext<'_, 'tcx>,
+    obligation: &HostEffectObligation<'tcx>,
+) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
+    let infcx = selcx.infcx;
+    let tcx = infcx.tcx;
+    let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
+    let mut candidate = None;
+
+    let mut consider_ty = obligation.predicate.self_ty();
+    while let ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) = *consider_ty.kind() {
+        if tcx.is_conditionally_const(alias_ty.def_id) {
+            for clause in elaborate(
+                tcx,
+                tcx.explicit_implied_const_bounds(alias_ty.def_id)
+                    .iter_instantiated_copied(tcx, alias_ty.args)
+                    .map(|(trait_ref, _)| {
+                        trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness)
+                    }),
             ) {
-                continue;
-            }
+                let bound_clause = clause.kind();
+                let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else {
+                    unreachable!("should not elaborate non-HostEffect from HostEffect")
+                };
+                let data = bound_clause.rebind(data);
+                if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id {
+                    continue;
+                }
 
-            let is_match = infcx.probe(|_| match_candidate(infcx, obligation, data).is_ok());
+                if !drcx.args_may_unify(
+                    obligation.predicate.trait_ref.args,
+                    data.skip_binder().trait_ref.args,
+                ) {
+                    continue;
+                }
 
-            if is_match {
-                if candidate.is_some() {
-                    return Err(EvaluationFailure::Ambiguous);
-                } else {
-                    candidate = Some(data);
+                let is_match = infcx
+                    .probe(|_| match_candidate(selcx, obligation, data, true, |_, _| {}).is_ok());
+
+                if is_match {
+                    if candidate.is_some() {
+                        return Err(EvaluationFailure::Ambiguous);
+                    } else {
+                        candidate = Some((data, alias_ty));
+                    }
                 }
             }
         }
+
+        if kind != ty::Projection {
+            break;
+        }
+
+        consider_ty = alias_ty.self_ty();
     }
 
-    if let Some(data) = candidate {
-        Ok(match_candidate(infcx, obligation, data)
-            .expect("candidate matched before, so it should match again"))
+    if let Some((data, alias_ty)) = candidate {
+        Ok(match_candidate(selcx, obligation, data, true, |selcx, nested| {
+            // An alias bound only holds if we also check the const conditions
+            // of the alias, so we need to register those, too.
+            let const_conditions = normalize_with_depth_to(
+                selcx,
+                obligation.param_env,
+                obligation.cause.clone(),
+                obligation.recursion_depth,
+                tcx.const_conditions(alias_ty.def_id).instantiate(tcx, alias_ty.args),
+                nested,
+            );
+            nested.extend(const_conditions.into_iter().map(|(trait_ref, _)| {
+                obligation
+                    .with(tcx, trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness))
+            }));
+        })
+        .expect("candidate matched before, so it should match again"))
     } else {
         Err(EvaluationFailure::NoSolution)
     }