Skip to content

Commit 63e4312

Browse files
committed
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.
2 parents 6d3f1be + 8691b96 commit 63e4312

28 files changed

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

@@ -109,23 +109,11 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
109109
self.add_outlives(r1_vid, r2_vid);
110110
}
111111

112-
GenericArgKind::Type(mut t1) => {
112+
GenericArgKind::Type(t1) => {
113113
// we don't actually use this for anything, but
114114
// the `TypeOutlives` code needs an origin.
115115
let origin = infer::RelateParamBound(DUMMY_SP, t1, None);
116116

117-
// Placeholder regions need to be converted now because it may
118-
// create new region variables, which can't be done later when
119-
// verifying these bounds.
120-
if t1.has_placeholders() {
121-
t1 = tcx.fold_regions(t1, |r, _| match *r {
122-
ty::RePlaceholder(placeholder) => {
123-
self.constraints.placeholder_region(self.infcx, placeholder)
124-
}
125-
_ => r,
126-
});
127-
}
128-
129117
TypeOutlives::new(
130118
&mut *self,
131119
tcx,
@@ -143,14 +131,32 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
143131
}
144132
}
145133

134+
/// Placeholder regions need to be converted eagerly because it may
135+
/// create new region variables, which we must not do when verifying
136+
/// our region bounds.
137+
///
138+
/// FIXME: This should get removed once higher ranked region obligations
139+
/// are dealt with during trait solving.
140+
fn replace_placeholders_with_nll<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T {
141+
if value.has_placeholders() {
142+
self.tcx.fold_regions(value, |r, _| match *r {
143+
ty::RePlaceholder(placeholder) => {
144+
self.constraints.placeholder_region(self.infcx, placeholder)
145+
}
146+
_ => r,
147+
})
148+
} else {
149+
value
150+
}
151+
}
152+
146153
fn verify_to_type_test(
147154
&mut self,
148155
generic_kind: GenericKind<'tcx>,
149156
region: ty::Region<'tcx>,
150157
verify_bound: VerifyBound<'tcx>,
151158
) -> TypeTest<'tcx> {
152159
let lower_bound = self.to_region_vid(region);
153-
154160
TypeTest { generic_kind, lower_bound, locations: self.locations, verify_bound }
155161
}
156162

@@ -198,6 +204,8 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'
198204
a: ty::Region<'tcx>,
199205
bound: VerifyBound<'tcx>,
200206
) {
207+
let kind = self.replace_placeholders_with_nll(kind);
208+
let bound = self.replace_placeholders_with_nll(bound);
201209
let type_test = self.verify_to_type_test(kind, a, bound);
202210
self.add_type_test(type_test);
203211
}

compiler/rustc_borrowck/src/type_check/free_region_relations.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
242242
let constraint_sets: Vec<_> = unnormalized_input_output_tys
243243
.flat_map(|ty| {
244244
debug!("build: input_or_output={:?}", ty);
245-
// We only add implied bounds for the normalized type as the unnormalized
246-
// type may not actually get checked by the caller.
247-
//
248-
// Can otherwise be unsound, see #91068.
245+
// We add implied bounds from both the unnormalized and normalized ty.
246+
// See issue #87748
247+
let constraints_implied1 = self.add_implied_bounds(ty);
249248
let TypeOpOutput { output: norm_ty, constraints: constraints1, .. } = self
250249
.param_env
251250
.and(type_op::normalize::Normalize::new(ty))
@@ -273,9 +272,10 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
273272
// }
274273
// ```
275274
// Both &Self::Bar and &() are WF
276-
let constraints_implied = self.add_implied_bounds(norm_ty);
275+
let constraints_implied2 =
276+
if ty != norm_ty { self.add_implied_bounds(norm_ty) } else { None };
277277
normalized_inputs_and_output.push(norm_ty);
278-
constraints1.into_iter().chain(constraints_implied)
278+
constraints1.into_iter().chain(constraints_implied1).chain(constraints_implied2)
279279
})
280280
.collect();
281281

compiler/rustc_borrowck/src/type_check/mod.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -1448,16 +1448,23 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
14481448
))
14491449
});
14501450
debug!(?sig);
1451-
let sig = self.normalize(sig, term_location);
1452-
self.check_call_dest(body, term, &sig, *destination, target, term_location);
1453-
1451+
// IMPORTANT: We have to prove well formed for the function signature before
1452+
// we normalize it, as otherwise types like `<&'a &'b () as Trait>::Assoc`
1453+
// get normalized away, causing us to ignore the `'b: 'a` bound used by the function.
1454+
//
1455+
// Normalization results in a well formed type if the input is well formed, so we
1456+
// don't have to check it twice.
1457+
//
1458+
// See #91068 for an example.
14541459
self.prove_predicates(
14551460
sig.inputs_and_output
14561461
.iter()
14571462
.map(|ty| ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into()))),
14581463
term_location.to_locations(),
14591464
ConstraintCategory::Boring,
14601465
);
1466+
let sig = self.normalize(sig, term_location);
1467+
self.check_call_dest(body, term, &sig, *destination, target, term_location);
14611468

14621469
// The ordinary liveness rules will ensure that all
14631470
// 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
@@ -187,7 +187,7 @@ pub enum GenericKind<'tcx> {
187187
/// }
188188
/// ```
189189
/// This is described with an `AnyRegion('a, 'b)` node.
190-
#[derive(Debug, Clone)]
190+
#[derive(Debug, Clone, TypeFoldable, TypeVisitable)]
191191
pub enum VerifyBound<'tcx> {
192192
/// See [`VerifyIfEq`] docs
193193
IfEq(ty::Binder<'tcx, VerifyIfEq<'tcx>>),

compiler/rustc_middle/src/ty/mod.rs

+23
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,29 @@ impl<'tcx> Predicate<'tcx> {
594594
}
595595
self
596596
}
597+
598+
/// Whether this projection can be soundly normalized.
599+
///
600+
/// Wf predicates must not be normalized, as normalization
601+
/// can remove required bounds which would cause us to
602+
/// unsoundly accept some programs. See #91068.
603+
#[inline]
604+
pub fn allow_normalization(self) -> bool {
605+
match self.kind().skip_binder() {
606+
PredicateKind::WellFormed(_) => false,
607+
PredicateKind::Trait(_)
608+
| PredicateKind::RegionOutlives(_)
609+
| PredicateKind::TypeOutlives(_)
610+
| PredicateKind::Projection(_)
611+
| PredicateKind::ObjectSafe(_)
612+
| PredicateKind::ClosureKind(_, _, _)
613+
| PredicateKind::Subtype(_)
614+
| PredicateKind::Coerce(_)
615+
| PredicateKind::ConstEvaluatable(_)
616+
| PredicateKind::ConstEquate(_, _)
617+
| PredicateKind::TypeWellFormedFromEnv(_) => true,
618+
}
619+
}
597620
}
598621

599622
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
@@ -264,8 +264,13 @@ fn compare_predicate_entailment<'tcx>(
264264

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

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)