Skip to content

Commit

Permalink
Auto merge of #126049 - compiler-errors:rework-use, r=oli-obk
Browse files Browse the repository at this point in the history
Rework `feature(precise_capturing)` to represent `use<...>` as a syntactical bound

Reworks `precise_capturing` for a recent lang-team consensus.

Specifically:

> The conclusion of the team is that we'll make use<..> a bound. That is, we'll support impl use<..> + Trait, impl Trait + use<..>, etc.

> For now, we will support at most one such bound in a list of bounds, and semantically we'll only support these bounds in the item bounds of RPIT-like impl Trait opaque types (i.e., in the places discussed in the RFC).

Lang decision in favor of this approach:

- #125836 (comment)

Tracking:

- #123432
  • Loading branch information
bors committed Jun 18, 2024
2 parents f873ae0 + 2273747 commit c1f62a7
Show file tree
Hide file tree
Showing 76 changed files with 628 additions and 574 deletions.
5 changes: 4 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,13 +307,16 @@ impl TraitBoundModifiers {
pub enum GenericBound {
Trait(PolyTraitRef, TraitBoundModifiers),
Outlives(Lifetime),
/// Precise capturing syntax: `impl Sized + use<'a>`
Use(ThinVec<PreciseCapturingArg>, Span),
}

impl GenericBound {
pub fn span(&self) -> Span {
match self {
GenericBound::Trait(t, ..) => t.span,
GenericBound::Outlives(l) => l.ident.span,
GenericBound::Use(_, span) => *span,
}
}
}
Expand Down Expand Up @@ -2162,7 +2165,7 @@ pub enum TyKind {
/// The `NodeId` exists to prevent lowering from having to
/// generate `NodeId`s on the fly, which would complicate
/// the generation of opaque `type Foo = impl Trait` items significantly.
ImplTrait(NodeId, GenericBounds, Option<P<(ThinVec<PreciseCapturingArg>, Span)>>),
ImplTrait(NodeId, GenericBounds),
/// No-op; kept solely so that we can pretty-print faithfully.
Paren(P<Ty>),
/// Unused for now.
Expand Down
12 changes: 6 additions & 6 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,14 +523,9 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
TyKind::TraitObject(bounds, _syntax) => {
visit_vec(bounds, |bound| vis.visit_param_bound(bound))
}
TyKind::ImplTrait(id, bounds, precise_capturing) => {
TyKind::ImplTrait(id, bounds) => {
vis.visit_id(id);
visit_vec(bounds, |bound| vis.visit_param_bound(bound));
if let Some((precise_capturing, _span)) = precise_capturing.as_deref_mut() {
for arg in precise_capturing {
vis.visit_precise_capturing_arg(arg);
}
}
}
TyKind::MacCall(mac) => vis.visit_mac_call(mac),
TyKind::AnonStruct(id, fields) | TyKind::AnonUnion(id, fields) => {
Expand Down Expand Up @@ -923,6 +918,11 @@ fn noop_visit_param_bound<T: MutVisitor>(pb: &mut GenericBound, vis: &mut T) {
match pb {
GenericBound::Trait(ty, _modifier) => vis.visit_poly_trait_ref(ty),
GenericBound::Outlives(lifetime) => noop_visit_lifetime(lifetime, vis),
GenericBound::Use(args, _) => {
for arg in args {
vis.visit_precise_capturing_arg(arg);
}
}
}
}

Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_ast/src/util/classify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,15 +184,17 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
None => break None,
},

ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds, _) => {
ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
match bounds.last() {
Some(ast::GenericBound::Trait(bound, _)) => {
match path_return_type(&bound.trait_ref.path) {
Some(trailing_ty) => ty = trailing_ty,
None => break None,
}
}
Some(ast::GenericBound::Outlives(_)) | None => break None,
Some(ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..)) | None => {
break None;
}
}
}

Expand Down
21 changes: 15 additions & 6 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ pub enum BoundKind {
/// E.g., `trait A: B`
SuperTraits,
}
impl BoundKind {
pub fn descr(self) -> &'static str {
match self {
BoundKind::Bound => "bounds",
BoundKind::Impl => "`impl Trait`",
BoundKind::TraitObject => "`dyn` trait object bounds",
BoundKind::SuperTraits => "supertrait bounds",
}
}
}

#[derive(Copy, Clone, Debug)]
pub enum FnKind<'a> {
Expand Down Expand Up @@ -497,13 +507,8 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
TyKind::TraitObject(bounds, ..) => {
walk_list!(visitor, visit_param_bound, bounds, BoundKind::TraitObject);
}
TyKind::ImplTrait(_, bounds, precise_capturing) => {
TyKind::ImplTrait(_, bounds) => {
walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl);
if let Some((precise_capturing, _span)) = precise_capturing.as_deref() {
for arg in precise_capturing {
try_visit!(visitor.visit_precise_capturing_arg(arg));
}
}
}
TyKind::Typeof(expression) => try_visit!(visitor.visit_anon_const(expression)),
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy | TyKind::Err(_) => {}
Expand Down Expand Up @@ -688,6 +693,10 @@ pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericB
match bound {
GenericBound::Trait(typ, _modifier) => visitor.visit_poly_trait_ref(typ),
GenericBound::Outlives(lifetime) => visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound),
GenericBound::Use(args, _) => {
walk_list!(visitor, visit_precise_capturing_arg, args);
V::Result::output()
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ ast_lowering_never_pattern_with_guard =
a guard on a never pattern will never be run
.suggestion = remove this guard
ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed on argument-position `impl Trait`
ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed in argument-position `impl Trait`
ast_lowering_previously_used_here = previously used here
Expand Down
139 changes: 71 additions & 68 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::Lrc;
use rustc_errors::{DiagArgFromDisplay, DiagCtxt, StashKey};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
use rustc_hir::def_id::{LocalDefId, LocalDefIdMap, CRATE_DEF_ID, LOCAL_CRATE};
use rustc_hir::{self as hir};
use rustc_hir::{
ConstArg, GenericArg, HirId, ItemLocalMap, MissingLifetimeKind, ParamName, TraitCandidate,
};
Expand Down Expand Up @@ -1384,14 +1384,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
None
}
// Ignore `use` syntax since that is not valid in objects.
GenericBound::Use(_, span) => {
this.dcx()
.span_delayed_bug(*span, "use<> not allowed in dyn types");
None
}
}));
let lifetime_bound =
lifetime_bound.unwrap_or_else(|| this.elided_dyn_bound(t.span));
(bounds, lifetime_bound)
});
hir::TyKind::TraitObject(bounds, lifetime_bound, *kind)
}
TyKind::ImplTrait(def_node_id, bounds, precise_capturing) => {
TyKind::ImplTrait(def_node_id, bounds) => {
let span = t.span;
match itctx {
ImplTraitContext::OpaqueTy { origin, fn_kind } => self.lower_opaque_impl_trait(
Expand All @@ -1401,12 +1407,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
bounds,
fn_kind,
itctx,
precise_capturing.as_deref().map(|(args, span)| (args.as_slice(), *span)),
),
ImplTraitContext::Universal => {
if let Some(&(_, span)) = precise_capturing.as_deref() {
if let Some(span) = bounds.iter().find_map(|bound| match *bound {
ast::GenericBound::Use(_, span) => Some(span),
_ => None,
}) {
self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnApit { span });
};
}

let span = t.span;

// HACK: pprust breaks strings with newlines when the type
Expand Down Expand Up @@ -1517,7 +1526,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
bounds: &GenericBounds,
fn_kind: Option<FnDeclKind>,
itctx: ImplTraitContext,
precise_capturing_args: Option<(&[PreciseCapturingArg], Span)>,
) -> hir::TyKind<'hir> {
// Make sure we know that some funky desugaring has been going on here.
// This is a first: there is code in other places like for loop
Expand All @@ -1526,59 +1534,64 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// frequently opened issues show.
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);

let captured_lifetimes_to_duplicate =
if let Some((precise_capturing, _)) = precise_capturing_args {
// We'll actually validate these later on; all we need is the list of
// lifetimes to duplicate during this portion of lowering.
precise_capturing
.iter()
.filter_map(|arg| match arg {
PreciseCapturingArg::Lifetime(lt) => Some(*lt),
PreciseCapturingArg::Arg(..) => None,
})
// Add in all the lifetimes mentioned in the bounds. We will error
// them out later, but capturing them here is important to make sure
// they actually get resolved in resolve_bound_vars.
.chain(lifetime_collector::lifetimes_in_bounds(self.resolver, bounds))
.collect()
} else {
match origin {
hir::OpaqueTyOrigin::TyAlias { .. } => {
// type alias impl trait and associated type position impl trait were
// decided to capture all in-scope lifetimes, which we collect for
// all opaques during resolution.
let captured_lifetimes_to_duplicate = if let Some(args) =
// We only look for one `use<...>` syntax since we syntactially reject more than one.
bounds.iter().find_map(
|bound| match bound {
ast::GenericBound::Use(a, _) => Some(a),
_ => None,
},
) {
// We'll actually validate these later on; all we need is the list of
// lifetimes to duplicate during this portion of lowering.
args.iter()
.filter_map(|arg| match arg {
PreciseCapturingArg::Lifetime(lt) => Some(*lt),
PreciseCapturingArg::Arg(..) => None,
})
// Add in all the lifetimes mentioned in the bounds. We will error
// them out later, but capturing them here is important to make sure
// they actually get resolved in resolve_bound_vars.
.chain(lifetime_collector::lifetimes_in_bounds(self.resolver, bounds))
.collect()
} else {
match origin {
hir::OpaqueTyOrigin::TyAlias { .. } => {
// type alias impl trait and associated type position impl trait were
// decided to capture all in-scope lifetimes, which we collect for
// all opaques during resolution.
self.resolver
.take_extra_lifetime_params(opaque_ty_node_id)
.into_iter()
.map(|(ident, id, _)| Lifetime { id, ident })
.collect()
}
hir::OpaqueTyOrigin::FnReturn(..) => {
if matches!(
fn_kind.expect("expected RPITs to be lowered with a FnKind"),
FnDeclKind::Impl | FnDeclKind::Trait
) || self.tcx.features().lifetime_capture_rules_2024
|| span.at_least_rust_2024()
{
// return-position impl trait in trait was decided to capture all
// in-scope lifetimes, which we collect for all opaques during resolution.
self.resolver
.take_extra_lifetime_params(opaque_ty_node_id)
.into_iter()
.map(|(ident, id, _)| Lifetime { id, ident })
.collect()
}
hir::OpaqueTyOrigin::FnReturn(..) => {
if matches!(
fn_kind.expect("expected RPITs to be lowered with a FnKind"),
FnDeclKind::Impl | FnDeclKind::Trait
) || self.tcx.features().lifetime_capture_rules_2024
|| span.at_least_rust_2024()
{
// return-position impl trait in trait was decided to capture all
// in-scope lifetimes, which we collect for all opaques during resolution.
self.resolver
.take_extra_lifetime_params(opaque_ty_node_id)
.into_iter()
.map(|(ident, id, _)| Lifetime { id, ident })
.collect()
} else {
// in fn return position, like the `fn test<'a>() -> impl Debug + 'a`
// example, we only need to duplicate lifetimes that appear in the
// bounds, since those are the only ones that are captured by the opaque.
lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)
}
}
hir::OpaqueTyOrigin::AsyncFn(..) => {
unreachable!("should be using `lower_async_fn_ret_ty`")
} else {
// in fn return position, like the `fn test<'a>() -> impl Debug + 'a`
// example, we only need to duplicate lifetimes that appear in the
// bounds, since those are the only ones that are captured by the opaque.
lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)
}
}
};
hir::OpaqueTyOrigin::AsyncFn(..) => {
unreachable!("should be using `lower_async_fn_ret_ty`")
}
}
};
debug!(?captured_lifetimes_to_duplicate);

self.lower_opaque_inner(
Expand All @@ -1588,7 +1601,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
captured_lifetimes_to_duplicate,
span,
opaque_ty_span,
precise_capturing_args,
|this| this.lower_param_bounds(bounds, itctx),
)
}
Expand All @@ -1601,7 +1613,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
captured_lifetimes_to_duplicate: FxIndexSet<Lifetime>,
span: Span,
opaque_ty_span: Span,
precise_capturing_args: Option<(&[PreciseCapturingArg], Span)>,
lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>],
) -> hir::TyKind<'hir> {
let opaque_ty_def_id = self.create_def(
Expand Down Expand Up @@ -1688,18 +1699,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// Install the remapping from old to new (if any). This makes sure that
// any lifetimes that would have resolved to the def-id of captured
// lifetimes are remapped to the new *synthetic* lifetimes of the opaque.
let (bounds, precise_capturing_args) =
this.with_remapping(captured_to_synthesized_mapping, |this| {
(
lower_item_bounds(this),
precise_capturing_args.map(|(precise_capturing, span)| {
(
this.lower_precise_capturing_args(precise_capturing),
this.lower_span(span),
)
}),
)
});
let bounds = this
.with_remapping(captured_to_synthesized_mapping, |this| lower_item_bounds(this));

let generic_params =
this.arena.alloc_from_iter(synthesized_lifetime_definitions.iter().map(
Expand Down Expand Up @@ -1744,7 +1745,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
origin,
lifetime_mapping,
in_trait,
precise_capturing_args,
};

// Generate an `type Foo = impl Trait;` declaration.
Expand Down Expand Up @@ -1955,7 +1955,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
captured_lifetimes,
span,
opaque_ty_span,
None,
|this| {
let bound = this.lower_coroutine_fn_output_type_to_bound(
output,
Expand Down Expand Up @@ -2038,6 +2037,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
GenericBound::Outlives(lifetime) => {
hir::GenericBound::Outlives(self.lower_lifetime(lifetime))
}
GenericBound::Use(args, span) => hir::GenericBound::Use(
self.lower_precise_capturing_args(args),
self.lower_span(*span),
),
}
}

Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ ast_passes_pattern_in_fn_pointer = patterns aren't allowed in function pointer t
ast_passes_pattern_in_foreign = patterns aren't allowed in foreign function declarations
.label = pattern not allowed in foreign function
ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing syntax
.label = second `use<...>` here
ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc}
ast_passes_show_span = {$msg}
ast_passes_stability_outside_std = stability attributes may not be used outside of the standard library
Expand Down
Loading

0 comments on commit c1f62a7

Please sign in to comment.