diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2024-04-08 22:06:23 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-04-08 22:06:23 +0200 |
| commit | 984767e500941a3c0de9af4edac641be72b6d1ad (patch) | |
| tree | 72985af6d64d5e9daf5820f6a07c91e7a031ca15 | |
| parent | 9570ac4d285f7c007202e62abae24d235899a2fc (diff) | |
| parent | 68b4257ccf0c94f855a46b48e48c4c73559eff84 (diff) | |
| download | rust-984767e500941a3c0de9af4edac641be72b6d1ad.tar.gz rust-984767e500941a3c0de9af4edac641be72b6d1ad.zip | |
Rollup merge of #123578 - lqd:regression-123275, r=compiler-errors
Restore `pred_known_to_hold_modulo_regions` As requested by `@lcnr` in https://github.com/rust-lang/rust/issues/123275#issuecomment-2031885563 this PR restores `pred_known_to_hold_modulo_regions` to fix that "unexpected unsized tail" beta regression. This also adds the reduced repro from https://github.com/rust-lang/rust/issues/123275#issuecomment-2041222851 as a sub-optimal test is better than no test at all, and it'll also cover #108721. It still ICEs on master, even though https://github.com/phlip9/rustc-warp-ice doesn't on nightly anymore, since https://github.com/rust-lang/rust/pull/122493. Fixes #123275. r? `@compiler-errors` but feel free to close if you'd rather have a better test instead cc `@wesleywiser` who had signed up to do the revert Will need a backport if we go with this PR: `@rustbot` label +beta-nominated
| -rw-r--r-- | compiler/rustc_trait_selection/src/traits/mod.rs | 50 | ||||
| -rw-r--r-- | tests/ui/traits/pred-known-to-hold-modulo-regions-unsized-tail.rs | 244 |
2 files changed, 291 insertions, 3 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 2c8116b779b..98d5b466cd0 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))] 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 00000000000..4e8c19d600d --- /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<F, T> TryFuture for F +where + F: ?Sized + Future<Output = Option<T>>, +{ + type Ok = T; +} +trait Executor {} +struct Exec {} +trait HttpBody { + type Data; +} +trait ConnStreamExec<F> {} +impl<F> ConnStreamExec<F> for Exec where H2Stream<F>: Send {} +impl<E, F> ConnStreamExec<F> for E where E: Executor {} +struct H2Stream<F> { + _fut: F, +} +trait NewSvcExec<S, E, W: Watcher<S, E>> { + fn execute_new_svc(&mut self, _fut: NewSvcTask<S, E, W>) { + unimplemented!() + } +} +impl<S, E, W> NewSvcExec<S, E, W> for Exec where W: Watcher<S, E> {} +trait Watcher<S, E> { + type Future; +} +struct NoopWatcher; +impl<S, E> Watcher<S, E> for NoopWatcher +where + S: HttpService, + E: ConnStreamExec<S::Future>, +{ + type Future = Option<<<S as HttpService>::ResBody as HttpBody>::Data>; +} +trait Service<Request> { + type Response; + type Future; +} +trait HttpService { + type ResBody: HttpBody; + type Future; +} +struct Body {} +impl HttpBody for Body { + type Data = String; +} +impl<S> HttpService for S +where + S: Service<(), Response = ()>, +{ + type ResBody = Body; + type Future = S::Future; +} +trait MakeServiceRef<Target> { + type ResBody; + type Service: HttpService<ResBody = Self::ResBody>; +} +impl<T, Target, S, F> MakeServiceRef<Target> 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, Target, Ret>(_f: F) -> MakeServiceFn<F> +where + F: FnMut(&Target) -> Ret, + Ret: Future, +{ + unimplemented!() +} +struct MakeServiceFn<F> { + _func: F, +} +impl<'t, F, Ret, Target, Svc> Service<&'t Target> for MakeServiceFn<F> +where + F: FnMut(&Target) -> Ret, + Ret: Future<Output = Option<Svc>>, +{ + type Response = Svc; + type Future = Option<()>; +} +struct AddrIncoming {} +struct Server<I, S, E> { + _incoming: I, + _make_service: S, + _protocol: E, +} +impl<I, S, E, B> Server<I, S, E> +where + S: MakeServiceRef<(), ResBody = B>, + B: HttpBody, + E: ConnStreamExec<<S::Service as HttpService>::Future>, + E: NewSvcExec<S::Service, E, NoopWatcher>, +{ + fn serve(&mut self) { + let fut = NewSvcTask::new(); + self._protocol.execute_new_svc(fut); + } +} +fn serve<S>(_make_service: S) -> Server<AddrIncoming, S, Exec> { + unimplemented!() +} +struct NewSvcTask<S, E, W: Watcher<S, E>> { + _state: State<S, E, W>, +} +struct State<S, E, W: Watcher<S, E>> { + _fut: W::Future, +} +impl<S, E, W: Watcher<S, E>> NewSvcTask<S, E, W> { + fn new() -> Self { + unimplemented!() + } +} +trait Filter { + type Extract; + type Future; + fn map<F>(self, _fun: F) -> MapFilter<Self, F> + where + Self: Sized, + { + unimplemented!() + } + fn wrap_with<W>(self, _wrapper: W) -> W::Wrapped + where + Self: Sized, + W: Wrap<Self>, + { + unimplemented!() + } +} +fn service<F>(_filter: F) -> FilteredService<F> +where + F: Filter, +{ + unimplemented!() +} +struct FilteredService<F> { + _filter: F, +} +impl<F> Service<()> for FilteredService<F> +where + F: Filter, +{ + type Response = (); + type Future = FilteredFuture<F::Future>; +} +struct FilteredFuture<F> { + _fut: F, +} +struct MapFilter<T, F> { + _filter: T, + _func: F, +} +impl<T, F> Filter for MapFilter<T, F> +where + T: Filter, + F: Func<T::Extract>, +{ + type Extract = F::Output; + type Future = MapFilterFuture<T, F>; +} +struct MapFilterFuture<T: Filter, F> { + _extract: T::Future, + _func: F, +} +trait Wrap<F> { + type Wrapped; +} +fn make_filter_fn<F, U>(_func: F) -> FilterFn<F> +where + F: Fn() -> U, +{ + unimplemented!() +} +struct FilterFn<F> { + _func: F, +} +impl<F, U> Filter for FilterFn<F> +where + F: Fn() -> U, + U: TryFuture, + U::Ok: Send, +{ + type Extract = U::Ok; + type Future = Option<U>; +} +fn trace<F>(_func: F) -> Trace<F> +where + F: Fn(), +{ + unimplemented!() +} +struct Trace<F> { + _func: F, +} +impl<FN, F> Wrap<F> for Trace<FN> { + type Wrapped = WithTrace<FN, F>; +} +struct WithTrace<FN, F> { + _filter: F, + _trace: FN, +} +impl<FN, F> Filter for WithTrace<FN, F> +where + F: Filter, +{ + type Extract = (); + type Future = (F::Future, fn(F::Extract)); +} +trait Func<Args> { + type Output; +} +impl<F, R> 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(); +} |
