diff options
226 files changed, 3572 insertions, 1663 deletions
diff --git a/Cargo.lock b/Cargo.lock index 2bf07149cc8..0b44201d56f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3969,7 +3969,6 @@ dependencies = [ "rustc_ty_utils", "rustc_typeck", "smallvec", - "tempfile", "tracing", "winapi", ] @@ -4080,6 +4079,7 @@ dependencies = [ "rustc_type_ir", "smallvec", "snap", + "tempfile", "tracing", ] @@ -4514,6 +4514,7 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_hir", + "rustc_index", "rustc_infer", "rustc_middle", "rustc_session", diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index f705d004422..ac2328a5824 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1390,7 +1390,7 @@ pub enum ExprKind { /// A closure (e.g., `move |a, b, c| a + b + c`). /// /// The final span is the span of the argument block `|...|`. - Closure(CaptureBy, Async, Movability, P<FnDecl>, P<Expr>, Span), + Closure(ClosureBinder, CaptureBy, Async, Movability, P<FnDecl>, P<Expr>, Span), /// A block (`'label: { ... }`). Block(P<Block>, Option<Label>), /// An async block (`async move { ... }`). @@ -1518,6 +1518,31 @@ pub enum Movability { Movable, } +/// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`. +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum ClosureBinder { + /// The binder is not present, all closure lifetimes are inferred. + NotPresent, + /// The binder is present. + For { + /// Span of the whole `for<>` clause + /// + /// ```text + /// for<'a, 'b> |_: &'a (), _: &'b ()| { ... } + /// ^^^^^^^^^^^ -- this + /// ``` + span: Span, + + /// Lifetimes in the `for<>` closure + /// + /// ```text + /// for<'a, 'b> |_: &'a (), _: &'b ()| { ... } + /// ^^^^^^ -- this + /// ``` + generic_params: P<[GenericParam]>, + }, +} + /// Represents a macro invocation. The `path` indicates which macro /// is being invoked, and the `args` are arguments passed to it. #[derive(Clone, Encodable, Decodable, Debug)] diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 85bb5296486..d933ea2da9e 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -125,6 +125,10 @@ pub trait MutVisitor: Sized { noop_visit_asyncness(a, self); } + fn visit_closure_binder(&mut self, b: &mut ClosureBinder) { + noop_visit_closure_binder(b, self); + } + fn visit_block(&mut self, b: &mut P<Block>) { noop_visit_block(b, self); } @@ -825,6 +829,17 @@ pub fn visit_constness<T: MutVisitor>(constness: &mut Const, vis: &mut T) { } } +pub fn noop_visit_closure_binder<T: MutVisitor>(binder: &mut ClosureBinder, vis: &mut T) { + match binder { + ClosureBinder::NotPresent => {} + ClosureBinder::For { span: _, generic_params } => { + let mut vec = std::mem::take(generic_params).into_vec(); + vec.flat_map_in_place(|param| vis.flat_map_generic_param(param)); + *generic_params = P::from_vec(vec); + } + } +} + pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut Async, vis: &mut T) { match asyncness { Async::Yes { span: _, closure_id, return_impl_trait_id } => { @@ -1336,7 +1351,8 @@ pub fn noop_visit_expr<T: MutVisitor>( vis.visit_expr(expr); arms.flat_map_in_place(|arm| vis.flat_map_arm(arm)); } - ExprKind::Closure(_capture_by, asyncness, _movability, decl, body, span) => { + ExprKind::Closure(binder, _capture_by, asyncness, _movability, decl, body, span) => { + vis.visit_closure_binder(binder); vis.visit_asyncness(asyncness); vis.visit_fn_decl(decl); vis.visit_expr(body); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 2ce8590d771..3f830acbf27 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -56,14 +56,14 @@ pub enum FnKind<'a> { Fn(FnCtxt, Ident, &'a FnSig, &'a Visibility, &'a Generics, Option<&'a Block>), /// E.g., `|x, y| body`. - Closure(&'a FnDecl, &'a Expr), + Closure(&'a ClosureBinder, &'a FnDecl, &'a Expr), } impl<'a> FnKind<'a> { pub fn header(&self) -> Option<&'a FnHeader> { match *self { FnKind::Fn(_, _, sig, _, _, _) => Some(&sig.header), - FnKind::Closure(_, _) => None, + FnKind::Closure(_, _, _) => None, } } @@ -77,7 +77,7 @@ impl<'a> FnKind<'a> { pub fn decl(&self) -> &'a FnDecl { match self { FnKind::Fn(_, _, sig, _, _, _) => &sig.decl, - FnKind::Closure(decl, _) => decl, + FnKind::Closure(_, decl, _) => decl, } } @@ -155,6 +155,9 @@ pub trait Visitor<'ast>: Sized { fn visit_generics(&mut self, g: &'ast Generics) { walk_generics(self, g) } + fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) { + walk_closure_binder(self, b) + } fn visit_where_predicate(&mut self, p: &'ast WherePredicate) { walk_where_predicate(self, p) } @@ -636,6 +639,15 @@ pub fn walk_generics<'a, V: Visitor<'a>>(visitor: &mut V, generics: &'a Generics walk_list!(visitor, visit_where_predicate, &generics.where_clause.predicates); } +pub fn walk_closure_binder<'a, V: Visitor<'a>>(visitor: &mut V, binder: &'a ClosureBinder) { + match binder { + ClosureBinder::NotPresent => {} + ClosureBinder::For { generic_params, span: _ } => { + walk_list!(visitor, visit_generic_param, generic_params) + } + } +} + pub fn walk_where_predicate<'a, V: Visitor<'a>>(visitor: &mut V, predicate: &'a WherePredicate) { match *predicate { WherePredicate::BoundPredicate(WhereBoundPredicate { @@ -682,7 +694,8 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>, _span: Spa walk_fn_decl(visitor, &sig.decl); walk_list!(visitor, visit_block, body); } - FnKind::Closure(decl, body) => { + FnKind::Closure(binder, decl, body) => { + visitor.visit_closure_binder(binder); walk_fn_decl(visitor, decl); visitor.visit_expr(body); } @@ -856,8 +869,8 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { visitor.visit_expr(subexpression); walk_list!(visitor, visit_arm, arms); } - ExprKind::Closure(_, _, _, ref decl, ref body, _decl_span) => { - visitor.visit_fn(FnKind::Closure(decl, body), expression.span, expression.id) + ExprKind::Closure(ref binder, _, _, _, ref decl, ref body, _decl_span) => { + visitor.visit_fn(FnKind::Closure(binder, decl, body), expression.span, expression.id) } ExprKind::Block(ref block, ref opt_label) => { walk_list!(visitor, visit_label, opt_label); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 9e02e7ed3b9..983efa48a45 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -155,6 +155,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_expr_await(span, expr) } ExprKind::Closure( + ref binder, capture_clause, asyncness, movability, @@ -164,6 +165,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) => { if let Async::Yes { closure_id, .. } = asyncness { self.lower_expr_async_closure( + binder, capture_clause, e.id, closure_id, @@ -173,6 +175,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) } else { self.lower_expr_closure( + binder, capture_clause, e.id, movability, @@ -605,13 +608,18 @@ impl<'hir> LoweringContext<'_, 'hir> { }); // `static |_task_context| -> <ret_ty> { body }`: - let generator_kind = hir::ExprKind::Closure { - capture_clause, - bound_generic_params: &[], - fn_decl, - body, - fn_decl_span: self.lower_span(span), - movability: Some(hir::Movability::Static), + let generator_kind = { + let c = self.arena.alloc(hir::Closure { + binder: hir::ClosureBinder::Default, + capture_clause, + bound_generic_params: &[], + fn_decl, + body, + fn_decl_span: self.lower_span(span), + movability: Some(hir::Movability::Static), + }); + + hir::ExprKind::Closure(c) }; let generator = hir::Expr { hir_id: self.lower_node_id(closure_node_id), @@ -831,6 +839,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_expr_closure( &mut self, + binder: &ClosureBinder, capture_clause: CaptureBy, closure_id: NodeId, movability: Movability, @@ -838,7 +847,9 @@ impl<'hir> LoweringContext<'_, 'hir> { body: &Expr, fn_decl_span: Span, ) -> hir::ExprKind<'hir> { - let (body, generator_option) = self.with_new_scopes(move |this| { + let (binder_clause, generic_params) = self.lower_closure_binder(binder); + + let (body_id, generator_option) = self.with_new_scopes(move |this| { let prev = this.current_item; this.current_item = Some(fn_decl_span); let mut generator_kind = None; @@ -853,18 +864,21 @@ impl<'hir> LoweringContext<'_, 'hir> { (body_id, generator_option) }); - self.with_lifetime_binder(closure_id, &[], |this, bound_generic_params| { + self.with_lifetime_binder(closure_id, generic_params, |this, bound_generic_params| { // Lower outside new scope to preserve `is_in_loop_condition`. let fn_decl = this.lower_fn_decl(decl, None, FnDeclKind::Closure, None); - hir::ExprKind::Closure { + let c = self.arena.alloc(hir::Closure { + binder: binder_clause, capture_clause, bound_generic_params, fn_decl, - body, + body: body_id, fn_decl_span: this.lower_span(fn_decl_span), movability: generator_option, - } + }); + + hir::ExprKind::Closure(c) }) } @@ -906,8 +920,24 @@ impl<'hir> LoweringContext<'_, 'hir> { } } + fn lower_closure_binder<'c>( + &mut self, + binder: &'c ClosureBinder, + ) -> (hir::ClosureBinder, &'c [GenericParam]) { + let (binder, params) = match binder { + ClosureBinder::NotPresent => (hir::ClosureBinder::Default, &[][..]), + &ClosureBinder::For { span, ref generic_params } => { + let span = self.lower_span(span); + (hir::ClosureBinder::For { span }, &**generic_params) + } + }; + + (binder, params) + } + fn lower_expr_async_closure( &mut self, + binder: &ClosureBinder, capture_clause: CaptureBy, closure_id: NodeId, inner_closure_id: NodeId, @@ -915,6 +945,15 @@ impl<'hir> LoweringContext<'_, 'hir> { body: &Expr, fn_decl_span: Span, ) -> hir::ExprKind<'hir> { + if let &ClosureBinder::For { span, .. } = binder { + self.tcx.sess.span_err( + span, + "`for<...>` binders on `async` closures are not currently supported", + ); + } + + let (binder_clause, generic_params) = self.lower_closure_binder(binder); + let outer_decl = FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) }; @@ -952,20 +991,22 @@ impl<'hir> LoweringContext<'_, 'hir> { body_id }); - self.with_lifetime_binder(closure_id, &[], |this, bound_generic_params| { + self.with_lifetime_binder(closure_id, generic_params, |this, bound_generic_params| { // We need to lower the declaration outside the new scope, because we // have to conserve the state of being inside a loop condition for the // closure argument types. let fn_decl = this.lower_fn_decl(&outer_decl, None, FnDeclKind::Closure, None); - hir::ExprKind::Closure { + let c = self.arena.alloc(hir::Closure { + binder: binder_clause, capture_clause, bound_generic_params, fn_decl, body, fn_decl_span: this.lower_span(fn_decl_span), movability: None, - } + }); + hir::ExprKind::Closure(c) }) } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 3942062656f..f284bf4650a 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1597,6 +1597,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .emit(); } + if let FnKind::Closure(ClosureBinder::For { generic_params, .. }, ..) = fk { + self.check_late_bound_lifetime_defs(generic_params); + } + if let FnKind::Fn( _, _, diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index fd2dd6cf6c7..e69f85eacf7 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -744,6 +744,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { "async closures are unstable", "to use an async block, remove the `||`: `async {`" ); + gate_all!( + closure_lifetime_binder, + "`for<...>` binders for closures are experimental", + "consider removing `for<...>`" + ); gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental"); gate_all!(generators, "yield syntax is experimental"); gate_all!(raw_ref_op, "raw address of syntax is experimental"); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 9f44f1b6cc2..ead38caee28 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -389,6 +389,7 @@ impl<'a> State<'a> { self.bclose(expr.span, empty); } ast::ExprKind::Closure( + ref binder, capture_clause, asyncness, movability, @@ -396,6 +397,7 @@ impl<'a> State<'a> { ref body, _, ) => { + self.print_closure_binder(binder); self.print_movability(movability); self.print_asyncness(asyncness); self.print_capture_clause(capture_clause); @@ -594,6 +596,15 @@ impl<'a> State<'a> { self.end(); // Close enclosing cbox. } + fn print_closure_binder(&mut self, binder: &ast::ClosureBinder) { + match binder { + ast::ClosureBinder::NotPresent => {} + ast::ClosureBinder::For { generic_params, .. } => { + self.print_formal_generic_params(&generic_params) + } + } + } + fn print_movability(&mut self, movability: ast::Movability) { match movability { ast::Movability::Static => self.word_space("static"), diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 6673d75d99d..dcfbecedfe8 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -137,7 +137,7 @@ impl ConstStability { pub enum StabilityLevel { // Reason for the current stability level and the relevant rust-lang issue Unstable { reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool }, - Stable { since: Symbol }, + Stable { since: Symbol, allowed_through_unstable_modules: bool }, } impl StabilityLevel { @@ -172,6 +172,7 @@ where let mut stab: Option<(Stability, Span)> = None; let mut const_stab: Option<(ConstStability, Span)> = None; let mut promotable = false; + let mut allowed_through_unstable_modules = false; let diagnostic = &sess.parse_sess.span_diagnostic; @@ -182,6 +183,7 @@ where sym::unstable, sym::stable, sym::rustc_promotable, + sym::rustc_allowed_through_unstable_modules, ] .iter() .any(|&s| attr.has_name(s)) @@ -193,6 +195,8 @@ where if attr.has_name(sym::rustc_promotable) { promotable = true; + } else if attr.has_name(sym::rustc_allowed_through_unstable_modules) { + allowed_through_unstable_modules = true; } // attributes with data else if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta { @@ -406,7 +410,7 @@ where match (feature, since) { (Some(feature), Some(since)) => { - let level = Stable { since }; + let level = Stable { since, allowed_through_unstable_modules: false }; if sym::stable == meta_name { stab = Some((Stability { level, feature }, attr.span)); } else { @@ -447,6 +451,27 @@ where } } + if allowed_through_unstable_modules { + if let Some(( + Stability { + level: StabilityLevel::Stable { ref mut allowed_through_unstable_modules, .. }, + .. + }, + _, + )) = stab + { + *allowed_through_unstable_modules = true; + } else { + struct_span_err!( + diagnostic, + item_sp, + E0789, + "`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute" + ) + .emit(); + } + } + (stab, const_stab) } diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 9c7671eee38..53c07a3d481 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -891,7 +891,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(local_did); let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind; debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr); - if let hir::ExprKind::Closure { body, fn_decl_span, .. } = expr { + if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = expr { for (captured_place, place) in self .infcx .tcx @@ -904,11 +904,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if target_place == place.as_ref() => { debug!("closure_span: found captured local {:?}", place); - let body = self.infcx.tcx.hir().body(*body); + let body = self.infcx.tcx.hir().body(body); let generator_kind = body.generator_kind(); return Some(( - *fn_decl_span, + fn_decl_span, generator_kind, captured_place.get_capture_kind_span(self.infcx.tcx), captured_place.get_path_span(self.infcx.tcx), diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index e60e11f11df..e41af17fbf9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -325,7 +325,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { // Can't have BrEnv in functions, constants or generators. bug!("BrEnv outside of closure."); }; - let hir::ExprKind::Closure { fn_decl_span, .. } + let hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }) = tcx.hir().expect_expr(self.mir_hir_id()).kind else { bug!("Closure is not defined by a closure expr"); @@ -701,16 +701,16 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let (return_span, mir_description, hir_ty) = match hir.get(mir_hir_id) { hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure { fn_decl, body, fn_decl_span, .. }, + kind: hir::ExprKind::Closure(&hir::Closure { fn_decl, body, fn_decl_span, .. }), .. }) => { let (mut span, mut hir_ty) = match fn_decl.output { hir::FnRetTy::DefaultReturn(_) => { - (tcx.sess.source_map().end_point(*fn_decl_span), None) + (tcx.sess.source_map().end_point(fn_decl_span), None) } hir::FnRetTy::Return(hir_ty) => (fn_decl.output.span(), Some(hir_ty)), }; - let mir_description = match hir.body(*body).generator_kind { + let mir_description = match hir.body(body).generator_kind { Some(hir::GeneratorKind::Async(gen)) => match gen { hir::AsyncGeneratorKind::Block => " of async block", hir::AsyncGeneratorKind::Closure => " of async closure", @@ -841,9 +841,9 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let yield_span = match tcx.hir().get(self.mir_hir_id()) { hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure { fn_decl_span, .. }, + kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), .. - }) => (tcx.sess.source_map().end_point(*fn_decl_span)), + }) => (tcx.sess.source_map().end_point(fn_decl_span)), _ => self.body.span, }; diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 42bddd1b6ed..829eaa305e8 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -534,8 +534,8 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl let mut template_strs = Vec::with_capacity(args.templates.len()); - for template_expr in args.templates.into_iter() { - if !template.is_empty() { + for (i, template_expr) in args.templates.into_iter().enumerate() { + if i != 0 { template.push(ast::InlineAsmTemplatePiece::String("\n".to_string())); } diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 9e50d33486c..01152ff7df5 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -294,7 +294,7 @@ impl<'cx, 'a> Context<'cx, 'a> { | ExprKind::Block(_, _) | ExprKind::Box(_) | ExprKind::Break(_, _) - | ExprKind::Closure(_, _, _, _, _, _) + | ExprKind::Closure(_, _, _, _, _, _, _) | ExprKind::ConstBlock(_) | ExprKind::Continue(_) | ExprKind::Err diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 6c2ac343544..4791151c7d3 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -14,6 +14,9 @@ use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{InnerSpan, Span}; use smallvec::SmallVec; +use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; +use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId}; +use rustc_parse_format::{Count, FormatSpec}; use std::borrow::Cow; use std::collections::hash_map::Entry; @@ -57,7 +60,7 @@ struct Context<'a, 'b> { /// Unique format specs seen for each argument. arg_unique_types: Vec<Vec<ArgumentType>>, /// Map from named arguments to their resolved indices. - names: FxHashMap<Symbol, usize>, + names: FxHashMap<Symbol, (usize, Span)>, /// The latest consecutive literal strings, or empty if there weren't any. literal: String, @@ -130,9 +133,9 @@ fn parse_args<'a>( ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream, -) -> PResult<'a, (P<ast::Expr>, Vec<P<ast::Expr>>, FxHashMap<Symbol, usize>)> { +) -> PResult<'a, (P<ast::Expr>, Vec<P<ast::Expr>>, FxHashMap<Symbol, (usize, Span)>)> { let mut args = Vec::<P<ast::Expr>>::new(); - let mut names = FxHashMap::<Symbol, usize>::default(); + let mut names = FxHashMap::<Symbol, (usize, Span)>::default(); let mut p = ecx.new_parser_from_tts(tts); @@ -197,7 +200,7 @@ fn parse_args<'a>( p.bump(); p.expect(&token::Eq)?; let e = p.parse_expr()?; - if let Some(prev) = names.get(&ident.name) { + if let Some((prev, _)) = names.get(&ident.name) { ecx.struct_span_err(e.span, &format!("duplicate argument named `{}`", ident)) .span_label(args[*prev].span, "previously here") .span_label(e.span, "duplicate argument") @@ -210,7 +213,7 @@ fn parse_args<'a>( // if the input is valid, we can simply append to the positional // args. And remember the names. let slot = args.len(); - names.insert(ident.name, slot); + names.insert(ident.name, (slot, ident.span)); args.push(e); } _ => { @@ -222,7 +225,7 @@ fn parse_args<'a>( ); err.span_label(e.span, "positional arguments must be before named arguments"); for pos in names.values() { - err.span_label(args[*pos].span, "named argument"); + err.span_label(args[pos.0].span, "named argument"); } err.emit(); } @@ -242,7 +245,8 @@ impl<'a, 'b> Context<'a, 'b> { fn resolve_name_inplace(&self, p: &mut parse::Piece<'_>) { // NOTE: the `unwrap_or` branch is needed in case of invalid format // arguments, e.g., `format_args!("{foo}")`. - let lookup = |s: &str| *self.names.get(&Symbol::intern(s)).unwrap_or(&0); + let lookup = + |s: &str| self.names.get(&Symbol::intern(s)).unwrap_or(&(0, Span::default())).0; match *p { parse::String(_) => {} @@ -548,7 +552,7 @@ impl<'a, 'b> Context<'a, 'b> { match self.names.get(&name) { Some(&idx) => { // Treat as positional arg. - self.verify_arg_type(Capture(idx), ty) + self.verify_arg_type(Capture(idx.0), ty) } None => { // For the moment capturing variables from format strings expanded from macros is @@ -565,7 +569,7 @@ impl<'a, 'b> Context<'a, 'b> { }; self.num_captured_args += 1; self.args.push(self.ecx.expr_ident(span, Ident::new(name, span))); - self.names.insert(name, idx); + self.names.insert(name, (idx, span)); self.verify_arg_type(Capture(idx), ty) } else { let msg = format!("there is no argument named `{}`", name); @@ -967,6 +971,49 @@ pub fn expand_format_args_nl<'cx>( expand_format_args_impl(ecx, sp, tts, true) } +fn lint_named_arguments_used_positionally( + names: FxHashMap<Symbol, (usize, Span)>, + cx: &mut Context<'_, '_>, + unverified_pieces: Vec<parse::Piece<'_>>, +) { + let mut used_argument_names = FxHashSet::<&str>::default(); + for piece in unverified_pieces { + if let rustc_parse_format::Piece::NextArgument(a) = piece { + match a.position { + rustc_parse_format::Position::ArgumentNamed(arg_name, _) => { + used_argument_names.insert(arg_name); + } + _ => {} + }; + match a.format { + FormatSpec { width: Count::CountIsName(s, _), .. } + | FormatSpec { precision: Count::CountIsName(s, _), .. } => { + used_argument_names.insert(s); + } + _ => {} + }; + } + } + + for (symbol, (index, span)) in names { + if !used_argument_names.contains(symbol.as_str()) { + let msg = format!("named argument `{}` is not used by name", symbol.as_str()); + let arg_span = cx.arg_spans[index]; + cx.ecx.buffered_early_lint.push(BufferedEarlyLint { + span: MultiSpan::from_span(span), + msg: msg.clone(), + node_id: ast::CRATE_NODE_ID, + lint_id: LintId::of(&NAMED_ARGUMENTS_USED_POSITIONALLY), + diagnostic: BuiltinLintDiagnostics::NamedArgumentUsedPositionally( + arg_span, + span, + symbol.to_string(), + ), + }); + } + } +} + /// Take the various parts of `format_args!(efmt, args..., name=names...)` /// and construct the appropriate formatting expression. pub fn expand_preparsed_format_args( @@ -974,7 +1021,7 @@ pub fn expand_preparsed_format_args( sp: Span, efmt: P<ast::Expr>, args: Vec<P<ast::Expr>>, - names: FxHashMap<Symbol, usize>, + names: FxHashMap<Symbol, (usize, Span)>, append_newline: bool, ) -> P<ast::Expr> { // NOTE: this verbose way of initializing `Vec<Vec<ArgumentType>>` is because @@ -1073,7 +1120,12 @@ pub fn expand_preparsed_format_args( .map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end))) .collect(); - let named_pos: FxHashSet<usize> = names.values().cloned().collect(); + let named_pos: FxHashSet<usize> = names.values().cloned().map(|(i, _)| i).collect(); + + // Clone `names` because `names` in Context get updated by verify_piece, which includes usages + // of the names of named arguments, resulting in incorrect errors if a name argument is used + // but not declared, such as: `println!("x = {x}");` + let named_arguments = names.clone(); let mut cx = Context { ecx, @@ -1101,9 +1153,11 @@ pub fn expand_preparsed_format_args( is_literal: parser.is_literal, }; - // This needs to happen *after* the Parser has consumed all pieces to create all the spans + // This needs to happen *after* the Parser has consumed all pieces to create all the spans. + // unverified_pieces is used later to check named argument names are used, so clone each piece. let pieces = unverified_pieces - .into_iter() + .iter() + .cloned() .map(|mut piece| { cx.verify_piece(&piece); cx.resolve_name_inplace(&mut piece); @@ -1265,6 +1319,11 @@ pub fn expand_preparsed_format_args( } diag.emit(); + } else if cx.invalid_refs.is_empty() && !named_arguments.is_empty() { + // Only check for unused named argument names if there are no other errors to avoid causing + // too much noise in output errors, such as when a named argument is entirely unused. + // We also only need to perform this check if there are actually named arguments. + lint_named_arguments_used_positionally(named_arguments, &mut cx, unverified_pieces); } cx.into_expr() diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 69813792fcf..cf591295b84 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -97,23 +97,26 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { unsafe { llvm::LLVMRustDIBuilderFinalize(self.builder); - // Debuginfo generation in LLVM by default uses a higher - // version of dwarf than macOS currently understands. We can - // instruct LLVM to emit an older version of dwarf, however, - // for macOS to understand. For more info see #11352 - // This can be overridden using --llvm-opts -dwarf-version,N. - // Android has the same issue (#22398) - let dwarf_version = - sess.opts.unstable_opts.dwarf_version.unwrap_or(sess.target.default_dwarf_version); - llvm::LLVMRustAddModuleFlag( - self.llmod, - llvm::LLVMModFlagBehavior::Warning, - "Dwarf Version\0".as_ptr().cast(), - dwarf_version, - ); - - // Indicate that we want CodeView debug information on MSVC - if sess.target.is_like_msvc { + if !sess.target.is_like_msvc { + // Debuginfo generation in LLVM by default uses a higher + // version of dwarf than macOS currently understands. We can + // instruct LLVM to emit an older version of dwarf, however, + // for macOS to understand. For more info see #11352 + // This can be overridden using --llvm-opts -dwarf-version,N. + // Android has the same issue (#22398) + let dwarf_version = sess + .opts + .unstable_opts + .dwarf_version + .unwrap_or(sess.target.default_dwarf_version); + llvm::LLVMRustAddModuleFlag( + self.llmod, + llvm::LLVMModFlagBehavior::Warning, + "Dwarf Version\0".as_ptr().cast(), + dwarf_version, + ); + } else { + // Indicate that we want CodeView debug information on MSVC llvm::LLVMRustAddModuleFlag( self.llmod, llvm::LLVMModFlagBehavior::Warning, diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 94acdea894b..878a670cba3 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -6,6 +6,7 @@ use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{ErrorGuaranteed, Handler}; use rustc_fs_util::fix_windows_verbatim_for_gcc; use rustc_hir::def_id::CrateNum; +use rustc_metadata::fs::{emit_metadata, METADATA_FILENAME}; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip}; @@ -28,10 +29,7 @@ use super::command::Command; use super::linker::{self, Linker}; use super::metadata::{create_rmeta_file, MetadataPosition}; use super::rpath::{self, RPathConfig}; -use crate::{ - looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib, - METADATA_FILENAME, -}; +use crate::{looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib}; use cc::windows_registry; use regex::Regex; @@ -241,22 +239,6 @@ pub fn each_linked_rlib( Ok(()) } -/// We use a temp directory here to avoid races between concurrent rustc processes, -/// such as builds in the same directory using the same filename for metadata while -/// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a -/// directory being searched for `extern crate` (observing an incomplete file). -/// The returned path is the temporary file containing the complete metadata. -pub fn emit_metadata(sess: &Session, metadata: &[u8], tmpdir: &MaybeTempDir) -> PathBuf { - let out_filename = tmpdir.as_ref().join(METADATA_FILENAME); - let result = fs::write(&out_filename, metadata); - - if let Err(e) = result { - sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); - } - - out_filename -} - /// Create an 'rlib'. /// /// An rlib in its current incarnation is essentially a renamed .a file. The rlib primarily contains diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 3dd607adee5..0302c28815a 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -16,14 +16,13 @@ use rustc_data_structures::memmap::Mmap; use rustc_data_structures::owning_ref::OwningRef; use rustc_data_structures::rustc_erase_owner; use rustc_data_structures::sync::MetadataRef; +use rustc_metadata::fs::METADATA_FILENAME; use rustc_metadata::EncodedMetadata; use rustc_session::cstore::MetadataLoader; use rustc_session::Session; use rustc_target::abi::Endian; use rustc_target::spec::{RelocModel, Target}; -use crate::METADATA_FILENAME; - /// The default metadata loader. This is used by cg_llvm and cg_clif. /// /// # Metadata location diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 4be3ae11e4e..1802eedf193 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -64,9 +64,6 @@ pub struct ModuleCodegen<M> { pub kind: ModuleKind, } -// FIXME(eddyb) maybe include the crate name in this? -pub const METADATA_FILENAME: &str = "lib.rmeta"; - impl<M> ModuleCodegen<M> { pub fn into_compiled_module( self, diff --git a/compiler/rustc_data_structures/src/memmap.rs b/compiler/rustc_data_structures/src/memmap.rs index 26b26415eea..917416df6b8 100644 --- a/compiler/rustc_data_structures/src/memmap.rs +++ b/compiler/rustc_data_structures/src/memmap.rs @@ -1,6 +1,6 @@ use std::fs::File; use std::io; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use crate::owning_ref::StableAddress; @@ -45,3 +45,64 @@ impl Deref for Mmap { // export any function that can cause the `Vec` to be re-allocated. As such the address of the // bytes inside this `Vec` is stable. unsafe impl StableAddress for Mmap {} + +#[cfg(not(target_arch = "wasm32"))] +pub struct MmapMut(memmap2::MmapMut); + +#[cfg(target_arch = "wasm32")] +pub struct MmapMut(Vec<u8>); + +#[cfg(not(target_arch = "wasm32"))] +impl MmapMut { + #[inline] + pub fn map_anon(len: usize) -> io::Result<Self> { + let mmap = memmap2::MmapMut::map_anon(len)?; + Ok(MmapMut(mmap)) + } + + #[inline] + pub fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } + + #[inline] + pub fn make_read_only(self) -> std::io::Result<Mmap> { + let mmap = self.0.make_read_only()?; + Ok(Mmap(mmap)) + } +} + +#[cfg(target_arch = "wasm32")] +impl MmapMut { + #[inline] + pub fn map_anon(len: usize) -> io::Result<Self> { + let data = Vec::with_capacity(len); + Ok(MmapMut(data)) + } + + #[inline] + pub fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + #[inline] + pub fn make_read_only(self) -> std::io::Result<Mmap> { + Ok(Mmap(self.0)) + } +} + +impl Deref for MmapMut { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + &*self.0 + } +} + +impl DerefMut for MmapMut { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + &mut *self.0 + } +} diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index d507293ccb0..977318b8589 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -644,4 +644,5 @@ E0788: include_str!("./error_codes/E0788.md"), // E0721, // `await` keyword // E0723, // unstable feature in `const` context // E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`. + E0789, // rustc_allowed_through_unstable_modules without stability attribute } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index bfe2d773788..e1f19064d52 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -12,7 +12,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{self, Lrc}; use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult}; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; -use rustc_lint_defs::BuiltinLintDiagnostics; +use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics}; use rustc_parse::{self, parser, MACRO_ARGUMENTS}; use rustc_session::{parse::ParseSess, Limit, Session, SessionDiagnostic}; use rustc_span::def_id::{CrateNum, DefId, LocalDefId}; @@ -988,6 +988,8 @@ pub struct ExtCtxt<'a> { pub expansions: FxHashMap<Span, Vec<String>>, /// Used for running pre-expansion lints on freshly loaded modules. pub(super) lint_store: LintStoreExpandDyn<'a>, + /// Used for storing lints generated during expansion, like `NAMED_ARGUMENTS_USED_POSITIONALLY` + pub buffered_early_lint: Vec<BufferedEarlyLint>, /// When we 'expand' an inert attribute, we leave it /// in the AST, but insert it here so that we know /// not to expand it again. @@ -1020,6 +1022,7 @@ impl<'a> ExtCtxt<'a> { force_mode: false, expansions: FxHashMap::default(), expanded_inert_attrs: MarkedAttrs::new(), + buffered_early_lint: vec![], } } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 74e9bbeeeaf..fa3e2a4a5b8 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -520,6 +520,7 @@ impl<'a> ExtCtxt<'a> { self.expr( span, ast::ExprKind::Closure( + ast::ClosureBinder::NotPresent, ast::CaptureBy::Ref, ast::Async::No, ast::Movability::Movable, diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 1fc4d09eb0a..ef4a1756416 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -148,6 +148,8 @@ declare_features! ( /// below (it has to be checked before expansion possibly makes /// macros disappear). (active, allow_internal_unstable, "1.0.0", None, None), + /// Allows using anonymous lifetimes in argument-position impl-trait. + (active, anonymous_lifetime_in_impl_trait, "1.63.0", None, None), /// Allows identifying the `compiler_builtins` crate. (active, compiler_builtins, "1.13.0", None, None), /// Outputs useful `assert!` messages @@ -328,6 +330,8 @@ declare_features! ( (active, cfg_target_thread_local, "1.7.0", Some(29594), None), /// Allow conditional compilation depending on rust version (active, cfg_version, "1.45.0", Some(64796), None), + /// Allows `for<...>` on closures and generators. + (active, closure_lifetime_binder, "1.64.0", Some(97362), None), /// Allows `#[track_caller]` on closures and generators. (active, closure_track_caller, "1.57.0", Some(87417), None), /// Allows to use the `#[cmse_nonsecure_entry]` attribute. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 6fcdfe44d8f..c806df82145 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -512,6 +512,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ allow_internal_unsafe, Normal, template!(Word), WarnFollowing, "allow_internal_unsafe side-steps the unsafe_code lint", ), + rustc_attr!(rustc_allowed_through_unstable_modules, Normal, template!(Word), WarnFollowing, + "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \ + through unstable paths"), // ========================================================================== // Internal attributes: Type system related: diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index a6d10f3adae..44335b7f42e 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -12,6 +12,7 @@ macro_rules! arena_types { [] asm_operand: (rustc_hir::InlineAsmOperand<'tcx>, rustc_span::Span), [] asm_template: rustc_ast::InlineAsmTemplatePiece, [] attribute: rustc_ast::Attribute, + [] closure: rustc_hir::Closure<'tcx>, [] block: rustc_hir::Block<'tcx>, [] bare_fn_ty: rustc_hir::BareFnTy<'tcx>, [] body: rustc_hir::Body<'tcx>, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ed874ae829b..9dab577d84b 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -922,6 +922,17 @@ pub struct Crate<'hir> { pub hir_hash: Fingerprint, } +#[derive(Debug, HashStable_Generic)] +pub struct Closure<'hir> { + pub binder: ClosureBinder, + pub capture_clause: CaptureBy, + pub bound_generic_params: &'hir [GenericParam<'hir>], + pub fn_decl: &'hir FnDecl<'hir>, + pub body: BodyId, + pub fn_decl_span: Span, + pub movability: Option<Movability>, +} + /// A block of statements `{ .. }`, which may have a label (in this case the /// `targeted_by_break` field will be `true`) and may be `unsafe` by means of /// the `rules` being anything but `DefaultBlock`. @@ -1915,14 +1926,7 @@ pub enum ExprKind<'hir> { /// /// This may also be a generator literal or an `async block` as indicated by the /// `Option<Movability>`. - Closure { - capture_clause: CaptureBy, - bound_generic_params: &'hir [GenericParam<'hir>], - fn_decl: &'hir FnDecl<'hir>, - body: BodyId, - fn_decl_span: Span, - movability: Option<Movability>, - }, + Closure(&'hir Closure<'hir>), /// A block (e.g., `'label: { ... }`). Block(&'hir Block<'hir>, Option<Label>), @@ -2700,6 +2704,17 @@ impl FnRetTy<'_> { } } +/// Represents `for<...>` binder before a closure +#[derive(Copy, Clone, Debug, HashStable_Generic)] +pub enum ClosureBinder { + /// Binder is not specified. + Default, + /// Binder is specified. + /// + /// Span points to the whole `for<...>`. + For { span: Span }, +} + #[derive(Encodable, Debug, HashStable_Generic)] pub struct Mod<'hir> { pub spans: ModSpans, @@ -3464,7 +3479,7 @@ impl<'hir> Node<'hir> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] mod size_asserts { rustc_data_structures::static_assert_size!(super::Block<'static>, 48); - rustc_data_structures::static_assert_size!(super::Expr<'static>, 64); + rustc_data_structures::static_assert_size!(super::Expr<'static>, 56); rustc_data_structures::static_assert_size!(super::Pat<'static>, 88); rustc_data_structures::static_assert_size!(super::QPath<'static>, 24); rustc_data_structures::static_assert_size!(super::Ty<'static>, 72); diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index b5d9769c578..d00b65da7e6 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -928,7 +928,7 @@ pub fn walk_fn_kind<'v, V: Visitor<'v>>(visitor: &mut V, function_kind: FnKind<' FnKind::ItemFn(_, generics, ..) => { visitor.visit_generics(generics); } - FnKind::Method(..) | FnKind::Closure => {} + FnKind::Closure | FnKind::Method(..) => {} } } @@ -1147,14 +1147,15 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) visitor.visit_expr(subexpression); walk_list!(visitor, visit_arm, arms); } - ExprKind::Closure { + ExprKind::Closure(&Closure { + binder: _, bound_generic_params, - ref fn_decl, + fn_decl, body, capture_clause: _, fn_decl_span: _, movability: _, - } => { + }) => { walk_list!(visitor, visit_generic_param, bound_generic_params); visitor.visit_fn(FnKind::Closure, fn_decl, body, expression.span, expression.hir_id) } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index e3c97ec357e..18b671d410d 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -6,6 +6,7 @@ use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; use rustc_ast_pretty::pp::{self, Breaks}; use rustc_ast_pretty::pprust::{Comments, PrintState}; use rustc_hir as hir; +use rustc_hir::LifetimeParamKind; use rustc_hir::{GenericArg, GenericParam, GenericParamKind, Node, Term}; use rustc_hir::{GenericBound, PatKind, RangeEnd, TraitBoundModifier}; use rustc_span::source_map::SourceMap; @@ -1452,15 +1453,16 @@ impl<'a> State<'a> { } self.bclose(expr.span); } - hir::ExprKind::Closure { + hir::ExprKind::Closure(&hir::Closure { + binder, capture_clause, bound_generic_params, fn_decl, body, fn_decl_span: _, movability: _, - } => { - self.print_formal_generic_params(bound_generic_params); + }) => { + self.print_closure_binder(binder, bound_generic_params); self.print_capture_clause(capture_clause); self.print_closure_params(fn_decl, body); @@ -2045,6 +2047,42 @@ impl<'a> State<'a> { } } + pub fn print_closure_binder( + &mut self, + binder: hir::ClosureBinder, + generic_params: &[GenericParam<'_>], + ) { + let generic_params = generic_params + .iter() + .filter(|p| { + matches!( + p, + GenericParam { + kind: GenericParamKind::Lifetime { kind: LifetimeParamKind::Explicit }, + .. + } + ) + }) + .collect::<Vec<_>>(); + + match binder { + hir::ClosureBinder::Default => {} + // we need to distinguish `|...| {}` from `for<> |...| {}` as `for<>` adds additional restrictions + hir::ClosureBinder::For { .. } if generic_params.is_empty() => self.word("for<>"), + hir::ClosureBinder::For { .. } => { + self.word("for"); + self.word("<"); + + self.commasep(Inconsistent, &generic_params, |s, param| { + s.print_generic_param(param) + }); + + self.word(">"); + self.nbsp(); + } + } + } + pub fn print_bounds<'b>( &mut self, prefix: &'static str, diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 4d29fc46946..066bf9681b6 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -6,7 +6,7 @@ use rustc_hir::def::Res; use rustc_hir::def::{CtorOf, DefKind, Namespace}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource}; +use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource}; use rustc_middle::hir::nested_filter; use rustc_middle::infer::unify_key::ConstVariableOriginKind; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; @@ -1051,7 +1051,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { if let Some(node_ty) = self.opt_node_type(expr.hir_id) { if let ( - &ExprKind::Closure { fn_decl, body, fn_decl_span, .. }, + &ExprKind::Closure(&Closure { fn_decl, body, fn_decl_span, .. }), ty::Closure(_, substs), ) = (&expr.kind, node_ty.kind()) { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 28f037cc61a..881682678db 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -21,6 +21,7 @@ use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; use rustc_middle::traits::select; +use rustc_middle::ty::abstract_const::AbstractConst; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::relate::RelateResult; @@ -1651,14 +1652,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { unevaluated: ty::Unevaluated<'tcx>, span: Option<Span>, ) -> EvalToValTreeResult<'tcx> { - let substs = self.resolve_vars_if_possible(unevaluated.substs); + let mut substs = self.resolve_vars_if_possible(unevaluated.substs); debug!(?substs); // Postpone the evaluation of constants whose substs depend on inference // variables if substs.has_infer_types_or_consts() { - debug!("substs have infer types or consts: {:?}", substs); - return Err(ErrorHandled::TooGeneric); + let ac = AbstractConst::new(self.tcx, unevaluated.shrink()); + if let Ok(None) = ac { + substs = InternalSubsts::identity_for_item(self.tcx, unevaluated.def.did); + } else { + return Err(ErrorHandled::TooGeneric); + } } let param_env_erased = self.tcx.erase_regions(param_env); diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 9aacfba3c82..1ecbc876c8d 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -46,7 +46,6 @@ rustc_query_impl = { path = "../rustc_query_impl" } rustc_resolve = { path = "../rustc_resolve" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_ty_utils = { path = "../rustc_ty_utils" } -tempfile = "3.2" [target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 97b8139f9da..334a595a88a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -5,18 +5,15 @@ use crate::util; use ast::CRATE_NODE_ID; use rustc_ast::{self as ast, visit}; use rustc_borrowck as mir_borrowck; -use rustc_codegen_ssa::back::link::emit_metadata; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::parallel; use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal}; -use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, PResult}; use rustc_expand::base::{ExtCtxt, LintStoreExpand, ResolverExpand}; -use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE}; +use rustc_hir::def_id::StableCrateId; use rustc_hir::definitions::Definitions; -use rustc_lint::{EarlyCheckNode, LintStore}; +use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore}; use rustc_metadata::creader::CStore; -use rustc_metadata::{encode_metadata, EncodedMetadata}; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepGraph; use rustc_middle::ty::query::{ExternProviders, Providers}; @@ -29,14 +26,13 @@ use rustc_query_impl::{OnDiskCache, Queries as TcxQueries}; use rustc_resolve::{Resolver, ResolverArenas}; use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType}; use rustc_session::cstore::{CrateStoreDyn, MetadataLoader, MetadataLoaderDyn}; -use rustc_session::output::{filename_for_input, filename_for_metadata}; +use rustc_session::output::filename_for_input; use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::FileName; use rustc_trait_selection::traits; use rustc_typeck as typeck; -use tempfile::Builder as TempFileBuilder; use tracing::{info, warn}; use std::any::Any; @@ -336,12 +332,15 @@ pub fn configure_and_expand( let lint_store = LintStoreExpandImpl(lint_store); let mut ecx = ExtCtxt::new(sess, cfg, resolver, Some(&lint_store)); - // Expand macros now! let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate)); // The rest is error reporting + sess.parse_sess.buffered_lints.with_lock(|buffered_lints: &mut Vec<BufferedEarlyLint>| { + buffered_lints.append(&mut ecx.buffered_early_lint); + }); + sess.time("check_unused_macros", || { ecx.check_unused_macros(); }); @@ -990,69 +989,6 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { Ok(()) } -fn encode_and_write_metadata( - tcx: TyCtxt<'_>, - outputs: &OutputFilenames, -) -> (EncodedMetadata, bool) { - #[derive(PartialEq, Eq, PartialOrd, Ord)] - enum MetadataKind { - None, - Uncompressed, - Compressed, - } - - let metadata_kind = tcx - .sess - .crate_types() - .iter() - .map(|ty| match *ty { - CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None, - - CrateType::Rlib => MetadataKind::Uncompressed, - - CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed, - }) - .max() - .unwrap_or(MetadataKind::None); - - let metadata = match metadata_kind { - MetadataKind::None => EncodedMetadata::new(), - MetadataKind::Uncompressed | MetadataKind::Compressed => encode_metadata(tcx), - }; - - let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata"); - - let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata); - if need_metadata_file { - let crate_name = tcx.crate_name(LOCAL_CRATE); - let out_filename = filename_for_metadata(tcx.sess, crate_name.as_str(), outputs); - // To avoid races with another rustc process scanning the output directory, - // we need to write the file somewhere else and atomically move it to its - // final destination, with an `fs::rename` call. In order for the rename to - // always succeed, the temporary file needs to be on the same filesystem, - // which is why we create it inside the output directory specifically. - let metadata_tmpdir = TempFileBuilder::new() - .prefix("rmeta") - .tempdir_in(out_filename.parent().unwrap()) - .unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err))); - let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps); - let metadata_filename = emit_metadata(tcx.sess, metadata.raw_data(), &metadata_tmpdir); - if let Err(e) = util::non_durable_rename(&metadata_filename, &out_filename) { - tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); - } - if tcx.sess.opts.json_artifact_notifications { - tcx.sess - .parse_sess - .span_diagnostic - .emit_artifact_notification(&out_filename, "metadata"); - } - } - - let need_metadata_module = metadata_kind == MetadataKind::Compressed; - - (metadata, need_metadata_module) -} - /// Runs the codegen backend, after which the AST and analysis can /// be discarded. pub fn start_codegen<'tcx>( @@ -1062,7 +998,8 @@ pub fn start_codegen<'tcx>( ) -> Box<dyn Any> { info!("Pre-codegen\n{:?}", tcx.debug_stats()); - let (metadata, need_metadata_module) = encode_and_write_metadata(tcx, outputs); + let (metadata, need_metadata_module) = + rustc_metadata::fs::encode_and_write_metadata(tcx, outputs); let codegen = tcx.sess.time("codegen_crate", move || { codegen_backend.codegen_crate(tcx, metadata, need_metadata_module) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 01173bff126..97856ecf22c 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -650,24 +650,6 @@ pub fn build_output_filenames( } } -#[cfg(not(target_os = "linux"))] -pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { - std::fs::rename(src, dst) -} - -/// This function attempts to bypass the auto_da_alloc heuristic implemented by some filesystems -/// such as btrfs and ext4. When renaming over a file that already exists then they will "helpfully" -/// write back the source file before committing the rename in case a developer forgot some of -/// the fsyncs in the open/write/fsync(file)/rename/fsync(dir) dance for atomic file updates. -/// -/// To avoid triggering this heuristic we delete the destination first, if it exists. -/// The cost of an extra syscall is much lower than getting descheduled for the sync IO. -#[cfg(target_os = "linux")] -pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { - let _ = std::fs::remove_file(dst); - std::fs::rename(src, dst) -} - /// Returns a version string such as "1.46.0 (04488afe3 2020-08-24)" pub fn version_str() -> Option<&'static str> { option_env!("CFG_VERSION") diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 5725c240320..13e3bb9a363 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -857,6 +857,18 @@ pub trait LintContext: Sized { Applicability::MachineApplicable, ); }, + BuiltinLintDiagnostics::NamedArgumentUsedPositionally(positional_arg, named_arg, name) => { + db.span_label(named_arg, "this named argument is only referred to by position in formatting string"); + let msg = format!("this formatting argument uses named argument `{}` by position", name); + db.span_label(positional_arg, msg); + db.span_suggestion_verbose( + positional_arg, + "use the named argument by name to avoid ambiguity", + format!("{{{}}}", name), + Applicability::MaybeIncorrect, + ); + + } } // Rewrap `db`, and pass control to the user. decorate(LintDiagnosticBuilder::new(db)); diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 3d42325d544..b0a7a87fda4 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -154,6 +154,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> self.check_id(closure_id); } } + run_early_pass!(self, check_fn_post, fk, span, id); } @@ -218,7 +219,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> // Explicitly check for lints associated with 'closure_id', since // it does not have a corresponding AST node match e.kind { - ast::ExprKind::Closure(_, ast::Async::Yes { closure_id, .. }, ..) + ast::ExprKind::Closure(_, _, ast::Async::Yes { closure_id, .. }, ..) | ast::ExprKind::Async(_, closure_id, ..) => self.check_id(closure_id), _ => {} } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 6d2cb63c1d7..39690851d1e 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3292,6 +3292,7 @@ declare_lint_pass! { TEST_UNSTABLE_LINT, FFI_UNWIND_CALLS, REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, + NAMED_ARGUMENTS_USED_POSITIONALLY, ] } @@ -3996,3 +3997,33 @@ declare_lint! { "call to foreign functions or function pointers with FFI-unwind ABI", @feature_gate = sym::c_unwind; } + +declare_lint! { + /// The `named_arguments_used_positionally` lint detects cases where named arguments are only + /// used positionally in format strings. This usage is valid but potentially very confusing. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(named_arguments_used_positionally)] + /// fn main() { + /// let _x = 5; + /// println!("{}", _x = 1); // Prints 1, will trigger lint + /// + /// println!("{}", _x); // Prints 5, no lint emitted + /// println!("{_x}", _x = _x); // Prints 5, no lint emitted + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Rust formatting strings can refer to named arguments by their position, but this usage is + /// potentially confusing. In particular, readers can incorrectly assume that the declaration + /// of named arguments is an assignment (which would produce the unit type). + /// For backwards compatibility, this is not a hard error. + pub NAMED_ARGUMENTS_USED_POSITIONALLY, + Warn, + "named arguments in format used positionally" +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 48f441e69d6..1bc7e7de660 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -467,6 +467,7 @@ pub enum BuiltinLintDiagnostics { /// If true, the lifetime will be fully elided. use_span: Option<(Span, bool)>, }, + NamedArgumentUsedPositionally(Span, Span, String), } /// Lints that are buffered up early on in the `Session` before the diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index 3ab4a8b7294..2c5db9d8b27 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -12,6 +12,7 @@ odht = { version = "0.3.1", features = ["nightly"] } snap = "1" tracing = "0.1" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +tempfile = "3.2" rustc_middle = { path = "../rustc_middle" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs new file mode 100644 index 00000000000..e6072901aaa --- /dev/null +++ b/compiler/rustc_metadata/src/fs.rs @@ -0,0 +1,137 @@ +use crate::{encode_metadata, EncodedMetadata}; + +use rustc_data_structures::temp_dir::MaybeTempDir; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::{CrateType, OutputFilenames, OutputType}; +use rustc_session::output::filename_for_metadata; +use rustc_session::Session; +use tempfile::Builder as TempFileBuilder; + +use std::fs; +use std::path::{Path, PathBuf}; + +// FIXME(eddyb) maybe include the crate name in this? +pub const METADATA_FILENAME: &str = "lib.rmeta"; + +/// We use a temp directory here to avoid races between concurrent rustc processes, +/// such as builds in the same directory using the same filename for metadata while +/// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a +/// directory being searched for `extern crate` (observing an incomplete file). +/// The returned path is the temporary file containing the complete metadata. +pub fn emit_metadata(sess: &Session, metadata: &[u8], tmpdir: &MaybeTempDir) -> PathBuf { + let out_filename = tmpdir.as_ref().join(METADATA_FILENAME); + let result = fs::write(&out_filename, metadata); + + if let Err(e) = result { + sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); + } + + out_filename +} + +pub fn encode_and_write_metadata( + tcx: TyCtxt<'_>, + outputs: &OutputFilenames, +) -> (EncodedMetadata, bool) { + #[derive(PartialEq, Eq, PartialOrd, Ord)] + enum MetadataKind { + None, + Uncompressed, + Compressed, + } + + let metadata_kind = tcx + .sess + .crate_types() + .iter() + .map(|ty| match *ty { + CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None, + + CrateType::Rlib => MetadataKind::Uncompressed, + + CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed, + }) + .max() + .unwrap_or(MetadataKind::None); + + let crate_name = tcx.crate_name(LOCAL_CRATE); + let out_filename = filename_for_metadata(tcx.sess, crate_name.as_str(), outputs); + // To avoid races with another rustc process scanning the output directory, + // we need to write the file somewhere else and atomically move it to its + // final destination, with an `fs::rename` call. In order for the rename to + // always succeed, the temporary file needs to be on the same filesystem, + // which is why we create it inside the output directory specifically. + let metadata_tmpdir = TempFileBuilder::new() + .prefix("rmeta") + .tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new(""))) + .unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err))); + let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps); + let metadata_filename = metadata_tmpdir.as_ref().join(METADATA_FILENAME); + + // Always create a file at `metadata_filename`, even if we have nothing to write to it. + // This simplifies the creation of the output `out_filename` when requested. + match metadata_kind { + MetadataKind::None => { + std::fs::File::create(&metadata_filename).unwrap_or_else(|e| { + tcx.sess.fatal(&format!( + "failed to create the file {}: {}", + metadata_filename.display(), + e + )) + }); + } + MetadataKind::Uncompressed | MetadataKind::Compressed => { + encode_metadata(tcx, &metadata_filename); + } + }; + + let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata"); + + // If the user requests metadata as output, rename `metadata_filename` + // to the expected output `out_filename`. The match above should ensure + // this file always exists. + let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata); + let (metadata_filename, metadata_tmpdir) = if need_metadata_file { + if let Err(e) = non_durable_rename(&metadata_filename, &out_filename) { + tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); + } + if tcx.sess.opts.json_artifact_notifications { + tcx.sess + .parse_sess + .span_diagnostic + .emit_artifact_notification(&out_filename, "metadata"); + } + (out_filename, None) + } else { + (metadata_filename, Some(metadata_tmpdir)) + }; + + // Load metadata back to memory: codegen may need to include it in object files. + let metadata = + EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|e| { + tcx.sess.fatal(&format!("failed to create encoded metadata from file: {}", e)) + }); + + let need_metadata_module = metadata_kind == MetadataKind::Compressed; + + (metadata, need_metadata_module) +} + +#[cfg(not(target_os = "linux"))] +pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { + std::fs::rename(src, dst) +} + +/// This function attempts to bypass the auto_da_alloc heuristic implemented by some filesystems +/// such as btrfs and ext4. When renaming over a file that already exists then they will "helpfully" +/// write back the source file before committing the rename in case a developer forgot some of +/// the fsyncs in the open/write/fsync(file)/rename/fsync(dir) dance for atomic file updates. +/// +/// To avoid triggering this heuristic we delete the destination first, if it exists. +/// The cost of an extra syscall is much lower than getting descheduled for the sync IO. +#[cfg(target_os = "linux")] +pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { + let _ = std::fs::remove_file(dst); + std::fs::rename(src, dst) +} diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 5ad16398695..6440f3e390c 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -34,6 +34,8 @@ mod native_libs; mod rmeta; pub mod creader; +pub mod fs; pub mod locator; +pub use fs::{emit_metadata, METADATA_FILENAME}; pub use rmeta::{encode_metadata, EncodedMetadata, METADATA_HEADER}; diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index ef5cb88c8f9..f0874f8f2da 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -21,7 +21,6 @@ use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::metadata::ModChild; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; -use rustc_middle::thir; use rustc_middle::ty::codec::TyDecoder; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::GeneratorDiagnosticData; @@ -638,7 +637,7 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Span { } } -impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [thir::abstract_const::Node<'tcx>] { +impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [ty::abstract_const::Node<'tcx>] { fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self { ty::codec::RefDecodable::decode(d) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index cd6847245e6..8e973009777 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -4,8 +4,10 @@ use crate::rmeta::*; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::memmap::{Mmap, MmapMut}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{join, par_iter, Lrc, ParallelIterator}; +use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{ @@ -27,8 +29,7 @@ use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt}; -use rustc_serialize::opaque::MemEncoder; -use rustc_serialize::{Encodable, Encoder}; +use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder}; use rustc_session::config::CrateType; use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib}; use rustc_span::hygiene::{ExpnIndex, HygieneEncodeContext, MacroKind}; @@ -39,12 +40,14 @@ use rustc_span::{ use rustc_target::abi::VariantIdx; use std::borrow::Borrow; use std::hash::Hash; +use std::io::{Read, Seek, Write}; use std::iter; use std::num::NonZeroUsize; +use std::path::{Path, PathBuf}; use tracing::{debug, trace}; pub(super) struct EncodeContext<'a, 'tcx> { - opaque: MemEncoder, + opaque: opaque::FileEncoder, tcx: TyCtxt<'tcx>, feat: &'tcx rustc_feature::Features, @@ -729,12 +732,19 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { assert_eq!(total_bytes, computed_total_bytes); if tcx.sess.meta_stats() { + self.opaque.flush(); + + // Rewind and re-read all the metadata to count the zero bytes we wrote. + let pos_before_rewind = self.opaque.file().stream_position().unwrap(); let mut zero_bytes = 0; - for e in self.opaque.data.iter() { - if *e == 0 { + self.opaque.file().rewind().unwrap(); + let file = std::io::BufReader::new(self.opaque.file()); + for e in file.bytes() { + if e.unwrap() == 0 { zero_bytes += 1; } } + assert_eq!(self.opaque.file().stream_position().unwrap(), pos_before_rewind); let perc = |bytes| (bytes * 100) as f64 / total_bytes as f64; let p = |label, bytes| { @@ -2133,24 +2143,58 @@ fn prefetch_mir(tcx: TyCtxt<'_>) { // will allow us to slice the metadata to the precise length that we just // generated regardless of trailing bytes that end up in it. -#[derive(Encodable, Decodable)] pub struct EncodedMetadata { - raw_data: Vec<u8>, + // The declaration order matters because `mmap` should be dropped before `_temp_dir`. + mmap: Option<Mmap>, + // We need to carry MaybeTempDir to avoid deleting the temporary + // directory while accessing the Mmap. + _temp_dir: Option<MaybeTempDir>, } impl EncodedMetadata { #[inline] - pub fn new() -> EncodedMetadata { - EncodedMetadata { raw_data: Vec::new() } + pub fn from_path(path: PathBuf, temp_dir: Option<MaybeTempDir>) -> std::io::Result<Self> { + let file = std::fs::File::open(&path)?; + let file_metadata = file.metadata()?; + if file_metadata.len() == 0 { + return Ok(Self { mmap: None, _temp_dir: None }); + } + let mmap = unsafe { Some(Mmap::map(file)?) }; + Ok(Self { mmap, _temp_dir: temp_dir }) } #[inline] pub fn raw_data(&self) -> &[u8] { - &self.raw_data + self.mmap.as_ref().map(|mmap| mmap.as_ref()).unwrap_or_default() + } +} + +impl<S: Encoder> Encodable<S> for EncodedMetadata { + fn encode(&self, s: &mut S) { + let slice = self.raw_data(); + slice.encode(s) + } +} + +impl<D: Decoder> Decodable<D> for EncodedMetadata { + fn decode(d: &mut D) -> Self { + let len = d.read_usize(); + let mmap = if len > 0 { + let mut mmap = MmapMut::map_anon(len).unwrap(); + for _ in 0..len { + (&mut mmap[..]).write(&[d.read_u8()]).unwrap(); + } + mmap.flush().unwrap(); + Some(mmap.make_read_only().unwrap()) + } else { + None + }; + + Self { mmap, _temp_dir: None } } } -pub fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata { +pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) { let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata"); // Since encoding metadata is not in a query, and nothing is cached, @@ -2158,7 +2202,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata { tcx.dep_graph.assert_ignored(); join( - || encode_metadata_impl(tcx), + || encode_metadata_impl(tcx, path), || { if tcx.sess.threads() == 1 { return; @@ -2168,12 +2212,12 @@ pub fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata { // It can be removed if it turns out to cause trouble or be detrimental to performance. join(|| prefetch_mir(tcx), || tcx.exported_symbols(LOCAL_CRATE)); }, - ) - .0 + ); } -fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata { - let mut encoder = MemEncoder::new(); +fn encode_metadata_impl(tcx: TyCtxt<'_>, path: &Path) { + let mut encoder = opaque::FileEncoder::new(path) + .unwrap_or_else(|err| tcx.sess.fatal(&format!("failed to create file encoder: {}", err))); encoder.emit_raw_bytes(METADATA_HEADER); // Will be filled with the root position after encoding everything. @@ -2208,20 +2252,29 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata { // culminating in the `CrateRoot` which points to all of it. let root = ecx.encode_crate_root(); - let mut result = ecx.opaque.finish(); + ecx.opaque.flush(); + + let mut file = ecx.opaque.file(); + // We will return to this position after writing the root position. + let pos_before_seek = file.stream_position().unwrap(); // Encode the root position. let header = METADATA_HEADER.len(); + file.seek(std::io::SeekFrom::Start(header as u64)) + .unwrap_or_else(|err| tcx.sess.fatal(&format!("failed to seek the file: {}", err))); let pos = root.position.get(); - result[header + 0] = (pos >> 24) as u8; - result[header + 1] = (pos >> 16) as u8; - result[header + 2] = (pos >> 8) as u8; - result[header + 3] = (pos >> 0) as u8; + file.write_all(&[(pos >> 24) as u8, (pos >> 16) as u8, (pos >> 8) as u8, (pos >> 0) as u8]) + .unwrap_or_else(|err| tcx.sess.fatal(&format!("failed to write to the file: {}", err))); - // Record metadata size for self-profiling - tcx.prof.artifact_size("crate_metadata", "crate_metadata", result.len() as u64); + // Return to the position where we are before writing the root position. + file.seek(std::io::SeekFrom::Start(pos_before_seek)).unwrap(); - EncodedMetadata { raw_data: result } + // Record metadata size for self-profiling + tcx.prof.artifact_size( + "crate_metadata", + "crate_metadata", + file.metadata().unwrap().len() as u64, + ); } pub fn provide(providers: &mut Providers) { @@ -2242,5 +2295,5 @@ pub fn provide(providers: &mut Providers) { }, ..*providers - }; + } } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index a50eb2a71cf..af1c09f4ae8 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -17,12 +17,11 @@ use rustc_middle::metadata::ModChild; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use rustc_middle::mir; -use rustc_middle::thir; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, ReprOptions, Ty}; use rustc_middle::ty::{GeneratorDiagnosticData, ParameterizedOverTcx, TyCtxt}; -use rustc_serialize::opaque::MemEncoder; +use rustc_serialize::opaque::FileEncoder; use rustc_session::config::SymbolManglingVersion; use rustc_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib}; use rustc_span::edition::Edition; @@ -323,7 +322,7 @@ macro_rules! define_tables { } impl TableBuilders { - fn encode(&self, buf: &mut MemEncoder) -> LazyTables { + fn encode(&self, buf: &mut FileEncoder) -> LazyTables { LazyTables { $($name: self.$name.encode(buf)),+ } @@ -361,7 +360,7 @@ define_tables! { mir_for_ctfe: Table<DefIndex, LazyValue<mir::Body<'static>>>, promoted_mir: Table<DefIndex, LazyValue<IndexVec<mir::Promoted, mir::Body<'static>>>>, // FIXME(compiler-errors): Why isn't this a LazyArray? - thir_abstract_const: Table<DefIndex, LazyValue<&'static [thir::abstract_const::Node<'static>]>>, + thir_abstract_const: Table<DefIndex, LazyValue<&'static [ty::abstract_const::Node<'static>]>>, impl_parent: Table<DefIndex, RawDefId>, impl_polarity: Table<DefIndex, ty::ImplPolarity>, constness: Table<DefIndex, hir::Constness>, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 5ab4269ae99..42759f0a652 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -4,8 +4,8 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_hir::def::{CtorKind, CtorOf}; use rustc_index::vec::Idx; use rustc_middle::ty::ParameterizedOverTcx; -use rustc_serialize::opaque::MemEncoder; -use rustc_serialize::Encoder; +use rustc_serialize::opaque::FileEncoder; +use rustc_serialize::Encoder as _; use rustc_span::hygiene::MacroKind; use std::convert::TryInto; use std::marker::PhantomData; @@ -281,7 +281,7 @@ where Some(value).write_to_bytes(&mut self.blocks[i]); } - pub(crate) fn encode<const N: usize>(&self, buf: &mut MemEncoder) -> LazyTable<I, T> + pub(crate) fn encode<const N: usize>(&self, buf: &mut FileEncoder) -> LazyTable<I, T> where Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>, { diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index d3e10fce8a0..3a59b2069b3 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -22,7 +22,7 @@ fn fn_decl<'hir>(node: Node<'hir>) -> Option<&'hir FnDecl<'hir>> { Node::Item(Item { kind: ItemKind::Fn(sig, _, _), .. }) | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(sig, _), .. }) | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(sig, _), .. }) => Some(&sig.decl), - Node::Expr(Expr { kind: ExprKind::Closure { fn_decl, .. }, .. }) + Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl, .. }), .. }) | Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_decl, ..), .. }) => { Some(fn_decl) } @@ -39,6 +39,7 @@ pub fn fn_sig<'hir>(node: Node<'hir>) -> Option<&'hir FnSig<'hir>> { } } +#[inline] pub fn associated_body<'hir>(node: Node<'hir>) -> Option<BodyId> { match node { Node::Item(Item { @@ -54,7 +55,7 @@ pub fn associated_body<'hir>(node: Node<'hir>) -> Option<BodyId> { kind: ImplItemKind::Const(_, body) | ImplItemKind::Fn(_, body), .. }) - | Node::Expr(Expr { kind: ExprKind::Closure { body, .. }, .. }) => Some(*body), + | Node::Expr(Expr { kind: ExprKind::Closure(Closure { body, .. }), .. }) => Some(*body), Node::AnonConst(constant) => Some(constant.body), @@ -279,8 +280,8 @@ impl<'hir> Map<'hir> { } Node::Field(_) => DefKind::Field, Node::Expr(expr) => match expr.kind { - ExprKind::Closure { movability: None, .. } => DefKind::Closure, - ExprKind::Closure { movability: Some(_), .. } => DefKind::Generator, + ExprKind::Closure(Closure { movability: None, .. }) => DefKind::Closure, + ExprKind::Closure(Closure { movability: Some(_), .. }) => DefKind::Generator, _ => bug!("def_kind: unsupported node: {}", self.node_to_string(hir_id)), }, Node::GenericParam(param) => match param.kind { @@ -486,35 +487,13 @@ impl<'hir> Map<'hir> { /// crate. If you would prefer to iterate over the bodies /// themselves, you can do `self.hir().krate().body_ids.iter()`. pub fn body_owners(self) -> impl Iterator<Item = LocalDefId> + 'hir { - self.krate() - .owners - .iter_enumerated() - .flat_map(move |(owner, owner_info)| { - let bodies = &owner_info.as_owner()?.nodes.bodies; - Some(bodies.iter().map(move |&(local_id, _)| { - let hir_id = HirId { owner, local_id }; - let body_id = BodyId { hir_id }; - self.body_owner_def_id(body_id) - })) - }) - .flatten() + self.tcx.hir_crate_items(()).body_owners.iter().copied() } pub fn par_body_owners<F: Fn(LocalDefId) + Sync + Send>(self, f: F) { use rustc_data_structures::sync::{par_iter, ParallelIterator}; - #[cfg(parallel_compiler)] - use rustc_rayon::iter::IndexedParallelIterator; - - par_iter(&self.krate().owners.raw).enumerate().for_each(|(owner, owner_info)| { - let owner = LocalDefId::new(owner); - if let MaybeOwner::Owner(owner_info) = owner_info { - par_iter(owner_info.nodes.bodies.range(..)).for_each(|(local_id, _)| { - let hir_id = HirId { owner, local_id: *local_id }; - let body_id = BodyId { hir_id }; - f(self.body_owner_def_id(body_id)) - }) - } - }); + + par_iter(&self.tcx.hir_crate_items(()).body_owners[..]).for_each(|&def_id| f(def_id)); } pub fn ty_param_owner(self, def_id: LocalDefId) -> LocalDefId { @@ -1021,7 +1000,9 @@ impl<'hir> Map<'hir> { _ => named_span(item.span, item.ident, None), }, Node::Ctor(_) => return self.opt_span(self.get_parent_node(hir_id)), - Node::Expr(Expr { kind: ExprKind::Closure { fn_decl_span, .. }, .. }) => *fn_decl_span, + Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl_span, .. }), .. }) => { + *fn_decl_span + } _ => self.span_with_body(hir_id), }; Some(span) @@ -1281,85 +1262,48 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { } pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalDefId) -> ModuleItems { - let mut collector = ModuleCollector { - tcx, - submodules: Vec::default(), - items: Vec::default(), - trait_items: Vec::default(), - impl_items: Vec::default(), - foreign_items: Vec::default(), - }; + let mut collector = ItemCollector::new(tcx, false); let (hir_mod, span, hir_id) = tcx.hir().get_module(module_id); collector.visit_mod(hir_mod, span, hir_id); - let ModuleCollector { submodules, items, trait_items, impl_items, foreign_items, .. } = - collector; + let ItemCollector { + submodules, + items, + trait_items, + impl_items, + foreign_items, + body_owners, + .. + } = collector; return ModuleItems { submodules: submodules.into_boxed_slice(), items: items.into_boxed_slice(), trait_items: trait_items.into_boxed_slice(), impl_items: impl_items.into_boxed_slice(), foreign_items: foreign_items.into_boxed_slice(), + body_owners: body_owners.into_boxed_slice(), }; - - struct ModuleCollector<'tcx> { - tcx: TyCtxt<'tcx>, - submodules: Vec<LocalDefId>, - items: Vec<ItemId>, - trait_items: Vec<TraitItemId>, - impl_items: Vec<ImplItemId>, - foreign_items: Vec<ForeignItemId>, - } - - impl<'hir> Visitor<'hir> for ModuleCollector<'hir> { - type NestedFilter = nested_filter::All; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - fn visit_item(&mut self, item: &'hir Item<'hir>) { - self.items.push(item.item_id()); - if let ItemKind::Mod(..) = item.kind { - // If this declares another module, do not recurse inside it. - self.submodules.push(item.def_id); - } else { - intravisit::walk_item(self, item) - } - } - - fn visit_trait_item(&mut self, item: &'hir TraitItem<'hir>) { - self.trait_items.push(item.trait_item_id()); - intravisit::walk_trait_item(self, item) - } - - fn visit_impl_item(&mut self, item: &'hir ImplItem<'hir>) { - self.impl_items.push(item.impl_item_id()); - intravisit::walk_impl_item(self, item) - } - - fn visit_foreign_item(&mut self, item: &'hir ForeignItem<'hir>) { - self.foreign_items.push(item.foreign_item_id()); - intravisit::walk_foreign_item(self, item) - } - } } pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { - let mut collector = CrateCollector { - tcx, - submodules: Vec::default(), - items: Vec::default(), - trait_items: Vec::default(), - impl_items: Vec::default(), - foreign_items: Vec::default(), - }; + let mut collector = ItemCollector::new(tcx, true); + // A "crate collector" and "module collector" start at a + // module item (the former starts at the crate root) but only + // the former needs to collect it. ItemCollector does not do this for us. + collector.submodules.push(CRATE_DEF_ID); tcx.hir().walk_toplevel_module(&mut collector); - let CrateCollector { submodules, items, trait_items, impl_items, foreign_items, .. } = - collector; + let ItemCollector { + submodules, + items, + trait_items, + impl_items, + foreign_items, + body_owners, + .. + } = collector; return ModuleItems { submodules: submodules.into_boxed_slice(), @@ -1367,47 +1311,96 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { trait_items: trait_items.into_boxed_slice(), impl_items: impl_items.into_boxed_slice(), foreign_items: foreign_items.into_boxed_slice(), + body_owners: body_owners.into_boxed_slice(), }; +} - struct CrateCollector<'tcx> { - tcx: TyCtxt<'tcx>, - submodules: Vec<LocalDefId>, - items: Vec<ItemId>, - trait_items: Vec<TraitItemId>, - impl_items: Vec<ImplItemId>, - foreign_items: Vec<ForeignItemId>, +struct ItemCollector<'tcx> { + // When true, it collects all items in the create, + // otherwise it collects items in some module. + crate_collector: bool, + tcx: TyCtxt<'tcx>, + submodules: Vec<LocalDefId>, + items: Vec<ItemId>, + trait_items: Vec<TraitItemId>, + impl_items: Vec<ImplItemId>, + foreign_items: Vec<ForeignItemId>, + body_owners: Vec<LocalDefId>, +} + +impl<'tcx> ItemCollector<'tcx> { + fn new(tcx: TyCtxt<'tcx>, crate_collector: bool) -> ItemCollector<'tcx> { + ItemCollector { + crate_collector, + tcx, + submodules: Vec::default(), + items: Vec::default(), + trait_items: Vec::default(), + impl_items: Vec::default(), + foreign_items: Vec::default(), + body_owners: Vec::default(), + } } +} + +impl<'hir> Visitor<'hir> for ItemCollector<'hir> { + type NestedFilter = nested_filter::All; - impl<'hir> Visitor<'hir> for CrateCollector<'hir> { - type NestedFilter = nested_filter::All; + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() + fn visit_item(&mut self, item: &'hir Item<'hir>) { + if associated_body(Node::Item(item)).is_some() { + self.body_owners.push(item.def_id); } - fn visit_item(&mut self, item: &'hir Item<'hir>) { - self.items.push(item.item_id()); + self.items.push(item.item_id()); + + // Items that are modules are handled here instead of in visit_mod. + if let ItemKind::Mod(module) = &item.kind { + self.submodules.push(item.def_id); + // A module collector does not recurse inside nested modules. + if self.crate_collector { + intravisit::walk_mod(self, module, item.hir_id()); + } + } else { intravisit::walk_item(self, item) } + } - fn visit_mod(&mut self, m: &'hir Mod<'hir>, _s: Span, n: HirId) { - self.submodules.push(n.owner); - intravisit::walk_mod(self, m, n); - } + fn visit_foreign_item(&mut self, item: &'hir ForeignItem<'hir>) { + self.foreign_items.push(item.foreign_item_id()); + intravisit::walk_foreign_item(self, item) + } + + fn visit_anon_const(&mut self, c: &'hir AnonConst) { + self.body_owners.push(self.tcx.hir().local_def_id(c.hir_id)); + intravisit::walk_anon_const(self, c) + } - fn visit_foreign_item(&mut self, item: &'hir ForeignItem<'hir>) { - self.foreign_items.push(item.foreign_item_id()); - intravisit::walk_foreign_item(self, item) + fn visit_expr(&mut self, ex: &'hir Expr<'hir>) { + if matches!(ex.kind, ExprKind::Closure { .. }) { + self.body_owners.push(self.tcx.hir().local_def_id(ex.hir_id)); } + intravisit::walk_expr(self, ex) + } - fn visit_trait_item(&mut self, item: &'hir TraitItem<'hir>) { - self.trait_items.push(item.trait_item_id()); - intravisit::walk_trait_item(self, item) + fn visit_trait_item(&mut self, item: &'hir TraitItem<'hir>) { + if associated_body(Node::TraitItem(item)).is_some() { + self.body_owners.push(item.def_id); } - fn visit_impl_item(&mut self, item: &'hir ImplItem<'hir>) { - self.impl_items.push(item.impl_item_id()); - intravisit::walk_impl_item(self, item) + self.trait_items.push(item.trait_item_id()); + intravisit::walk_trait_item(self, item) + } + + fn visit_impl_item(&mut self, item: &'hir ImplItem<'hir>) { + if associated_body(Node::ImplItem(item)).is_some() { + self.body_owners.push(item.def_id); } + + self.impl_items.push(item.impl_item_id()); + intravisit::walk_impl_item(self, item) } } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 070a063c881..a605e234be9 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -44,6 +44,7 @@ pub struct ModuleItems { trait_items: Box<[TraitItemId]>, impl_items: Box<[ImplItemId]>, foreign_items: Box<[ForeignItemId]>, + body_owners: Box<[LocalDefId]>, } impl ModuleItems { diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 2de8d318090..96e068a3601 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -471,13 +471,15 @@ impl<'tcx> TyCtxt<'tcx> { /// /// This function will also check if the item is deprecated. /// If so, and `id` is not `None`, a deprecated lint attached to `id` will be emitted. + /// + /// Returns `true` if item is allowed aka, stable or unstable under an enabled feature. pub fn check_stability( self, def_id: DefId, id: Option<HirId>, span: Span, method_span: Option<Span>, - ) { + ) -> bool { self.check_stability_allow_unstable(def_id, id, span, method_span, AllowUnstable::No) } @@ -490,6 +492,8 @@ impl<'tcx> TyCtxt<'tcx> { /// If so, and `id` is not `None`, a deprecated lint attached to `id` will be emitted. /// /// Pass `AllowUnstable::Yes` to `allow_unstable` to force an unstable item to be allowed. Deprecation warnings will be emitted normally. + /// + /// Returns `true` if item is allowed aka, stable or unstable under an enabled feature. pub fn check_stability_allow_unstable( self, def_id: DefId, @@ -497,7 +501,7 @@ impl<'tcx> TyCtxt<'tcx> { span: Span, method_span: Option<Span>, allow_unstable: AllowUnstable, - ) { + ) -> bool { self.check_optional_stability( def_id, id, @@ -516,6 +520,8 @@ impl<'tcx> TyCtxt<'tcx> { /// missing stability attributes (not necessarily just emit a `bug!`). This is necessary /// for default generic parameters, which only have stability attributes if they were /// added after the type on which they're defined. + /// + /// Returns `true` if item is allowed aka, stable or unstable under an enabled feature. pub fn check_optional_stability( self, def_id: DefId, @@ -524,13 +530,16 @@ impl<'tcx> TyCtxt<'tcx> { method_span: Option<Span>, allow_unstable: AllowUnstable, unmarked: impl FnOnce(Span, DefId), - ) { + ) -> bool { let soft_handler = |lint, span, msg: &_| { self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| { lint.build(msg).emit(); }) }; - match self.eval_stability_allow_unstable(def_id, id, span, method_span, allow_unstable) { + let eval_result = + self.eval_stability_allow_unstable(def_id, id, span, method_span, allow_unstable); + let is_allowed = matches!(eval_result, EvalResult::Allow); + match eval_result { EvalResult::Allow => {} EvalResult::Deny { feature, reason, issue, suggestion, is_soft } => report_unstable( self.sess, @@ -544,6 +553,8 @@ impl<'tcx> TyCtxt<'tcx> { ), EvalResult::Unmarked => unmarked(span, def_id), } + + is_allowed } pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index cbc45526e89..bdae7e5fcd6 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -351,7 +351,7 @@ rustc_queries! { /// Try to build an abstract representation of the given constant. query thir_abstract_const( key: DefId - ) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorGuaranteed> { + ) -> Result<Option<&'tcx [ty::abstract_const::Node<'tcx>]>, ErrorGuaranteed> { desc { |tcx| "building an abstract representation for {}", tcx.def_path_str(key), } @@ -360,7 +360,7 @@ rustc_queries! { /// Try to build an abstract representation of the given constant. query thir_abstract_const_of_const_arg( key: (LocalDefId, DefId) - ) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorGuaranteed> { + ) -> Result<Option<&'tcx [ty::abstract_const::Node<'tcx>]>, ErrorGuaranteed> { desc { |tcx| "building an abstract representation for the const argument {}", diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 3e5f6bb8f0b..36db8d04918 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -30,7 +30,6 @@ use rustc_target::asm::InlineAsmRegOrRegClass; use std::fmt; use std::ops::Index; -pub mod abstract_const; pub mod visit; newtype_index! { diff --git a/compiler/rustc_middle/src/thir/abstract_const.rs b/compiler/rustc_middle/src/thir/abstract_const.rs deleted file mode 100644 index 527dbd1cd09..00000000000 --- a/compiler/rustc_middle/src/thir/abstract_const.rs +++ /dev/null @@ -1,61 +0,0 @@ -//! A subset of a mir body used for const evaluatability checking. -use crate::mir; -use crate::ty::{self, Ty, TyCtxt}; -use rustc_errors::ErrorGuaranteed; - -rustc_index::newtype_index! { - /// An index into an `AbstractConst`. - pub struct NodeId { - derive [HashStable] - DEBUG_FORMAT = "n{}", - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] -pub enum CastKind { - /// thir::ExprKind::As - As, - /// thir::ExprKind::Use - Use, -} - -/// A node of an `AbstractConst`. -#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] -pub enum Node<'tcx> { - Leaf(ty::Const<'tcx>), - Binop(mir::BinOp, NodeId, NodeId), - UnaryOp(mir::UnOp, NodeId), - FunctionCall(NodeId, &'tcx [NodeId]), - Cast(CastKind, NodeId, Ty<'tcx>), -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] -pub enum NotConstEvaluatable { - Error(ErrorGuaranteed), - MentionsInfer, - MentionsParam, -} - -impl From<ErrorGuaranteed> for NotConstEvaluatable { - fn from(e: ErrorGuaranteed) -> NotConstEvaluatable { - NotConstEvaluatable::Error(e) - } -} - -TrivialTypeTraversalAndLiftImpls! { - NotConstEvaluatable, -} - -impl<'tcx> TyCtxt<'tcx> { - #[inline] - pub fn thir_abstract_const_opt_const_arg( - self, - def: ty::WithOptConstParam<rustc_hir::def_id::DefId>, - ) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> { - if let Some((did, param_did)) = def.as_const_arg() { - self.thir_abstract_const_of_const_arg((did, param_did)) - } else { - self.thir_abstract_const(def.did) - } - } -} diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index fe7f72024d3..955f2bdfa1d 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -10,7 +10,7 @@ mod structural_impls; pub mod util; use crate::infer::canonical::Canonical; -use crate::thir::abstract_const::NotConstEvaluatable; +use crate::ty::abstract_const::NotConstEvaluatable; use crate::ty::subst::SubstsRef; use crate::ty::{self, AdtKind, Ty, TyCtxt}; diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs new file mode 100644 index 00000000000..bed809930da --- /dev/null +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -0,0 +1,194 @@ +//! A subset of a mir body used for const evaluatability checking. +use crate::mir; +use crate::ty::visit::TypeVisitable; +use crate::ty::{self, subst::Subst, DelaySpanBugEmitted, EarlyBinder, SubstsRef, Ty, TyCtxt}; +use rustc_errors::ErrorGuaranteed; +use rustc_hir::def_id::DefId; +use std::cmp; +use std::ops::ControlFlow; + +rustc_index::newtype_index! { + /// An index into an `AbstractConst`. + pub struct NodeId { + derive [HashStable] + DEBUG_FORMAT = "n{}", + } +} + +/// A tree representing an anonymous constant. +/// +/// This is only able to represent a subset of `MIR`, +/// and should not leak any information about desugarings. +#[derive(Debug, Clone, Copy)] +pub struct AbstractConst<'tcx> { + // FIXME: Consider adding something like `IndexSlice` + // and use this here. + inner: &'tcx [Node<'tcx>], + substs: SubstsRef<'tcx>, +} + +impl<'tcx> AbstractConst<'tcx> { + pub fn new( + tcx: TyCtxt<'tcx>, + uv: ty::Unevaluated<'tcx, ()>, + ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> { + let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?; + debug!("AbstractConst::new({:?}) = {:?}", uv, inner); + Ok(inner.map(|inner| AbstractConst { inner, substs: tcx.erase_regions(uv.substs) })) + } + + pub fn from_const( + tcx: TyCtxt<'tcx>, + ct: ty::Const<'tcx>, + ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> { + match ct.kind() { + ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv.shrink()), + ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => Err(reported), + _ => Ok(None), + } + } + + #[inline] + pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> { + AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs } + } + + #[inline] + pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> { + let node = self.inner.last().copied().unwrap(); + match node { + Node::Leaf(leaf) => Node::Leaf(EarlyBinder(leaf).subst(tcx, self.substs)), + Node::Cast(kind, operand, ty) => { + Node::Cast(kind, operand, EarlyBinder(ty).subst(tcx, self.substs)) + } + // Don't perform substitution on the following as they can't directly contain generic params + Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node, + } + } + + pub fn unify_failure_kind(self, tcx: TyCtxt<'tcx>) -> FailureKind { + let mut failure_kind = FailureKind::Concrete; + walk_abstract_const::<!, _>(tcx, self, |node| { + match node.root(tcx) { + Node::Leaf(leaf) => { + if leaf.has_infer_types_or_consts() { + failure_kind = FailureKind::MentionsInfer; + } else if leaf.has_param_types_or_consts() { + failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); + } + } + Node::Cast(_, _, ty) => { + if ty.has_infer_types_or_consts() { + failure_kind = FailureKind::MentionsInfer; + } else if ty.has_param_types_or_consts() { + failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); + } + } + Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => {} + } + ControlFlow::CONTINUE + }); + failure_kind + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +pub enum CastKind { + /// thir::ExprKind::As + As, + /// thir::ExprKind::Use + Use, +} + +/// A node of an `AbstractConst`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +pub enum Node<'tcx> { + Leaf(ty::Const<'tcx>), + Binop(mir::BinOp, NodeId, NodeId), + UnaryOp(mir::UnOp, NodeId), + FunctionCall(NodeId, &'tcx [NodeId]), + Cast(CastKind, NodeId, Ty<'tcx>), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +pub enum NotConstEvaluatable { + Error(ErrorGuaranteed), + MentionsInfer, + MentionsParam, +} + +impl From<ErrorGuaranteed> for NotConstEvaluatable { + fn from(e: ErrorGuaranteed) -> NotConstEvaluatable { + NotConstEvaluatable::Error(e) + } +} + +TrivialTypeTraversalAndLiftImpls! { + NotConstEvaluatable, +} + +impl<'tcx> TyCtxt<'tcx> { + #[inline] + pub fn thir_abstract_const_opt_const_arg( + self, + def: ty::WithOptConstParam<DefId>, + ) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> { + if let Some((did, param_did)) = def.as_const_arg() { + self.thir_abstract_const_of_const_arg((did, param_did)) + } else { + self.thir_abstract_const(def.did) + } + } +} + +#[instrument(skip(tcx, f), level = "debug")] +pub fn walk_abstract_const<'tcx, R, F>( + tcx: TyCtxt<'tcx>, + ct: AbstractConst<'tcx>, + mut f: F, +) -> ControlFlow<R> +where + F: FnMut(AbstractConst<'tcx>) -> ControlFlow<R>, +{ + #[instrument(skip(tcx, f), level = "debug")] + fn recurse<'tcx, R>( + tcx: TyCtxt<'tcx>, + ct: AbstractConst<'tcx>, + f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>, + ) -> ControlFlow<R> { + f(ct)?; + let root = ct.root(tcx); + debug!(?root); + match root { + Node::Leaf(_) => ControlFlow::CONTINUE, + Node::Binop(_, l, r) => { + recurse(tcx, ct.subtree(l), f)?; + recurse(tcx, ct.subtree(r), f) + } + Node::UnaryOp(_, v) => recurse(tcx, ct.subtree(v), f), + Node::FunctionCall(func, args) => { + recurse(tcx, ct.subtree(func), f)?; + args.iter().try_for_each(|&arg| recurse(tcx, ct.subtree(arg), f)) + } + Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f), + } + } + + recurse(tcx, ct, &mut f) +} + +// We were unable to unify the abstract constant with +// a constant found in the caller bounds, there are +// now three possible cases here. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum FailureKind { + /// The abstract const still references an inference + /// variable, in this case we return `TooGeneric`. + MentionsInfer, + /// The abstract const references a generic parameter, + /// this means that we emit an error here. + MentionsParam, + /// The substs are concrete enough that we can simply + /// try and evaluate the given constant. + Concrete, +} diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index f5ce43f3afb..8ead0512274 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -128,6 +128,14 @@ impl<'tcx> ClosureKind { None } } + + pub fn to_def_id(&self, tcx: TyCtxt<'_>) -> DefId { + match self { + ClosureKind::Fn => tcx.lang_items().fn_once_trait().unwrap(), + ClosureKind::FnMut => tcx.lang_items().fn_mut_trait().unwrap(), + ClosureKind::FnOnce => tcx.lang_items().fn_trait().unwrap(), + } + } } /// A composite describing a `Place` that is captured by a closure. diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 9a363914dc3..e6ea3d88853 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -12,7 +12,6 @@ use crate::mir::{ self, interpret::{AllocId, ConstAllocation}, }; -use crate::thir; use crate::traits; use crate::ty::subst::SubstsRef; use crate::ty::{self, AdtDef, Ty}; @@ -346,7 +345,7 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> } impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> - for [thir::abstract_const::Node<'tcx>] + for [ty::abstract_const::Node<'tcx>] { fn decode(decoder: &mut D) -> &'tcx Self { decoder.interner().arena.alloc_from_iter( @@ -356,7 +355,7 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> } impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> - for [thir::abstract_const::NodeId] + for [ty::abstract_const::NodeId] { fn decode(decoder: &mut D) -> &'tcx Self { decoder.interner().arena.alloc_from_iter( diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 22a1fd34e27..f0acb02933a 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -92,6 +92,7 @@ pub use self::sty::{ pub use self::trait_def::TraitDef; pub mod _match; +pub mod abstract_const; pub mod adjustment; pub mod binding; pub mod cast; diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 54ba9e84fdb..e189ee2fc4d 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -3,7 +3,7 @@ use rustc_index::vec::{Idx, IndexVec}; use crate::middle::exported_symbols::ExportedSymbol; use crate::mir::Body; -use crate::thir::abstract_const::Node; +use crate::ty::abstract_const::Node; use crate::ty::{ self, Const, FnSig, GeneratorDiagnosticData, GenericPredicates, Predicate, TraitRef, Ty, }; diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index e6d42af9817..7ae26cccb38 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -68,9 +68,10 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_ // Figure out what primary body this item has. let (body_id, return_ty_span, span_with_body) = match tcx.hir().get(id) { - Node::Expr(hir::Expr { kind: hir::ExprKind::Closure { fn_decl, body, .. }, .. }) => { - (*body, fn_decl.output.span(), None) - } + Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { fn_decl, body, .. }), + .. + }) => (*body, fn_decl.output.span(), None), Node::Item(hir::Item { kind: hir::ItemKind::Fn(hir::FnSig { decl, .. }, _, body_id), span, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 0d9c57908c1..478d5e4792a 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -15,10 +15,10 @@ use rustc_ast::util::classify; use rustc_ast::util::literal::LitError; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; use rustc_ast::visit::Visitor; -use rustc_ast::StmtKind; use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp, DUMMY_NODE_ID}; use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind}; use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; +use rustc_ast::{ClosureBinder, StmtKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult}; @@ -1343,11 +1343,7 @@ impl<'a> Parser<'a> { self.parse_if_expr(attrs) } else if self.check_keyword(kw::For) { if self.choose_generics_over_qpath(1) { - // NOTE(Centril, eddyb): DO NOT REMOVE! Beyond providing parser recovery, - // this is an insurance policy in case we allow qpaths in (tuple-)struct patterns. - // When `for <Foo as Bar>::Proj in $expr $block` is wanted, - // you can disambiguate in favor of a pattern with `(...)`. - self.recover_quantified_closure_expr(attrs) + self.parse_closure_expr(attrs) } else { assert!(self.eat_keyword(kw::For)); self.parse_for_expr(None, self.prev_token.span, attrs) @@ -2096,29 +2092,21 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new())) } - /// Recover on an explicitly quantified closure expression, e.g., `for<'a> |x: &'a u8| *x + 1`. - fn recover_quantified_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + /// Parses a closure expression (e.g., `move |args| expr`). + fn parse_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { let lo = self.token.span; - let _ = self.parse_late_bound_lifetime_defs()?; - let span_for = lo.to(self.prev_token.span); - let closure = self.parse_closure_expr(attrs)?; - self.struct_span_err(span_for, "cannot introduce explicit parameters for a closure") - .span_label(closure.span, "the parameters are attached to this closure") - .span_suggestion( - span_for, - "remove the parameters", - "", - Applicability::MachineApplicable, - ) - .emit(); + let binder = if self.check_keyword(kw::For) { + let lo = self.token.span; + let lifetime_defs = self.parse_late_bound_lifetime_defs()?; + let span = lo.to(self.prev_token.span); - Ok(self.mk_expr_err(lo.to(closure.span))) - } + self.sess.gated_spans.gate(sym::closure_lifetime_binder, span); - /// Parses a closure expression (e.g., `move |args| expr`). - fn parse_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { - let lo = self.token.span; + ClosureBinder::For { span, generic_params: P::from_vec(lifetime_defs) } + } else { + ClosureBinder::NotPresent + }; let movability = if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; @@ -2162,7 +2150,15 @@ impl<'a> Parser<'a> { let closure = self.mk_expr( lo.to(body.span), - ExprKind::Closure(capture_clause, asyncness, movability, decl, body, lo.to(decl_hi)), + ExprKind::Closure( + binder, + capture_clause, + asyncness, + movability, + decl, + body, + lo.to(decl_hi), + ), attrs, ); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f3d8a09a297..e626a1e4ed1 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -139,6 +139,7 @@ impl CheckAttrVisitor<'_> { | sym::rustc_const_stable | sym::unstable | sym::stable + | sym::rustc_allowed_through_unstable_modules | sym::rustc_promotable => self.check_stability_promotable(&attr, span, target), _ => true, }; diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs index 68b2a052391..cdda0e388dd 100644 --- a/compiler/rustc_passes/src/loops.rs +++ b/compiler/rustc_passes/src/loops.rs @@ -57,7 +57,13 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { hir::ExprKind::Loop(ref b, _, source, _) => { self.with_context(Loop(source), |v| v.visit_block(&b)); } - hir::ExprKind::Closure { ref fn_decl, body, fn_decl_span, movability, .. } => { + hir::ExprKind::Closure(&hir::Closure { + ref fn_decl, + body, + fn_decl_span, + movability, + .. + }) => { let cx = if let Some(Movability::Static) = movability { AsyncClosure(fn_decl_span) } else { diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index c830ab11e8e..f7e3fac6b2e 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -273,7 +273,10 @@ impl<'tcx> ReachableContext<'tcx> { } hir::ImplItemKind::TyAlias(_) => {} }, - Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { body, .. }, .. }) => { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(&hir::Closure { body, .. }), + .. + }) => { self.visit_nested_body(body); } // Nothing to recurse on for these diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 77c8f7d2713..4e091c5b70d 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -1,6 +1,7 @@ //! A pass that annotates every item and method with its stability level, //! propagating default levels lexically from parent to children ast nodes. +use attr::StabilityLevel; use rustc_attr::{self as attr, ConstStability, Stability}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::struct_span_err; @@ -223,7 +224,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // Check if deprecated_since < stable_since. If it is, // this is *almost surely* an accident. - if let (&Some(dep_since), &attr::Stable { since: stab_since }) = + if let (&Some(dep_since), &attr::Stable { since: stab_since, .. }) = (&depr.as_ref().and_then(|(d, _)| d.since), &stab.level) { // Explicit version of iter::order::lt to handle parse errors properly @@ -773,7 +774,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, id: hir::HirId) { if let Some(def_id) = path.res.opt_def_id() { let method_span = path.segments.last().map(|s| s.ident.span); - self.tcx.check_stability_allow_unstable( + let item_is_allowed = self.tcx.check_stability_allow_unstable( def_id, Some(id), path.span, @@ -783,8 +784,52 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } else { AllowUnstable::No }, - ) + ); + + let is_allowed_through_unstable_modules = |def_id| { + self.tcx + .lookup_stability(def_id) + .map(|stab| match stab.level { + StabilityLevel::Stable { allowed_through_unstable_modules, .. } => { + allowed_through_unstable_modules + } + _ => false, + }) + .unwrap_or(false) + }; + + if item_is_allowed && !is_allowed_through_unstable_modules(def_id) { + // Check parent modules stability as well if the item the path refers to is itself + // stable. We only emit warnings for unstable path segments if the item is stable + // or allowed because stability is often inherited, so the most common case is that + // both the segments and the item are unstable behind the same feature flag. + // + // We check here rather than in `visit_path_segment` to prevent visiting the last + // path segment twice + // + // We include special cases via #[rustc_allowed_through_unstable_modules] for items + // that were accidentally stabilized through unstable paths before this check was + // added, such as `core::intrinsics::transmute` + let parents = path.segments.iter().rev().skip(1); + for path_segment in parents { + if let Some(def_id) = path_segment.res.as_ref().and_then(Res::opt_def_id) { + // use `None` for id to prevent deprecation check + self.tcx.check_stability_allow_unstable( + def_id, + None, + path.span, + None, + if is_unstable_reexport(self.tcx, id) { + AllowUnstable::Yes + } else { + AllowUnstable::No + }, + ); + } + } + } } + intravisit::walk_path(self, path) } } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 5560d44aa0d..4e73c26d35f 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -23,7 +23,7 @@ use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::middle::privacy::{AccessLevel, AccessLevels}; use rustc_middle::span_bug; -use rustc_middle::thir::abstract_const::Node as ACNode; +use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst, Node as ACNode}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::{self, Const, DefIdTree, GenericParamDefKind}; @@ -32,7 +32,6 @@ use rustc_session::lint; use rustc_span::hygiene::Transparency; use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; -use rustc_trait_selection::traits::const_evaluatable::{self, AbstractConst}; use std::marker::PhantomData; use std::ops::ControlFlow; @@ -164,7 +163,7 @@ where tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, ) -> ControlFlow<V::BreakTy> { - const_evaluatable::walk_abstract_const(tcx, ct, |node| match node.root(tcx) { + walk_abstract_const(tcx, ct, |node| match node.root(tcx) { ACNode::Leaf(leaf) => self.visit_const(leaf), ACNode::Cast(_, _, ty) => self.visit_ty(ty), ACNode::Binop(..) | ACNode::UnaryOp(..) | ACNode::FunctionCall(_, _) => { diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index 4c25075327f..56fd90c9855 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -9,7 +9,6 @@ use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use rustc_middle::mir::{self, interpret}; -use rustc_middle::thir; use rustc_middle::ty::codec::{RefDecodable, TyDecoder, TyEncoder}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_query_system::dep_graph::DepContext; @@ -766,7 +765,7 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> } } -impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [thir::abstract_const::Node<'tcx>] { +impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [ty::abstract_const::Node<'tcx>] { fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { RefDecodable::decode(d) } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 52706fbb9e6..66641fb2cb2 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -259,7 +259,7 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { fn visit_expr(&mut self, expr: &'a Expr) { let parent_def = match expr.kind { ExprKind::MacCall(..) => return self.visit_macro_invoc(expr.id), - ExprKind::Closure(_, asyncness, ..) => { + ExprKind::Closure(_, _, asyncness, ..) => { // Async closures desugar to closures inside of closures, so // we must create two defs. let closure_def = self.create_def(expr.id, DefPathData::ClosureExpr, expr.span); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 098b5a0c92e..3ea285b723b 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -268,6 +268,7 @@ enum LifetimeBinderKind { WhereBound, Item, Function, + Closure, ImplBlock, } @@ -281,6 +282,7 @@ impl LifetimeBinderKind { Item => "item", ImplBlock => "impl block", Function => "function", + Closure => "closure", } } } @@ -758,7 +760,10 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { // We don't need to deal with patterns in parameters, because // they are not possible for foreign or bodiless functions. self.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(fn_id, false), + LifetimeRibKind::AnonymousCreateParameter { + binder: fn_id, + report_in_path: false, + }, |this| walk_list!(this, visit_param, &sig.decl.inputs), ); self.with_lifetime_rib( @@ -792,18 +797,13 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { // generic parameters. This is especially useful for `async fn`, where // these fresh generic parameters can be applied to the opaque `impl Trait` // return type. - let rib = if async_node_id.is_some() { - // Only emit a hard error for `async fn`, since this kind of - // elision has always been allowed in regular `fn`s. + this.with_lifetime_rib( LifetimeRibKind::AnonymousCreateParameter { binder: fn_id, - report_in_path: true, - } - } else { - LifetimeRibKind::AnonymousPassThrough(fn_id, false) - }; - this.with_lifetime_rib( - rib, + // Only emit a hard error for `async fn`, since this kind of + // elision has always been allowed in regular `fn`s. + report_in_path: async_node_id.is_some(), + }, // Add each argument to the rib. |this| this.resolve_params(&declaration.inputs), ); @@ -868,19 +868,30 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { this.in_func_body = previous_state; } } - FnKind::Closure(declaration, body) => { - // We do not have any explicit generic lifetime parameter. - // FIXME(rfc3216): Change when implementing `for<>` bounds on closures. + FnKind::Closure(binder, declaration, body) => { + this.visit_closure_binder(binder); + this.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { - binder: fn_id, - report_in_path: false, + match binder { + // We do not have any explicit generic lifetime parameter. + ClosureBinder::NotPresent => { + LifetimeRibKind::AnonymousCreateParameter { + binder: fn_id, + report_in_path: false, + } + } + ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError, }, // Add each argument to the rib. |this| this.resolve_params(&declaration.inputs), ); this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(fn_id, true), + match binder { + ClosureBinder::NotPresent => { + LifetimeRibKind::AnonymousPassThrough(fn_id, true) + } + ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError, + }, |this| visit::walk_fn_ret_ty(this, &declaration.output), ); @@ -915,6 +926,18 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } } + fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) { + match b { + ClosureBinder::NotPresent => {} + ClosureBinder::For { generic_params, .. } => { + self.visit_generic_params( + &generic_params, + self.diagnostic_metadata.current_self_item.is_some(), + ); + } + } + } + fn visit_generic_arg(&mut self, arg: &'ast GenericArg) { debug!("visit_generic_arg({:?})", arg); let prev = replace(&mut self.diagnostic_metadata.currently_processing_generics, true); @@ -3519,7 +3542,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // `async |x| ...` gets desugared to `|x| future_from_generator(|| ...)`, so we need to // resolve the arguments within the proper scopes so that usages of them inside the // closure are detected as upvars rather than normal closure arg usages. - ExprKind::Closure(_, Async::Yes { .. }, _, ref fn_decl, ref body, _span) => { + ExprKind::Closure(_, _, Async::Yes { .. }, _, ref fn_decl, ref body, _span) => { self.with_rib(ValueNS, NormalRibKind, |this| { this.with_label_rib(ClosureOrAsyncRibKind, |this| { // Resolve arguments: @@ -3539,6 +3562,18 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }); } // For closures, ClosureOrAsyncRibKind is added in visit_fn + ExprKind::Closure(ClosureBinder::For { ref generic_params, span }, ..) => { + self.with_generic_param_rib( + &generic_params, + NormalRibKind, + LifetimeRibKind::Generics { + binder: expr.id, + kind: LifetimeBinderKind::Closure, + span, + }, + |this| visit::walk_expr(this, expr), + ); + } ExprKind::Closure(..) => visit::walk_expr(self, expr), ExprKind::Async(..) => { self.with_label_rib(ClosureOrAsyncRibKind, |this| visit::walk_expr(this, expr)); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 677d7036b2f..1e53c73620a 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -70,6 +70,8 @@ pub(crate) enum ForLifetimeSpanType { BoundTail, TypeEmpty, TypeTail, + ClosureEmpty, + ClosureTail, } impl ForLifetimeSpanType { @@ -77,13 +79,15 @@ impl ForLifetimeSpanType { match self { Self::BoundEmpty | Self::BoundTail => "bound", Self::TypeEmpty | Self::TypeTail => "type", + Self::ClosureEmpty | Self::ClosureTail => "closure", } } pub(crate) fn suggestion(&self, sugg: &str) -> String { match self { Self::BoundEmpty | Self::TypeEmpty => format!("for<{}> ", sugg), - Self::BoundTail | Self::TypeTail => format!(", {}", sugg), + Self::ClosureEmpty => format!("for<{}>", sugg), + Self::BoundTail | Self::TypeTail | Self::ClosureTail => format!(", {}", sugg), } } } diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 557dbecfabe..0eb11cd3e9f 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -15,7 +15,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefIdMap, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node}; -use rustc_hir::{GenericParamKind, HirIdMap}; +use rustc_hir::{GenericParamKind, HirIdMap, LifetimeParamKind}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_lifetime::*; @@ -571,7 +571,54 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Closure { bound_generic_params, .. } = e.kind { + if let hir::ExprKind::Closure(hir::Closure { + binder, bound_generic_params, fn_decl, .. + }) = e.kind + { + if let &hir::ClosureBinder::For { span: for_sp, .. } = binder { + fn span_of_infer(ty: &hir::Ty<'_>) -> Option<Span> { + struct V(Option<Span>); + + impl<'v> Visitor<'v> for V { + fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { + match t.kind { + _ if self.0.is_some() => (), + hir::TyKind::Infer => { + self.0 = Some(t.span); + } + _ => intravisit::walk_ty(self, t), + } + } + } + + let mut v = V(None); + v.visit_ty(ty); + v.0 + } + + let infer_in_rt_sp = match fn_decl.output { + hir::FnRetTy::DefaultReturn(sp) => Some(sp), + hir::FnRetTy::Return(ty) => span_of_infer(ty), + }; + + let infer_spans = fn_decl + .inputs + .into_iter() + .filter_map(span_of_infer) + .chain(infer_in_rt_sp) + .collect::<Vec<_>>(); + + if !infer_spans.is_empty() { + self.tcx.sess + .struct_span_err( + infer_spans, + "implicit types in closure signatures are forbidden when `for<...>` is present", + ) + .span_label(for_sp, "`for<...>` is here") + .emit(); + } + } + let next_early_index = self.next_early_index(); let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = bound_generic_params @@ -584,6 +631,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { (pair, r) }) .unzip(); + self.map.late_bound_vars.insert(e.hir_id, binders); let scope = Scope::Binder { hir_id: e.hir_id, @@ -595,11 +643,41 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { allow_late_bound: true, where_bound_origin: None, }; + + if let &hir::ClosureBinder::For { span, .. } = binder { + let last_lt = bound_generic_params + .iter() + .filter(|p| { + matches!( + p, + GenericParam { + kind: GenericParamKind::Lifetime { + kind: LifetimeParamKind::Explicit + }, + .. + } + ) + }) + .last(); + let (span, span_type) = match last_lt { + Some(GenericParam { span: last_sp, .. }) => { + (last_sp.shrink_to_hi(), ForLifetimeSpanType::ClosureTail) + } + None => (span, ForLifetimeSpanType::ClosureEmpty), + }; + self.missing_named_lifetime_spots + .push(MissingLifetimeSpot::HigherRanked { span, span_type }); + } + self.with(scope, |this| { // a closure has no bounds, so everything // contained within is scoped within its binder. intravisit::walk_expr(this, e) }); + + if let hir::ClosureBinder::For { .. } = binder { + self.missing_named_lifetime_spots.pop(); + } } else { intravisit::walk_expr(self, e) } @@ -1677,7 +1755,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { break None; } - Scope::Binder { ref lifetimes, scope_type, s, .. } => { + Scope::Binder { ref lifetimes, scope_type, s, where_bound_origin, .. } => { if let Some(&def) = lifetimes.get(®ion_def_id) { break Some(def.shifted(late_depth)); } @@ -1685,6 +1763,21 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { BinderScopeType::Normal => late_depth += 1, BinderScopeType::Concatenating => {} } + // Fresh lifetimes in APIT used to be allowed in async fns and forbidden in + // regular fns. + if let Some(hir::PredicateOrigin::ImplTrait) = where_bound_origin + && let hir::LifetimeName::Param(_, hir::ParamName::Fresh) = lifetime_ref.name + && let hir::IsAsync::NotAsync = self.tcx.asyncness(lifetime_ref.hir_id.owner) + && !self.tcx.features().anonymous_lifetime_in_impl_trait + { + rustc_session::parse::feature_err( + &self.tcx.sess.parse_sess, + sym::anonymous_lifetime_in_impl_trait, + lifetime_ref.span, + "anonymous lifetimes in `impl Trait` are unstable", + ).emit(); + return; + } scope = s; } diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index a4175f4c5f3..e2e0e1f5b30 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -1353,7 +1353,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { } } } - hir::ExprKind::Closure { ref fn_decl, body, .. } => { + hir::ExprKind::Closure(&hir::Closure { ref fn_decl, body, .. }) => { let id = format!("${}", ex.hir_id); // walk arg and return types diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index 366efe9cfa5..5c17ef6ace2 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -297,6 +297,10 @@ impl FileEncoder { } } + pub fn file(&self) -> &File { + &self.file + } + #[inline] fn capacity(&self) -> usize { self.buf.len() diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 1d509a45d41..e169d3c7cfb 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -644,7 +644,10 @@ pub fn debug_hygiene_data(verbose: bool) -> String { let expn_data = expn_data.as_ref().expect("no expansion data for an expansion ID"); debug_expn_data((&id.to_expn_id(), expn_data)) }); + // Sort the hash map for more reproducible output. + // Because of this, it is fine to rely on the unstable iteration order of the map. + #[allow(rustc::potential_query_instability)] let mut foreign_expn_data: Vec<_> = data.foreign_expn_data.iter().collect(); foreign_expn_data.sort_by_key(|(id, _)| (id.krate, id.local_id)); foreign_expn_data.into_iter().for_each(debug_expn_data); @@ -1208,6 +1211,7 @@ impl HygieneEncodeContext { // It's fine to iterate over a HashMap, because the serialization // of the table that we insert data into doesn't depend on insertion // order + #[allow(rustc::potential_query_instability)] for_all_ctxts_in(latest_ctxts.into_iter(), |index, ctxt, data| { if self.serialized_ctxts.lock().insert(ctxt) { encode_ctxt(encoder, index, data); @@ -1216,6 +1220,8 @@ impl HygieneEncodeContext { let latest_expns = { std::mem::take(&mut *self.latest_expns.lock()) }; + // Same as above, this is fine as we are inserting into a order-independent hashset + #[allow(rustc::potential_query_instability)] for_all_expns_in(latest_expns.into_iter(), |expn, data, hash| { if self.serialized_expns.lock().insert(expn) { encode_expn(encoder, expn, data, hash); diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index ce08f87ecdf..cf306928151 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -20,7 +20,6 @@ #![feature(negative_impls)] #![feature(min_specialization)] #![feature(rustc_attrs)] -#![allow(rustc::potential_query_instability)] #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 99912b491cb..0c271c04709 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -341,6 +341,7 @@ symbols! { always, and, and_then, + anonymous_lifetime_in_impl_trait, any, append_const_msg, arbitrary_enum_discriminant, @@ -459,6 +460,7 @@ symbols! { clone_closures, clone_from, closure, + closure_lifetime_binder, closure_to_fn_coercion, closure_track_caller, cmp, @@ -1189,6 +1191,7 @@ symbols! { rustc_allocator_nounwind, rustc_allow_const_fn_unstable, rustc_allow_incoherent_impl, + rustc_allowed_through_unstable_modules, rustc_attrs, rustc_box, rustc_builtin_macro, diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 3a152eff485..e6284b1c4ac 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -10,22 +10,153 @@ //! generic constants mentioned in the `caller_bounds` of the current environment. use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; -use rustc_index::vec::IndexVec; use rustc_infer::infer::InferCtxt; -use rustc_middle::mir; -use rustc_middle::mir::interpret::{ErrorHandled, LitToConstError, LitToConstInput}; -use rustc_middle::thir; -use rustc_middle::thir::abstract_const::{self, Node, NodeId, NotConstEvaluatable}; -use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, DelaySpanBugEmitted, EarlyBinder, TyCtxt, TypeVisitable}; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::abstract_const::{ + walk_abstract_const, AbstractConst, FailureKind, Node, NotConstEvaluatable, +}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; use rustc_session::lint; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; -use std::cmp; use std::iter; use std::ops::ControlFlow; +pub struct ConstUnifyCtxt<'tcx> { + pub tcx: TyCtxt<'tcx>, + pub param_env: ty::ParamEnv<'tcx>, +} + +impl<'tcx> ConstUnifyCtxt<'tcx> { + // Substitutes generics repeatedly to allow AbstractConsts to unify where a + // ConstKind::Unevaluated could be turned into an AbstractConst that would unify e.g. + // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] + #[inline] + #[instrument(skip(self), level = "debug")] + fn try_replace_substs_in_root( + &self, + mut abstr_const: AbstractConst<'tcx>, + ) -> Option<AbstractConst<'tcx>> { + while let Node::Leaf(ct) = abstr_const.root(self.tcx) { + match AbstractConst::from_const(self.tcx, ct) { + Ok(Some(act)) => abstr_const = act, + Ok(None) => break, + Err(_) => return None, + } + } + + Some(abstr_const) + } + + /// Tries to unify two abstract constants using structural equality. + #[instrument(skip(self), level = "debug")] + pub fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { + let a = if let Some(a) = self.try_replace_substs_in_root(a) { + a + } else { + return true; + }; + + let b = if let Some(b) = self.try_replace_substs_in_root(b) { + b + } else { + return true; + }; + + let a_root = a.root(self.tcx); + let b_root = b.root(self.tcx); + debug!(?a_root, ?b_root); + + match (a_root, b_root) { + (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { + let a_ct = a_ct.eval(self.tcx, self.param_env); + debug!("a_ct evaluated: {:?}", a_ct); + let b_ct = b_ct.eval(self.tcx, self.param_env); + debug!("b_ct evaluated: {:?}", b_ct); + + if a_ct.ty() != b_ct.ty() { + return false; + } + + match (a_ct.kind(), b_ct.kind()) { + // We can just unify errors with everything to reduce the amount of + // emitted errors here. + (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, + (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { + a_param == b_param + } + (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, + // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]` + // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This + // means that we only allow inference variables if they are equal. + (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, + // We expand generic anonymous constants at the start of this function, so this + // branch should only be taking when dealing with associated constants, at + // which point directly comparing them seems like the desired behavior. + // + // FIXME(generic_const_exprs): This isn't actually the case. + // We also take this branch for concrete anonymous constants and + // expand generic anonymous constants with concrete substs. + (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { + a_uv == b_uv + } + // FIXME(generic_const_exprs): We may want to either actually try + // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like + // this, for now we just return false here. + _ => false, + } + } + (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { + self.try_unify(a.subtree(al), b.subtree(bl)) + && self.try_unify(a.subtree(ar), b.subtree(br)) + } + (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { + self.try_unify(a.subtree(av), b.subtree(bv)) + } + (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) + if a_args.len() == b_args.len() => + { + self.try_unify(a.subtree(a_f), b.subtree(b_f)) + && iter::zip(a_args, b_args) + .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn))) + } + (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) + if (a_ty == b_ty) && (a_kind == b_kind) => + { + self.try_unify(a.subtree(a_operand), b.subtree(b_operand)) + } + // use this over `_ => false` to make adding variants to `Node` less error prone + (Node::Cast(..), _) + | (Node::FunctionCall(..), _) + | (Node::UnaryOp(..), _) + | (Node::Binop(..), _) + | (Node::Leaf(..), _) => false, + } + } +} + +#[instrument(skip(tcx), level = "debug")] +pub fn try_unify_abstract_consts<'tcx>( + tcx: TyCtxt<'tcx>, + (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), + param_env: ty::ParamEnv<'tcx>, +) -> bool { + (|| { + if let Some(a) = AbstractConst::new(tcx, a)? { + if let Some(b) = AbstractConst::new(tcx, b)? { + let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; + return Ok(const_unify_ctxt.try_unify(a, b)); + } + } + + Ok(false) + })() + .unwrap_or_else(|_: ErrorGuaranteed| true) + // FIXME(generic_const_exprs): We should instead have this + // method return the resulting `ty::Const` and return `ConstKind::Error` + // on `ErrorGuaranteed`. +} + /// Check if a given constant can be evaluated. #[instrument(skip(infcx), level = "debug")] pub fn is_const_evaluatable<'cx, 'tcx>( @@ -41,48 +172,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( if satisfied_from_param_env(tcx, ct, param_env)? { return Ok(()); } - - // We were unable to unify the abstract constant with - // a constant found in the caller bounds, there are - // now three possible cases here. - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] - enum FailureKind { - /// The abstract const still references an inference - /// variable, in this case we return `TooGeneric`. - MentionsInfer, - /// The abstract const references a generic parameter, - /// this means that we emit an error here. - MentionsParam, - /// The substs are concrete enough that we can simply - /// try and evaluate the given constant. - Concrete, - } - let mut failure_kind = FailureKind::Concrete; - walk_abstract_const::<!, _>(tcx, ct, |node| match node.root(tcx) { - Node::Leaf(leaf) => { - if leaf.has_infer_types_or_consts() { - failure_kind = FailureKind::MentionsInfer; - } else if leaf.has_param_types_or_consts() { - failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); - } - - ControlFlow::CONTINUE - } - Node::Cast(_, _, ty) => { - if ty.has_infer_types_or_consts() { - failure_kind = FailureKind::MentionsInfer; - } else if ty.has_param_types_or_consts() { - failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); - } - - ControlFlow::CONTINUE - } - Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => { - ControlFlow::CONTINUE - } - }); - - match failure_kind { + match ct.unify_failure_kind(tcx) { FailureKind::MentionsInfer => { return Err(NotConstEvaluatable::MentionsInfer); } @@ -216,593 +306,3 @@ fn satisfied_from_param_env<'tcx>( Ok(false) } - -/// A tree representing an anonymous constant. -/// -/// This is only able to represent a subset of `MIR`, -/// and should not leak any information about desugarings. -#[derive(Debug, Clone, Copy)] -pub struct AbstractConst<'tcx> { - // FIXME: Consider adding something like `IndexSlice` - // and use this here. - inner: &'tcx [Node<'tcx>], - substs: SubstsRef<'tcx>, -} - -impl<'tcx> AbstractConst<'tcx> { - pub fn new( - tcx: TyCtxt<'tcx>, - uv: ty::Unevaluated<'tcx, ()>, - ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> { - let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?; - debug!("AbstractConst::new({:?}) = {:?}", uv, inner); - Ok(inner.map(|inner| AbstractConst { inner, substs: tcx.erase_regions(uv.substs) })) - } - - pub fn from_const( - tcx: TyCtxt<'tcx>, - ct: ty::Const<'tcx>, - ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> { - match ct.kind() { - ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv.shrink()), - ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => Err(reported), - _ => Ok(None), - } - } - - #[inline] - pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> { - AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs } - } - - #[inline] - pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> { - let node = self.inner.last().copied().unwrap(); - match node { - Node::Leaf(leaf) => Node::Leaf(EarlyBinder(leaf).subst(tcx, self.substs)), - Node::Cast(kind, operand, ty) => { - Node::Cast(kind, operand, EarlyBinder(ty).subst(tcx, self.substs)) - } - // Don't perform substitution on the following as they can't directly contain generic params - Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node, - } - } -} - -struct AbstractConstBuilder<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - body_id: thir::ExprId, - body: &'a thir::Thir<'tcx>, - /// The current WIP node tree. - nodes: IndexVec<NodeId, Node<'tcx>>, -} - -impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { - fn root_span(&self) -> Span { - self.body.exprs[self.body_id].span - } - - fn error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> { - let reported = self - .tcx - .sess - .struct_span_err(self.root_span(), "overly complex generic constant") - .span_label(span, msg) - .help("consider moving this anonymous constant into a `const` function") - .emit(); - - Err(reported) - } - fn maybe_supported_error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> { - let reported = self - .tcx - .sess - .struct_span_err(self.root_span(), "overly complex generic constant") - .span_label(span, msg) - .help("consider moving this anonymous constant into a `const` function") - .note("this operation may be supported in the future") - .emit(); - - Err(reported) - } - - #[instrument(skip(tcx, body, body_id), level = "debug")] - fn new( - tcx: TyCtxt<'tcx>, - (body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId), - ) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorGuaranteed> { - let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() }; - - struct IsThirPolymorphic<'a, 'tcx> { - is_poly: bool, - thir: &'a thir::Thir<'tcx>, - } - - use crate::rustc_middle::thir::visit::Visitor; - use thir::visit; - - impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { - fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool { - if expr.ty.has_param_types_or_consts() { - return true; - } - - match expr.kind { - thir::ExprKind::NamedConst { substs, .. } => substs.has_param_types_or_consts(), - thir::ExprKind::ConstParam { .. } => true, - thir::ExprKind::Repeat { value, count } => { - self.visit_expr(&self.thir()[value]); - count.has_param_types_or_consts() - } - _ => false, - } - } - - fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { - if pat.ty.has_param_types_or_consts() { - return true; - } - - match pat.kind.as_ref() { - thir::PatKind::Constant { value } => value.has_param_types_or_consts(), - thir::PatKind::Range(thir::PatRange { lo, hi, .. }) => { - lo.has_param_types_or_consts() || hi.has_param_types_or_consts() - } - _ => false, - } - } - } - - impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { - fn thir(&self) -> &'a thir::Thir<'tcx> { - &self.thir - } - - #[instrument(skip(self), level = "debug")] - fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) { - self.is_poly |= self.expr_is_poly(expr); - if !self.is_poly { - visit::walk_expr(self, expr) - } - } - - #[instrument(skip(self), level = "debug")] - fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) { - self.is_poly |= self.pat_is_poly(pat); - if !self.is_poly { - visit::walk_pat(self, pat); - } - } - } - - let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body }; - visit::walk_expr(&mut is_poly_vis, &body[body_id]); - debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly); - if !is_poly_vis.is_poly { - return Ok(None); - } - - Ok(Some(builder)) - } - - /// We do not allow all binary operations in abstract consts, so filter disallowed ones. - fn check_binop(op: mir::BinOp) -> bool { - use mir::BinOp::*; - match op { - Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le - | Ne | Ge | Gt => true, - Offset => false, - } - } - - /// While we currently allow all unary operations, we still want to explicitly guard against - /// future changes here. - fn check_unop(op: mir::UnOp) -> bool { - use mir::UnOp::*; - match op { - Not | Neg => true, - } - } - - /// Builds the abstract const by walking the thir and bailing out when - /// encountering an unsupported operation. - fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> { - debug!("Abstractconstbuilder::build: body={:?}", &*self.body); - self.recurse_build(self.body_id)?; - - for n in self.nodes.iter() { - if let Node::Leaf(ct) = n { - if let ty::ConstKind::Unevaluated(ct) = ct.kind() { - // `AbstractConst`s should not contain any promoteds as they require references which - // are not allowed. - assert_eq!(ct.promoted, None); - assert_eq!(ct, self.tcx.erase_regions(ct)); - } - } - } - - Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter())) - } - - fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorGuaranteed> { - use thir::ExprKind; - let node = &self.body.exprs[node]; - Ok(match &node.kind { - // I dont know if handling of these 3 is correct - &ExprKind::Scope { value, .. } => self.recurse_build(value)?, - &ExprKind::PlaceTypeAscription { source, .. } - | &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?, - &ExprKind::Literal { lit, neg} => { - let sp = node.span; - let constant = - match self.tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) { - Ok(c) => c, - Err(LitToConstError::Reported) => { - self.tcx.const_error(node.ty) - } - Err(LitToConstError::TypeError) => { - bug!("encountered type error in lit_to_const") - } - }; - - self.nodes.push(Node::Leaf(constant)) - } - &ExprKind::NonHirLiteral { lit , user_ty: _} => { - let val = ty::ValTree::from_scalar_int(lit); - self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) - } - &ExprKind::ZstLiteral { user_ty: _ } => { - let val = ty::ValTree::zst(); - self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) - } - &ExprKind::NamedConst { def_id, substs, user_ty: _ } => { - let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs); - - let constant = self.tcx.mk_const(ty::ConstS { - kind: ty::ConstKind::Unevaluated(uneval), - ty: node.ty, - }); - - self.nodes.push(Node::Leaf(constant)) - } - - ExprKind::ConstParam {param, ..} => { - let const_param = self.tcx.mk_const(ty::ConstS { - kind: ty::ConstKind::Param(*param), - ty: node.ty, - }); - self.nodes.push(Node::Leaf(const_param)) - } - - ExprKind::Call { fun, args, .. } => { - let fun = self.recurse_build(*fun)?; - - let mut new_args = Vec::<NodeId>::with_capacity(args.len()); - for &id in args.iter() { - new_args.push(self.recurse_build(id)?); - } - let new_args = self.tcx.arena.alloc_slice(&new_args); - self.nodes.push(Node::FunctionCall(fun, new_args)) - } - &ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => { - let lhs = self.recurse_build(lhs)?; - let rhs = self.recurse_build(rhs)?; - self.nodes.push(Node::Binop(op, lhs, rhs)) - } - &ExprKind::Unary { op, arg } if Self::check_unop(op) => { - let arg = self.recurse_build(arg)?; - self.nodes.push(Node::UnaryOp(op, arg)) - } - // This is necessary so that the following compiles: - // - // ``` - // fn foo<const N: usize>(a: [(); N + 1]) { - // bar::<{ N + 1 }>(); - // } - // ``` - ExprKind::Block { body: thir::Block { stmts: box [], expr: Some(e), .. } } => { - self.recurse_build(*e)? - } - // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a - // "coercion cast" i.e. using a coercion or is a no-op. - // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested) - &ExprKind::Use { source } => { - let arg = self.recurse_build(source)?; - self.nodes.push(Node::Cast(abstract_const::CastKind::Use, arg, node.ty)) - } - &ExprKind::Cast { source } => { - let arg = self.recurse_build(source)?; - self.nodes.push(Node::Cast(abstract_const::CastKind::As, arg, node.ty)) - } - ExprKind::Borrow{ arg, ..} => { - let arg_node = &self.body.exprs[*arg]; - - // Skip reborrows for now until we allow Deref/Borrow/AddressOf - // expressions. - // FIXME(generic_const_exprs): Verify/explain why this is sound - if let ExprKind::Deref {arg} = arg_node.kind { - self.recurse_build(arg)? - } else { - self.maybe_supported_error( - node.span, - "borrowing is not supported in generic constants", - )? - } - } - // FIXME(generic_const_exprs): We may want to support these. - ExprKind::AddressOf { .. } | ExprKind::Deref {..}=> self.maybe_supported_error( - node.span, - "dereferencing or taking the address is not supported in generic constants", - )?, - ExprKind::Repeat { .. } | ExprKind::Array { .. } => self.maybe_supported_error( - node.span, - "array construction is not supported in generic constants", - )?, - ExprKind::Block { .. } => self.maybe_supported_error( - node.span, - "blocks are not supported in generic constant", - )?, - ExprKind::NeverToAny { .. } => self.maybe_supported_error( - node.span, - "converting nevers to any is not supported in generic constant", - )?, - ExprKind::Tuple { .. } => self.maybe_supported_error( - node.span, - "tuple construction is not supported in generic constants", - )?, - ExprKind::Index { .. } => self.maybe_supported_error( - node.span, - "indexing is not supported in generic constant", - )?, - ExprKind::Field { .. } => self.maybe_supported_error( - node.span, - "field access is not supported in generic constant", - )?, - ExprKind::ConstBlock { .. } => self.maybe_supported_error( - node.span, - "const blocks are not supported in generic constant", - )?, - ExprKind::Adt(_) => self.maybe_supported_error( - node.span, - "struct/enum construction is not supported in generic constants", - )?, - // dont know if this is correct - ExprKind::Pointer { .. } => - self.error(node.span, "pointer casts are not allowed in generic constants")?, - ExprKind::Yield { .. } => - self.error(node.span, "generator control flow is not allowed in generic constants")?, - ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => self - .error( - node.span, - "loops and loop control flow are not supported in generic constants", - )?, - ExprKind::Box { .. } => - self.error(node.span, "allocations are not allowed in generic constants")?, - - ExprKind::Unary { .. } => unreachable!(), - // we handle valid unary/binary ops above - ExprKind::Binary { .. } => - self.error(node.span, "unsupported binary operation in generic constants")?, - ExprKind::LogicalOp { .. } => - self.error(node.span, "unsupported operation in generic constants, short-circuiting operations would imply control flow")?, - ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { - self.error(node.span, "assignment is not supported in generic constants")? - } - ExprKind::Closure { .. } | ExprKind::Return { .. } => self.error( - node.span, - "closures and function keywords are not supported in generic constants", - )?, - // let expressions imply control flow - ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => - self.error(node.span, "control flow is not supported in generic constants")?, - ExprKind::InlineAsm { .. } => { - self.error(node.span, "assembly is not supported in generic constants")? - } - - // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen - ExprKind::VarRef { .. } - | ExprKind::UpvarRef { .. } - | ExprKind::StaticRef { .. } - | ExprKind::ThreadLocalRef(_) => { - self.error(node.span, "unsupported operation in generic constant")? - } - }) - } -} - -/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead. -pub(super) fn thir_abstract_const<'tcx>( - tcx: TyCtxt<'tcx>, - def: ty::WithOptConstParam<LocalDefId>, -) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorGuaranteed> { - if tcx.features().generic_const_exprs { - match tcx.def_kind(def.did) { - // FIXME(generic_const_exprs): We currently only do this for anonymous constants, - // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether - // we want to look into them or treat them as opaque projections. - // - // Right now we do neither of that and simply always fail to unify them. - DefKind::AnonConst | DefKind::InlineConst => (), - _ => return Ok(None), - } - - let body = tcx.thir_body(def)?; - - AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))? - .map(AbstractConstBuilder::build) - .transpose() - } else { - Ok(None) - } -} - -#[instrument(skip(tcx), level = "debug")] -pub(super) fn try_unify_abstract_consts<'tcx>( - tcx: TyCtxt<'tcx>, - (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), - param_env: ty::ParamEnv<'tcx>, -) -> bool { - (|| { - if let Some(a) = AbstractConst::new(tcx, a)? { - if let Some(b) = AbstractConst::new(tcx, b)? { - let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; - return Ok(const_unify_ctxt.try_unify(a, b)); - } - } - - Ok(false) - })() - .unwrap_or_else(|_: ErrorGuaranteed| true) - // FIXME(generic_const_exprs): We should instead have this - // method return the resulting `ty::Const` and return `ConstKind::Error` - // on `ErrorGuaranteed`. -} - -#[instrument(skip(tcx, f), level = "debug")] -pub fn walk_abstract_const<'tcx, R, F>( - tcx: TyCtxt<'tcx>, - ct: AbstractConst<'tcx>, - mut f: F, -) -> ControlFlow<R> -where - F: FnMut(AbstractConst<'tcx>) -> ControlFlow<R>, -{ - #[instrument(skip(tcx, f), level = "debug")] - fn recurse<'tcx, R>( - tcx: TyCtxt<'tcx>, - ct: AbstractConst<'tcx>, - f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>, - ) -> ControlFlow<R> { - f(ct)?; - let root = ct.root(tcx); - debug!(?root); - match root { - Node::Leaf(_) => ControlFlow::CONTINUE, - Node::Binop(_, l, r) => { - recurse(tcx, ct.subtree(l), f)?; - recurse(tcx, ct.subtree(r), f) - } - Node::UnaryOp(_, v) => recurse(tcx, ct.subtree(v), f), - Node::FunctionCall(func, args) => { - recurse(tcx, ct.subtree(func), f)?; - args.iter().try_for_each(|&arg| recurse(tcx, ct.subtree(arg), f)) - } - Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f), - } - } - - recurse(tcx, ct, &mut f) -} - -struct ConstUnifyCtxt<'tcx> { - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, -} - -impl<'tcx> ConstUnifyCtxt<'tcx> { - // Substitutes generics repeatedly to allow AbstractConsts to unify where a - // ConstKind::Unevaluated could be turned into an AbstractConst that would unify e.g. - // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] - #[inline] - #[instrument(skip(self), level = "debug")] - fn try_replace_substs_in_root( - &self, - mut abstr_const: AbstractConst<'tcx>, - ) -> Option<AbstractConst<'tcx>> { - while let Node::Leaf(ct) = abstr_const.root(self.tcx) { - match AbstractConst::from_const(self.tcx, ct) { - Ok(Some(act)) => abstr_const = act, - Ok(None) => break, - Err(_) => return None, - } - } - - Some(abstr_const) - } - - /// Tries to unify two abstract constants using structural equality. - #[instrument(skip(self), level = "debug")] - fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { - let a = if let Some(a) = self.try_replace_substs_in_root(a) { - a - } else { - return true; - }; - - let b = if let Some(b) = self.try_replace_substs_in_root(b) { - b - } else { - return true; - }; - - let a_root = a.root(self.tcx); - let b_root = b.root(self.tcx); - debug!(?a_root, ?b_root); - - match (a_root, b_root) { - (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { - let a_ct = a_ct.eval(self.tcx, self.param_env); - debug!("a_ct evaluated: {:?}", a_ct); - let b_ct = b_ct.eval(self.tcx, self.param_env); - debug!("b_ct evaluated: {:?}", b_ct); - - if a_ct.ty() != b_ct.ty() { - return false; - } - - match (a_ct.kind(), b_ct.kind()) { - // We can just unify errors with everything to reduce the amount of - // emitted errors here. - (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, - (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { - a_param == b_param - } - (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, - // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]` - // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This - // means that we only allow inference variables if they are equal. - (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, - // We expand generic anonymous constants at the start of this function, so this - // branch should only be taking when dealing with associated constants, at - // which point directly comparing them seems like the desired behavior. - // - // FIXME(generic_const_exprs): This isn't actually the case. - // We also take this branch for concrete anonymous constants and - // expand generic anonymous constants with concrete substs. - (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { - a_uv == b_uv - } - // FIXME(generic_const_exprs): We may want to either actually try - // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like - // this, for now we just return false here. - _ => false, - } - } - (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { - self.try_unify(a.subtree(al), b.subtree(bl)) - && self.try_unify(a.subtree(ar), b.subtree(br)) - } - (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { - self.try_unify(a.subtree(av), b.subtree(bv)) - } - (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) - if a_args.len() == b_args.len() => - { - self.try_unify(a.subtree(a_f), b.subtree(b_f)) - && iter::zip(a_args, b_args) - .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn))) - } - (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) - if (a_ty == b_ty) && (a_kind == b_kind) => - { - self.try_unify(a.subtree(a_operand), b.subtree(b_operand)) - } - // use this over `_ => false` to make adding variants to `Node` less error prone - (Node::Cast(..), _) - | (Node::FunctionCall(..), _) - | (Node::UnaryOp(..), _) - | (Node::Binop(..), _) - | (Node::Leaf(..), _) => false, - } - } -} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 379fc54349a..02196a8f16d 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -24,8 +24,8 @@ use rustc_hir::Item; use rustc_hir::Node; use rustc_infer::infer::error_reporting::same_type_modulo_infer; use rustc_infer::traits::{AmbiguousSelection, TraitEngine}; -use rustc_middle::thir::abstract_const::NotConstEvaluatable; use rustc_middle::traits::select::OverflowError; +use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{ @@ -1086,7 +1086,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let hir = self.tcx.hir(); Some(match node { Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure { body, fn_decl_span, .. }, + kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }), .. }) => ( sm.guess_head_span(fn_decl_span), @@ -2179,6 +2179,33 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { } } + ty::PredicateKind::ConstEvaluatable(data) => { + if predicate.references_error() || self.is_tainted_by_errors() { + return; + } + let subst = data.substs.iter().find(|g| g.has_infer_types_or_consts()); + if let Some(subst) = subst { + let err = self.emit_inference_failure_err( + body_id, + span, + subst, + ErrorCode::E0284, + true, + ); + err + } else { + // If we can't find a substitution, just print a generic error + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ); + err.span_label(span, &format!("cannot satisfy `{}`", predicate)); + err + } + } _ => { if self.tcx.sess.has_errors().is_some() || self.is_tainted_by_errors() { return; diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 7c9ee64a0c2..7e8872d9018 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -103,7 +103,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }) }), hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure { body, movability, .. }, + kind: hir::ExprKind::Closure(hir::Closure { body, movability, .. }), .. }) => self.describe_generator(*body).or_else(|| { Some(if movability.is_some() { "an async closure" } else { "a closure" }) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index b08fc482186..ff4ef92f42e 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -786,7 +786,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Get the name of the callable and the arguments to be used in the suggestion. let (snippet, sugg) = match hir.get_if_local(def_id) { Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure { fn_decl, fn_decl_span, .. }, + kind: hir::ExprKind::Closure(hir::Closure { fn_decl, fn_decl_span, .. }), .. })) => { err.span_label(*fn_decl_span, "consider calling this closure"); diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 78600652254..4aa62f8078d 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -6,7 +6,7 @@ use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProce use rustc_infer::traits::ProjectionCacheKey; use rustc_infer::traits::{SelectionError, TraitEngine, TraitEngineExt as _, TraitObligation}; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::thir::abstract_const::NotConstEvaluatable; +use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::ToPredicate; diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 74d2eb17b6b..0ad1b47a890 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -845,20 +845,6 @@ pub fn provide(providers: &mut ty::query::Providers) { vtable_entries, vtable_trait_upcasting_coercion_new_vptr_slot, subst_and_check_impossible_predicates, - thir_abstract_const: |tcx, def_id| { - let def_id = def_id.expect_local(); - if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) { - tcx.thir_abstract_const_of_const_arg(def) - } else { - const_evaluatable::thir_abstract_const(tcx, ty::WithOptConstParam::unknown(def_id)) - } - }, - thir_abstract_const_of_const_arg: |tcx, (did, param_did)| { - const_evaluatable::thir_abstract_const( - tcx, - ty::WithOptConstParam { did, const_param_did: Some(param_did) }, - ) - }, try_unify_abstract_consts: |tcx, param_env_and| { let (param_env, (a, b)) = param_env_and.into_parts(); const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env) diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index ac1811244ca..2921ce0ffef 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -11,12 +11,12 @@ use super::elaborate_predicates; use crate::infer::TyCtxtInferExt; -use crate::traits::const_evaluatable::{self, AbstractConst}; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{self, Obligation, ObligationCause}; use rustc_errors::{FatalError, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst}; use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst}; use rustc_middle::ty::{ self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, @@ -841,15 +841,13 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( // // This shouldn't really matter though as we can't really use any // constants which are not considered const evaluatable. - use rustc_middle::thir::abstract_const::Node; + use rustc_middle::ty::abstract_const::Node; if let Ok(Some(ct)) = AbstractConst::new(self.tcx, uv.shrink()) { - const_evaluatable::walk_abstract_const(self.tcx, ct, |node| { - match node.root(self.tcx) { - Node::Leaf(leaf) => self.visit_const(leaf), - Node::Cast(_, _, ty) => self.visit_ty(ty), - Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => { - ControlFlow::CONTINUE - } + walk_abstract_const(self.tcx, ct, |node| match node.root(self.tcx) { + Node::Leaf(leaf) => self.visit_const(leaf), + Node::Cast(_, _, ty) => self.visit_ty(ty), + Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => { + ControlFlow::CONTINUE } }) } else { diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 2bb53a466ca..7c5673c8632 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -32,7 +32,7 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_middle::dep_graph::{DepKind, DepNodeIndex}; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::thir::abstract_const::NotConstEvaluatable; +use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::print::with_no_trimmed_paths; diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml index d03d675bfd2..caad2ed4274 100644 --- a/compiler/rustc_ty_utils/Cargo.toml +++ b/compiler/rustc_ty_utils/Cargo.toml @@ -15,3 +15,4 @@ rustc_session = { path = "../rustc_session" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_type_ir = { path = "../rustc_type_ir" } +rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 0b83cdb78dc..7c2f4db94ff 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -1,4 +1,12 @@ -use rustc_middle::ty::{self, TyCtxt}; +use rustc_errors::ErrorGuaranteed; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::LocalDefId; +use rustc_index::vec::IndexVec; +use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::ty::abstract_const::{CastKind, Node, NodeId}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; +use rustc_middle::{mir, thir}; +use rustc_span::Span; use rustc_target::abi::VariantIdx; use std::iter; @@ -72,6 +80,390 @@ pub(crate) fn destructure_const<'tcx>( ty::DestructuredConst { variant, fields } } +pub struct AbstractConstBuilder<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body_id: thir::ExprId, + body: &'a thir::Thir<'tcx>, + /// The current WIP node tree. + nodes: IndexVec<NodeId, Node<'tcx>>, +} + +impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { + fn root_span(&self) -> Span { + self.body.exprs[self.body_id].span + } + + fn error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> { + let reported = self + .tcx + .sess + .struct_span_err(self.root_span(), "overly complex generic constant") + .span_label(span, msg) + .help("consider moving this anonymous constant into a `const` function") + .emit(); + + Err(reported) + } + fn maybe_supported_error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> { + let reported = self + .tcx + .sess + .struct_span_err(self.root_span(), "overly complex generic constant") + .span_label(span, msg) + .help("consider moving this anonymous constant into a `const` function") + .note("this operation may be supported in the future") + .emit(); + + Err(reported) + } + + #[instrument(skip(tcx, body, body_id), level = "debug")] + pub fn new( + tcx: TyCtxt<'tcx>, + (body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId), + ) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorGuaranteed> { + let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() }; + + struct IsThirPolymorphic<'a, 'tcx> { + is_poly: bool, + thir: &'a thir::Thir<'tcx>, + } + + use crate::rustc_middle::thir::visit::Visitor; + use thir::visit; + + impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { + fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool { + if expr.ty.has_param_types_or_consts() { + return true; + } + + match expr.kind { + thir::ExprKind::NamedConst { substs, .. } => substs.has_param_types_or_consts(), + thir::ExprKind::ConstParam { .. } => true, + thir::ExprKind::Repeat { value, count } => { + self.visit_expr(&self.thir()[value]); + count.has_param_types_or_consts() + } + _ => false, + } + } + + fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { + if pat.ty.has_param_types_or_consts() { + return true; + } + + match pat.kind.as_ref() { + thir::PatKind::Constant { value } => value.has_param_types_or_consts(), + thir::PatKind::Range(thir::PatRange { lo, hi, .. }) => { + lo.has_param_types_or_consts() || hi.has_param_types_or_consts() + } + _ => false, + } + } + } + + impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { + fn thir(&self) -> &'a thir::Thir<'tcx> { + &self.thir + } + + #[instrument(skip(self), level = "debug")] + fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) { + self.is_poly |= self.expr_is_poly(expr); + if !self.is_poly { + visit::walk_expr(self, expr) + } + } + + #[instrument(skip(self), level = "debug")] + fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) { + self.is_poly |= self.pat_is_poly(pat); + if !self.is_poly { + visit::walk_pat(self, pat); + } + } + } + + let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body }; + visit::walk_expr(&mut is_poly_vis, &body[body_id]); + debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly); + if !is_poly_vis.is_poly { + return Ok(None); + } + + Ok(Some(builder)) + } + + /// We do not allow all binary operations in abstract consts, so filter disallowed ones. + fn check_binop(op: mir::BinOp) -> bool { + use mir::BinOp::*; + match op { + Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le + | Ne | Ge | Gt => true, + Offset => false, + } + } + + /// While we currently allow all unary operations, we still want to explicitly guard against + /// future changes here. + fn check_unop(op: mir::UnOp) -> bool { + use mir::UnOp::*; + match op { + Not | Neg => true, + } + } + + /// Builds the abstract const by walking the thir and bailing out when + /// encountering an unsupported operation. + pub fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> { + debug!("AbstractConstBuilder::build: body={:?}", &*self.body); + self.recurse_build(self.body_id)?; + + for n in self.nodes.iter() { + if let Node::Leaf(ct) = n { + if let ty::ConstKind::Unevaluated(ct) = ct.kind() { + // `AbstractConst`s should not contain any promoteds as they require references which + // are not allowed. + assert_eq!(ct.promoted, None); + assert_eq!(ct, self.tcx.erase_regions(ct)); + } + } + } + + Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter())) + } + + fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorGuaranteed> { + use thir::ExprKind; + let node = &self.body.exprs[node]; + Ok(match &node.kind { + // I dont know if handling of these 3 is correct + &ExprKind::Scope { value, .. } => self.recurse_build(value)?, + &ExprKind::PlaceTypeAscription { source, .. } + | &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?, + &ExprKind::Literal { lit, neg} => { + let sp = node.span; + let constant = + match self.tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) { + Ok(c) => c, + Err(LitToConstError::Reported) => { + self.tcx.const_error(node.ty) + } + Err(LitToConstError::TypeError) => { + bug!("encountered type error in lit_to_const") + } + }; + + self.nodes.push(Node::Leaf(constant)) + } + &ExprKind::NonHirLiteral { lit , user_ty: _} => { + let val = ty::ValTree::from_scalar_int(lit); + self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) + } + &ExprKind::ZstLiteral { user_ty: _ } => { + let val = ty::ValTree::zst(); + self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) + } + &ExprKind::NamedConst { def_id, substs, user_ty: _ } => { + let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs); + + let constant = self.tcx.mk_const(ty::ConstS { + kind: ty::ConstKind::Unevaluated(uneval), + ty: node.ty, + }); + + self.nodes.push(Node::Leaf(constant)) + } + + ExprKind::ConstParam {param, ..} => { + let const_param = self.tcx.mk_const(ty::ConstS { + kind: ty::ConstKind::Param(*param), + ty: node.ty, + }); + self.nodes.push(Node::Leaf(const_param)) + } + + ExprKind::Call { fun, args, .. } => { + let fun = self.recurse_build(*fun)?; + + let mut new_args = Vec::<NodeId>::with_capacity(args.len()); + for &id in args.iter() { + new_args.push(self.recurse_build(id)?); + } + let new_args = self.tcx.arena.alloc_slice(&new_args); + self.nodes.push(Node::FunctionCall(fun, new_args)) + } + &ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => { + let lhs = self.recurse_build(lhs)?; + let rhs = self.recurse_build(rhs)?; + self.nodes.push(Node::Binop(op, lhs, rhs)) + } + &ExprKind::Unary { op, arg } if Self::check_unop(op) => { + let arg = self.recurse_build(arg)?; + self.nodes.push(Node::UnaryOp(op, arg)) + } + // This is necessary so that the following compiles: + // + // ``` + // fn foo<const N: usize>(a: [(); N + 1]) { + // bar::<{ N + 1 }>(); + // } + // ``` + ExprKind::Block { body: thir::Block { stmts: box [], expr: Some(e), .. } } => { + self.recurse_build(*e)? + } + // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a + // "coercion cast" i.e. using a coercion or is a no-op. + // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested) + &ExprKind::Use { source } => { + let arg = self.recurse_build(source)?; + self.nodes.push(Node::Cast(CastKind::Use, arg, node.ty)) + } + &ExprKind::Cast { source } => { + let arg = self.recurse_build(source)?; + self.nodes.push(Node::Cast(CastKind::As, arg, node.ty)) + } + ExprKind::Borrow{ arg, ..} => { + let arg_node = &self.body.exprs[*arg]; + + // Skip reborrows for now until we allow Deref/Borrow/AddressOf + // expressions. + // FIXME(generic_const_exprs): Verify/explain why this is sound + if let ExprKind::Deref { arg } = arg_node.kind { + self.recurse_build(arg)? + } else { + self.maybe_supported_error( + node.span, + "borrowing is not supported in generic constants", + )? + } + } + // FIXME(generic_const_exprs): We may want to support these. + ExprKind::AddressOf { .. } | ExprKind::Deref {..}=> self.maybe_supported_error( + node.span, + "dereferencing or taking the address is not supported in generic constants", + )?, + ExprKind::Repeat { .. } | ExprKind::Array { .. } => self.maybe_supported_error( + node.span, + "array construction is not supported in generic constants", + )?, + ExprKind::Block { .. } => self.maybe_supported_error( + node.span, + "blocks are not supported in generic constant", + )?, + ExprKind::NeverToAny { .. } => self.maybe_supported_error( + node.span, + "converting nevers to any is not supported in generic constant", + )?, + ExprKind::Tuple { .. } => self.maybe_supported_error( + node.span, + "tuple construction is not supported in generic constants", + )?, + ExprKind::Index { .. } => self.maybe_supported_error( + node.span, + "indexing is not supported in generic constant", + )?, + ExprKind::Field { .. } => self.maybe_supported_error( + node.span, + "field access is not supported in generic constant", + )?, + ExprKind::ConstBlock { .. } => self.maybe_supported_error( + node.span, + "const blocks are not supported in generic constant", + )?, + ExprKind::Adt(_) => self.maybe_supported_error( + node.span, + "struct/enum construction is not supported in generic constants", + )?, + // dont know if this is correct + ExprKind::Pointer { .. } => + self.error(node.span, "pointer casts are not allowed in generic constants")?, + ExprKind::Yield { .. } => + self.error(node.span, "generator control flow is not allowed in generic constants")?, + ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => self + .error( + node.span, + "loops and loop control flow are not supported in generic constants", + )?, + ExprKind::Box { .. } => + self.error(node.span, "allocations are not allowed in generic constants")?, + + ExprKind::Unary { .. } => unreachable!(), + // we handle valid unary/binary ops above + ExprKind::Binary { .. } => + self.error(node.span, "unsupported binary operation in generic constants")?, + ExprKind::LogicalOp { .. } => + self.error(node.span, "unsupported operation in generic constants, short-circuiting operations would imply control flow")?, + ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { + self.error(node.span, "assignment is not supported in generic constants")? + } + ExprKind::Closure { .. } | ExprKind::Return { .. } => self.error( + node.span, + "closures and function keywords are not supported in generic constants", + )?, + // let expressions imply control flow + ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => + self.error(node.span, "control flow is not supported in generic constants")?, + ExprKind::InlineAsm { .. } => { + self.error(node.span, "assembly is not supported in generic constants")? + } + + // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen + ExprKind::VarRef { .. } + | ExprKind::UpvarRef { .. } + | ExprKind::StaticRef { .. } + | ExprKind::ThreadLocalRef(_) => { + self.error(node.span, "unsupported operation in generic constant")? + } + }) + } +} + +/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead. +pub fn thir_abstract_const<'tcx>( + tcx: TyCtxt<'tcx>, + def: ty::WithOptConstParam<LocalDefId>, +) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> { + if tcx.features().generic_const_exprs { + match tcx.def_kind(def.did) { + // FIXME(generic_const_exprs): We currently only do this for anonymous constants, + // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether + // we want to look into them or treat them as opaque projections. + // + // Right now we do neither of that and simply always fail to unify them. + DefKind::AnonConst | DefKind::InlineConst => (), + _ => return Ok(None), + } + + let body = tcx.thir_body(def)?; + + AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))? + .map(AbstractConstBuilder::build) + .transpose() + } else { + Ok(None) + } +} + pub fn provide(providers: &mut ty::query::Providers) { - *providers = ty::query::Providers { destructure_const, ..*providers }; + *providers = ty::query::Providers { + destructure_const, + thir_abstract_const: |tcx, def_id| { + let def_id = def_id.expect_local(); + if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) { + tcx.thir_abstract_const_of_const_arg(def) + } else { + thir_abstract_const(tcx, ty::WithOptConstParam::unknown(def_id)) + } + }, + thir_abstract_const_of_const_arg: |tcx, (did, param_did)| { + thir_abstract_const( + tcx, + ty::WithOptConstParam { did, const_param_did: Some(param_did) }, + ) + }, + ..*providers + }; } diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 7624d31b40b..09f5c2a11aa 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -7,6 +7,8 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(control_flow_enum)] #![feature(let_else)] +#![feature(never_type)] +#![feature(box_patterns)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 1d4e64b6bfc..699237446cf 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -439,7 +439,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // as the rest of the type. As such, we ignore missing // stability attributes. }, - ) + ); } if let (hir::TyKind::Infer, false) = (&ty.kind, self.astconv.allow_ty_infer()) { self.inferred_params.push(ty.span); diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 3af73abe5ce..d14d06237be 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -285,29 +285,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let parent_node = hir.get(parent_hir_id); if let ( hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure { fn_decl_span, body, .. }, + kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, body, .. }), .. }), hir::ExprKind::Block(..), ) = (parent_node, callee_node) { - let fn_decl_span = if hir.body(*body).generator_kind + let fn_decl_span = if hir.body(body).generator_kind == Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure)) { // Actually need to unwrap a few more layers of HIR to get to // the _real_ closure... let async_closure = hir.get_parent_node(hir.get_parent_node(parent_hir_id)); if let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure { fn_decl_span, .. }, + kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), .. }) = hir.get(async_closure) { - *fn_decl_span + fn_decl_span } else { return; } } else { - *fn_decl_span + fn_decl_span }; let start = fn_decl_span.shrink_to_lo(); diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index acd7e4a92dc..9c9a2096ae9 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -1577,8 +1577,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { let parent_id = fcx.tcx.hir().get_parent_node(id); let parent = fcx.tcx.hir().get(parent_id); if let Some(expr) = expression - && let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure { body, .. }, .. }) = parent - && !matches!(fcx.tcx.hir().body(*body).value.kind, hir::ExprKind::Block(..)) + && let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { body, .. }), .. }) = parent + && !matches!(fcx.tcx.hir().body(body).value.kind, hir::ExprKind::Block(..)) { fcx.suggest_missing_semicolon(&mut err, expr, expected, true); } diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index dc553d1441e..740261cfe74 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -483,7 +483,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let param_parent = self.tcx.hir().get_parent_node(*param_hir_id); let Some(Node::Expr(hir::Expr { hir_id: expr_hir_id, - kind: hir::ExprKind::Closure { fn_decl: closure_fn_decl, .. }, + kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }), .. })) = self.tcx.hir().find(param_parent) else { return None; diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 30b76b922c7..2400211394e 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -35,7 +35,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ExprKind, HirId, QPath}; +use rustc_hir::{Closure, ExprKind, HirId, QPath}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::InferOk; @@ -319,7 +319,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Match(discrim, arms, match_src) => { self.check_match(expr, &discrim, arms, expected, match_src) } - ExprKind::Closure { capture_clause, fn_decl, body, movability, .. } => { + ExprKind::Closure(&Closure { capture_clause, fn_decl, body, movability, .. }) => { self.check_expr_closure(expr, capture_clause, &fn_decl, body, movability, expected) } ExprKind::Block(body, _) => self.check_block_with_expected(&body, expected), diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 60ee2233ed9..ec045d3e70c 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -21,15 +21,18 @@ use rustc_hir::def_id::DefId; use rustc_hir::{ExprKind, Node, QPath}; use rustc_index::vec::IndexVec; use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt}; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::InferOk; use rustc_infer::infer::TypeTrace; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt}; +use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::{self, Span}; -use rustc_trait_selection::traits::{self, ObligationCauseCode, StatementAsExpression}; +use rustc_trait_selection::traits::{ + self, ObligationCauseCode, SelectionContext, StatementAsExpression, +}; use std::iter; use std::slice; @@ -89,7 +92,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args_no_rcvr, false, tuple_arguments, - None, + method.ok().map(|method| method.def_id), ); return self.tcx.ty_error(); } @@ -393,41 +396,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if !call_appears_satisfied { - // Next, let's construct the error - let (error_span, full_call_span, ctor_of) = match &call_expr.kind { - hir::ExprKind::Call( - hir::Expr { - span, - kind: - hir::ExprKind::Path(hir::QPath::Resolved( - _, - hir::Path { res: Res::Def(DefKind::Ctor(of, _), _), .. }, - )), - .. - }, - _, - ) => (call_span, *span, Some(of)), - hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None), - hir::ExprKind::MethodCall(path_segment, _, span) => { - let ident_span = path_segment.ident.span; - let ident_span = if let Some(args) = path_segment.args { - ident_span.with_hi(args.span_ext.hi()) - } else { - ident_span - }; - ( - *span, ident_span, None, // methods are never ctors - ) - } - k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), - }; - let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span); - let call_name = match ctor_of { - Some(CtorOf::Struct) => "struct", - Some(CtorOf::Variant) => "enum variant", - None => "function", - }; - let compatibility_diagonal = IndexVec::from_raw(compatibility_diagonal); let provided_args = IndexVec::from_iter(provided_args.iter().take(if c_variadic { minimum_input_count @@ -451,13 +419,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { compatibility_diagonal, formal_and_expected_inputs, provided_args, - full_call_span, - error_span, - args_span, - call_name, c_variadic, err_code, fn_def_id, + call_span, + call_expr, ); } } @@ -467,14 +433,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>, formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>, provided_args: IndexVec<ProvidedIdx, &'tcx hir::Expr<'tcx>>, - full_call_span: Span, - error_span: Span, - args_span: Span, - call_name: &str, c_variadic: bool, err_code: &str, fn_def_id: Option<DefId>, + call_span: Span, + call_expr: &hir::Expr<'tcx>, ) { + // Next, let's construct the error + let (error_span, full_call_span, ctor_of) = match &call_expr.kind { + hir::ExprKind::Call( + hir::Expr { + span, + kind: + hir::ExprKind::Path(hir::QPath::Resolved( + _, + hir::Path { res: Res::Def(DefKind::Ctor(of, _), _), .. }, + )), + .. + }, + _, + ) => (call_span, *span, Some(of)), + hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None), + hir::ExprKind::MethodCall(path_segment, _, span) => { + let ident_span = path_segment.ident.span; + let ident_span = if let Some(args) = path_segment.args { + ident_span.with_hi(args.span_ext.hi()) + } else { + ident_span + }; + ( + *span, ident_span, None, // methods are never ctors + ) + } + k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), + }; + let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span); + let call_name = match ctor_of { + Some(CtorOf::Struct) => "struct", + Some(CtorOf::Variant) => "enum variant", + None => "function", + }; + // Don't print if it has error types or is just plain `_` fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool { tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var()) @@ -495,6 +494,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (self.resolve_vars_if_possible(ty), expr.span) }) .collect(); + let callee_expr = match &call_expr.peel_blocks().kind { + hir::ExprKind::Call(callee, _) => Some(*callee), + hir::ExprKind::MethodCall(_, callee, _) => { + if let Some((DefKind::AssocFn, def_id)) = + self.typeck_results.borrow().type_dependent_def(call_expr.hir_id) + && let Some(assoc) = tcx.opt_associated_item(def_id) + && assoc.fn_has_self_parameter + { + Some(&callee[0]) + } else { + None + } + } + _ => None, + }; + let callee_ty = callee_expr + .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr)); // A "softer" version of the `demand_compatible`, which checks types without persisting them, // and treats error types differently @@ -631,7 +647,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); }; - label_fn_like(tcx, &mut err, fn_def_id); + self.label_fn_like(&mut err, fn_def_id, callee_ty); err.emit(); return; } @@ -721,7 +737,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("arguments to this {} are incorrect", call_name), ); // Call out where the function is defined - label_fn_like(tcx, &mut err, fn_def_id); + self.label_fn_like(&mut err, fn_def_id, callee_ty); err.emit(); return; } @@ -1003,7 +1019,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Call out where the function is defined - label_fn_like(tcx, &mut err, fn_def_id); + self.label_fn_like(&mut err, fn_def_id, callee_ty); // And add a suggestion block for all of the parameters let suggestion_text = match suggestion_text { @@ -1795,47 +1811,126 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } -} -fn label_fn_like<'tcx>( - tcx: TyCtxt<'tcx>, - err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>, - def_id: Option<DefId>, -) { - let Some(def_id) = def_id else { - return; - }; - - if let Some(def_span) = tcx.def_ident_span(def_id) { - let mut spans: MultiSpan = def_span.into(); - - let params = tcx - .hir() - .get_if_local(def_id) - .and_then(|node| node.body_id()) - .into_iter() - .flat_map(|id| tcx.hir().body(id).params); - - for param in params { - spans.push_span_label(param.span, ""); + fn label_fn_like( + &self, + err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>, + callable_def_id: Option<DefId>, + callee_ty: Option<Ty<'tcx>>, + ) { + let Some(mut def_id) = callable_def_id else { + return; + }; + + if let Some(assoc_item) = self.tcx.opt_associated_item(def_id) + // Possibly points at either impl or trait item, so try to get it + // to point to trait item, then get the parent. + // This parent might be an impl in the case of an inherent function, + // but the next check will fail. + && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id) + && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id) + // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce" + && let Some(call_kind) = ty::ClosureKind::from_def_id(self.tcx, maybe_trait_def_id) + && let Some(callee_ty) = callee_ty + { + let callee_ty = callee_ty.peel_refs(); + match *callee_ty.kind() { + ty::Param(param) => { + let param = + self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx); + if param.kind.is_synthetic() { + // if it's `impl Fn() -> ..` then just fall down to the def-id based logic + def_id = param.def_id; + } else { + // Otherwise, find the predicate that makes this generic callable, + // and point at that. + let instantiated = self + .tcx + .explicit_predicates_of(self.body_id.owner) + .instantiate_identity(self.tcx); + // FIXME(compiler-errors): This could be problematic if something has two + // fn-like predicates with different args, but callable types really never + // do that, so it's OK. + for (predicate, span) in + std::iter::zip(instantiated.predicates, instantiated.spans) + { + if let ty::PredicateKind::Trait(pred) = predicate.kind().skip_binder() + && pred.self_ty().peel_refs() == callee_ty + && ty::ClosureKind::from_def_id(self.tcx, pred.def_id()).is_some() + { + err.span_note(span, "callable defined here"); + return; + } + } + } + } + ty::Opaque(new_def_id, _) + | ty::Closure(new_def_id, _) + | ty::FnDef(new_def_id, _) => { + def_id = new_def_id; + } + _ => { + // Look for a user-provided impl of a `Fn` trait, and point to it. + let new_def_id = self.probe(|_| { + let trait_ref = ty::TraitRef::new( + call_kind.to_def_id(self.tcx), + self.tcx.mk_substs([ + ty::GenericArg::from(callee_ty), + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: rustc_span::DUMMY_SP, + }) + .into(), + ].into_iter()), + ); + let obligation = traits::Obligation::new( + traits::ObligationCause::dummy(), + self.param_env, + ty::Binder::dummy(ty::TraitPredicate { + trait_ref, + constness: ty::BoundConstness::NotConst, + polarity: ty::ImplPolarity::Positive, + }), + ); + match SelectionContext::new(&self).select(&obligation) { + Ok(Some(traits::ImplSource::UserDefined(impl_source))) => { + Some(impl_source.impl_def_id) + } + _ => None + } + }); + if let Some(new_def_id) = new_def_id { + def_id = new_def_id; + } else { + return; + } + } + } } - let def_kind = tcx.def_kind(def_id); - err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); - } else { - match tcx.hir().get_if_local(def_id) { - Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure { fn_decl_span, .. }, - .. - })) => { - let spans: MultiSpan = (*fn_decl_span).into(); + if let Some(def_span) = self.tcx.def_ident_span(def_id) && !def_span.is_dummy() { + let mut spans: MultiSpan = def_span.into(); - // Note: We don't point to param spans here because they overlap - // with the closure span itself + let params = self + .tcx + .hir() + .get_if_local(def_id) + .and_then(|node| node.body_id()) + .into_iter() + .flat_map(|id| self.tcx.hir().body(id).params); - err.span_note(spans, "closure defined here"); + for param in params { + spans.push_span_label(param.span, ""); } - _ => {} + + let def_kind = self.tcx.def_kind(def_id); + err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); + } else { + let def_kind = self.tcx.def_kind(def_id); + err.span_note( + self.tcx.def_span(def_id), + &format!("{} defined here", def_kind.descr(def_id)), + ); } } } diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_typeck/src/check/region.rs index a1a92c62ad2..0c33a243e10 100644 --- a/compiler/rustc_typeck/src/check/region.rs +++ b/compiler/rustc_typeck/src/check/region.rs @@ -335,7 +335,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h match expr.kind { // Manually recurse over closures and inline consts, because they are the only // case of nested bodies that share the parent environment. - hir::ExprKind::Closure { body, .. } + hir::ExprKind::Closure(&hir::Closure { body, .. }) | hir::ExprKind::ConstBlock(hir::AnonConst { body, .. }) => { let body = visitor.tcx.hir().body(body); visitor.visit_body(body); diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 824ddb10a37..3bd3e2d8091 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -142,7 +142,7 @@ struct InferBorrowKindVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { match expr.kind { - hir::ExprKind::Closure { capture_clause, body: body_id, .. } => { + hir::ExprKind::Closure(&hir::Closure { capture_clause, body: body_id, .. }) => { let body = self.fcx.tcx.hir().body(body_id); self.visit_body(body); self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, capture_clause); diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index 41d241f84ac..0cbb0e25d0d 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -263,7 +263,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { self.fix_index_builtin_expr(e); match e.kind { - hir::ExprKind::Closure { body, .. } => { + hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let body = self.fcx.tcx.hir().body(body); for param in body.params { self.visit_node_id(e.span, param.hir_id); diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 9795be1a912..6ec741269e8 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1717,8 +1717,10 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { // provide junk type parameter defs - the only place that // cares about anything but the length is instantiation, // and we don't do that for closures. - if let Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { movability: gen, .. }, .. }) = - node + if let Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { movability: gen, .. }), + .. + }) = node { let dummy_args = if gen.is_some() { &["<resume_ty>", "<yield_ty>", "<return_ty>", "<witness>", "<upvars>"][..] @@ -2564,7 +2566,7 @@ fn is_foreign_item(tcx: TyCtxt<'_>, def_id: DefId) -> bool { fn generator_kind(tcx: TyCtxt<'_>, def_id: DefId) -> Option<hir::GeneratorKind> { match tcx.hir().get_if_local(def_id) { Some(Node::Expr(&rustc_hir::Expr { - kind: rustc_hir::ExprKind::Closure { body, .. }, + kind: rustc_hir::ExprKind::Closure(&rustc_hir::Closure { body, .. }), .. })) => tcx.hir().body(body).generator_kind(), Some(_) => None, diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index d3816d70b63..541de5c9029 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -108,7 +108,6 @@ #![feature(const_refs_to_cell)] #![feature(core_c_str)] #![feature(core_intrinsics)] -#![feature(core_ffi_c)] #![feature(const_eval_select)] #![feature(const_pin)] #![feature(cstr_from_bytes_until_nul)] @@ -125,6 +124,7 @@ #![cfg_attr(test, feature(new_uninit))] #![feature(nonnull_slice_from_raw_parts)] #![feature(pattern)] +#![feature(pointer_byte_offsets)] #![feature(ptr_internals)] #![feature(ptr_metadata)] #![feature(ptr_sub_ptr)] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index a248cd458df..b89b03683ba 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -907,8 +907,7 @@ impl<T: ?Sized> Rc<T> { let offset = unsafe { data_offset(ptr) }; // Reverse the offset to find the original RcBox. - let rc_ptr = - unsafe { (ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut RcBox<T>) }; + let rc_ptr = unsafe { ptr.byte_sub(offset) as *mut RcBox<T> }; unsafe { Self::from_ptr(rc_ptr) } } @@ -2331,7 +2330,7 @@ impl<T: ?Sized> Weak<T> { let offset = unsafe { data_offset(ptr) }; // Thus, we reverse the offset to get the whole RcBox. // SAFETY: the pointer originated from a Weak, so this offset is safe. - unsafe { (ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut RcBox<T>) } + unsafe { ptr.byte_sub(offset) as *mut RcBox<T> } }; // SAFETY: we now have recovered the original Weak pointer, so can create the Weak. @@ -2684,7 +2683,7 @@ impl<T: ?Sized> Unpin for Rc<T> {} /// /// The pointer must point to (and have valid metadata for) a previously /// valid instance of T, but the T is allowed to be dropped. -unsafe fn data_offset<T: ?Sized>(ptr: *const T) -> isize { +unsafe fn data_offset<T: ?Sized>(ptr: *const T) -> usize { // Align the unsized value to the end of the RcBox. // Because RcBox is repr(C), it will always be the last field in memory. // SAFETY: since the only unsized types possible are slices, trait objects, @@ -2695,7 +2694,7 @@ unsafe fn data_offset<T: ?Sized>(ptr: *const T) -> isize { } #[inline] -fn data_offset_align(align: usize) -> isize { +fn data_offset_align(align: usize) -> usize { let layout = Layout::new::<RcBox<()>>(); - (layout.size() + layout.padding_needed_for(align)) as isize + layout.size() + layout.padding_needed_for(align) } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 24e849aab4c..4c03cc3ed15 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -908,8 +908,7 @@ impl<T: ?Sized> Arc<T> { let offset = data_offset(ptr); // Reverse the offset to find the original ArcInner. - let arc_ptr = - (ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut ArcInner<T>); + let arc_ptr = ptr.byte_sub(offset) as *mut ArcInner<T>; Self::from_ptr(arc_ptr) } @@ -1942,7 +1941,7 @@ impl<T: ?Sized> Weak<T> { let offset = unsafe { data_offset(ptr) }; // Thus, we reverse the offset to get the whole RcBox. // SAFETY: the pointer originated from a Weak, so this offset is safe. - unsafe { (ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut ArcInner<T>) } + unsafe { ptr.byte_sub(offset) as *mut ArcInner<T> } }; // SAFETY: we now have recovered the original Weak pointer, so can create the Weak. @@ -2749,7 +2748,7 @@ impl<T: ?Sized> Unpin for Arc<T> {} /// /// The pointer must point to (and have valid metadata for) a previously /// valid instance of T, but the T is allowed to be dropped. -unsafe fn data_offset<T: ?Sized>(ptr: *const T) -> isize { +unsafe fn data_offset<T: ?Sized>(ptr: *const T) -> usize { // Align the unsized value to the end of the ArcInner. // Because RcBox is repr(C), it will always be the last field in memory. // SAFETY: since the only unsized types possible are slices, trait objects, @@ -2760,7 +2759,7 @@ unsafe fn data_offset<T: ?Sized>(ptr: *const T) -> isize { } #[inline] -fn data_offset_align(align: usize) -> isize { +fn data_offset_align(align: usize) -> usize { let layout = Layout::new::<ArcInner<()>>(); - (layout.size() + layout.padding_needed_for(align)) as isize + layout.size() + layout.padding_needed_for(align) } diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 367cdcdcc06..bf5d0c941e9 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -12,7 +12,6 @@ #![feature(const_ptr_write)] #![feature(const_try)] #![feature(core_c_str)] -#![feature(core_ffi_c)] #![feature(core_intrinsics)] #![feature(drain_filter)] #![feature(exact_size_is_empty)] diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 43e4b7f08e2..3b711c6b72d 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -26,7 +26,7 @@ macro_rules! type_alias_no_nz { } => { #[doc = include_str!($Docfile)] $( $Cfg )* - #[unstable(feature = "core_ffi_c", issue = "94501")] + #[stable(feature = "core_ffi_c", since = "1.64.0")] pub type $Alias = $Real; } } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 2895c923adc..9097ffc2cc5 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1457,6 +1457,7 @@ extern "rust-intrinsic" { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(bootstrap), rustc_allowed_through_unstable_modules)] #[rustc_const_stable(feature = "const_transmute", since = "1.56.0")] #[rustc_diagnostic_item = "transmute"] pub fn transmute<T, U>(e: T) -> U; @@ -2649,6 +2650,7 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) { /// Here is an example of how this could cause a problem: /// ```no_run /// #![feature(const_eval_select)] +/// #![feature(core_intrinsics)] /// use std::hint::unreachable_unchecked; /// use std::intrinsics::const_eval_select; /// diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 673a39c298f..bd62bc5c305 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1537,7 +1537,7 @@ pub(crate) mod builtin { /// Unstable implementation detail of the `rustc` compiler, do not use. #[rustc_builtin_macro] #[stable(feature = "rust1", since = "1.0.0")] - #[allow_internal_unstable(core_intrinsics, libstd_sys_internals)] + #[allow_internal_unstable(core_intrinsics, libstd_sys_internals, rt)] #[deprecated(since = "1.52.0", note = "rustc-serialize is deprecated and no longer supported")] #[doc(hidden)] // While technically stable, using it is unstable, and deprecated. Hide it. pub macro RustcDecodable($item:item) { @@ -1547,7 +1547,7 @@ pub(crate) mod builtin { /// Unstable implementation detail of the `rustc` compiler, do not use. #[rustc_builtin_macro] #[stable(feature = "rust1", since = "1.0.0")] - #[allow_internal_unstable(core_intrinsics)] + #[allow_internal_unstable(core_intrinsics, rt)] #[deprecated(since = "1.52.0", note = "rustc-serialize is deprecated and no longer supported")] #[doc(hidden)] // While technically stable, using it is unstable, and deprecated. Hide it. pub macro RustcEncodable($item:item) { diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 2c54bfb35eb..5849a37444f 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -19,7 +19,6 @@ #![feature(const_ptr_write)] #![feature(const_trait_impl)] #![feature(const_likely)] -#![feature(core_ffi_c)] #![feature(core_intrinsics)] #![feature(core_private_bignum)] #![feature(core_private_diy_float)] diff --git a/library/core/tests/unicode.rs b/library/core/tests/unicode.rs index c28ea859115..bbace0ef66c 100644 --- a/library/core/tests/unicode.rs +++ b/library/core/tests/unicode.rs @@ -1,5 +1,5 @@ #[test] pub fn version() { - let (major, _minor, _update) = core::unicode::UNICODE_VERSION; + let (major, _minor, _update) = core::char::UNICODE_VERSION; assert!(major >= 10); } diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 87f213b1608..57f16f9517f 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -156,7 +156,7 @@ use core::array; use core::convert::Infallible; use crate::alloc::{AllocError, LayoutError}; -use crate::any::TypeId; +use crate::any::{Demand, Provider, TypeId}; use crate::backtrace::Backtrace; use crate::borrow::Cow; use crate::cell; @@ -295,6 +295,85 @@ pub trait Error: Debug + Display { fn cause(&self) -> Option<&dyn Error> { self.source() } + + /// Provides type based access to context intended for error reports. + /// + /// Used in conjunction with [`Demand::provide_value`] and [`Demand::provide_ref`] to extract + /// references to member variables from `dyn Error` trait objects. + /// + /// # Example + /// + /// ```rust + /// #![feature(provide_any)] + /// #![feature(error_generic_member_access)] + /// use core::fmt; + /// use core::any::Demand; + /// + /// #[derive(Debug)] + /// struct MyBacktrace { + /// // ... + /// } + /// + /// impl MyBacktrace { + /// fn new() -> MyBacktrace { + /// // ... + /// # MyBacktrace {} + /// } + /// } + /// + /// #[derive(Debug)] + /// struct SourceError { + /// // ... + /// } + /// + /// impl fmt::Display for SourceError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "Example Source Error") + /// } + /// } + /// + /// impl std::error::Error for SourceError {} + /// + /// #[derive(Debug)] + /// struct Error { + /// source: SourceError, + /// backtrace: MyBacktrace, + /// } + /// + /// impl fmt::Display for Error { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "Example Error") + /// } + /// } + /// + /// impl std::error::Error for Error { + /// fn provide<'a>(&'a self, req: &mut Demand<'a>) { + /// req + /// .provide_ref::<MyBacktrace>(&self.backtrace) + /// .provide_ref::<dyn std::error::Error + 'static>(&self.source); + /// } + /// } + /// + /// fn main() { + /// let backtrace = MyBacktrace::new(); + /// let source = SourceError {}; + /// let error = Error { source, backtrace }; + /// let dyn_error = &error as &dyn std::error::Error; + /// let backtrace_ref = dyn_error.request_ref::<MyBacktrace>().unwrap(); + /// + /// assert!(core::ptr::eq(&error.backtrace, backtrace_ref)); + /// } + /// ``` + #[unstable(feature = "error_generic_member_access", issue = "none")] + #[allow(unused_variables)] + fn provide<'a>(&'a self, req: &mut Demand<'a>) {} +} + +#[unstable(feature = "error_generic_member_access", issue = "none")] +impl Provider for dyn Error + 'static { + fn provide<'a>(&'a self, req: &mut Demand<'a>) { + self.provide(req) + } } mod private { @@ -831,6 +910,18 @@ impl dyn Error + 'static { None } } + + /// Request a reference of type `T` as context about this error. + #[unstable(feature = "error_generic_member_access", issue = "none")] + pub fn request_ref<T: ?Sized + 'static>(&self) -> Option<&T> { + core::any::request_ref(self) + } + + /// Request a value of type `T` as context about this error. + #[unstable(feature = "error_generic_member_access", issue = "none")] + pub fn request_value<T: 'static>(&self) -> Option<T> { + core::any::request_value(self) + } } impl dyn Error + 'static + Send { @@ -854,6 +945,18 @@ impl dyn Error + 'static + Send { pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> { <dyn Error + 'static>::downcast_mut::<T>(self) } + + /// Request a reference of type `T` as context about this error. + #[unstable(feature = "error_generic_member_access", issue = "none")] + pub fn request_ref<T: ?Sized + 'static>(&self) -> Option<&T> { + <dyn Error + 'static>::request_ref(self) + } + + /// Request a value of type `T` as context about this error. + #[unstable(feature = "error_generic_member_access", issue = "none")] + pub fn request_value<T: 'static>(&self) -> Option<T> { + <dyn Error + 'static>::request_value(self) + } } impl dyn Error + 'static + Send + Sync { @@ -877,6 +980,18 @@ impl dyn Error + 'static + Send + Sync { pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> { <dyn Error + 'static>::downcast_mut::<T>(self) } + + /// Request a reference of type `T` as context about this error. + #[unstable(feature = "error_generic_member_access", issue = "none")] + pub fn request_ref<T: ?Sized + 'static>(&self) -> Option<&T> { + <dyn Error + 'static>::request_ref(self) + } + + /// Request a value of type `T` as context about this error. + #[unstable(feature = "error_generic_member_access", issue = "none")] + pub fn request_value<T: 'static>(&self) -> Option<T> { + <dyn Error + 'static>::request_value(self) + } } impl dyn Error { diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index 94ae97a2e40..484f42dafc3 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -168,6 +168,12 @@ pub type FromBytesWithNulError = core::ffi::FromBytesWithNulError; #[stable(feature = "rust1", since = "1.0.0")] pub use self::os_str::{OsStr, OsString}; +#[stable(feature = "core_ffi_c", since = "1.64.0")] +pub use core::ffi::{ + c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, + c_ulong, c_ulonglong, c_ushort, +}; + #[stable(feature = "core_c_void", since = "1.30.0")] pub use core::ffi::c_void; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 298321f41e4..8fbc5e27d02 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -214,7 +214,7 @@ #![cfg_attr(not(bootstrap), deny(ffi_unwind_calls))] // std may use features in a platform-specific way #![allow(unused_features)] -#![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count))] +#![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count, rt))] #![cfg_attr( all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform) @@ -286,6 +286,7 @@ #![feature(panic_internals)] #![feature(portable_simd)] #![feature(prelude_2024)] +#![feature(provide_any)] #![feature(ptr_as_uninit)] #![feature(raw_os_nonzero)] #![feature(slice_internals)] @@ -297,6 +298,7 @@ // Library features (alloc): #![feature(alloc_layout_extra)] #![feature(alloc_c_string)] +#![feature(alloc_ffi)] #![feature(allocator_api)] #![feature(get_mut_unchecked)] #![feature(map_try_insert)] @@ -317,7 +319,6 @@ #![feature(cfg_eval)] #![feature(concat_bytes)] #![feature(const_format_args)] -#![feature(core_ffi_c)] #![feature(core_panic)] #![feature(custom_test_frameworks)] #![feature(edition_panic)] diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index ac16f476143..45bc56efb3b 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -11,7 +11,7 @@ use crate::thread::Result; #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] -#[allow_internal_unstable(libstd_sys_internals, const_format_args, core_panic)] +#[allow_internal_unstable(libstd_sys_internals, const_format_args, core_panic, rt)] #[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_2015_macro")] #[rustc_macro_transparency = "semitransparent"] pub macro panic_2015 { diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 712454d5048..1aa79f5566a 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -1764,6 +1764,7 @@ impl<'a> Builder<'a> { if !target.contains("windows") { let needs_unstable_opts = target.contains("linux") + || target.contains("solaris") || target.contains("windows") || target.contains("bsd") || target.contains("dragonfly") diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 4aadc3943c9..cba013b5bb6 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -39,7 +39,11 @@ fn missing_tool(tool_name: &str, skip: bool) { if skip { println!("Unable to build {}, skipping dist", tool_name) } else { - panic!("Unable to build {}", tool_name) + let help = "note: not all tools are available on all nightlies\nhelp: see https://forge.rust-lang.org/infra/toolstate.html for more information"; + panic!( + "Unable to build submodule tool {} (use `missing-tools = true` to ignore this failure)\n{}", + tool_name, help + ) } } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 459b0fed6e8..c1fdece9ec6 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -854,7 +854,7 @@ fn render_stability_since_raw( } let const_title_and_stability = match const_stability { - Some(ConstStability { level: StabilityLevel::Stable { since }, .. }) + Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) if Some(since) != containing_const_ver => { Some((format!("const since {}", since), format!("const: {}", since))) diff --git a/src/test/codegen/intrinsics/const_eval_select.rs b/src/test/codegen/intrinsics/const_eval_select.rs index 34e653b4b9d..db8a04763d3 100644 --- a/src/test/codegen/intrinsics/const_eval_select.rs +++ b/src/test/codegen/intrinsics/const_eval_select.rs @@ -2,6 +2,7 @@ #![crate_type = "lib"] #![feature(const_eval_select)] +#![feature(core_intrinsics)] use std::intrinsics::const_eval_select; diff --git a/src/test/run-make-fulldeps/issue-26092/Makefile b/src/test/run-make-fulldeps/issue-26092/Makefile index 27631c31c4a..885f45a0224 100644 --- a/src/test/run-make-fulldeps/issue-26092/Makefile +++ b/src/test/run-make-fulldeps/issue-26092/Makefile @@ -1,4 +1,6 @@ -include ../tools.mk +# This test ensures that rustc does not panic with `-o ""` option. + all: - $(RUSTC) -o "" blank.rs 2>&1 | $(CGREP) -i 'No such file or directory' + $(RUSTC) -o "" blank.rs 2>&1 | $(CGREP) -i 'panic' && exit 1 || exit 0 diff --git a/src/test/ui-fulldeps/internal-lints/rustc_pass_by_value.rs b/src/test/ui-fulldeps/internal-lints/rustc_pass_by_value.rs index 402c41f3766..10bab2d889a 100644 --- a/src/test/ui-fulldeps/internal-lints/rustc_pass_by_value.rs +++ b/src/test/ui-fulldeps/internal-lints/rustc_pass_by_value.rs @@ -93,7 +93,7 @@ impl CustomStruct { fn test_alias( value: CustomAlias, - reference: &CustomAlias, //~ ERROR passing `CustomAlias<>` by reference + reference: &CustomAlias, //~ ERROR passing `CustomAlias<'_>` by reference ) { } } diff --git a/src/test/ui-fulldeps/internal-lints/rustc_pass_by_value.stderr b/src/test/ui-fulldeps/internal-lints/rustc_pass_by_value.stderr index 7f6e57b38f3..69cf20656d7 100644 --- a/src/test/ui-fulldeps/internal-lints/rustc_pass_by_value.stderr +++ b/src/test/ui-fulldeps/internal-lints/rustc_pass_by_value.stderr @@ -94,11 +94,11 @@ error: passing `CustomStruct` by reference LL | reference: &CustomStruct, | ^^^^^^^^^^^^^ help: try passing by value: `CustomStruct` -error: passing `CustomAlias<>` by reference +error: passing `CustomAlias<'_>` by reference --> $DIR/rustc_pass_by_value.rs:96:20 | LL | reference: &CustomAlias, - | ^^^^^^^^^^^^ help: try passing by value: `CustomAlias<>` + | ^^^^^^^^^^^^ help: try passing by value: `CustomAlias<'_>` error: passing `WithParameters<T, 1>` by reference --> $DIR/rustc_pass_by_value.rs:110:20 diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs index a37d3a32571..a679b7b4e19 100644 --- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs @@ -114,6 +114,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) { let decl = P(FnDecl { inputs: vec![], output: FnRetTy::Default(DUMMY_SP) }); iter_exprs(depth - 1, &mut |e| { g(ExprKind::Closure( + ClosureBinder::NotPresent, CaptureBy::Value, Async::No, Movability::Movable, diff --git a/src/test/ui/argument-suggestions/exotic-calls.rs b/src/test/ui/argument-suggestions/exotic-calls.rs new file mode 100644 index 00000000000..a18e967668d --- /dev/null +++ b/src/test/ui/argument-suggestions/exotic-calls.rs @@ -0,0 +1,26 @@ +fn foo<T: Fn()>(t: T) { + t(1i32); + //~^ ERROR this function takes 0 arguments but 1 argument was supplied +} + +fn bar(t: impl Fn()) { + t(1i32); + //~^ ERROR this function takes 0 arguments but 1 argument was supplied +} + +fn baz() -> impl Fn() { + || {} +} + +fn baz2() { + baz()(1i32) + //~^ ERROR this function takes 0 arguments but 1 argument was supplied +} + +fn qux() { + let x = || {}; + x(1i32); + //~^ ERROR this function takes 0 arguments but 1 argument was supplied +} + +fn main() {} diff --git a/src/test/ui/argument-suggestions/exotic-calls.stderr b/src/test/ui/argument-suggestions/exotic-calls.stderr new file mode 100644 index 00000000000..ca93ecc4e38 --- /dev/null +++ b/src/test/ui/argument-suggestions/exotic-calls.stderr @@ -0,0 +1,67 @@ +error[E0057]: this function takes 0 arguments but 1 argument was supplied + --> $DIR/exotic-calls.rs:2:5 + | +LL | t(1i32); + | ^ ---- argument of type `i32` unexpected + | +note: callable defined here + --> $DIR/exotic-calls.rs:1:11 + | +LL | fn foo<T: Fn()>(t: T) { + | ^^^^ +help: remove the extra argument + | +LL | t(); + | ~~~ + +error[E0057]: this function takes 0 arguments but 1 argument was supplied + --> $DIR/exotic-calls.rs:7:5 + | +LL | t(1i32); + | ^ ---- argument of type `i32` unexpected + | +note: type parameter defined here + --> $DIR/exotic-calls.rs:6:11 + | +LL | fn bar(t: impl Fn()) { + | ^^^^^^^^^ +help: remove the extra argument + | +LL | t(); + | ~~~ + +error[E0057]: this function takes 0 arguments but 1 argument was supplied + --> $DIR/exotic-calls.rs:16:5 + | +LL | baz()(1i32) + | ^^^^^ ---- argument of type `i32` unexpected + | +note: opaque type defined here + --> $DIR/exotic-calls.rs:11:13 + | +LL | fn baz() -> impl Fn() { + | ^^^^^^^^^ +help: remove the extra argument + | +LL | baz()() + | + +error[E0057]: this function takes 0 arguments but 1 argument was supplied + --> $DIR/exotic-calls.rs:22:5 + | +LL | x(1i32); + | ^ ---- argument of type `i32` unexpected + | +note: closure defined here + --> $DIR/exotic-calls.rs:21:13 + | +LL | let x = || {}; + | ^^ +help: remove the extra argument + | +LL | x(); + | ~~~ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0057`. diff --git a/src/test/ui/asm/aarch64/srcloc.rs b/src/test/ui/asm/aarch64/srcloc.rs index 040d4df546f..dbb6cbb9437 100644 --- a/src/test/ui/asm/aarch64/srcloc.rs +++ b/src/test/ui/asm/aarch64/srcloc.rs @@ -118,5 +118,12 @@ fn main() { //~^^^^^^^^^^ ERROR: unrecognized instruction mnemonic //~^^^^^^^ ERROR: unrecognized instruction mnemonic //~^^^^^^^^ ERROR: unrecognized instruction mnemonic + + asm!( + "", + "\n", + "invalid_instruction" + ); + //~^^ ERROR: unrecognized instruction mnemonic } } diff --git a/src/test/ui/asm/aarch64/srcloc.stderr b/src/test/ui/asm/aarch64/srcloc.stderr index f8b645c23f5..2e17b60b912 100644 --- a/src/test/ui/asm/aarch64/srcloc.stderr +++ b/src/test/ui/asm/aarch64/srcloc.stderr @@ -274,5 +274,17 @@ note: instantiated into assembly here LL | invalid_instruction4 | ^ -error: aborting due to 23 previous errors +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:125:14 + | +LL | "invalid_instruction" + | ^ + | +note: instantiated into assembly here + --> <inline asm>:4:1 + | +LL | invalid_instruction + | ^ + +error: aborting due to 24 previous errors diff --git a/src/test/ui/asm/type-check-1.stderr b/src/test/ui/asm/type-check-1.stderr index 5a997b47d73..162ff1d32bc 100644 --- a/src/test/ui/asm/type-check-1.stderr +++ b/src/test/ui/asm/type-check-1.stderr @@ -33,33 +33,6 @@ LL | asm!("{}", sym x); | = help: `sym` operands must refer to either a function or a static -error[E0308]: mismatched types - --> $DIR/type-check-1.rs:58:26 - | -LL | asm!("{}", const 0f32); - | ^^^^ expected integer, found `f32` - -error[E0308]: mismatched types - --> $DIR/type-check-1.rs:60:26 - | -LL | asm!("{}", const 0 as *mut u8); - | ^^^^^^^^^^^^ expected integer, found *-ptr - | - = note: expected type `{integer}` - found raw pointer `*mut u8` - -error[E0308]: mismatched types - --> $DIR/type-check-1.rs:62:26 - | -LL | asm!("{}", const &0); - | ^^ expected integer, found `&{integer}` - | -help: consider removing the borrow - | -LL - asm!("{}", const &0); -LL + asm!("{}", const 0); - | - error: invalid asm output --> $DIR/type-check-1.rs:15:29 | @@ -124,6 +97,33 @@ LL | asm!("{}", inout(reg) v[..]); = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly error[E0308]: mismatched types + --> $DIR/type-check-1.rs:58:26 + | +LL | asm!("{}", const 0f32); + | ^^^^ expected integer, found `f32` + +error[E0308]: mismatched types + --> $DIR/type-check-1.rs:60:26 + | +LL | asm!("{}", const 0 as *mut u8); + | ^^^^^^^^^^^^ expected integer, found *-ptr + | + = note: expected type `{integer}` + found raw pointer `*mut u8` + +error[E0308]: mismatched types + --> $DIR/type-check-1.rs:62:26 + | +LL | asm!("{}", const &0); + | ^^ expected integer, found `&{integer}` + | +help: consider removing the borrow + | +LL - asm!("{}", const &0); +LL + asm!("{}", const 0); + | + +error[E0308]: mismatched types --> $DIR/type-check-1.rs:76:25 | LL | global_asm!("{}", const 0f32); diff --git a/src/test/ui/asm/x86_64/srcloc.rs b/src/test/ui/asm/x86_64/srcloc.rs index 8a21d759772..1135ad2e1c6 100644 --- a/src/test/ui/asm/x86_64/srcloc.rs +++ b/src/test/ui/asm/x86_64/srcloc.rs @@ -120,5 +120,12 @@ fn main() { //~^^^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction2' //~^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction3' //~^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction4' + + asm!( + "", + "\n", + "invalid_instruction" + ); + //~^^ ERROR: invalid instruction mnemonic 'invalid_instruction' } } diff --git a/src/test/ui/asm/x86_64/srcloc.stderr b/src/test/ui/asm/x86_64/srcloc.stderr index b62c8948289..8899c1b916b 100644 --- a/src/test/ui/asm/x86_64/srcloc.stderr +++ b/src/test/ui/asm/x86_64/srcloc.stderr @@ -286,5 +286,17 @@ note: instantiated into assembly here LL | invalid_instruction4 | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 23 previous errors; 1 warning emitted +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:127:14 + | +LL | "invalid_instruction" + | ^ + | +note: instantiated into assembly here + --> <inline asm>:5:1 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 24 previous errors; 1 warning emitted diff --git a/src/test/ui/closures/binder/async-closure-with-binder.rs b/src/test/ui/closures/binder/async-closure-with-binder.rs new file mode 100644 index 00000000000..4fa599d37cb --- /dev/null +++ b/src/test/ui/closures/binder/async-closure-with-binder.rs @@ -0,0 +1,8 @@ +// edition:2021 +#![feature(closure_lifetime_binder)] +#![feature(async_closure)] +fn main() { + for<'a> async || (); + //~^ ERROR `for<...>` binders on `async` closures are not currently supported + //~^^ ERROR implicit types in closure signatures are forbidden when `for<...>` is present +} diff --git a/src/test/ui/closures/binder/async-closure-with-binder.stderr b/src/test/ui/closures/binder/async-closure-with-binder.stderr new file mode 100644 index 00000000000..1d4628b1a49 --- /dev/null +++ b/src/test/ui/closures/binder/async-closure-with-binder.stderr @@ -0,0 +1,16 @@ +error: `for<...>` binders on `async` closures are not currently supported + --> $DIR/async-closure-with-binder.rs:5:5 + | +LL | for<'a> async || (); + | ^^^^^^^ + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/async-closure-with-binder.rs:5:5 + | +LL | for<'a> async || (); + | -------^^^^^^^^^ + | | + | `for<...>` is here + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/closures/binder/implicit-return.rs b/src/test/ui/closures/binder/implicit-return.rs new file mode 100644 index 00000000000..d34e5721d91 --- /dev/null +++ b/src/test/ui/closures/binder/implicit-return.rs @@ -0,0 +1,6 @@ +#![feature(closure_lifetime_binder)] + +fn main() { + let _f = for<'a> |_: &'a ()| {}; + //~^ implicit types in closure signatures are forbidden when `for<...>` is present +} diff --git a/src/test/ui/closures/binder/implicit-return.stderr b/src/test/ui/closures/binder/implicit-return.stderr new file mode 100644 index 00000000000..5bfb9711334 --- /dev/null +++ b/src/test/ui/closures/binder/implicit-return.stderr @@ -0,0 +1,10 @@ +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-return.rs:4:34 + | +LL | let _f = for<'a> |_: &'a ()| {}; + | ------- ^ + | | + | `for<...>` is here + +error: aborting due to previous error + diff --git a/src/test/ui/closures/binder/implicit-stuff.rs b/src/test/ui/closures/binder/implicit-stuff.rs new file mode 100644 index 00000000000..09e4c747afe --- /dev/null +++ b/src/test/ui/closures/binder/implicit-stuff.rs @@ -0,0 +1,27 @@ +#![feature(closure_lifetime_binder)] + +fn main() { + // Implicit types + let _ = for<> || {}; //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _ = for<'a> || -> &'a _ { &() }; //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _ = for<'a> |x| -> &'a () { x }; //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _ = for<'a> |x: &'a _| -> &'a () { x }; //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _ = for<'a> |x: &'a Vec::<_>| -> &'a Vec::<()> { x }; //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _ = for<'a> |x: &'a Vec<()>| -> &'a Vec<_> { x }; //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _ = for<'a> |x: &'a _| -> &'a &'a () { x }; //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _ = for<'a> |x: &'a _, y, z: _| -> &'a _ { //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _: &u8 = x; + let _: u32 = y; + let _: i32 = z; + x + }; + + // Lifetime elision + let _ = for<> |_: &()| -> () {}; //~ ERROR `&` without an explicit lifetime name cannot be used here + let _ = for<> |x: &()| -> &() { x }; //~ ERROR `&` without an explicit lifetime name cannot be used here + //~| ERROR `&` without an explicit lifetime name cannot be used here + let _ = for<> |x: &'_ ()| -> &'_ () { x }; //~ ERROR `'_` cannot be used here + //~| ERROR `'_` cannot be used here + let _ = for<'a> |x: &()| -> &'a () { x }; //~ ERROR `&` without an explicit lifetime name cannot be used here + let _ = for<'a> |x: &'a ()| -> &() { x }; //~ ERROR `&` without an explicit lifetime name cannot be used here +} diff --git a/src/test/ui/closures/binder/implicit-stuff.stderr b/src/test/ui/closures/binder/implicit-stuff.stderr new file mode 100644 index 00000000000..779a08a44e5 --- /dev/null +++ b/src/test/ui/closures/binder/implicit-stuff.stderr @@ -0,0 +1,107 @@ +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/implicit-stuff.rs:20:23 + | +LL | let _ = for<> |_: &()| -> () {}; + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/implicit-stuff.rs:21:23 + | +LL | let _ = for<> |x: &()| -> &() { x }; + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/implicit-stuff.rs:21:31 + | +LL | let _ = for<> |x: &()| -> &() { x }; + | ^ explicit lifetime name needed here + +error[E0637]: `'_` cannot be used here + --> $DIR/implicit-stuff.rs:23:24 + | +LL | let _ = for<> |x: &'_ ()| -> &'_ () { x }; + | ^^ `'_` is a reserved lifetime name + +error[E0637]: `'_` cannot be used here + --> $DIR/implicit-stuff.rs:23:35 + | +LL | let _ = for<> |x: &'_ ()| -> &'_ () { x }; + | ^^ `'_` is a reserved lifetime name + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/implicit-stuff.rs:25:25 + | +LL | let _ = for<'a> |x: &()| -> &'a () { x }; + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/implicit-stuff.rs:26:36 + | +LL | let _ = for<'a> |x: &'a ()| -> &() { x }; + | ^ explicit lifetime name needed here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:5:22 + | +LL | let _ = for<> || {}; + | ----- ^ + | | + | `for<...>` is here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:6:31 + | +LL | let _ = for<'a> || -> &'a _ { &() }; + | ------- ^ + | | + | `for<...>` is here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:7:22 + | +LL | let _ = for<'a> |x| -> &'a () { x }; + | ------- ^ + | | + | `for<...>` is here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:8:29 + | +LL | let _ = for<'a> |x: &'a _| -> &'a () { x }; + | ------- ^ + | | + | `for<...>` is here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:9:35 + | +LL | let _ = for<'a> |x: &'a Vec::<_>| -> &'a Vec::<()> { x }; + | ------- ^ + | | + | `for<...>` is here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:10:49 + | +LL | let _ = for<'a> |x: &'a Vec<()>| -> &'a Vec<_> { x }; + | ------- `for<...>` is here ^ + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:11:29 + | +LL | let _ = for<'a> |x: &'a _| -> &'a &'a () { x }; + | ------- ^ + | | + | `for<...>` is here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:12:29 + | +LL | let _ = for<'a> |x: &'a _, y, z: _| -> &'a _ { + | ------- ^ ^ ^ ^ + | | + | `for<...>` is here + +error: aborting due to 15 previous errors + +For more information about this error, try `rustc --explain E0637`. diff --git a/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.rs b/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.rs new file mode 100644 index 00000000000..b476dd50cc9 --- /dev/null +++ b/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.rs @@ -0,0 +1,7 @@ +#![feature(closure_lifetime_binder)] +fn main() { + for<> |_: &'a ()| -> () {}; + //~^ ERROR use of undeclared lifetime name `'a` + for<'a> |_: &'b ()| -> () {}; + //~^ ERROR use of undeclared lifetime name `'b` +} diff --git a/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.stderr b/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.stderr new file mode 100644 index 00000000000..1381acc15ca --- /dev/null +++ b/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.stderr @@ -0,0 +1,33 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/suggestion-for-introducing-lifetime-into-binder.rs:3:16 + | +LL | for<> |_: &'a ()| -> () {}; + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | for<'a, > |_: &'a ()| -> () {}; + | +++ +help: consider introducing lifetime `'a` here + | +LL | fn main<'a>() { + | ++++ + +error[E0261]: use of undeclared lifetime name `'b` + --> $DIR/suggestion-for-introducing-lifetime-into-binder.rs:5:18 + | +LL | for<'a> |_: &'b ()| -> () {}; + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'b` here + | +LL | for<'b, 'a> |_: &'b ()| -> () {}; + | +++ +help: consider introducing lifetime `'b` here + | +LL | fn main<'b>() { + | ++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0261`. diff --git a/src/test/ui/const-generics/const-arg-in-const-arg.full.stderr b/src/test/ui/const-generics/const-arg-in-const-arg.full.stderr index dbbdb2a0ce3..8672e79b3e8 100644 --- a/src/test/ui/const-generics/const-arg-in-const-arg.full.stderr +++ b/src/test/ui/const-generics/const-arg-in-const-arg.full.stderr @@ -23,30 +23,6 @@ LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } | ^^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:30:23 - | -LL | let _ = [0; faz::<'a>(&())]; - | ^^ - | -note: the late bound lifetime parameter is introduced here - --> $DIR/const-arg-in-const-arg.rs:8:14 - | -LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } - | ^^ - -error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:33:23 - | -LL | let _ = [0; faz::<'b>(&())]; - | ^^ - | -note: the late bound lifetime parameter is introduced here - --> $DIR/const-arg-in-const-arg.rs:8:14 - | -LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } - | ^^ - -error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present --> $DIR/const-arg-in-const-arg.rs:41:24 | LL | let _: Foo<{ faz::<'a>(&()) }>; @@ -118,6 +94,30 @@ LL | let _ = [0; bar::<N>()]; | = help: try adding a `where` bound using this expression: `where [(); bar::<N>()]:` +error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/const-arg-in-const-arg.rs:30:23 + | +LL | let _ = [0; faz::<'a>(&())]; + | ^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/const-arg-in-const-arg.rs:8:14 + | +LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } + | ^^ + +error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/const-arg-in-const-arg.rs:33:23 + | +LL | let _ = [0; faz::<'b>(&())]; + | ^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/const-arg-in-const-arg.rs:8:14 + | +LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } + | ^^ + error: unconstrained generic constant --> $DIR/const-arg-in-const-arg.rs:47:19 | diff --git a/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr b/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr index 6ca9a2a4859..f1353aa9943 100644 --- a/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr +++ b/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr @@ -241,21 +241,21 @@ LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } | ^^ error[E0747]: unresolved item provided when a constant was expected - --> $DIR/const-arg-in-const-arg.rs:27:23 + --> $DIR/const-arg-in-const-arg.rs:38:24 | -LL | let _ = [0; bar::<N>()]; - | ^ +LL | let _: Foo<{ bar::<N>() }>; + | ^ | help: if this generic argument was intended as a const parameter, surround it with braces | -LL | let _ = [0; bar::<{ N }>()]; - | + + +LL | let _: Foo<{ bar::<{ N }>() }>; + | + + error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:30:23 + --> $DIR/const-arg-in-const-arg.rs:41:24 | -LL | let _ = [0; faz::<'a>(&())]; - | ^^ +LL | let _: Foo<{ faz::<'a>(&()) }>; + | ^^ | note: the late bound lifetime parameter is introduced here --> $DIR/const-arg-in-const-arg.rs:8:14 @@ -264,10 +264,10 @@ LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } | ^^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:33:23 + --> $DIR/const-arg-in-const-arg.rs:44:24 | -LL | let _ = [0; faz::<'b>(&())]; - | ^^ +LL | let _: Foo<{ faz::<'b>(&()) }>; + | ^^ | note: the late bound lifetime parameter is introduced here --> $DIR/const-arg-in-const-arg.rs:8:14 @@ -275,22 +275,30 @@ note: the late bound lifetime parameter is introduced here LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } | ^^ +error: constant expression depends on a generic parameter + --> $DIR/const-arg-in-const-arg.rs:25:17 + | +LL | let _ = [0; foo::<T>()]; + | ^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + error[E0747]: unresolved item provided when a constant was expected - --> $DIR/const-arg-in-const-arg.rs:38:24 + --> $DIR/const-arg-in-const-arg.rs:27:23 | -LL | let _: Foo<{ bar::<N>() }>; - | ^ +LL | let _ = [0; bar::<N>()]; + | ^ | help: if this generic argument was intended as a const parameter, surround it with braces | -LL | let _: Foo<{ bar::<{ N }>() }>; - | + + +LL | let _ = [0; bar::<{ N }>()]; + | + + error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:41:24 + --> $DIR/const-arg-in-const-arg.rs:30:23 | -LL | let _: Foo<{ faz::<'a>(&()) }>; - | ^^ +LL | let _ = [0; faz::<'a>(&())]; + | ^^ | note: the late bound lifetime parameter is introduced here --> $DIR/const-arg-in-const-arg.rs:8:14 @@ -299,10 +307,10 @@ LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } | ^^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:44:24 + --> $DIR/const-arg-in-const-arg.rs:33:23 | -LL | let _: Foo<{ faz::<'b>(&()) }>; - | ^^ +LL | let _ = [0; faz::<'b>(&())]; + | ^^ | note: the late bound lifetime parameter is introduced here --> $DIR/const-arg-in-const-arg.rs:8:14 @@ -310,14 +318,6 @@ note: the late bound lifetime parameter is introduced here LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } | ^^ -error: constant expression depends on a generic parameter - --> $DIR/const-arg-in-const-arg.rs:25:17 - | -LL | let _ = [0; foo::<T>()]; - | ^^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - error[E0747]: unresolved item provided when a constant was expected --> $DIR/const-arg-in-const-arg.rs:49:27 | diff --git a/src/test/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.rs b/src/test/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.rs index c6c196db6f2..79e9834b54e 100644 --- a/src/test/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.rs +++ b/src/test/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.rs @@ -16,7 +16,6 @@ fn use_dyn<const N: usize>(v: &dyn Foo<N>) where [u8; N + 1]: Sized { } fn main() { - // FIXME(generic_const_exprs): Improve the error message here. use_dyn(&()); //~^ ERROR type annotations needed } diff --git a/src/test/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.stderr b/src/test/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.stderr index ce75314ada7..59e9fee1eaf 100644 --- a/src/test/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.stderr +++ b/src/test/ui/const-generics/generic_const_exprs/object-safety-ok-infer-err.stderr @@ -1,14 +1,18 @@ -error[E0284]: type annotations needed: cannot satisfy `the constant `use_dyn::<{_: usize}>::{constant#0}` can be evaluated` - --> $DIR/object-safety-ok-infer-err.rs:20:5 +error[E0284]: type annotations needed + --> $DIR/object-safety-ok-infer-err.rs:19:5 | LL | use_dyn(&()); - | ^^^^^^^ cannot satisfy `the constant `use_dyn::<{_: usize}>::{constant#0}` can be evaluated` + | ^^^^^^^ cannot infer the value of the const parameter `N` declared on the function `use_dyn` | note: required by a bound in `use_dyn` --> $DIR/object-safety-ok-infer-err.rs:14:55 | LL | fn use_dyn<const N: usize>(v: &dyn Foo<N>) where [u8; N + 1]: Sized { | ^^^^^ required by this bound in `use_dyn` +help: consider specifying the generic argument + | +LL | use_dyn::<N>(&()); + | +++++ error: aborting due to previous error diff --git a/src/test/ui/const-generics/overlapping_impls.rs b/src/test/ui/const-generics/overlapping_impls.rs new file mode 100644 index 00000000000..e599eadd8cf --- /dev/null +++ b/src/test/ui/const-generics/overlapping_impls.rs @@ -0,0 +1,36 @@ +// check-pass +#![allow(incomplete_features)] +#![feature(adt_const_params)] +#![feature(generic_const_exprs)] +use std::marker::PhantomData; + +struct Foo<const I: i32, const J: i32> {} + +const ONE: i32 = 1; +const TWO: i32 = 2; + +impl<const I: i32> Foo<I, ONE> { + pub fn foo() {} +} + +impl<const I: i32> Foo<I, TWO> { + pub fn foo() {} +} + + +pub struct Foo2<const P: Protocol, T> { + _marker: PhantomData<T>, +} + +#[derive(PartialEq, Eq)] +pub enum Protocol { + Variant1, + Variant2, +} + +pub trait Bar {} + +impl<T> Bar for Foo2<{ Protocol::Variant1 }, T> {} +impl<T> Bar for Foo2<{ Protocol::Variant2 }, T> {} + +fn main() {} diff --git a/src/test/ui/const-generics/parent_generics_of_encoding_impl_trait.rs b/src/test/ui/const-generics/parent_generics_of_encoding_impl_trait.rs index ed81c01bb17..7a78e0f109c 100644 --- a/src/test/ui/const-generics/parent_generics_of_encoding_impl_trait.rs +++ b/src/test/ui/const-generics/parent_generics_of_encoding_impl_trait.rs @@ -7,5 +7,5 @@ extern crate generics_of_parent_impl_trait; fn main() { // check for `impl Trait<{ const }>` which has a parent of a `DefKind::TyParam` generics_of_parent_impl_trait::foo([()]); - //~^ error: type annotations needed: + //~^ error: type annotations needed } diff --git a/src/test/ui/const-generics/parent_generics_of_encoding_impl_trait.stderr b/src/test/ui/const-generics/parent_generics_of_encoding_impl_trait.stderr index 99085711513..87ff7babe71 100644 --- a/src/test/ui/const-generics/parent_generics_of_encoding_impl_trait.stderr +++ b/src/test/ui/const-generics/parent_generics_of_encoding_impl_trait.stderr @@ -1,8 +1,8 @@ -error[E0284]: type annotations needed: cannot satisfy `the constant `foo::{opaque#0}::{constant#0}` can be evaluated` +error[E0284]: type annotations needed --> $DIR/parent_generics_of_encoding_impl_trait.rs:9:5 | LL | generics_of_parent_impl_trait::foo([()]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `foo::{opaque#0}::{constant#0}` can be evaluated` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer the value of const parameter `N` declared on the function `foo` | note: required by a bound in `foo` --> $DIR/auxiliary/generics_of_parent_impl_trait.rs:6:48 diff --git a/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr b/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr index 4cd0fd2eaf7..34ec8aadbcf 100644 --- a/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr +++ b/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr @@ -1,16 +1,16 @@ error[E0133]: call to unsafe function is unsafe and requires unsafe function or block - --> $DIR/const-extern-fn-requires-unsafe.rs:9:17 + --> $DIR/const-extern-fn-requires-unsafe.rs:12:5 | -LL | let a: [u8; foo()]; - | ^^^^^ call to unsafe function +LL | foo(); + | ^^^^^ call to unsafe function | = note: consult the function's documentation for information on how to avoid undefined behavior error[E0133]: call to unsafe function is unsafe and requires unsafe function or block - --> $DIR/const-extern-fn-requires-unsafe.rs:12:5 + --> $DIR/const-extern-fn-requires-unsafe.rs:9:17 | -LL | foo(); - | ^^^^^ call to unsafe function +LL | let a: [u8; foo()]; + | ^^^^^ call to unsafe function | = note: consult the function's documentation for information on how to avoid undefined behavior diff --git a/src/test/ui/feature-gates/feature-gate-closure_lifetime_binder.rs b/src/test/ui/feature-gates/feature-gate-closure_lifetime_binder.rs new file mode 100644 index 00000000000..b0b494fa3ff --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-closure_lifetime_binder.rs @@ -0,0 +1,8 @@ +fn main() { + for<> || -> () {}; + //~^ ERROR `for<...>` binders for closures are experimental + for<'a> || -> () {}; + //~^ ERROR `for<...>` binders for closures are experimental + for<'a, 'b> |_: &'a ()| -> () {}; + //~^ ERROR `for<...>` binders for closures are experimental +} diff --git a/src/test/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr b/src/test/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr new file mode 100644 index 00000000000..aea5cfeed07 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-closure_lifetime_binder.stderr @@ -0,0 +1,33 @@ +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:2:5 + | +LL | for<> || -> () {}; + | ^^^^^ + | + = note: see issue #97362 <https://github.com/rust-lang/rust/issues/97362> for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:4:5 + | +LL | for<'a> || -> () {}; + | ^^^^^^^ + | + = note: see issue #97362 <https://github.com/rust-lang/rust/issues/97362> for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = help: consider removing `for<...>` + +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/feature-gate-closure_lifetime_binder.rs:6:5 + | +LL | for<'a, 'b> |_: &'a ()| -> () {}; + | ^^^^^^^^^^^ + | + = note: see issue #97362 <https://github.com/rust-lang/rust/issues/97362> for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = help: consider removing `for<...>` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/generic-associated-types/bugs/issue-87748.stderr b/src/test/ui/generic-associated-types/bugs/issue-87748.stderr index 60bb48efbc8..ac197dfe6ff 100644 --- a/src/test/ui/generic-associated-types/bugs/issue-87748.stderr +++ b/src/test/ui/generic-associated-types/bugs/issue-87748.stderr @@ -4,12 +4,12 @@ error[E0478]: lifetime bound not satisfied LL | fn do_sth(_: u32) {} | ^^^^^^^^^^^^^^^^^ | -note: lifetime parameter instantiated with the anonymous lifetime #2 defined here +note: lifetime parameter instantiated with the anonymous lifetime as defined here --> $DIR/issue-87748.rs:18:5 | LL | fn do_sth(_: u32) {} | ^^^^^^^^^^^^^^^^^ -note: but lifetime parameter must outlive the anonymous lifetime #1 defined here +note: but lifetime parameter must outlive the anonymous lifetime as defined here --> $DIR/issue-87748.rs:18:5 | LL | fn do_sth(_: u32) {} diff --git a/src/test/ui/generic-associated-types/issue-95305.rs b/src/test/ui/generic-associated-types/issue-95305.rs index 2365daada11..e2f1710fa28 100644 --- a/src/test/ui/generic-associated-types/issue-95305.rs +++ b/src/test/ui/generic-associated-types/issue-95305.rs @@ -3,7 +3,7 @@ // at some point in the future. #![feature(generic_associated_types)] - +#![feature(anonymous_lifetime_in_impl_trait)] trait Foo { type Item<'a>; } @@ -11,7 +11,10 @@ trait Foo { fn foo(x: &impl Foo<Item<'_> = u32>) { } //~^ ERROR `'_` cannot be used here [E0637] +// Ok: the anonymous lifetime is bound to the function. fn bar(x: &impl for<'a> Foo<Item<'a> = &'_ u32>) { } - //~^ ERROR missing lifetime specifier + +// Ok: the anonymous lifetime is bound to the function. +fn baz(x: &impl for<'a> Foo<Item<'a> = &u32>) { } fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-95305.stderr b/src/test/ui/generic-associated-types/issue-95305.stderr index 8624d880d4e..d8557525f54 100644 --- a/src/test/ui/generic-associated-types/issue-95305.stderr +++ b/src/test/ui/generic-associated-types/issue-95305.stderr @@ -4,18 +4,6 @@ error[E0637]: `'_` cannot be used here LL | fn foo(x: &impl Foo<Item<'_> = u32>) { } | ^^ `'_` is a reserved lifetime name -error[E0106]: missing lifetime specifier - --> $DIR/issue-95305.rs:14:41 - | -LL | fn bar(x: &impl for<'a> Foo<Item<'a> = &'_ u32>) { } - | ^^ expected named lifetime parameter - | -help: consider using the `'a` lifetime - | -LL | fn bar(x: &impl for<'a> Foo<Item<'a> = &'a u32>) { } - | ~~ - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0106, E0637. -For more information about an error, try `rustc --explain E0106`. +For more information about this error, try `rustc --explain E0637`. diff --git a/src/test/ui/intrinsics/const-eval-select-bad.rs b/src/test/ui/intrinsics/const-eval-select-bad.rs index 7d924e2b7f3..52f4e594f1a 100644 --- a/src/test/ui/intrinsics/const-eval-select-bad.rs +++ b/src/test/ui/intrinsics/const-eval-select-bad.rs @@ -1,4 +1,5 @@ #![feature(const_eval_select)] +#![feature(core_intrinsics)] use std::intrinsics::const_eval_select; diff --git a/src/test/ui/intrinsics/const-eval-select-bad.stderr b/src/test/ui/intrinsics/const-eval-select-bad.stderr index 1d3bff3a724..6103d6c6e3a 100644 --- a/src/test/ui/intrinsics/const-eval-select-bad.stderr +++ b/src/test/ui/intrinsics/const-eval-select-bad.stderr @@ -1,18 +1,18 @@ -error[E0277]: the trait bound `[closure@$DIR/const-eval-select-bad.rs:6:27: 6:29]: ~const FnOnce<()>` is not satisfied - --> $DIR/const-eval-select-bad.rs:6:27 +error[E0277]: the trait bound `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]: ~const FnOnce<()>` is not satisfied + --> $DIR/const-eval-select-bad.rs:7:27 | LL | const_eval_select((), || {}, || {}); - | ----------------- ^^^^^ expected an `FnOnce<()>` closure, found `[closure@$DIR/const-eval-select-bad.rs:6:27: 6:29]` + | ----------------- ^^^^^ expected an `FnOnce<()>` closure, found `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]` | | | required by a bound introduced by this call | - = help: the trait `~const FnOnce<()>` is not implemented for `[closure@$DIR/const-eval-select-bad.rs:6:27: 6:29]` -note: the trait `FnOnce<()>` is implemented for `[closure@$DIR/const-eval-select-bad.rs:6:27: 6:29]`, but that implementation is not `const` - --> $DIR/const-eval-select-bad.rs:6:27 + = help: the trait `~const FnOnce<()>` is not implemented for `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]` +note: the trait `FnOnce<()>` is implemented for `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]`, but that implementation is not `const` + --> $DIR/const-eval-select-bad.rs:7:27 | LL | const_eval_select((), || {}, || {}); | ^^^^^ - = note: wrap the `[closure@$DIR/const-eval-select-bad.rs:6:27: 6:29]` in a closure with no arguments: `|| { /* code */ }` + = note: wrap the `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics.rs:LL:COL | @@ -20,7 +20,7 @@ LL | F: ~const FnOnce<ARG, Output = RET>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` error[E0277]: the trait bound `{integer}: ~const FnOnce<()>` is not satisfied - --> $DIR/const-eval-select-bad.rs:8:27 + --> $DIR/const-eval-select-bad.rs:9:27 | LL | const_eval_select((), 42, 0xDEADBEEF); | ----------------- ^^ expected an `FnOnce<()>` closure, found `{integer}` @@ -36,7 +36,7 @@ LL | F: ~const FnOnce<ARG, Output = RET>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` error[E0277]: expected a `FnOnce<()>` closure, found `{integer}` - --> $DIR/const-eval-select-bad.rs:8:31 + --> $DIR/const-eval-select-bad.rs:9:31 | LL | const_eval_select((), 42, 0xDEADBEEF); | ----------------- ^^^^^^^^^^ expected an `FnOnce<()>` closure, found `{integer}` @@ -52,7 +52,7 @@ LL | G: FnOnce<ARG, Output = RET> + ~const Destruct, | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` error[E0271]: type mismatch resolving `<fn(i32) -> bool {bar} as FnOnce<(i32,)>>::Output == i32` - --> $DIR/const-eval-select-bad.rs:28:5 + --> $DIR/const-eval-select-bad.rs:29:5 | LL | const_eval_select((1,), foo, bar); | ^^^^^^^^^^^^^^^^^ expected `i32`, found `bool` @@ -64,7 +64,7 @@ LL | G: FnOnce<ARG, Output = RET> + ~const Destruct, | ^^^^^^^^^^^^ required by this bound in `const_eval_select` error[E0631]: type mismatch in function arguments - --> $DIR/const-eval-select-bad.rs:33:32 + --> $DIR/const-eval-select-bad.rs:34:32 | LL | const fn foo(n: i32) -> i32 { | --------------------------- found signature of `fn(i32) -> _` diff --git a/src/test/ui/intrinsics/const-eval-select-stability.rs b/src/test/ui/intrinsics/const-eval-select-stability.rs index db2462aee59..f9554decec1 100644 --- a/src/test/ui/intrinsics/const-eval-select-stability.rs +++ b/src/test/ui/intrinsics/const-eval-select-stability.rs @@ -1,5 +1,6 @@ #![feature(staged_api)] #![feature(const_eval_select)] +#![feature(core_intrinsics)] #![stable(since = "1.0", feature = "ui_test")] use std::intrinsics::const_eval_select; diff --git a/src/test/ui/intrinsics/const-eval-select-stability.stderr b/src/test/ui/intrinsics/const-eval-select-stability.stderr index 79641bbb46a..65b507b887b 100644 --- a/src/test/ui/intrinsics/const-eval-select-stability.stderr +++ b/src/test/ui/intrinsics/const-eval-select-stability.stderr @@ -1,5 +1,5 @@ error: `const_eval_select` is not yet stable as a const fn - --> $DIR/const-eval-select-stability.rs:16:5 + --> $DIR/const-eval-select-stability.rs:17:5 | LL | const_eval_select((), nothing, log); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/intrinsics/const-eval-select-x86_64.rs b/src/test/ui/intrinsics/const-eval-select-x86_64.rs index afec8e054bb..f3924acf0fa 100644 --- a/src/test/ui/intrinsics/const-eval-select-x86_64.rs +++ b/src/test/ui/intrinsics/const-eval-select-x86_64.rs @@ -2,6 +2,7 @@ // only-x86_64 #![feature(const_eval_select)] +#![feature(core_intrinsics)] use std::intrinsics::const_eval_select; use std::arch::x86_64::*; use std::mem::transmute; diff --git a/src/test/ui/intrinsics/const-eval-select.rs b/src/test/ui/intrinsics/const-eval-select.rs index 744db2f15b0..9ff20d3fbdd 100644 --- a/src/test/ui/intrinsics/const-eval-select.rs +++ b/src/test/ui/intrinsics/const-eval-select.rs @@ -1,6 +1,7 @@ // run-pass #![feature(const_eval_select)] +#![feature(core_intrinsics)] use std::intrinsics::const_eval_select; diff --git a/src/test/ui/issues/issue-16939.stderr b/src/test/ui/issues/issue-16939.stderr index 294524f0b61..aaa3c49b3d8 100644 --- a/src/test/ui/issues/issue-16939.stderr +++ b/src/test/ui/issues/issue-16939.stderr @@ -4,11 +4,11 @@ error[E0057]: this function takes 0 arguments but 1 argument was supplied LL | |t| f(t); | ^ - argument unexpected | -note: associated function defined here - --> $SRC_DIR/core/src/ops/function.rs:LL:COL +note: callable defined here + --> $DIR/issue-16939.rs:4:12 | -LL | extern "rust-call" fn call(&self, args: Args) -> Self::Output; - | ^^^^ +LL | fn _foo<F: Fn()> (f: F) { + | ^^^^ help: remove the extra argument | LL | |t| f(); diff --git a/src/test/ui/issues/issue-37884.stderr b/src/test/ui/issues/issue-37884.stderr index e83590a8f59..e9f50b41f6a 100644 --- a/src/test/ui/issues/issue-37884.stderr +++ b/src/test/ui/issues/issue-37884.stderr @@ -6,7 +6,7 @@ LL | fn next(&'a mut self) -> Option<Self::Item> | = note: expected fn pointer `fn(&mut RepeatMut<'a, T>) -> Option<_>` found fn pointer `fn(&'a mut RepeatMut<'a, T>) -> Option<_>` -note: the anonymous lifetime #1 defined here... +note: the anonymous lifetime as defined here... --> $DIR/issue-37884.rs:6:5 | LL | fn next(&'a mut self) -> Option<Self::Item> diff --git a/src/test/ui/issues/issue-47486.stderr b/src/test/ui/issues/issue-47486.stderr index ca57b2d7e01..b45f57b7b84 100644 --- a/src/test/ui/issues/issue-47486.stderr +++ b/src/test/ui/issues/issue-47486.stderr @@ -1,3 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/issue-47486.rs:2:10 + | +LL | () < std::mem::size_of::<_>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `usize` + error[E0282]: type annotations needed --> $DIR/issue-47486.rs:3:11 | @@ -9,12 +15,6 @@ help: consider specifying the generic argument LL | [0u8; std::mem::size_of::<_>()]; | ~~~~~ -error[E0308]: mismatched types - --> $DIR/issue-47486.rs:2:10 - | -LL | () < std::mem::size_of::<_>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `usize` - error: aborting due to 2 previous errors Some errors have detailed explanations: E0282, E0308. diff --git a/src/test/ui/lint/lint-stability.rs b/src/test/ui/lint/lint-stability.rs index 464b32c5f43..d0f0e9f8071 100644 --- a/src/test/ui/lint/lint-stability.rs +++ b/src/test/ui/lint/lint-stability.rs @@ -191,11 +191,11 @@ mod inheritance { stable_mod::unstable(); //~ ERROR use of unstable library feature stable_mod::stable(); - unstable_mod::deprecated(); + unstable_mod::deprecated(); //~ ERROR use of unstable library feature unstable_mod::unstable(); //~ ERROR use of unstable library feature let _ = Unstable::UnstableVariant; //~ ERROR use of unstable library feature - let _ = Unstable::StableVariant; + let _ = Unstable::StableVariant; //~ ERROR use of unstable library feature let x: usize = 0; x.stable(); diff --git a/src/test/ui/lint/lint-stability.stderr b/src/test/ui/lint/lint-stability.stderr index 167140ef92b..bd1a57dc4cc 100644 --- a/src/test/ui/lint/lint-stability.stderr +++ b/src/test/ui/lint/lint-stability.stderr @@ -295,6 +295,14 @@ LL | stable_mod::unstable(); = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/lint-stability.rs:194:9 + | +LL | unstable_mod::deprecated(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'unstable_test_feature' --> $DIR/lint-stability.rs:195:9 | LL | unstable_mod::unstable(); @@ -311,6 +319,14 @@ LL | let _ = Unstable::UnstableVariant; = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/lint-stability.rs:198:17 + | +LL | let _ = Unstable::StableVariant; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'unstable_test_feature' --> $DIR/lint-stability.rs:88:48 | LL | struct S1<T: TraitWithAssociatedTypes>(T::TypeUnstable); @@ -326,6 +342,6 @@ LL | TypeUnstable = u8, | = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable -error: aborting due to 41 previous errors +error: aborting due to 43 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/macros/issue-98466-allow.rs b/src/test/ui/macros/issue-98466-allow.rs new file mode 100644 index 00000000000..c260148c148 --- /dev/null +++ b/src/test/ui/macros/issue-98466-allow.rs @@ -0,0 +1,16 @@ +// check-pass +#![allow(named_arguments_used_positionally)] + +fn main() { + let mut _x: usize; + _x = 1; + println!("_x is {}", _x = 5); + println!("_x is {}", y = _x); + println!("first positional arg {}, second positional arg {}, _x is {}", 1, 2, y = _x); + + let mut _x: usize; + _x = 1; + let _f = format!("_x is {}", _x = 5); + let _f = format!("_x is {}", y = _x); + let _f = format!("first positional arg {}, second positional arg {}, _x is {}", 1, 2, y = _x); +} diff --git a/src/test/ui/macros/issue-98466.fixed b/src/test/ui/macros/issue-98466.fixed new file mode 100644 index 00000000000..e46e22f001f --- /dev/null +++ b/src/test/ui/macros/issue-98466.fixed @@ -0,0 +1,51 @@ +// check-pass +// run-rustfix + +fn main() { + let mut _x: usize; + _x = 1; + println!("_x is {_x}", _x = 5); + //~^ WARNING named argument `_x` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + println!("_x is {y}", y = _x); + //~^ WARNING named argument `y` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + println!("first positional arg {}, second positional arg {}, _x is {y}", 1, 2, y = _x); + //~^ WARNING named argument `y` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + + let mut _x: usize; + _x = 1; + let _f = format!("_x is {_x}", _x = 5); + //~^ WARNING named argument `_x` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + let _f = format!("_x is {y}", y = _x); + //~^ WARNING named argument `y` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + let _f = format!("first positional arg {}, second positional arg {}, _x is {y}", 1, 2, y = _x); + //~^ WARNING named argument `y` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + + let s = "0.009"; + // Confirm that named arguments used in formatting are correctly considered. + println!(".{:0<width$}", s, width = _x); + + let region = "abc"; + let width = 8; + let ls = "abcde"; + let full = "abcde"; + // Confirm that named arguments used in formatting are correctly considered. + println!( + "| {r:rw$?} | {ui:4?} | {v}", + r = region, + rw = width, + ui = ls, + v = full, + ); + + // Confirm that named arguments used in formatting are correctly considered. + println!("{:.a$}", "aaaaaaaaaaaaaaaaaa", a = 4); + + // Confirm that named arguments used in formatting are correctly considered. + println!("{:._a$}", "aaaaaaaaaaaaaaaaaa", _a = 4); +} diff --git a/src/test/ui/macros/issue-98466.rs b/src/test/ui/macros/issue-98466.rs new file mode 100644 index 00000000000..2c3b099afde --- /dev/null +++ b/src/test/ui/macros/issue-98466.rs @@ -0,0 +1,51 @@ +// check-pass +// run-rustfix + +fn main() { + let mut _x: usize; + _x = 1; + println!("_x is {}", _x = 5); + //~^ WARNING named argument `_x` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + println!("_x is {}", y = _x); + //~^ WARNING named argument `y` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + println!("first positional arg {}, second positional arg {}, _x is {}", 1, 2, y = _x); + //~^ WARNING named argument `y` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + + let mut _x: usize; + _x = 1; + let _f = format!("_x is {}", _x = 5); + //~^ WARNING named argument `_x` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + let _f = format!("_x is {}", y = _x); + //~^ WARNING named argument `y` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + let _f = format!("first positional arg {}, second positional arg {}, _x is {}", 1, 2, y = _x); + //~^ WARNING named argument `y` is not used by name [named_arguments_used_positionally] + //~| HELP use the named argument by name to avoid ambiguity + + let s = "0.009"; + // Confirm that named arguments used in formatting are correctly considered. + println!(".{:0<width$}", s, width = _x); + + let region = "abc"; + let width = 8; + let ls = "abcde"; + let full = "abcde"; + // Confirm that named arguments used in formatting are correctly considered. + println!( + "| {r:rw$?} | {ui:4?} | {v}", + r = region, + rw = width, + ui = ls, + v = full, + ); + + // Confirm that named arguments used in formatting are correctly considered. + println!("{:.a$}", "aaaaaaaaaaaaaaaaaa", a = 4); + + // Confirm that named arguments used in formatting are correctly considered. + println!("{:._a$}", "aaaaaaaaaaaaaaaaaa", _a = 4); +} diff --git a/src/test/ui/macros/issue-98466.stderr b/src/test/ui/macros/issue-98466.stderr new file mode 100644 index 00000000000..ad11d181b62 --- /dev/null +++ b/src/test/ui/macros/issue-98466.stderr @@ -0,0 +1,81 @@ +warning: named argument `_x` is not used by name + --> $DIR/issue-98466.rs:7:26 + | +LL | println!("_x is {}", _x = 5); + | -- ^^ this named argument is only referred to by position in formatting string + | | + | this formatting argument uses named argument `_x` by position + | + = note: `#[warn(named_arguments_used_positionally)]` on by default +help: use the named argument by name to avoid ambiguity + | +LL | println!("_x is {_x}", _x = 5); + | ~~~~ + +warning: named argument `y` is not used by name + --> $DIR/issue-98466.rs:10:26 + | +LL | println!("_x is {}", y = _x); + | -- ^ this named argument is only referred to by position in formatting string + | | + | this formatting argument uses named argument `y` by position + | +help: use the named argument by name to avoid ambiguity + | +LL | println!("_x is {y}", y = _x); + | ~~~ + +warning: named argument `y` is not used by name + --> $DIR/issue-98466.rs:13:83 + | +LL | println!("first positional arg {}, second positional arg {}, _x is {}", 1, 2, y = _x); + | -- ^ this named argument is only referred to by position in formatting string + | | + | this formatting argument uses named argument `y` by position + | +help: use the named argument by name to avoid ambiguity + | +LL | println!("first positional arg {}, second positional arg {}, _x is {y}", 1, 2, y = _x); + | ~~~ + +warning: named argument `_x` is not used by name + --> $DIR/issue-98466.rs:19:34 + | +LL | let _f = format!("_x is {}", _x = 5); + | -- ^^ this named argument is only referred to by position in formatting string + | | + | this formatting argument uses named argument `_x` by position + | +help: use the named argument by name to avoid ambiguity + | +LL | let _f = format!("_x is {_x}", _x = 5); + | ~~~~ + +warning: named argument `y` is not used by name + --> $DIR/issue-98466.rs:22:34 + | +LL | let _f = format!("_x is {}", y = _x); + | -- ^ this named argument is only referred to by position in formatting string + | | + | this formatting argument uses named argument `y` by position + | +help: use the named argument by name to avoid ambiguity + | +LL | let _f = format!("_x is {y}", y = _x); + | ~~~ + +warning: named argument `y` is not used by name + --> $DIR/issue-98466.rs:25:91 + | +LL | let _f = format!("first positional arg {}, second positional arg {}, _x is {}", 1, 2, y = _x); + | -- ^ this named argument is only referred to by position in formatting string + | | + | this formatting argument uses named argument `y` by position + | +help: use the named argument by name to avoid ambiguity + | +LL | let _f = format!("first positional arg {}, second positional arg {}, _x is {y}", 1, 2, y = _x); + | ~~~ + +warning: 6 warnings emitted + diff --git a/src/test/ui/mismatched_types/overloaded-calls-bad.stderr b/src/test/ui/mismatched_types/overloaded-calls-bad.stderr index cb93a7ad900..475ea9dfaf1 100644 --- a/src/test/ui/mismatched_types/overloaded-calls-bad.stderr +++ b/src/test/ui/mismatched_types/overloaded-calls-bad.stderr @@ -6,11 +6,11 @@ LL | let ans = s("what"); | | | arguments to this function are incorrect | -note: associated function defined here - --> $SRC_DIR/core/src/ops/function.rs:LL:COL +note: implementation defined here + --> $DIR/overloaded-calls-bad.rs:10:1 | -LL | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; - | ^^^^^^^^ +LL | impl FnMut<(isize,)> for S { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0057]: this function takes 1 argument but 0 arguments were supplied --> $DIR/overloaded-calls-bad.rs:29:15 @@ -18,11 +18,11 @@ error[E0057]: this function takes 1 argument but 0 arguments were supplied LL | let ans = s(); | ^-- an argument of type `isize` is missing | -note: associated function defined here - --> $SRC_DIR/core/src/ops/function.rs:LL:COL +note: implementation defined here + --> $DIR/overloaded-calls-bad.rs:10:1 | -LL | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; - | ^^^^^^^^ +LL | impl FnMut<(isize,)> for S { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: provide the argument | LL | let ans = s(/* isize */); @@ -36,11 +36,11 @@ LL | let ans = s("burma", "shave"); | | | expected `isize`, found `&str` | -note: associated function defined here - --> $SRC_DIR/core/src/ops/function.rs:LL:COL +note: implementation defined here + --> $DIR/overloaded-calls-bad.rs:10:1 | -LL | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; - | ^^^^^^^^ +LL | impl FnMut<(isize,)> for S { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the extra argument | LL | let ans = s(/* isize */); diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr index 5a84e3b81a6..3e37fcb2141 100644 --- a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr @@ -143,36 +143,36 @@ LL | fn duplicate_custom_4<T: A + Copy + Trait>(t: S<T>) -> (S<T>, S<T>) | ++++++++++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:83:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:75:9 | -LL | fn existing_colon_in_where<T>(t: T) - | - move occurs because `t` has type `T`, which does not implement the `Copy` trait -... +LL | fn existing_colon<T:>(t: T) { + | - move occurs because `t` has type `T`, which does not implement the `Copy` trait +LL | LL | [t, t]; | - ^ value used here after move | | | value moved here | -help: consider further restricting type parameter `T` +help: consider restricting type parameter `T` | -LL | T:, T: Copy - | ~~~~~~~~~ +LL | fn existing_colon<T: Copy>(t: T) { + | ++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:75:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:83:9 | -LL | fn existing_colon<T:>(t: T) { - | - move occurs because `t` has type `T`, which does not implement the `Copy` trait -LL | +LL | fn existing_colon_in_where<T>(t: T) + | - move occurs because `t` has type `T`, which does not implement the `Copy` trait +... LL | [t, t]; | - ^ value used here after move | | | value moved here | -help: consider restricting type parameter `T` +help: consider further restricting type parameter `T` | -LL | fn existing_colon<T: Copy>(t: T) { - | ++++ +LL | T:, T: Copy + | ~~~~~~~~~ error: aborting due to 11 previous errors diff --git a/src/test/ui/nll/ty-outlives/impl-trait-captures.stderr b/src/test/ui/nll/ty-outlives/impl-trait-captures.stderr index 42d9f057aaa..06256ebbc29 100644 --- a/src/test/ui/nll/ty-outlives/impl-trait-captures.stderr +++ b/src/test/ui/nll/ty-outlives/impl-trait-captures.stderr @@ -2,14 +2,14 @@ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appea --> $DIR/impl-trait-captures.rs:11:5 | LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> { - | -- hidden type `&ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrAnon(0)) T` captures the anonymous lifetime defined here + | -- hidden type `&ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[1afc]::foo::'_), '_)) T` captures the anonymous lifetime defined here LL | x | ^ | -help: to declare that the `impl Trait` captures `ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrAnon(0))`, you can add an explicit `ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrAnon(0))` lifetime bound +help: to declare that the `impl Trait` captures `ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[1afc]::foo::'_), '_))`, you can add an explicit `ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[1afc]::foo::'_), '_))` lifetime bound | -LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> + ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrAnon(0)) { - | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> + ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[1afc]::foo::'_), '_)) { + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/parser/recover-quantified-closure.rs b/src/test/ui/parser/recover-quantified-closure.rs index 381324738f6..10af39b7007 100644 --- a/src/test/ui/parser/recover-quantified-closure.rs +++ b/src/test/ui/parser/recover-quantified-closure.rs @@ -1,10 +1,12 @@ fn main() { for<'a> |x: &'a u8| *x + 1; - //~^ ERROR cannot introduce explicit parameters for a closure + //~^ ERROR `for<...>` binders for closures are experimental + //~^^ ERROR implicit types in closure signatures are forbidden when `for<...>` is present } enum Foo { Bar } fn foo(x: impl Iterator<Item = Foo>) { for <Foo>::Bar in x {} //~^ ERROR expected one of `move`, `static`, `|` + //~^^ ERROR `for<...>` binders for closures are experimental } diff --git a/src/test/ui/parser/recover-quantified-closure.stderr b/src/test/ui/parser/recover-quantified-closure.stderr index 0f011326516..39eec80f658 100644 --- a/src/test/ui/parser/recover-quantified-closure.stderr +++ b/src/test/ui/parser/recover-quantified-closure.stderr @@ -1,16 +1,37 @@ -error: cannot introduce explicit parameters for a closure +error: expected one of `move`, `static`, `|`, or `||`, found `::` + --> $DIR/recover-quantified-closure.rs:9:14 + | +LL | for <Foo>::Bar in x {} + | ^^ expected one of `move`, `static`, `|`, or `||` + +error[E0658]: `for<...>` binders for closures are experimental --> $DIR/recover-quantified-closure.rs:2:5 | LL | for<'a> |x: &'a u8| *x + 1; - | ^^^^^^^ ------------------ the parameters are attached to this closure - | | - | help: remove the parameters + | ^^^^^^^ + | + = note: see issue #97362 <https://github.com/rust-lang/rust/issues/97362> for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = help: consider removing `for<...>` -error: expected one of `move`, `static`, `|`, or `||`, found `::` - --> $DIR/recover-quantified-closure.rs:8:14 +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/recover-quantified-closure.rs:9:5 | LL | for <Foo>::Bar in x {} - | ^^ expected one of `move`, `static`, `|`, or `||` + | ^^^^^^^^^ + | + = note: see issue #97362 <https://github.com/rust-lang/rust/issues/97362> for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = help: consider removing `for<...>` + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/recover-quantified-closure.rs:2:25 + | +LL | for<'a> |x: &'a u8| *x + 1; + | ------- ^ + | | + | `for<...>` is here -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/proc-macro/attribute-with-error.stderr b/src/test/ui/proc-macro/attribute-with-error.stderr index 127c49957c1..7f3a7e670b9 100644 --- a/src/test/ui/proc-macro/attribute-with-error.stderr +++ b/src/test/ui/proc-macro/attribute-with-error.stderr @@ -1,12 +1,4 @@ error[E0308]: mismatched types - --> $DIR/attribute-with-error.rs:25:22 - | -LL | let a: i32 = "foo"; - | --- ^^^^^ expected `i32`, found `&str` - | | - | expected due to this - -error[E0308]: mismatched types --> $DIR/attribute-with-error.rs:10:18 | LL | let a: i32 = "foo"; @@ -23,6 +15,14 @@ LL | let b: i32 = "f'oo"; | expected due to this error[E0308]: mismatched types + --> $DIR/attribute-with-error.rs:25:22 + | +LL | let a: i32 = "foo"; + | --- ^^^^^ expected `i32`, found `&str` + | | + | expected due to this + +error[E0308]: mismatched types --> $DIR/attribute-with-error.rs:35:22 | LL | let a: i32 = "foo"; diff --git a/src/test/ui/repeat-expr/repeat_count.stderr b/src/test/ui/repeat-expr/repeat_count.stderr index 59bcd954a1f..e222c141f8b 100644 --- a/src/test/ui/repeat-expr/repeat_count.stderr +++ b/src/test/ui/repeat-expr/repeat_count.stderr @@ -31,6 +31,12 @@ LL | let e = [0; "foo"]; | ^^^^^ expected `usize`, found `&str` error[E0308]: mismatched types + --> $DIR/repeat_count.rs:31:17 + | +LL | let g = [0; G { g: () }]; + | ^^^^^^^^^^^ expected `usize`, found struct `G` + +error[E0308]: mismatched types --> $DIR/repeat_count.rs:19:17 | LL | let f = [0; -4_isize]; @@ -57,12 +63,6 @@ help: change the type of the numeric literal from `u8` to `usize` LL | let f = [0; 4usize]; | ~~~~~ -error[E0308]: mismatched types - --> $DIR/repeat_count.rs:31:17 - | -LL | let g = [0; G { g: () }]; - | ^^^^^^^^^^^ expected `usize`, found struct `G` - error: aborting due to 9 previous errors Some errors have detailed explanations: E0308, E0435. diff --git a/src/test/ui/stability-attribute/accidental-stable-in-unstable.rs b/src/test/ui/stability-attribute/accidental-stable-in-unstable.rs new file mode 100644 index 00000000000..f8bbe90cfc5 --- /dev/null +++ b/src/test/ui/stability-attribute/accidental-stable-in-unstable.rs @@ -0,0 +1,10 @@ +#![crate_type = "lib"] +extern crate core; + +// Known accidental stabilizations with no known users, slated for un-stabilization +// fully stable @ core::char::UNICODE_VERSION +use core::unicode::UNICODE_VERSION; //~ ERROR use of unstable library feature 'unicode_internals' + +// Known accidental stabilizations with known users +// fully stable @ core::mem::transmute +use core::intrinsics::transmute; // depended upon by rand_core diff --git a/src/test/ui/stability-attribute/accidental-stable-in-unstable.stderr b/src/test/ui/stability-attribute/accidental-stable-in-unstable.stderr new file mode 100644 index 00000000000..ff733822cab --- /dev/null +++ b/src/test/ui/stability-attribute/accidental-stable-in-unstable.stderr @@ -0,0 +1,11 @@ +error[E0658]: use of unstable library feature 'unicode_internals' + --> $DIR/accidental-stable-in-unstable.rs:6:5 + | +LL | use core::unicode::UNICODE_VERSION; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unicode_internals)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/stability-attribute/allowed-through-unstable.rs b/src/test/ui/stability-attribute/allowed-through-unstable.rs new file mode 100644 index 00000000000..ff0228e4da6 --- /dev/null +++ b/src/test/ui/stability-attribute/allowed-through-unstable.rs @@ -0,0 +1,9 @@ +// Test for new `#[rustc_allowed_through_unstable_modules]` attribute +// +// aux-build:allowed-through-unstable-core.rs +#![crate_type = "lib"] + +extern crate allowed_through_unstable_core; + +use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstable; +use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowedThroughUnstable; //~ ERROR use of unstable library feature 'unstable_test_feature' diff --git a/src/test/ui/stability-attribute/allowed-through-unstable.stderr b/src/test/ui/stability-attribute/allowed-through-unstable.stderr new file mode 100644 index 00000000000..132c00b89b2 --- /dev/null +++ b/src/test/ui/stability-attribute/allowed-through-unstable.stderr @@ -0,0 +1,12 @@ +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/allowed-through-unstable.rs:9:5 + | +LL | use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowedThroughUnstable; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #1 <https://github.com/rust-lang/rust/issues/1> for more information + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/stability-attribute/auxiliary/allowed-through-unstable-core.rs b/src/test/ui/stability-attribute/auxiliary/allowed-through-unstable-core.rs new file mode 100644 index 00000000000..b597009a309 --- /dev/null +++ b/src/test/ui/stability-attribute/auxiliary/allowed-through-unstable-core.rs @@ -0,0 +1,14 @@ +#![crate_type = "lib"] +#![feature(staged_api)] +#![feature(rustc_attrs)] +#![stable(feature = "stable_test_feature", since = "1.2.0")] + +#[unstable(feature = "unstable_test_feature", issue = "1")] +pub mod unstable_module { + #[stable(feature = "stable_test_feature", since = "1.2.0")] + #[rustc_allowed_through_unstable_modules] + pub trait OldStableTraitAllowedThoughUnstable {} + + #[stable(feature = "stable_test_feature", since = "1.2.0")] + pub trait NewStableTraitNotAllowedThroughUnstable {} +} diff --git a/src/test/ui/stability-attribute/auxiliary/stable-in-unstable-core.rs b/src/test/ui/stability-attribute/auxiliary/stable-in-unstable-core.rs new file mode 100644 index 00000000000..e45b00f994a --- /dev/null +++ b/src/test/ui/stability-attribute/auxiliary/stable-in-unstable-core.rs @@ -0,0 +1,8 @@ +#![feature(staged_api)] +#![stable(feature = "stable_test_feature", since = "1.2.0")] + +#[unstable(feature = "unstable_test_feature", issue = "1")] +pub mod new_unstable_module { + #[stable(feature = "stable_test_feature", since = "1.2.0")] + pub trait OldTrait {} +} diff --git a/src/test/ui/stability-attribute/auxiliary/stable-in-unstable-std.rs b/src/test/ui/stability-attribute/auxiliary/stable-in-unstable-std.rs new file mode 100644 index 00000000000..28ad8c28da1 --- /dev/null +++ b/src/test/ui/stability-attribute/auxiliary/stable-in-unstable-std.rs @@ -0,0 +1,11 @@ +#![feature(staged_api)] +#![feature(unstable_test_feature)] +#![stable(feature = "stable_test_feature", since = "1.2.0")] + +extern crate stable_in_unstable_core; + +#[stable(feature = "stable_test_feature", since = "1.2.0")] +pub mod old_stable_module { + #[stable(feature = "stable_test_feature", since = "1.2.0")] + pub use stable_in_unstable_core::new_unstable_module::OldTrait; +} diff --git a/src/test/ui/stability-attribute/stable-in-unstable.rs b/src/test/ui/stability-attribute/stable-in-unstable.rs new file mode 100644 index 00000000000..272a1a97234 --- /dev/null +++ b/src/test/ui/stability-attribute/stable-in-unstable.rs @@ -0,0 +1,46 @@ +// This test is meant to test that we can have a stable item in an unstable module, and that +// calling that item through the unstable module is unstable, but that re-exporting it from another +// crate in a stable module is fine. +// +// This is necessary to support moving items from `std` into `core` or `alloc` unstably while still +// exporting the original stable interface in `std`, such as moving `Error` into `core`. +// +// aux-build:stable-in-unstable-core.rs +// aux-build:stable-in-unstable-std.rs +#![crate_type = "lib"] + +extern crate stable_in_unstable_core; +extern crate stable_in_unstable_std; + +mod isolated1 { + use stable_in_unstable_core::new_unstable_module; //~ ERROR use of unstable library feature 'unstable_test_feature' + use stable_in_unstable_core::new_unstable_module::OldTrait; //~ ERROR use of unstable library feature 'unstable_test_feature' +} + +mod isolated2 { + use stable_in_unstable_std::old_stable_module::OldTrait; + + struct LocalType; + + impl OldTrait for LocalType {} +} + +mod isolated3 { + use stable_in_unstable_core::new_unstable_module::OldTrait; //~ ERROR use of unstable library feature 'unstable_test_feature' + + struct LocalType; + + impl OldTrait for LocalType {} +} + +mod isolated4 { + struct LocalType; + + impl stable_in_unstable_core::new_unstable_module::OldTrait for LocalType {} //~ ERROR use of unstable library feature 'unstable_test_feature' +} + +mod isolated5 { + struct LocalType; + + impl stable_in_unstable_std::old_stable_module::OldTrait for LocalType {} +} diff --git a/src/test/ui/stability-attribute/stable-in-unstable.stderr b/src/test/ui/stability-attribute/stable-in-unstable.stderr new file mode 100644 index 00000000000..e123d83584c --- /dev/null +++ b/src/test/ui/stability-attribute/stable-in-unstable.stderr @@ -0,0 +1,39 @@ +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/stable-in-unstable.rs:16:9 + | +LL | use stable_in_unstable_core::new_unstable_module; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #1 <https://github.com/rust-lang/rust/issues/1> for more information + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/stable-in-unstable.rs:17:9 + | +LL | use stable_in_unstable_core::new_unstable_module::OldTrait; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #1 <https://github.com/rust-lang/rust/issues/1> for more information + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/stable-in-unstable.rs:29:9 + | +LL | use stable_in_unstable_core::new_unstable_module::OldTrait; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #1 <https://github.com/rust-lang/rust/issues/1> for more information + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/stable-in-unstable.rs:39:10 + | +LL | impl stable_in_unstable_core::new_unstable_module::OldTrait for LocalType {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #1 <https://github.com/rust-lang/rust/issues/1> for more information + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/suggestions/impl-trait-missing-lifetime-gated.rs b/src/test/ui/suggestions/impl-trait-missing-lifetime-gated.rs new file mode 100644 index 00000000000..fe291e021bc --- /dev/null +++ b/src/test/ui/suggestions/impl-trait-missing-lifetime-gated.rs @@ -0,0 +1,21 @@ +// edition:2021 +// gate-test-anonymous_lifetime_in_impl_trait +// Verify the behaviour of `feature(anonymous_lifetime_in_impl_trait)`. + +fn f(_: impl Iterator<Item = &'_ ()>) {} +//~^ ERROR anonymous lifetimes in `impl Trait` are unstable + +fn g(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() } +//~^ ERROR anonymous lifetimes in `impl Trait` are unstable +//~| ERROR missing lifetime specifier + +// Anonymous lifetimes in async fn are already allowed. +// This is understood as `fn foo<'_1>(_: impl Iterator<Item = &'_1 ()>) {}`. +async fn h(_: impl Iterator<Item = &'_ ()>) {} + +// Anonymous lifetimes in async fn are already allowed. +// But that lifetime does not participate in resolution. +async fn i(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() } +//~^ ERROR missing lifetime specifier + +fn main() {} diff --git a/src/test/ui/suggestions/impl-trait-missing-lifetime-gated.stderr b/src/test/ui/suggestions/impl-trait-missing-lifetime-gated.stderr new file mode 100644 index 00000000000..9adc9679eee --- /dev/null +++ b/src/test/ui/suggestions/impl-trait-missing-lifetime-gated.stderr @@ -0,0 +1,44 @@ +error[E0658]: anonymous lifetimes in `impl Trait` are unstable + --> $DIR/impl-trait-missing-lifetime-gated.rs:5:31 + | +LL | fn f(_: impl Iterator<Item = &'_ ()>) {} + | ^^ + | + = help: add `#![feature(anonymous_lifetime_in_impl_trait)]` to the crate attributes to enable + +error[E0106]: missing lifetime specifier + --> $DIR/impl-trait-missing-lifetime-gated.rs:8:50 + | +LL | fn g(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() } + | ^^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments +help: consider using the `'static` lifetime + | +LL | fn g(x: impl Iterator<Item = &'_ ()>) -> Option<&'static ()> { x.next() } + | ~~~~~~~ + +error[E0658]: anonymous lifetimes in `impl Trait` are unstable + --> $DIR/impl-trait-missing-lifetime-gated.rs:8:31 + | +LL | fn g(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() } + | ^^ + | + = help: add `#![feature(anonymous_lifetime_in_impl_trait)]` to the crate attributes to enable + +error[E0106]: missing lifetime specifier + --> $DIR/impl-trait-missing-lifetime-gated.rs:18:56 + | +LL | async fn i(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() } + | ^^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments +help: consider using the `'static` lifetime + | +LL | async fn i(x: impl Iterator<Item = &'_ ()>) -> Option<&'static ()> { x.next() } + | ~~~~~~~ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0106, E0658. +For more information about an error, try `rustc --explain E0106`. diff --git a/src/test/ui/suggestions/impl-trait-missing-lifetime.rs b/src/test/ui/suggestions/impl-trait-missing-lifetime.rs index 22dc448c97f..dcc716f56b7 100644 --- a/src/test/ui/suggestions/impl-trait-missing-lifetime.rs +++ b/src/test/ui/suggestions/impl-trait-missing-lifetime.rs @@ -1,2 +1,19 @@ -fn f(_: impl Iterator<Item = &'_ ()>) {} //~ ERROR missing lifetime specifier +// edition:2021 + +#![feature(anonymous_lifetime_in_impl_trait)] + +// This is understood as `fn foo<'_1>(_: impl Iterator<Item = &'_1 ()>) {}`. +fn f(_: impl Iterator<Item = &'_ ()>) {} + +// But that lifetime does not participate in resolution. +fn g(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() } +//~^ ERROR missing lifetime specifier + +// This is understood as `fn foo<'_1>(_: impl Iterator<Item = &'_1 ()>) {}`. +async fn h(_: impl Iterator<Item = &'_ ()>) {} + +// But that lifetime does not participate in resolution. +async fn i(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() } +//~^ ERROR missing lifetime specifier + fn main() {} diff --git a/src/test/ui/suggestions/impl-trait-missing-lifetime.stderr b/src/test/ui/suggestions/impl-trait-missing-lifetime.stderr index a3a339b13c4..d3c64cb466d 100644 --- a/src/test/ui/suggestions/impl-trait-missing-lifetime.stderr +++ b/src/test/ui/suggestions/impl-trait-missing-lifetime.stderr @@ -1,14 +1,27 @@ error[E0106]: missing lifetime specifier - --> $DIR/impl-trait-missing-lifetime.rs:1:31 + --> $DIR/impl-trait-missing-lifetime.rs:9:50 | -LL | fn f(_: impl Iterator<Item = &'_ ()>) {} - | ^^ expected named lifetime parameter +LL | fn g(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() } + | ^^ expected named lifetime parameter | -help: consider introducing a named lifetime parameter + = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments +help: consider using the `'static` lifetime | -LL | fn f<'a>(_: impl Iterator<Item = &'a ()>) {} - | ++++ ~~ +LL | fn g(x: impl Iterator<Item = &'_ ()>) -> Option<&'static ()> { x.next() } + | ~~~~~~~ -error: aborting due to previous error +error[E0106]: missing lifetime specifier + --> $DIR/impl-trait-missing-lifetime.rs:16:56 + | +LL | async fn i(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() } + | ^^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments +help: consider using the `'static` lifetime + | +LL | async fn i(x: impl Iterator<Item = &'_ ()>) -> Option<&'static ()> { x.next() } + | ~~~~~~~ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr b/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr index 229c4b824f2..a763eb6f2f8 100644 --- a/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr +++ b/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr @@ -1,4 +1,23 @@ error[E0277]: `<impl Iterator as Iterator>::Item` doesn't implement `Debug` + --> $DIR/impl-trait-with-missing-bounds.rs:6:13 + | +LL | qux(constraint); + | --- ^^^^^^^^^^ `<impl Iterator as Iterator>::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | | + | required by a bound introduced by this call + | + = help: the trait `Debug` is not implemented for `<impl Iterator as Iterator>::Item` +note: required by a bound in `qux` + --> $DIR/impl-trait-with-missing-bounds.rs:50:16 + | +LL | fn qux(_: impl std::fmt::Debug) {} + | ^^^^^^^^^^^^^^^ required by this bound in `qux` +help: introduce a type parameter with a trait bound instead of using `impl Trait` + | +LL | fn foo<I: Iterator>(constraints: I) where <I as Iterator>::Item: Debug { + | +++++++++++++ ~ ++++++++++++++++++++++++++++++++++ + +error[E0277]: `<impl Iterator as Iterator>::Item` doesn't implement `Debug` --> $DIR/impl-trait-with-missing-bounds.rs:14:13 | LL | qux(constraint); @@ -75,25 +94,6 @@ LL | fn bak<I: Iterator + std::fmt::Debug>(constraints: I) where <I as Iterator> | +++++++++++++++++++++++++++++++ ~ ++++++++++++++++++++++++++++++++++ error[E0277]: `<impl Iterator as Iterator>::Item` doesn't implement `Debug` - --> $DIR/impl-trait-with-missing-bounds.rs:6:13 - | -LL | qux(constraint); - | --- ^^^^^^^^^^ `<impl Iterator as Iterator>::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug` - | | - | required by a bound introduced by this call - | - = help: the trait `Debug` is not implemented for `<impl Iterator as Iterator>::Item` -note: required by a bound in `qux` - --> $DIR/impl-trait-with-missing-bounds.rs:50:16 - | -LL | fn qux(_: impl std::fmt::Debug) {} - | ^^^^^^^^^^^^^^^ required by this bound in `qux` -help: introduce a type parameter with a trait bound instead of using `impl Trait` - | -LL | fn foo<I: Iterator>(constraints: I) where <I as Iterator>::Item: Debug { - | +++++++++++++ ~ ++++++++++++++++++++++++++++++++++ - -error[E0277]: `<impl Iterator as Iterator>::Item` doesn't implement `Debug` --> $DIR/impl-trait-with-missing-bounds.rs:45:13 | LL | qux(constraint); diff --git a/src/test/ui/suggestions/suggest-ref-macro.stderr b/src/test/ui/suggestions/suggest-ref-macro.stderr index b0ac770c06f..84cbc93571a 100644 --- a/src/test/ui/suggestions/suggest-ref-macro.stderr +++ b/src/test/ui/suggestions/suggest-ref-macro.stderr @@ -1,4 +1,26 @@ error[E0308]: mismatched types + --> $DIR/suggest-ref-macro.rs:8:1 + | +LL | #[hello] + | ^^^^^^^^ + | | + | expected `&mut i32`, found integer + | arguments to this function are incorrect + | +note: function defined here + --> $DIR/suggest-ref-macro.rs:8:1 + | +LL | #[hello] + | _-^^^^^^^ +LL | | fn abc() {} +LL | | +LL | | fn x(_: &mut i32) {} +LL | | +LL | | macro_rules! bla { + | |_____________- + = note: this error originates in the attribute macro `hello` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types --> $DIR/suggest-ref-macro.rs:15:11 | LL | x(123); @@ -36,28 +58,6 @@ note: function defined here LL | fn x(_: &mut i32) {} | ^ ----------- -error[E0308]: mismatched types - --> $DIR/suggest-ref-macro.rs:8:1 - | -LL | #[hello] - | ^^^^^^^^ - | | - | expected `&mut i32`, found integer - | arguments to this function are incorrect - | -note: function defined here - --> $DIR/suggest-ref-macro.rs:8:1 - | -LL | #[hello] - | _-^^^^^^^ -LL | | fn abc() {} -LL | | -LL | | fn x(_: &mut i32) {} -LL | | -LL | | macro_rules! bla { - | |_____________- - = note: this error originates in the attribute macro `hello` (in Nightly builds, run with -Z macro-backtrace for more info) - error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/union/union-derive-clone.mirunsafeck.stderr b/src/test/ui/union/union-derive-clone.mirunsafeck.stderr index a4d692f8497..148fb504670 100644 --- a/src/test/ui/union/union-derive-clone.mirunsafeck.stderr +++ b/src/test/ui/union/union-derive-clone.mirunsafeck.stderr @@ -1,3 +1,20 @@ +error[E0277]: the trait bound `U1: Copy` is not satisfied + --> $DIR/union-derive-clone.rs:6:10 + | +LL | #[derive(Clone)] + | ^^^^^ the trait `Copy` is not implemented for `U1` + | +note: required by a bound in `AssertParamIsCopy` + --> $SRC_DIR/core/src/clone.rs:LL:COL + | +LL | pub struct AssertParamIsCopy<T: Copy + ?Sized> { + | ^^^^ required by this bound in `AssertParamIsCopy` + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `U1` with `#[derive(Copy)]` + | +LL | #[derive(Copy)] + | + error[E0599]: the method `clone` exists for union `U5<CloneNoCopy>`, but its trait bounds were not satisfied --> $DIR/union-derive-clone.rs:38:15 | @@ -26,23 +43,6 @@ help: consider annotating `CloneNoCopy` with `#[derive(Clone, Copy)]` LL | #[derive(Clone, Copy)] | -error[E0277]: the trait bound `U1: Copy` is not satisfied - --> $DIR/union-derive-clone.rs:6:10 - | -LL | #[derive(Clone)] - | ^^^^^ the trait `Copy` is not implemented for `U1` - | -note: required by a bound in `AssertParamIsCopy` - --> $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | pub struct AssertParamIsCopy<T: Copy + ?Sized> { - | ^^^^ required by this bound in `AssertParamIsCopy` - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider annotating `U1` with `#[derive(Copy)]` - | -LL | #[derive(Copy)] - | - error: aborting due to 2 previous errors Some errors have detailed explanations: E0277, E0599. diff --git a/src/test/ui/union/union-derive-clone.thirunsafeck.stderr b/src/test/ui/union/union-derive-clone.thirunsafeck.stderr index a4d692f8497..148fb504670 100644 --- a/src/test/ui/union/union-derive-clone.thirunsafeck.stderr +++ b/src/test/ui/union/union-derive-clone.thirunsafeck.stderr @@ -1,3 +1,20 @@ +error[E0277]: the trait bound `U1: Copy` is not satisfied + --> $DIR/union-derive-clone.rs:6:10 + | +LL | #[derive(Clone)] + | ^^^^^ the trait `Copy` is not implemented for `U1` + | +note: required by a bound in `AssertParamIsCopy` + --> $SRC_DIR/core/src/clone.rs:LL:COL + | +LL | pub struct AssertParamIsCopy<T: Copy + ?Sized> { + | ^^^^ required by this bound in `AssertParamIsCopy` + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `U1` with `#[derive(Copy)]` + | +LL | #[derive(Copy)] + | + error[E0599]: the method `clone` exists for union `U5<CloneNoCopy>`, but its trait bounds were not satisfied --> $DIR/union-derive-clone.rs:38:15 | @@ -26,23 +43,6 @@ help: consider annotating `CloneNoCopy` with `#[derive(Clone, Copy)]` LL | #[derive(Clone, Copy)] | -error[E0277]: the trait bound `U1: Copy` is not satisfied - --> $DIR/union-derive-clone.rs:6:10 - | -LL | #[derive(Clone)] - | ^^^^^ the trait `Copy` is not implemented for `U1` - | -note: required by a bound in `AssertParamIsCopy` - --> $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | pub struct AssertParamIsCopy<T: Copy + ?Sized> { - | ^^^^ required by this bound in `AssertParamIsCopy` - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider annotating `U1` with `#[derive(Copy)]` - | -LL | #[derive(Copy)] - | - error: aborting due to 2 previous errors Some errors have detailed explanations: E0277, E0599. diff --git a/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr b/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr index f976466841c..94f6dc26624 100644 --- a/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr +++ b/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr @@ -21,11 +21,11 @@ note: because this has an unmet lifetime requirement | LL | pub struct Wrapper<T: Trait>(T); | ^^^^^ introduces a `'static` lifetime requirement -note: the anonymous lifetime #1 defined here... - --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:5 +note: the anonymous lifetime as defined here... + --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:29 | LL | pub fn repro(_: Wrapper<Ref>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^ note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl` --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:13:1 | diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs index 4b3a04f1255..ad206b5fb30 100644 --- a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs +++ b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs @@ -6,7 +6,7 @@ use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{BlockCheckMode, Expr, ExprKind}; +use rustc_hir::{BlockCheckMode, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -51,7 +51,7 @@ struct ExVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if let ExprKind::Closure { body, .. } = expr.kind { + if let ExprKind::Closure(&Closure { body, .. }) = expr.kind { // do not lint if the closure is called using an iterator (see #1141) if_chain! { if let Some(parent) = get_parent_expr(self.cx, expr); diff --git a/src/tools/clippy/clippy_lints/src/bytecount.rs b/src/tools/clippy/clippy_lints/src/bytecount.rs index 4e530256321..326ce34082a 100644 --- a/src/tools/clippy/clippy_lints/src/bytecount.rs +++ b/src/tools/clippy/clippy_lints/src/bytecount.rs @@ -5,7 +5,7 @@ use clippy_utils::visitors::is_local_used; use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind}; +use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, UintTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { if count.ident.name == sym::count; if let ExprKind::MethodCall(filter, [filter_recv, filter_arg], _) = count_recv.kind; if filter.ident.name == sym!(filter); - if let ExprKind::Closure { body, .. } = filter_arg.kind; + if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind; let body = cx.tcx.hir().body(body); if let [param] = body.params; if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind; diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 42fac550ec6..80c84014bfd 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -7,7 +7,7 @@ use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, ExprKind, Param, PatKind, Unsafety}; +use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; @@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { return; } let body = match expr.kind { - ExprKind::Closure { body, .. } => cx.tcx.hir().body(body), + ExprKind::Closure(&Closure { body, .. }) => cx.tcx.hir().body(body), _ => return, }; if body.value.span.from_expansion() { diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs index 78b5ec8ec1e..01c7eef4e04 100644 --- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs +++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{higher, match_def_path, path_def_id, paths}; -use rustc_hir::{BorrowKind, Expr, ExprKind}; +use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::{sym, Symbol}; @@ -159,7 +159,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { } } if method.ident.name == sym!(flat_map) && args.len() == 2 { - if let ExprKind::Closure { body, .. } = args[1].kind { + if let ExprKind::Closure(&Closure { body, .. }) = args[1].kind { let body = cx.tcx.hir().body(body); return is_infinite(cx, &body.value); } diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs index 39f68a8a1b4..94db1773fda 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{get_trait_def_id, paths, return_ty, trait_ref_of_method}; use if_chain::if_chain; -use rustc_hir::{ImplItem, ImplItemKind}; +use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { let decl = &signature.decl; if decl.implicit_self.has_implicit_self(); if decl.inputs.len() == 1; - if impl_item.generics.params.is_empty(); + if impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })); // Check if return type is String if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::String); diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 5c0bd57ac50..083c437a293 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -9,8 +9,8 @@ use rustc_hir::intravisit::{ use rustc_hir::FnRetTy::Return; use rustc_hir::{ BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, - ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin, - TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, + ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, LifetimeParamKind, ParamName, PolyTraitRef, + PredicateOrigin, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter as middle_nested_filter; @@ -338,7 +338,10 @@ fn could_use_elision<'tcx>( fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> { let mut allowed_lts = FxHashSet::default(); for par in named_generics.iter() { - if let GenericParamKind::Lifetime { .. } = par.kind { + if let GenericParamKind::Lifetime { + kind: LifetimeParamKind::Explicit, + } = par.kind + { allowed_lts.insert(RefLt::Named(par.name.ident().name)); } } @@ -379,6 +382,7 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { self.lts.push(RefLt::Static); } else if let LifetimeName::Param(_, ParamName::Fresh) = lt.name { // Fresh lifetimes generated should be ignored. + self.lts.push(RefLt::Unnamed); } else if lt.is_elided() { self.lts.push(RefLt::Unnamed); } else { diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index 0b6d9adb553..a7ef562b21f 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -9,7 +9,7 @@ use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath}; +use rustc_hir::{BinOpKind, BorrowKind, Closure, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath}; use rustc_lint::LateContext; use rustc_middle::middle::region; use rustc_middle::ty::{self, Ty}; @@ -369,7 +369,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { self.visit_expr(expr); } }, - ExprKind::Closure { body, .. } => { + ExprKind::Closure(&Closure { body, .. }) => { let body = self.cx.tcx.hir().body(body); self.visit_expr(&body.value); }, diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index a5715975066..b94bbd2bd41 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -8,7 +8,7 @@ use clippy_utils::{ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp}; +use rustc_hir::{Closure, def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_span::{symbol::sym, Symbol}; @@ -220,7 +220,7 @@ fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tc if let Some(e) = e { self.visit_expr(e); } - } else if let ExprKind::Closure { body: id, .. } = e.kind { + } else if let ExprKind::Closure(&Closure { body: id, .. }) = e.kind { if is_res_used(self.cx, self.iter_expr.path, id) { self.uses_iter = true; } @@ -260,7 +260,7 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: & if let Some(e) = e { self.visit_expr(e); } - } else if let ExprKind::Closure { body: id, .. } = e.kind { + } else if let ExprKind::Closure(&Closure { body: id, .. }) = e.kind { self.used_iter = is_res_used(self.cx, self.iter_expr.path, id); } else { walk_expr(self, e); @@ -307,7 +307,7 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: & if let Some(e) = e { self.visit_expr(e); } - } else if let ExprKind::Closure { body: id, .. } = e.kind { + } else if let ExprKind::Closure(&Closure { body: id, .. }) = e.kind { self.used_after = is_res_used(self.cx, self.iter_expr.path, id); } else { walk_expr(self, e); diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index d7d8a592152..93a34f452f6 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -6,7 +6,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, HirId, + AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, HirId, IsAsync, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind, }; use rustc_lint::{LateContext, LateLintPass}; @@ -177,7 +177,7 @@ fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) if let Some(block_expr) = block.expr; if let Some(args) = match_function_call(cx, block_expr, &FUTURE_FROM_GENERATOR); if args.len() == 1; - if let Expr{kind: ExprKind::Closure { body, .. }, ..} = args[0]; + if let Expr{kind: ExprKind::Closure(&Closure { body, .. }), ..} = args[0]; let closure_body = cx.tcx.hir().body(body); if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block)); then { diff --git a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs index 18cfd003767..9abf2507b92 100644 --- a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs +++ b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs @@ -5,7 +5,7 @@ use clippy_utils::{is_lang_ctor, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; -use rustc_hir::{Expr, ExprKind, PatKind}; +use rustc_hir::{Closure, Expr, ExprKind, PatKind}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; @@ -88,7 +88,7 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { } } if_chain! { - if let ExprKind::Closure { body, .. } = map_expr.kind; + if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind; let body = cx.tcx.hir().body(body); if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind; diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs index c35e1e021ef..42d2577cc31 100644 --- a/src/tools/clippy/clippy_lints/src/manual_retain.rs +++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs @@ -148,7 +148,7 @@ fn check_to_owned( fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, filter_expr: &hir::Expr<'_>) { if let hir::ExprKind::MethodCall(_, [_, closure], _) = filter_expr.kind - && let hir::ExprKind::Closure{ body, ..} = closure.kind + && let hir::ExprKind::Closure(&hir::Closure { body, ..}) = closure.kind && let filter_body = cx.tcx.hir().body(body) && let [filter_params] = filter_body.params && let Some(sugg) = match filter_params.pat.kind { diff --git a/src/tools/clippy/clippy_lints/src/map_clone.rs b/src/tools/clippy/clippy_lints/src/map_clone.rs index 3533de54a1e..95c312f1fe2 100644 --- a/src/tools/clippy/clippy_lints/src/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/map_clone.rs @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { if method.ident.name == sym::map; let ty = cx.typeck_results().expr_ty(&args[0]); if is_type_diagnostic_item(cx, ty, sym::Option) || is_trait_method(cx, e, sym::Iterator); - if let hir::ExprKind::Closure { body, .. } = args[1].kind; + if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = args[1].kind; then { let closure_body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(&closure_body.value); diff --git a/src/tools/clippy/clippy_lints/src/map_err_ignore.rs b/src/tools/clippy/clippy_lints/src/map_err_ignore.rs index 0c221441048..21d0e19eb0a 100644 --- a/src/tools/clippy/clippy_lints/src/map_err_ignore.rs +++ b/src/tools/clippy/clippy_lints/src/map_err_ignore.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind}; +use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -119,12 +119,12 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { if method.ident.as_str() == "map_err" && args.len() == 2 { // make sure the first argument is a closure, and grab the CaptureRef, BodyId, and fn_decl_span // fields - if let ExprKind::Closure { + if let ExprKind::Closure(&Closure { capture_clause, body, fn_decl_span, .. - } = args[1].kind + }) = args[1].kind { // check if this is by Reference (meaning there's no move statement) if capture_clause == CaptureBy::Ref { diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs index 663246b4c86..af9d948af00 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -169,7 +169,7 @@ fn unit_closure<'tcx>( expr: &hir::Expr<'_>, ) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> { if_chain! { - if let hir::ExprKind::Closure { fn_decl, body, .. } = expr.kind; + if let hir::ExprKind::Closure(&hir::Closure { fn_decl, body, .. }) = expr.kind; let body = cx.tcx.hir().body(body); let body_expr = &body.value; if fn_decl.inputs.len() == 1; diff --git a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs index d31b736982b..2f117e4dcc3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs @@ -150,7 +150,7 @@ pub(crate) trait BindInsteadOfMap { } match arg.kind { - hir::ExprKind::Closure { body, fn_decl_span, .. } => { + hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) => { let closure_body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(&closure_body.value); diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs index 58c3e52e138..7dbfd95c50d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -6,7 +6,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::{Expr, ExprKind, PatKind, PathSegment, QPath, UnOp}; +use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp}; use rustc_lint::LateContext; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, Symbol}; @@ -22,8 +22,8 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy hir::ExprKind::Path(QPath::Resolved(_, segments)) => { segments.segments.last().unwrap().ident.name == method_name }, - hir::ExprKind::Closure { body, .. } => { - let body = cx.tcx.hir().body(*body); + hir::ExprKind::Closure(&hir::Closure { body, .. }) => { + let body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(&body.value); let arg_id = body.params[0].pat.hir_id; match closure_expr.kind { @@ -106,7 +106,7 @@ pub(super) fn check<'tcx>( if is_trait_method(cx, map_recv, sym::Iterator); // filter(|x| ...is_some())... - if let ExprKind::Closure { body: filter_body_id, .. } = filter_arg.kind; + if let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind; let filter_body = cx.tcx.hir().body(filter_body_id); if let [filter_param] = filter_body.params; // optional ref pattern: `filter(|&x| ..)` @@ -129,7 +129,7 @@ pub(super) fn check<'tcx>( if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; // ...map(|x| ...unwrap()) - if let ExprKind::Closure { body: map_body_id, .. } = map_arg.kind; + if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind; let map_body = cx.tcx.hir().body(map_body_id); if let [map_param] = map_body.params; if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs index 912499bf96b..20cad0f181e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs @@ -51,7 +51,7 @@ pub(super) fn check<'tcx>( .map_or(false, |fun_def_id| { deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) }), - hir::ExprKind::Closure { body, .. } => { + hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let closure_body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(&closure_body.value); diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs index 2d71bd6f240..5a39b82b027 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs @@ -71,7 +71,7 @@ pub(super) fn check<'tcx>( if is_option { let self_snippet = snippet(cx, recv.span, ".."); if_chain! { - if let hir::ExprKind::Closure { body, fn_decl_span, .. } = map_arg.kind; + if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = map_arg.kind; let arg_snippet = snippet(cx, fn_decl_span, ".."); let body = cx.tcx.hir().body(body); if let Some((func, [arg_char])) = reduce_unit_expression(&body.value); diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs index b11f4531a91..7572ba3fe9a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs @@ -41,7 +41,7 @@ pub(super) fn check<'tcx>( let mut applicability = Applicability::MachineApplicable; let any_search_snippet = if_chain! { if search_method == "find"; - if let hir::ExprKind::Closure { body, .. } = search_arg.kind; + if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind; let closure_body = cx.tcx.hir().body(body); if let Some(closure_arg) = closure_body.params.get(0); then { diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index a405467f5e8..bafa6fc584d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -18,7 +18,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< return; } - if let hir::ExprKind::Closure { body, .. } = arg.kind { + if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind { let body = cx.tcx.hir().body(body); let arg_id = body.params[0].pat.hir_id; let mutates_arg = diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs index 913c4dbedc3..c3531d4d051 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs @@ -29,7 +29,7 @@ pub(super) fn check( ) { if_chain! { // Extract the body of the closure passed to fold - if let hir::ExprKind::Closure { body, .. } = acc.kind; + if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = acc.kind; let closure_body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(&closure_body.value); diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 865f6d0318e..21767d74c87 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>( let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); if is_option || is_result { - if let hir::ExprKind::Closure { body, .. } = arg.kind { + if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind { let body = cx.tcx.hir().body(body); let body_expr = &body.value; diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs index 48ac695f2ac..10e188ecb79 100644 --- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs +++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs @@ -1,7 +1,7 @@ use rustc_errors::Applicability; use rustc_hir::{ intravisit::{walk_expr, Visitor}, - Expr, ExprKind, Stmt, StmtKind, + Closure, Expr, ExprKind, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { if has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some(); // Skip the lint if the body is not block because this is simpler than `for` loop. // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. - if let ExprKind::Closure { body, .. } = for_each_arg.kind; + if let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind; let body = cx.tcx.hir().body(body); if let ExprKind::Block(..) = body.value.kind; then { diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs index 677ac998b56..d461668077e 100644 --- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs @@ -11,7 +11,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor}; use rustc_hir::{ - Arm, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment, + Arm, Closure, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; @@ -298,7 +298,7 @@ impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> { }, ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms), // since analysing the closure is not easy, just set all variables in it to side-effect - ExprKind::Closure { body, .. } => { + ExprKind::Closure(&Closure { body, .. }) => { let body = self.tcx.hir().body(body); self.visit_body(body); let vars = std::mem::take(&mut self.ret_vars); diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 25b73918c0a..8571607054a 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -495,12 +495,13 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio if let FnRetTy::Return(ty) = sig.decl.output && let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) { + let out_region = cx.tcx.named_region(out.hir_id); let args: Option<Vec<_>> = sig .decl .inputs .iter() .filter_map(get_rptr_lm) - .filter(|&(lt, _, _)| lt.name == out.name) + .filter(|&(lt, _, _)| cx.tcx.named_region(lt.hir_id) == out_region) .map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span)) .collect(); if let Some(args) = args diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index 65ed798867d..f5a93cebab8 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -69,7 +69,7 @@ impl EarlyLintPass for RedundantClosureCall { if_chain! { if let ast::ExprKind::Call(ref paren, _) = expr.kind; if let ast::ExprKind::Paren(ref closure) = paren.kind; - if let ast::ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind; + if let ast::ExprKind::Closure(_, _, _, _, ref decl, ref block, _) = closure.kind; then { let mut visitor = ReturnVisitor::new(); visitor.visit_expr(block); diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs index c4c1aa11004..fe885990595 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -582,7 +582,7 @@ fn ident_difference_expr_with_base_location( | (Await(_), Await(_)) | (Async(_, _, _), Async(_, _, _)) | (Block(_, _), Block(_, _)) - | (Closure(_, _, _, _, _, _), Closure(_, _, _, _, _, _)) + | (Closure(_, _, _, _, _, _, _), Closure(_, _, _, _, _, _, _)) | (Match(_, _), Match(_, _)) | (Loop(_, _), Loop(_, _)) | (ForLoop(_, _, _, _), ForLoop(_, _, _, _)) diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs index f35f44eda56..94945b2e1a9 100644 --- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs @@ -31,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m return false; } - let ltopt = if lt.is_elided() { + let ltopt = if lt.name.is_anonymous() { String::new() } else { format!("{} ", lt.name.ident().as_str()) diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs index f58da7ce9b4..b0fce91abeb 100644 --- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs +++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::{get_trait_def_id, paths}; use if_chain::if_chain; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, ExprKind, StmtKind}; +use rustc_hir::{Closure, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::{GenericPredicates, PredicateKind, ProjectionPredicate, TraitPredicate}; @@ -116,7 +116,7 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Span, Option<Span>)> { if_chain! { - if let ExprKind::Closure { body, fn_decl_span, .. } = arg.kind; + if let ExprKind::Closure(&Closure { body, fn_decl_span, .. }) = arg.kind; if let ty::Closure(_def_id, substs) = &cx.typeck_results().node_type(arg.hir_id).kind(); let ret_ty = substs.as_closure().sig().output(); let ty = cx.tcx.erase_late_bound_regions(ret_ty); diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs index 7d4373b2a57..ea5aadbbca1 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs @@ -3,7 +3,7 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; +use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, subst::GenericArgKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -155,7 +155,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> { if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind; if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; - if let [vec, Expr { kind: ExprKind::Closure{ body: closure_body_id, .. }, .. }] = args; + if let [vec, Expr { kind: ExprKind::Closure(Closure { body: closure_body_id, .. }), .. }] = args; if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::Vec); if let closure_body = cx.tcx.hir().body(*closure_body_id); if let &[ diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 2c8820eb7e1..bbb04c9945a 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -6,7 +6,7 @@ use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_ast::LitIntType; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::{ArrayLen, ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind}; +use rustc_hir::{ArrayLen, Closure, ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::{Ident, Symbol}; @@ -466,13 +466,13 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(scrutinee); self.slice(arms, |arm| self.arm(arm)); }, - ExprKind::Closure { + ExprKind::Closure(&Closure { capture_clause, fn_decl, body: body_id, movability, .. - } => { + }) => { let movability = OptionPat::new(movability.map(|m| format!("Movability::{m:?}"))); let ret_ty = match fn_decl.output { diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 177e754ee09..431b09d53c3 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -168,8 +168,8 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv), (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp), (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm), - (Closure(lc, la, lm, lf, lb, _), Closure(rc, ra, rm, rf, rb, _)) => { - lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(lb, rb) + (Closure(lb, lc, la, lm, lf, le, _), Closure(rb, rc, ra, rm, rf, re, _)) => { + eq_closure_binder(lb, rb) && lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(le, re) }, (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb), (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt), @@ -561,6 +561,15 @@ pub fn eq_fn_decl(l: &FnDecl, r: &FnDecl) -> bool { }) } +pub fn eq_closure_binder(l: &ClosureBinder, r: &ClosureBinder) -> bool { + match (l, r) { + (ClosureBinder::NotPresent, ClosureBinder::NotPresent) => true, + (ClosureBinder::For { generic_params: lp, .. }, ClosureBinder::For { generic_params: rp, .. }) => + lp.len() == rp.len() && std::iter::zip(lp.iter(), rp.iter()).all(|(l, r)| eq_generic_param(l, r)), + _ => false, + } +} + pub fn eq_fn_ret_ty(l: &FnRetTy, r: &FnRetTy) -> bool { match (l, r) { (FnRetTy::Default(_), FnRetTy::Default(_)) => true, diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 942f14ddd3d..1a8e8c99631 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHasher; use rustc_hir::def::Res; use rustc_hir::HirIdMap; use rustc_hir::{ - ArrayLen, BinOpKind, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId, + ArrayLen, BinOpKind, Closure, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, }; @@ -663,9 +663,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(e); self.hash_ty(ty); }, - ExprKind::Closure { + ExprKind::Closure(&Closure { capture_clause, body, .. - } => { + }) => { std::mem::discriminant(&capture_clause).hash(&mut self.s); // closures inherit TypeckResults self.hash_expr(&self.cx.tcx.hir().body(body).value); diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 1b32f0aaeb8..242d4315378 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -79,10 +79,10 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl, - HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, - Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, - UnOp, + def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination, Expr, + ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, + Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, + TraitRef, TyKind, UnOp, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::place::PlaceBase; @@ -1699,7 +1699,7 @@ pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'t _, &[ Expr { - kind: ExprKind::Closure { body, .. }, + kind: ExprKind::Closure(&Closure { body, .. }), .. }, ], @@ -1786,7 +1786,7 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool } match expr.kind { - ExprKind::Closure { body, .. } => is_body_identity_function(cx, cx.tcx.hir().body(body)), + ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)), _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)), } } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index f3283588c73..3bf75bcbee8 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -354,7 +354,7 @@ fn check_terminator<'a, 'tcx>( fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bool { tcx.is_const_fn(def_id) && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| { - if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level { + if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level { // Checking MSRV is manually necessary because `rustc` has no such concept. This entire // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`. // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index aa119539b1b..4326a103d44 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -8,7 +8,7 @@ use rustc_ast::{ast, token}; use rustc_ast_pretty::pprust::token_kind_to_string; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::{ExprKind, HirId, MutTy, TyKind}; +use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::hir::place::ProjectionKind; @@ -790,7 +790,7 @@ pub struct DerefClosure { /// /// note: this only works on single line immutable closures with exactly one input parameter. pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<'_>) -> Option<DerefClosure> { - if let hir::ExprKind::Closure { fn_decl, body, .. } = closure.kind { + if let hir::ExprKind::Closure(&Closure { fn_decl, body, .. }) = closure.kind { let closure_body = cx.tcx.hir().body(body); // is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`) // a type annotation is present if param `kind` is different from `TyKind::Infer` diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs index e688db1c39d..88a6bebb68c 100644 --- a/src/tools/rustfmt/src/closures.rs +++ b/src/tools/rustfmt/src/closures.rs @@ -11,6 +11,7 @@ use crate::overflow::OverflowableItem; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::Shape; use crate::source_map::SpanUtils; +use crate::types::rewrite_lifetime_param; use crate::utils::{last_line_width, left_most_sub_expr, stmt_expr, NodeIdExt}; // This module is pretty messy because of the rules around closures and blocks: @@ -24,6 +25,7 @@ use crate::utils::{last_line_width, left_most_sub_expr, stmt_expr, NodeIdExt}; // can change whether it is treated as an expression or statement. pub(crate) fn rewrite_closure( + binder: &ast::ClosureBinder, capture: ast::CaptureBy, is_async: &ast::Async, movability: ast::Movability, @@ -36,7 +38,7 @@ pub(crate) fn rewrite_closure( debug!("rewrite_closure {:?}", body); let (prefix, extra_offset) = rewrite_closure_fn_decl( - capture, is_async, movability, fn_decl, body, span, context, shape, + binder, capture, is_async, movability, fn_decl, body, span, context, shape, )?; // 1 = space between `|...|` and body. let body_shape = shape.offset_left(extra_offset)?; @@ -227,6 +229,7 @@ fn rewrite_closure_block( // Return type is (prefix, extra_offset) fn rewrite_closure_fn_decl( + binder: &ast::ClosureBinder, capture: ast::CaptureBy, asyncness: &ast::Async, movability: ast::Movability, @@ -236,6 +239,17 @@ fn rewrite_closure_fn_decl( context: &RewriteContext<'_>, shape: Shape, ) -> Option<(String, usize)> { + let binder = match binder { + ast::ClosureBinder::For { generic_params, .. } if generic_params.is_empty() => { + "for<> ".to_owned() + } + ast::ClosureBinder::For { generic_params, .. } => { + let lifetime_str = rewrite_lifetime_param(context, shape, generic_params)?; + format!("for<{lifetime_str}> ") + } + ast::ClosureBinder::NotPresent => "".to_owned(), + }; + let immovable = if movability == ast::Movability::Static { "static " } else { @@ -250,7 +264,7 @@ fn rewrite_closure_fn_decl( // 4 = "|| {".len(), which is overconservative when the closure consists of // a single expression. let nested_shape = shape - .shrink_left(immovable.len() + is_async.len() + mover.len())? + .shrink_left(binder.len() + immovable.len() + is_async.len() + mover.len())? .sub_width(4)?; // 1 = | @@ -288,7 +302,7 @@ fn rewrite_closure_fn_decl( .tactic(tactic) .preserve_newline(true); let list_str = write_list(&item_vec, &fmt)?; - let mut prefix = format!("{}{}{}|{}|", immovable, is_async, mover, list_str); + let mut prefix = format!("{}{}{}{}|{}|", binder, immovable, is_async, mover, list_str); if !ret_str.is_empty() { if prefix.contains('\n') { @@ -312,8 +326,15 @@ pub(crate) fn rewrite_last_closure( expr: &ast::Expr, shape: Shape, ) -> Option<String> { - if let ast::ExprKind::Closure(capture, ref is_async, movability, ref fn_decl, ref body, _) = - expr.kind + if let ast::ExprKind::Closure( + ref binder, + capture, + ref is_async, + movability, + ref fn_decl, + ref body, + _, + ) = expr.kind { let body = match body.kind { ast::ExprKind::Block(ref block, _) @@ -326,7 +347,7 @@ pub(crate) fn rewrite_last_closure( _ => body, }; let (prefix, extra_offset) = rewrite_closure_fn_decl( - capture, is_async, movability, fn_decl, body, expr.span, context, shape, + binder, capture, is_async, movability, fn_decl, body, expr.span, context, shape, )?; // If the closure goes multi line before its body, do not overflow the closure. if prefix.contains('\n') { diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index e4cc93026f1..a7b73ba78c5 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -203,11 +203,17 @@ pub(crate) fn format_expr( Some("yield".to_string()) } } - ast::ExprKind::Closure(capture, ref is_async, movability, ref fn_decl, ref body, _) => { - closures::rewrite_closure( - capture, is_async, movability, fn_decl, body, expr.span, context, shape, - ) - } + ast::ExprKind::Closure( + ref binder, + capture, + ref is_async, + movability, + ref fn_decl, + ref body, + _, + ) => closures::rewrite_closure( + binder, capture, is_async, movability, fn_decl, body, expr.span, context, shape, + ), ast::ExprKind::Try(..) | ast::ExprKind::Field(..) | ast::ExprKind::MethodCall(..) diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 64a201e45dd..2627886db10 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -1067,7 +1067,7 @@ pub(crate) fn can_be_overflowed_type( } /// Returns `None` if there is no `LifetimeDef` in the given generic parameters. -fn rewrite_lifetime_param( +pub(crate) fn rewrite_lifetime_param( context: &RewriteContext<'_>, shape: Shape, generic_params: &[ast::GenericParam], diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 4b26f4e40df..cd852855602 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -479,7 +479,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr | ast::ExprKind::Binary(_, _, ref expr) | ast::ExprKind::Index(_, ref expr) | ast::ExprKind::Unary(_, ref expr) - | ast::ExprKind::Closure(_, _, _, _, ref expr, _) + | ast::ExprKind::Closure(_, _, _, _, _, ref expr, _) | ast::ExprKind::Try(ref expr) | ast::ExprKind::Yield(Some(ref expr)) => is_block_expr(context, expr, repr), // This can only be a string lit diff --git a/src/tools/rustfmt/tests/source/closure.rs b/src/tools/rustfmt/tests/source/closure.rs index e93cc3fb40f..b2d28b305d0 100644 --- a/src/tools/rustfmt/tests/source/closure.rs +++ b/src/tools/rustfmt/tests/source/closure.rs @@ -51,6 +51,16 @@ fn main() { "--emit=dep-info" } else { a } }); + + for<> || -> () {}; + for< >|| -> () {}; + for< +> || -> () {}; + +for< 'a + ,'b, +'c > |_: &'a (), _: &'b (), _: &'c ()| -> () {}; + } fn issue311() { diff --git a/src/tools/rustfmt/tests/target/closure.rs b/src/tools/rustfmt/tests/target/closure.rs index f3107d19c2f..e8b4ff7a96b 100644 --- a/src/tools/rustfmt/tests/target/closure.rs +++ b/src/tools/rustfmt/tests/target/closure.rs @@ -71,6 +71,12 @@ fn main() { a } }); + + for<> || -> () {}; + for<> || -> () {}; + for<> || -> () {}; + + for<'a, 'b, 'c> |_: &'a (), _: &'b (), _: &'c ()| -> () {}; } fn issue311() { diff --git a/src/tools/tidy/src/error_codes_check.rs b/src/tools/tidy/src/error_codes_check.rs index e56ce3329cc..4ffa1fa8b28 100644 --- a/src/tools/tidy/src/error_codes_check.rs +++ b/src/tools/tidy/src/error_codes_check.rs @@ -11,7 +11,7 @@ use regex::Regex; // A few of those error codes can't be tested but all the others can and *should* be tested! const EXEMPTED_FROM_TEST: &[&str] = &[ "E0279", "E0313", "E0377", "E0461", "E0462", "E0465", "E0476", "E0490", "E0514", "E0519", - "E0523", "E0554", "E0640", "E0717", "E0729", + "E0523", "E0554", "E0640", "E0717", "E0729", "E0789", ]; // Some error codes don't have any tests apparently... |
