From d16444803836fa5fd665f9f5e303a9f4d113edd6 Mon Sep 17 00:00:00 2001 From: yifei Date: Thu, 19 Jan 2023 00:38:09 +0800 Subject: [PATCH] fix: Unexpected trait bound not satisfied in HRTB --- .../src/traits/select/mod.rs | 47 +++++++++++- tests/ui/traits/issue-103563.rs | 75 +++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 tests/ui/traits/issue-103563.rs diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index f90da95d51668..76ac4cfa4c4c7 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 0000000000000..cd3eea09b997d --- /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 { + type Response; + + fn call(&mut self, req: Request) -> Self::Response; +} + +pub struct LogService { + inner: S, +} + +impl Service for LogService +where + S: Service, + 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(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(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(), + } + } +}