Skip to content

Deny non-lifetime bound vars in for<..> || closure binders #108186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ declare_features! (
/// Allows using the `non_exhaustive_omitted_patterns` lint.
(active, non_exhaustive_omitted_patterns_lint, "1.57.0", Some(89554), None),
/// Allows `for<T>` binders in where-clauses
(incomplete, non_lifetime_binders, "CURRENT_RUSTC_VERSION", Some(1), None),
(incomplete, non_lifetime_binders, "CURRENT_RUSTC_VERSION", Some(108185), None),
/// Allows making `dyn Trait` well-formed even if `Trait` is not object safe.
/// In that case, `dyn Trait: Trait` does not hold. Moreover, coercions and
/// casts in safe Rust to `dyn Trait` for such a `Trait` is also forbidden.
Expand Down
54 changes: 8 additions & 46 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// (*) -- not late-bound, won't change
}

Some(rbv::ResolvedArg::Error(_)) => {
bug!("only ty/ct should resolve as ResolvedArg::Error")
}

None => {
self.re_infer(def, lifetime.ident.span).unwrap_or_else(|| {
debug!(?lifetime, "unelided lifetime in signature");
Expand Down Expand Up @@ -2689,6 +2693,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let index = generics.param_def_id_to_index[&def_id.to_def_id()];
tcx.mk_ty_param(index, tcx.hir().ty_param_name(def_id))
}
Some(rbv::ResolvedArg::Error(guar)) => tcx.ty_error_with_guaranteed(guar),
arg => bug!("unexpected bound var resolution for {hir_id:?}: {arg:?}"),
}
}
Expand Down Expand Up @@ -2893,22 +2898,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
hir::TyKind::BareFn(bf) => {
require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, ast_ty.span);

let fn_ptr_ty = tcx.mk_fn_ptr(self.ty_of_fn(
tcx.mk_fn_ptr(self.ty_of_fn(
ast_ty.hir_id,
bf.unsafety,
bf.abi,
bf.decl,
None,
Some(ast_ty),
));

if let Some(guar) =
deny_non_region_late_bound(tcx, bf.generic_params, "function pointer")
{
tcx.ty_error_with_guaranteed(guar)
} else {
fn_ptr_ty
}
))
}
hir::TyKind::TraitObject(bounds, lifetime, repr) => {
self.maybe_lint_bare_trait(ast_ty, in_path);
Expand All @@ -2917,21 +2914,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
TraitObjectSyntax::DynStar => ty::DynStar,
};

let object_ty = self.conv_object_ty_poly_trait_ref(
ast_ty.span,
bounds,
lifetime,
borrowed,
repr,
);

if let Some(guar) = bounds.iter().find_map(|trait_ref| {
deny_non_region_late_bound(tcx, trait_ref.bound_generic_params, "trait object")
}) {
tcx.ty_error_with_guaranteed(guar)
} else {
object_ty
}
self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed, repr)
}
hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
debug!(?maybe_qself, ?path);
Expand Down Expand Up @@ -3392,24 +3375,3 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
}
}

fn deny_non_region_late_bound(
tcx: TyCtxt<'_>,
params: &[hir::GenericParam<'_>],
where_: &str,
) -> Option<ErrorGuaranteed> {
params.iter().find_map(|bad_param| {
let what = match bad_param.kind {
hir::GenericParamKind::Type { .. } => "type",
hir::GenericParamKind::Const { .. } => "const",
hir::GenericParamKind::Lifetime { .. } => return None,
};

let mut diag = tcx.sess.struct_span_err(
bad_param.span,
format!("late-bound {what} parameter not allowed on {where_} types"),
);

Some(if tcx.features().non_lifetime_binders { diag.emit() } else { diag.delay_as_bug() })
})
}
7 changes: 6 additions & 1 deletion compiler/rustc_hir_analysis/src/collect/generics_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,12 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
Some(rbv::ResolvedArg::StaticLifetime | rbv::ResolvedArg::EarlyBound(..)) => {}
Some(rbv::ResolvedArg::LateBound(debruijn, _, _))
if debruijn < self.outer_index => {}
Some(rbv::ResolvedArg::LateBound(..) | rbv::ResolvedArg::Free(..)) | None => {
Some(
rbv::ResolvedArg::LateBound(..)
| rbv::ResolvedArg::Free(..)
| rbv::ResolvedArg::Error(_),
)
| None => {
self.has_late_bound_regions = Some(lt.ident.span);
}
}
Expand Down
138 changes: 100 additions & 38 deletions compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl RegionExt for ResolvedArg {

fn id(&self) -> Option<DefId> {
match *self {
ResolvedArg::StaticLifetime => None,
ResolvedArg::StaticLifetime | ResolvedArg::Error(_) => None,

ResolvedArg::EarlyBound(id)
| ResolvedArg::LateBound(_, _, id)
Expand Down Expand Up @@ -336,7 +336,57 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
}
}
}

fn visit_poly_trait_ref_inner(
&mut self,
trait_ref: &'tcx hir::PolyTraitRef<'tcx>,
non_lifetime_binder_allowed: NonLifetimeBinderAllowed,
) {
debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref);

let (mut binders, scope_type) = self.poly_trait_ref_binder_info();

let initial_bound_vars = binders.len() as u32;
let mut bound_vars: FxIndexMap<LocalDefId, ResolvedArg> = FxIndexMap::default();
let binders_iter =
trait_ref.bound_generic_params.iter().enumerate().map(|(late_bound_idx, param)| {
let pair = ResolvedArg::late(initial_bound_vars + late_bound_idx as u32, param);
let r = late_arg_as_bound_arg(self.tcx, &pair.1, param);
bound_vars.insert(pair.0, pair.1);
r
});
binders.extend(binders_iter);

if let NonLifetimeBinderAllowed::Deny(where_) = non_lifetime_binder_allowed {
deny_non_region_late_bound(self.tcx, &mut bound_vars, where_);
}

debug!(?binders);
self.record_late_bound_vars(trait_ref.trait_ref.hir_ref_id, binders);

// 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
// just reuse the concatenation functionality also present in nested trait
// refs.
let scope = Scope::Binder {
hir_id: trait_ref.trait_ref.hir_ref_id,
bound_vars,
s: self.scope,
scope_type,
where_bound_origin: None,
};
self.with(scope, |this| {
walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
this.visit_trait_ref(&trait_ref.trait_ref);
});
}
}

enum NonLifetimeBinderAllowed {
Deny(&'static str),
Allow,
}

impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;

Expand Down Expand Up @@ -400,7 +450,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
}
}

let (bound_vars, binders): (FxIndexMap<LocalDefId, ResolvedArg>, Vec<_>) =
let (mut bound_vars, binders): (FxIndexMap<LocalDefId, ResolvedArg>, Vec<_>) =
bound_generic_params
.iter()
.enumerate()
Expand All @@ -411,6 +461,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
})
.unzip();

deny_non_region_late_bound(self.tcx, &mut bound_vars, "closures");

self.record_late_bound_vars(e.hir_id, binders);
let scope = Scope::Binder {
hir_id: e.hir_id,
Expand Down Expand Up @@ -567,7 +619,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
match ty.kind {
hir::TyKind::BareFn(c) => {
let (bound_vars, binders): (FxIndexMap<LocalDefId, ResolvedArg>, Vec<_>) = c
let (mut bound_vars, binders): (FxIndexMap<LocalDefId, ResolvedArg>, Vec<_>) = c
.generic_params
.iter()
.enumerate()
Expand All @@ -577,6 +629,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
(pair, r)
})
.unzip();

deny_non_region_late_bound(self.tcx, &mut bound_vars, "function pointer types");

self.record_late_bound_vars(ty.hir_id, binders);
let scope = Scope::Binder {
hir_id: ty.hir_id,
Expand All @@ -596,7 +651,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
let scope = Scope::TraitRefBoundary { s: self.scope };
self.with(scope, |this| {
for bound in bounds {
this.visit_poly_trait_ref(bound);
this.visit_poly_trait_ref_inner(
bound,
NonLifetimeBinderAllowed::Deny("trait object types"),
);
}
});
match lifetime.res {
Expand Down Expand Up @@ -967,39 +1025,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
}

fn visit_poly_trait_ref(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) {
debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref);

let (mut binders, scope_type) = self.poly_trait_ref_binder_info();

let initial_bound_vars = binders.len() as u32;
let mut bound_vars: FxIndexMap<LocalDefId, ResolvedArg> = FxIndexMap::default();
let binders_iter =
trait_ref.bound_generic_params.iter().enumerate().map(|(late_bound_idx, param)| {
let pair = ResolvedArg::late(initial_bound_vars + late_bound_idx as u32, param);
let r = late_arg_as_bound_arg(self.tcx, &pair.1, param);
bound_vars.insert(pair.0, pair.1);
r
});
binders.extend(binders_iter);

debug!(?binders);
self.record_late_bound_vars(trait_ref.trait_ref.hir_ref_id, binders);

// 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
// just reuse the concatenation functionality also present in nested trait
// refs.
let scope = Scope::Binder {
hir_id: trait_ref.trait_ref.hir_ref_id,
bound_vars,
s: self.scope,
scope_type,
where_bound_origin: None,
};
self.with(scope, |this| {
walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
this.visit_trait_ref(&trait_ref.trait_ref);
});
self.visit_poly_trait_ref_inner(trait_ref, NonLifetimeBinderAllowed::Allow);
}
}

Expand Down Expand Up @@ -1364,7 +1390,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
return;
}

span_bug!(self.tcx.hir().span(hir_id), "could not resolve {param_def_id:?}",);
self.tcx
.sess
.delay_span_bug(self.tcx.hir().span(hir_id), "could not resolve {param_def_id:?}");
}

#[instrument(level = "debug", skip(self))]
Expand Down Expand Up @@ -1915,3 +1943,37 @@ fn is_late_bound_map(
}
}
}

pub fn deny_non_region_late_bound(
tcx: TyCtxt<'_>,
bound_vars: &mut FxIndexMap<LocalDefId, ResolvedArg>,
where_: &str,
) {
let mut first = true;

for (var, arg) in bound_vars {
let Node::GenericParam(param) = tcx.hir().get_by_def_id(*var) else {
bug!();
};

let what = match param.kind {
hir::GenericParamKind::Type { .. } => "type",
hir::GenericParamKind::Const { .. } => "const",
hir::GenericParamKind::Lifetime { .. } => continue,
};

let mut diag = tcx.sess.struct_span_err(
param.span,
format!("late-bound {what} parameter not allowed on {where_}"),
);

let guar = if tcx.features().non_lifetime_binders && first {
diag.emit()
} else {
diag.delay_as_bug()
};

first = false;
*arg = ResolvedArg::Error(guar);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
rbv::ResolvedArg::StaticLifetime
| rbv::ResolvedArg::Free(_, _)
| rbv::ResolvedArg::EarlyBound(_)
| rbv::ResolvedArg::LateBound(_, _, _),
| rbv::ResolvedArg::LateBound(_, _, _)
| rbv::ResolvedArg::Error(_),
)
| None,
_,
Expand Down Expand Up @@ -211,7 +212,8 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
rbv::ResolvedArg::StaticLifetime
| rbv::ResolvedArg::EarlyBound(_)
| rbv::ResolvedArg::LateBound(_, _, _)
| rbv::ResolvedArg::Free(_, _),
| rbv::ResolvedArg::Free(_, _)
| rbv::ResolvedArg::Error(_),
)
| None,
_,
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/middle/resolve_bound_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use crate::ty;

use rustc_data_structures::fx::FxHashMap;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::DefId;
use rustc_hir::{ItemLocalId, OwnerId};
use rustc_macros::HashStable;
Expand All @@ -13,6 +14,7 @@ pub enum ResolvedArg {
EarlyBound(/* decl */ DefId),
LateBound(ty::DebruijnIndex, /* late-bound index */ u32, /* decl */ DefId),
Free(DefId, /* lifetime decl */ DefId),
Error(ErrorGuaranteed),
}

/// A set containing, at most, one known element.
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/ty/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ impl<'tcx> Const<'tcx> {
ty::ConstKind::Bound(debruijn, ty::BoundVar::from_u32(index)),
ty,
)),
Some(rbv::ResolvedArg::Error(guar)) => {
Some(tcx.const_error_with_guaranteed(ty, guar))
}
arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", expr.hir_id),
}
}
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/bounds-lifetime.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ error[E0658]: only lifetime parameters can be used in this context
LL | type D = for<'a, T> fn();
| ^
|
= note: see issue #1 <https://github.com/rust-lang/rust/issues/1> for more information
= note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
= help: add `#![feature(non_lifetime_binders)]` to the crate attributes to enable

error[E0658]: only lifetime parameters can be used in this context
Expand All @@ -31,7 +31,7 @@ error[E0658]: only lifetime parameters can be used in this context
LL | type E = dyn for<T> Fn();
| ^
|
= note: see issue #1 <https://github.com/rust-lang/rust/issues/1> for more information
= note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
= help: add `#![feature(non_lifetime_binders)]` to the crate attributes to enable

error: aborting due to 5 previous errors
Expand Down
7 changes: 7 additions & 0 deletions tests/ui/closures/binder/const-bound.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#![feature(closure_lifetime_binder, non_lifetime_binders)]
//~^ WARN is incomplete and may not be safe to use

fn main() {
for<const N: i32> || -> () {};
//~^ ERROR late-bound const parameter not allowed on closures
}
Loading