Skip to content
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

Implement partial support for non-lifetime binders #107489

Merged
merged 6 commits into from
Feb 17, 2023
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
34 changes: 0 additions & 34 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,27 +294,6 @@ impl<'a> AstValidator<'a> {
}
}

fn check_late_bound_lifetime_defs(&self, params: &[GenericParam]) {
// Check only lifetime parameters are present and that the lifetime
// parameters that are present have no bounds.
let non_lt_param_spans: Vec<_> = params
.iter()
.filter_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => {
if !param.bounds.is_empty() {
let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect();
self.session.emit_err(ForbiddenLifetimeBound { spans });
}
None
}
_ => Some(param.ident.span),
})
.collect();
if !non_lt_param_spans.is_empty() {
self.session.emit_err(ForbiddenNonLifetimeParam { spans: non_lt_param_spans });
}
}

fn check_fn_decl(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) {
self.check_decl_num_args(fn_decl);
self.check_decl_cvaradic_pos(fn_decl);
Expand Down Expand Up @@ -745,7 +724,6 @@ impl<'a> AstValidator<'a> {
)
.emit();
});
self.check_late_bound_lifetime_defs(&bfty.generic_params);
if let Extern::Implicit(_) = bfty.ext {
let sig_span = self.session.source_map().next_point(ty.span.shrink_to_lo());
self.maybe_lint_missing_abi(sig_span, ty.id);
Expand Down Expand Up @@ -1318,9 +1296,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
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);

// 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,
Expand Down Expand Up @@ -1396,11 +1371,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
visit::walk_param_bound(self, bound)
}

fn visit_poly_trait_ref(&mut self, t: &'a PolyTraitRef) {
self.check_late_bound_lifetime_defs(&t.bound_generic_params);
visit::walk_poly_trait_ref(self, t);
}

fn visit_variant_data(&mut self, s: &'a VariantData) {
self.with_banned_assoc_ty_bound(|this| visit::walk_struct_def(this, s))
}
Expand Down Expand Up @@ -1437,10 +1407,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
.emit();
}

if let FnKind::Closure(ClosureBinder::For { generic_params, .. }, ..) = fk {
self.check_late_bound_lifetime_defs(generic_params);
}

if let FnKind::Fn(
_,
_,
Expand Down
55 changes: 54 additions & 1 deletion compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi;

use crate::errors::ForbiddenLifetimeBound;

macro_rules! gate_feature_fn {
($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $help: expr) => {{
let (visitor, has_feature, span, name, explain, help) =
Expand Down Expand Up @@ -136,6 +138,34 @@ impl<'a> PostExpansionVisitor<'a> {
}
ImplTraitVisitor { vis: self }.visit_ty(ty);
}

fn check_late_bound_lifetime_defs(&self, params: &[ast::GenericParam]) {
// Check only lifetime parameters are present and that the lifetime
// parameters that are present have no bounds.
let non_lt_param_spans: Vec<_> = params
.iter()
.filter_map(|param| match param.kind {
ast::GenericParamKind::Lifetime { .. } => None,
_ => Some(param.ident.span),
})
.collect();
// FIXME: gate_feature_post doesn't really handle multispans...
if !non_lt_param_spans.is_empty() && !self.features.non_lifetime_binders {
feature_err(
&self.sess.parse_sess,
sym::non_lifetime_binders,
non_lt_param_spans,
rustc_errors::fluent::ast_passes_forbidden_non_lifetime_param,
)
.emit();
}
for param in params {
if !param.bounds.is_empty() {
let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect();
self.sess.emit_err(ForbiddenLifetimeBound { spans });
}
}
}
}

impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
Expand All @@ -147,7 +177,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
..
}) = attr_info
{
gate_feature_fn!(self, has_feature, attr.span, *name, descr);
gate_feature_fn!(self, has_feature, attr.span, *name, *descr);
}
// Check unstable flavors of the `#[doc]` attribute.
if attr.has_name(sym::doc) {
Expand Down Expand Up @@ -306,6 +336,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
ast::TyKind::BareFn(bare_fn_ty) => {
// Function pointers cannot be `const`
self.check_extern(bare_fn_ty.ext, ast::Const::No);
self.check_late_bound_lifetime_defs(&bare_fn_ty.generic_params);
}
ast::TyKind::Never => {
gate_feature_post!(&self, never_type, ty.span, "the `!` type is experimental");
Expand All @@ -318,6 +349,19 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
visit::walk_ty(self, ty)
}

fn visit_generics(&mut self, g: &'a ast::Generics) {
for predicate in &g.where_clause.predicates {
match predicate {
ast::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, g);
}

fn visit_fn_ret_ty(&mut self, ret_ty: &'a ast::FnRetTy) {
if let ast::FnRetTy::Ty(output_ty) = ret_ty {
if let ast::TyKind::Never = output_ty.kind {
Expand Down Expand Up @@ -437,12 +481,21 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
visit::walk_pat(self, pattern)
}

fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef) {
self.check_late_bound_lifetime_defs(&t.bound_generic_params);
visit::walk_poly_trait_ref(self, t);
}

fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
if let Some(header) = fn_kind.header() {
// Stability of const fn methods are covered in `visit_assoc_item` below.
self.check_extern(header.ext, header.constness);
}

if let FnKind::Closure(ast::ClosureBinder::For { generic_params, .. }, ..) = fn_kind {
self.check_late_bound_lifetime_defs(generic_params);
}

if fn_kind.ctxt() != Some(FnCtxt::Foreign) && fn_kind.decl().c_variadic() {
gate_feature_post!(&self, c_variadic, span, "C-variadic functions are unstable");
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_attr/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,7 @@ pub fn eval_condition(
sess,
sym::cfg_target_compact,
cfg.span,
&"compact `cfg(target(..))` is experimental and subject to change"
"compact `cfg(target(..))` is experimental and subject to change"
).emit();
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,8 @@ declare_features! (
(active, no_sanitize, "1.42.0", Some(39699), None),
/// 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),
/// 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
91 changes: 75 additions & 16 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::errors::{
AmbiguousLifetimeBound, MultipleRelaxedDefaultBounds, TraitObjectDeclaredWithNoTraits,
TypeofReservedKeywordUsed, ValueOfAssociatedStructAlreadySpecified,
};
use crate::middle::resolve_lifetime as rl;
use crate::middle::resolve_bound_vars as rbv;
use crate::require_c_abi_if_c_variadic;
use rustc_ast::TraitObjectSyntax;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
Expand Down Expand Up @@ -225,10 +225,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let tcx = self.tcx();
let lifetime_name = |def_id| tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id));

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

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

Some(rl::Region::EarlyBound(def_id)) => {
Some(rbv::ResolvedArg::EarlyBound(def_id)) => {
let name = tcx.hir().ty_param_name(def_id.expect_local());
let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local());
let generics = tcx.generics_of(item_def_id);
let index = generics.param_def_id_to_index[&def_id];
tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, index, name }))
}

Some(rl::Region::Free(scope, id)) => {
Some(rbv::ResolvedArg::Free(scope, id)) => {
let name = lifetime_name(id.expect_local());
tcx.mk_region(ty::ReFree(ty::FreeRegion {
scope,
Expand Down Expand Up @@ -1604,7 +1604,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
self.ast_region_to_region(lifetime, None)
} else {
self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| {
if tcx.named_region(lifetime.hir_id).is_some() {
if tcx.named_bound_var(lifetime.hir_id).is_some() {
self.ast_region_to_region(lifetime, None)
} else {
self.re_infer(None, span).unwrap_or_else(|| {
Expand Down Expand Up @@ -2598,6 +2598,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&self,
opt_self_ty: Option<Ty<'tcx>>,
path: &hir::Path<'_>,
hir_id: hir::HirId,
permit_variants: bool,
) -> Ty<'tcx> {
let tcx = self.tcx();
Expand Down Expand Up @@ -2661,11 +2662,25 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
});

let def_id = def_id.expect_local();
let item_def_id = tcx.hir().ty_param_owner(def_id);
let generics = tcx.generics_of(item_def_id);
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))
match tcx.named_bound_var(hir_id) {
Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => {
let name =
tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local()));
let br = ty::BoundTy {
var: ty::BoundVar::from_u32(index),
kind: ty::BoundTyKind::Param(def_id, name),
};
tcx.mk_ty(ty::Bound(debruijn, br))
}
Some(rbv::ResolvedArg::EarlyBound(_)) => {
let def_id = def_id.expect_local();
let item_def_id = tcx.hir().ty_param_owner(def_id);
let generics = tcx.generics_of(item_def_id);
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))
}
arg => bug!("unexpected bound var resolution for {hir_id:?}: {arg:?}"),
}
}
Res::SelfTyParam { .. } => {
// `Self` in trait or type alias.
Expand Down Expand Up @@ -2868,27 +2883,50 @@ 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);

tcx.mk_fn_ptr(self.ty_of_fn(
let fn_ptr_ty = 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);
let repr = match repr {
TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn,
TraitObjectSyntax::DynStar => ty::DynStar,
};
self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed, repr)

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
}
}
hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
debug!(?maybe_qself, ?path);
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.ast_ty_to_ty(qself));
self.res_to_ty(opt_self_ty, path, false)
self.res_to_ty(opt_self_ty, path, ast_ty.hir_id, false)
}
&hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => {
let opaque_ty = tcx.hir().item(item_id);
Expand Down Expand Up @@ -3344,3 +3382,24 @@ 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() })
})
}
4 changes: 2 additions & 2 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ use std::iter;

mod generics_of;
mod item_bounds;
mod lifetimes;
mod predicates_of;
mod resolve_bound_vars;
mod type_of;

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

pub fn provide(providers: &mut Providers) {
lifetimes::provide(providers);
resolve_bound_vars::provide(providers);
*providers = Providers {
opt_const_param_of: type_of::opt_const_param_of,
type_of: type_of::type_of,
Expand Down
11 changes: 6 additions & 5 deletions compiler/rustc_hir_analysis/src/collect/generics_of.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::middle::resolve_lifetime as rl;
use crate::middle::resolve_bound_vars as rbv;
use hir::{
intravisit::{self, Visitor},
GenericParamKind, HirId, Node,
Expand Down Expand Up @@ -394,10 +394,11 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
return;
}

match self.tcx.named_region(lt.hir_id) {
Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {}
Some(rl::Region::LateBound(debruijn, _, _)) if debruijn < self.outer_index => {}
Some(rl::Region::LateBound(..) | rl::Region::Free(..)) | None => {
match self.tcx.named_bound_var(lt.hir_id) {
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 => {
self.has_late_bound_regions = Some(lt.ident.span);
}
}
Expand Down
Loading