diff --git a/compiler/rustc_builtin_macros/src/format/expand.rs b/compiler/rustc_builtin_macros/src/format/expand.rs index d1c114a67ac..a2a8213dafc 100644 --- a/compiler/rustc_builtin_macros/src/format/expand.rs +++ b/compiler/rustc_builtin_macros/src/format/expand.rs @@ -164,18 +164,32 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P [(0, Display), (0, LowerHex), (1, Display)] - let mut argmap = FxIndexSet::default(); + // 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] - let format_options = ecx.expr_array_ref( + ecx.expr_array_ref( macsp, fmt.template .iter() @@ -184,34 +198,18 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P [(0, Display), (0, LowerHex), (0, Display), (1, Display)] - let args = fmt - .template - .iter() - .filter_map(|piece| { - let FormatArgsPiece::Placeholder(placeholder) = piece else { return None }; - Some(( - placeholder.argument.index.ok()?, - ArgumentType::Format(placeholder.format_trait), - )) - }) - .collect(); - (args, None) - }; + ) + }); // 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 = args.len() == fmt.arguments.len() - && args.iter().enumerate().all(|(i, &(j, _))| i == j) + let use_simple_array = argmap.len() == fmt.arguments.len() + && argmap.iter().enumerate().all(|(i, &(j, _))| i == j) && fmt.arguments.iter().skip(1).all(|(arg, _)| !may_contain_yield_point(arg)); - let args_expr = if use_simple_array { + let args = if use_simple_array { // Generate: // &[ // ::core::fmt::ArgumentV1::new_display(&arg0), @@ -222,7 +220,7 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P, fmt: FormatArgs) -> P, fmt: FormatArgs) -> P, fmt: FormatArgs) -> P, fmt: FormatArgs) -> P