Skip to content

Commit 4568e7d

Browse files
committed
Move nested quantification check to ast_validation
1 parent 9891582 commit 4568e7d

File tree

2 files changed

+52
-75
lines changed

2 files changed

+52
-75
lines changed

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ struct AstValidator<'a> {
8181
is_assoc_ty_bound_banned: bool,
8282

8383
lint_buffer: &'a mut LintBuffer,
84+
85+
/// This is slightly complicated. Our representation for poly-trait-refs contains a single
86+
/// binder and thus we only allow a single level of quantification. However,
87+
/// the syntax of Rust permits quantification in two places in where clauses,
88+
/// e.g., `T: for <'a> Foo<'a>` and `for <'a, 'b> &'b T: Foo<'a>`. If both are
89+
/// defined, then error.
90+
trait_ref_hack: bool,
8491
}
8592

8693
impl<'a> AstValidator<'a> {
@@ -1213,8 +1220,25 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
12131220
deny_equality_constraints(self, predicate, generics);
12141221
}
12151222
}
1223+
walk_list!(self, visit_generic_param, &generics.params);
1224+
for predicate in &generics.where_clause.predicates {
1225+
match predicate {
1226+
WherePredicate::BoundPredicate(bound_pred) => {
1227+
// A type binding, eg `for<'c> Foo: Send+Clone+'c`
1228+
self.check_late_bound_lifetime_defs(&bound_pred.bound_generic_params);
12161229

1217-
visit::walk_generics(self, generics)
1230+
self.visit_ty(&bound_pred.bounded_ty);
1231+
1232+
self.trait_ref_hack = !bound_pred.bound_generic_params.is_empty();
1233+
walk_list!(self, visit_param_bound, &bound_pred.bounds);
1234+
walk_list!(self, visit_generic_param, &bound_pred.bound_generic_params);
1235+
self.trait_ref_hack = false;
1236+
}
1237+
_ => {
1238+
self.visit_where_predicate(predicate);
1239+
}
1240+
}
1241+
}
12181242
}
12191243

12201244
fn visit_generic_param(&mut self, param: &'a GenericParam) {
@@ -1263,17 +1287,21 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
12631287
visit::walk_pat(self, pat)
12641288
}
12651289

1266-
fn visit_where_predicate(&mut self, p: &'a WherePredicate) {
1267-
if let &WherePredicate::BoundPredicate(ref bound_predicate) = p {
1268-
// A type binding, eg `for<'c> Foo: Send+Clone+'c`
1269-
self.check_late_bound_lifetime_defs(&bound_predicate.bound_generic_params);
1270-
}
1271-
visit::walk_where_predicate(self, p);
1272-
}
1273-
12741290
fn visit_poly_trait_ref(&mut self, t: &'a PolyTraitRef, m: &'a TraitBoundModifier) {
12751291
self.check_late_bound_lifetime_defs(&t.bound_generic_params);
1292+
if self.trait_ref_hack && !t.bound_generic_params.is_empty() {
1293+
struct_span_err!(
1294+
self.err_handler(),
1295+
t.span,
1296+
E0316,
1297+
"nested quantification of lifetimes"
1298+
)
1299+
.emit();
1300+
}
1301+
let trait_ref_hack = self.trait_ref_hack;
1302+
self.trait_ref_hack = false;
12761303
visit::walk_poly_trait_ref(self, t, m);
1304+
self.trait_ref_hack = trait_ref_hack;
12771305
}
12781306

12791307
fn visit_variant_data(&mut self, s: &'a VariantData) {
@@ -1492,6 +1520,7 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) ->
14921520
is_impl_trait_banned: false,
14931521
is_assoc_ty_bound_banned: false,
14941522
lint_buffer: lints,
1523+
trait_ref_hack: false,
14951524
};
14961525
visit::walk_crate(&mut validator, krate);
14971526

compiler/rustc_resolve/src/late/lifetimes.rs

Lines changed: 14 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -278,29 +278,6 @@ enum BinderScopeType {
278278
/// you had `T: for<'a> Foo<Bar: for<'b> Baz<'a, 'b>>`, then the `for<'a>`
279279
/// scope uses `PolyTraitRef`.
280280
PolyTraitRef,
281-
/// This is slightly complicated. Our representation for poly-trait-refs contains a single
282-
/// binder and thus we only allow a single level of quantification. However,
283-
/// the syntax of Rust permits quantification in two places in where clauses,
284-
/// e.g., `T: for <'a> Foo<'a>` and `for <'a, 'b> &'b T: Foo<'a>`. In order
285-
/// to get the De Bruijn indices correct when representing these constraints,
286-
/// we should only introduce one scope. However, we want to support both
287-
/// locations for the quantifier and during lifetime resolution we want
288-
/// precise information (so we can't desugar in an earlier phase). Moreso,
289-
/// an error here doesn't cause a bail from type checking, so we need to be
290-
/// extra careful that we don't lose any bound var information for *either*
291-
/// syntactic binder and that we track all lifetimes defined in both binders.
292-
///
293-
/// This mechanism is similar to the concatenation done in nested poly trait
294-
/// refs, i.e. the inner syntactic binder extends upon the lifetimes on the
295-
/// outer syntactic binder. However, we require a separate variant here to
296-
/// distinguish `for<'a> T: for<'b> Foo<'a, 'b>` from
297-
/// `T: for<'a> Bar<Baz: for<'b> Foo<'a, 'b>>`. In this case, the innermost
298-
/// `: for<'b> Foo<'a, 'b>` both have a `for<'a>` scope above it. However,
299-
/// in the former case, we must emit an error because this is invalid syntax.
300-
/// Put another way: `PolyTraitRef` and `BoundedTy` behave identically except
301-
/// that `BoundedTy` is used to signal that an error should be emitted if
302-
/// another syntactic binder is found.
303-
BoundedTy,
304281
/// Within a syntactic trait ref, there may be multiple poly trait refs that
305282
/// are nested (under the `associcated_type_bounds` feature). The binders of
306283
/// the innner poly trait refs are extended from the outer poly trait refs
@@ -309,8 +286,7 @@ enum BinderScopeType {
309286
/// would be `Concatenating`. This also used in trait refs in where clauses
310287
/// where we have two binders `for<> T: for<> Foo` (I've intentionally left
311288
/// out any lifetimes because they aren't needed to show the two scopes).
312-
/// See `BoundedTy` for a bit more details, but the inner `for<>` has a scope
313-
/// of `Concatenating`.
289+
/// The inner `for<>` has a scope of `Concatenating`.
314290
Concatenating,
315291
/// Any other binder scopes. These are "normal" in that they increase the binder
316292
/// depth, are fully syntactic, don't concatenate, and don't have special syntactical
@@ -1311,7 +1287,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
13111287
next_early_index,
13121288
track_lifetime_uses: true,
13131289
opaque_type_parent: false,
1314-
scope_type: BinderScopeType::BoundedTy,
1290+
scope_type: BinderScopeType::PolyTraitRef,
13151291
};
13161292
this.with(scope, |old_scope, this| {
13171293
this.check_lifetime_params(old_scope, &bound_generic_params);
@@ -1344,30 +1320,24 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
13441320
// FIXME(jackh726): This is pretty weird. `LangItemTrait` doesn't go
13451321
// through the regular poly trait ref code, so we don't get another
13461322
// chance to introduce a binder. For now, I'm keeping the existing logic
1347-
// of "if there isn't a `BoundedTy` scope above us, add one", but I
1323+
// of "if there isn't a Binder scope above us, add one", but I
13481324
// imagine there's a better way to go about this.
13491325
let mut scope = self.scope;
13501326
let trait_ref_hack = loop {
13511327
match scope {
1352-
Scope::Body { .. } | Scope::Root => {
1328+
Scope::TraitRefBoundary { .. } | Scope::Body { .. } | Scope::Root => {
13531329
break false;
13541330
}
13551331

1332+
Scope::Binder { .. } => {
1333+
break true;
1334+
}
1335+
13561336
Scope::Elision { s, .. }
13571337
| Scope::ObjectLifetimeDefault { s, .. }
13581338
| Scope::Supertrait { s, .. } => {
13591339
scope = s;
13601340
}
1361-
1362-
Scope::TraitRefBoundary { .. } => {
1363-
break false;
1364-
}
1365-
1366-
Scope::Binder { scope_type, lifetimes, .. } => {
1367-
let trait_ref_hack =
1368-
matches!(scope_type, BinderScopeType::BoundedTy) && !lifetimes.is_empty();
1369-
break trait_ref_hack;
1370-
}
13711341
}
13721342
};
13731343
match bound {
@@ -1402,10 +1372,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
14021372
let next_early_index = self.next_early_index();
14031373
let mut scope = self.scope;
14041374
let mut supertrait_lifetimes = vec![];
1405-
let (mut binders, trait_ref_hack, scope_type) = loop {
1375+
let (mut binders, scope_type) = loop {
14061376
match scope {
14071377
Scope::Body { .. } | Scope::Root => {
1408-
break (vec![], false, BinderScopeType::PolyTraitRef);
1378+
break (vec![], BinderScopeType::PolyTraitRef);
14091379
}
14101380

14111381
Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => {
@@ -1420,10 +1390,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
14201390
Scope::TraitRefBoundary { .. } => {
14211391
// We should only see super trait lifetimes if there is a `Binder` above
14221392
assert!(supertrait_lifetimes.is_empty());
1423-
break (vec![], false, BinderScopeType::PolyTraitRef);
1393+
break (vec![], BinderScopeType::PolyTraitRef);
14241394
}
14251395

1426-
Scope::Binder { hir_id, scope_type, lifetimes, .. } => {
1396+
Scope::Binder { hir_id, scope_type, .. } => {
14271397
if let BinderScopeType::Other = scope_type {
14281398
bug!(
14291399
"Expected all syntacic poly trait refs to be surrounded by a `TraitRefBoundary`"
@@ -1434,30 +1404,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
14341404
let mut full_binders =
14351405
self.map.late_bound_vars.entry(*hir_id).or_default().clone();
14361406
full_binders.extend(supertrait_lifetimes.into_iter());
1437-
let trait_ref_hack =
1438-
matches!(scope_type, BinderScopeType::BoundedTy) && !lifetimes.is_empty();
1439-
break (full_binders, trait_ref_hack, BinderScopeType::Concatenating);
1407+
break (full_binders, BinderScopeType::Concatenating);
14401408
}
14411409
}
14421410
};
14431411

1444-
// See note on `BinderScopeType::BoundedTy`. If `for<..>`
1445-
// has been defined in both the outer and inner part of the
1446-
// trait ref, emit an error.
1447-
let has_lifetimes = trait_ref.bound_generic_params.iter().any(|param| match param.kind {
1448-
GenericParamKind::Lifetime { .. } => true,
1449-
_ => false,
1450-
});
1451-
if trait_ref_hack && has_lifetimes {
1452-
struct_span_err!(
1453-
self.tcx.sess,
1454-
trait_ref.span,
1455-
E0316,
1456-
"nested quantification of lifetimes"
1457-
)
1458-
.emit();
1459-
}
1460-
14611412
let initial_bound_vars = binders.len() as u32;
14621413
let mut lifetimes: FxHashMap<hir::ParamName, Region> = FxHashMap::default();
14631414
let binders_iter = trait_ref
@@ -1486,7 +1437,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
14861437
// Always introduce a scope here, even if this is in a where clause and
14871438
// we introduced the binders around the bounded Ty. In that case, we
14881439
// just reuse the concatenation functionality also present in nested trait
1489-
// refs. See `BinderScopeType::BoundedTy` for more details on that case.
1440+
// refs.
14901441
let scope = Scope::Binder {
14911442
hir_id: trait_ref.trait_ref.hir_ref_id,
14921443
lifetimes,
@@ -2319,7 +2270,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
23192270
}
23202271
match scope_type {
23212272
BinderScopeType::Other => late_depth += 1,
2322-
BinderScopeType::BoundedTy => late_depth += 1,
23232273
BinderScopeType::PolyTraitRef => late_depth += 1,
23242274
BinderScopeType::Concatenating => {}
23252275
}
@@ -3051,7 +3001,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
30513001
}
30523002
match scope_type {
30533003
BinderScopeType::Other => late_depth += 1,
3054-
BinderScopeType::BoundedTy => late_depth += 1,
30553004
BinderScopeType::PolyTraitRef => late_depth += 1,
30563005
BinderScopeType::Concatenating => {}
30573006
}
@@ -3216,7 +3165,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
32163165
Scope::Binder { s, scope_type, .. } => {
32173166
match scope_type {
32183167
BinderScopeType::Other => late_depth += 1,
3219-
BinderScopeType::BoundedTy => late_depth += 1,
32203168
BinderScopeType::PolyTraitRef => late_depth += 1,
32213169
BinderScopeType::Concatenating => {}
32223170
}

0 commit comments

Comments
 (0)