diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_ast/src/format.rs (renamed from compiler/rustc_builtin_macros/src/format/ast.rs) | 68 | ||||
-rw-r--r-- | compiler/rustc_builtin_macros/src/format.rs | 33 | ||||
-rw-r--r-- | compiler/rustc_builtin_macros/src/format/expand.rs | 353 |
3 files changed, 70 insertions, 384 deletions
diff --git a/compiler/rustc_builtin_macros/src/format/ast.rs b/compiler/rustc_ast/src/format.rs index 01dbffa21..d021bea5e 100644 --- a/compiler/rustc_builtin_macros/src/format/ast.rs +++ b/compiler/rustc_ast/src/format.rs @@ -1,5 +1,5 @@ -use rustc_ast::ptr::P; -use rustc_ast::Expr; +use crate::ptr::P; +use crate::Expr; use rustc_data_structures::fx::FxHashMap; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -39,7 +39,7 @@ use rustc_span::Span; /// Basically the "AST" for a complete `format_args!()`. /// /// E.g., `format_args!("hello {name}");`. -#[derive(Clone, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct FormatArgs { pub span: Span, pub template: Vec<FormatArgsPiece>, @@ -49,7 +49,7 @@ pub struct FormatArgs { /// A piece of a format template string. /// /// E.g. "hello" or "{name}". -#[derive(Clone, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum FormatArgsPiece { Literal(Symbol), Placeholder(FormatPlaceholder), @@ -59,7 +59,7 @@ pub enum FormatArgsPiece { /// /// E.g. `1, 2, name="ferris", n=3`, /// but also implicit captured arguments like `x` in `format_args!("{x}")`. -#[derive(Clone, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct FormatArguments { arguments: Vec<FormatArgument>, num_unnamed_args: usize, @@ -67,6 +67,12 @@ pub struct FormatArguments { names: FxHashMap<Symbol, usize>, } +// FIXME: Rustdoc has trouble proving Send/Sync for this. See #106930. +#[cfg(parallel_compiler)] +unsafe impl Sync for FormatArguments {} +#[cfg(parallel_compiler)] +unsafe impl Send for FormatArguments {} + impl FormatArguments { pub fn new() -> Self { Self { @@ -121,18 +127,22 @@ impl FormatArguments { &self.arguments[..self.num_explicit_args] } - pub fn into_vec(self) -> Vec<FormatArgument> { - self.arguments + pub fn all_args(&self) -> &[FormatArgument] { + &self.arguments[..] + } + + pub fn all_args_mut(&mut self) -> &mut [FormatArgument] { + &mut self.arguments[..] } } -#[derive(Clone, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct FormatArgument { pub kind: FormatArgumentKind, pub expr: P<Expr>, } -#[derive(Clone, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum FormatArgumentKind { /// `format_args(…, arg)` Normal, @@ -152,7 +162,7 @@ impl FormatArgumentKind { } } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)] pub struct FormatPlaceholder { /// Index into [`FormatArgs::arguments`]. pub argument: FormatArgPosition, @@ -164,7 +174,7 @@ pub struct FormatPlaceholder { pub format_options: FormatOptions, } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)] pub struct FormatArgPosition { /// Which argument this position refers to (Ok), /// or would've referred to if it existed (Err). @@ -175,7 +185,7 @@ pub struct FormatArgPosition { pub span: Option<Span>, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)] pub enum FormatArgPositionKind { /// `{}` or `{:.*}` Implicit, @@ -185,7 +195,7 @@ pub enum FormatArgPositionKind { Named, } -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq, Hash)] pub enum FormatTrait { /// `{}` Display, @@ -207,7 +217,7 @@ pub enum FormatTrait { UpperHex, } -#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[derive(Clone, Encodable, Decodable, Default, Debug, PartialEq, Eq)] pub struct FormatOptions { /// The width. E.g. `{:5}` or `{:width$}`. pub width: Option<FormatCount>, @@ -217,11 +227,33 @@ pub struct FormatOptions { pub alignment: Option<FormatAlignment>, /// The fill character. E.g. the `.` in `{:.>10}`. pub fill: Option<char>, - /// The `+`, `-`, `0`, `#`, `x?` and `X?` flags. - pub flags: u32, + /// The `+` or `-` flag. + pub sign: Option<FormatSign>, + /// The `#` flag. + pub alternate: bool, + /// The `0` flag. E.g. the `0` in `{:02x}`. + pub zero_pad: bool, + /// The `x` or `X` flag (for `Debug` only). E.g. the `x` in `{:x?}`. + pub debug_hex: Option<FormatDebugHex>, +} + +#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)] +pub enum FormatSign { + /// The `+` flag. + Plus, + /// The `-` flag. + Minus, +} + +#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)] +pub enum FormatDebugHex { + /// The `x` flag in `{:x?}`. + Lower, + /// The `X` flag in `{:X?}`. + Upper, } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)] pub enum FormatAlignment { /// `{:<}` Left, @@ -231,7 +263,7 @@ pub enum FormatAlignment { Center, } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)] pub enum FormatCount { /// `{:5}` or `{:.5}` Literal(usize), diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 9f4bbbc62..e93a23394 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -1,7 +1,11 @@ use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; -use rustc_ast::Expr; +use rustc_ast::{ + Expr, ExprKind, FormatAlignment, FormatArgPosition, FormatArgPositionKind, FormatArgs, + FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArguments, FormatCount, + FormatDebugHex, FormatOptions, FormatPlaceholder, FormatSign, FormatTrait, +}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, Applicability, MultiSpan, PResult}; use rustc_expand::base::{self, *}; @@ -12,21 +16,15 @@ use rustc_span::{BytePos, InnerSpan, Span}; use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId}; -mod ast; -use ast::*; - -mod expand; -use expand::expand_parsed_format_args; - // The format_args!() macro is expanded in three steps: // 1. First, `parse_args` will parse the `(literal, arg, arg, name=arg, name=arg)` syntax, // but doesn't parse the template (the literal) itself. // 2. Second, `make_format_args` will parse the template, the format options, resolve argument references, -// produce diagnostics, and turn the whole thing into a `FormatArgs` structure. -// 3. Finally, `expand_parsed_format_args` will turn that `FormatArgs` structure -// into the expression that the macro expands to. +// produce diagnostics, and turn the whole thing into a `FormatArgs` AST node. +// 3. Much later, in AST lowering (rustc_ast_lowering), that `FormatArgs` structure will be turned +// into the expression of type `core::fmt::Arguments`. -// See format/ast.rs for the FormatArgs structure and glossary. +// See rustc_ast/src/format.rs for the FormatArgs structure and glossary. // Only used in parse_args and report_invalid_references, // to indicate how a referred argument was used. @@ -437,7 +435,16 @@ pub fn make_format_args( format_options: FormatOptions { fill: format.fill, alignment, - flags: format.flags, + sign: format.sign.map(|s| match s { + parse::Sign::Plus => FormatSign::Plus, + parse::Sign::Minus => FormatSign::Minus, + }), + alternate: format.alternate, + zero_pad: format.zero_pad, + debug_hex: format.debug_hex.map(|s| match s { + parse::DebugHex::Lower => FormatDebugHex::Lower, + parse::DebugHex::Upper => FormatDebugHex::Upper, + }), precision, width, }, @@ -850,7 +857,7 @@ fn expand_format_args_impl<'cx>( match parse_args(ecx, sp, tts) { Ok((efmt, args)) => { if let Ok(format_args) = make_format_args(ecx, efmt, args, nl) { - MacEager::expr(expand_parsed_format_args(ecx, format_args)) + MacEager::expr(ecx.expr(sp, ExprKind::FormatArgs(P(format_args)))) } else { MacEager::expr(DummyResult::raw_expr(sp, true)) } diff --git a/compiler/rustc_builtin_macros/src/format/expand.rs b/compiler/rustc_builtin_macros/src/format/expand.rs deleted file mode 100644 index 9dde5efcb..000000000 --- a/compiler/rustc_builtin_macros/src/format/expand.rs +++ /dev/null @@ -1,353 +0,0 @@ -use super::*; -use rustc_ast as ast; -use rustc_ast::visit::{self, Visitor}; -use rustc_ast::{BlockCheckMode, UnsafeSource}; -use rustc_data_structures::fx::FxIndexSet; -use rustc_span::{sym, symbol::kw}; - -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -enum ArgumentType { - Format(FormatTrait), - Usize, -} - -fn make_argument(ecx: &ExtCtxt<'_>, sp: Span, arg: P<ast::Expr>, ty: ArgumentType) -> P<ast::Expr> { - // Generate: - // ::core::fmt::ArgumentV1::new_…(arg) - use ArgumentType::*; - use FormatTrait::*; - ecx.expr_call_global( - sp, - ecx.std_path(&[ - sym::fmt, - sym::ArgumentV1, - match ty { - Format(Display) => sym::new_display, - Format(Debug) => sym::new_debug, - Format(LowerExp) => sym::new_lower_exp, - Format(UpperExp) => sym::new_upper_exp, - Format(Octal) => sym::new_octal, - Format(Pointer) => sym::new_pointer, - Format(Binary) => sym::new_binary, - Format(LowerHex) => sym::new_lower_hex, - Format(UpperHex) => sym::new_upper_hex, - Usize => sym::from_usize, - }, - ]), - vec![arg], - ) -} - -fn make_count( - ecx: &ExtCtxt<'_>, - sp: Span, - count: &Option<FormatCount>, - argmap: &mut FxIndexSet<(usize, ArgumentType)>, -) -> P<ast::Expr> { - // Generate: - // ::core::fmt::rt::v1::Count::…(…) - match count { - Some(FormatCount::Literal(n)) => ecx.expr_call_global( - sp, - ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Is]), - vec![ecx.expr_usize(sp, *n)], - ), - Some(FormatCount::Argument(arg)) => { - if let Ok(arg_index) = arg.index { - let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize)); - ecx.expr_call_global( - sp, - ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Param]), - vec![ecx.expr_usize(sp, i)], - ) - } else { - DummyResult::raw_expr(sp, true) - } - } - None => ecx.expr_path(ecx.path_global( - sp, - ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Implied]), - )), - } -} - -fn make_format_spec( - ecx: &ExtCtxt<'_>, - sp: Span, - placeholder: &FormatPlaceholder, - argmap: &mut FxIndexSet<(usize, ArgumentType)>, -) -> P<ast::Expr> { - // Generate: - // ::core::fmt::rt::v1::Argument { - // position: 0usize, - // format: ::core::fmt::rt::v1::FormatSpec { - // fill: ' ', - // align: ::core::fmt::rt::v1::Alignment::Unknown, - // flags: 0u32, - // precision: ::core::fmt::rt::v1::Count::Implied, - // width: ::core::fmt::rt::v1::Count::Implied, - // }, - // } - let position = match placeholder.argument.index { - Ok(arg_index) => { - let (i, _) = - argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait))); - ecx.expr_usize(sp, i) - } - Err(_) => DummyResult::raw_expr(sp, true), - }; - let fill = ecx.expr_char(sp, placeholder.format_options.fill.unwrap_or(' ')); - let align = ecx.expr_path(ecx.path_global( - sp, - ecx.std_path(&[ - sym::fmt, - sym::rt, - sym::v1, - sym::Alignment, - match placeholder.format_options.alignment { - Some(FormatAlignment::Left) => sym::Left, - Some(FormatAlignment::Right) => sym::Right, - Some(FormatAlignment::Center) => sym::Center, - None => sym::Unknown, - }, - ]), - )); - let flags = ecx.expr_u32(sp, placeholder.format_options.flags); - let prec = make_count(ecx, sp, &placeholder.format_options.precision, argmap); - let width = make_count(ecx, sp, &placeholder.format_options.width, argmap); - ecx.expr_struct( - sp, - ecx.path_global(sp, ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Argument])), - vec![ - ecx.field_imm(sp, Ident::new(sym::position, sp), position), - ecx.field_imm( - sp, - Ident::new(sym::format, sp), - ecx.expr_struct( - sp, - ecx.path_global( - sp, - ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::FormatSpec]), - ), - vec![ - ecx.field_imm(sp, Ident::new(sym::fill, sp), fill), - ecx.field_imm(sp, Ident::new(sym::align, sp), align), - ecx.field_imm(sp, Ident::new(sym::flags, sp), flags), - ecx.field_imm(sp, Ident::new(sym::precision, sp), prec), - ecx.field_imm(sp, Ident::new(sym::width, sp), width), - ], - ), - ), - ], - ) -} - -pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P<ast::Expr> { - let macsp = ecx.with_def_site_ctxt(ecx.call_site()); - - let lit_pieces = ecx.expr_array_ref( - fmt.span, - fmt.template - .iter() - .enumerate() - .filter_map(|(i, piece)| match piece { - &FormatArgsPiece::Literal(s) => Some(ecx.expr_str(fmt.span, s)), - &FormatArgsPiece::Placeholder(_) => { - // Inject empty string before placeholders when not already preceded by a literal piece. - if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) { - Some(ecx.expr_str(fmt.span, kw::Empty)) - } else { - None - } - } - }) - .collect(), - ); - - // Whether we'll use the `Arguments::new_v1_formatted` form (true), - // or the `Arguments::new_v1` form (false). - let mut use_format_options = false; - - // Create a list of all _unique_ (argument, format trait) combinations. - // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)] - let mut argmap = FxIndexSet::default(); - for piece in &fmt.template { - let FormatArgsPiece::Placeholder(placeholder) = piece else { continue }; - if placeholder.format_options != Default::default() { - // Can't use basic form if there's any formatting options. - use_format_options = true; - } - if let Ok(index) = placeholder.argument.index { - if !argmap.insert((index, ArgumentType::Format(placeholder.format_trait))) { - // Duplicate (argument, format trait) combination, - // which we'll only put once in the args array. - use_format_options = true; - } - } - } - - let format_options = use_format_options.then(|| { - // Generate: - // &[format_spec_0, format_spec_1, format_spec_2] - ecx.expr_array_ref( - macsp, - fmt.template - .iter() - .filter_map(|piece| { - let FormatArgsPiece::Placeholder(placeholder) = piece else { return None }; - Some(make_format_spec(ecx, macsp, placeholder, &mut argmap)) - }) - .collect(), - ) - }); - - let arguments = fmt.arguments.into_vec(); - - // If the args array contains exactly all the original arguments once, - // in order, we can use a simple array instead of a `match` construction. - // However, if there's a yield point in any argument except the first one, - // we don't do this, because an ArgumentV1 cannot be kept across yield points. - let use_simple_array = argmap.len() == arguments.len() - && argmap.iter().enumerate().all(|(i, &(j, _))| i == j) - && arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr)); - - let args = if use_simple_array { - // Generate: - // &[ - // ::core::fmt::ArgumentV1::new_display(&arg0), - // ::core::fmt::ArgumentV1::new_lower_hex(&arg1), - // ::core::fmt::ArgumentV1::new_debug(&arg2), - // ] - ecx.expr_array_ref( - macsp, - arguments - .into_iter() - .zip(argmap) - .map(|(arg, (_, ty))| { - let sp = arg.expr.span.with_ctxt(macsp.ctxt()); - make_argument(ecx, sp, ecx.expr_addr_of(sp, arg.expr), ty) - }) - .collect(), - ) - } else { - // Generate: - // match (&arg0, &arg1, &arg2) { - // args => &[ - // ::core::fmt::ArgumentV1::new_display(args.0), - // ::core::fmt::ArgumentV1::new_lower_hex(args.1), - // ::core::fmt::ArgumentV1::new_debug(args.0), - // ] - // } - let args_ident = Ident::new(sym::args, macsp); - let args = argmap - .iter() - .map(|&(arg_index, ty)| { - if let Some(arg) = arguments.get(arg_index) { - let sp = arg.expr.span.with_ctxt(macsp.ctxt()); - make_argument( - ecx, - sp, - ecx.expr_field( - sp, - ecx.expr_ident(macsp, args_ident), - Ident::new(sym::integer(arg_index), macsp), - ), - ty, - ) - } else { - DummyResult::raw_expr(macsp, true) - } - }) - .collect(); - ecx.expr_addr_of( - macsp, - ecx.expr_match( - macsp, - ecx.expr_tuple( - macsp, - arguments - .into_iter() - .map(|arg| { - ecx.expr_addr_of(arg.expr.span.with_ctxt(macsp.ctxt()), arg.expr) - }) - .collect(), - ), - vec![ecx.arm(macsp, ecx.pat_ident(macsp, args_ident), ecx.expr_array(macsp, args))], - ), - ) - }; - - if let Some(format_options) = format_options { - // Generate: - // ::core::fmt::Arguments::new_v1_formatted( - // lit_pieces, - // args, - // format_options, - // unsafe { ::core::fmt::UnsafeArg::new() } - // ) - ecx.expr_call_global( - macsp, - ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1_formatted]), - vec![ - lit_pieces, - args, - format_options, - ecx.expr_block(P(ast::Block { - stmts: vec![ecx.stmt_expr(ecx.expr_call_global( - macsp, - ecx.std_path(&[sym::fmt, sym::UnsafeArg, sym::new]), - Vec::new(), - ))], - id: ast::DUMMY_NODE_ID, - rules: BlockCheckMode::Unsafe(UnsafeSource::CompilerGenerated), - span: macsp, - tokens: None, - could_be_bare_literal: false, - })), - ], - ) - } else { - // Generate: - // ::core::fmt::Arguments::new_v1( - // lit_pieces, - // args, - // ) - ecx.expr_call_global( - macsp, - ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1]), - vec![lit_pieces, args], - ) - } -} - -fn may_contain_yield_point(e: &ast::Expr) -> bool { - struct MayContainYieldPoint(bool); - - impl Visitor<'_> for MayContainYieldPoint { - fn visit_expr(&mut self, e: &ast::Expr) { - if let ast::ExprKind::Await(_) | ast::ExprKind::Yield(_) = e.kind { - self.0 = true; - } else { - visit::walk_expr(self, e); - } - } - - fn visit_mac_call(&mut self, _: &ast::MacCall) { - self.0 = true; - } - - fn visit_attribute(&mut self, _: &ast::Attribute) { - // Conservatively assume this may be a proc macro attribute in - // expression position. - self.0 = true; - } - - fn visit_item(&mut self, _: &ast::Item) { - // Do not recurse into nested items. - } - } - - let mut visitor = MayContainYieldPoint(false); - visitor.visit_expr(e); - visitor.0 -} |