diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 395540764ea05..7630eedcd6481 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2439,6 +2439,12 @@ pub enum Const { No, } +impl Const { + pub fn is_const(self) -> bool { + matches!(self, Const::Yes { .. }) + } +} + /// Item defaultness. /// For details see the [RFC #2532](/~https://github.com/rust-lang/rfcs/pull/2532). #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] diff --git a/compiler/rustc_ast/src/format.rs b/compiler/rustc_ast/src/format.rs index 805596ff00a98..dbba33b2985e7 100644 --- a/compiler/rustc_ast/src/format.rs +++ b/compiler/rustc_ast/src/format.rs @@ -1,5 +1,7 @@ use crate::ptr::P; +use crate::Const; use crate::Expr; +use crate::NodeId; use rustc_data_structures::fx::FxHashMap; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -44,6 +46,20 @@ pub struct FormatArgs { pub span: Span, pub template: Vec, pub arguments: FormatArguments, + pub panic: FormatPanicKind, +} + +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum FormatPanicKind { + /// Regular `format_args!`. + Format, + + /// Format and panic. Used by `panic_args!`. + Panic { + /// The id of the generated cold path function. + id: NodeId, + constness: Const, + }, } /// A piece of a format template string. diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index e3504a5638e47..2062a03a31f34 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -109,6 +109,10 @@ pub trait MutVisitor: Sized { noop_visit_item_kind(i, self); } + fn visit_assoc_item_kind(&mut self, i: &mut AssocItemKind) { + noop_visit_assoc_item_kind(i, self); + } + fn flat_map_trait_item(&mut self, i: P) -> SmallVec<[P; 1]> { noop_flat_map_assoc_item(i, self) } @@ -1120,6 +1124,13 @@ pub fn noop_flat_map_assoc_item( visitor.visit_ident(ident); visitor.visit_vis(vis); visit_attrs(attrs, visitor); + visitor.visit_assoc_item_kind(kind); + visitor.visit_span(span); + visit_lazy_tts(tokens, visitor); + smallvec![item] +} + +pub fn noop_visit_assoc_item_kind(kind: &mut AssocItemKind, visitor: &mut T) { match kind { AssocItemKind::Const(item) => { visit_const_item(item, visitor); @@ -1147,9 +1158,6 @@ pub fn noop_flat_map_assoc_item( } AssocItemKind::MacCall(mac) => visitor.visit_mac_call(mac), } - visitor.visit_span(span); - visit_lazy_tts(tokens, visitor); - smallvec![item] } fn visit_const_item( @@ -1313,6 +1321,13 @@ pub fn noop_visit_inline_asm_sym( } pub fn noop_visit_format_args(fmt: &mut FormatArgs, vis: &mut T) { + match &mut fmt.panic { + FormatPanicKind::Format => (), + FormatPanicKind::Panic { id, constness } => { + vis.visit_id(id); + visit_constness(constness, vis); + } + } for arg in fmt.arguments.all_args_mut() { if let FormatArgumentKind::Named(name) = &mut arg.kind { vis.visit_ident(name); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 7408b4fb0af48..4a8024b590468 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1790,7 +1790,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc(self.expr_call_mut(span, e, args)) } - fn expr_call_lang_item_fn_mut( + pub(super) fn expr_call_lang_item_fn_mut( &mut self, span: Span, lang_item: hir::LangItem, @@ -1811,7 +1811,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc(self.expr_call_lang_item_fn_mut(span, lang_item, args, hir_id)) } - fn expr_lang_item_path( + pub(super) fn expr_lang_item_path( &mut self, span: Span, lang_item: hir::LangItem, diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index afcf8b15cd800..f7f96fc09df3f 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -1,30 +1,341 @@ use super::LoweringContext; +use hir::def::{DefKind, Res}; +use hir::definitions::DefPathData; use rustc_ast as ast; use rustc_ast::visit::{self, Visitor}; use rustc_ast::*; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; +use rustc_session::Session; use rustc_span::{ + def_id::LocalDefId, sym, symbol::{kw, Ident}, Span, Symbol, }; +use rustc_target::spec::abi::Abi; use std::borrow::Cow; impl<'hir> LoweringContext<'_, 'hir> { pub(crate) fn lower_format_args(&mut self, sp: Span, fmt: &FormatArgs) -> hir::ExprKind<'hir> { - // Never call the const constructor of `fmt::Arguments` if the - // format_args!() had any arguments _before_ flattening/inlining. - let allow_const = fmt.arguments.all_args().is_empty(); - let mut fmt = Cow::Borrowed(fmt); - if self.tcx.sess.opts.unstable_opts.flatten_format_args { - fmt = flatten_format_args(fmt); - fmt = inline_literals(fmt); + // Optimize the format arguments + let (allow_const, fmt) = process_args(self.tcx.sess, fmt); + + // Generate access of the `index` argument. + let arg_access = |this: &mut LoweringContext<'_, 'hir>, index: usize, span| { + let arg = this.lower_expr(&fmt.arguments.all_args()[index].expr); + this.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg)) + }; + + match fmt.panic { + ast::FormatPanicKind::Format => { + expand_format_args(self, sp, &fmt, allow_const, arg_access) + } + ast::FormatPanicKind::Panic { id, constness: _ } => { + self.lower_panic_args(id, sp, &fmt, arg_access) + } + } + } + + fn lower_panic_args( + &mut self, + cold_path: NodeId, + span: Span, + fmt: &FormatArgs, + mut arg_access: impl FnMut(&mut LoweringContext<'_, 'hir>, usize, Span) -> hir::Expr<'hir>, + ) -> hir::ExprKind<'hir> { + // Call the cold path function passing on the arguments required for formatting. + let span = self.lower_span(span); + let arena = self.arena; + + let args = &*arena.alloc_from_iter( + fmt.arguments + .all_args() + .iter() + .enumerate() + .map(|(i, arg)| arg_access(self, i, arg.expr.span.with_ctxt(span.ctxt()))), + ); + + let item = self.local_def_id(cold_path); + let res = Res::Def(DefKind::Fn, item.to_def_id()); + let path_hir_id = self.next_id(); + let path = hir::ExprKind::Path(hir::QPath::Resolved( + None, + arena.alloc(hir::Path { + span, + res, + segments: arena_vec![self; hir::PathSegment::new( + Ident::new(sym::panic_cold, span), path_hir_id, res + )], + }), + )); + let path = self.expr(span, path); + let call = arena.alloc(self.expr(span, hir::ExprKind::Call(arena.alloc(path), args))); + let item = self.stmt( + span, + hir::StmtKind::Item(hir::ItemId { owner_id: hir::OwnerId { def_id: item } }), + ); + let stmts = arena_vec![self; item]; + hir::ExprKind::Block(self.block_all(span, stmts, Some(call)), None) + } + + fn generic_ty(&mut self, span: Span, param: &hir::GenericParam<'_>) -> &'hir hir::Ty<'hir> { + let arena = self.arena; + let path_hir_id = self.next_id(); + let res = Res::Def(DefKind::TyParam, param.def_id.to_def_id()); + let path = hir::QPath::Resolved( + None, + arena.alloc(hir::Path { + span, + res, + segments: arena_vec![self; hir::PathSegment::new(param.name.ident(), path_hir_id, res)], + }), + ); + arena.alloc(self.ty(span, hir::TyKind::Path(path))) + } + + fn create_generic_param( + &mut self, + span: Span, + name: Symbol, + data: DefPathData, + kind: hir::GenericParamKind<'hir>, + ) -> hir::GenericParam<'hir> { + let node_id = self.next_node_id(); + let def_id = self.create_def(self.current_hir_id_owner.def_id, node_id, data, span); + let hir_id = self.lower_node_id(node_id); + hir::GenericParam { + def_id, + hir_id, + name: hir::ParamName::Plain(Ident { name, span }), + span, + kind, + colon_span: None, + pure_wrt_drop: false, + source: hir::GenericParamSource::Generics, + } + } + + fn generic_bounds( + &mut self, + span: Span, + traits: &[FormatTrait], + ) -> &'hir [hir::GenericBound<'hir>] { + // Maps from the format trait to the lang item + let map_trait = |t| match t { + FormatTrait::Display => hir::LangItem::FormatDisplay, + FormatTrait::Debug => hir::LangItem::FormatDebug, + FormatTrait::LowerExp => hir::LangItem::FormatLowerExp, + FormatTrait::UpperExp => hir::LangItem::FormatUpperExp, + FormatTrait::Octal => hir::LangItem::FormatOctal, + FormatTrait::Pointer => hir::LangItem::FormatPointer, + FormatTrait::Binary => hir::LangItem::FormatBinary, + FormatTrait::LowerHex => hir::LangItem::FormatLowerHex, + FormatTrait::UpperHex => hir::LangItem::FormatUpperHex, + }; + + self.arena.alloc_from_iter(traits.iter().map(|t| { + hir::GenericBound::LangItemTrait( + map_trait(*t), + span, + self.next_id(), + self.arena.alloc(hir::GenericArgs::none()), + ) + })) + } + + /// Lowers the cold path function for panic_args! + pub(crate) fn lower_panic_args_cold( + &mut self, + fmt: &FormatArgs, + span: Span, + ) -> &'hir hir::Item<'hir> { + let FormatPanicKind::Panic { id, constness } = fmt.panic else { panic!() }; + + let (allow_const, fmt) = process_args(self.tcx.sess, fmt); + + let arena = self.arena; + + let span = self.lower_span(span); + let hir_id = self.lower_node_id(id); + + let arg_count = fmt.arguments.all_args().len(); + + // Create the generic parameters `A0`, `A1`, .., `Ai`. + let mut generics: Vec<_> = (0..arg_count) + .map(|i| { + let name = Symbol::intern(&format!("A{i}")); + self.create_generic_param( + span, + name, + DefPathData::TypeNs(name), + hir::GenericParamKind::Type { default: None, synthetic: false }, + ) + }) + .collect(); + + // Create the lifetime 'a used in the parameter types. + let lifetime_name = Symbol::intern("'a"); + let lifetime: LocalDefId = { + let param = self.create_generic_param( + span, + lifetime_name, + DefPathData::LifetimeNs(lifetime_name), + hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }, + ); + let def_id = param.def_id; + generics.insert(0, param); + def_id + }; + + let body_id = self.lower_body(|this| { + // Create parameter bindings `a0`, `a1`, .., `ai`. + let bindings: Vec<(hir::HirId, Ident)> = (0..arg_count) + .map(|i| (this.next_id(), Ident::new(Symbol::intern(&format!("a{i}")), span))) + .collect(); + let params = arena.alloc_from_iter((0..arg_count).map(|i| { + let pat = arena.alloc(hir::Pat { + hir_id: bindings[i].0, + kind: hir::PatKind::Binding( + BindingAnnotation::NONE, + bindings[i].0, + bindings[i].1, + None, + ), + span, + default_binding_modes: true, + }); + let hir_id = this.next_id(); + hir::Param { hir_id, pat, ty_span: span, span } + })); + + // Generate access of the `i` format argument via parameter binding `ai`. + let arg_access = |this: &mut LoweringContext<'_, 'hir>, i: usize, span| { + this.expr_ident_mut(span, bindings[i].1, bindings[i].0) + }; + + // Generate formatting code. + let args = expand_format_args(this, span, &fmt, allow_const, arg_access); + let args = this.expr(span, args); + + // Call `panic_fmt` with the generated `Arguments`. + let panic = arena.alloc(this.expr_call_lang_item_fn_mut( + span, + hir::LangItem::PanicFmt, + arena_vec![this; args], + None, + )); + + let block = arena.alloc(hir::Block { + stmts: &[], + expr: Some(panic), + hir_id: this.next_id(), + rules: hir::BlockCheckMode::DefaultBlock, + span, + targeted_by_break: false, + }); + (params, this.expr_block(block)) + }); + + // Compute trait bounds we need to apply to each format argument. + let mut arg_traits: Vec> = (0..arg_count).map(|_| Vec::new()).collect(); + for piece in &fmt.template { + let FormatArgsPiece::Placeholder(placeholder) = piece else { continue }; + if let Ok(index) = placeholder.argument.index { + if !arg_traits[index].iter().any(|t| *t == placeholder.format_trait) { + arg_traits[index].push(placeholder.format_trait); + } + } } - expand_format_args(self, sp, &fmt, allow_const) + + // Create where bound required for format arguments, like, A0: Display + Debug. + let predicates = + arena.alloc_from_iter(arg_traits.into_iter().enumerate().filter_map(|(i, traits)| { + (!traits.is_empty()).then(|| { + hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { + hir_id: self.next_id(), + span, + origin: hir::PredicateOrigin::GenericParam, + bound_generic_params: &[], + bounded_ty: self.generic_ty(span, &generics[1 + i]), + bounds: self.generic_bounds(span, &traits), + }) + }) + })); + + // Create input parameter types &'a A0, &'a A1, .., &'a Ai + let inputs = arena.alloc_from_iter((0..arg_count).map(|i| { + let ty = self.generic_ty(span, &generics[1 + i]); + let hir_id = self.next_id(); + let lifetime = arena.alloc(hir::Lifetime { + hir_id, + ident: Ident::new(lifetime_name, span), + res: hir::LifetimeName::Param(lifetime), + }); + self.ty(span, hir::TyKind::Ref(lifetime, hir::MutTy { ty, mutbl: Mutability::Not })) + })); + + let decl = arena.alloc(hir::FnDecl { + inputs, + // Return type ! + output: hir::FnRetTy::Return(self.arena.alloc(hir::Ty { + kind: hir::TyKind::Never, + span, + hir_id: self.next_id(), + })), + c_variadic: false, + lifetime_elision_allowed: false, + implicit_self: hir::ImplicitSelfKind::None, + }); + let sig = hir::FnSig { + decl, + header: hir::FnHeader { + unsafety: hir::Unsafety::Normal, + asyncness: hir::IsAsync::NotAsync, + constness: self.lower_constness(constness), + abi: Abi::Rust, + }, + span, + }; + let generics = arena.alloc(hir::Generics { + params: arena.alloc_from_iter(generics), + predicates, + has_where_clause_predicates: false, + where_clause_span: span, + span, + }); + let kind = hir::ItemKind::Fn(sig, generics, body_id); + let g = &self.tcx.sess.parse_sess.attr_id_generator; + self.lower_attrs( + hir_id, + &[ + attr::mk_attr_nested_word(g, ast::AttrStyle::Outer, sym::inline, sym::never, span), + attr::mk_attr_word(g, ast::AttrStyle::Outer, sym::track_caller, span), + attr::mk_attr_word(g, ast::AttrStyle::Outer, sym::cold, span), + ], + ); + arena.alloc(hir::Item { + owner_id: hir_id.expect_owner(), + ident: Ident::new(sym::panic_cold, span), + kind, + vis_span: span, + span, + }) } } +fn process_args<'a>(sess: &Session, fmt: &'a FormatArgs) -> (bool, Cow<'a, FormatArgs>) { + // Never call the const constructor of `fmt::Arguments` if the + // format_args!() had any arguments _before_ flattening/inlining. + let allow_const = fmt.arguments.all_args().is_empty(); + let mut fmt = Cow::Borrowed(fmt); + if sess.opts.unstable_opts.flatten_format_args { + fmt = flatten_format_args(fmt); + fmt = inline_literals(fmt); + } + (allow_const, fmt) +} + /// Flattens nested `format_args!()` into one. /// /// Turns @@ -351,6 +662,7 @@ fn expand_format_args<'hir>( macsp: Span, fmt: &FormatArgs, allow_const: bool, + mut arg_access: impl FnMut(&mut LoweringContext<'_, 'hir>, usize, Span) -> hir::Expr<'hir>, ) -> hir::ExprKind<'hir> { let mut incomplete_lit = String::new(); let lit_pieces = @@ -410,15 +722,11 @@ fn expand_format_args<'hir>( let format_options = use_format_options.then(|| { // Generate: // &[format_spec_0, format_spec_1, format_spec_2] - let elements: Vec<_> = fmt - .template - .iter() - .filter_map(|piece| { - let FormatArgsPiece::Placeholder(placeholder) = piece else { return None }; - Some(make_format_spec(ctx, macsp, placeholder, &mut argmap)) - }) - .collect(); - ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements)) + let elements = ctx.arena.alloc_from_iter(fmt.template.iter().filter_map(|piece| { + let FormatArgsPiece::Placeholder(placeholder) = piece else { return None }; + Some(make_format_spec(ctx, macsp, placeholder, &mut argmap)) + })); + ctx.expr_array_ref(macsp, elements) }); let arguments = fmt.arguments.all_args(); @@ -477,25 +785,19 @@ fn expand_format_args<'hir>( // ::new_debug(&arg2), // … // ] - let elements: Vec<_> = arguments - .iter() - .zip(argmap) - .map(|(arg, ((_, ty), placeholder_span))| { + let elements = ctx.arena.alloc_from_iter(arguments.iter().enumerate().zip(argmap).map( + |((i, arg), ((_, ty), placeholder_span))| { let placeholder_span = placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt()); let arg_span = match arg.kind { FormatArgumentKind::Captured(_) => placeholder_span, _ => arg.expr.span.with_ctxt(macsp.ctxt()), }; - let arg = ctx.lower_expr(&arg.expr); - let ref_arg = ctx.arena.alloc(ctx.expr( - arg_span, - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg), - )); + let ref_arg = ctx.arena.alloc(arg_access(ctx, i, arg_span)); make_argument(ctx, placeholder_span, ref_arg, ty) - }) - .collect(); - ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements)) + }, + )); + ctx.expr_array_ref(macsp, elements) } else { // Generate: // &match (&arg0, &arg1, &…) { @@ -528,19 +830,13 @@ fn expand_format_args<'hir>( make_argument(ctx, placeholder_span, arg, ty) }, )); - let elements: Vec<_> = arguments - .iter() - .map(|arg| { - let arg_expr = ctx.lower_expr(&arg.expr); - ctx.expr( - arg.expr.span.with_ctxt(macsp.ctxt()), - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr), - ) - }) - .collect(); - let args_tuple = ctx - .arena - .alloc(ctx.expr(macsp, hir::ExprKind::Tup(ctx.arena.alloc_from_iter(elements)))); + let elements = ctx.arena.alloc_from_iter( + arguments + .iter() + .enumerate() + .map(|(i, arg)| arg_access(ctx, i, arg.expr.span.with_ctxt(macsp.ctxt()))), + ); + let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements))); let array = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args))); let match_arms = ctx.arena.alloc_from_iter([ctx.arm(args_pat, array)]); let match_expr = ctx.arena.alloc(ctx.expr_match( diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index a59c83de0f46f..fe563b2c6657d 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -114,6 +114,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { AstOwner::Item(item) => self.lower_item(item), AstOwner::AssocItem(item, ctxt) => self.lower_assoc_item(item, ctxt), AstOwner::ForeignItem(item) => self.lower_foreign_item(item), + AstOwner::PanicArgsCold(fmt, span) => self.lower_panic_args_cold(fmt, span), } } @@ -175,6 +176,11 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { fn lower_foreign_item(&mut self, item: &ForeignItem) { self.with_lctx(item.id, |lctx| hir::OwnerNode::ForeignItem(lctx.lower_foreign_item(item))) } + + fn lower_panic_args_cold(&mut self, fmt: &FormatArgs, span: Span) { + let FormatPanicKind::Panic { id, constness: _ } = fmt.panic else { panic!() }; + self.with_lctx(id, |lctx| hir::OwnerNode::Item(lctx.lower_panic_args_cold(fmt, span))) + } } impl<'hir> LoweringContext<'_, 'hir> { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a6d1ef33f4069..3ec29b126c52d 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -360,6 +360,9 @@ enum AstOwner<'a> { Item(&'a ast::Item), AssocItem(&'a ast::AssocItem, visit::AssocCtxt), ForeignItem(&'a ast::ForeignItem), + + /// An owner for the generated function for `panic_args!` + PanicArgsCold(&'a ast::FormatArgs, Span), } fn index_crate<'a>( @@ -377,6 +380,13 @@ fn index_crate<'a>( index: IndexVec>, } + impl<'s, 'a> Indexer<'s, 'a> { + fn set_owner(&mut self, id: NodeId, owner: AstOwner<'a>) { + let def_id = self.node_id_to_def_id[&id]; + *self.index.ensure_contains_elem(def_id, || AstOwner::NonOwner) = owner; + } + } + impl<'a> visit::Visitor<'a> for Indexer<'_, 'a> { fn visit_attribute(&mut self, _: &'a Attribute) { // We do not want to lower expressions that appear in attributes, @@ -384,24 +394,28 @@ fn index_crate<'a>( } fn visit_item(&mut self, item: &'a ast::Item) { - let def_id = self.node_id_to_def_id[&item.id]; - *self.index.ensure_contains_elem(def_id, || AstOwner::NonOwner) = AstOwner::Item(item); - visit::walk_item(self, item) + self.set_owner(item.id, AstOwner::Item(item)); + visit::walk_item(self, item); } fn visit_assoc_item(&mut self, item: &'a ast::AssocItem, ctxt: visit::AssocCtxt) { - let def_id = self.node_id_to_def_id[&item.id]; - *self.index.ensure_contains_elem(def_id, || AstOwner::NonOwner) = - AstOwner::AssocItem(item, ctxt); + self.set_owner(item.id, AstOwner::AssocItem(item, ctxt)); visit::walk_assoc_item(self, item, ctxt); } fn visit_foreign_item(&mut self, item: &'a ast::ForeignItem) { - let def_id = self.node_id_to_def_id[&item.id]; - *self.index.ensure_contains_elem(def_id, || AstOwner::NonOwner) = - AstOwner::ForeignItem(item); + self.set_owner(item.id, AstOwner::ForeignItem(item)); visit::walk_foreign_item(self, item); } + + fn visit_expr(&mut self, ex: &'a ast::Expr) { + if let ast::ExprKind::FormatArgs(ref fmt) = ex.kind { + if let ast::FormatPanicKind::Panic { id, .. } = fmt.panic { + self.set_owner(id, AstOwner::PanicArgsCold(&*fmt, ex.span)); + } + } + visit::walk_expr(self, ex) + } } } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 39741a03930d1..e1bbeec7c22de 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -549,7 +549,10 @@ impl<'a> State<'a> { } ast::ExprKind::FormatArgs(fmt) => { // FIXME: This should have its own syntax, distinct from a macro invocation. - self.word("format_args!"); + self.word(match fmt.panic { + ast::FormatPanicKind::Format => "format_args!", + ast::FormatPanicKind::Panic { .. } => "panic_args!", + }); self.popen(); self.rbox(0, Inconsistent); self.word(reconstruct_format_args_template_string(&fmt.template)); diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index ede95dbf897e7..f8c1bdb5f87ed 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -1,10 +1,10 @@ use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{token, StmtKind}; +use rustc_ast::{token, FormatPanicKind, StmtKind}; use rustc_ast::{ - Expr, ExprKind, FormatAlignment, FormatArgPosition, FormatArgPositionKind, FormatArgs, + Const, Expr, ExprKind, FormatAlignment, FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArguments, FormatCount, - FormatDebugHex, FormatOptions, FormatPlaceholder, FormatSign, FormatTrait, + FormatDebugHex, FormatOptions, FormatPlaceholder, FormatSign, FormatTrait, DUMMY_NODE_ID, }; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, MultiSpan, PResult, SingleLabelManySpans}; @@ -157,6 +157,7 @@ fn make_format_args( ecx: &mut ExtCtxt<'_>, input: MacroInput, append_newline: bool, + panic: bool, ) -> Result { let msg = "format argument must be a string literal"; let unexpanded_fmt_span = input.fmtstr.span; @@ -530,7 +531,23 @@ fn make_format_args( } } - Ok(FormatArgs { span: fmt_span, template, arguments: args }) + Ok(FormatArgs { + span: fmt_span, + template, + arguments: args, + panic: if panic { + FormatPanicKind::Panic { + id: DUMMY_NODE_ID, + constness: if ecx.current_expansion.in_const { + Const::Yes(fmt_span) + } else { + Const::No + }, + } + } else { + FormatPanicKind::Format + }, + }) } fn invalid_placeholder_type_error( @@ -849,11 +866,12 @@ fn expand_format_args_impl<'cx>( mut sp: Span, tts: TokenStream, nl: bool, + panic: bool, ) -> Box { sp = ecx.with_def_site_ctxt(sp); match parse_args(ecx, sp, tts) { Ok(input) => { - if let Ok(format_args) = make_format_args(ecx, input, nl) { + if let Ok(format_args) = make_format_args(ecx, input, nl, panic) { MacEager::expr(ecx.expr(sp, ExprKind::FormatArgs(P(format_args)))) } else { MacEager::expr(DummyResult::raw_expr(sp, true)) @@ -871,7 +889,7 @@ pub fn expand_format_args<'cx>( sp: Span, tts: TokenStream, ) -> Box { - expand_format_args_impl(ecx, sp, tts, false) + expand_format_args_impl(ecx, sp, tts, false, false) } pub fn expand_format_args_nl<'cx>( @@ -879,5 +897,13 @@ pub fn expand_format_args_nl<'cx>( sp: Span, tts: TokenStream, ) -> Box { - expand_format_args_impl(ecx, sp, tts, true) + expand_format_args_impl(ecx, sp, tts, true, false) +} + +pub fn expand_panic_args<'cx>( + ecx: &'cx mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, +) -> Box { + expand_format_args_impl(ecx, sp, tts, false, true) } diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index ebf1448f55c99..b032560d03c68 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -84,6 +84,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { format_args_nl: format::expand_format_args_nl, format_args: format::expand_format_args, const_format_args: format::expand_format_args, + panic_args: format::expand_panic_args, global_asm: asm::expand_global_asm, include_bytes: source_util::expand_include_bytes, include_str: source_util::expand_include_str, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index c4d2a374f0c67..98fabf6a2d780 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1001,6 +1001,8 @@ pub struct ExpansionData { /// Some parent node that is close to this macro call pub lint_node_id: NodeId, pub is_trailing_mac: bool, + /// Currently in a constant context or constant function. + pub in_const: bool, } /// One of these is made during expansion and incrementally updated as we go; @@ -1050,6 +1052,7 @@ impl<'a> ExtCtxt<'a> { dir_ownership: DirOwnership::Owned { relative: None }, lint_node_id: ast::CRATE_NODE_ID, is_trailing_mac: false, + in_const: false, }, force_mode: false, expansions: FxIndexMap::default(), diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 34d16bf00cd67..9dc798910e1c6 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -9,6 +9,7 @@ use crate::mbe::diagnostics::annotate_err_with_kind; use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod}; use crate::placeholders::{placeholder, PlaceholderExpander}; +use ast::AnonConst; use rustc_ast as ast; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; @@ -1748,6 +1749,13 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { }); } + fn with_const(&mut self, in_const: bool, f: impl FnOnce(&mut Self) -> R) -> R { + let old = mem::replace(&mut self.cx.current_expansion.in_const, in_const); + let r = f(self); + self.cx.current_expansion.in_const = old; + r + } + fn flat_map_node>( &mut self, mut node: Node, @@ -1929,6 +1937,34 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { self.visit_node(node) } + fn visit_item_kind(&mut self, i: &mut ItemKind) { + let in_const = match i { + ItemKind::Const(..) | ItemKind::Static(..) => true, + ItemKind::Fn(f) if f.sig.header.constness.is_const() => true, + _ => false, + }; + self.with_const(in_const, |this| { + noop_visit_item_kind(i, this); + }); + } + + fn visit_assoc_item_kind(&mut self, i: &mut AssocItemKind) { + let in_const = match i { + AssocItemKind::Const(..) => true, + AssocItemKind::Fn(f) if f.sig.header.constness.is_const() => true, + _ => false, + }; + self.with_const(in_const, |this| { + noop_visit_assoc_item_kind(i, this); + }); + } + + fn visit_anon_const(&mut self, c: &mut AnonConst) { + self.with_const(true, |this| { + noop_visit_anon_const(c, this); + }); + } + fn visit_ty(&mut self, node: &mut P) { self.visit_node(node) } @@ -1942,7 +1978,26 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { if let Some(attr) = node.attrs.first() { self.cfg().maybe_emit_expr_attr_err(attr); } - self.visit_node(node) + + // Use the same `in_const` logic in both `visit_expr` and `filter_map_expr` as + // expressions only gets called for one of them. + let in_const = match &node.kind { + ast::ExprKind::Closure(c) => c.constness.is_const(), + _ => self.cx.current_expansion.in_const, + }; + + self.with_const(in_const, |this| this.visit_node(node)); + } + + fn filter_map_expr(&mut self, node: P) -> Option> { + // Use the same `in_const` logic in both `visit_expr` and `filter_map_expr` as + // expressions only gets called for one of them. + let in_const = match &node.kind { + ast::ExprKind::Closure(c) => c.constness.is_const(), + _ => self.cx.current_expansion.in_const, + }; + + self.with_const(in_const, |this| this.flat_map_node(AstNodeWrapper::new(node, OptExprTag))) } fn visit_method_receiver_expr(&mut self, node: &mut P) { @@ -1953,10 +2008,6 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { }) } - fn filter_map_expr(&mut self, node: P) -> Option> { - self.flat_map_node(AstNodeWrapper::new(node, OptExprTag)) - } - fn visit_block(&mut self, node: &mut P) { let orig_dir_ownership = mem::replace( &mut self.cx.current_expansion.dir_ownership, diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 23b20543d5355..d33e0e818eec4 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -250,6 +250,17 @@ language_item_table! { FormatPlaceholder, sym::format_placeholder, format_placeholder, Target::Struct, GenericRequirement::None; FormatUnsafeArg, sym::format_unsafe_arg, format_unsafe_arg, Target::Struct, GenericRequirement::None; + // Lang items needed for `panic_args!()`. + FormatDisplay, sym::display_trait, format_display_trait, Target::Trait, GenericRequirement::None; + FormatDebug, sym::debug_trait, format_debug_trait, Target::Trait, GenericRequirement::None; + FormatLowerExp, sym::lower_exp_trait, format_lower_exp_trait, Target::Trait, GenericRequirement::None; + FormatUpperExp, sym::upper_exp_trait, format_upper_exp_trait, Target::Trait, GenericRequirement::None; + FormatOctal, sym::octal_trait, format_octal_trait, Target::Trait, GenericRequirement::None; + FormatPointer, sym::pointer_trait, format_pointer_trait, Target::Trait, GenericRequirement::None; + FormatBinary, sym::binary_trait, format_binary_trait, Target::Trait, GenericRequirement::None; + FormatLowerHex, sym::lower_hex_trait, format_lower_hex_trait, Target::Trait, GenericRequirement::None; + FormatUpperHex, sym::upper_hex_trait, format_upper_hex_trait, Target::Trait, GenericRequirement::None; + ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None; DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 30db450870b60..9fbd37f81d2fb 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1460,6 +1460,13 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> { } } + fn visit_format_args(&mut self, fmt: &'b ast::FormatArgs) { + if let ast::FormatPanicKind::Panic { id, .. } = fmt.panic { + self.r.visibilities.insert(self.r.local_def_id(id), ty::Visibility::Public); + } + visit::walk_format_args(self, fmt); + } + // Constructs the reduced graph for one variant. Variants exist in the // type and value namespaces. fn visit_variant(&mut self, variant: &'b ast::Variant) { diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 356d7f365fe71..2d30fb6bf5956 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -311,6 +311,12 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { } } + fn visit_format_args(&mut self, fmt: &'a ast::FormatArgs) { + if let ast::FormatPanicKind::Panic { id, .. } = fmt.panic { + self.create_def(id, DefPathData::ValueNs(sym::panic_args), fmt.span); + } + visit::walk_format_args(self, fmt); + } // This method is called only when we are visiting an individual field // after expanding an attribute on it. fn visit_field_def(&mut self, field: &'a FieldDef) { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 656deebb5d06b..d54b934623ed0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -425,6 +425,7 @@ symbols! { begin_panic, bench, bin, + binary_trait, bind_by_move_pattern_guards, bindings_after_at, bitand, @@ -605,6 +606,7 @@ symbols! { debug_assertions, debug_struct, debug_struct_fields_finish, + debug_trait, debug_tuple, debug_tuple_fields_finish, debugger_visualizer, @@ -637,6 +639,7 @@ symbols! { discriminant_type, discriminant_value, dispatch_from_dyn, + display_trait, div, div_assign, do_not_recommend, @@ -923,6 +926,8 @@ symbols! { logf32, logf64, loop_break_value, + lower_exp_trait, + lower_hex_trait, lt, macro_at_most_once_rep, macro_attributes_in_derive_output, @@ -1067,6 +1072,7 @@ symbols! { notable_trait, note, object_safe_for_dispatch, + octal_trait, of, offset, offset_of, @@ -1097,8 +1103,11 @@ symbols! { panic_2015, panic_2021, panic_abort, + panic_args, + panic_args_macro, panic_bounds_check, panic_cannot_unwind, + panic_cold, panic_display, panic_fmt, panic_handler, @@ -1133,6 +1142,7 @@ symbols! { pointee_trait, pointer, pointer_like, + pointer_trait, poll, position, post_dash_lto: "post-lto", @@ -1656,6 +1666,8 @@ symbols! { unwind_safe_trait, unwrap, unwrap_or, + upper_exp_trait, + upper_hex_trait, use_extern_macros, use_nested_groups, used, diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 9ce6093f1d1f3..70c4ba9c0d85e 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -545,6 +545,7 @@ impl Display for Arguments<'_> { #[doc(alias = "{:?}")] #[rustc_diagnostic_item = "Debug"] #[rustc_trivial_field_reads] +#[cfg_attr(not(bootstrap), lang = "debug_trait")] pub trait Debug { /// Formats the value using the given formatter. /// @@ -644,6 +645,7 @@ pub use macros::Debug; )] #[doc(alias = "{}")] #[rustc_diagnostic_item = "Display"] +#[cfg_attr(not(bootstrap), lang = "display_trait")] #[stable(feature = "rust1", since = "1.0.0")] pub trait Display { /// Formats the value using the given formatter. @@ -718,6 +720,7 @@ pub trait Display { /// /// assert_eq!(format!("l as octal is: {l:#06o}"), "l as octal is: 0o0011"); /// ``` +#[cfg_attr(not(bootstrap), lang = "octal_trait")] #[stable(feature = "rust1", since = "1.0.0")] pub trait Octal { /// Formats the value using the given formatter. @@ -775,6 +778,7 @@ pub trait Octal { /// "l as binary is: 0b000000000000000000000001101011" /// ); /// ``` +#[cfg_attr(not(bootstrap), lang = "binary_trait")] #[stable(feature = "rust1", since = "1.0.0")] pub trait Binary { /// Formats the value using the given formatter. @@ -830,6 +834,7 @@ pub trait Binary { /// /// assert_eq!(format!("l as hex is: {l:#010x}"), "l as hex is: 0x00000009"); /// ``` +#[cfg_attr(not(bootstrap), lang = "lower_hex_trait")] #[stable(feature = "rust1", since = "1.0.0")] pub trait LowerHex { /// Formats the value using the given formatter. @@ -885,6 +890,7 @@ pub trait LowerHex { /// /// assert_eq!(format!("l as hex is: {l:#010X}"), "l as hex is: 0x7FFFFFFF"); /// ``` +#[cfg_attr(not(bootstrap), lang = "upper_hex_trait")] #[stable(feature = "rust1", since = "1.0.0")] pub trait UpperHex { /// Formats the value using the given formatter. @@ -935,6 +941,7 @@ pub trait UpperHex { /// assert_eq!(l_ptr.len(), 18); /// assert_eq!(&l_ptr[..2], "0x"); /// ``` +#[cfg_attr(not(bootstrap), lang = "pointer_trait")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Pointer"] pub trait Pointer { @@ -987,6 +994,7 @@ pub trait Pointer { /// "l in scientific notation is: 001e2" /// ); /// ``` +#[cfg_attr(not(bootstrap), lang = "lower_exp_trait")] #[stable(feature = "rust1", since = "1.0.0")] pub trait LowerExp { /// Formats the value using the given formatter. @@ -1038,6 +1046,7 @@ pub trait LowerExp { /// "l in scientific notation is: 001E2" /// ); /// ``` +#[cfg_attr(not(bootstrap), lang = "upper_exp_trait")] #[stable(feature = "rust1", since = "1.0.0")] pub trait UpperExp { /// Formats the value using the given formatter. diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 46628bcea000d..819d163dd1ec7 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -892,6 +892,20 @@ pub(crate) mod builtin { ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; } + /// Panics with a formatted string. Takes the same arguments as [`format_args`]. + /// + /// This macro is used by the panic macros. + #[unstable(feature = "panic_args", issue = "none")] + #[cfg_attr(not(test), rustc_diagnostic_item = "panic_args_macro")] + #[allow_internal_unstable(core_panic, fmt_internals, const_fmt_arguments_new)] + #[rustc_builtin_macro] + #[macro_export] + #[cfg(not(bootstrap))] + macro_rules! panic_args { + ($fmt:expr) => {{ /* compiler built-in */ }}; + ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; + } + /// Same as [`format_args`], but adds a newline in the end. #[unstable( feature = "format_args_nl", diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 20be60d35353e..f1e951b05064b 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -47,6 +47,7 @@ pub macro panic_2015 { #[allow_internal_unstable(core_panic, const_format_args)] #[rustc_diagnostic_item = "core_panic_2021_macro"] #[rustc_macro_transparency = "semitransparent"] +#[cfg(any(bootstrap, feature = "panic_immediate_abort"))] pub macro panic_2021 { () => ( $crate::panicking::panic("explicit panic") @@ -62,6 +63,68 @@ pub macro panic_2021 { }), } +#[doc(hidden)] +#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] +#[allow_internal_unstable( + core_panic, + core_intrinsics, + const_dispatch, + const_eval_select, + const_format_args, + panic_args, + rustc_attrs +)] +#[rustc_diagnostic_item = "core_panic_2021_macro"] +#[rustc_macro_transparency = "semitransparent"] +#[cfg(not(any(bootstrap, feature = "panic_immediate_abort")))] +pub macro panic_2021 { + () => ({ + // Create a function so that the argument for `track_caller` + // can be moved inside if possible. + #[cold] + #[track_caller] + #[inline(never)] + const fn panic_cold_explicit() -> ! { + $crate::panicking::panic_explicit() + } + panic_cold_explicit(); + }), + // Special-case the single-argument case for const_panic. + ("{}", $arg:expr $(,)?) => ({ + #[cold] + #[track_caller] + #[inline(never)] + #[rustc_do_not_const_check] // Allow the call to `panic_display`. + const fn panic_cold_display(arg: &T) -> ! { + $crate::panicking::panic_display(arg) + } + + let arg = &$arg; + + // Ensure the caller could call `panic_display` itself directly. + // Const checking will ensure only &str is allowed. + if false { + $crate::panicking::panic_display(arg); + } + + // Call `panic_display` directly for const eval since `panic_cold_display` can not be + // evaluated as it is marked with `rustc_do_not_const_check`. + + // SAFETY: These branches are observably equivalent as `panic_cold_display` is just an + // indirect way of calling `panic_display`. + unsafe { + $crate::intrinsics::const_eval_select( + (arg,), + $crate::panicking::panic_display, + panic_cold_display + ); + } + }), + ($($t:tt)+) => ({ + $crate::panic_args!($($t)+); + }), +} + #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")] #[allow_internal_unstable(core_panic)] diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 281f8c1e1663c..36d14473f90cc 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -152,6 +152,14 @@ pub const fn panic_str(expr: &str) -> ! { panic_display(&expr); } +#[track_caller] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] +#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[rustc_const_unstable(feature = "core_panic", issue = "none")] +pub const fn panic_explicit() -> ! { + panic_display(&"explicit panic"); +} + #[inline] #[track_caller] #[rustc_diagnostic_item = "unreachable_display"] // needed for `non-fmt-panics` lint diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index 367cd6bd41337..6c44cdad0c85a 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,11 +1,13 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; use if_chain::if_chain; +use rustc_ast::LitKind; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; declare_clippy_lint! { /// ### What it does @@ -134,15 +136,56 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { } } +fn expr_might_diverge(e: &Expr<'_>) -> bool { + match e.kind { + ExprKind::Path(..) => false, + ExprKind::AddrOf(.., e) => expr_might_diverge(e), + ExprKind::If( + &Expr { + kind: + ExprKind::Lit(Spanned { + node: LitKind::Bool(false), + .. + }), + .. + }, + _, + None, + ) => false, + _ => true, + } +} + +fn stmt_might_diverge(stmt: &Stmt<'_>) -> bool { + match stmt.kind { + StmtKind::Item(..) => false, + StmtKind::Local(Local { + init: Some(e), + els: None, + .. + }) => expr_might_diverge(e), + StmtKind::Expr(e) | StmtKind::Semi(e) => expr_might_diverge(e), + _ => true, + } +} + impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { match e.kind { // fix #10776 ExprKind::Block(block, ..) => match (block.stmts, block.expr) { - ([], Some(e)) => self.visit_expr(e), - ([stmt], None) => match stmt.kind { - StmtKind::Expr(e) | StmtKind::Semi(e) => self.visit_expr(e), - _ => {}, + (stmts, Some(e)) => { + if stmts.iter().all(|stmt| !stmt_might_diverge(stmt)) { + self.visit_expr(e) + } + }, + ([first @ .., stmt], None) => { + if first.iter().all(|stmt| !stmt_might_diverge(stmt)) { + match stmt.kind { + StmtKind::Expr(e) | StmtKind::Semi(e) => self.visit_expr(e), + _ => {}, + } + } }, _ => {}, }, diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index 173f9841d4469..1d453ba749fee 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -3,6 +3,7 @@ use crate::visitors::{for_each_expr, Descend}; use arrayvec::ArrayVec; +use hir::{Block, Local, Stmt, StmtKind}; use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath}; @@ -25,6 +26,7 @@ const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ sym::eprintln_macro, sym::format_args_macro, sym::format_macro, + sym::panic_args_macro, sym::print_macro, sym::println_macro, sym::std_panic_macro, @@ -222,17 +224,54 @@ pub enum PanicExpn<'a> { /// A single argument that implements `Display` - `panic!("{}", object)` Display(&'a Expr<'a>), /// Anything else - `panic!("error {}: {}", a, b)` - Format(&'a Expr<'a>), + Format(Option<&'a Expr<'a>>), } impl<'a> PanicExpn<'a> { pub fn parse(expr: &'a Expr<'a>) -> Option { - let ExprKind::Call(callee, [arg, rest @ ..]) = &expr.kind else { + // Pattern match on the special single-argument case for const_panic in `panic_2021`. + if let ExprKind::Block( + &Block { + stmts: + [ + Stmt { kind: StmtKind::Item(..), .. }, + Stmt { + kind: + StmtKind::Local(&Local { + init: Some(&Expr { kind: ExprKind::AddrOf(_, _, arg), .. }), + .. + }), + .. + }, + Stmt { kind: StmtKind::Expr(&Expr { kind: ExprKind::If(..), .. }), .. }, + .., + ], + .. + }, + .., + ) = &expr.kind + { + return Some(Self::Display(arg)); + } + + let ExprKind::Call(callee, args) = &expr.kind else { return None; }; let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None; }; + let name = path.segments.last().unwrap().ident.as_str(); + + // These may have no arguments + match name { + "panic_cold_explicit" => return Some(Self::Empty), + "panic_cold" => return Some(Self::Format(None)), + _ => (), + }; + + let [arg, rest @ ..] = args else { + return None; + }; let result = match path.segments.last().unwrap().ident.as_str() { "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty, "panic" | "panic_str" => Self::Str(arg), @@ -241,8 +280,8 @@ impl<'a> PanicExpn<'a> { return None; }; Self::Display(e) - }, - "panic_fmt" => Self::Format(arg), + } + "panic_fmt" => Self::Format(Some(arg)), // Since Rust 1.52, `assert_{eq,ne}` macros expand to use: // `core::panicking::assert_failed(.., left_val, right_val, None | Some(format_args!(..)));` "assert_failed" => { @@ -254,10 +293,10 @@ impl<'a> PanicExpn<'a> { // `msg_arg` is either `None` (no custom message) or `Some(format_args!(..))` (custom message) let msg_arg = &rest[2]; match msg_arg.kind { - ExprKind::Call(_, [fmt_arg]) => Self::Format(fmt_arg), + ExprKind::Call(_, [fmt_arg]) => Self::Format(Some(fmt_arg)), _ => Self::Empty, } - }, + } _ => return None, }; Some(result) @@ -399,10 +438,17 @@ pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, let format_args_expr = for_each_expr(start, |expr| { let ctxt = expr.span.ctxt(); if ctxt.outer_expn().is_descendant_of(expn_id) { - if macro_backtrace(expr.span) - .map(|macro_call| cx.tcx.item_name(macro_call.def_id)) - .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl)) - { + if macro_backtrace(expr.span).map(|macro_call| cx.tcx.item_name(macro_call.def_id)).any( + |name| { + matches!( + name, + sym::const_format_args + | sym::format_args + | sym::format_args_nl + | sym::panic_args + ) + }, + ) { ControlFlow::Break(expr) } else { ControlFlow::Continue(Descend::Yes) diff --git a/src/tools/clippy/tests/ui/diverging_sub_expression.stderr b/src/tools/clippy/tests/ui/diverging_sub_expression.stderr index d8021c5d7ba83..c53286614c050 100644 --- a/src/tools/clippy/tests/ui/diverging_sub_expression.stderr +++ b/src/tools/clippy/tests/ui/diverging_sub_expression.stderr @@ -37,7 +37,7 @@ error: sub-expression diverges LL | _ => true || panic!("boo"), | ^^^^^^^^^^^^^ | - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: sub-expression diverges --> $DIR/diverging_sub_expression.rs:48:29 diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs index 1ee048bf7f6c3..e1b95aa577604 100644 --- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs @@ -4,14 +4,18 @@ //@no-rustfix use std::sync::atomic::Ordering; // #[non_exhaustive] enum +fn repeat() -> ! { + panic!() +} + pub fn f(x: Ordering) { match x { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => panic!(), + Ordering::AcqRel | Ordering::SeqCst => repeat(), #[deny(non_exhaustive_omitted_patterns)] - _ => panic!(), + _ => repeat(), } } @@ -25,8 +29,8 @@ mod f { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => panic!(), - _ => panic!(), + Ordering::AcqRel | Ordering::SeqCst => repeat(), + _ => repeat(), } } } @@ -38,9 +42,9 @@ pub fn g(x: Ordering) { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => panic!(), + Ordering::AcqRel | Ordering::SeqCst => repeat(), //~^ ERROR: this match arm has an identical body to the `_` wildcard arm - _ => panic!(), + _ => repeat(), } } @@ -52,9 +56,9 @@ mod g { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => panic!(), + Ordering::AcqRel | Ordering::SeqCst => repeat(), //~^ ERROR: this match arm has an identical body to the `_` wildcard arm - _ => panic!(), + _ => repeat(), } } } diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr index a039536338bcb..ae6b02ab1b507 100644 --- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr @@ -1,29 +1,29 @@ error: this match arm has an identical body to the `_` wildcard arm - --> $DIR/match_same_arms_non_exhaustive.rs:41:9 + --> $DIR/match_same_arms_non_exhaustive.rs:45:9 | -LL | Ordering::AcqRel | Ordering::SeqCst => panic!(), +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here - --> $DIR/match_same_arms_non_exhaustive.rs:43:9 + --> $DIR/match_same_arms_non_exhaustive.rs:47:9 | -LL | _ => panic!(), +LL | _ => repeat(), | ^^^^^^^^^^^^^ = note: `-D clippy::match-same-arms` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` error: this match arm has an identical body to the `_` wildcard arm - --> $DIR/match_same_arms_non_exhaustive.rs:55:13 + --> $DIR/match_same_arms_non_exhaustive.rs:59:13 | -LL | Ordering::AcqRel | Ordering::SeqCst => panic!(), +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here - --> $DIR/match_same_arms_non_exhaustive.rs:57:13 + --> $DIR/match_same_arms_non_exhaustive.rs:61:13 | -LL | _ => panic!(), +LL | _ => repeat(), | ^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/panic/panic1.stderr b/src/tools/miri/tests/panic/panic1.stderr index 4eb4244d7472c..26254c31d0179 100644 --- a/src/tools/miri/tests/panic/panic1.stderr +++ b/src/tools/miri/tests/panic/panic1.stderr @@ -5,8 +5,10 @@ stack backtrace: at RUSTLIB/std/src/panicking.rs:LL:CC 1: std::rt::panic_fmt at RUSTLIB/core/src/panicking.rs:LL:CC - 2: main + 2: main::panic_args + at RUSTLIB/core/src/panic.rs:LL:CC + 3: main at $DIR/panic1.rs:LL:CC - 3: >::call_once - shim(fn()) + 4: >::call_once - shim(fn()) at RUSTLIB/core/src/ops/function.rs:LL:CC note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. diff --git a/tests/ui/consts/const-eval/const_panic_2021.stderr b/tests/ui/consts/const-eval/const_panic_2021.stderr index 192fa3a12c25c..21b8a0754b414 100644 --- a/tests/ui/consts/const-eval/const_panic_2021.stderr +++ b/tests/ui/consts/const-eval/const_panic_2021.stderr @@ -4,7 +4,7 @@ error[E0080]: evaluation of constant value failed LL | const A: () = std::panic!("blåhaj"); | ^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'blåhaj', $DIR/const_panic_2021.rs:6:15 | - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `std::panic` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic_args` which comes from the expansion of the macro `std::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic_2021.rs:9:15 @@ -44,7 +44,7 @@ error[E0080]: evaluation of constant value failed LL | const A_CORE: () = core::panic!("shark"); | ^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'shark', $DIR/const_panic_2021.rs:21:20 | - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic_args` which comes from the expansion of the macro `core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic_2021.rs:24:20 diff --git a/tests/ui/consts/const-float-bits-reject-conv.stderr b/tests/ui/consts/const-float-bits-reject-conv.stderr index 7ad0225209420..cb884f3469fd2 100644 --- a/tests/ui/consts/const-float-bits-reject-conv.stderr +++ b/tests/ui/consts/const-float-bits-reject-conv.stderr @@ -12,7 +12,7 @@ note: inside `f32::MASKED_NAN1` | LL | const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA; | ^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/num/f32.rs:LL:COL @@ -28,7 +28,7 @@ note: inside `f32::MASKED_NAN2` | LL | const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555; | ^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant used --> $DIR/const-float-bits-reject-conv.rs:35:34 @@ -68,7 +68,7 @@ note: inside `f64::MASKED_NAN1` | LL | const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA; | ^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/num/f64.rs:LL:COL @@ -84,7 +84,7 @@ note: inside `f64::MASKED_NAN2` | LL | const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555; | ^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant used --> $DIR/const-float-bits-reject-conv.rs:57:34 diff --git a/tests/ui/fmt/ifmt-unimpl.stderr b/tests/ui/fmt/ifmt-unimpl.stderr index 4c0ac52865d77..e11aaa90b34dc 100644 --- a/tests/ui/fmt/ifmt-unimpl.stderr +++ b/tests/ui/fmt/ifmt-unimpl.stderr @@ -6,16 +6,6 @@ LL | format!("{:X}", "3"); | | | required by a bound introduced by this call | - = help: the following other types implement trait `UpperHex`: - isize - i8 - i16 - i32 - i64 - i128 - usize - u8 - and 20 others = note: required for `&str` to implement `UpperHex` note: required by a bound in `core::fmt::rt::Argument::<'a>::new_upper_hex` --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL