diff options
| author | bors <bors@rust-lang.org> | 2022-08-09 16:39:43 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-08-09 16:39:43 +0000 |
| commit | 63e4312e6bd50ec9859c363402209809fb8155d5 (patch) | |
| tree | 4953dbef9f6b944e18af8388d48adf94a4ce92b2 /compiler | |
| parent | 6d3f1beae1720055e5a30f4dbe7a9e7fb810c65e (diff) | |
| parent | 8691b96eee9635756b957ac8d9e5bd963cb73f12 (diff) | |
| download | rust-63e4312e6bd50ec9859c363402209809fb8155d5.tar.gz rust-63e4312e6bd50ec9859c363402209809fb8155d5.zip | |
Auto merge of #99217 - lcnr:implied-bounds-pre-norm, r=lcnr
consider unnormalized types for implied bounds extracted, and slightly modified, from #98900 The idea here is that generally, rustc is split into things which can assume its inputs are well formed[^1], and things which have verify that themselves. Generally most predicates should only deal with well formed inputs, e.g. a `&'a &'b (): Trait` predicate should be able to assume that `'b: 'a` holds. Normalization can loosen wf requirements (see #91068) and must therefore not be used in places which still have to check well formedness. The only such place should hopefully be `WellFormed` predicates fixes #87748 and #98543 r? `@jackh726` cc `@rust-lang/types` [^1]: These places may still encounter non-wf inputs and have to deal with them without causing an ICE as we may check for well formedness out of order.
Diffstat (limited to 'compiler')
8 files changed, 90 insertions, 26 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 16796091830..9fab7ad914a 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -6,7 +6,7 @@ use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; use rustc_infer::infer::{self, InferCtxt, SubregionOrigin}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::TypeVisitable; +use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{Span, DUMMY_SP}; @@ -109,23 +109,11 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { self.add_outlives(r1_vid, r2_vid); } - GenericArgKind::Type(mut t1) => { + GenericArgKind::Type(t1) => { // we don't actually use this for anything, but // the `TypeOutlives` code needs an origin. let origin = infer::RelateParamBound(DUMMY_SP, t1, None); - // Placeholder regions need to be converted now because it may - // create new region variables, which can't be done later when - // verifying these bounds. - if t1.has_placeholders() { - t1 = tcx.fold_regions(t1, |r, _| match *r { - ty::RePlaceholder(placeholder) => { - self.constraints.placeholder_region(self.infcx, placeholder) - } - _ => r, - }); - } - TypeOutlives::new( &mut *self, tcx, @@ -143,6 +131,25 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { } } + /// Placeholder regions need to be converted eagerly because it may + /// create new region variables, which we must not do when verifying + /// our region bounds. + /// + /// FIXME: This should get removed once higher ranked region obligations + /// are dealt with during trait solving. + fn replace_placeholders_with_nll<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T { + if value.has_placeholders() { + self.tcx.fold_regions(value, |r, _| match *r { + ty::RePlaceholder(placeholder) => { + self.constraints.placeholder_region(self.infcx, placeholder) + } + _ => r, + }) + } else { + value + } + } + fn verify_to_type_test( &mut self, generic_kind: GenericKind<'tcx>, @@ -150,7 +157,6 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { verify_bound: VerifyBound<'tcx>, ) -> TypeTest<'tcx> { let lower_bound = self.to_region_vid(region); - TypeTest { generic_kind, lower_bound, locations: self.locations, verify_bound } } @@ -198,6 +204,8 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<' a: ty::Region<'tcx>, bound: VerifyBound<'tcx>, ) { + let kind = self.replace_placeholders_with_nll(kind); + let bound = self.replace_placeholders_with_nll(bound); let type_test = self.verify_to_type_test(kind, a, bound); self.add_type_test(type_test); } diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index cc0318ede54..74655369faf 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -242,10 +242,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { let constraint_sets: Vec<_> = unnormalized_input_output_tys .flat_map(|ty| { debug!("build: input_or_output={:?}", ty); - // We only add implied bounds for the normalized type as the unnormalized - // type may not actually get checked by the caller. - // - // Can otherwise be unsound, see #91068. + // We add implied bounds from both the unnormalized and normalized ty. + // See issue #87748 + let constraints_implied1 = self.add_implied_bounds(ty); let TypeOpOutput { output: norm_ty, constraints: constraints1, .. } = self .param_env .and(type_op::normalize::Normalize::new(ty)) @@ -273,9 +272,10 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // } // ``` // Both &Self::Bar and &() are WF - let constraints_implied = self.add_implied_bounds(norm_ty); + let constraints_implied2 = + if ty != norm_ty { self.add_implied_bounds(norm_ty) } else { None }; normalized_inputs_and_output.push(norm_ty); - constraints1.into_iter().chain(constraints_implied) + constraints1.into_iter().chain(constraints_implied1).chain(constraints_implied2) }) .collect(); diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index d32b1edcd8f..7bf7f7357bf 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1448,9 +1448,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { )) }); debug!(?sig); - let sig = self.normalize(sig, term_location); - self.check_call_dest(body, term, &sig, *destination, target, term_location); - + // IMPORTANT: We have to prove well formed for the function signature before + // we normalize it, as otherwise types like `<&'a &'b () as Trait>::Assoc` + // get normalized away, causing us to ignore the `'b: 'a` bound used by the function. + // + // Normalization results in a well formed type if the input is well formed, so we + // don't have to check it twice. + // + // See #91068 for an example. self.prove_predicates( sig.inputs_and_output .iter() @@ -1458,6 +1463,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { term_location.to_locations(), ConstraintCategory::Boring, ); + let sig = self.normalize(sig, term_location); + self.check_call_dest(body, term, &sig, *destination, target, term_location); // The ordinary liveness rules will ensure that all // regions in the type of the callee are live here. We diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 0d4472a1cfd..780e6ead10e 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -187,7 +187,7 @@ pub enum GenericKind<'tcx> { /// } /// ``` /// This is described with an `AnyRegion('a, 'b)` node. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, TypeFoldable, TypeVisitable)] pub enum VerifyBound<'tcx> { /// See [`VerifyIfEq`] docs IfEq(ty::Binder<'tcx, VerifyIfEq<'tcx>>), diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 79309097e78..a7833ab6431 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -594,6 +594,29 @@ impl<'tcx> Predicate<'tcx> { } self } + + /// Whether this projection can be soundly normalized. + /// + /// Wf predicates must not be normalized, as normalization + /// can remove required bounds which would cause us to + /// unsoundly accept some programs. See #91068. + #[inline] + pub fn allow_normalization(self) -> bool { + match self.kind().skip_binder() { + PredicateKind::WellFormed(_) => false, + PredicateKind::Trait(_) + | PredicateKind::RegionOutlives(_) + | PredicateKind::TypeOutlives(_) + | PredicateKind::Projection(_) + | PredicateKind::ObjectSafe(_) + | PredicateKind::ClosureKind(_, _, _) + | PredicateKind::Subtype(_) + | PredicateKind::Coerce(_) + | PredicateKind::ConstEvaluatable(_) + | PredicateKind::ConstEquate(_, _) + | PredicateKind::TypeWellFormedFromEnv(_) => true, + } + } } impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Predicate<'tcx> { diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index adf47ece69d..74625cc7bb7 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -619,6 +619,15 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { constant.eval(self.selcx.tcx(), self.param_env) } } + + #[inline] + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) { + p.super_fold_with(self) + } else { + p + } + } } pub struct BoundVarReplacer<'me, 'tcx> { diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 449d7a7b47b..38b3a4b7253 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -351,4 +351,16 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { mir::ConstantKind::Val(_, _) => constant.try_super_fold_with(self)?, }) } + + #[inline] + fn try_fold_predicate( + &mut self, + p: ty::Predicate<'tcx>, + ) -> Result<ty::Predicate<'tcx>, Self::Error> { + if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) { + p.try_super_fold_with(self) + } else { + Ok(p) + } + } } diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 666498403c4..15a995ae59a 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -264,8 +264,13 @@ fn compare_predicate_entailment<'tcx>( let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs); let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig); + // Next, add all inputs and output as well-formed tys. Importantly, + // we have to do this before normalization, since the normalized ty may + // not contain the input parameters. See issue #87748. + wf_tys.extend(trait_sig.inputs_and_output.iter()); let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig); - // Add the resulting inputs and output as well-formed. + // We also have to add the normalized trait signature + // as we don't normalize during implied bounds computation. wf_tys.extend(trait_sig.inputs_and_output.iter()); let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)); |
