Skip to content

Commit 71bad87

Browse files
authored
Rollup merge of rust-lang#107489 - compiler-errors:non_lifetime_binders, r=cjgillot
Implement partial support for non-lifetime binders This implements support for non-lifetime binders. It's pretty useless currently, but I wanted to put this up so the implementation can be discussed. Specifically, this piggybacks off of the late-bound lifetime collection code in `rustc_hir_typeck::collect::lifetimes`. This seems like a necessary step given the fact we don't resolve late-bound regions until this point, and binders are sometimes merged. Q: I'm not sure if I should go along this route, or try to modify the earlier nameres code to compute the right bound var indices for type and const binders eagerly... If so, I'll need to rename all these queries to something more appropriate (I've done this for `resolve_lifetime::Region` -> `resolve_lifetime::ResolvedArg`) cc rust-lang/types-team#81 r? `@ghost`
2 parents 9bb6e60 + 0162292 commit 71bad87

File tree

48 files changed

+709
-343
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+709
-343
lines changed

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -294,27 +294,6 @@ impl<'a> AstValidator<'a> {
294294
}
295295
}
296296

297-
fn check_late_bound_lifetime_defs(&self, params: &[GenericParam]) {
298-
// Check only lifetime parameters are present and that the lifetime
299-
// parameters that are present have no bounds.
300-
let non_lt_param_spans: Vec<_> = params
301-
.iter()
302-
.filter_map(|param| match param.kind {
303-
GenericParamKind::Lifetime { .. } => {
304-
if !param.bounds.is_empty() {
305-
let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect();
306-
self.session.emit_err(ForbiddenLifetimeBound { spans });
307-
}
308-
None
309-
}
310-
_ => Some(param.ident.span),
311-
})
312-
.collect();
313-
if !non_lt_param_spans.is_empty() {
314-
self.session.emit_err(ForbiddenNonLifetimeParam { spans: non_lt_param_spans });
315-
}
316-
}
317-
318297
fn check_fn_decl(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) {
319298
self.check_decl_num_args(fn_decl);
320299
self.check_decl_cvaradic_pos(fn_decl);
@@ -745,7 +724,6 @@ impl<'a> AstValidator<'a> {
745724
)
746725
.emit();
747726
});
748-
self.check_late_bound_lifetime_defs(&bfty.generic_params);
749727
if let Extern::Implicit(_) = bfty.ext {
750728
let sig_span = self.session.source_map().next_point(ty.span.shrink_to_lo());
751729
self.maybe_lint_missing_abi(sig_span, ty.id);
@@ -1318,9 +1296,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
13181296
for predicate in &generics.where_clause.predicates {
13191297
match predicate {
13201298
WherePredicate::BoundPredicate(bound_pred) => {
1321-
// A type binding, eg `for<'c> Foo: Send+Clone+'c`
1322-
self.check_late_bound_lifetime_defs(&bound_pred.bound_generic_params);
1323-
13241299
// This is slightly complicated. Our representation for poly-trait-refs contains a single
13251300
// binder and thus we only allow a single level of quantification. However,
13261301
// the syntax of Rust permits quantification in two places in where clauses,
@@ -1396,11 +1371,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
13961371
visit::walk_param_bound(self, bound)
13971372
}
13981373

1399-
fn visit_poly_trait_ref(&mut self, t: &'a PolyTraitRef) {
1400-
self.check_late_bound_lifetime_defs(&t.bound_generic_params);
1401-
visit::walk_poly_trait_ref(self, t);
1402-
}
1403-
14041374
fn visit_variant_data(&mut self, s: &'a VariantData) {
14051375
self.with_banned_assoc_ty_bound(|this| visit::walk_struct_def(this, s))
14061376
}
@@ -1437,10 +1407,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
14371407
.emit();
14381408
}
14391409

1440-
if let FnKind::Closure(ClosureBinder::For { generic_params, .. }, ..) = fk {
1441-
self.check_late_bound_lifetime_defs(generic_params);
1442-
}
1443-
14441410
if let FnKind::Fn(
14451411
_,
14461412
_,

compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use rustc_span::symbol::sym;
1111
use rustc_span::Span;
1212
use rustc_target::spec::abi;
1313

14+
use crate::errors::ForbiddenLifetimeBound;
15+
1416
macro_rules! gate_feature_fn {
1517
($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $help: expr) => {{
1618
let (visitor, has_feature, span, name, explain, help) =
@@ -136,6 +138,34 @@ impl<'a> PostExpansionVisitor<'a> {
136138
}
137139
ImplTraitVisitor { vis: self }.visit_ty(ty);
138140
}
141+
142+
fn check_late_bound_lifetime_defs(&self, params: &[ast::GenericParam]) {
143+
// Check only lifetime parameters are present and that the lifetime
144+
// parameters that are present have no bounds.
145+
let non_lt_param_spans: Vec<_> = params
146+
.iter()
147+
.filter_map(|param| match param.kind {
148+
ast::GenericParamKind::Lifetime { .. } => None,
149+
_ => Some(param.ident.span),
150+
})
151+
.collect();
152+
// FIXME: gate_feature_post doesn't really handle multispans...
153+
if !non_lt_param_spans.is_empty() && !self.features.non_lifetime_binders {
154+
feature_err(
155+
&self.sess.parse_sess,
156+
sym::non_lifetime_binders,
157+
non_lt_param_spans,
158+
rustc_errors::fluent::ast_passes_forbidden_non_lifetime_param,
159+
)
160+
.emit();
161+
}
162+
for param in params {
163+
if !param.bounds.is_empty() {
164+
let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect();
165+
self.sess.emit_err(ForbiddenLifetimeBound { spans });
166+
}
167+
}
168+
}
139169
}
140170

141171
impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
@@ -147,7 +177,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
147177
..
148178
}) = attr_info
149179
{
150-
gate_feature_fn!(self, has_feature, attr.span, *name, descr);
180+
gate_feature_fn!(self, has_feature, attr.span, *name, *descr);
151181
}
152182
// Check unstable flavors of the `#[doc]` attribute.
153183
if attr.has_name(sym::doc) {
@@ -306,6 +336,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
306336
ast::TyKind::BareFn(bare_fn_ty) => {
307337
// Function pointers cannot be `const`
308338
self.check_extern(bare_fn_ty.ext, ast::Const::No);
339+
self.check_late_bound_lifetime_defs(&bare_fn_ty.generic_params);
309340
}
310341
ast::TyKind::Never => {
311342
gate_feature_post!(&self, never_type, ty.span, "the `!` type is experimental");
@@ -318,6 +349,19 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
318349
visit::walk_ty(self, ty)
319350
}
320351

352+
fn visit_generics(&mut self, g: &'a ast::Generics) {
353+
for predicate in &g.where_clause.predicates {
354+
match predicate {
355+
ast::WherePredicate::BoundPredicate(bound_pred) => {
356+
// A type binding, eg `for<'c> Foo: Send+Clone+'c`
357+
self.check_late_bound_lifetime_defs(&bound_pred.bound_generic_params);
358+
}
359+
_ => {}
360+
}
361+
}
362+
visit::walk_generics(self, g);
363+
}
364+
321365
fn visit_fn_ret_ty(&mut self, ret_ty: &'a ast::FnRetTy) {
322366
if let ast::FnRetTy::Ty(output_ty) = ret_ty {
323367
if let ast::TyKind::Never = output_ty.kind {
@@ -437,12 +481,21 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
437481
visit::walk_pat(self, pattern)
438482
}
439483

484+
fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef) {
485+
self.check_late_bound_lifetime_defs(&t.bound_generic_params);
486+
visit::walk_poly_trait_ref(self, t);
487+
}
488+
440489
fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
441490
if let Some(header) = fn_kind.header() {
442491
// Stability of const fn methods are covered in `visit_assoc_item` below.
443492
self.check_extern(header.ext, header.constness);
444493
}
445494

495+
if let FnKind::Closure(ast::ClosureBinder::For { generic_params, .. }, ..) = fn_kind {
496+
self.check_late_bound_lifetime_defs(generic_params);
497+
}
498+
446499
if fn_kind.ctxt() != Some(FnCtxt::Foreign) && fn_kind.decl().c_variadic() {
447500
gate_feature_post!(&self, c_variadic, span, "C-variadic functions are unstable");
448501
}

compiler/rustc_attr/src/builtin.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,7 @@ pub fn eval_condition(
731731
sess,
732732
sym::cfg_target_compact,
733733
cfg.span,
734-
&"compact `cfg(target(..))` is experimental and subject to change"
734+
"compact `cfg(target(..))` is experimental and subject to change"
735735
).emit();
736736
}
737737

compiler/rustc_feature/src/active.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,8 @@ declare_features! (
473473
(active, no_sanitize, "1.42.0", Some(39699), None),
474474
/// Allows using the `non_exhaustive_omitted_patterns` lint.
475475
(active, non_exhaustive_omitted_patterns_lint, "1.57.0", Some(89554), None),
476+
/// Allows `for<T>` binders in where-clauses
477+
(incomplete, non_lifetime_binders, "CURRENT_RUSTC_VERSION", Some(1), None),
476478
/// Allows making `dyn Trait` well-formed even if `Trait` is not object safe.
477479
/// In that case, `dyn Trait: Trait` does not hold. Moreover, coercions and
478480
/// casts in safe Rust to `dyn Trait` for such a `Trait` is also forbidden.

compiler/rustc_hir_analysis/src/astconv/mod.rs

Lines changed: 75 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::errors::{
1414
AmbiguousLifetimeBound, MultipleRelaxedDefaultBounds, TraitObjectDeclaredWithNoTraits,
1515
TypeofReservedKeywordUsed, ValueOfAssociatedStructAlreadySpecified,
1616
};
17-
use crate::middle::resolve_lifetime as rl;
17+
use crate::middle::resolve_bound_vars as rbv;
1818
use crate::require_c_abi_if_c_variadic;
1919
use rustc_ast::TraitObjectSyntax;
2020
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -225,10 +225,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
225225
let tcx = self.tcx();
226226
let lifetime_name = |def_id| tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id));
227227

228-
match tcx.named_region(lifetime.hir_id) {
229-
Some(rl::Region::Static) => tcx.lifetimes.re_static,
228+
match tcx.named_bound_var(lifetime.hir_id) {
229+
Some(rbv::ResolvedArg::StaticLifetime) => tcx.lifetimes.re_static,
230230

231-
Some(rl::Region::LateBound(debruijn, index, def_id)) => {
231+
Some(rbv::ResolvedArg::LateBound(debruijn, index, def_id)) => {
232232
let name = lifetime_name(def_id.expect_local());
233233
let br = ty::BoundRegion {
234234
var: ty::BoundVar::from_u32(index),
@@ -237,15 +237,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
237237
tcx.mk_region(ty::ReLateBound(debruijn, br))
238238
}
239239

240-
Some(rl::Region::EarlyBound(def_id)) => {
240+
Some(rbv::ResolvedArg::EarlyBound(def_id)) => {
241241
let name = tcx.hir().ty_param_name(def_id.expect_local());
242242
let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local());
243243
let generics = tcx.generics_of(item_def_id);
244244
let index = generics.param_def_id_to_index[&def_id];
245245
tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, index, name }))
246246
}
247247

248-
Some(rl::Region::Free(scope, id)) => {
248+
Some(rbv::ResolvedArg::Free(scope, id)) => {
249249
let name = lifetime_name(id.expect_local());
250250
tcx.mk_region(ty::ReFree(ty::FreeRegion {
251251
scope,
@@ -1604,7 +1604,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
16041604
self.ast_region_to_region(lifetime, None)
16051605
} else {
16061606
self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| {
1607-
if tcx.named_region(lifetime.hir_id).is_some() {
1607+
if tcx.named_bound_var(lifetime.hir_id).is_some() {
16081608
self.ast_region_to_region(lifetime, None)
16091609
} else {
16101610
self.re_infer(None, span).unwrap_or_else(|| {
@@ -2598,6 +2598,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
25982598
&self,
25992599
opt_self_ty: Option<Ty<'tcx>>,
26002600
path: &hir::Path<'_>,
2601+
hir_id: hir::HirId,
26012602
permit_variants: bool,
26022603
) -> Ty<'tcx> {
26032604
let tcx = self.tcx();
@@ -2661,11 +2662,25 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
26612662
}
26622663
});
26632664

2664-
let def_id = def_id.expect_local();
2665-
let item_def_id = tcx.hir().ty_param_owner(def_id);
2666-
let generics = tcx.generics_of(item_def_id);
2667-
let index = generics.param_def_id_to_index[&def_id.to_def_id()];
2668-
tcx.mk_ty_param(index, tcx.hir().ty_param_name(def_id))
2665+
match tcx.named_bound_var(hir_id) {
2666+
Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => {
2667+
let name =
2668+
tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local()));
2669+
let br = ty::BoundTy {
2670+
var: ty::BoundVar::from_u32(index),
2671+
kind: ty::BoundTyKind::Param(def_id, name),
2672+
};
2673+
tcx.mk_ty(ty::Bound(debruijn, br))
2674+
}
2675+
Some(rbv::ResolvedArg::EarlyBound(_)) => {
2676+
let def_id = def_id.expect_local();
2677+
let item_def_id = tcx.hir().ty_param_owner(def_id);
2678+
let generics = tcx.generics_of(item_def_id);
2679+
let index = generics.param_def_id_to_index[&def_id.to_def_id()];
2680+
tcx.mk_ty_param(index, tcx.hir().ty_param_name(def_id))
2681+
}
2682+
arg => bug!("unexpected bound var resolution for {hir_id:?}: {arg:?}"),
2683+
}
26692684
}
26702685
Res::SelfTyParam { .. } => {
26712686
// `Self` in trait or type alias.
@@ -2868,27 +2883,50 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
28682883
hir::TyKind::BareFn(bf) => {
28692884
require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, ast_ty.span);
28702885

2871-
tcx.mk_fn_ptr(self.ty_of_fn(
2886+
let fn_ptr_ty = tcx.mk_fn_ptr(self.ty_of_fn(
28722887
ast_ty.hir_id,
28732888
bf.unsafety,
28742889
bf.abi,
28752890
bf.decl,
28762891
None,
28772892
Some(ast_ty),
2878-
))
2893+
));
2894+
2895+
if let Some(guar) =
2896+
deny_non_region_late_bound(tcx, bf.generic_params, "function pointer")
2897+
{
2898+
tcx.ty_error_with_guaranteed(guar)
2899+
} else {
2900+
fn_ptr_ty
2901+
}
28792902
}
28802903
hir::TyKind::TraitObject(bounds, lifetime, repr) => {
28812904
self.maybe_lint_bare_trait(ast_ty, in_path);
28822905
let repr = match repr {
28832906
TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn,
28842907
TraitObjectSyntax::DynStar => ty::DynStar,
28852908
};
2886-
self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed, repr)
2909+
2910+
let object_ty = self.conv_object_ty_poly_trait_ref(
2911+
ast_ty.span,
2912+
bounds,
2913+
lifetime,
2914+
borrowed,
2915+
repr,
2916+
);
2917+
2918+
if let Some(guar) = bounds.iter().find_map(|trait_ref| {
2919+
deny_non_region_late_bound(tcx, trait_ref.bound_generic_params, "trait object")
2920+
}) {
2921+
tcx.ty_error_with_guaranteed(guar)
2922+
} else {
2923+
object_ty
2924+
}
28872925
}
28882926
hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
28892927
debug!(?maybe_qself, ?path);
28902928
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.ast_ty_to_ty(qself));
2891-
self.res_to_ty(opt_self_ty, path, false)
2929+
self.res_to_ty(opt_self_ty, path, ast_ty.hir_id, false)
28922930
}
28932931
&hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => {
28942932
let opaque_ty = tcx.hir().item(item_id);
@@ -3344,3 +3382,24 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
33443382
}
33453383
}
33463384
}
3385+
3386+
fn deny_non_region_late_bound(
3387+
tcx: TyCtxt<'_>,
3388+
params: &[hir::GenericParam<'_>],
3389+
where_: &str,
3390+
) -> Option<ErrorGuaranteed> {
3391+
params.iter().find_map(|bad_param| {
3392+
let what = match bad_param.kind {
3393+
hir::GenericParamKind::Type { .. } => "type",
3394+
hir::GenericParamKind::Const { .. } => "const",
3395+
hir::GenericParamKind::Lifetime { .. } => return None,
3396+
};
3397+
3398+
let mut diag = tcx.sess.struct_span_err(
3399+
bad_param.span,
3400+
format!("late-bound {what} parameter not allowed on {where_} types"),
3401+
);
3402+
3403+
Some(if tcx.features().non_lifetime_binders { diag.emit() } else { diag.delay_as_bug() })
3404+
})
3405+
}

compiler/rustc_hir_analysis/src/collect.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ use std::iter;
4141

4242
mod generics_of;
4343
mod item_bounds;
44-
mod lifetimes;
4544
mod predicates_of;
45+
mod resolve_bound_vars;
4646
mod type_of;
4747

4848
///////////////////////////////////////////////////////////////////////////
@@ -53,7 +53,7 @@ fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
5353
}
5454

5555
pub fn provide(providers: &mut Providers) {
56-
lifetimes::provide(providers);
56+
resolve_bound_vars::provide(providers);
5757
*providers = Providers {
5858
opt_const_param_of: type_of::opt_const_param_of,
5959
type_of: type_of::type_of,

compiler/rustc_hir_analysis/src/collect/generics_of.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::middle::resolve_lifetime as rl;
1+
use crate::middle::resolve_bound_vars as rbv;
22
use hir::{
33
intravisit::{self, Visitor},
44
GenericParamKind, HirId, Node,
@@ -394,10 +394,11 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
394394
return;
395395
}
396396

397-
match self.tcx.named_region(lt.hir_id) {
398-
Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {}
399-
Some(rl::Region::LateBound(debruijn, _, _)) if debruijn < self.outer_index => {}
400-
Some(rl::Region::LateBound(..) | rl::Region::Free(..)) | None => {
397+
match self.tcx.named_bound_var(lt.hir_id) {
398+
Some(rbv::ResolvedArg::StaticLifetime | rbv::ResolvedArg::EarlyBound(..)) => {}
399+
Some(rbv::ResolvedArg::LateBound(debruijn, _, _))
400+
if debruijn < self.outer_index => {}
401+
Some(rbv::ResolvedArg::LateBound(..) | rbv::ResolvedArg::Free(..)) | None => {
401402
self.has_late_bound_regions = Some(lt.ident.span);
402403
}
403404
}

0 commit comments

Comments
 (0)