Skip to content

Commit b9306e1

Browse files
committed
don't normalize wf predicates
this allows us to soundly use unnormalized projections for wf
1 parent 2fdbf07 commit b9306e1

26 files changed

+226
-102
lines changed

compiler/rustc_borrowck/src/type_check/constraint_conversion.rs

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound};
66
use rustc_infer::infer::{self, InferCtxt, SubregionOrigin};
77
use rustc_middle::mir::ConstraintCategory;
88
use rustc_middle::ty::subst::GenericArgKind;
9-
use rustc_middle::ty::TypeVisitable;
9+
use rustc_middle::ty::TypeFoldable;
1010
use rustc_middle::ty::{self, TyCtxt};
1111
use rustc_span::{Span, DUMMY_SP};
1212

@@ -99,23 +99,11 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
9999
self.add_outlives(r1_vid, r2_vid);
100100
}
101101

102-
GenericArgKind::Type(mut t1) => {
102+
GenericArgKind::Type(t1) => {
103103
// we don't actually use this for anything, but
104104
// the `TypeOutlives` code needs an origin.
105105
let origin = infer::RelateParamBound(DUMMY_SP, t1, None);
106106

107-
// Placeholder regions need to be converted now because it may
108-
// create new region variables, which can't be done later when
109-
// verifying these bounds.
110-
if t1.has_placeholders() {
111-
t1 = tcx.fold_regions(t1, |r, _| match *r {
112-
ty::RePlaceholder(placeholder) => {
113-
self.constraints.placeholder_region(self.infcx, placeholder)
114-
}
115-
_ => r,
116-
});
117-
}
118-
119107
TypeOutlives::new(
120108
&mut *self,
121109
tcx,
@@ -133,14 +121,32 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
133121
}
134122
}
135123

124+
/// Placeholder regions need to be converted eagerly because it may
125+
/// create new region variables, which we must not do when verifying
126+
/// our region bounds.
127+
///
128+
/// FIXME: This should get removed once higher ranked region obligations
129+
/// are dealt with during trait solving.
130+
fn replace_placeholders_with_nll<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T {
131+
if value.has_placeholders() {
132+
self.tcx.fold_regions(value, |r, _| match *r {
133+
ty::RePlaceholder(placeholder) => {
134+
self.constraints.placeholder_region(self.infcx, placeholder)
135+
}
136+
_ => r,
137+
})
138+
} else {
139+
value
140+
}
141+
}
142+
136143
fn verify_to_type_test(
137144
&mut self,
138145
generic_kind: GenericKind<'tcx>,
139146
region: ty::Region<'tcx>,
140147
verify_bound: VerifyBound<'tcx>,
141148
) -> TypeTest<'tcx> {
142149
let lower_bound = self.to_region_vid(region);
143-
144150
TypeTest { generic_kind, lower_bound, locations: self.locations, verify_bound }
145151
}
146152

@@ -188,6 +194,8 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'
188194
a: ty::Region<'tcx>,
189195
bound: VerifyBound<'tcx>,
190196
) {
197+
let kind = self.replace_placeholders_with_nll(kind);
198+
let bound = self.replace_placeholders_with_nll(bound);
191199
let type_test = self.verify_to_type_test(kind, a, bound);
192200
self.add_type_test(type_test);
193201
}

compiler/rustc_borrowck/src/type_check/free_region_relations.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
253253
let constraint_sets: Vec<_> = unnormalized_input_output_tys
254254
.flat_map(|ty| {
255255
debug!("build: input_or_output={:?}", ty);
256-
// We only add implied bounds for the normalized type as the unnormalized
257-
// type may not actually get checked by the caller.
258-
//
259-
// Can otherwise be unsound, see #91068.
256+
// We add implied bounds from both the unnormalized and normalized ty.
257+
// See issue #87748
258+
let constraints_implied1 = self.add_implied_bounds(ty);
260259
let TypeOpOutput { output: norm_ty, constraints: constraints1, .. } = self
261260
.param_env
262261
.and(type_op::normalize::Normalize::new(ty))
@@ -284,9 +283,10 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
284283
// }
285284
// ```
286285
// Both &Self::Bar and &() are WF
287-
let constraints_implied = self.add_implied_bounds(norm_ty);
286+
let constraints_implied2 =
287+
if ty != norm_ty { self.add_implied_bounds(norm_ty) } else { None };
288288
normalized_inputs_and_output.push(norm_ty);
289-
constraints1.into_iter().chain(constraints_implied)
289+
constraints1.into_iter().chain(constraints_implied1).chain(constraints_implied2)
290290
})
291291
.collect();
292292

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,16 +1469,23 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
14691469
))
14701470
});
14711471
debug!(?sig);
1472-
let sig = self.normalize(sig, term_location);
1473-
self.check_call_dest(body, term, &sig, *destination, target, term_location);
1474-
1472+
// IMPORTANT: We have to prove well formed for the function signature before
1473+
// we normalize it, as otherwise types like `<&'a &'b () as Trait>::Assoc`
1474+
// get normalized away, causing us to ignore the `'b: 'a` bound used by the function.
1475+
//
1476+
// Normalization results in a well formed type if the input is well formed, so we
1477+
// don't have to check it twice.
1478+
//
1479+
// See #91068 for an example.
14751480
self.prove_predicates(
14761481
sig.inputs_and_output
14771482
.iter()
14781483
.map(|ty| ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into()))),
14791484
term_location.to_locations(),
14801485
ConstraintCategory::Boring,
14811486
);
1487+
let sig = self.normalize(sig, term_location);
1488+
self.check_call_dest(body, term, &sig, *destination, target, term_location);
14821489

14831490
// The ordinary liveness rules will ensure that all
14841491
// regions in the type of the callee are live here. We

compiler/rustc_infer/src/infer/region_constraints/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ pub enum GenericKind<'tcx> {
188188
/// }
189189
/// ```
190190
/// This is described with an `AnyRegion('a, 'b)` node.
191-
#[derive(Debug, Clone)]
191+
#[derive(Debug, Clone, TypeFoldable, TypeVisitable)]
192192
pub enum VerifyBound<'tcx> {
193193
/// See [`VerifyIfEq`] docs
194194
IfEq(ty::Binder<'tcx, VerifyIfEq<'tcx>>),

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,29 @@ impl<'tcx> Predicate<'tcx> {
588588
}
589589
self
590590
}
591+
592+
/// Whether this projection can be soundly normalized.
593+
///
594+
/// Wf predicates must not be normalized, as normalization
595+
/// can remove required bounds which would cause us to
596+
/// unsoundly accept some programs. See #91068.
597+
#[inline]
598+
pub fn allow_normalization(self) -> bool {
599+
match self.kind().skip_binder() {
600+
PredicateKind::WellFormed(_) => false,
601+
PredicateKind::Trait(_)
602+
| PredicateKind::RegionOutlives(_)
603+
| PredicateKind::TypeOutlives(_)
604+
| PredicateKind::Projection(_)
605+
| PredicateKind::ObjectSafe(_)
606+
| PredicateKind::ClosureKind(_, _, _)
607+
| PredicateKind::Subtype(_)
608+
| PredicateKind::Coerce(_)
609+
| PredicateKind::ConstEvaluatable(_)
610+
| PredicateKind::ConstEquate(_, _)
611+
| PredicateKind::TypeWellFormedFromEnv(_) => true,
612+
}
613+
}
591614
}
592615

593616
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Predicate<'tcx> {

compiler/rustc_trait_selection/src/traits/project.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,15 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
619619
constant.eval(self.selcx.tcx(), self.param_env)
620620
}
621621
}
622+
623+
#[inline]
624+
fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
625+
if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) {
626+
p.super_fold_with(self)
627+
} else {
628+
p
629+
}
630+
}
622631
}
623632

624633
pub struct BoundVarReplacer<'me, 'tcx> {

compiler/rustc_trait_selection/src/traits/query/normalize.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,4 +351,16 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
351351
mir::ConstantKind::Val(_, _) => constant.try_super_fold_with(self)?,
352352
})
353353
}
354+
355+
#[inline]
356+
fn try_fold_predicate(
357+
&mut self,
358+
p: ty::Predicate<'tcx>,
359+
) -> Result<ty::Predicate<'tcx>, Self::Error> {
360+
if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) {
361+
p.try_super_fold_with(self)
362+
} else {
363+
Ok(p)
364+
}
365+
}
354366
}

compiler/rustc_typeck/src/check/compare_method.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,13 @@ fn compare_predicate_entailment<'tcx>(
262262

263263
let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
264264
let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig);
265+
// Next, add all inputs and output as well-formed tys. Importantly,
266+
// we have to do this before normalization, since the normalized ty may
267+
// not contain the input parameters. See issue #87748.
268+
wf_tys.extend(trait_sig.inputs_and_output.iter());
265269
let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig);
266-
// Add the resulting inputs and output as well-formed.
270+
// We also have to add the normalized trait signature
271+
// as we don't normalize during implied bounds computation.
267272
wf_tys.extend(trait_sig.inputs_and_output.iter());
268273
let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig));
269274

src/test/ui/associated-types/issue-59324.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ pub trait ThriftService<Bug: NotFoo>:
1515
{
1616
fn get_service(
1717
//~^ ERROR the trait bound `Bug: Foo` is not satisfied
18+
//~| ERROR the trait bound `Bug: Foo` is not satisfied
1819
&self,
1920
) -> Self::AssocType;
20-
//~^ the trait bound `Bug: Foo` is not satisfied
2121
}
2222

2323
fn with_factory<H>(factory: dyn ThriftService<()>) {}

src/test/ui/associated-types/issue-59324.stderr

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ LL | |
2020
LL | |
2121
LL | | Service<AssocType = <Bug as Foo>::OnlyFoo>
2222
... |
23-
LL | |
23+
LL | | ) -> Self::AssocType;
2424
LL | | }
2525
| |_^ the trait `Foo` is not implemented for `Bug`
2626
|
@@ -34,6 +34,7 @@ error[E0277]: the trait bound `Bug: Foo` is not satisfied
3434
|
3535
LL | / fn get_service(
3636
LL | |
37+
LL | |
3738
LL | | &self,
3839
LL | | ) -> Self::AssocType;
3940
| |_________________________^ the trait `Foo` is not implemented for `Bug`
@@ -50,10 +51,10 @@ LL | fn with_factory<H>(factory: dyn ThriftService<()>) {}
5051
| ^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()`
5152

5253
error[E0277]: the trait bound `Bug: Foo` is not satisfied
53-
--> $DIR/issue-59324.rs:19:10
54+
--> $DIR/issue-59324.rs:16:8
5455
|
55-
LL | ) -> Self::AssocType;
56-
| ^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `Bug`
56+
LL | fn get_service(
57+
| ^^^^^^^^^^^ the trait `Foo` is not implemented for `Bug`
5758
|
5859
help: consider further restricting this bound
5960
|

0 commit comments

Comments
 (0)