From 54f8db84329116889fce5d58292b5cc6b83642e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Sat, 6 Apr 2024 23:25:58 +0000 Subject: [PATCH 1/2] add non-regression test for issue 123275 --- ...own-to-hold-modulo-regions-unsized-tail.rs | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 tests/ui/traits/pred-known-to-hold-modulo-regions-unsized-tail.rs diff --git a/tests/ui/traits/pred-known-to-hold-modulo-regions-unsized-tail.rs b/tests/ui/traits/pred-known-to-hold-modulo-regions-unsized-tail.rs new file mode 100644 index 0000000000000..4e8c19d600d61 --- /dev/null +++ b/tests/ui/traits/pred-known-to-hold-modulo-regions-unsized-tail.rs @@ -0,0 +1,244 @@ +// This is a non-regression test for issues #108721 and its duplicate #123275 (hopefully, because +// the test is still convoluted and the ICE is fiddly). +// +// `pred_known_to_hold_modulo_regions` prevented "unexpected unsized tail" ICEs with warp/hyper but +// was unknowingly removed in #120463. + +//@ build-pass: the ICE happened in codegen + +use std::future::Future; +trait TryFuture: Future { + type Ok; +} +impl TryFuture for F +where + F: ?Sized + Future>, +{ + type Ok = T; +} +trait Executor {} +struct Exec {} +trait HttpBody { + type Data; +} +trait ConnStreamExec {} +impl ConnStreamExec for Exec where H2Stream: Send {} +impl ConnStreamExec for E where E: Executor {} +struct H2Stream { + _fut: F, +} +trait NewSvcExec> { + fn execute_new_svc(&mut self, _fut: NewSvcTask) { + unimplemented!() + } +} +impl NewSvcExec for Exec where W: Watcher {} +trait Watcher { + type Future; +} +struct NoopWatcher; +impl Watcher for NoopWatcher +where + S: HttpService, + E: ConnStreamExec, +{ + type Future = Option<<::ResBody as HttpBody>::Data>; +} +trait Service { + type Response; + type Future; +} +trait HttpService { + type ResBody: HttpBody; + type Future; +} +struct Body {} +impl HttpBody for Body { + type Data = String; +} +impl HttpService for S +where + S: Service<(), Response = ()>, +{ + type ResBody = Body; + type Future = S::Future; +} +trait MakeServiceRef { + type ResBody; + type Service: HttpService; +} +impl MakeServiceRef for T +where + T: for<'a> Service<&'a Target, Response = S, Future = F>, + S: HttpService, +{ + type Service = S; + type ResBody = S::ResBody; +} +fn make_service_fn(_f: F) -> MakeServiceFn +where + F: FnMut(&Target) -> Ret, + Ret: Future, +{ + unimplemented!() +} +struct MakeServiceFn { + _func: F, +} +impl<'t, F, Ret, Target, Svc> Service<&'t Target> for MakeServiceFn +where + F: FnMut(&Target) -> Ret, + Ret: Future>, +{ + type Response = Svc; + type Future = Option<()>; +} +struct AddrIncoming {} +struct Server { + _incoming: I, + _make_service: S, + _protocol: E, +} +impl Server +where + S: MakeServiceRef<(), ResBody = B>, + B: HttpBody, + E: ConnStreamExec<::Future>, + E: NewSvcExec, +{ + fn serve(&mut self) { + let fut = NewSvcTask::new(); + self._protocol.execute_new_svc(fut); + } +} +fn serve(_make_service: S) -> Server { + unimplemented!() +} +struct NewSvcTask> { + _state: State, +} +struct State> { + _fut: W::Future, +} +impl> NewSvcTask { + fn new() -> Self { + unimplemented!() + } +} +trait Filter { + type Extract; + type Future; + fn map(self, _fun: F) -> MapFilter + where + Self: Sized, + { + unimplemented!() + } + fn wrap_with(self, _wrapper: W) -> W::Wrapped + where + Self: Sized, + W: Wrap, + { + unimplemented!() + } +} +fn service(_filter: F) -> FilteredService +where + F: Filter, +{ + unimplemented!() +} +struct FilteredService { + _filter: F, +} +impl Service<()> for FilteredService +where + F: Filter, +{ + type Response = (); + type Future = FilteredFuture; +} +struct FilteredFuture { + _fut: F, +} +struct MapFilter { + _filter: T, + _func: F, +} +impl Filter for MapFilter +where + T: Filter, + F: Func, +{ + type Extract = F::Output; + type Future = MapFilterFuture; +} +struct MapFilterFuture { + _extract: T::Future, + _func: F, +} +trait Wrap { + type Wrapped; +} +fn make_filter_fn(_func: F) -> FilterFn +where + F: Fn() -> U, +{ + unimplemented!() +} +struct FilterFn { + _func: F, +} +impl Filter for FilterFn +where + F: Fn() -> U, + U: TryFuture, + U::Ok: Send, +{ + type Extract = U::Ok; + type Future = Option; +} +fn trace(_func: F) -> Trace +where + F: Fn(), +{ + unimplemented!() +} +struct Trace { + _func: F, +} +impl Wrap for Trace { + type Wrapped = WithTrace; +} +struct WithTrace { + _filter: F, + _trace: FN, +} +impl Filter for WithTrace +where + F: Filter, +{ + type Extract = (); + type Future = (F::Future, fn(F::Extract)); +} +trait Func { + type Output; +} +impl Func<()> for F +where + F: Fn() -> R, +{ + type Output = R; +} +fn main() { + let make_service = make_service_fn(|_| { + let tracer = trace(|| unimplemented!()); + let filter = make_filter_fn(|| std::future::ready(Some(()))) + .map(|| "Hello, world") + .wrap_with(tracer); + let svc = service(filter); + std::future::ready(Some(svc)) + }); + let mut server = serve(make_service); + server.serve(); +} From 68b4257ccf0c94f855a46b48e48c4c73559eff84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Sat, 6 Apr 2024 23:29:59 +0000 Subject: [PATCH 2/2] Revert "remove `pred_known_to_hold_modulo_regions`" This reverts commit 399a258f46074740862568b124c02f7b7d04638c. --- .../rustc_trait_selection/src/traits/mod.rs | 50 +++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 2c8116b779b45..98d5b466cd00d 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -119,7 +119,9 @@ pub fn predicates_for_generics<'tcx>( /// Determines whether the type `ty` is known to meet `bound` and /// returns true if so. Returns false if `ty` either does not meet -/// `bound` or is not known to meet bound. +/// `bound` or is not known to meet bound (note that this is +/// conservative towards *no impl*, which is the opposite of the +/// `evaluate` methods). pub fn type_known_to_meet_bound_modulo_regions<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -127,8 +129,50 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>( def_id: DefId, ) -> bool { let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]); - let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, trait_ref); - infcx.predicate_must_hold_modulo_regions(&obligation) + pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref) +} + +/// FIXME(@lcnr): this function doesn't seem right and shouldn't exist? +/// +/// Ping me on zulip if you want to use this method and need help with finding +/// an appropriate replacement. +#[instrument(level = "debug", skip(infcx, param_env, pred), ret)] +fn pred_known_to_hold_modulo_regions<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + pred: impl ToPredicate<'tcx>, +) -> bool { + let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred); + + let result = infcx.evaluate_obligation_no_overflow(&obligation); + debug!(?result); + + if result.must_apply_modulo_regions() { + true + } else if result.may_apply() { + // Sometimes obligations are ambiguous because the recursive evaluator + // is not smart enough, so we fall back to fulfillment when we're not certain + // that an obligation holds or not. Even still, we must make sure that + // the we do no inference in the process of checking this obligation. + let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env)); + infcx.probe(|_| { + let ocx = ObligationCtxt::new(infcx); + ocx.register_obligation(obligation); + + let errors = ocx.select_all_or_error(); + match errors.as_slice() { + // Only known to hold if we did no inference. + [] => infcx.shallow_resolve(goal) == goal, + + errors => { + debug!(?errors); + false + } + } + }) + } else { + false + } } #[instrument(level = "debug", skip(tcx, elaborated_env))]