diff --git a/compiler/rustc_ast_passes/src/diagnostics.rs b/compiler/rustc_ast_passes/src/diagnostics.rs index db006e50aaa31..0ef20e112ed0b 100644 --- a/compiler/rustc_ast_passes/src/diagnostics.rs +++ b/compiler/rustc_ast_passes/src/diagnostics.rs @@ -1257,3 +1257,35 @@ pub(crate) struct VarargsWithoutPattern { #[primary_span] pub span: Span, } + +#[derive(Subdiagnostic)] +pub(crate) enum ClosureLifetimeBinderBindingTypeSugg { + #[multipart_suggestion( + "consider setting the binding type instead", + applicability = "machine-applicable", + style = "verbose" + )] + MachineApplicable { + #[suggestion_part(code = ": {ty}")] + binding: Span, + ty: String, + #[suggestion_part(code = "{closure}")] + closure_header: Span, + closure: String, + }, + /// Used when the body references other simple paths: they may be captures (or free items). + /// Without name resolution we can't tell, so rustfix must not auto-apply. + #[multipart_suggestion( + "consider setting the binding type instead", + applicability = "maybe-incorrect", + style = "verbose" + )] + MaybeIncorrect { + #[suggestion_part(code = ": {ty}")] + binding: Span, + ty: String, + #[suggestion_part(code = "{closure}")] + closure_header: Span, + closure: String, + }, +} diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 348e179018002..a7fc717064690 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,13 +1,18 @@ use rustc_ast::visit::{self, AssocCtxt, FnKind, Visitor}; -use rustc_ast::{self as ast, AttrVec, GenericBound, NodeId, PatKind, attr, token}; +use rustc_ast::{ + self as ast, AttrVec, BindingMode, ByRef, GenericBound, GenericParamKind, NodeId, PatKind, + attr, token, +}; +use rustc_ast_pretty::pprust; use rustc_attr_parsing::AttributeParser; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::msg; use rustc_feature::Features; use rustc_hir::Attribute; use rustc_hir::attrs::AttributeKind; use rustc_session::Session; use rustc_session::diagnostics::{feature_err, feature_warn}; -use rustc_span::{Span, Spanned, Symbol, sym}; +use rustc_span::{Ident, Span, Spanned, Symbol, sym}; use crate::diagnostics; @@ -48,7 +53,13 @@ macro_rules! gate_multi { } pub fn check_attribute(attr: &ast::Attribute, sess: &Session, features: &Features) { - PostExpansionVisitor { sess, features }.visit_attribute(attr) + PostExpansionVisitor { + sess, + features, + let_binding: None, + handled_closure_lifetime_binders: FxHashSet::default(), + } + .visit_attribute(attr) } struct PostExpansionVisitor<'a> { @@ -56,6 +67,14 @@ struct PostExpansionVisitor<'a> { // `sess` contains a `Features`, but this might not be that one. features: &'a Features, + + /// Set while visiting the initializer of a `let` binding whose RHS is directly a closure. + /// Used to suggest moving `for<...>` binders onto the binding's type. + let_binding: Option<&'a ast::Local>, + + /// Binder spans for which we already emitted the `closure_lifetime_binder` gate while walking + /// the live AST. Remaining pre-expansion spans (e.g. under `#[cfg(false)]`) are gated later. + handled_closure_lifetime_binders: FxHashSet, } // ----------------------------------------------------------------------------- @@ -68,6 +87,34 @@ struct PostExpansionVisitor<'a> { // Instead, register a pre-expansion feature gate using `gate_all` in fn `check_crate`. impl<'a> PostExpansionVisitor<'a> { + /// Gate `for<...>` binders on closures, suggesting a `fn` pointer binding type when possible. + fn gate_closure_lifetime_binder(&mut self, closure: &ast::Closure, binder_span: Span) { + self.handled_closure_lifetime_binders.insert(binder_span); + + if self.features.closure_lifetime_binder() + || binder_span.allows_unstable(sym::closure_lifetime_binder) + { + return; + } + + let mut err = feature_err( + self.sess, + sym::closure_lifetime_binder, + binder_span, + "`for<...>` binders for closures are experimental", + ); + + if let Some(sugg) = + closure_lifetime_binder_binding_type_sugg(self.sess, self.let_binding, closure) + { + err.subdiagnostic(sugg); + } else { + err.help("consider removing `for<...>`"); + } + + err.emit(); + } + /// Feature gate `impl Trait` inside `type Alias = $type_expr;`. fn check_impl_trait(&self, ty: &ast::Ty, in_associated_ty: bool) { struct ImplTraitVisitor<'a> { @@ -307,8 +354,32 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { visit::walk_generic_args(self, args); } + fn visit_local(&mut self, local: &'a ast::Local) { + // Only track direct `let pat = for<'a> |...| ...` inits; parenthesized or otherwise + // wrapped closures fall back to the simpler help. + if let Some(init) = local.kind.init() + && matches!(init.kind, ast::ExprKind::Closure(_)) + { + let prev = self.let_binding.replace(local); + visit::walk_local(self, local); + self.let_binding = prev; + } else { + visit::walk_local(self, local); + } + } + fn visit_expr(&mut self, e: &'a ast::Expr) { - match e.kind { + match &e.kind { + ast::ExprKind::Closure(closure) => { + if let ast::ClosureBinder::For { span, .. } = &closure.binder { + self.gate_closure_lifetime_binder(closure, *span); + } + // Nested expressions inside the closure are not the `let` initializer. + let prev = self.let_binding.take(); + visit::walk_expr(self, e); + self.let_binding = prev; + return; + } ast::ExprKind::TryBlock(_, None) => { // `try { ... }` is old and is only gated post-expansion here. gate!(self, try_blocks, e.span, "`try` expression is experimental"); @@ -320,14 +391,14 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { kind: token::LitKind::Float | token::LitKind::Integer, suffix, .. - }) => match suffix { + }) => match *suffix { Some(sym::f16) => { gate!(self, f16, e.span, "the type `f16` is unstable") } Some(sym::f128) => { gate!(self, f128, e.span, "the type `f128` is unstable") } - _ => (), + _ => {} }, _ => {} } @@ -440,7 +511,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { check_new_solver_banned_features(sess, features); check_features_requiring_new_solver(sess, features); - let mut visitor = PostExpansionVisitor { sess, features }; + let mut visitor = PostExpansionVisitor { + sess, + features, + let_binding: None, + handled_closure_lifetime_binders: FxHashSet::default(), + }; // ----------------------------------------------------------------------------- // PRE-EXPANSION FEATURE GATES FOR UNSTABLE SYNTAX @@ -503,11 +579,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { "`async` trait bounds are unstable", "use the desugared name of the async trait, such as `AsyncFn`" ); - gate_all!( - closure_lifetime_binder, - "`for<...>` binders for closures are experimental", - "consider removing `for<...>`" - ); + // `closure_lifetime_binder` is gated in `PostExpansionVisitor` (with a richer suggestion when + // possible). Spans not seen there — notably under `#[cfg(false)]` — are handled after the walk. gate_all!( half_open_range_patterns_in_slices, "half-open range patterns in slices are unstable" @@ -625,6 +698,301 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { // ----------------------------------------------------------------------------- visit::walk_crate(&mut visitor, krate); + + // Reject `for<...>` closure binders that never reached the AST walk (e.g. `#[cfg(false)]`). + if !visitor.features.closure_lifetime_binder() { + for &span in spans.get(&sym::closure_lifetime_binder).into_flat_iter() { + if span.allows_unstable(sym::closure_lifetime_binder) + || visitor.handled_closure_lifetime_binders.contains(&span) + { + continue; + } + feature_err( + sess, + sym::closure_lifetime_binder, + span, + "`for<...>` binders for closures are experimental", + ) + .with_help("consider removing `for<...>`") + .emit(); + } + } +} + +/// Build a suggestion rewriting +/// `let cl = for<'a> |x: &'a T| -> U { ... }` into +/// `let cl: for<'a> fn(&'a T) -> U = |x| { ... }` when that is a reasonable alternative. +fn closure_lifetime_binder_binding_type_sugg( + sess: &Session, + local: Option<&ast::Local>, + closure: &ast::Closure, +) -> Option { + let local = local?; + if local.ty.is_some() { + return None; + } + // Only by-value `let ident = ...` / `let mut ident = ...` bindings. + if !matches!(&local.pat.kind, PatKind::Ident(BindingMode(ByRef::No, _), _, None)) { + return None; + } + + // Explicit `move`/`use`/`async`/`const`/`static` closures are not `fn` pointers. + if !matches!(closure.capture_clause, ast::CaptureBy::Ref) + || closure.coroutine_kind.is_some() + || matches!(closure.constness, ast::Const::Yes(_)) + || matches!(closure.movability, ast::Movability::Static) + { + return None; + } + + let ast::ClosureBinder::For { span: binder_span, generic_params } = &closure.binder else { + return None; + }; + + // `for` / `for<'a: 'static>` are not valid on `fn` pointer types. + if !generic_params + .iter() + .all(|param| matches!(param.kind, GenericParamKind::Lifetime) && param.bounds.is_empty()) + { + return None; + } + + // Need fully explicit parameter and return types to form a useful `fn` type. A top-level or + // nested `_` (e.g. `-> _`, `&'a _`) must not be copied into a MachineApplicable suggestion. + let ast::FnRetTy::Ty(ret_ty) = &closure.fn_decl.output else { + return None; + }; + if ty_contains_infer(ret_ty) + || closure.fn_decl.inputs.iter().any(|param| ty_contains_infer(¶m.ty)) + { + return None; + } + + // Only by-value binding patterns (and `_`) can be rewritten safely. + if !closure.fn_decl.inputs.iter().all(|param| { + matches!( + ¶m.pat.kind, + PatKind::Wild | PatKind::Ident(BindingMode(ByRef::No, _), _, None) + ) + }) { + return None; + } + + // `pprust::pat_to_string` drops parameter attributes; don't emit a lossy rewrite. + if closure.fn_decl.inputs.iter().any(|param| !param.attrs.is_empty()) { + return None; + } + + // Don't rewrite macro-expanded closures; hygiene makes capture analysis unreliable and the + // suggestion would point into the macro definition. + if binder_span.from_expansion() || closure.fn_decl_span.from_expansion() { + return None; + } + + let binder = sess.source_map().span_to_snippet(*binder_span).ok()?; + let inputs: String = closure + .fn_decl + .inputs + .iter() + .map(|param| pprust::ty_to_string(¶m.ty)) + .intersperse(", ".to_string()) + .collect(); + let ty = format!("{binder} fn({inputs}) -> {}", pprust::ty_to_string(ret_ty)); + + let closure_pats: String = closure + .fn_decl + .inputs + .iter() + .map(|param| pprust::pat_to_string(¶m.pat)) + .intersperse(", ".to_string()) + .collect(); + + let binding = local.pat.span.shrink_to_hi(); + let closure_header = binder_span.to(closure.fn_decl_span); + let closure_code = format!("|{closure_pats}|"); + + // `CaptureBy::Ref` only means no `move`/`use`. Without name resolution, any other simple + // path may be an env capture (including uppercase locals) or a free item. Offer the rewrite + // only as maybe-incorrect in that case so rustfix won't auto-apply a breaking change. + // Paths bound locally in the body (e.g. `let n = ...; n`) are fine for `fn` pointers. + if closure_body_has_free_simple_path(closure) { + Some(diagnostics::ClosureLifetimeBinderBindingTypeSugg::MaybeIncorrect { + binding, + ty, + closure_header, + closure: closure_code, + }) + } else { + Some(diagnostics::ClosureLifetimeBinderBindingTypeSugg::MachineApplicable { + binding, + ty, + closure_header, + closure: closure_code, + }) + } +} + +/// Returns true if `ty` contains any `_` inference placeholder, including nested forms like +/// `&'a _` or `(_, u8)`. +fn ty_contains_infer(ty: &ast::Ty) -> bool { + struct InferVisitor { + found: bool, + } + + impl<'a> Visitor<'a> for InferVisitor { + fn visit_ty(&mut self, ty: &'a ast::Ty) { + if self.found { + return; + } + if matches!(ty.kind, ast::TyKind::Infer) { + self.found = true; + return; + } + visit::walk_ty(self, ty); + } + } + + let mut visitor = InferVisitor { found: false }; + visitor.visit_ty(ty); + visitor.found +} + +/// Returns true if the closure body contains a single-segment value path that is neither a +/// parameter nor a name bound inside the body. +/// +/// Locals are tracked as hygiene-aware [`Ident`]s (name + `SyntaxContext`) so a macro parameter +/// `$x` is not confused with a closure parameter `x` that happens to share a spelling. +/// +/// This is intentionally AST-only and conservative: free functions and constructors look the same +/// as captures here. Callers should downgrade suggestion applicability when this is true. +fn closure_body_has_free_simple_path(closure: &ast::Closure) -> bool { + let mut known_locals = FxHashSet::default(); + for param in &closure.fn_decl.inputs { + if let PatKind::Ident(_, ident, _) = param.pat.kind { + known_locals.insert(ident); + } + } + + struct FreePathVisitor { + known_locals: FxHashSet, + has_free_path: bool, + } + + impl FreePathVisitor { + fn bind_pat(&mut self, pat: &ast::Pat) { + match &pat.kind { + PatKind::Ident(_, ident, sub) => { + self.known_locals.insert(*ident); + if let Some(sub) = sub { + self.bind_pat(sub); + } + } + PatKind::Tuple(pats) + | PatKind::TupleStruct(_, _, pats) + | PatKind::Slice(pats) + | PatKind::Or(pats) => { + for pat in pats { + self.bind_pat(pat); + } + } + PatKind::Struct(_, _, fields, _) => { + for field in fields { + self.bind_pat(&field.pat); + } + } + PatKind::Box(pat) + | PatKind::Deref(pat) + | PatKind::Ref(pat, ..) + | PatKind::Paren(pat) => self.bind_pat(pat), + _ => {} + } + } + } + + impl<'a> Visitor<'a> for FreePathVisitor { + fn visit_ty(&mut self, _: &'a ast::Ty) { + // Paths in types are not value captures. + } + + fn visit_block(&mut self, block: &'a ast::Block) { + let old = self.known_locals.clone(); + visit::walk_block(self, block); + self.known_locals = old; + } + + fn visit_local(&mut self, local: &'a ast::Local) { + // Visit the initializer (and `else` block) before binding names from the pattern. + // Bindings are not in scope in the `else` block. + if let Some((init, els)) = local.kind.init_else_opt() { + self.visit_expr(init); + if let Some(els) = els { + // Must go through `visit_block` so locals declared in the `else` block do not + // leak into `known_locals` for code after the `let else`. + self.visit_block(els); + } + } + self.bind_pat(&local.pat); + } + + fn visit_arm(&mut self, arm: &'a ast::Arm) { + let old = self.known_locals.clone(); + self.bind_pat(&arm.pat); + visit::walk_arm(self, arm); + self.known_locals = old; + } + + fn visit_expr(&mut self, expr: &'a ast::Expr) { + if self.has_free_path { + return; + } + if let ast::ExprKind::Path(None, path) = &expr.kind + && let [seg] = path.segments.as_slice() + && seg.args.is_none() + && !self.known_locals.contains(&seg.ident) + { + self.has_free_path = true; + return; + } + match &expr.kind { + // `let` bindings from let-chains / `if let` / `while let` conditions. The enclosing + // `If` / `While` arms restore `known_locals` so these do not escape that scope. + ast::ExprKind::Let(pat, scrutinee, _, _) => { + self.visit_expr(scrutinee); + self.bind_pat(pat); + } + // `if`/`if let`/`if` let-chains: condition bindings are in scope for the then + // branch only, not the else branch or anything after the `if`. + ast::ExprKind::If(cond, then_block, else_opt) => { + let old = self.known_locals.clone(); + self.visit_expr(cond); + self.visit_block(then_block); + self.known_locals = old; + if let Some(els) = else_opt { + self.visit_expr(els); + } + } + // `while`/`while let`: condition bindings are in scope for the loop body only. + ast::ExprKind::While(cond, body, _) => { + let old = self.known_locals.clone(); + self.visit_expr(cond); + self.visit_block(body); + self.known_locals = old; + } + ast::ExprKind::ForLoop(for_loop) => { + self.visit_expr(&for_loop.iter); + let old = self.known_locals.clone(); + self.bind_pat(&for_loop.pat); + self.visit_block(&for_loop.body); + self.known_locals = old; + } + _ => visit::walk_expr(self, expr), + } + } + } + + let mut visitor = FreePathVisitor { known_locals, has_free_path: false }; + visitor.visit_expr(&closure.body); + visitor.has_free_path } fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 34044e72ab92b..1da2fe771136b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2462,6 +2462,8 @@ impl<'a> Parser<'a> { let (bound_vars, _) = self.parse_higher_ranked_binder()?; let span = lo.to(self.prev_token.span); + // Pre-expansion gate so `#[cfg(false)]` code is still rejected. The post-expansion + // visitor may replace this with a richer diagnostic when the AST is available. self.psess.gated_spans.gate(sym::closure_lifetime_binder, span); ClosureBinder::For { span, generic_params: bound_vars } diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.rs b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.rs new file mode 100644 index 0000000000000..c1cc4cc03aa18 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.rs @@ -0,0 +1,20 @@ +//@ compile-flags: --error-format=json +//@ forbid-output: MachineApplicable +//@ forbid-output: MaybeIncorrect + +// Macro-expanded closures must not get a structured fn-pointer rewrite (hygiene + spans point +// into the macro). Expect only the simple help. + +macro_rules! make { + ($x:ident) => { + for<'a> |x: &'a i32| -> i32 { *x + $x } + //~^ ERROR `for<...>` binders for closures are experimental + //~| HELP add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + //~| HELP consider removing `for<...>` + }; +} + +fn main() { + let x = 1; + let _cl = make!(x); +} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.stderr b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.stderr new file mode 100644 index 0000000000000..1f960c02be929 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-macro.stderr @@ -0,0 +1,43 @@ +{"$message_type":"diagnostic","message":"`for<...>` binders for closures are experimental","code":{"code":"E0658","explanation":"An unstable feature was used. + +Erroneous code example: + +```compile_fail,E0658 +use std::intrinsics; // error: use of unstable library feature `core_intrinsics` +``` + +If you're using a stable or a beta version of rustc, you won't be able to use +any unstable features. In order to do so, please switch to a nightly version of +rustc (by using [rustup]). + +If you're using a nightly version of rustc, just add the corresponding feature +to be able to use it: + +``` +#![feature(core_intrinsics)] + +use std::intrinsics; // ok! +``` + +[rustup]: https://rust-lang.github.io/rustup/concepts/channels.html +"},"level":"error","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-macro.rs","byte_start":304,"byte_end":311,"line_start":10,"line_end":10,"column_start":9,"column_end":16,"is_primary":true,"text":[{"text":" for<'a> |x: &'a i32| -> i32 { *x + $x }","highlight_start":9,"highlight_end":16}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":{"span":{"file_name":"$DIR/feature-gate-closure_lifetime_binder-macro.rs","byte_start":605,"byte_end":613,"line_start":19,"line_end":19,"column_start":15,"column_end":23,"is_primary":false,"text":[{"text":" let _cl = make!(x);","highlight_start":15,"highlight_end":23}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},"macro_decl_name":"make!","def_site_span":{"file_name":"$DIR/feature-gate-closure_lifetime_binder-macro.rs","byte_start":256,"byte_end":273,"line_start":8,"line_end":8,"column_start":1,"column_end":18,"is_primary":false,"text":[{"text":"macro_rules! make {","highlight_start":1,"highlight_end":18}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}}}],"children":[{"message":"see issue #97362 for more information","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable","code":null,"level":"help","spans":[],"children":[],"rendered":null},{"message":"this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"consider removing `for<...>`","code":null,"level":"help","spans":[],"children":[],"rendered":null}],"rendered":"error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder-macro.rs:10:9 + | +LL | for<'a> |x: &'a i32| -> i32 { *x + $x } + | ^^^^^^^ +... +LL | let _cl = make!(x); + | -------- in this macro invocation + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + = note: this error originates in the macro `make` (in Nightly builds, run with -Z macro-backtrace for more info) + +"} +{"$message_type":"diagnostic","message":"aborting due to 1 previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 1 previous error + +"} +{"$message_type":"diagnostic","message":"For more information about this error, try `rustc --explain E0658`.","code":null,"level":"failure-note","spans":[],"children":[],"rendered":"For more information about this error, try `rustc --explain E0658`. +"} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.rs b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.rs new file mode 100644 index 0000000000000..8d7ab9cc2b54c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.rs @@ -0,0 +1,34 @@ +//@ edition: 2024 +//@ compile-flags: --error-format=json +//@ error-pattern: "suggestion_applicability":"MaybeIncorrect" + +// Capturing closures must not get a MachineApplicable rewrite. Cover plain captures, let-else +// leakage, and let-chain shadowing — all should report MaybeIncorrect in JSON. + +fn main() { + let y = 1; + let _capture = for<'a> |x: &'a i32| -> i32 { *x + y }; + //~^ ERROR `for<...>` binders for closures are experimental + + let let_else_env = 1; + let _let_else = for<'a> |x: &'a i32| -> i32 { + //~^ ERROR `for<...>` binders for closures are experimental + let Some(_) = None:: else { + let let_else_env = 0; + return let_else_env; + }; + *x + let_else_env + }; + + let chain_env = 1; + let _let_chain = for<'a> |x: &'a i32| -> i32 { + //~^ ERROR `for<...>` binders for closures are experimental + if let Some(chain_env) = None:: + && chain_env == 0 + { + 0 + } else { + *x + chain_env + } + }; +} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.stderr b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.stderr new file mode 100644 index 0000000000000..6cf49f6effdbd --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-maybe-incorrect.stderr @@ -0,0 +1,119 @@ +{"$message_type":"diagnostic","message":"`for<...>` binders for closures are experimental","code":{"code":"E0658","explanation":"An unstable feature was used. + +Erroneous code example: + +```compile_fail,E0658 +use std::intrinsics; // error: use of unstable library feature `core_intrinsics` +``` + +If you're using a stable or a beta version of rustc, you won't be able to use +any unstable features. In order to do so, please switch to a nightly version of +rustc (by using [rustup]). + +If you're using a nightly version of rustc, just add the corresponding feature +to be able to use it: + +``` +#![feature(core_intrinsics)] + +use std::intrinsics; // ok! +``` + +[rustup]: https://rust-lang.github.io/rustup/concepts/channels.html +"},"level":"error","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":345,"byte_end":352,"line_start":10,"line_end":10,"column_start":20,"column_end":27,"is_primary":true,"text":[{"text":" let _capture = for<'a> |x: &'a i32| -> i32 { *x + y };","highlight_start":20,"highlight_end":27}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"see issue #97362 for more information","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable","code":null,"level":"help","spans":[],"children":[],"rendered":null},{"message":"this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"consider setting the binding type instead","code":null,"level":"help","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":342,"byte_end":342,"line_start":10,"line_end":10,"column_start":17,"column_end":17,"is_primary":true,"text":[{"text":" let _capture = for<'a> |x: &'a i32| -> i32 { *x + y };","highlight_start":17,"highlight_end":17}],"label":null,"suggested_replacement":": for<'a> fn(&'a i32) -> i32","suggestion_applicability":"MaybeIncorrect","expansion":null},{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":345,"byte_end":372,"line_start":10,"line_end":10,"column_start":20,"column_end":47,"is_primary":true,"text":[{"text":" let _capture = for<'a> |x: &'a i32| -> i32 { *x + y };","highlight_start":20,"highlight_end":47}],"label":null,"suggested_replacement":"|x|","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs:10:20 + | +LL | let _capture = for<'a> |x: &'a i32| -> i32 { *x + y }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _capture = for<'a> |x: &'a i32| -> i32 { *x + y }; +LL + let _capture: for<'a> fn(&'a i32) -> i32 = |x| { *x + y }; + | + +"} +{"$message_type":"diagnostic","message":"`for<...>` binders for closures are experimental","code":{"code":"E0658","explanation":"An unstable feature was used. + +Erroneous code example: + +```compile_fail,E0658 +use std::intrinsics; // error: use of unstable library feature `core_intrinsics` +``` + +If you're using a stable or a beta version of rustc, you won't be able to use +any unstable features. In order to do so, please switch to a nightly version of +rustc (by using [rustup]). + +If you're using a nightly version of rustc, just add the corresponding feature +to be able to use it: + +``` +#![feature(core_intrinsics)] + +use std::intrinsics; // ok! +``` + +[rustup]: https://rust-lang.github.io/rustup/concepts/channels.html +"},"level":"error","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":496,"byte_end":503,"line_start":14,"line_end":14,"column_start":21,"column_end":28,"is_primary":true,"text":[{"text":" let _let_else = for<'a> |x: &'a i32| -> i32 {","highlight_start":21,"highlight_end":28}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"see issue #97362 for more information","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable","code":null,"level":"help","spans":[],"children":[],"rendered":null},{"message":"this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"consider setting the binding type instead","code":null,"level":"help","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":493,"byte_end":493,"line_start":14,"line_end":14,"column_start":18,"column_end":18,"is_primary":true,"text":[{"text":" let _let_else = for<'a> |x: &'a i32| -> i32 {","highlight_start":18,"highlight_end":18}],"label":null,"suggested_replacement":": for<'a> fn(&'a i32) -> i32","suggestion_applicability":"MaybeIncorrect","expansion":null},{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":496,"byte_end":523,"line_start":14,"line_end":14,"column_start":21,"column_end":48,"is_primary":true,"text":[{"text":" let _let_else = for<'a> |x: &'a i32| -> i32 {","highlight_start":21,"highlight_end":48}],"label":null,"suggested_replacement":"|x|","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs:14:21 + | +LL | let _let_else = for<'a> |x: &'a i32| -> i32 { + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _let_else = for<'a> |x: &'a i32| -> i32 { +LL + let _let_else: for<'a> fn(&'a i32) -> i32 = |x| { + | + +"} +{"$message_type":"diagnostic","message":"`for<...>` binders for closures are experimental","code":{"code":"E0658","explanation":"An unstable feature was used. + +Erroneous code example: + +```compile_fail,E0658 +use std::intrinsics; // error: use of unstable library feature `core_intrinsics` +``` + +If you're using a stable or a beta version of rustc, you won't be able to use +any unstable features. In order to do so, please switch to a nightly version of +rustc (by using [rustup]). + +If you're using a nightly version of rustc, just add the corresponding feature +to be able to use it: + +``` +#![feature(core_intrinsics)] + +use std::intrinsics; // ok! +``` + +[rustup]: https://rust-lang.github.io/rustup/concepts/channels.html +"},"level":"error","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":791,"byte_end":798,"line_start":24,"line_end":24,"column_start":22,"column_end":29,"is_primary":true,"text":[{"text":" let _let_chain = for<'a> |x: &'a i32| -> i32 {","highlight_start":22,"highlight_end":29}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"see issue #97362 for more information","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable","code":null,"level":"help","spans":[],"children":[],"rendered":null},{"message":"this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"consider setting the binding type instead","code":null,"level":"help","spans":[{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":788,"byte_end":788,"line_start":24,"line_end":24,"column_start":19,"column_end":19,"is_primary":true,"text":[{"text":" let _let_chain = for<'a> |x: &'a i32| -> i32 {","highlight_start":19,"highlight_end":19}],"label":null,"suggested_replacement":": for<'a> fn(&'a i32) -> i32","suggestion_applicability":"MaybeIncorrect","expansion":null},{"file_name":"$DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs","byte_start":791,"byte_end":818,"line_start":24,"line_end":24,"column_start":22,"column_end":49,"is_primary":true,"text":[{"text":" let _let_chain = for<'a> |x: &'a i32| -> i32 {","highlight_start":22,"highlight_end":49}],"label":null,"suggested_replacement":"|x|","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder-maybe-incorrect.rs:24:22 + | +LL | let _let_chain = for<'a> |x: &'a i32| -> i32 { + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _let_chain = for<'a> |x: &'a i32| -> i32 { +LL + let _let_chain: for<'a> fn(&'a i32) -> i32 = |x| { + | + +"} +{"$message_type":"diagnostic","message":"aborting due to 3 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 3 previous errors + +"} +{"$message_type":"diagnostic","message":"For more information about this error, try `rustc --explain E0658`.","code":null,"level":"failure-note","spans":[],"children":[],"rendered":"For more information about this error, try `rustc --explain E0658`. +"} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.fixed b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.fixed new file mode 100644 index 0000000000000..30250eca548ad --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.fixed @@ -0,0 +1,10 @@ +//@ run-rustfix +//@ rustfix-only-machine-applicable + +// Verify the #160431 rewrite is MachineApplicable: rustfix applies it and the result compiles +// without `#![feature(closure_lifetime_binder)]`. + +fn main() { + let _cl: for<'a> fn(&'a str) -> (&'a str, &'a str) = |x| { x.split_at(0) }; + //~^ ERROR `for<...>` binders for closures are experimental +} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.rs b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.rs new file mode 100644 index 0000000000000..372a03e6e5d99 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.rs @@ -0,0 +1,10 @@ +//@ run-rustfix +//@ rustfix-only-machine-applicable + +// Verify the #160431 rewrite is MachineApplicable: rustfix applies it and the result compiles +// without `#![feature(closure_lifetime_binder)]`. + +fn main() { + let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; + //~^ ERROR `for<...>` binders for closures are experimental +} diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.stderr b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.stderr new file mode 100644 index 0000000000000..afdf6ece14144 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder-rustfix.stderr @@ -0,0 +1,18 @@ +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder-rustfix.rs:8:15 + | +LL | let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; +LL + let _cl: for<'a> fn(&'a str) -> (&'a str, &'a str) = |x| { x.split_at(0) }; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.rs b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.rs index b0b494fa3ff13..cb62f426083f4 100644 --- a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.rs +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.rs @@ -1,3 +1,5 @@ +//@ edition: 2024 + fn main() { for<> || -> () {}; //~^ ERROR `for<...>` binders for closures are experimental @@ -5,4 +7,121 @@ fn main() { //~^ ERROR `for<...>` binders for closures are experimental for<'a, 'b> |_: &'a ()| -> () {}; //~^ ERROR `for<...>` binders for closures are experimental + + // Issue #160431: suggest moving the binder onto a `fn` pointer binding type. + let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; + //~^ ERROR `for<...>` binders for closures are experimental + + // Local temporaries in the body are fine for `fn` pointers (machine-applicable). + let _tmp = for<'a> |x: &'a str| -> usize { + //~^ ERROR `for<...>` binders for closures are experimental + let n = x.len(); + n + }; + + // Already has a type ascription — fall back to the simple help. + let _typed: _ = for<'a> |x: &'a str| -> &'a str { x }; + //~^ ERROR `for<...>` binders for closures are experimental + + // Infer placeholders must not be copied into a MachineApplicable `fn` type. + let _ret_infer = for<'a> |x: &'a str| -> _ { x }; + //~^ ERROR `for<...>` binders for closures are experimental + //~| ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _nested_infer = for<'a> |x: &'a _| -> &'a str { x }; + //~^ ERROR `for<...>` binders for closures are experimental + //~| ERROR implicit types in closure signatures are forbidden when `for<...>` is present + + // Explicit `move` closures are not `fn` pointers. + let y = 1; + let _move = for<'a> move |x: &'a i32| -> i32 { *x + y }; + //~^ ERROR `for<...>` binders for closures are experimental + + // Possible captures (any case) still get a suggestion, but only as maybe-incorrect. + let z = 1; + let _capture = for<'a> |x: &'a i32| -> i32 { *x + z }; + //~^ ERROR `for<...>` binders for closures are experimental + let Y = 1; + let _upper = for<'a> |x: &'a i32| -> i32 { *x + Y }; + //~^ ERROR `for<...>` binders for closures are experimental + + // `if let` bindings must not escape into the `else` branch (or past the `if`). + let if_let_env = 1; + let _if_let = for<'a> |x: &'a i32| -> i32 { + //~^ ERROR `for<...>` binders for closures are experimental + if let Some(if_let_env) = None:: { + if_let_env + } else { + *x + if_let_env + } + }; + + // Same for `while let`. + let while_let_env = 1; + let _while_let = for<'a> |x: &'a i32| -> i32 { + //~^ ERROR `for<...>` binders for closures are experimental + while let Some(while_let_env) = None:: { + let _ = while_let_env; + break; + } + *x + while_let_env + }; + + // Let-chain bindings are scoped to the `if` as well (same name as the outer capture). + let chain_env = 1; + let _let_chain = for<'a> |x: &'a i32| -> i32 { + //~^ ERROR `for<...>` binders for closures are experimental + if let Some(chain_env) = None:: + && chain_env == 0 + { + 0 + } else { + *x + chain_env + } + }; + + // Locals declared in a `let else` block must not leak past it. + let let_else_env = 1; + let _let_else = for<'a> |x: &'a i32| -> i32 { + //~^ ERROR `for<...>` binders for closures are experimental + let Some(_) = None:: else { + let let_else_env = 0; + return let_else_env; + }; + *x + let_else_env + }; + + // Free functions look like captures to the AST heuristic; suggestion is maybe-incorrect. + let _freefn = for<'a> |x: &'a i32| -> i32 { add(*x, 1) }; + //~^ ERROR `for<...>` binders for closures are experimental + + // `ref` bindings on the `let` are not rewritten. + let ref _ref_cl = for<'a> |x: &'a str| -> &'a str { x }; + //~^ ERROR `for<...>` binders for closures are experimental + + // `ref` closure parameters are not rewritten. + let _ref_param = for<'a> |ref x: &'a str| -> &'a str { *x }; + //~^ ERROR `for<...>` binders for closures are experimental + + // Parameter attributes would be dropped by the rewrite — fall back. + let _attrs = for<'a> |#[allow(unused)] x: &'a str| -> &'a str { x }; + //~^ ERROR `for<...>` binders for closures are experimental + + // Non-lifetime binders are not valid on `fn` pointers. + let _ty_binder = for |x: T| -> T { x }; + //~^ ERROR `for<...>` binders for closures are experimental + //~| ERROR only lifetime parameters can be used in this context + + // Bounded lifetime binders are not valid on `fn` pointers. + let _bound = for<'a: 'static> |x: &'a str| -> &'a str { x }; + //~^ ERROR `for<...>` binders for closures are experimental + //~| ERROR bounds cannot be used in this context + + // Pre-expansion gating still rejects binders under `#[cfg(false)]`. + #[cfg(false)] + let _cfg = for<'a> |x: &'a str| -> &'a str { x }; + //~^ ERROR `for<...>` binders for closures are experimental +} + +fn add(a: i32, b: i32) -> i32 { + a + b } diff --git a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr index 96e428fb9a37e..cc703db9c61e8 100644 --- a/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr +++ b/tests/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr @@ -1,5 +1,5 @@ error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:2:5 + --> $DIR/feature-gate-closure_lifetime_binder.rs:4:5 | LL | for<> || -> () {}; | ^^^^^ @@ -10,7 +10,7 @@ LL | for<> || -> () {}; = help: consider removing `for<...>` error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:4:5 + --> $DIR/feature-gate-closure_lifetime_binder.rs:6:5 | LL | for<'a> || -> () {}; | ^^^^^^^ @@ -21,7 +21,7 @@ LL | for<'a> || -> () {}; = help: consider removing `for<...>` error[E0658]: `for<...>` binders for closures are experimental - --> $DIR/feature-gate-closure_lifetime_binder.rs:6:5 + --> $DIR/feature-gate-closure_lifetime_binder.rs:8:5 | LL | for<'a, 'b> |_: &'a ()| -> () {}; | ^^^^^^^^^^^ @@ -31,6 +31,283 @@ LL | for<'a, 'b> |_: &'a ()| -> () {}; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: consider removing `for<...>` -error: aborting due to 3 previous errors +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:12:15 + | +LL | let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _cl = for<'a> |x: &'a str| -> (&'a str, &'a str) { x.split_at(0) }; +LL + let _cl: for<'a> fn(&'a str) -> (&'a str, &'a str) = |x| { x.split_at(0) }; + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:16:16 + | +LL | let _tmp = for<'a> |x: &'a str| -> usize { + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _tmp = for<'a> |x: &'a str| -> usize { +LL + let _tmp: for<'a> fn(&'a str) -> usize = |x| { + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:23:21 + | +LL | let _typed: _ = for<'a> |x: &'a str| -> &'a str { x }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:27:22 + | +LL | let _ret_infer = for<'a> |x: &'a str| -> _ { x }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:30:25 + | +LL | let _nested_infer = for<'a> |x: &'a _| -> &'a str { x }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:36:17 + | +LL | let _move = for<'a> move |x: &'a i32| -> i32 { *x + y }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:41:20 + | +LL | let _capture = for<'a> |x: &'a i32| -> i32 { *x + z }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _capture = for<'a> |x: &'a i32| -> i32 { *x + z }; +LL + let _capture: for<'a> fn(&'a i32) -> i32 = |x| { *x + z }; + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:44:18 + | +LL | let _upper = for<'a> |x: &'a i32| -> i32 { *x + Y }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _upper = for<'a> |x: &'a i32| -> i32 { *x + Y }; +LL + let _upper: for<'a> fn(&'a i32) -> i32 = |x| { *x + Y }; + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:49:19 + | +LL | let _if_let = for<'a> |x: &'a i32| -> i32 { + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _if_let = for<'a> |x: &'a i32| -> i32 { +LL + let _if_let: for<'a> fn(&'a i32) -> i32 = |x| { + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:60:22 + | +LL | let _while_let = for<'a> |x: &'a i32| -> i32 { + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _while_let = for<'a> |x: &'a i32| -> i32 { +LL + let _while_let: for<'a> fn(&'a i32) -> i32 = |x| { + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:71:22 + | +LL | let _let_chain = for<'a> |x: &'a i32| -> i32 { + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _let_chain = for<'a> |x: &'a i32| -> i32 { +LL + let _let_chain: for<'a> fn(&'a i32) -> i32 = |x| { + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:84:21 + | +LL | let _let_else = for<'a> |x: &'a i32| -> i32 { + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _let_else = for<'a> |x: &'a i32| -> i32 { +LL + let _let_else: for<'a> fn(&'a i32) -> i32 = |x| { + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:94:19 + | +LL | let _freefn = for<'a> |x: &'a i32| -> i32 { add(*x, 1) }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider setting the binding type instead + | +LL - let _freefn = for<'a> |x: &'a i32| -> i32 { add(*x, 1) }; +LL + let _freefn: for<'a> fn(&'a i32) -> i32 = |x| { add(*x, 1) }; + | + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:98:23 + | +LL | let ref _ref_cl = for<'a> |x: &'a str| -> &'a str { x }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:102:22 + | +LL | let _ref_param = for<'a> |ref x: &'a str| -> &'a str { *x }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:106:18 + | +LL | let _attrs = for<'a> |#[allow(unused)] x: &'a str| -> &'a str { x }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:110:22 + | +LL | let _ty_binder = for |x: T| -> T { x }; + | ^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0658]: only lifetime parameters can be used in this context + --> $DIR/feature-gate-closure_lifetime_binder.rs:110:26 + | +LL | let _ty_binder = for |x: T| -> T { x }; + | ^ + | + = note: see issue #108185 for more information + = help: add `#![feature(non_lifetime_binders)]` 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[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:115:18 + | +LL | let _bound = for<'a: 'static> |x: &'a str| -> &'a str { x }; + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error: bounds cannot be used in this context + --> $DIR/feature-gate-closure_lifetime_binder.rs:115:26 + | +LL | let _bound = for<'a: 'static> |x: &'a str| -> &'a str { x }; + | ^^^^^^^ + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:121:16 + | +LL | let _cfg = for<'a> |x: &'a str| -> &'a str { x }; + | ^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/feature-gate-closure_lifetime_binder.rs:27:46 + | +LL | let _ret_infer = for<'a> |x: &'a str| -> _ { x }; + | ------- ^ + | | + | `for<...>` is here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/feature-gate-closure_lifetime_binder.rs:30:41 + | +LL | let _nested_infer = for<'a> |x: &'a _| -> &'a str { x }; + | ------- ^ + | | + | `for<...>` is here + +error: aborting due to 26 previous errors For more information about this error, try `rustc --explain E0658`.