Auto merge of #110100 - compiler-errors:no-infer-pred-must-hold, r=jackh726

do not allow inference in `predicate_must_hold` (alternative approach)

See the FCP description for more info, but tl;dr is that we should not return `EvaluatedToOkModuloRegions` if an obligation may hold only with some choice of inference vars being constrained.

Attempts to solve this in the approach laid out by lcnr here: https://github.com/rust-lang/rust/pull/109558#discussion_r1147318134, rather than by eagerly replacing infer vars with placeholders which is a bit too restrictive.

r? `@ghost`
This commit is contained in:
bors 2023-05-19 03:36:37 +00:00
commit 19ca5692f6
4 changed files with 46 additions and 24 deletions

View File

@ -143,35 +143,36 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
fn pred_known_to_hold_modulo_regions<'tcx>( fn pred_known_to_hold_modulo_regions<'tcx>(
infcx: &InferCtxt<'tcx>, infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
pred: impl ToPredicate<'tcx> + TypeVisitable<TyCtxt<'tcx>>, pred: impl ToPredicate<'tcx>,
) -> bool { ) -> bool {
let has_non_region_infer = pred.has_non_region_infer();
let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred); let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred);
let result = infcx.evaluate_obligation_no_overflow(&obligation); let result = infcx.evaluate_obligation_no_overflow(&obligation);
debug!(?result); debug!(?result);
if result.must_apply_modulo_regions() && !has_non_region_infer { if result.must_apply_modulo_regions() {
true true
} else if result.may_apply() { } else if result.may_apply() {
// Because of inference "guessing", selection can sometimes claim // Sometimes obligations are ambiguous because the recursive evaluator
// to succeed while the success requires a guess. To ensure // is not smart enough, so we fall back to fulfillment when we're not certain
// this function's result remains infallible, we must confirm // that an obligation holds or not. Even still, we must make sure that
// that guess. While imperfect, I believe this is sound. // 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_in_snapshot(infcx);
ocx.register_obligation(obligation);
// The handling of regions in this area of the code is terrible, let errors = ocx.select_all_or_error();
// see issue #29149. We should be able to improve on this with match errors.as_slice() {
// NLL. // Only known to hold if we did no inference.
let ocx = ObligationCtxt::new(infcx); [] => infcx.shallow_resolve(goal) == goal,
ocx.register_obligation(obligation);
let errors = ocx.select_all_or_error(); errors => {
match errors.as_slice() { debug!(?errors);
[] => true, false
errors => { }
debug!(?errors);
false
} }
} })
} else { } else {
false false
} }

View File

@ -537,14 +537,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation: &PredicateObligation<'tcx>, obligation: &PredicateObligation<'tcx>,
) -> Result<EvaluationResult, OverflowError> { ) -> Result<EvaluationResult, OverflowError> {
self.evaluation_probe(|this| { self.evaluation_probe(|this| {
if this.tcx().trait_solver_next() { let goal =
this.evaluate_predicates_recursively_in_new_solver([obligation.clone()]) this.infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
let mut result = if this.tcx().trait_solver_next() {
this.evaluate_predicates_recursively_in_new_solver([obligation.clone()])?
} else { } else {
this.evaluate_predicate_recursively( this.evaluate_predicate_recursively(
TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()),
obligation.clone(), obligation.clone(),
) )?
};
// If the predicate has done any inference, then downgrade the
// result to ambiguous.
if this.infcx.shallow_resolve(goal) != goal {
result = result.max(EvaluatedToAmbig);
} }
Ok(result)
}) })
} }

View File

@ -1,5 +1,3 @@
// run-pass
#![allow(dead_code)] #![allow(dead_code)]
#![allow(drop_copy)] #![allow(drop_copy)]
@ -20,6 +18,7 @@ fn assert_impls_fn<R,T: Fn()->R>(_: &T){}
fn main() { fn main() {
let n = None; let n = None;
//~^ ERROR type annotations needed for `Option<T>`
let e = S(&n); let e = S(&n);
let f = || { let f = || {
// S being copy is critical for this to work // S being copy is critical for this to work

View File

@ -0,0 +1,14 @@
error[E0282]: type annotations needed for `Option<T>`
--> $DIR/copy-guessing.rs:20:9
|
LL | let n = None;
| ^
|
help: consider giving `n` an explicit type, where the type for type parameter `T` is specified
|
LL | let n: Option<T> = None;
| +++++++++++
error: aborting due to previous error
For more information about this error, try `rustc --explain E0282`.