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

+23-15
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

+6-6
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

+10-3
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

+1-1
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

+23
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

+9
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

+12
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

+6-1
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

+1-1
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

+5-4
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
|

src/test/ui/fn/implied-bounds-unnorm-associated-type-2.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// check-pass
1+
// check-fail
22

33
trait Trait {
44
type Type;
@@ -17,6 +17,7 @@ where
1717

1818
fn g<'a, 'b>() {
1919
f::<'a, 'b>(());
20+
//~^ ERROR lifetime may not live long enough
2021
}
2122

2223
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/implied-bounds-unnorm-associated-type-2.rs:19:5
3+
|
4+
LL | fn g<'a, 'b>() {
5+
| -- -- lifetime `'b` defined here
6+
| |
7+
| lifetime `'a` defined here
8+
LL | f::<'a, 'b>(());
9+
| ^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
10+
|
11+
= help: consider adding the following bound: `'b: 'a`
12+
= note: requirement occurs because of a function pointer to `f`
13+
= note: the function `f` is invariant over the parameter `'a`
14+
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
15+
16+
error: aborting due to previous error
17+

src/test/ui/fn/implied-bounds-unnorm-associated-type-3.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
// check-fail
2-
// See issue #91899. If we treat unnormalized args as WF, `Self` can also be a
3-
// source of unsoundness.
1+
// check-pass
42

53
pub trait Yokeable<'a>: 'static {
64
type Output: 'a;
@@ -17,7 +15,6 @@ pub trait ZeroCopyFrom<C: ?Sized>: for<'a> Yokeable<'a> {
1715

1816
impl<T> ZeroCopyFrom<[T]> for &'static [T] {
1917
fn zero_copy_from<'b>(cart: &'b [T]) -> &'b [T] {
20-
//~^ the parameter
2118
cart
2219
}
2320
}

src/test/ui/fn/implied-bounds-unnorm-associated-type-3.stderr

-14
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// A regression test for #98543
2+
3+
trait Trait {
4+
type Type;
5+
}
6+
7+
impl<T> Trait for T {
8+
type Type = ();
9+
}
10+
11+
fn f<'a, 'b>(s: &'b str, _: <&'a &'b () as Trait>::Type) -> &'a str
12+
where
13+
&'a &'b (): Trait, // <- adding this bound is the change from #91068
14+
{
15+
s
16+
}
17+
18+
fn main() {
19+
let x = String::from("Hello World!");
20+
let y = f(&x, ());
21+
drop(x);
22+
//~^ ERROR cannot move out of `x` because it is borrowed
23+
println!("{}", y);
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0505]: cannot move out of `x` because it is borrowed
2+
--> $DIR/implied-bounds-unnorm-associated-type-4.rs:21:10
3+
|
4+
LL | let y = f(&x, ());
5+
| -- borrow of `x` occurs here
6+
LL | drop(x);
7+
| ^ move out of `x` occurs here
8+
LL |
9+
LL | println!("{}", y);
10+
| - borrow later used here
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0505`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
trait Trait<'a>: 'a {
2+
type Type;
3+
}
4+
5+
// if the `T: 'a` bound gets implied we would probably get ub here again
6+
impl<'a, T> Trait<'a> for T {
7+
//~^ ERROR the parameter type `T` may not live long enough
8+
type Type = ();
9+
}
10+
11+
fn f<'a, 'b>(s: &'b str, _: <&'b () as Trait<'a>>::Type) -> &'a str
12+
where
13+
&'b (): Trait<'a>,
14+
{
15+
s
16+
}
17+
18+
fn main() {
19+
let x = String::from("Hello World!");
20+
let y = f(&x, ());
21+
drop(x);
22+
println!("{}", y);
23+
}

0 commit comments

Comments
 (0)