From a076eae0d27915f352d92db1b63661e62750d7ae Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 3 Apr 2024 19:58:50 -0400 Subject: [PATCH 01/11] Parsing , pre-lowering support for precise captures --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_ast/src/mut_visit.rs | 5 +++- compiler/rustc_ast/src/visit.rs | 3 ++- compiler/rustc_ast_lowering/src/lib.rs | 3 ++- .../rustc_ast_passes/src/ast_validation.rs | 2 +- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_ast_pretty/src/pprust/state.rs | 3 ++- compiler/rustc_feature/src/unstable.rs | 2 ++ compiler/rustc_lint/src/unused.rs | 2 +- compiler/rustc_parse/src/parser/generics.rs | 2 +- compiler/rustc_parse/src/parser/item.rs | 2 +- compiler/rustc_parse/src/parser/ty.rs | 24 +++++++++++++++---- compiler/rustc_resolve/src/late.rs | 2 +- .../rustc_resolve/src/late/diagnostics.rs | 2 +- compiler/rustc_span/src/symbol.rs | 1 + 15 files changed, 41 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 5b708cf4e1a55..55a934c48a805 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2132,7 +2132,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), + ImplTrait(NodeId, GenericBounds, Option>), /// No-op; kept solely so that we can pretty-print faithfully. Paren(P), /// Unused for now. diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index da57def263df5..b8a5eeebf3a05 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -518,9 +518,12 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { TyKind::TraitObject(bounds, _syntax) => { visit_vec(bounds, |bound| vis.visit_param_bound(bound)) } - TyKind::ImplTrait(id, bounds) => { + TyKind::ImplTrait(id, bounds, precise_capturing) => { vis.visit_id(id); visit_vec(bounds, |bound| vis.visit_param_bound(bound)); + visit_opt(precise_capturing, |precise_capturing| { + vis.visit_generic_args(precise_capturing); + }); } TyKind::MacCall(mac) => vis.visit_mac_call(mac), TyKind::AnonStruct(id, fields) | TyKind::AnonUnion(id, fields) => { diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 9e9ae52962d87..9174002172703 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -457,8 +457,9 @@ 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) => { + TyKind::ImplTrait(_, bounds, precise_capturing) => { walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl); + visit_opt!(visitor, visit_generic_args, precise_capturing); } TyKind::Typeof(expression) => try_visit!(visitor.visit_anon_const(expression)), TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy | TyKind::Err(_) => {} diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 5005c22d4cc3a..a4470622972e7 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1398,7 +1398,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }); hir::TyKind::TraitObject(bounds, lifetime_bound, *kind) } - TyKind::ImplTrait(def_node_id, bounds) => { + TyKind::ImplTrait(def_node_id, bounds, precise_capturing) => { + assert!(precise_capturing.is_none(), "precise captures not supported yet!"); let span = t.span; match itctx { ImplTraitContext::OpaqueTy { origin, fn_kind } => self.lower_opaque_impl_trait( diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index cb4dcf3ae75f2..495e90e967b93 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -737,7 +737,7 @@ impl<'a> AstValidator<'a> { } } } - TyKind::ImplTrait(_, bounds) => { + TyKind::ImplTrait(_, bounds, _) => { if self.is_impl_trait_banned { self.dcx().emit_err(errors::ImplTraitPath { span: ty.span }); } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index d7cd3efe408ba..70a3ccb0f4402 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -569,6 +569,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); gate_all!(postfix_match, "postfix match is experimental"); gate_all!(mut_ref, "mutable by-reference bindings are experimental"); + gate_all!(precise_capturing, "precise captures on `impl Trait` are experimental"); if !visitor.features.never_patterns { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 51ccfe89fbdbc..6553ed72532cb 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1150,7 +1150,8 @@ impl<'a> State<'a> { } self.print_type_bounds(bounds); } - ast::TyKind::ImplTrait(_, bounds) => { + ast::TyKind::ImplTrait(_, bounds, _precise_capturing) => { + // TODO(precise_capturing): self.word_nbsp("impl"); self.print_type_bounds(bounds); } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index e6b19817de385..792cc1066f5ad 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -535,6 +535,8 @@ declare_features! ( (unstable, must_not_suspend, "1.57.0", Some(83310)), /// Allows `mut ref` and `mut ref mut` identifier patterns. (incomplete, mut_ref, "CURRENT_RUSTC_VERSION", Some(123076)), + /// Allows `use<'a, 'b, A, B>` in `impl use<...> Trait` for precise capture of generic args. + (incomplete, precise_capturing, "CURRENT_RUSTC_VERSION", Some(123432)), /// Allows using `#[naked]` on functions. (unstable, naked_functions, "1.9.0", Some(90957)), /// Allows specifying the as-needed link modifier diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 503caa35358ab..3fe1f21d56a22 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1235,7 +1235,7 @@ impl EarlyLintPass for UnusedParens { ast::TyKind::TraitObject(..) => {} ast::TyKind::BareFn(b) if self.with_self_ty_parens && b.generic_params.len() > 0 => {} - ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {} + ast::TyKind::ImplTrait(_, bounds, _) if bounds.len() > 1 => {} _ => { let spans = if !ty.span.from_expansion() { r.span diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index fde16ac957dfe..93a15c938ecf1 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -62,7 +62,7 @@ impl<'a> Parser<'a> { let snapshot = self.create_snapshot_for_diagnostic(); match self.parse_ty() { Ok(p) => { - if let TyKind::ImplTrait(_, bounds) = &p.kind { + if let TyKind::ImplTrait(_, bounds, None) = &p.kind { let span = impl_span.to(self.token.span.shrink_to_lo()); let mut err = self.dcx().struct_span_err( span, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index d54eb8dc4c9eb..fd72dca6e21ea 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -625,7 +625,7 @@ impl<'a> Parser<'a> { // This notably includes paths passed through `ty` macro fragments (#46438). TyKind::Path(None, path) => path, other => { - if let TyKind::ImplTrait(_, bounds) = other + if let TyKind::ImplTrait(_, bounds, None) = other && let [bound] = bounds.as_slice() { // Suggest removing extra `impl` keyword: diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 1cea32cb90fea..5d6c4f39ae111 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -316,7 +316,7 @@ impl<'a> Parser<'a> { TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn) } (TyKind::TraitObject(bounds, _), kw::Impl) => { - TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds) + TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, None) } _ => return Err(err), }; @@ -655,7 +655,6 @@ impl<'a> Parser<'a> { /// Parses an `impl B0 + ... + Bn` type. fn parse_impl_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> { - // Always parse bounds greedily for better error recovery. if self.token.is_lifetime() { self.look_ahead(1, |t| { if let token::Ident(sym, _) = t.kind { @@ -669,9 +668,26 @@ impl<'a> Parser<'a> { } }) } + + // parse precise captures, if any. + let precise_capturing = if self.eat_keyword(kw::Use) { + self.expect_lt()?; + let use_span = self.prev_token.span; + self.psess.gated_spans.gate(sym::precise_capturing, use_span); + let lo = self.token.span; + let args = self.parse_angle_args(None)?; + self.expect_gt()?; + Some(ast::AngleBracketedArgs { args, span: lo.to(self.prev_token.span) }.into()) + } else { + None + }; + + // Always parse bounds greedily for better error recovery. let bounds = self.parse_generic_bounds()?; + *impl_dyn_multi = bounds.len() > 1 || self.prev_token.kind == TokenKind::BinOp(token::Plus); - Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)) + + Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, precise_capturing)) } /// Is a `dyn B0 + ... + Bn` type allowed here? @@ -957,7 +973,7 @@ impl<'a> Parser<'a> { Applicability::MaybeIncorrect, ) } - TyKind::ImplTrait(_, bounds) + TyKind::ImplTrait(_, bounds, None) if let [GenericBound::Trait(tr, ..), ..] = bounds.as_slice() => { ( diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 33c9c7fcc6208..9e881532311a6 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -793,7 +793,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, self.r.record_partial_res(ty.id, PartialRes::new(res)); visit::walk_ty(self, ty) } - TyKind::ImplTrait(node_id, _) => { + TyKind::ImplTrait(node_id, _, _) => { let candidates = self.lifetime_elision_candidates.take(); visit::walk_ty(self, ty); self.record_lifetime_params_for_impl_trait(*node_id); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index bb4294fbcfb2b..d79c638fa0789 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3121,7 +3121,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { .inputs .iter() .filter_map(|param| match ¶m.ty.kind { - TyKind::ImplTrait(_, bounds) => Some(bounds), + TyKind::ImplTrait(_, bounds, _) => Some(bounds), _ => None, }) .flat_map(|bounds| bounds.into_iter()) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index bfd0f77c237b2..19c3fc5894308 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1375,6 +1375,7 @@ symbols! { powif32, powif64, pre_dash_lto: "pre-lto", + precise_capturing, precise_pointer_size_matching, pref_align_of, prefetch_read_data, From 647b672f16f6db2f156b69668ca963ec28016464 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 3 Apr 2024 20:54:23 -0400 Subject: [PATCH 02/11] Begin AST lowering for precise captures --- compiler/rustc_ast_lowering/src/lib.rs | 79 ++++++++++++------- .../precise-capturing/higher-ranked.rs | 18 +++++ .../precise-capturing/higher-ranked.stderr | 11 +++ .../impl-trait/precise-capturing/outlives.rs | 16 ++++ .../precise-capturing/outlives.stderr | 11 +++ 5 files changed, 106 insertions(+), 29 deletions(-) create mode 100644 tests/ui/impl-trait/precise-capturing/higher-ranked.rs create mode 100644 tests/ui/impl-trait/precise-capturing/higher-ranked.stderr create mode 100644 tests/ui/impl-trait/precise-capturing/outlives.rs create mode 100644 tests/ui/impl-trait/precise-capturing/outlives.stderr diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a4470622972e7..32b2f7c86cadd 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1399,7 +1399,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::TyKind::TraitObject(bounds, lifetime_bound, *kind) } TyKind::ImplTrait(def_node_id, bounds, precise_capturing) => { - assert!(precise_capturing.is_none(), "precise captures not supported yet!"); let span = t.span; match itctx { ImplTraitContext::OpaqueTy { origin, fn_kind } => self.lower_opaque_impl_trait( @@ -1409,8 +1408,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { bounds, fn_kind, itctx, + precise_capturing.as_deref(), ), ImplTraitContext::Universal => { + assert!( + precise_capturing.is_none(), + "TODO: precise captures not supported on universals!" + ); let span = t.span; // HACK: pprust breaks strings with newlines when the type @@ -1521,6 +1525,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { bounds: &GenericBounds, fn_kind: Option, itctx: ImplTraitContext, + precise_capturing: Option<&ast::GenericArgs>, ) -> 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 @@ -1529,40 +1534,56 @@ 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 = 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. + let captured_lifetimes_to_duplicate = if let Some(precise_capturing) = precise_capturing { + let ast::GenericArgs::AngleBracketed(precise_capturing) = precise_capturing else { + panic!("we only parse angle-bracketed 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 + .args + .iter() + .filter_map(|arg| match arg { + ast::AngleBracketedArg::Arg(ast::GenericArg::Lifetime(lt)) => Some(*lt), + _ => None, + }) + .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() - } 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`") + 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`") + } } }; debug!(?captured_lifetimes_to_duplicate); diff --git a/tests/ui/impl-trait/precise-capturing/higher-ranked.rs b/tests/ui/impl-trait/precise-capturing/higher-ranked.rs new file mode 100644 index 0000000000000..c9faaaed96892 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/higher-ranked.rs @@ -0,0 +1,18 @@ +//@ check-pass + +// Show how precise captures allow us to skip capturing a higher-ranked lifetime + +#![feature(lifetime_capture_rules_2024, precise_capturing)] +//~^ WARN the feature `precise_capturing` is incomplete + +trait Trait<'a> { + type Item; +} + +impl Trait<'_> for () { + type Item = Vec<()>; +} + +fn hello() -> impl for<'a> Trait<'a, Item = impl use<> IntoIterator> {} + +fn main() {} \ No newline at end of file diff --git a/tests/ui/impl-trait/precise-capturing/higher-ranked.stderr b/tests/ui/impl-trait/precise-capturing/higher-ranked.stderr new file mode 100644 index 0000000000000..e48d6d42af079 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/higher-ranked.stderr @@ -0,0 +1,11 @@ +warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/higher-ranked.rs:5:41 + | +LL | #![feature(lifetime_capture_rules_2024, precise_capturing)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #123432 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/impl-trait/precise-capturing/outlives.rs b/tests/ui/impl-trait/precise-capturing/outlives.rs new file mode 100644 index 0000000000000..71e6333934e7d --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/outlives.rs @@ -0,0 +1,16 @@ +//@ check-pass + +// Show that precise captures allow us to skip a lifetime param for outlives + +#![feature(lifetime_capture_rules_2024, precise_capturing)] +//~^ WARN the feature `precise_capturing` is incomplete + +fn hello<'a: 'a, 'b: 'b>() -> impl use<'a> Sized { } + +fn outlives<'a, T: 'a>(_: T) {} + +fn test<'a, 'b>() { + outlives::<'a, _>(hello::<'a, 'b>()); +} + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/outlives.stderr b/tests/ui/impl-trait/precise-capturing/outlives.stderr new file mode 100644 index 0000000000000..405c09cccd947 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/outlives.stderr @@ -0,0 +1,11 @@ +warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/outlives.rs:5:41 + | +LL | #![feature(lifetime_capture_rules_2024, precise_capturing)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #123432 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + From fc9e344874ce718c951016ba29f7fcabb36f26c3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 3 Apr 2024 21:47:02 -0400 Subject: [PATCH 03/11] Use dedicated PreciseCapturingArg for representing what goes in use<> --- compiler/rustc_ast/src/ast.rs | 10 ++- compiler/rustc_ast/src/mut_visit.rs | 31 ++++++++- compiler/rustc_ast/src/visit.rs | 32 ++++++++- compiler/rustc_ast_lowering/src/lib.rs | 89 +++++++++++++------------- compiler/rustc_parse/src/parser/ty.rs | 30 +++++++-- compiler/rustc_resolve/src/late.rs | 10 +++ 6 files changed, 146 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 55a934c48a805..623a6437c532a 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2132,7 +2132,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>), + ImplTrait(NodeId, GenericBounds, Option>), /// No-op; kept solely so that we can pretty-print faithfully. Paren(P), /// Unused for now. @@ -2188,6 +2188,14 @@ pub enum TraitObjectSyntax { None, } +#[derive(Clone, PartialEq, Encodable, Decodable, Debug)] +pub enum PreciseCapturingArg { + /// Lifetime parameter + Lifetime(Lifetime), + /// Type or const parameter + Arg(Ident, NodeId), +} + /// Inline assembly operand explicit register or register class. /// /// E.g., `"eax"` as in `asm!("mov eax, 2", out("eax") result)`. diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index b8a5eeebf3a05..cb6258739374c 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -259,6 +259,14 @@ pub trait MutVisitor: Sized { noop_visit_param_bound(tpb, self); } + fn visit_precise_capturing_args(&mut self, args: &mut ThinVec) { + noop_visit_precise_capturing_args(args, self); + } + + fn visit_precise_capturing_arg(&mut self, arg: &mut PreciseCapturingArg) { + noop_visit_precise_capturing_arg(arg, self); + } + fn visit_mt(&mut self, mt: &mut MutTy) { noop_visit_mt(mt, self); } @@ -522,7 +530,7 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { vis.visit_id(id); visit_vec(bounds, |bound| vis.visit_param_bound(bound)); visit_opt(precise_capturing, |precise_capturing| { - vis.visit_generic_args(precise_capturing); + vis.visit_precise_capturing_args(precise_capturing); }); } TyKind::MacCall(mac) => vis.visit_mac_call(mac), @@ -917,6 +925,27 @@ pub fn noop_visit_param_bound(pb: &mut GenericBound, vis: &mut T) } } +pub fn noop_visit_precise_capturing_args( + args: &mut ThinVec, + vis: &mut T, +) { + for arg in args { + vis.visit_precise_capturing_arg(arg); + } +} + +pub fn noop_visit_precise_capturing_arg(arg: &mut PreciseCapturingArg, vis: &mut T) { + match arg { + PreciseCapturingArg::Lifetime(lt) => { + vis.visit_lifetime(lt); + } + PreciseCapturingArg::Arg(ident, id) => { + vis.visit_ident(ident); + vis.visit_id(id); + } + } +} + pub fn noop_flat_map_generic_param( mut param: GenericParam, vis: &mut T, diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 9174002172703..b7e4d31edd70b 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -20,6 +20,7 @@ use rustc_span::Span; pub use rustc_ast_ir::visit::VisitorResult; pub use rustc_ast_ir::{try_visit, visit_opt, walk_list, walk_visitable_list}; +use thin_vec::ThinVec; #[derive(Copy, Clone, Debug, PartialEq)] pub enum AssocCtxt { @@ -184,6 +185,12 @@ pub trait Visitor<'ast>: Sized { fn visit_param_bound(&mut self, bounds: &'ast GenericBound, _ctxt: BoundKind) -> Self::Result { walk_param_bound(self, bounds) } + fn visit_precise_capturing_args(&mut self, args: &'ast ThinVec) { + walk_precise_capturing_args(self, args); + } + fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) { + walk_precise_capturing_arg(self, arg); + } fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef) -> Self::Result { walk_poly_trait_ref(self, t) } @@ -459,7 +466,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result { } TyKind::ImplTrait(_, bounds, precise_capturing) => { walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl); - visit_opt!(visitor, visit_generic_args, precise_capturing); + visit_opt!(visitor, visit_precise_capturing_args, precise_capturing); } TyKind::Typeof(expression) => try_visit!(visitor.visit_anon_const(expression)), TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy | TyKind::Err(_) => {} @@ -638,6 +645,29 @@ pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericB } } +pub fn walk_precise_capturing_args<'a, V: Visitor<'a>>( + visitor: &mut V, + args: &'a ThinVec, +) { + for arg in args { + visitor.visit_precise_capturing_arg(arg); + } +} + +pub fn walk_precise_capturing_arg<'a, V: Visitor<'a>>( + visitor: &mut V, + arg: &'a PreciseCapturingArg, +) { + match arg { + PreciseCapturingArg::Lifetime(lt) => { + visitor.visit_lifetime(lt, LifetimeCtxt::GenericArg); + } + PreciseCapturingArg::Arg(ident, _) => { + visitor.visit_ident(*ident); + } + } +} + pub fn walk_generic_param<'a, V: Visitor<'a>>( visitor: &mut V, param: &'a GenericParam, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 32b2f7c86cadd..731dda39e9a8a 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1525,7 +1525,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { bounds: &GenericBounds, fn_kind: Option, itctx: ImplTraitContext, - precise_capturing: Option<&ast::GenericArgs>, + precise_capturing: Option<&[ast::PreciseCapturingArg]>, ) -> 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 @@ -1534,58 +1534,55 @@ 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 { - let ast::GenericArgs::AngleBracketed(precise_capturing) = precise_capturing else { - panic!("we only parse angle-bracketed 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 - .args - .iter() - .filter_map(|arg| match arg { - ast::AngleBracketedArg::Arg(ast::GenericArg::Lifetime(lt)) => Some(*lt), - _ => None, - }) - .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. + 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 { + ast::PreciseCapturingArg::Lifetime(lt) => Some(*lt), + ast::PreciseCapturingArg::Arg(..) => None, + }) + .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() - } 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::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`") } } - hir::OpaqueTyOrigin::AsyncFn(..) => { - unreachable!("should be using `lower_async_fn_ret_ty`") - } - } - }; + }; debug!(?captured_lifetimes_to_duplicate); self.lower_opaque_inner( diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 5d6c4f39ae111..62f0602e1fa28 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -1,4 +1,4 @@ -use super::{Parser, PathStyle, TokenType, Trailing}; +use super::{Parser, PathStyle, SeqSep, TokenType, Trailing}; use crate::errors::{ self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType, @@ -14,7 +14,7 @@ use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef, - TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind, DUMMY_NODE_ID, + PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind, DUMMY_NODE_ID, }; use rustc_errors::{Applicability, PResult}; use rustc_span::symbol::{kw, sym, Ident}; @@ -671,13 +671,10 @@ impl<'a> Parser<'a> { // parse precise captures, if any. let precise_capturing = if self.eat_keyword(kw::Use) { - self.expect_lt()?; let use_span = self.prev_token.span; self.psess.gated_spans.gate(sym::precise_capturing, use_span); - let lo = self.token.span; - let args = self.parse_angle_args(None)?; - self.expect_gt()?; - Some(ast::AngleBracketedArgs { args, span: lo.to(self.prev_token.span) }.into()) + let args = self.parse_precise_capturing_args()?; + Some(args) } else { None }; @@ -690,6 +687,25 @@ impl<'a> Parser<'a> { Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, precise_capturing)) } + fn parse_precise_capturing_args(&mut self) -> PResult<'a, ThinVec> { + Ok(self + .parse_unspanned_seq( + &TokenKind::Lt, + &TokenKind::Gt, + SeqSep::trailing_allowed(token::Comma), + |self_| { + if self_.check_ident() { + Ok(PreciseCapturingArg::Arg(self_.parse_ident().unwrap(), DUMMY_NODE_ID)) + } else if self_.check_lifetime() { + Ok(PreciseCapturingArg::Lifetime(self_.expect_lifetime())) + } else { + self_.unexpected_any() + } + }, + )? + .0) + } + /// Is a `dyn B0 + ... + Bn` type allowed here? fn is_explicit_dyn_type(&mut self) -> bool { self.check_keyword(kw::Dyn) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 9e881532311a6..e4bcdc96f44c8 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1047,10 +1047,20 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, }); self.diag_metadata.current_function = previous_value; } + fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, use_ctxt: visit::LifetimeCtxt) { self.resolve_lifetime(lifetime, use_ctxt) } + fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) { + match arg { + PreciseCapturingArg::Lifetime(_) => visit::walk_precise_capturing_arg(self, arg), + PreciseCapturingArg::Arg(ident, _) => { + todo!("cannot resolve args yet: {ident}"); + } + } + } + fn visit_generics(&mut self, generics: &'ast Generics) { self.visit_generic_params(&generics.params, self.diag_metadata.current_self_item.is_some()); for p in &generics.where_clause.predicates { From c897092654bae65d5695ef3b1a5850c15513ed5d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 3 Apr 2024 22:34:53 -0400 Subject: [PATCH 04/11] Implement resolution, parse use --- compiler/rustc_parse/src/parser/ty.rs | 8 +++++++- compiler/rustc_resolve/src/late.rs | 24 ++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 62f0602e1fa28..a24ecf938ba4b 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -694,7 +694,13 @@ impl<'a> Parser<'a> { &TokenKind::Gt, SeqSep::trailing_allowed(token::Comma), |self_| { - if self_.check_ident() { + if self_.check_keyword(kw::SelfUpper) { + self_.bump(); + Ok(PreciseCapturingArg::Arg( + self_.prev_token.ident().unwrap().0, + DUMMY_NODE_ID, + )) + } else if self_.check_ident() { Ok(PreciseCapturingArg::Arg(self_.parse_ident().unwrap(), DUMMY_NODE_ID)) } else if self_.check_lifetime() { Ok(PreciseCapturingArg::Lifetime(self_.expect_lifetime())) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index e4bcdc96f44c8..c7e12284b50f5 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1055,8 +1055,28 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) { match arg { PreciseCapturingArg::Lifetime(_) => visit::walk_precise_capturing_arg(self, arg), - PreciseCapturingArg::Arg(ident, _) => { - todo!("cannot resolve args yet: {ident}"); + PreciseCapturingArg::Arg(ident, node_id) => { + let ident = ident.normalize_to_macros_2_0(); + 'found: { + for (rib_t, rib_v) in + std::iter::zip(&self.ribs.type_ns, &self.ribs.value_ns).rev() + { + if let Some(res) = rib_t.bindings.get(&ident).or(rib_v.bindings.get(&ident)) + { + self.r.record_partial_res(*node_id, PartialRes::new(*res)); + break 'found; + } + } + self.report_error( + ident.span, + ResolutionError::FailedToResolve { + segment: Some(ident.name), + label: "could not find type or const parameter".to_string(), + suggestion: None, + module: None, + }, + ); + } } } } From 41cf87b71b792b40155cd79f96234a89ae7dc27f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 4 Apr 2024 10:48:47 -0400 Subject: [PATCH 05/11] Lower and resolve precise captures in HIR --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_ast/src/mut_visit.rs | 17 ++----- compiler/rustc_ast/src/visit.rs | 19 ++------ compiler/rustc_ast_lowering/src/lib.rs | 47 ++++++++++++++++--- .../src/lifetime_collector.rs | 15 +++--- compiler/rustc_hir/src/hir.rs | 9 ++++ compiler/rustc_hir/src/intravisit.rs | 20 +++++++- .../src/collect/resolve_bound_vars.rs | 25 ++++++++++ 8 files changed, 108 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 623a6437c532a..ec445bc5ed300 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -63,7 +63,7 @@ impl fmt::Debug for Label { /// A "Lifetime" is an annotation of the scope in which variable /// can be used, e.g. `'a` in `&'a i32`. -#[derive(Clone, Encodable, Decodable, Copy, PartialEq, Eq)] +#[derive(Clone, Encodable, Decodable, Copy, PartialEq, Eq, Hash)] pub struct Lifetime { pub id: NodeId, pub ident: Ident, diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index cb6258739374c..5c9e1313b5b82 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -259,10 +259,6 @@ pub trait MutVisitor: Sized { noop_visit_param_bound(tpb, self); } - fn visit_precise_capturing_args(&mut self, args: &mut ThinVec) { - noop_visit_precise_capturing_args(args, self); - } - fn visit_precise_capturing_arg(&mut self, arg: &mut PreciseCapturingArg) { noop_visit_precise_capturing_arg(arg, self); } @@ -530,7 +526,9 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { vis.visit_id(id); visit_vec(bounds, |bound| vis.visit_param_bound(bound)); visit_opt(precise_capturing, |precise_capturing| { - vis.visit_precise_capturing_args(precise_capturing); + for arg in precise_capturing { + vis.visit_precise_capturing_arg(arg); + } }); } TyKind::MacCall(mac) => vis.visit_mac_call(mac), @@ -925,15 +923,6 @@ pub fn noop_visit_param_bound(pb: &mut GenericBound, vis: &mut T) } } -pub fn noop_visit_precise_capturing_args( - args: &mut ThinVec, - vis: &mut T, -) { - for arg in args { - vis.visit_precise_capturing_arg(arg); - } -} - pub fn noop_visit_precise_capturing_arg(arg: &mut PreciseCapturingArg, vis: &mut T) { match arg { PreciseCapturingArg::Lifetime(lt) => { diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index b7e4d31edd70b..055afc422edb4 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -20,7 +20,6 @@ use rustc_span::Span; pub use rustc_ast_ir::visit::VisitorResult; pub use rustc_ast_ir::{try_visit, visit_opt, walk_list, walk_visitable_list}; -use thin_vec::ThinVec; #[derive(Copy, Clone, Debug, PartialEq)] pub enum AssocCtxt { @@ -185,9 +184,6 @@ pub trait Visitor<'ast>: Sized { fn visit_param_bound(&mut self, bounds: &'ast GenericBound, _ctxt: BoundKind) -> Self::Result { walk_param_bound(self, bounds) } - fn visit_precise_capturing_args(&mut self, args: &'ast ThinVec) { - walk_precise_capturing_args(self, args); - } fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) { walk_precise_capturing_arg(self, arg); } @@ -466,7 +462,11 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result { } TyKind::ImplTrait(_, bounds, precise_capturing) => { walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl); - visit_opt!(visitor, visit_precise_capturing_args, precise_capturing); + if let Some(precise_capturing) = precise_capturing { + 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(_) => {} @@ -645,15 +645,6 @@ pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericB } } -pub fn walk_precise_capturing_args<'a, V: Visitor<'a>>( - visitor: &mut V, - args: &'a ThinVec, -) { - for arg in args { - visitor.visit_precise_capturing_arg(arg); - } -} - pub fn walk_precise_capturing_arg<'a, V: Visitor<'a>>( visitor: &mut V, arg: &'a PreciseCapturingArg, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 731dda39e9a8a..2a220dd4df632 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -48,6 +48,7 @@ use rustc_ast::{self as ast, *}; use rustc_ast_pretty::pprust; use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; @@ -1525,7 +1526,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { bounds: &GenericBounds, fn_kind: Option, itctx: ImplTraitContext, - precise_capturing: Option<&[ast::PreciseCapturingArg]>, + precise_capturing_args: Option<&[PreciseCapturingArg]>, ) -> 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 @@ -1541,9 +1542,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { precise_capturing .iter() .filter_map(|arg| match arg { - ast::PreciseCapturingArg::Lifetime(lt) => Some(*lt), - ast::PreciseCapturingArg::Arg(..) => None, + 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 { @@ -1592,6 +1597,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { captured_lifetimes_to_duplicate, span, opaque_ty_span, + precise_capturing_args, |this| this.lower_param_bounds(bounds, itctx), ) } @@ -1601,9 +1607,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { opaque_ty_node_id: NodeId, origin: hir::OpaqueTyOrigin, in_trait: bool, - captured_lifetimes_to_duplicate: Vec, + captured_lifetimes_to_duplicate: FxIndexSet, span: Span, opaque_ty_span: Span, + precise_capturing_args: Option<&[PreciseCapturingArg]>, lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>], ) -> hir::TyKind<'hir> { let opaque_ty_def_id = self.create_def( @@ -1690,8 +1697,15 @@ 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 = this - .with_remapping(captured_to_synthesized_mapping, |this| lower_item_bounds(this)); + let (bounds, precise_capturing_args) = + this.with_remapping(captured_to_synthesized_mapping, |this| { + ( + lower_item_bounds(this), + precise_capturing_args.map(|precise_capturing| { + this.lower_precise_capturing_args(precise_capturing) + }), + ) + }); let generic_params = this.arena.alloc_from_iter(synthesized_lifetime_definitions.iter().map( @@ -1736,6 +1750,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { origin, lifetime_mapping, in_trait, + precise_capturing_args, }; // Generate an `type Foo = impl Trait;` declaration. @@ -1768,6 +1783,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) } + fn lower_precise_capturing_args( + &mut self, + precise_capturing_args: &[PreciseCapturingArg], + ) -> &'hir [hir::PreciseCapturingArg<'hir>] { + self.arena.alloc_from_iter(precise_capturing_args.iter().map(|arg| match arg { + PreciseCapturingArg::Lifetime(lt) => { + hir::PreciseCapturingArg::Lifetime(self.lower_lifetime(lt)) + } + PreciseCapturingArg::Arg(_, node_id) => { + let res = self.resolver.get_partial_res(*node_id).map_or(Res::Err, |partial_res| { + partial_res.full_res().expect("no partial res expected for precise capture arg") + }); + hir::PreciseCapturingArg::Param(self.lower_res(res), self.lower_node_id(*node_id)) + } + })) + } + fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Ident] { self.arena.alloc_from_iter(decl.inputs.iter().map(|param| match param.pat.kind { PatKind::Ident(_, ident, _) => self.lower_ident(ident), @@ -1908,7 +1940,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, allowed_features); - let captured_lifetimes: Vec<_> = self + let captured_lifetimes = self .resolver .take_extra_lifetime_params(opaque_ty_node_id) .into_iter() @@ -1922,6 +1954,7 @@ 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, diff --git a/compiler/rustc_ast_lowering/src/lifetime_collector.rs b/compiler/rustc_ast_lowering/src/lifetime_collector.rs index 4b1c057cdbf1d..5456abd489beb 100644 --- a/compiler/rustc_ast_lowering/src/lifetime_collector.rs +++ b/compiler/rustc_ast_lowering/src/lifetime_collector.rs @@ -1,6 +1,7 @@ use super::ResolverAstLoweringExt; use rustc_ast::visit::{self, BoundKind, LifetimeCtxt, Visitor}; use rustc_ast::{GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind}; +use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def::{DefKind, LifetimeRes, Res}; use rustc_middle::span_bug; use rustc_middle::ty::ResolverAstLowering; @@ -10,27 +11,23 @@ use rustc_span::Span; struct LifetimeCollectVisitor<'ast> { resolver: &'ast ResolverAstLowering, current_binders: Vec, - collected_lifetimes: Vec, + collected_lifetimes: FxIndexSet, } impl<'ast> LifetimeCollectVisitor<'ast> { fn new(resolver: &'ast ResolverAstLowering) -> Self { - Self { resolver, current_binders: Vec::new(), collected_lifetimes: Vec::new() } + Self { resolver, current_binders: Vec::new(), collected_lifetimes: FxIndexSet::default() } } fn record_lifetime_use(&mut self, lifetime: Lifetime) { match self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error) { LifetimeRes::Param { binder, .. } | LifetimeRes::Fresh { binder, .. } => { if !self.current_binders.contains(&binder) { - if !self.collected_lifetimes.contains(&lifetime) { - self.collected_lifetimes.push(lifetime); - } + self.collected_lifetimes.insert(lifetime); } } LifetimeRes::Static | LifetimeRes::Error => { - if !self.collected_lifetimes.contains(&lifetime) { - self.collected_lifetimes.push(lifetime); - } + self.collected_lifetimes.insert(lifetime); } LifetimeRes::Infer => {} res => { @@ -111,7 +108,7 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> { pub(crate) fn lifetimes_in_bounds( resolver: &ResolverAstLowering, bounds: &GenericBounds, -) -> Vec { +) -> FxIndexSet { let mut visitor = LifetimeCollectVisitor::new(resolver); for bound in bounds { visitor.visit_param_bound(bound, BoundKind::Bound); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index c6e3ad31f013f..dc76deb18152e 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2557,6 +2557,15 @@ pub struct OpaqueTy<'hir> { /// originating from a trait method. This makes it so that the opaque is /// lowered as an associated type. pub in_trait: bool, + /// List of arguments captured via `impl use<'a, P, ...> Trait` syntax. + pub precise_capturing_args: Option<&'hir [PreciseCapturingArg<'hir>]>, +} + +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub enum PreciseCapturingArg<'hir> { + Lifetime(&'hir Lifetime), + /// Non-lifetime argument (type or const) + Param(Res, HirId), } /// From whence the opaque type came. diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 5da9d4444da39..d3ede3a904f9c 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -413,6 +413,9 @@ pub trait Visitor<'v>: Sized { fn visit_param_bound(&mut self, bounds: &'v GenericBound<'v>) -> Self::Result { walk_param_bound(self, bounds) } + fn visit_precise_capturing_arg(&mut self, arg: &'v PreciseCapturingArg<'v>) -> Self::Result { + walk_precise_capturing_arg(self, arg) + } fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef<'v>) -> Self::Result { walk_poly_trait_ref(self, t) } @@ -526,10 +529,15 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: try_visit!(visitor.visit_ty(ty)); try_visit!(visitor.visit_generics(generics)); } - ItemKind::OpaqueTy(&OpaqueTy { generics, bounds, .. }) => { + ItemKind::OpaqueTy(&OpaqueTy { generics, bounds, precise_capturing_args, .. }) => { try_visit!(visitor.visit_id(item.hir_id())); try_visit!(walk_generics(visitor, generics)); walk_list!(visitor, visit_param_bound, bounds); + if let Some(precise_capturing_args) = precise_capturing_args { + for arg in precise_capturing_args { + try_visit!(visitor.visit_precise_capturing_arg(arg)); + } + } } ItemKind::Enum(ref enum_definition, ref generics) => { try_visit!(visitor.visit_generics(generics)); @@ -1137,6 +1145,16 @@ pub fn walk_param_bound<'v, V: Visitor<'v>>( } } +pub fn walk_precise_capturing_arg<'v, V: Visitor<'v>>( + visitor: &mut V, + arg: &'v PreciseCapturingArg<'v>, +) -> V::Result { + match *arg { + PreciseCapturingArg::Lifetime(lt) => visitor.visit_lifetime(lt), + PreciseCapturingArg::Param(_, hir_id) => visitor.visit_id(hir_id), + } +} + pub fn walk_poly_trait_ref<'v, V: Visitor<'v>>( visitor: &mut V, trait_ref: &'v PolyTraitRef<'v>, diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 3d16f1420d997..8e29269e8fd1f 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -557,6 +557,31 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } } + fn visit_precise_capturing_arg( + &mut self, + arg: &'tcx hir::PreciseCapturingArg<'tcx>, + ) -> Self::Result { + match *arg { + hir::PreciseCapturingArg::Lifetime(lt) => match lt.res { + LifetimeName::Param(def_id) => { + self.resolve_lifetime_ref(def_id, lt); + } + LifetimeName::Error => {} + LifetimeName::ImplicitObjectLifetimeDefault + | LifetimeName::Infer + | LifetimeName::Static => todo!("TODO: Error on invalid lifetime"), + }, + hir::PreciseCapturingArg::Param(res, hir_id) => match res { + Res::Def(DefKind::TyParam | DefKind::ConstParam, def_id) + | Res::SelfTyParam { trait_: def_id } => { + self.resolve_type_ref(def_id.expect_local(), hir_id); + } + Res::Err => {} + _ => todo!("TODO: Error on invalid param res"), + }, + } + } + fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) { match item.kind { hir::ForeignItemKind::Fn(_, _, generics) => { From 42ba57c0133306ff510bf641e9d90b33419d7ac7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 4 Apr 2024 12:54:56 -0400 Subject: [PATCH 06/11] Validation and other things --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_ast/src/mut_visit.rs | 4 +- compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_ast_lowering/messages.ftl | 2 + compiler/rustc_ast_lowering/src/errors.rs | 7 ++ compiler/rustc_ast_lowering/src/lib.rs | 9 +- compiler/rustc_ast_pretty/src/pprust/state.rs | 12 ++- compiler/rustc_feature/src/unstable.rs | 4 +- compiler/rustc_hir_analysis/messages.ftl | 10 ++ .../rustc_hir_analysis/src/check/check.rs | 95 ++++++++++++++++++- .../src/collect/resolve_bound_vars.rs | 13 ++- compiler/rustc_hir_analysis/src/errors.rs | 3 + .../src/errors/precise_captures.rs | 33 +++++++ compiler/rustc_parse/src/parser/ty.rs | 2 +- compiler/rustc_resolve/src/late.rs | 35 +++++++ .../precise-capturing/bad-lifetimes.rs | 14 +++ .../precise-capturing/bad-lifetimes.stderr | 45 +++++++++ .../precise-capturing/bad-params.rs | 16 ++++ .../precise-capturing/bad-params.stderr | 30 ++++++ .../forgot-to-capture-const.rs | 7 ++ .../forgot-to-capture-const.stderr | 19 ++++ .../forgot-to-capture-lifetime.rs | 7 ++ .../forgot-to-capture-lifetime.stderr | 19 ++++ .../forgot-to-capture-type.rs | 10 ++ .../forgot-to-capture-type.stderr | 35 +++++++ .../precise-capturing/higher-ranked.rs | 2 +- 26 files changed, 418 insertions(+), 19 deletions(-) create mode 100644 compiler/rustc_hir_analysis/src/errors/precise_captures.rs create mode 100644 tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs create mode 100644 tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr create mode 100644 tests/ui/impl-trait/precise-capturing/bad-params.rs create mode 100644 tests/ui/impl-trait/precise-capturing/bad-params.stderr create mode 100644 tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs create mode 100644 tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr create mode 100644 tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs create mode 100644 tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr create mode 100644 tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs create mode 100644 tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index ec445bc5ed300..ace20a005ee40 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2132,7 +2132,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>), + ImplTrait(NodeId, GenericBounds, Option, Span)>>), /// No-op; kept solely so that we can pretty-print faithfully. Paren(P), /// Unused for now. diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 5c9e1313b5b82..204f33cab345f 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -525,11 +525,11 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { TyKind::ImplTrait(id, bounds, precise_capturing) => { vis.visit_id(id); visit_vec(bounds, |bound| vis.visit_param_bound(bound)); - visit_opt(precise_capturing, |precise_capturing| { + 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) => { diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 055afc422edb4..a9c29e077d13d 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -462,7 +462,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result { } TyKind::ImplTrait(_, bounds, precise_capturing) => { walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl); - if let Some(precise_capturing) = precise_capturing { + if let Some((precise_capturing, _span)) = precise_capturing.as_deref() { for arg in precise_capturing { try_visit!(visitor.visit_precise_capturing_arg(arg)); } diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index d91d65497e1c1..a23e714ef017e 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -127,6 +127,8 @@ 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_previously_used_here = previously used here ast_lowering_register1 = register `{$reg1_name}` diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 6fd980ed3ca96..ca0821e2c9edb 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -414,3 +414,10 @@ pub(crate) struct AsyncBoundOnlyForFnTraits { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(ast_lowering_no_precise_captures_on_apit)] +pub(crate) struct NoPreciseCapturesOnApit { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 2a220dd4df632..b586d2a1bf564 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1409,13 +1409,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { bounds, fn_kind, itctx, - precise_capturing.as_deref(), + precise_capturing.as_deref().map(|(args, _)| args.as_slice()), ), ImplTraitContext::Universal => { - assert!( - precise_capturing.is_none(), - "TODO: precise captures not supported on universals!" - ); + if let Some(&(_, span)) = precise_capturing.as_deref() { + self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnApit { span }); + }; let span = t.span; // HACK: pprust breaks strings with newlines when the type diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 6553ed72532cb..db9633bb1cbb7 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1150,9 +1150,17 @@ impl<'a> State<'a> { } self.print_type_bounds(bounds); } - ast::TyKind::ImplTrait(_, bounds, _precise_capturing) => { - // TODO(precise_capturing): + ast::TyKind::ImplTrait(_, bounds, precise_capturing_args) => { self.word_nbsp("impl"); + if let Some((precise_capturing_args, ..)) = precise_capturing_args.as_deref() { + self.word("use"); + self.word("<"); + self.commasep(Inconsistent, precise_capturing_args, |s, arg| match arg { + ast::PreciseCapturingArg::Arg(a, _) => s.print_ident(*a), + ast::PreciseCapturingArg::Lifetime(lt) => s.print_lifetime(*lt), + }); + self.word(">") + } self.print_type_bounds(bounds); } ast::TyKind::Array(ty, length) => { diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 792cc1066f5ad..7986add68b7f0 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -535,8 +535,6 @@ declare_features! ( (unstable, must_not_suspend, "1.57.0", Some(83310)), /// Allows `mut ref` and `mut ref mut` identifier patterns. (incomplete, mut_ref, "CURRENT_RUSTC_VERSION", Some(123076)), - /// Allows `use<'a, 'b, A, B>` in `impl use<...> Trait` for precise capture of generic args. - (incomplete, precise_capturing, "CURRENT_RUSTC_VERSION", Some(123432)), /// Allows using `#[naked]` on functions. (unstable, naked_functions, "1.9.0", Some(90957)), /// Allows specifying the as-needed link modifier @@ -569,6 +567,8 @@ declare_features! ( (unstable, optimize_attribute, "1.34.0", Some(54882)), /// Allows postfix match `expr.match { ... }` (unstable, postfix_match, "CURRENT_RUSTC_VERSION", Some(121618)), + /// Allows `use<'a, 'b, A, B>` in `impl use<...> Trait` for precise capture of generic args. + (incomplete, precise_capturing, "CURRENT_RUSTC_VERSION", Some(123432)), /// Allows macro attributes on expressions, statements and non-inline modules. (unstable, proc_macro_hygiene, "1.30.0", Some(54727)), /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions. diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 86b8b6d6b2b6b..81cf3067a1ed6 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -37,6 +37,8 @@ hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit wh .label = deref recursion limit reached .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`) +hir_analysis_bad_precise_capture = expected {$kind} parameter in `use<...>` precise captures list, found {$found} + hir_analysis_cannot_capture_late_bound_const = cannot capture late-bound const parameter in {$what} .label = parameter defined here @@ -214,6 +216,10 @@ hir_analysis_late_bound_lifetime_in_apit = `impl Trait` can only mention lifetim hir_analysis_late_bound_type_in_apit = `impl Trait` can only mention type parameters from an fn or impl .label = type parameter declared here +hir_analysis_lifetime_not_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list + .label = lifetime captured due to being mentioned in the bounds of the `impl Trait` + .param_label = this lifetime parameter is captured + hir_analysis_lifetimes_or_bounds_mismatch_on_trait = lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration .label = lifetimes do not match {$item_kind} in trait @@ -339,6 +345,10 @@ hir_analysis_param_in_ty_of_assoc_const_binding = *[normal] the {$param_def_kind} `{$param_name}` is defined here } +hir_analysis_param_not_captured = `impl Trait` must mention all {$kind} parameters in scope + .label = {$kind} parameter is implicitly captured by this `impl Trait` + .note = currently, all {$kind} parameters are required to be mentioned in the precise captures list + hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation .help = add `#![feature(unboxed_closures)]` to the crate attributes to use it diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 216b89fd4f150..f4d443c324a09 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1,10 +1,10 @@ use crate::check::intrinsicck::InlineAsmCtxt; -use crate::errors::LinkageType; use super::compare_impl_item::check_type_bounds; use super::compare_impl_item::{compare_impl_method, compare_impl_ty}; use super::*; use rustc_attr as attr; +use rustc_data_structures::unord::UnordSet; use rustc_errors::{codes::*, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind}; @@ -12,6 +12,7 @@ use rustc_hir::Node; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, TraitEngineExt as _}; use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; +use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::stability::EvalResult; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::fold::BottomUpFolder; @@ -474,6 +475,94 @@ fn sanity_check_found_hidden_type<'tcx>( } } +fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId) { + let hir::OpaqueTy { precise_capturing_args, .. } = + *tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); + let Some(precise_capturing_args) = precise_capturing_args else { + // No precise capturing args; nothing to validate + return; + }; + + let mut expected_captures = UnordSet::default(); + for arg in precise_capturing_args { + match *arg { + hir::PreciseCapturingArg::Lifetime(&hir::Lifetime { hir_id, .. }) + | hir::PreciseCapturingArg::Param(_, hir_id) => match tcx.named_bound_var(hir_id) { + Some(ResolvedArg::EarlyBound(def_id)) => { + expected_captures.insert(def_id); + } + _ => { + tcx.dcx().span_delayed_bug( + tcx.hir().span(hir_id), + "parameter should have been resolved", + ); + } + }, + } + } + + let variances = tcx.variances_of(opaque_def_id); + let mut def_id = Some(opaque_def_id.to_def_id()); + while let Some(generics) = def_id { + let generics = tcx.generics_of(generics); + def_id = generics.parent; + + for param in &generics.params { + if expected_captures.contains(¶m.def_id) { + assert_eq!( + variances[param.index as usize], + ty::Invariant, + "precise captured param should be invariant" + ); + continue; + } + + match param.kind { + ty::GenericParamDefKind::Lifetime => { + // Check if the lifetime param was captured but isn't named in the precise captures list. + if variances[param.index as usize] == ty::Invariant { + let param_span = + if let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) + | ty::ReLateParam(ty::LateParamRegion { + bound_region: ty::BoundRegionKind::BrNamed(def_id, _), + .. + }) = *tcx + .map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()) + { + Some(tcx.def_span(def_id)) + } else { + None + }; + // FIXME(precise_capturing): Structured suggestion for this would be useful + tcx.dcx().emit_err(errors::LifetimeNotCaptured { + use_span: tcx.def_span(param.def_id), + param_span, + opaque_span: tcx.def_span(opaque_def_id), + }); + continue; + } + } + ty::GenericParamDefKind::Type { .. } => { + // FIXME(precise_capturing): Structured suggestion for this would be useful + tcx.dcx().emit_err(errors::ParamNotCaptured { + param_span: tcx.def_span(param.def_id), + opaque_span: tcx.def_span(opaque_def_id), + kind: "type", + }); + } + ty::GenericParamDefKind::Const { .. } => { + // FIXME(precise_capturing): Structured suggestion for this would be useful + tcx.dcx().emit_err(errors::ParamNotCaptured { + param_span: tcx.def_span(param.def_id), + opaque_span: tcx.def_span(opaque_def_id), + kind: "const", + }); + } + } + } + } +} + fn is_enum_of_nonnullable_ptr<'tcx>( tcx: TyCtxt<'tcx>, adt_def: AdtDef<'tcx>, @@ -499,7 +588,7 @@ fn check_static_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) { ty::Adt(adt_def, args) => !is_enum_of_nonnullable_ptr(tcx, *adt_def, *args), _ => true, } { - tcx.dcx().emit_err(LinkageType { span: tcx.def_span(def_id) }); + tcx.dcx().emit_err(errors::LinkageType { span: tcx.def_span(def_id) }); } } } @@ -566,6 +655,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_union(tcx, def_id); } DefKind::OpaqueTy => { + check_opaque_precise_captures(tcx, def_id); + let origin = tcx.opaque_type_origin(def_id); if let hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = origin diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 8e29269e8fd1f..8d5f4c13721f9 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -569,7 +569,13 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { LifetimeName::Error => {} LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Infer - | LifetimeName::Static => todo!("TODO: Error on invalid lifetime"), + | LifetimeName::Static => { + self.tcx.dcx().emit_err(errors::BadPreciseCapture { + span: lt.ident.span, + kind: "lifetime", + found: format!("`{}`", lt.ident.name), + }); + } }, hir::PreciseCapturingArg::Param(res, hir_id) => match res { Res::Def(DefKind::TyParam | DefKind::ConstParam, def_id) @@ -577,7 +583,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { self.resolve_type_ref(def_id.expect_local(), hir_id); } Res::Err => {} - _ => todo!("TODO: Error on invalid param res"), + _ => { + // This is handled in resolve + self.tcx.dcx().delayed_bug(format!("parameter should have been resolved")); + } }, } } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index d129614e0e1aa..867ee772a30a2 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -10,6 +10,9 @@ use rustc_span::{symbol::Ident, Span, Symbol}; mod pattern_types; pub use pattern_types::*; +mod precise_captures; +pub(crate) use precise_captures::*; + #[derive(Diagnostic)] #[diag(hir_analysis_ambiguous_assoc_item)] pub struct AmbiguousAssocItem<'a> { diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs new file mode 100644 index 0000000000000..3b22437abb249 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs @@ -0,0 +1,33 @@ +use rustc_macros::Diagnostic; +use rustc_span::Span; + +#[derive(Diagnostic)] +#[diag(hir_analysis_param_not_captured)] +#[note] +pub struct ParamNotCaptured { + #[primary_span] + pub param_span: Span, + #[label] + pub opaque_span: Span, + pub kind: &'static str, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_lifetime_not_captured)] +pub struct LifetimeNotCaptured { + #[primary_span] + pub use_span: Span, + #[label(hir_analysis_param_label)] + pub param_span: Option, + #[label] + pub opaque_span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_bad_precise_capture)] +pub struct BadPreciseCapture { + #[primary_span] + pub span: Span, + pub kind: &'static str, + pub found: String, +} diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index a24ecf938ba4b..91f4823f65d4d 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -674,7 +674,7 @@ impl<'a> Parser<'a> { let use_span = self.prev_token.span; self.psess.gated_spans.gate(sym::precise_capturing, use_span); let args = self.parse_precise_capturing_args()?; - Some(args) + Some(P((args, use_span))) } else { None }; diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index c7e12284b50f5..7203adab135aa 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1054,7 +1054,10 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) { match arg { + // Lower the lifetime regularly; we'll resolve the lifetime and check + // it's a parameter later on in HIR lowering. PreciseCapturingArg::Lifetime(_) => visit::walk_precise_capturing_arg(self, arg), + PreciseCapturingArg::Arg(ident, node_id) => { let ident = ident.normalize_to_macros_2_0(); 'found: { @@ -1064,6 +1067,38 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, if let Some(res) = rib_t.bindings.get(&ident).or(rib_v.bindings.get(&ident)) { self.r.record_partial_res(*node_id, PartialRes::new(*res)); + + // Validate that this is a parameter + match res { + Res::Def(DefKind::TyParam | DefKind::ConstParam, _) + | Res::SelfTyParam { .. } => {} + Res::SelfTyAlias { .. } => { + self.report_error( + ident.span, + ResolutionError::FailedToResolve { + segment: Some(ident.name), + label: "`Self` cannot be captured because it is not a type parameter".to_string(), + suggestion: None, + module: None, + }, + ); + } + _ => { + self.report_error( + ident.span, + ResolutionError::FailedToResolve { + segment: Some(ident.name), + label: format!( + "expected type or const parameter, found {}", + res.descr() + ), + suggestion: None, + module: None, + }, + ); + } + } + break 'found; } } diff --git a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs new file mode 100644 index 0000000000000..623063a8f5028 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs @@ -0,0 +1,14 @@ +#![feature(precise_capturing)] +//~^ WARN the feature `precise_capturing` is incomplete + +fn no_elided_lt() -> impl use<'_> Sized {} +//~^ ERROR missing lifetime specifier +//~| ERROR expected lifetime parameter in `use<...>` precise captures list, found `'_` + +fn static_lt() -> impl use<'static> Sized {} +//~^ ERROR expected lifetime parameter in `use<...>` precise captures list, found `'static` + +fn missing_lt() -> impl use<'missing> Sized {} +//~^ ERROR use of undeclared lifetime name `'missing` + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr new file mode 100644 index 0000000000000..a926362c50c83 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr @@ -0,0 +1,45 @@ +error[E0106]: missing lifetime specifier + --> $DIR/bad-lifetimes.rs:4:31 + | +LL | fn no_elided_lt() -> impl use<'_> Sized {} + | ^^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values + | +LL | fn no_elided_lt() -> impl use<'static> Sized {} + | ~~~~~~~ + +error[E0261]: use of undeclared lifetime name `'missing` + --> $DIR/bad-lifetimes.rs:11:29 + | +LL | fn missing_lt() -> impl use<'missing> Sized {} + | - ^^^^^^^^ undeclared lifetime + | | + | help: consider introducing lifetime `'missing` here: `<'missing>` + +warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/bad-lifetimes.rs:1:12 + | +LL | #![feature(precise_capturing)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #123432 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: expected lifetime parameter in `use<...>` precise captures list, found `'_` + --> $DIR/bad-lifetimes.rs:4:31 + | +LL | fn no_elided_lt() -> impl use<'_> Sized {} + | ^^ + +error: expected lifetime parameter in `use<...>` precise captures list, found `'static` + --> $DIR/bad-lifetimes.rs:8:28 + | +LL | fn static_lt() -> impl use<'static> Sized {} + | ^^^^^^^ + +error: aborting due to 4 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0106, E0261. +For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.rs b/tests/ui/impl-trait/precise-capturing/bad-params.rs new file mode 100644 index 0000000000000..c600fa96f794f --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/bad-params.rs @@ -0,0 +1,16 @@ +#![feature(precise_capturing)] +//~^ WARN the feature `precise_capturing` is incomplete + +fn missing() -> impl use Sized {} +//~^ ERROR could not find type or const parameter + +fn missing_self() -> impl use Sized {} +//~^ ERROR could not find type or const parameter + +struct MyType; +impl MyType { + fn self_is_not_param() -> impl use Sized {} + //~^ ERROR `Self` cannot be captured because it is not a type parameter +} + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.stderr b/tests/ui/impl-trait/precise-capturing/bad-params.stderr new file mode 100644 index 0000000000000..bb5fe76ae3e3f --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/bad-params.stderr @@ -0,0 +1,30 @@ +error[E0433]: failed to resolve: could not find type or const parameter + --> $DIR/bad-params.rs:4:26 + | +LL | fn missing() -> impl use Sized {} + | ^ could not find type or const parameter + +error[E0433]: failed to resolve: could not find type or const parameter + --> $DIR/bad-params.rs:7:31 + | +LL | fn missing_self() -> impl use Sized {} + | ^^^^ could not find type or const parameter + +error[E0433]: failed to resolve: `Self` cannot be captured because it is not a type parameter + --> $DIR/bad-params.rs:12:40 + | +LL | fn self_is_not_param() -> impl use Sized {} + | ^^^^ `Self` cannot be captured because it is not a type parameter + +warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/bad-params.rs:1:12 + | +LL | #![feature(precise_capturing)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #123432 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs new file mode 100644 index 0000000000000..1b604e6c358ba --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs @@ -0,0 +1,7 @@ +#![feature(precise_capturing)] +//~^ WARN the feature `precise_capturing` is incomplete + +fn constant() -> impl use<> Sized {} +//~^ ERROR `impl Trait` must mention all const parameters in scope + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr new file mode 100644 index 0000000000000..9c99f2b711eab --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr @@ -0,0 +1,19 @@ +warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/forgot-to-capture-const.rs:1:12 + | +LL | #![feature(precise_capturing)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #123432 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: `impl Trait` must mention all const parameters in scope + --> $DIR/forgot-to-capture-const.rs:4:13 + | +LL | fn constant() -> impl use<> Sized {} + | ^^^^^^^^^^^^^^ ---------------- const parameter is implicitly captured by this `impl Trait` + | + = note: currently, all const parameters are required to be mentioned in the precise captures list + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs new file mode 100644 index 0000000000000..7e856ba95172f --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs @@ -0,0 +1,7 @@ +#![feature(precise_capturing)] +//~^ WARN the feature `precise_capturing` is incomplete + +fn type_param() -> impl use<> Sized {} +//~^ ERROR `impl Trait` must mention all type parameters in scope + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr new file mode 100644 index 0000000000000..1ef46a36e461e --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr @@ -0,0 +1,19 @@ +warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/forgot-to-capture-lifetime.rs:1:12 + | +LL | #![feature(precise_capturing)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #123432 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: `impl Trait` must mention all type parameters in scope + --> $DIR/forgot-to-capture-lifetime.rs:4:15 + | +LL | fn type_param() -> impl use<> Sized {} + | ^ ---------------- type parameter is implicitly captured by this `impl Trait` + | + = note: currently, all type parameters are required to be mentioned in the precise captures list + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs new file mode 100644 index 0000000000000..cc86bf83107e1 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs @@ -0,0 +1,10 @@ +#![feature(precise_capturing)] +//~^ WARN the feature `precise_capturing` is incomplete + +fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x } +//~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list + +fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x } +//~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr new file mode 100644 index 0000000000000..58f284ec6bc16 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr @@ -0,0 +1,35 @@ +warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/forgot-to-capture-type.rs:1:12 + | +LL | #![feature(precise_capturing)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #123432 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list + --> $DIR/forgot-to-capture-type.rs:4:58 + | +LL | fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x } + | -- -----------------^^---- + | | | + | | lifetime captured due to being mentioned in the bounds of the `impl Trait` + | this lifetime parameter is captured + +error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds + --> $DIR/forgot-to-capture-type.rs:7:60 + | +LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x } + | -- ---------------- ^ + | | | + | | opaque type defined here + | hidden type `&'a ()` captures the lifetime `'a` as defined here + | +help: to declare that `impl Sized` captures `'a`, you can add an explicit `'a` lifetime bound + | +LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized + 'a { x } + | ++++ + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/impl-trait/precise-capturing/higher-ranked.rs b/tests/ui/impl-trait/precise-capturing/higher-ranked.rs index c9faaaed96892..28fb1fa4b9ec6 100644 --- a/tests/ui/impl-trait/precise-capturing/higher-ranked.rs +++ b/tests/ui/impl-trait/precise-capturing/higher-ranked.rs @@ -15,4 +15,4 @@ impl Trait<'_> for () { fn hello() -> impl for<'a> Trait<'a, Item = impl use<> IntoIterator> {} -fn main() {} \ No newline at end of file +fn main() {} From 02d7317af218343818dcb107cda80750e9604a3a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 4 Apr 2024 14:46:26 -0400 Subject: [PATCH 07/11] Add hir::Node::PreciseCapturingNonLifetimeArg --- compiler/rustc_ast_lowering/src/index.rs | 17 ++++++++++ compiler/rustc_ast_lowering/src/lib.rs | 8 +++-- compiler/rustc_hir/src/hir.rs | 11 ++++++- compiler/rustc_hir/src/intravisit.rs | 2 +- .../rustc_hir_analysis/src/check/check.rs | 4 ++- .../src/collect/resolve_bound_vars.rs | 4 +-- compiler/rustc_hir_pretty/src/lib.rs | 1 + compiler/rustc_middle/src/hir/map/mod.rs | 2 ++ .../forgot-to-capture-lifetime.rs | 7 ++-- .../forgot-to-capture-lifetime.stderr | 28 ++++++++++++---- .../forgot-to-capture-type.rs | 10 +++--- .../forgot-to-capture-type.stderr | 33 ++++++++----------- .../precise-capturing/self-capture.rs | 10 ++++++ .../precise-capturing/self-capture.stderr | 11 +++++++ 14 files changed, 110 insertions(+), 38 deletions(-) create mode 100644 tests/ui/impl-trait/precise-capturing/self-capture.rs create mode 100644 tests/ui/impl-trait/precise-capturing/self-capture.stderr diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 4c552289a8165..93be9b9b8cf5a 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -385,4 +385,21 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { fn visit_pattern_type_pattern(&mut self, p: &'hir hir::Pat<'hir>) { self.visit_pat(p) } + + fn visit_precise_capturing_arg( + &mut self, + arg: &'hir PreciseCapturingArg<'hir>, + ) -> Self::Result { + match arg { + PreciseCapturingArg::Lifetime(_) => { + // This is represented as a `Node::Lifetime`, intravisit will get to it below. + } + PreciseCapturingArg::Param(param) => self.insert( + param.ident.span, + param.hir_id, + Node::PreciseCapturingNonLifetimeArg(param), + ), + } + intravisit::walk_precise_capturing_arg(self, arg); + } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index b586d2a1bf564..84776f920c46f 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1790,11 +1790,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { PreciseCapturingArg::Lifetime(lt) => { hir::PreciseCapturingArg::Lifetime(self.lower_lifetime(lt)) } - PreciseCapturingArg::Arg(_, node_id) => { + PreciseCapturingArg::Arg(ident, node_id) => { let res = self.resolver.get_partial_res(*node_id).map_or(Res::Err, |partial_res| { partial_res.full_res().expect("no partial res expected for precise capture arg") }); - hir::PreciseCapturingArg::Param(self.lower_res(res), self.lower_node_id(*node_id)) + hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg { + hir_id: self.lower_node_id(*node_id), + ident: self.lower_ident(*ident), + res: self.lower_res(res), + }) } })) } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index dc76deb18152e..00b1ea061cc73 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2565,7 +2565,14 @@ pub struct OpaqueTy<'hir> { pub enum PreciseCapturingArg<'hir> { Lifetime(&'hir Lifetime), /// Non-lifetime argument (type or const) - Param(Res, HirId), + Param(PreciseCapturingNonLifetimeArg), +} + +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub struct PreciseCapturingNonLifetimeArg { + pub hir_id: HirId, + pub ident: Ident, + pub res: Res, } /// From whence the opaque type came. @@ -3544,6 +3551,7 @@ pub enum Node<'hir> { WhereBoundPredicate(&'hir WhereBoundPredicate<'hir>), // FIXME: Merge into `Node::Infer`. ArrayLenInfer(&'hir InferArg), + PreciseCapturingNonLifetimeArg(&'hir PreciseCapturingNonLifetimeArg), // Created by query feeding Synthetic, // Span by reference to minimize `Node`'s size @@ -3580,6 +3588,7 @@ impl<'hir> Node<'hir> { Node::TypeBinding(b) => Some(b.ident), Node::PatField(f) => Some(f.ident), Node::ExprField(f) => Some(f.ident), + Node::PreciseCapturingNonLifetimeArg(a) => Some(a.ident), Node::Param(..) | Node::AnonConst(..) | Node::ConstBlock(..) diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index d3ede3a904f9c..cd9f9ff9109c1 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1151,7 +1151,7 @@ pub fn walk_precise_capturing_arg<'v, V: Visitor<'v>>( ) -> V::Result { match *arg { PreciseCapturingArg::Lifetime(lt) => visitor.visit_lifetime(lt), - PreciseCapturingArg::Param(_, hir_id) => visitor.visit_id(hir_id), + PreciseCapturingArg::Param(param) => visitor.visit_id(param.hir_id), } } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index f4d443c324a09..592a8648f1440 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -487,7 +487,9 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe for arg in precise_capturing_args { match *arg { hir::PreciseCapturingArg::Lifetime(&hir::Lifetime { hir_id, .. }) - | hir::PreciseCapturingArg::Param(_, hir_id) => match tcx.named_bound_var(hir_id) { + | hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg { + hir_id, .. + }) => match tcx.named_bound_var(hir_id) { Some(ResolvedArg::EarlyBound(def_id)) => { expected_captures.insert(def_id); } diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 8d5f4c13721f9..fb3713140ffed 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -577,10 +577,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }); } }, - hir::PreciseCapturingArg::Param(res, hir_id) => match res { + hir::PreciseCapturingArg::Param(param) => match param.res { Res::Def(DefKind::TyParam | DefKind::ConstParam, def_id) | Res::SelfTyParam { trait_: def_id } => { - self.resolve_type_ref(def_id.expect_local(), hir_id); + self.resolve_type_ref(def_id.expect_local(), param.hir_id); } Res::Err => {} _ => { diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 39312614c1b19..3d3b16baf6970 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -99,6 +99,7 @@ impl<'a> State<'a> { Node::PatField(a) => self.print_patfield(a), Node::Arm(a) => self.print_arm(a), Node::Infer(_) => self.word("_"), + Node::PreciseCapturingNonLifetimeArg(param) => self.print_ident(param.ident), Node::Block(a) => { // Containing cbox, will be closed by print-block at `}`. self.cbox(INDENT_UNIT); diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 72f849b534a81..88de834c4362d 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -909,6 +909,7 @@ impl<'hir> Map<'hir> { Node::Crate(item) => item.spans.inner_span, Node::WhereBoundPredicate(pred) => pred.span, Node::ArrayLenInfer(inf) => inf.span, + Node::PreciseCapturingNonLifetimeArg(param) => param.ident.span, Node::Synthetic => unreachable!(), Node::Err(span) => *span, } @@ -1176,6 +1177,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { Node::ArrayLenInfer(_) => node_str("array len infer"), Node::Synthetic => unreachable!(), Node::Err(_) => node_str("error"), + Node::PreciseCapturingNonLifetimeArg(_param) => node_str("parameter"), } } diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs index 7e856ba95172f..cc86bf83107e1 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs @@ -1,7 +1,10 @@ #![feature(precise_capturing)] //~^ WARN the feature `precise_capturing` is incomplete -fn type_param() -> impl use<> Sized {} -//~^ ERROR `impl Trait` must mention all type parameters in scope +fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x } +//~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list + +fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x } +//~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr index 1ef46a36e461e..e472c898050a3 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr @@ -7,13 +7,29 @@ LL | #![feature(precise_capturing)] = note: see issue #123432 for more information = note: `#[warn(incomplete_features)]` on by default -error: `impl Trait` must mention all type parameters in scope - --> $DIR/forgot-to-capture-lifetime.rs:4:15 +error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list + --> $DIR/forgot-to-capture-lifetime.rs:4:58 | -LL | fn type_param() -> impl use<> Sized {} - | ^ ---------------- type parameter is implicitly captured by this `impl Trait` +LL | fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x } + | -- -----------------^^---- + | | | + | | lifetime captured due to being mentioned in the bounds of the `impl Trait` + | this lifetime parameter is captured + +error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds + --> $DIR/forgot-to-capture-lifetime.rs:7:60 + | +LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x } + | -- ---------------- ^ + | | | + | | opaque type defined here + | hidden type `&'a ()` captures the lifetime `'a` as defined here + | +help: to declare that `impl Sized` captures `'a`, you can add an explicit `'a` lifetime bound | - = note: currently, all type parameters are required to be mentioned in the precise captures list +LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized + 'a { x } + | ++++ -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs index cc86bf83107e1..6eaff01183d4b 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs @@ -1,10 +1,12 @@ #![feature(precise_capturing)] //~^ WARN the feature `precise_capturing` is incomplete -fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x } -//~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list +fn type_param() -> impl use<> Sized {} +//~^ ERROR `impl Trait` must mention all type parameters in scope -fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x } -//~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds +trait Foo { +//~^ ERROR `impl Trait` must mention all type parameters in scope + fn bar() -> impl use<> Sized; +} fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr index 58f284ec6bc16..a8eb4547dcdda 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr @@ -7,29 +7,24 @@ LL | #![feature(precise_capturing)] = note: see issue #123432 for more information = note: `#[warn(incomplete_features)]` on by default -error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list - --> $DIR/forgot-to-capture-type.rs:4:58 +error: `impl Trait` must mention all type parameters in scope + --> $DIR/forgot-to-capture-type.rs:4:15 | -LL | fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x } - | -- -----------------^^---- - | | | - | | lifetime captured due to being mentioned in the bounds of the `impl Trait` - | this lifetime parameter is captured - -error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds - --> $DIR/forgot-to-capture-type.rs:7:60 +LL | fn type_param() -> impl use<> Sized {} + | ^ ---------------- type parameter is implicitly captured by this `impl Trait` | -LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x } - | -- ---------------- ^ - | | | - | | opaque type defined here - | hidden type `&'a ()` captures the lifetime `'a` as defined here + = note: currently, all type parameters are required to be mentioned in the precise captures list + +error: `impl Trait` must mention all type parameters in scope + --> $DIR/forgot-to-capture-type.rs:7:1 | -help: to declare that `impl Sized` captures `'a`, you can add an explicit `'a` lifetime bound +LL | trait Foo { + | ^^^^^^^^^ +LL | +LL | fn bar() -> impl use<> Sized; + | ---------------- type parameter is implicitly captured by this `impl Trait` | -LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized + 'a { x } - | ++++ + = note: currently, all type parameters are required to be mentioned in the precise captures list error: aborting due to 2 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/impl-trait/precise-capturing/self-capture.rs b/tests/ui/impl-trait/precise-capturing/self-capture.rs new file mode 100644 index 0000000000000..ecbc388e27be6 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/self-capture.rs @@ -0,0 +1,10 @@ +//@ check-pass + +#![feature(precise_capturing)] +//~^ WARN the feature `precise_capturing` is incomplete + +trait Foo { + fn bar<'a>() -> impl use Sized; +} + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/self-capture.stderr b/tests/ui/impl-trait/precise-capturing/self-capture.stderr new file mode 100644 index 0000000000000..5a058c6826d28 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/self-capture.stderr @@ -0,0 +1,11 @@ +warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/self-capture.rs:3:12 + | +LL | #![feature(precise_capturing)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #123432 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + From ce8961039e244b1e4e0fa02fc10d59a22abc9ea3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 4 Apr 2024 15:06:30 -0400 Subject: [PATCH 08/11] Some ordering and duplication checks --- compiler/rustc_hir_analysis/messages.ftl | 6 ++ .../rustc_hir_analysis/src/check/check.rs | 57 ++++++++++++++----- .../src/errors/precise_captures.rs | 22 ++++++- .../feature-gate-precise-capturing.rs | 4 ++ .../feature-gate-precise-capturing.stderr | 13 +++++ .../impl-trait/precise-capturing/ordering.rs | 16 ++++++ .../precise-capturing/ordering.stderr | 37 ++++++++++++ 7 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-precise-capturing.rs create mode 100644 tests/ui/feature-gates/feature-gate-precise-capturing.stderr create mode 100644 tests/ui/impl-trait/precise-capturing/ordering.rs create mode 100644 tests/ui/impl-trait/precise-capturing/ordering.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 81cf3067a1ed6..4ac2965bd5f0c 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -113,6 +113,9 @@ hir_analysis_drop_impl_on_wrong_item = hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported +hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice + .label = parameter captured again here + hir_analysis_empty_specialization = specialization impl does not specialize any associated items .note = impl is a specialization of this impl @@ -216,6 +219,9 @@ hir_analysis_late_bound_lifetime_in_apit = `impl Trait` can only mention lifetim hir_analysis_late_bound_type_in_apit = `impl Trait` can only mention type parameters from an fn or impl .label = type parameter declared here +hir_analysis_lifetime_must_be_first = lifetime parameter `{$name}` must be listed before non-lifetime parameters + .label = move the lifetime before this parameter + hir_analysis_lifetime_not_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list .label = lifetime captured due to being mentioned in the bounds of the `impl Trait` .param_label = this lifetime parameter is captured diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 592a8648f1440..1e8cd50ca0de1 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -4,7 +4,7 @@ use super::compare_impl_item::check_type_bounds; use super::compare_impl_item::{compare_impl_method, compare_impl_ty}; use super::*; use rustc_attr as attr; -use rustc_data_structures::unord::UnordSet; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{codes::*, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind}; @@ -484,22 +484,51 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe }; let mut expected_captures = UnordSet::default(); + let mut seen_params = UnordMap::default(); + let mut prev_non_lifetime_param = None; for arg in precise_capturing_args { - match *arg { - hir::PreciseCapturingArg::Lifetime(&hir::Lifetime { hir_id, .. }) - | hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg { - hir_id, .. - }) => match tcx.named_bound_var(hir_id) { - Some(ResolvedArg::EarlyBound(def_id)) => { - expected_captures.insert(def_id); + let (hir_id, ident) = match *arg { + hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg { + hir_id, + ident, + .. + }) => { + if prev_non_lifetime_param.is_none() { + prev_non_lifetime_param = Some(ident); } - _ => { - tcx.dcx().span_delayed_bug( - tcx.hir().span(hir_id), - "parameter should have been resolved", - ); + (hir_id, ident) + } + hir::PreciseCapturingArg::Lifetime(&hir::Lifetime { hir_id, ident, .. }) => { + if let Some(prev_non_lifetime_param) = prev_non_lifetime_param { + tcx.dcx().emit_err(errors::LifetimesMustBeFirst { + lifetime_span: ident.span, + name: ident.name, + other_span: prev_non_lifetime_param.span, + }); } - }, + (hir_id, ident) + } + }; + + let ident = ident.normalize_to_macros_2_0(); + if let Some(span) = seen_params.insert(ident, ident.span) { + tcx.dcx().emit_err(errors::DuplicatePreciseCapture { + name: ident.name, + first_span: span, + second_span: ident.span, + }); + } + + match tcx.named_bound_var(hir_id) { + Some(ResolvedArg::EarlyBound(def_id)) => { + expected_captures.insert(def_id); + } + _ => { + tcx.dcx().span_delayed_bug( + tcx.hir().span(hir_id), + "parameter should have been resolved", + ); + } } } diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs index 3b22437abb249..e2eb9c72bf235 100644 --- a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs +++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs @@ -1,5 +1,5 @@ use rustc_macros::Diagnostic; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] #[diag(hir_analysis_param_not_captured)] @@ -31,3 +31,23 @@ pub struct BadPreciseCapture { pub kind: &'static str, pub found: String, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_duplicate_precise_capture)] +pub struct DuplicatePreciseCapture { + #[primary_span] + pub first_span: Span, + pub name: Symbol, + #[label] + pub second_span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_lifetime_must_be_first)] +pub struct LifetimesMustBeFirst { + #[primary_span] + pub lifetime_span: Span, + pub name: Symbol, + #[label] + pub other_span: Span, +} diff --git a/tests/ui/feature-gates/feature-gate-precise-capturing.rs b/tests/ui/feature-gates/feature-gate-precise-capturing.rs new file mode 100644 index 0000000000000..0c3b49776230c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-precise-capturing.rs @@ -0,0 +1,4 @@ +fn hello() -> impl use<> Sized {} +//~^ ERROR precise captures on `impl Trait` are experimental + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-precise-capturing.stderr b/tests/ui/feature-gates/feature-gate-precise-capturing.stderr new file mode 100644 index 0000000000000..102b39148f9f0 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-precise-capturing.stderr @@ -0,0 +1,13 @@ +error[E0658]: precise captures on `impl Trait` are experimental + --> $DIR/feature-gate-precise-capturing.rs:1:20 + | +LL | fn hello() -> impl use<> Sized {} + | ^^^ + | + = note: see issue #123432 for more information + = help: add `#![feature(precise_capturing)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/impl-trait/precise-capturing/ordering.rs b/tests/ui/impl-trait/precise-capturing/ordering.rs new file mode 100644 index 0000000000000..2bace798c5702 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/ordering.rs @@ -0,0 +1,16 @@ +#![feature(precise_capturing)] +//~^ WARN the feature `precise_capturing` is incomplete + +fn lt<'a>() -> impl use<'a, 'a> Sized {} +//~^ ERROR cannot capture parameter `'a` twice + +fn ty() -> impl use Sized {} +//~^ ERROR cannot capture parameter `T` twice + +fn ct() -> impl use Sized {} +//~^ ERROR cannot capture parameter `N` twice + +fn ordering<'a, T>() -> impl use Sized {} +//~^ ERROR lifetime parameter `'a` must be listed before non-lifetime parameters + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/ordering.stderr b/tests/ui/impl-trait/precise-capturing/ordering.stderr new file mode 100644 index 0000000000000..3f545108df563 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/ordering.stderr @@ -0,0 +1,37 @@ +warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/ordering.rs:1:12 + | +LL | #![feature(precise_capturing)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #123432 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: cannot capture parameter `'a` twice + --> $DIR/ordering.rs:4:25 + | +LL | fn lt<'a>() -> impl use<'a, 'a> Sized {} + | ^^ -- parameter captured again here + +error: cannot capture parameter `T` twice + --> $DIR/ordering.rs:7:24 + | +LL | fn ty() -> impl use Sized {} + | ^ - parameter captured again here + +error: cannot capture parameter `N` twice + --> $DIR/ordering.rs:10:37 + | +LL | fn ct() -> impl use Sized {} + | ^ - parameter captured again here + +error: lifetime parameter `'a` must be listed before non-lifetime parameters + --> $DIR/ordering.rs:13:37 + | +LL | fn ordering<'a, T>() -> impl use Sized {} + | - ^^ + | | + | move the lifetime before this parameter + +error: aborting due to 4 previous errors; 1 warning emitted + From 52c6b101ea18ed6f09367bf459ac55ffe473cd9c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 4 Apr 2024 20:23:52 -0400 Subject: [PATCH 09/11] Use a path instead of an ident (and stop manually resolving) --- compiler/rustc_ast/src/ast.rs | 4 +- compiler/rustc_ast/src/mut_visit.rs | 4 +- compiler/rustc_ast/src/visit.rs | 4 +- compiler/rustc_ast_lowering/src/lib.rs | 11 ++-- compiler/rustc_ast_pretty/src/pprust/state.rs | 2 +- compiler/rustc_hir_analysis/messages.ftl | 3 + .../src/collect/resolve_bound_vars.rs | 16 ++++- .../src/errors/precise_captures.rs | 10 +++ compiler/rustc_parse/src/parser/ty.rs | 7 +- compiler/rustc_resolve/src/late.rs | 66 ++++--------------- .../precise-capturing/bad-params.rs | 6 +- .../precise-capturing/bad-params.stderr | 32 +++++---- 12 files changed, 81 insertions(+), 84 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index ace20a005ee40..0cf96797028da 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2188,12 +2188,12 @@ pub enum TraitObjectSyntax { None, } -#[derive(Clone, PartialEq, Encodable, Decodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum PreciseCapturingArg { /// Lifetime parameter Lifetime(Lifetime), /// Type or const parameter - Arg(Ident, NodeId), + Arg(Path, NodeId), } /// Inline assembly operand explicit register or register class. diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 204f33cab345f..c4e49d7dbea44 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -928,8 +928,8 @@ pub fn noop_visit_precise_capturing_arg(arg: &mut PreciseCapturin PreciseCapturingArg::Lifetime(lt) => { vis.visit_lifetime(lt); } - PreciseCapturingArg::Arg(ident, id) => { - vis.visit_ident(ident); + PreciseCapturingArg::Arg(path, id) => { + vis.visit_path(path); vis.visit_id(id); } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index a9c29e077d13d..968d10ad4872c 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -653,8 +653,8 @@ pub fn walk_precise_capturing_arg<'a, V: Visitor<'a>>( PreciseCapturingArg::Lifetime(lt) => { visitor.visit_lifetime(lt, LifetimeCtxt::GenericArg); } - PreciseCapturingArg::Arg(ident, _) => { - visitor.visit_ident(*ident); + PreciseCapturingArg::Arg(path, id) => { + visitor.visit_path(path, *id); } } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 84776f920c46f..a21d6019cf164 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1790,13 +1790,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { PreciseCapturingArg::Lifetime(lt) => { hir::PreciseCapturingArg::Lifetime(self.lower_lifetime(lt)) } - PreciseCapturingArg::Arg(ident, node_id) => { - let res = self.resolver.get_partial_res(*node_id).map_or(Res::Err, |partial_res| { + PreciseCapturingArg::Arg(path, id) => { + let [segment] = path.segments.as_slice() else { + panic!(); + }; + let res = self.resolver.get_partial_res(*id).map_or(Res::Err, |partial_res| { partial_res.full_res().expect("no partial res expected for precise capture arg") }); hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg { - hir_id: self.lower_node_id(*node_id), - ident: self.lower_ident(*ident), + hir_id: self.lower_node_id(*id), + ident: self.lower_ident(segment.ident), res: self.lower_res(res), }) } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index db9633bb1cbb7..9690994097238 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1156,7 +1156,7 @@ impl<'a> State<'a> { self.word("use"); self.word("<"); self.commasep(Inconsistent, precise_capturing_args, |s, arg| match arg { - ast::PreciseCapturingArg::Arg(a, _) => s.print_ident(*a), + ast::PreciseCapturingArg::Arg(p, _) => s.print_path(p, false, 0), ast::PreciseCapturingArg::Lifetime(lt) => s.print_lifetime(*lt), }); self.word(">") diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 4ac2965bd5f0c..bc6583444c33c 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -39,6 +39,9 @@ hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit wh hir_analysis_bad_precise_capture = expected {$kind} parameter in `use<...>` precise captures list, found {$found} +hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias + .label = `Self` is not a generic argument, but an alias to the type of the {$what} + hir_analysis_cannot_capture_late_bound_const = cannot capture late-bound const parameter in {$what} .label = parameter defined here diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index fb3713140ffed..78c2665fd1f3f 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -583,9 +583,19 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { self.resolve_type_ref(def_id.expect_local(), param.hir_id); } Res::Err => {} - _ => { - // This is handled in resolve - self.tcx.dcx().delayed_bug(format!("parameter should have been resolved")); + Res::SelfTyAlias { alias_to, .. } => { + self.tcx.dcx().emit_err(errors::PreciseCaptureSelfAlias { + span: param.ident.span, + self_span: self.tcx.def_span(alias_to), + what: self.tcx.def_descr(alias_to), + }); + } + res => { + self.tcx.dcx().emit_err(errors::BadPreciseCapture { + span: param.ident.span, + kind: "type or const", + found: res.descr().to_string(), + }); } }, } diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs index e2eb9c72bf235..520bf1d9f404d 100644 --- a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs +++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs @@ -32,6 +32,16 @@ pub struct BadPreciseCapture { pub found: String, } +#[derive(Diagnostic)] +#[diag(hir_analysis_precise_capture_self_alias)] +pub struct PreciseCaptureSelfAlias { + #[primary_span] + pub span: Span, + #[label] + pub self_span: Span, + pub what: &'static str, +} + #[derive(Diagnostic)] #[diag(hir_analysis_duplicate_precise_capture)] pub struct DuplicatePreciseCapture { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 91f4823f65d4d..f78a039394b84 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -697,11 +697,14 @@ impl<'a> Parser<'a> { if self_.check_keyword(kw::SelfUpper) { self_.bump(); Ok(PreciseCapturingArg::Arg( - self_.prev_token.ident().unwrap().0, + ast::Path::from_ident(self_.prev_token.ident().unwrap().0), DUMMY_NODE_ID, )) } else if self_.check_ident() { - Ok(PreciseCapturingArg::Arg(self_.parse_ident().unwrap(), DUMMY_NODE_ID)) + Ok(PreciseCapturingArg::Arg( + ast::Path::from_ident(self_.parse_ident()?), + DUMMY_NODE_ID, + )) } else if self_.check_lifetime() { Ok(PreciseCapturingArg::Lifetime(self_.expect_lifetime())) } else { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 7203adab135aa..64434412886b3 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1056,64 +1056,22 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, match arg { // Lower the lifetime regularly; we'll resolve the lifetime and check // it's a parameter later on in HIR lowering. - PreciseCapturingArg::Lifetime(_) => visit::walk_precise_capturing_arg(self, arg), + PreciseCapturingArg::Lifetime(_) => {} - PreciseCapturingArg::Arg(ident, node_id) => { - let ident = ident.normalize_to_macros_2_0(); - 'found: { - for (rib_t, rib_v) in - std::iter::zip(&self.ribs.type_ns, &self.ribs.value_ns).rev() - { - if let Some(res) = rib_t.bindings.get(&ident).or(rib_v.bindings.get(&ident)) - { - self.r.record_partial_res(*node_id, PartialRes::new(*res)); - - // Validate that this is a parameter - match res { - Res::Def(DefKind::TyParam | DefKind::ConstParam, _) - | Res::SelfTyParam { .. } => {} - Res::SelfTyAlias { .. } => { - self.report_error( - ident.span, - ResolutionError::FailedToResolve { - segment: Some(ident.name), - label: "`Self` cannot be captured because it is not a type parameter".to_string(), - suggestion: None, - module: None, - }, - ); - } - _ => { - self.report_error( - ident.span, - ResolutionError::FailedToResolve { - segment: Some(ident.name), - label: format!( - "expected type or const parameter, found {}", - res.descr() - ), - suggestion: None, - module: None, - }, - ); - } - } - - break 'found; - } - } - self.report_error( - ident.span, - ResolutionError::FailedToResolve { - segment: Some(ident.name), - label: "could not find type or const parameter".to_string(), - suggestion: None, - module: None, - }, - ); + PreciseCapturingArg::Arg(path, id) => { + let mut check_ns = |ns| { + self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns).is_some() + }; + // Like `Ty::Param`, we try resolving this as both a const and a type. + if !check_ns(TypeNS) && check_ns(ValueNS) { + self.smart_resolve_path(*id, &None, path, PathSource::Expr(None)); + } else { + self.smart_resolve_path(*id, &None, path, PathSource::Type); } } } + + visit::walk_precise_capturing_arg(self, arg) } fn visit_generics(&mut self, generics: &'ast Generics) { diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.rs b/tests/ui/impl-trait/precise-capturing/bad-params.rs index c600fa96f794f..195bd1b67aebd 100644 --- a/tests/ui/impl-trait/precise-capturing/bad-params.rs +++ b/tests/ui/impl-trait/precise-capturing/bad-params.rs @@ -2,15 +2,15 @@ //~^ WARN the feature `precise_capturing` is incomplete fn missing() -> impl use Sized {} -//~^ ERROR could not find type or const parameter +//~^ ERROR cannot find type `T` in this scope fn missing_self() -> impl use Sized {} -//~^ ERROR could not find type or const parameter +//~^ ERROR cannot find type `Self` in this scope struct MyType; impl MyType { fn self_is_not_param() -> impl use Sized {} - //~^ ERROR `Self` cannot be captured because it is not a type parameter + //~^ ERROR `Self` can't be captured in `use<...>` precise captures list, since it is an alias } fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.stderr b/tests/ui/impl-trait/precise-capturing/bad-params.stderr index bb5fe76ae3e3f..09694a800c41c 100644 --- a/tests/ui/impl-trait/precise-capturing/bad-params.stderr +++ b/tests/ui/impl-trait/precise-capturing/bad-params.stderr @@ -1,20 +1,21 @@ -error[E0433]: failed to resolve: could not find type or const parameter +error[E0412]: cannot find type `T` in this scope --> $DIR/bad-params.rs:4:26 | LL | fn missing() -> impl use Sized {} - | ^ could not find type or const parameter + | ^ not found in this scope + | +help: you might be missing a type parameter + | +LL | fn missing() -> impl use Sized {} + | +++ -error[E0433]: failed to resolve: could not find type or const parameter +error[E0411]: cannot find type `Self` in this scope --> $DIR/bad-params.rs:7:31 | LL | fn missing_self() -> impl use Sized {} - | ^^^^ could not find type or const parameter - -error[E0433]: failed to resolve: `Self` cannot be captured because it is not a type parameter - --> $DIR/bad-params.rs:12:40 - | -LL | fn self_is_not_param() -> impl use Sized {} - | ^^^^ `Self` cannot be captured because it is not a type parameter + | ------------ ^^^^ `Self` is only available in impls, traits, and type definitions + | | + | `Self` not allowed in a function warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/bad-params.rs:1:12 @@ -25,6 +26,15 @@ LL | #![feature(precise_capturing)] = note: see issue #123432 for more information = note: `#[warn(incomplete_features)]` on by default +error: `Self` can't be captured in `use<...>` precise captures list, since it is an alias + --> $DIR/bad-params.rs:12:40 + | +LL | impl MyType { + | ----------- `Self` is not a generic argument, but an alias to the type of the implementation +LL | fn self_is_not_param() -> impl use Sized {} + | ^^^^ + error: aborting due to 3 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0433`. +Some errors have detailed explanations: E0411, E0412. +For more information about an error, try `rustc --explain E0411`. From ac7651ccaf7447e695bf505916463927b78e433f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 4 Apr 2024 20:36:18 -0400 Subject: [PATCH 10/11] More polishing --- compiler/rustc_hir/src/hir.rs | 5 +++++ compiler/rustc_hir_analysis/messages.ftl | 6 +++--- compiler/rustc_hir_analysis/src/check/check.rs | 8 ++++++++ .../nice_region_error/static_impl_trait.rs | 1 + compiler/rustc_parse/src/parser/ty.rs | 4 +++- compiler/rustc_resolve/src/late.rs | 6 ++++++ tests/ui/impl-trait/precise-capturing/apit.rs | 7 +++++++ .../ui/impl-trait/precise-capturing/apit.stderr | 17 +++++++++++++++++ .../impl-trait/precise-capturing/bad-params.rs | 3 +++ .../precise-capturing/bad-params.stderr | 8 +++++++- tests/ui/impl-trait/precise-capturing/elided.rs | 8 ++++++++ .../impl-trait/precise-capturing/elided.stderr | 11 +++++++++++ 12 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 tests/ui/impl-trait/precise-capturing/apit.rs create mode 100644 tests/ui/impl-trait/precise-capturing/apit.stderr create mode 100644 tests/ui/impl-trait/precise-capturing/elided.rs create mode 100644 tests/ui/impl-trait/precise-capturing/elided.stderr diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 00b1ea061cc73..b39056d8690c9 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2568,6 +2568,11 @@ pub enum PreciseCapturingArg<'hir> { Param(PreciseCapturingNonLifetimeArg), } +/// We need to have a [`Node`] for the [`HirId`] that we attach the type/const param +/// resolution to. Lifetimes don't have this problem, and for them, it's actually +/// kind of detrimental to use a custom node type versus just using [`Lifetime`], +/// since resolve_bound_vars operates on `Lifetime`s. +// FIXME(precise_capturing): Investigate storing this as a path instead? #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct PreciseCapturingNonLifetimeArg { pub hir_id: HirId, diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index bc6583444c33c..0ff78ebff9953 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -39,9 +39,6 @@ hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit wh hir_analysis_bad_precise_capture = expected {$kind} parameter in `use<...>` precise captures list, found {$found} -hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias - .label = `Self` is not a generic argument, but an alias to the type of the {$what} - hir_analysis_cannot_capture_late_bound_const = cannot capture late-bound const parameter in {$what} .label = parameter defined here @@ -374,6 +371,9 @@ hir_analysis_pattern_type_wild_pat = "wildcard patterns are not permitted for pa hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind} .label = not allowed in type signatures +hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias + .label = `Self` is not a generic argument, but an alias to the type of the {$what} + hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}` .note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}` diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 1e8cd50ca0de1..8c85d13650b40 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -475,6 +475,14 @@ fn sanity_check_found_hidden_type<'tcx>( } } +/// Check that the opaque's precise captures list is valid (if present). +/// We check this for regular `impl Trait`s and also RPITITs, even though the latter +/// are technically GATs. +/// +/// This function is responsible for: +/// 1. Checking that all type/const params are mention in the captures list. +/// 2. Checking that all lifetimes that are implicitly captured are mentioned. +/// 3. Asserting that all parameters mentioned in the captures list are invariant. fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId) { let hir::OpaqueTy { precise_capturing_args, .. } = *tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index fe70b631cdb82..0bbabefaf9540 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -283,6 +283,7 @@ pub fn suggest_new_region_bound( continue; } match fn_return.kind { + // FIXME(precise_captures): Suggest adding to `use<...>` list instead. TyKind::OpaqueDef(item_id, _, _) => { let item = tcx.hir().item(item_id); let ItemKind::OpaqueTy(opaque) = &item.kind else { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index f78a039394b84..a6195fa769383 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -669,7 +669,9 @@ impl<'a> Parser<'a> { }) } - // parse precise captures, if any. + // parse precise captures, if any. This is `use<'lt, 'lt, P, P>`; a list of + // lifetimes and ident params (including SelfUpper). These are validated later + // for order, duplication, and whether they actually reference params. let precise_capturing = if self.eat_keyword(kw::Use) { let use_span = self.prev_token.span; self.psess.gated_spans.gate(sym::precise_capturing, use_span); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 64434412886b3..ba1391bc378af 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1059,6 +1059,12 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, PreciseCapturingArg::Lifetime(_) => {} PreciseCapturingArg::Arg(path, id) => { + // we want `impl use` to try to resolve `C` as both a type parameter or + // a const parameter. Since the resolver specifically doesn't allow having + // two generic params with the same name, even if they're a different namespace, + // it doesn't really matter which we try resolving first, but just like + // `Ty::Param` we just fall back to the value namespace only if it's missing + // from the type namespace. let mut check_ns = |ns| { self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns).is_some() }; diff --git a/tests/ui/impl-trait/precise-capturing/apit.rs b/tests/ui/impl-trait/precise-capturing/apit.rs new file mode 100644 index 0000000000000..efcac9ebb0b84 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/apit.rs @@ -0,0 +1,7 @@ +#![feature(precise_capturing)] +//~^ WARN the feature `precise_capturing` is incomplete + +fn hello(_: impl use<> Sized) {} +//~^ ERROR `use<...>` precise capturing syntax not allowed on argument-position `impl Trait` + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/apit.stderr b/tests/ui/impl-trait/precise-capturing/apit.stderr new file mode 100644 index 0000000000000..36bf80d9e2f96 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/apit.stderr @@ -0,0 +1,17 @@ +warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/apit.rs:1:12 + | +LL | #![feature(precise_capturing)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #123432 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: `use<...>` precise capturing syntax not allowed on argument-position `impl Trait` + --> $DIR/apit.rs:4:18 + | +LL | fn hello(_: impl use<> Sized) {} + | ^^^ + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.rs b/tests/ui/impl-trait/precise-capturing/bad-params.rs index 195bd1b67aebd..7970d49bf7c07 100644 --- a/tests/ui/impl-trait/precise-capturing/bad-params.rs +++ b/tests/ui/impl-trait/precise-capturing/bad-params.rs @@ -13,4 +13,7 @@ impl MyType { //~^ ERROR `Self` can't be captured in `use<...>` precise captures list, since it is an alias } +fn hello() -> impl use Sized {} +//~^ ERROR expected type or const parameter in `use<...>` precise captures list, found function + fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.stderr b/tests/ui/impl-trait/precise-capturing/bad-params.stderr index 09694a800c41c..27bf05302f9b1 100644 --- a/tests/ui/impl-trait/precise-capturing/bad-params.stderr +++ b/tests/ui/impl-trait/precise-capturing/bad-params.stderr @@ -34,7 +34,13 @@ LL | impl MyType { LL | fn self_is_not_param() -> impl use Sized {} | ^^^^ -error: aborting due to 3 previous errors; 1 warning emitted +error: expected type or const parameter in `use<...>` precise captures list, found function + --> $DIR/bad-params.rs:16:24 + | +LL | fn hello() -> impl use Sized {} + | ^^^^^ + +error: aborting due to 4 previous errors; 1 warning emitted Some errors have detailed explanations: E0411, E0412. For more information about an error, try `rustc --explain E0411`. diff --git a/tests/ui/impl-trait/precise-capturing/elided.rs b/tests/ui/impl-trait/precise-capturing/elided.rs new file mode 100644 index 0000000000000..de80e8a5d581b --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/elided.rs @@ -0,0 +1,8 @@ +//@ check-pass + +#![feature(precise_capturing)] +//~^ WARN the feature `precise_capturing` is incomplete + +fn elided(x: &()) -> impl use<'_> Sized { x } + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/elided.stderr b/tests/ui/impl-trait/precise-capturing/elided.stderr new file mode 100644 index 0000000000000..38da0828de972 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/elided.stderr @@ -0,0 +1,11 @@ +warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/elided.rs:3:12 + | +LL | #![feature(precise_capturing)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #123432 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + From 8ddd2805de6ad8200868befc6ae703243fac3a46 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 4 Apr 2024 20:52:56 -0400 Subject: [PATCH 11/11] Rustfmt, clippy --- src/tools/clippy/clippy_utils/src/ast_utils.rs | 11 ++++++++++- src/tools/rustfmt/src/types.rs | 9 +++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index f594a40ff59ad..9f0bd4ea7e2e2 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -709,7 +709,8 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool { (Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)), (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp), (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, eq_generic_bound), - (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, eq_generic_bound), + (ImplTrait(_, lg, lc), ImplTrait(_, rg, rc)) => + over(lg, rg, eq_generic_bound) && both(lc, rc, |lc, rc| over(lc.0.as_slice(), rc.0.as_slice(), eq_precise_capture)), (Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), _ => false, @@ -770,6 +771,14 @@ pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool { } } +pub fn eq_precise_capture(l: &PreciseCapturingArg, r: &PreciseCapturingArg) -> bool { + match (l, r) { + (PreciseCapturingArg::Lifetime(l), PreciseCapturingArg::Lifetime(r)) => l.ident == r.ident, + (PreciseCapturingArg::Arg(l, _), PreciseCapturingArg::Arg(r, _)) => l.segments[0].ident == r.segments[0].ident, + _ => false, + } +} + fn eq_term(l: &Term, r: &Term) -> bool { match (l, r) { (Term::Ty(l), Term::Ty(r)) => eq_ty(l, r), diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 10a87f6e69850..fe2d28ae1b9df 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -843,7 +843,11 @@ impl Rewrite for ast::Ty { rewrite_macro(mac, None, context, shape, MacroPosition::Expression) } ast::TyKind::ImplicitSelf => Some(String::from("")), - ast::TyKind::ImplTrait(_, ref it) => { + ast::TyKind::ImplTrait(_, ref it, ref captures) => { + // FIXME(precise_capturing): Implement formatting. + if captures.is_some() { + return None; + } // Empty trait is not a parser error. if it.is_empty() { return Some("impl".to_owned()); @@ -1106,7 +1110,8 @@ fn join_bounds_inner( pub(crate) fn opaque_ty(ty: &Option>) -> Option<&ast::GenericBounds> { ty.as_ref().and_then(|t| match &t.kind { - ast::TyKind::ImplTrait(_, bounds) => Some(bounds), + // FIXME(precise_capturing): Implement support here + ast::TyKind::ImplTrait(_, bounds, _) => Some(bounds), _ => None, }) }