Skip to content

Commit

Permalink
Auto merge of #98462 - cjgillot:no-apit-hrtb-beta, r=Mark-Simulacrum
Browse files Browse the repository at this point in the history
[beta] Fail gracefully when encountering an HRTB in APIT.

Backport of #97683
The diagnostic is a bit worse, but still better than an ICE.

r? `@ehuss`
  • Loading branch information
bors committed Jun 26, 2022
2 parents d8ffc1f + a081507 commit 747075d
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 8 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ impl<'hir> WherePredicate<'hir> {
}
}

#[derive(Debug, HashStable_Generic, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, HashStable_Generic, PartialEq, Eq)]
pub enum PredicateOrigin {
WhereClause,
GenericParam,
Expand Down
72 changes: 65 additions & 7 deletions compiler/rustc_resolve/src/late/lifetimes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,13 @@ enum Scope<'a> {
/// In some cases not allowing late bounds allows us to avoid ICEs.
/// This is almost ways set to true.
allow_late_bound: bool,

/// If this binder comes from a where clause, specify how it was created.
/// This is used to diagnose inaccessible lifetimes in APIT:
/// ```ignore (illustrative)
/// fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
/// ```
where_bound_origin: Option<hir::PredicateOrigin>,
},

/// Lifetimes introduced by a fn are scoped to the call-site for that fn,
Expand Down Expand Up @@ -301,8 +308,9 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
opaque_type_parent,
scope_type,
hir_id,
s: _,
allow_late_bound,
where_bound_origin,
s: _,
} => f
.debug_struct("Binder")
.field("lifetimes", lifetimes)
Expand All @@ -311,8 +319,9 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
.field("opaque_type_parent", opaque_type_parent)
.field("scope_type", scope_type)
.field("hir_id", hir_id)
.field("s", &"..")
.field("allow_late_bound", allow_late_bound)
.field("where_bound_origin", where_bound_origin)
.field("s", &"..")
.finish(),
Scope::Body { id, s: _ } => {
f.debug_struct("Body").field("id", id).field("s", &"..").finish()
Expand Down Expand Up @@ -701,6 +710,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
opaque_type_parent: false,
scope_type: BinderScopeType::Normal,
allow_late_bound: true,
where_bound_origin: None,
};
self.with(scope, move |_old_scope, this| {
intravisit::walk_fn(this, fk, fd, b, s, hir_id)
Expand Down Expand Up @@ -829,6 +839,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
scope_type: BinderScopeType::Normal,
s: ROOT_SCOPE,
allow_late_bound: false,
where_bound_origin: None,
};
self.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &generics.params);
Expand Down Expand Up @@ -896,6 +907,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
opaque_type_parent: false,
scope_type: BinderScopeType::Normal,
allow_late_bound: true,
where_bound_origin: None,
};
self.with(scope, |old_scope, this| {
// a bare fn has no bounds, so everything
Expand Down Expand Up @@ -1091,6 +1103,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
opaque_type_parent: false,
scope_type: BinderScopeType::Normal,
allow_late_bound: false,
where_bound_origin: None,
};
this.with(scope, |_old_scope, this| {
this.visit_generics(generics);
Expand All @@ -1112,6 +1125,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
opaque_type_parent: false,
scope_type: BinderScopeType::Normal,
allow_late_bound: false,
where_bound_origin: None,
};
self.with(scope, |_old_scope, this| {
let scope = Scope::TraitRefBoundary { s: this.scope };
Expand Down Expand Up @@ -1172,6 +1186,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
opaque_type_parent: true,
scope_type: BinderScopeType::Normal,
allow_late_bound: false,
where_bound_origin: None,
};
self.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &generics.params);
Expand Down Expand Up @@ -1242,6 +1257,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
opaque_type_parent: true,
scope_type: BinderScopeType::Normal,
allow_late_bound: true,
where_bound_origin: None,
};
self.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &generics.params);
Expand Down Expand Up @@ -1356,6 +1372,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
ref bounded_ty,
bounds,
ref bound_generic_params,
origin,
..
}) => {
let (lifetimes, binders): (FxIndexMap<hir::ParamName, Region>, Vec<_>) =
Expand Down Expand Up @@ -1387,6 +1404,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
opaque_type_parent: false,
scope_type: BinderScopeType::Normal,
allow_late_bound: true,
where_bound_origin: Some(origin),
};
this.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &bound_generic_params);
Expand Down Expand Up @@ -1461,6 +1479,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
opaque_type_parent: false,
scope_type,
allow_late_bound: true,
where_bound_origin: None,
};
self.with(scope, |_, this| {
intravisit::walk_param_bound(this, bound);
Expand Down Expand Up @@ -1514,6 +1533,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
opaque_type_parent: false,
scope_type,
allow_late_bound: true,
where_bound_origin: None,
};
self.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params);
Expand Down Expand Up @@ -2207,6 +2227,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
track_lifetime_uses: false,
scope_type: BinderScopeType::Normal,
allow_late_bound: true,
where_bound_origin: None,
};
self.with(scope, move |old_scope, this| {
this.check_lifetime_params(old_scope, &generics.params);
Expand Down Expand Up @@ -2321,12 +2342,49 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
}

self.insert_lifetime(lifetime_ref, def);
} else {
self.tcx.sess.delay_span_bug(
lifetime_ref.span,
&format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,),
);
return;
}

// We may fail to resolve higher-ranked lifetimes that are mentionned by APIT.
// AST-based resolution does not care for impl-trait desugaring, which are the
// responibility of lowering. This may create a mismatch between the resolution
// AST found (`region_def_id`) which points to HRTB, and what HIR allows.
// ```
// fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
// ```
//
// In such case, walk back the binders to diagnose it properly.
let mut scope = self.scope;
loop {
match *scope {
Scope::Binder {
where_bound_origin: Some(hir::PredicateOrigin::ImplTrait), ..
} => {
self.tcx
.sess
.struct_span_err(
lifetime_ref.span,
"`impl Trait` can only mention lifetimes bound at the fn or impl level",
)
.emit();
return;
}
Scope::Root => break,
Scope::Binder { s, .. }
| Scope::Body { s, .. }
| Scope::Elision { s, .. }
| Scope::ObjectLifetimeDefault { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. } => {
scope = s;
}
}
}

self.tcx.sess.delay_span_bug(
lifetime_ref.span,
&format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,),
);
}

fn visit_segment_args(
Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/impl-trait/universal_wrong_hrtb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
trait Trait<'a> {
type Assoc;
}

fn test_argument_position(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
//~^ ERROR `impl Trait` can only mention lifetimes bound at the fn or impl level

fn main() {}
8 changes: 8 additions & 0 deletions src/test/ui/impl-trait/universal_wrong_hrtb.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: `impl Trait` can only mention lifetimes bound at the fn or impl level
--> $DIR/universal_wrong_hrtb.rs:5:73
|
LL | fn test_argument_position(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
| ^^

error: aborting due to previous error

0 comments on commit 747075d

Please sign in to comment.