about summary refs log tree commit diff
diff options
context:
space:
mode:
authoryifei <lyfmagic99@gmail.com>2023-01-19 00:38:09 +0800
committeryifei <lyfmagic99@gmail.com>2023-02-13 19:39:44 +0800
commitd16444803836fa5fd665f9f5e303a9f4d113edd6 (patch)
tree4a52c244ac2b9b5ea1eb79ea539769f7b522b035
parent21f683935257713eae8549e8b328367006097053 (diff)
downloadrust-d16444803836fa5fd665f9f5e303a9f4d113edd6.tar.gz
rust-d16444803836fa5fd665f9f5e303a9f4d113edd6.zip
fix: Unexpected trait bound not satisfied in HRTB
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs47
-rw-r--r--tests/ui/traits/issue-103563.rs75
2 files changed, 121 insertions, 1 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index f90da95d516..76ac4cfa4c4 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -27,6 +27,7 @@ use super::{
 
 use crate::infer::{InferCtxt, InferOk, TypeFreshener};
 use crate::traits::error_reporting::TypeErrCtxtExt;
+use crate::traits::project::try_normalize_with_depth_to;
 use crate::traits::project::ProjectAndUnifyResult;
 use crate::traits::project::ProjectionCacheKeyExt;
 use crate::traits::ProjectionCacheKey;
@@ -1017,7 +1018,51 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             return Ok(cycle_result);
         }
 
-        let (result, dep_node) = self.in_task(|this| this.evaluate_stack(&stack));
+        let (result, dep_node) = self.in_task(|this| {
+            let mut result = this.evaluate_stack(&stack)?;
+
+            // fix issue #103563, we don't normalize
+            // nested obligations which produced by `TraitDef` candidate
+            // (i.e. using bounds on assoc items as assumptions).
+            // because we don't have enough information to
+            // normalize these obligations before evaluating.
+            // so we will try to normalize the obligation and evaluate again.
+            // we will replace it with new solver in the future.
+            if EvaluationResult::EvaluatedToErr == result
+                && fresh_trait_pred.has_projections()
+                && fresh_trait_pred.is_global()
+            {
+                let mut nested_obligations = Vec::new();
+                let predicate = try_normalize_with_depth_to(
+                    this,
+                    param_env,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    obligation.predicate,
+                    &mut nested_obligations,
+                );
+                if predicate != obligation.predicate {
+                    let mut nested_result = EvaluationResult::EvaluatedToOk;
+                    for obligation in nested_obligations {
+                        nested_result = cmp::max(
+                            this.evaluate_predicate_recursively(stack.list(), obligation)?,
+                            nested_result,
+                        );
+                    }
+
+                    if nested_result.must_apply_modulo_regions() {
+                        let obligation = obligation.with(this.tcx(), predicate);
+                        result = cmp::max(
+                            nested_result,
+                            this.evaluate_trait_predicate_recursively(stack.list(), obligation)?,
+                        );
+                    }
+                }
+            }
+
+            Ok::<_, OverflowError>(result)
+        });
+
         let result = result?;
 
         if !result.must_apply_modulo_regions() {
diff --git a/tests/ui/traits/issue-103563.rs b/tests/ui/traits/issue-103563.rs
new file mode 100644
index 00000000000..cd3eea09b99
--- /dev/null
+++ b/tests/ui/traits/issue-103563.rs
@@ -0,0 +1,75 @@
+// build-pass
+
+fn main() {
+    let mut log_service = LogService { inner: Inner };
+    log_service.call(());
+}
+
+pub trait Service<Request> {
+    type Response;
+
+    fn call(&mut self, req: Request) -> Self::Response;
+}
+
+pub struct LogService<S> {
+    inner: S,
+}
+
+impl<T, U, S> Service<T> for LogService<S>
+where
+    S: Service<T, Response = U>,
+    U: Extension + 'static,
+    for<'a> U::Item<'a>: std::fmt::Debug,
+{
+    type Response = S::Response;
+
+    fn call(&mut self, req: T) -> Self::Response {
+        self.inner.call(req)
+    }
+}
+
+pub struct Inner;
+
+impl Service<()> for Inner {
+    type Response = Resp;
+
+    fn call(&mut self, req: ()) -> Self::Response {
+        Resp::A(req)
+    }
+}
+
+pub trait Extension {
+    type Item<'a>;
+
+    fn touch<F>(self, f: F) -> Self
+    where
+        for<'a> F: Fn(Self::Item<'a>);
+}
+
+pub enum Resp {
+    A(()),
+}
+
+impl Extension for Resp {
+    type Item<'a> = RespItem<'a>;
+    fn touch<F>(self, _f: F) -> Self
+    where
+        for<'a> F: Fn(Self::Item<'a>),
+    {
+        match self {
+            Self::A(a) => Self::A(a),
+        }
+    }
+}
+
+pub enum RespItem<'a> {
+    A(&'a ()),
+}
+
+impl<'a> std::fmt::Debug for RespItem<'a> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::A(arg0) => f.debug_tuple("A").field(arg0).finish(),
+        }
+    }
+}