mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-29 10:13:54 +00:00
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:
commit
19ca5692f6
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
14
tests/ui/traits/copy-guessing.stderr
Normal file
14
tests/ui/traits/copy-guessing.stderr
Normal 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`.
|
Loading…
Reference in New Issue
Block a user