Skip to content

Commit 089e8c0

Browse files
authored
Rollup merge of #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 6379c72 + 95f35fe commit 089e8c0

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

-34
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

+54-1
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

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

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

+75-16
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_re_late_bound(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_re_early_bound(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_re_free(scope, ty::BrNamed(id, name))
251251

@@ -1607,7 +1607,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
16071607
self.ast_region_to_region(lifetime, None)
16081608
} else {
16091609
self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| {
1610-
if tcx.named_region(lifetime.hir_id).is_some() {
1610+
if tcx.named_bound_var(lifetime.hir_id).is_some() {
16111611
self.ast_region_to_region(lifetime, None)
16121612
} else {
16131613
self.re_infer(None, span).unwrap_or_else(|| {
@@ -2600,6 +2600,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
26002600
&self,
26012601
opt_self_ty: Option<Ty<'tcx>>,
26022602
path: &hir::Path<'_>,
2603+
hir_id: hir::HirId,
26032604
permit_variants: bool,
26042605
) -> Ty<'tcx> {
26052606
let tcx = self.tcx();
@@ -2663,11 +2664,25 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
26632664
}
26642665
});
26652666

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

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

compiler/rustc_hir_analysis/src/collect.rs

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

+6-5
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)