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
This commit is contained in:
commit
984767e500
@ -119,7 +119,9 @@ pub fn predicates_for_generics<'tcx>(
|
|||||||
|
|
||||||
/// Determines whether the type `ty` is known to meet `bound` and
|
/// Determines whether the type `ty` is known to meet `bound` and
|
||||||
/// returns true if so. Returns false if `ty` either does not meet
|
/// 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>(
|
pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
|
||||||
infcx: &InferCtxt<'tcx>,
|
infcx: &InferCtxt<'tcx>,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
@ -127,8 +129,50 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
|
|||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]);
|
let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]);
|
||||||
let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, trait_ref);
|
pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref)
|
||||||
infcx.predicate_must_hold_modulo_regions(&obligation)
|
}
|
||||||
|
|
||||||
|
/// 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))]
|
#[instrument(level = "debug", skip(tcx, elaborated_env))]
|
||||||
|
@ -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();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user