Move nested quantification check to ast_validation
This commit is contained in:
parent
9891582897
commit
4568e7d62e
2 changed files with 52 additions and 75 deletions
|
@ -81,6 +81,13 @@ struct AstValidator<'a> {
|
||||||
is_assoc_ty_bound_banned: bool,
|
is_assoc_ty_bound_banned: bool,
|
||||||
|
|
||||||
lint_buffer: &'a mut LintBuffer,
|
lint_buffer: &'a mut LintBuffer,
|
||||||
|
|
||||||
|
/// This is slightly complicated. Our representation for poly-trait-refs contains a single
|
||||||
|
/// binder and thus we only allow a single level of quantification. However,
|
||||||
|
/// the syntax of Rust permits quantification in two places in where clauses,
|
||||||
|
/// e.g., `T: for <'a> Foo<'a>` and `for <'a, 'b> &'b T: Foo<'a>`. If both are
|
||||||
|
/// defined, then error.
|
||||||
|
trait_ref_hack: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AstValidator<'a> {
|
impl<'a> AstValidator<'a> {
|
||||||
|
@ -1213,8 +1220,25 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
deny_equality_constraints(self, predicate, generics);
|
deny_equality_constraints(self, predicate, generics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
walk_list!(self, visit_generic_param, &generics.params);
|
||||||
|
for predicate in &generics.where_clause.predicates {
|
||||||
|
match predicate {
|
||||||
|
WherePredicate::BoundPredicate(bound_pred) => {
|
||||||
|
// A type binding, eg `for<'c> Foo: Send+Clone+'c`
|
||||||
|
self.check_late_bound_lifetime_defs(&bound_pred.bound_generic_params);
|
||||||
|
|
||||||
visit::walk_generics(self, generics)
|
self.visit_ty(&bound_pred.bounded_ty);
|
||||||
|
|
||||||
|
self.trait_ref_hack = !bound_pred.bound_generic_params.is_empty();
|
||||||
|
walk_list!(self, visit_param_bound, &bound_pred.bounds);
|
||||||
|
walk_list!(self, visit_generic_param, &bound_pred.bound_generic_params);
|
||||||
|
self.trait_ref_hack = false;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.visit_where_predicate(predicate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_generic_param(&mut self, param: &'a GenericParam) {
|
fn visit_generic_param(&mut self, param: &'a GenericParam) {
|
||||||
|
@ -1263,17 +1287,21 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
visit::walk_pat(self, pat)
|
visit::walk_pat(self, pat)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_where_predicate(&mut self, p: &'a WherePredicate) {
|
|
||||||
if let &WherePredicate::BoundPredicate(ref bound_predicate) = p {
|
|
||||||
// A type binding, eg `for<'c> Foo: Send+Clone+'c`
|
|
||||||
self.check_late_bound_lifetime_defs(&bound_predicate.bound_generic_params);
|
|
||||||
}
|
|
||||||
visit::walk_where_predicate(self, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_poly_trait_ref(&mut self, t: &'a PolyTraitRef, m: &'a TraitBoundModifier) {
|
fn visit_poly_trait_ref(&mut self, t: &'a PolyTraitRef, m: &'a TraitBoundModifier) {
|
||||||
self.check_late_bound_lifetime_defs(&t.bound_generic_params);
|
self.check_late_bound_lifetime_defs(&t.bound_generic_params);
|
||||||
|
if self.trait_ref_hack && !t.bound_generic_params.is_empty() {
|
||||||
|
struct_span_err!(
|
||||||
|
self.err_handler(),
|
||||||
|
t.span,
|
||||||
|
E0316,
|
||||||
|
"nested quantification of lifetimes"
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
let trait_ref_hack = self.trait_ref_hack;
|
||||||
|
self.trait_ref_hack = false;
|
||||||
visit::walk_poly_trait_ref(self, t, m);
|
visit::walk_poly_trait_ref(self, t, m);
|
||||||
|
self.trait_ref_hack = trait_ref_hack;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_variant_data(&mut self, s: &'a VariantData) {
|
fn visit_variant_data(&mut self, s: &'a VariantData) {
|
||||||
|
@ -1492,6 +1520,7 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) ->
|
||||||
is_impl_trait_banned: false,
|
is_impl_trait_banned: false,
|
||||||
is_assoc_ty_bound_banned: false,
|
is_assoc_ty_bound_banned: false,
|
||||||
lint_buffer: lints,
|
lint_buffer: lints,
|
||||||
|
trait_ref_hack: false,
|
||||||
};
|
};
|
||||||
visit::walk_crate(&mut validator, krate);
|
visit::walk_crate(&mut validator, krate);
|
||||||
|
|
||||||
|
|
|
@ -278,29 +278,6 @@ enum BinderScopeType {
|
||||||
/// you had `T: for<'a> Foo<Bar: for<'b> Baz<'a, 'b>>`, then the `for<'a>`
|
/// you had `T: for<'a> Foo<Bar: for<'b> Baz<'a, 'b>>`, then the `for<'a>`
|
||||||
/// scope uses `PolyTraitRef`.
|
/// scope uses `PolyTraitRef`.
|
||||||
PolyTraitRef,
|
PolyTraitRef,
|
||||||
/// This is slightly complicated. Our representation for poly-trait-refs contains a single
|
|
||||||
/// binder and thus we only allow a single level of quantification. However,
|
|
||||||
/// the syntax of Rust permits quantification in two places in where clauses,
|
|
||||||
/// e.g., `T: for <'a> Foo<'a>` and `for <'a, 'b> &'b T: Foo<'a>`. In order
|
|
||||||
/// to get the De Bruijn indices correct when representing these constraints,
|
|
||||||
/// we should only introduce one scope. However, we want to support both
|
|
||||||
/// locations for the quantifier and during lifetime resolution we want
|
|
||||||
/// precise information (so we can't desugar in an earlier phase). Moreso,
|
|
||||||
/// an error here doesn't cause a bail from type checking, so we need to be
|
|
||||||
/// extra careful that we don't lose any bound var information for *either*
|
|
||||||
/// syntactic binder and that we track all lifetimes defined in both binders.
|
|
||||||
///
|
|
||||||
/// This mechanism is similar to the concatenation done in nested poly trait
|
|
||||||
/// refs, i.e. the inner syntactic binder extends upon the lifetimes on the
|
|
||||||
/// outer syntactic binder. However, we require a separate variant here to
|
|
||||||
/// distinguish `for<'a> T: for<'b> Foo<'a, 'b>` from
|
|
||||||
/// `T: for<'a> Bar<Baz: for<'b> Foo<'a, 'b>>`. In this case, the innermost
|
|
||||||
/// `: for<'b> Foo<'a, 'b>` both have a `for<'a>` scope above it. However,
|
|
||||||
/// in the former case, we must emit an error because this is invalid syntax.
|
|
||||||
/// Put another way: `PolyTraitRef` and `BoundedTy` behave identically except
|
|
||||||
/// that `BoundedTy` is used to signal that an error should be emitted if
|
|
||||||
/// another syntactic binder is found.
|
|
||||||
BoundedTy,
|
|
||||||
/// Within a syntactic trait ref, there may be multiple poly trait refs that
|
/// Within a syntactic trait ref, there may be multiple poly trait refs that
|
||||||
/// are nested (under the `associcated_type_bounds` feature). The binders of
|
/// are nested (under the `associcated_type_bounds` feature). The binders of
|
||||||
/// the innner poly trait refs are extended from the outer poly trait refs
|
/// the innner poly trait refs are extended from the outer poly trait refs
|
||||||
|
@ -309,8 +286,7 @@ enum BinderScopeType {
|
||||||
/// would be `Concatenating`. This also used in trait refs in where clauses
|
/// would be `Concatenating`. This also used in trait refs in where clauses
|
||||||
/// where we have two binders `for<> T: for<> Foo` (I've intentionally left
|
/// where we have two binders `for<> T: for<> Foo` (I've intentionally left
|
||||||
/// out any lifetimes because they aren't needed to show the two scopes).
|
/// out any lifetimes because they aren't needed to show the two scopes).
|
||||||
/// See `BoundedTy` for a bit more details, but the inner `for<>` has a scope
|
/// The inner `for<>` has a scope of `Concatenating`.
|
||||||
/// of `Concatenating`.
|
|
||||||
Concatenating,
|
Concatenating,
|
||||||
/// Any other binder scopes. These are "normal" in that they increase the binder
|
/// Any other binder scopes. These are "normal" in that they increase the binder
|
||||||
/// depth, are fully syntactic, don't concatenate, and don't have special syntactical
|
/// 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> {
|
||||||
next_early_index,
|
next_early_index,
|
||||||
track_lifetime_uses: true,
|
track_lifetime_uses: true,
|
||||||
opaque_type_parent: false,
|
opaque_type_parent: false,
|
||||||
scope_type: BinderScopeType::BoundedTy,
|
scope_type: BinderScopeType::PolyTraitRef,
|
||||||
};
|
};
|
||||||
this.with(scope, |old_scope, this| {
|
this.with(scope, |old_scope, this| {
|
||||||
this.check_lifetime_params(old_scope, &bound_generic_params);
|
this.check_lifetime_params(old_scope, &bound_generic_params);
|
||||||
|
@ -1344,30 +1320,24 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
// FIXME(jackh726): This is pretty weird. `LangItemTrait` doesn't go
|
// FIXME(jackh726): This is pretty weird. `LangItemTrait` doesn't go
|
||||||
// through the regular poly trait ref code, so we don't get another
|
// through the regular poly trait ref code, so we don't get another
|
||||||
// chance to introduce a binder. For now, I'm keeping the existing logic
|
// chance to introduce a binder. For now, I'm keeping the existing logic
|
||||||
// of "if there isn't a `BoundedTy` scope above us, add one", but I
|
// of "if there isn't a Binder scope above us, add one", but I
|
||||||
// imagine there's a better way to go about this.
|
// imagine there's a better way to go about this.
|
||||||
let mut scope = self.scope;
|
let mut scope = self.scope;
|
||||||
let trait_ref_hack = loop {
|
let trait_ref_hack = loop {
|
||||||
match scope {
|
match scope {
|
||||||
Scope::Body { .. } | Scope::Root => {
|
Scope::TraitRefBoundary { .. } | Scope::Body { .. } | Scope::Root => {
|
||||||
break false;
|
break false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Scope::Binder { .. } => {
|
||||||
|
break true;
|
||||||
|
}
|
||||||
|
|
||||||
Scope::Elision { s, .. }
|
Scope::Elision { s, .. }
|
||||||
| Scope::ObjectLifetimeDefault { s, .. }
|
| Scope::ObjectLifetimeDefault { s, .. }
|
||||||
| Scope::Supertrait { s, .. } => {
|
| Scope::Supertrait { s, .. } => {
|
||||||
scope = s;
|
scope = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope::TraitRefBoundary { .. } => {
|
|
||||||
break false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope::Binder { scope_type, lifetimes, .. } => {
|
|
||||||
let trait_ref_hack =
|
|
||||||
matches!(scope_type, BinderScopeType::BoundedTy) && !lifetimes.is_empty();
|
|
||||||
break trait_ref_hack;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match bound {
|
match bound {
|
||||||
|
@ -1402,10 +1372,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
let next_early_index = self.next_early_index();
|
let next_early_index = self.next_early_index();
|
||||||
let mut scope = self.scope;
|
let mut scope = self.scope;
|
||||||
let mut supertrait_lifetimes = vec![];
|
let mut supertrait_lifetimes = vec![];
|
||||||
let (mut binders, trait_ref_hack, scope_type) = loop {
|
let (mut binders, scope_type) = loop {
|
||||||
match scope {
|
match scope {
|
||||||
Scope::Body { .. } | Scope::Root => {
|
Scope::Body { .. } | Scope::Root => {
|
||||||
break (vec![], false, BinderScopeType::PolyTraitRef);
|
break (vec![], BinderScopeType::PolyTraitRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => {
|
Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => {
|
||||||
|
@ -1420,10 +1390,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
Scope::TraitRefBoundary { .. } => {
|
Scope::TraitRefBoundary { .. } => {
|
||||||
// We should only see super trait lifetimes if there is a `Binder` above
|
// We should only see super trait lifetimes if there is a `Binder` above
|
||||||
assert!(supertrait_lifetimes.is_empty());
|
assert!(supertrait_lifetimes.is_empty());
|
||||||
break (vec![], false, BinderScopeType::PolyTraitRef);
|
break (vec![], BinderScopeType::PolyTraitRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope::Binder { hir_id, scope_type, lifetimes, .. } => {
|
Scope::Binder { hir_id, scope_type, .. } => {
|
||||||
if let BinderScopeType::Other = scope_type {
|
if let BinderScopeType::Other = scope_type {
|
||||||
bug!(
|
bug!(
|
||||||
"Expected all syntacic poly trait refs to be surrounded by a `TraitRefBoundary`"
|
"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> {
|
||||||
let mut full_binders =
|
let mut full_binders =
|
||||||
self.map.late_bound_vars.entry(*hir_id).or_default().clone();
|
self.map.late_bound_vars.entry(*hir_id).or_default().clone();
|
||||||
full_binders.extend(supertrait_lifetimes.into_iter());
|
full_binders.extend(supertrait_lifetimes.into_iter());
|
||||||
let trait_ref_hack =
|
break (full_binders, BinderScopeType::Concatenating);
|
||||||
matches!(scope_type, BinderScopeType::BoundedTy) && !lifetimes.is_empty();
|
|
||||||
break (full_binders, trait_ref_hack, BinderScopeType::Concatenating);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// See note on `BinderScopeType::BoundedTy`. If `for<..>`
|
|
||||||
// has been defined in both the outer and inner part of the
|
|
||||||
// trait ref, emit an error.
|
|
||||||
let has_lifetimes = trait_ref.bound_generic_params.iter().any(|param| match param.kind {
|
|
||||||
GenericParamKind::Lifetime { .. } => true,
|
|
||||||
_ => false,
|
|
||||||
});
|
|
||||||
if trait_ref_hack && has_lifetimes {
|
|
||||||
struct_span_err!(
|
|
||||||
self.tcx.sess,
|
|
||||||
trait_ref.span,
|
|
||||||
E0316,
|
|
||||||
"nested quantification of lifetimes"
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
let initial_bound_vars = binders.len() as u32;
|
let initial_bound_vars = binders.len() as u32;
|
||||||
let mut lifetimes: FxHashMap<hir::ParamName, Region> = FxHashMap::default();
|
let mut lifetimes: FxHashMap<hir::ParamName, Region> = FxHashMap::default();
|
||||||
let binders_iter = trait_ref
|
let binders_iter = trait_ref
|
||||||
|
@ -1486,7 +1437,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
// Always introduce a scope here, even if this is in a where clause and
|
// Always introduce a scope here, even if this is in a where clause and
|
||||||
// we introduced the binders around the bounded Ty. In that case, we
|
// we introduced the binders around the bounded Ty. In that case, we
|
||||||
// just reuse the concatenation functionality also present in nested trait
|
// just reuse the concatenation functionality also present in nested trait
|
||||||
// refs. See `BinderScopeType::BoundedTy` for more details on that case.
|
// refs.
|
||||||
let scope = Scope::Binder {
|
let scope = Scope::Binder {
|
||||||
hir_id: trait_ref.trait_ref.hir_ref_id,
|
hir_id: trait_ref.trait_ref.hir_ref_id,
|
||||||
lifetimes,
|
lifetimes,
|
||||||
|
@ -2319,7 +2270,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
match scope_type {
|
match scope_type {
|
||||||
BinderScopeType::Other => late_depth += 1,
|
BinderScopeType::Other => late_depth += 1,
|
||||||
BinderScopeType::BoundedTy => late_depth += 1,
|
|
||||||
BinderScopeType::PolyTraitRef => late_depth += 1,
|
BinderScopeType::PolyTraitRef => late_depth += 1,
|
||||||
BinderScopeType::Concatenating => {}
|
BinderScopeType::Concatenating => {}
|
||||||
}
|
}
|
||||||
|
@ -3051,7 +3001,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
match scope_type {
|
match scope_type {
|
||||||
BinderScopeType::Other => late_depth += 1,
|
BinderScopeType::Other => late_depth += 1,
|
||||||
BinderScopeType::BoundedTy => late_depth += 1,
|
|
||||||
BinderScopeType::PolyTraitRef => late_depth += 1,
|
BinderScopeType::PolyTraitRef => late_depth += 1,
|
||||||
BinderScopeType::Concatenating => {}
|
BinderScopeType::Concatenating => {}
|
||||||
}
|
}
|
||||||
|
@ -3216,7 +3165,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||||
Scope::Binder { s, scope_type, .. } => {
|
Scope::Binder { s, scope_type, .. } => {
|
||||||
match scope_type {
|
match scope_type {
|
||||||
BinderScopeType::Other => late_depth += 1,
|
BinderScopeType::Other => late_depth += 1,
|
||||||
BinderScopeType::BoundedTy => late_depth += 1,
|
|
||||||
BinderScopeType::PolyTraitRef => late_depth += 1,
|
BinderScopeType::PolyTraitRef => late_depth += 1,
|
||||||
BinderScopeType::Concatenating => {}
|
BinderScopeType::Concatenating => {}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue