Rollup merge of #142755 - aDotInTheVoid:rdj-shattrs, r=GuillaumeGomez
rustdoc: Remove `FormatRenderer::cache`
We only called it it one place, which isn't generic and can be replaced with a field access.
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 718edad..f297bf9 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -2289,12 +2289,12 @@ pub(super) fn expr_array_ref(
span: Span,
elements: &'hir [hir::Expr<'hir>],
) -> hir::Expr<'hir> {
- let addrof = hir::ExprKind::AddrOf(
- hir::BorrowKind::Ref,
- hir::Mutability::Not,
- self.arena.alloc(self.expr(span, hir::ExprKind::Array(elements))),
- );
- self.expr(span, addrof)
+ let array = self.arena.alloc(self.expr(span, hir::ExprKind::Array(elements)));
+ self.expr_ref(span, array)
+ }
+
+ pub(super) fn expr_ref(&mut self, span: Span, expr: &'hir hir::Expr<'hir>) -> hir::Expr<'hir> {
+ self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr))
}
pub(super) fn expr(&mut self, span: Span, kind: hir::ExprKind<'hir>) -> hir::Expr<'hir> {
diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs
index 17b443b..12f0af7 100644
--- a/compiler/rustc_ast_lowering/src/format.rs
+++ b/compiler/rustc_ast_lowering/src/format.rs
@@ -1,7 +1,5 @@
-use core::ops::ControlFlow;
use std::borrow::Cow;
-use rustc_ast::visit::Visitor;
use rustc_ast::*;
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir as hir;
@@ -476,77 +474,52 @@ fn expand_format_args<'hir>(
return hir::ExprKind::Call(new, new_args);
}
- // 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 Argument cannot be kept across yield points.
- //
- // This is an optimization, speeding up compilation about 1-2% in some cases.
- // See https://github.com/rust-lang/rust/pull/106770#issuecomment-1380790609
- 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 arguments.is_empty() {
+ let (let_statements, args) = if arguments.is_empty() {
// Generate:
- // &<core::fmt::Argument>::none()
+ // []
+ (vec![], ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(&[]))))
+ } else if argmap.len() == 1 && arguments.len() == 1 {
+ // Only one argument, so we don't need to make the `args` tuple.
//
- // Note:
- // `none()` just returns `[]`. We use `none()` rather than `[]` to limit the lifetime.
- //
- // This makes sure that this still fails to compile, even when the argument is inlined:
- //
- // ```
- // let f = format_args!("{}", "a");
- // println!("{f}"); // error E0716
- // ```
- //
- // Cases where keeping the object around is allowed, such as `format_args!("a")`,
- // are handled above by the `allow_const` case.
- let none_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
- macsp,
- hir::LangItem::FormatArgument,
- sym::none,
- ));
- let none = ctx.expr_call(macsp, none_fn, &[]);
- ctx.expr(macsp, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, none))
- } else if use_simple_array {
// Generate:
- // &[
- // <core::fmt::Argument>::new_display(&arg0),
- // <core::fmt::Argument>::new_lower_hex(&arg1),
- // <core::fmt::Argument>::new_debug(&arg2),
- // …
- // ]
- let elements = ctx.arena.alloc_from_iter(arguments.iter().zip(argmap).map(
- |(arg, ((_, ty), placeholder_span))| {
+ // super let args = [<core::fmt::Argument>::new_display(&arg)];
+ let args = ctx.arena.alloc_from_iter(argmap.iter().map(
+ |(&(arg_index, ty), &placeholder_span)| {
+ let arg = &arguments[arg_index];
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(ctx.expr_ref(arg.span.with_ctxt(macsp.ctxt()), arg));
make_argument(ctx, placeholder_span, ref_arg, ty)
},
));
- ctx.expr_array_ref(macsp, elements)
- } else {
- // Generate:
- // &match (&arg0, &arg1, &…) {
- // args => [
- // <core::fmt::Argument>::new_display(args.0),
- // <core::fmt::Argument>::new_lower_hex(args.1),
- // <core::fmt::Argument>::new_debug(args.0),
- // …
- // ]
- // }
+ let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
let args_ident = Ident::new(sym::args, macsp);
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
+ let let_statement = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
+ (vec![let_statement], ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)))
+ } else {
+ // Generate:
+ // super let args = (&arg0, &arg1, &…);
+ let args_ident = Ident::new(sym::args, macsp);
+ let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
+ let elements = ctx.arena.alloc_from_iter(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),
+ )
+ }));
+ let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
+ let let_statement_1 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args_tuple));
+
+ // Generate:
+ // super let args = [
+ // <core::fmt::Argument>::new_display(args.0),
+ // <core::fmt::Argument>::new_lower_hex(args.1),
+ // <core::fmt::Argument>::new_debug(args.0),
+ // …
+ // ];
let args = ctx.arena.alloc_from_iter(argmap.iter().map(
|(&(arg_index, ty), &placeholder_span)| {
let arg = &arguments[arg_index];
@@ -567,58 +540,47 @@ fn expand_format_args<'hir>(
make_argument(ctx, placeholder_span, arg, ty)
},
));
- let elements = ctx.arena.alloc_from_iter(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),
- )
- }));
- 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(
- macsp,
- args_tuple,
- match_arms,
- hir::MatchSource::FormatArgs,
- ));
- ctx.expr(
- macsp,
- hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, match_expr),
+ let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
+ let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
+ let let_statement_2 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
+ (
+ vec![let_statement_1, let_statement_2],
+ ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)),
)
};
- if let Some(format_options) = format_options {
+ // Generate:
+ // &args
+ let args = ctx.expr_ref(macsp, args);
+
+ let call = if let Some(format_options) = format_options {
// Generate:
- // <core::fmt::Arguments>::new_v1_formatted(
- // lit_pieces,
- // args,
- // format_options,
- // unsafe { ::core::fmt::UnsafeArg::new() }
- // )
+ // unsafe {
+ // <core::fmt::Arguments>::new_v1_formatted(
+ // lit_pieces,
+ // args,
+ // format_options,
+ // )
+ // }
let new_v1_formatted = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
macsp,
hir::LangItem::FormatArguments,
sym::new_v1_formatted,
));
- let unsafe_arg_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
- macsp,
- hir::LangItem::FormatUnsafeArg,
- sym::new,
- ));
- let unsafe_arg_new_call = ctx.expr_call(macsp, unsafe_arg_new, &[]);
+ let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options]);
+ let call = ctx.expr_call(macsp, new_v1_formatted, args);
let hir_id = ctx.next_id();
- let unsafe_arg = ctx.expr_block(ctx.arena.alloc(hir::Block {
- stmts: &[],
- expr: Some(unsafe_arg_new_call),
- hir_id,
- rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
- span: macsp,
- targeted_by_break: false,
- }));
- let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options, unsafe_arg]);
- hir::ExprKind::Call(new_v1_formatted, args)
+ hir::ExprKind::Block(
+ ctx.arena.alloc(hir::Block {
+ stmts: &[],
+ expr: Some(call),
+ hir_id,
+ rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
+ span: macsp,
+ targeted_by_break: false,
+ }),
+ None,
+ )
} else {
// Generate:
// <core::fmt::Arguments>::new_v1(
@@ -632,37 +594,23 @@ fn expand_format_args<'hir>(
));
let new_args = ctx.arena.alloc_from_iter([lit_pieces, args]);
hir::ExprKind::Call(new_v1, new_args)
+ };
+
+ if !let_statements.is_empty() {
+ // Generate:
+ // {
+ // super let …
+ // super let …
+ // <core::fmt::Arguments>::new_…(…)
+ // }
+ let call = ctx.arena.alloc(ctx.expr(macsp, call));
+ let block = ctx.block_all(macsp, ctx.arena.alloc_from_iter(let_statements), Some(call));
+ hir::ExprKind::Block(block, None)
+ } else {
+ call
}
}
-fn may_contain_yield_point(e: &ast::Expr) -> bool {
- struct MayContainYieldPoint;
-
- impl Visitor<'_> for MayContainYieldPoint {
- type Result = ControlFlow<()>;
-
- fn visit_expr(&mut self, e: &ast::Expr) -> ControlFlow<()> {
- if let ast::ExprKind::Await(_, _) | ast::ExprKind::Yield(_) = e.kind {
- ControlFlow::Break(())
- } else {
- visit::walk_expr(self, e)
- }
- }
-
- fn visit_mac_call(&mut self, _: &ast::MacCall) -> ControlFlow<()> {
- // Macros should be expanded at this point.
- unreachable!("unexpanded macro in ast lowering");
- }
-
- fn visit_item(&mut self, _: &ast::Item) -> ControlFlow<()> {
- // Do not recurse into nested items.
- ControlFlow::Continue(())
- }
- }
-
- MayContainYieldPoint.visit_expr(e).is_break()
-}
-
fn for_all_argument_indexes(template: &mut [FormatArgsPiece], mut f: impl FnMut(&mut usize)) {
for piece in template {
let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index e74fd1d..26d7c0c 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -2292,6 +2292,26 @@ fn stmt_let_pat(
self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
}
+ fn stmt_super_let_pat(
+ &mut self,
+ span: Span,
+ pat: &'hir hir::Pat<'hir>,
+ init: Option<&'hir hir::Expr<'hir>>,
+ ) -> hir::Stmt<'hir> {
+ let hir_id = self.next_id();
+ let local = hir::LetStmt {
+ super_: Some(span),
+ hir_id,
+ init,
+ pat,
+ els: None,
+ source: hir::LocalSource::Normal,
+ span: self.lower_span(span),
+ ty: None,
+ };
+ self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
+ }
+
fn block_expr(&mut self, expr: &'hir hir::Expr<'hir>) -> &'hir hir::Block<'hir> {
self.block_all(expr.span, &[], Some(expr))
}
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index cdc01dc..9ebf4c1 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -38,7 +38,8 @@ pub enum InstructionSetAttr {
ArmT32,
}
-#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic, Default)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, PrintAttribute)]
+#[derive(Encodable, Decodable, HashStable_Generic)]
pub enum OptimizeAttr {
/// No `#[optimize(..)]` attribute
#[default]
@@ -201,6 +202,9 @@ pub enum AttributeKind {
span: Span,
},
+ /// Represents `#[cold]`.
+ Cold(Span),
+
/// Represents `#[rustc_confusables]`.
Confusables {
symbols: ThinVec<Symbol>,
@@ -229,7 +233,8 @@ pub enum AttributeKind {
/// Represents `#[rustc_macro_transparency]`.
MacroTransparency(Transparency),
-
+ /// Represents `#[optimize(size|speed)]`
+ Optimize(OptimizeAttr, Span),
/// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
Repr(ThinVec<(ReprAttr, Span)>),
diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
new file mode 100644
index 0000000..1b03525
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
@@ -0,0 +1,58 @@
+use rustc_attr_data_structures::{AttributeKind, OptimizeAttr};
+use rustc_feature::{AttributeTemplate, template};
+use rustc_span::sym;
+
+use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
+use crate::context::{AcceptContext, Stage};
+use crate::parser::ArgParser;
+
+pub(crate) struct OptimizeParser;
+
+impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
+ const PATH: &[rustc_span::Symbol] = &[sym::optimize];
+ const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
+ const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
+ const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none");
+
+ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+ let Some(list) = args.list() else {
+ cx.expected_list(cx.attr_span);
+ return None;
+ };
+
+ let Some(single) = list.single() else {
+ cx.expected_single_argument(list.span);
+ return None;
+ };
+
+ let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
+ Some(sym::size) => OptimizeAttr::Size,
+ Some(sym::speed) => OptimizeAttr::Speed,
+ Some(sym::none) => OptimizeAttr::DoNotOptimize,
+ _ => {
+ cx.expected_specific_argument(single.span(), vec!["size", "speed", "none"]);
+ OptimizeAttr::Default
+ }
+ };
+
+ Some(AttributeKind::Optimize(res, cx.attr_span))
+ }
+}
+
+pub(crate) struct ColdParser;
+
+impl<S: Stage> SingleAttributeParser<S> for ColdParser {
+ const PATH: &[rustc_span::Symbol] = &[sym::cold];
+ const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
+ const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
+ const TEMPLATE: AttributeTemplate = template!(Word);
+
+ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+ if !args.no_args() {
+ cx.expected_no_args(args.span().unwrap_or(cx.attr_span));
+ return None;
+ };
+
+ Some(AttributeKind::Cold(cx.attr_span))
+ }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
index fa2a608..78a696a 100644
--- a/compiler/rustc_attr_parsing/src/attributes/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -28,6 +28,7 @@
pub(crate) mod allow_unstable;
pub(crate) mod cfg;
+pub(crate) mod codegen_attrs;
pub(crate) mod confusables;
pub(crate) mod deprecation;
pub(crate) mod inline;
@@ -86,8 +87,19 @@ pub(crate) trait AttributeParser<S: Stage>: Default + 'static {
/// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple
/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
pub(crate) trait SingleAttributeParser<S: Stage>: 'static {
+ /// The single path of the attribute this parser accepts.
+ ///
+ /// If you need the parser to accept more than one path, use [`AttributeParser`] instead
const PATH: &[Symbol];
+
+ /// Configures the precedence of attributes with the same `PATH` on a syntax node.
const ATTRIBUTE_ORDER: AttributeOrder;
+
+ /// Configures what to do when when the same attribute is
+ /// applied more than once on the same syntax node.
+ ///
+ /// [`ATTRIBUTE_ORDER`](Self::ATTRIBUTE_ORDER) specified which one is assumed to be correct,
+ /// and this specified whether to, for example, warn or error on the other one.
const ON_DUPLICATE: OnDuplicate<S>;
/// The template this attribute parser should implement. Used for diagnostics.
@@ -97,6 +109,8 @@ pub(crate) trait SingleAttributeParser<S: Stage>: 'static {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind>;
}
+/// Use in combination with [`SingleAttributeParser`].
+/// `Single<T: SingleAttributeParser>` implements [`AttributeParser`].
pub(crate) struct Single<T: SingleAttributeParser<S>, S: Stage>(
PhantomData<(S, T)>,
Option<(AttributeKind, Span)>,
@@ -229,6 +243,10 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
const PATH: &[rustc_span::Symbol];
type Item;
+ /// A function that converts individual items (of type [`Item`](Self::Item)) into the final attribute.
+ ///
+ /// For example, individual representations fomr `#[repr(...)]` attributes into an `AttributeKind::Repr(x)`,
+ /// where `x` is a vec of these individual reprs.
const CONVERT: ConvertFn<Self::Item>;
/// The template this attribute parser should implement. Used for diagnostics.
@@ -241,6 +259,8 @@ fn extend<'c>(
) -> impl IntoIterator<Item = Self::Item> + 'c;
}
+/// Use in combination with [`CombineAttributeParser`].
+/// `Combine<T: CombineAttributeParser>` implements [`AttributeParser`].
pub(crate) struct Combine<T: CombineAttributeParser<S>, S: Stage>(
PhantomData<(S, T)>,
ThinVec<<T as CombineAttributeParser<S>>::Item>,
diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs
index c9f9f34..a31ca8f 100644
--- a/compiler/rustc_attr_parsing/src/attributes/repr.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs
@@ -25,7 +25,8 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
const PATH: &[Symbol] = &[sym::repr];
const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
// FIXME(jdonszelmann): never used
- const TEMPLATE: AttributeTemplate = template!(List: "C");
+ const TEMPLATE: AttributeTemplate =
+ template!(List: "C | Rust | align(...) | packed(...) | <integer type> | transparent");
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index d757063..648bd43 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -15,6 +15,7 @@
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
+use crate::attributes::codegen_attrs::{ColdParser, OptimizeParser};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
@@ -105,9 +106,11 @@ mod late {
// tidy-alphabetical-start
Single<AsPtrParser>,
+ Single<ColdParser>,
Single<ConstStabilityIndirectParser>,
Single<DeprecationParser>,
Single<InlineParser>,
+ Single<OptimizeParser>,
Single<RustcForceInlineParser>,
Single<TransparencyParser>,
// tidy-alphabetical-end
@@ -232,6 +235,16 @@ pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed {
})
}
+ pub(crate) fn expected_no_args(&self, args_span: Span) -> ErrorGuaranteed {
+ self.emit_err(AttributeParseError {
+ span: args_span,
+ attr_span: self.attr_span,
+ template: self.template.clone(),
+ attribute: self.attr_path.clone(),
+ reason: AttributeParseErrorReason::ExpectedNoArgs,
+ })
+ }
+
/// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
/// a nicer error message talking about the specific name that was found lacking a value.
pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed {
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 337921a..29f2e44 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -474,6 +474,7 @@ pub(crate) struct UnrecognizedReprHint {
}
pub(crate) enum AttributeParseErrorReason {
+ ExpectedNoArgs,
ExpectedStringLiteral { byte_string: Option<Span> },
ExpectedSingleArgument,
ExpectedList,
@@ -529,6 +530,10 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
diag.span_label(self.span, format!("didn't expect a literal here"));
diag.code(E0565);
}
+ AttributeParseErrorReason::ExpectedNoArgs => {
+ diag.span_label(self.span, format!("didn't expect any arguments here"));
+ diag.code(E0565);
+ }
AttributeParseErrorReason::ExpectedNameValue(None) => {
diag.span_label(
self.span,
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 34d3684..98dc898 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -3201,14 +3201,6 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
let expr_ty: Option<Ty<'_>> =
visitor.prop_expr.map(|expr| typeck_results.expr_ty(expr).peel_refs());
- let is_format_arguments_item = if let Some(expr_ty) = expr_ty
- && let ty::Adt(adt, _) = expr_ty.kind()
- {
- self.infcx.tcx.is_lang_item(adt.did(), LangItem::FormatArguments)
- } else {
- false
- };
-
if visitor.found == 0
&& stmt.span.contains(proper_span)
&& let Some(p) = sm.span_to_margin(stmt.span)
@@ -3236,25 +3228,17 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
""
};
- if !is_format_arguments_item {
- let addition = format!(
- "let {}binding = {};\n{}",
- mutability,
- s,
- " ".repeat(p)
- );
- err.multipart_suggestion_verbose(
- msg,
- vec![
- (stmt.span.shrink_to_lo(), addition),
- (proper_span, "binding".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- } else {
- err.note("the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used");
- err.note("to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>");
- }
+ let addition =
+ format!("let {}binding = {};\n{}", mutability, s, " ".repeat(p));
+ err.multipart_suggestion_verbose(
+ msg,
+ vec![
+ (stmt.span.shrink_to_lo(), addition),
+ (proper_span, "binding".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+
suggested = true;
break;
}
diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl
index 18a8a5a..55a28bc 100644
--- a/compiler/rustc_codegen_gcc/messages.ftl
+++ b/compiler/rustc_codegen_gcc/messages.ftl
@@ -1,7 +1,3 @@
-codegen_gcc_unknown_ctarget_feature_prefix =
- unknown feature specified for `-Ctarget-feature`: `{$feature}`
- .note = features must begin with a `+` to enable or `-` to disable it
-
codegen_gcc_unwinding_inline_asm =
GCC backend does not support unwinding from inline asm
@@ -16,15 +12,3 @@
codegen_gcc_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto`
codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err})
-
-codegen_gcc_unknown_ctarget_feature =
- unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}`
- .note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future
- .possible_feature = you might have meant: `{$rust_feature}`
- .consider_filing_feature_request = consider filing a feature request
-
-codegen_gcc_missing_features =
- add the missing features in a `target_feature` attribute
-
-codegen_gcc_target_feature_disable_or_enable =
- the target features {$features} must all be either enabled or disabled together
diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs
index 7786be9..b7e7343 100644
--- a/compiler/rustc_codegen_gcc/src/errors.rs
+++ b/compiler/rustc_codegen_gcc/src/errors.rs
@@ -1,31 +1,7 @@
-use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_macros::Diagnostic;
use rustc_span::Span;
#[derive(Diagnostic)]
-#[diag(codegen_gcc_unknown_ctarget_feature_prefix)]
-#[note]
-pub(crate) struct UnknownCTargetFeaturePrefix<'a> {
- pub feature: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_unknown_ctarget_feature)]
-#[note]
-pub(crate) struct UnknownCTargetFeature<'a> {
- pub feature: &'a str,
- #[subdiagnostic]
- pub rust_feature: PossibleFeature<'a>,
-}
-
-#[derive(Subdiagnostic)]
-pub(crate) enum PossibleFeature<'a> {
- #[help(codegen_gcc_possible_feature)]
- Some { rust_feature: &'a str },
- #[help(codegen_gcc_consider_filing_feature_request)]
- None,
-}
-
-#[derive(Diagnostic)]
#[diag(codegen_gcc_unwinding_inline_asm)]
pub(crate) struct UnwindingInlineAsm {
#[primary_span]
diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs
index 2e00d5f..42ba406 100644
--- a/compiler/rustc_codegen_gcc/src/gcc_util.rs
+++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs
@@ -1,20 +1,12 @@
#[cfg(feature = "master")]
use gccjit::Context;
-use rustc_codegen_ssa::codegen_attrs::check_tied_features;
-use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::unord::UnordSet;
+use rustc_codegen_ssa::target_features;
use rustc_session::Session;
-use rustc_session::features::{StabilityExt, retpoline_features_by_flags};
-use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES;
use smallvec::{SmallVec, smallvec};
-use crate::errors::{PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix};
-
-fn gcc_features_by_flags(sess: &Session) -> Vec<&str> {
- let mut features: Vec<&str> = Vec::new();
- retpoline_features_by_flags(sess, &mut features);
- features
+fn gcc_features_by_flags(sess: &Session, features: &mut Vec<String>) {
+ target_features::retpoline_features_by_flags(sess, features);
+ // FIXME: LLVM also sets +reserve-x18 here under some conditions.
}
/// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
@@ -44,98 +36,29 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
features.extend(sess.target.features.split(',').filter(|v| !v.is_empty()).map(String::from));
// -Ctarget-features
- let known_features = sess.target.rust_target_features();
- let mut featsmap = FxHashMap::default();
-
- // Compute implied features
- let mut all_rust_features = vec![];
- for feature in sess.opts.cg.target_feature.split(',').chain(gcc_features_by_flags(sess)) {
- if let Some(feature) = feature.strip_prefix('+') {
- all_rust_features.extend(
- UnordSet::from(sess.target.implied_target_features(feature))
- .to_sorted_stable_ord()
- .iter()
- .map(|&&s| (true, s)),
- )
- } else if let Some(feature) = feature.strip_prefix('-') {
- // FIXME: Why do we not remove implied features on "-" here?
- // We do the equivalent above in `target_config`.
- // See <https://github.com/rust-lang/rust/issues/134792>.
- all_rust_features.push((false, feature));
- } else if !feature.is_empty() && diagnostics {
- sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature });
- }
- }
- // Remove features that are meant for rustc, not codegen.
- all_rust_features.retain(|&(_, feature)| {
- // Retain if it is not a rustc feature
- !RUSTC_SPECIFIC_FEATURES.contains(&feature)
- });
-
- // Check feature validity.
- if diagnostics {
- for &(enable, feature) in &all_rust_features {
- let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
- match feature_state {
- None => {
- let rust_feature = known_features.iter().find_map(|&(rust_feature, _, _)| {
- let gcc_features = to_gcc_features(sess, rust_feature);
- if gcc_features.contains(&feature) && !gcc_features.contains(&rust_feature)
- {
- Some(rust_feature)
- } else {
- None
- }
- });
- let unknown_feature = if let Some(rust_feature) = rust_feature {
- UnknownCTargetFeature {
- feature,
- rust_feature: PossibleFeature::Some { rust_feature },
- }
- } else {
- UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
- };
- sess.dcx().emit_warn(unknown_feature);
- }
- Some(&(_, stability, _)) => {
- stability.verify_feature_enabled_by_flag(sess, enable, feature);
- }
- }
-
- // FIXME(nagisa): figure out how to not allocate a full hashset here.
- featsmap.insert(feature, enable);
- }
- }
-
- // Translate this into GCC features.
- let feats =
- all_rust_features.iter().flat_map(|&(enable, feature)| {
- let enable_disable = if enable { '+' } else { '-' };
+ target_features::flag_to_backend_features(
+ sess,
+ diagnostics,
+ |feature| to_gcc_features(sess, feature),
+ |feature, enable| {
// We run through `to_gcc_features` when
// passing requests down to GCC. This means that all in-language
// features also work on the command line instead of having two
// different names when the GCC name and the Rust name differ.
- to_gcc_features(sess, feature)
- .iter()
- .flat_map(|feat| to_gcc_features(sess, feat).into_iter())
- .map(|feature| {
- if enable_disable == '-' {
- format!("-{}", feature)
- } else {
- feature.to_string()
- }
- })
- .collect::<Vec<_>>()
- });
- features.extend(feats);
+ features.extend(
+ to_gcc_features(sess, feature)
+ .iter()
+ .flat_map(|feat| to_gcc_features(sess, feat).into_iter())
+ .map(
+ |feature| {
+ if !enable { format!("-{}", feature) } else { feature.to_string() }
+ },
+ ),
+ );
+ },
+ );
- if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) {
- sess.dcx().emit_err(TargetFeatureDisableOrEnable {
- features: f,
- span: None,
- missing_features: None,
- });
- }
+ gcc_features_by_flags(sess, &mut features);
features
}
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index aa57655..a912678 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -102,6 +102,7 @@
CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn,
};
use rustc_codegen_ssa::base::codegen_crate;
+use rustc_codegen_ssa::target_features::cfg_target_feature;
use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods};
use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig};
use rustc_data_structures::fx::FxIndexMap;
@@ -476,42 +477,21 @@ fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel {
/// Returns the features that should be set in `cfg(target_feature)`.
fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig {
- // TODO(antoyo): use global_gcc_features.
- let f = |allow_unstable| {
- sess.target
- .rust_target_features()
- .iter()
- .filter_map(|&(feature, gate, _)| {
- if allow_unstable
- || (gate.in_cfg()
- && (sess.is_nightly_build() || gate.requires_nightly().is_none()))
- {
- Some(feature)
- } else {
- None
- }
- })
- .filter(|feature| {
- // TODO: we disable Neon for now since we don't support the LLVM intrinsics for it.
- if *feature == "neon" {
- return false;
- }
- target_info.cpu_supports(feature)
- // cSpell:disable
- /*
- adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma,
- avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq,
- bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
- sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves
- */
- // cSpell:enable
- })
- .map(Symbol::intern)
- .collect()
- };
-
- let target_features = f(false);
- let unstable_target_features = f(true);
+ let (unstable_target_features, target_features) = cfg_target_feature(sess, |feature| {
+ // TODO: we disable Neon for now since we don't support the LLVM intrinsics for it.
+ if feature == "neon" {
+ return false;
+ }
+ target_info.cpu_supports(feature)
+ // cSpell:disable
+ /*
+ adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma,
+ avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq,
+ bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
+ sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves
+ */
+ // cSpell:enable
+ });
let has_reliable_f16 = target_info.supports_target_dependent_type(CType::Float16);
let has_reliable_f128 = target_info.supports_target_dependent_type(CType::Float128);
diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl
index 3faeb9b..3885f18 100644
--- a/compiler/rustc_codegen_llvm/messages.ftl
+++ b/compiler/rustc_codegen_llvm/messages.ftl
@@ -59,16 +59,6 @@
codegen_llvm_target_machine = could not create LLVM TargetMachine for triple: {$triple}
codegen_llvm_target_machine_with_llvm_err = could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err}
-codegen_llvm_unknown_ctarget_feature =
- unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}`
- .note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future
- .possible_feature = you might have meant: `{$rust_feature}`
- .consider_filing_feature_request = consider filing a feature request
-
-codegen_llvm_unknown_ctarget_feature_prefix =
- unknown feature specified for `-Ctarget-feature`: `{$feature}`
- .note = features must begin with a `+` to enable or `-` to disable it
-
codegen_llvm_unknown_debuginfo_compression = unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo
codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err}
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index 8bc74fb..d50ad8a 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -3,36 +3,12 @@
use rustc_data_structures::small_c_str::SmallCStr;
use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
-use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_macros::Diagnostic;
use rustc_span::Span;
use crate::fluent_generated as fluent;
#[derive(Diagnostic)]
-#[diag(codegen_llvm_unknown_ctarget_feature_prefix)]
-#[note]
-pub(crate) struct UnknownCTargetFeaturePrefix<'a> {
- pub feature: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_unknown_ctarget_feature)]
-#[note]
-pub(crate) struct UnknownCTargetFeature<'a> {
- pub feature: &'a str,
- #[subdiagnostic]
- pub rust_feature: PossibleFeature<'a>,
-}
-
-#[derive(Subdiagnostic)]
-pub(crate) enum PossibleFeature<'a> {
- #[help(codegen_llvm_possible_feature)]
- Some { rust_feature: &'a str },
- #[help(codegen_llvm_consider_filing_feature_request)]
- None,
-}
-
-#[derive(Diagnostic)]
#[diag(codegen_llvm_symbol_already_defined)]
pub(crate) struct SymbolAlreadyDefined<'a> {
#[primary_span]
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 0e77bc4..6fd07d5 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -6,27 +6,20 @@
use std::{ptr, slice, str};
use libc::c_int;
-use rustc_codegen_ssa::TargetConfig;
use rustc_codegen_ssa::base::wants_wasm_eh;
-use rustc_codegen_ssa::codegen_attrs::check_tied_features;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_codegen_ssa::target_features::cfg_target_feature;
+use rustc_codegen_ssa::{TargetConfig, target_features};
+use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::small_c_str::SmallCStr;
-use rustc_data_structures::unord::UnordSet;
use rustc_fs_util::path_to_c_string;
use rustc_middle::bug;
use rustc_session::Session;
use rustc_session::config::{PrintKind, PrintRequest};
-use rustc_session::features::{StabilityExt, retpoline_features_by_flags};
-use rustc_span::Symbol;
use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport};
-use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES};
use smallvec::{SmallVec, smallvec};
use crate::back::write::create_informational_target_machine;
-use crate::errors::{
- FixedX18InvalidArch, PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix,
-};
-use crate::llvm;
+use crate::{errors, llvm};
static INIT: Once = Once::new();
@@ -195,15 +188,6 @@ fn with_dependencies(
) -> Self {
Self { llvm_feature_name, dependencies }
}
-
- fn contains(&'a self, feat: &str) -> bool {
- self.iter().any(|dep| dep == feat)
- }
-
- fn iter(&'a self) -> impl Iterator<Item = &'a str> {
- let dependencies = self.dependencies.iter().map(|feat| feat.as_str());
- std::iter::once(self.llvm_feature_name).chain(dependencies)
- }
}
impl<'a> IntoIterator for LLVMFeature<'a> {
@@ -216,18 +200,22 @@ fn into_iter(self) -> Self::IntoIter {
}
}
-// WARNING: the features after applying `to_llvm_features` must be known
-// to LLVM or the feature detection code will walk past the end of the feature
-// array, leading to crashes.
-//
-// To find a list of LLVM's names, see llvm-project/llvm/lib/Target/{ARCH}/*.td
-// where `{ARCH}` is the architecture name. Look for instances of `SubtargetFeature`.
-//
-// Check the current rustc fork of LLVM in the repo at https://github.com/rust-lang/llvm-project/.
-// The commit in use can be found via the `llvm-project` submodule in
-// https://github.com/rust-lang/rust/tree/master/src Though note that Rust can also be build with
-// an external precompiled version of LLVM which might lead to failures if the oldest tested /
-// supported LLVM version doesn't yet support the relevant intrinsics.
+/// Convert a Rust feature name to an LLVM feature name. Returning `None` means the
+/// feature should be skipped, usually because it is not supported by the current
+/// LLVM version.
+///
+/// WARNING: the features after applying `to_llvm_features` must be known
+/// to LLVM or the feature detection code will walk past the end of the feature
+/// array, leading to crashes.
+///
+/// To find a list of LLVM's names, see llvm-project/llvm/lib/Target/{ARCH}/*.td
+/// where `{ARCH}` is the architecture name. Look for instances of `SubtargetFeature`.
+///
+/// Check the current rustc fork of LLVM in the repo at
+/// <https://github.com/rust-lang/llvm-project/>. The commit in use can be found via the
+/// `llvm-project` submodule in <https://github.com/rust-lang/rust/tree/master/src> Though note that
+/// Rust can also be build with an external precompiled version of LLVM which might lead to failures
+/// if the oldest tested / supported LLVM version doesn't yet support the relevant intrinsics.
pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFeature<'a>> {
let arch = if sess.target.arch == "x86_64" {
"x86"
@@ -343,98 +331,25 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig {
// the target CPU, that is still expanded to target features (with all their implied features)
// by LLVM.
let target_machine = create_informational_target_machine(sess, true);
- // Compute which of the known target features are enabled in the 'base' target machine. We only
- // consider "supported" features; "forbidden" features are not reflected in `cfg` as of now.
- let mut features: FxHashSet<Symbol> = sess
- .target
- .rust_target_features()
- .iter()
- .filter(|(feature, _, _)| {
- // skip checking special features, as LLVM may not understand them
- if RUSTC_SPECIAL_FEATURES.contains(feature) {
- return true;
- }
- if let Some(feat) = to_llvm_features(sess, feature) {
- for llvm_feature in feat {
- let cstr = SmallCStr::new(llvm_feature);
- // `LLVMRustHasFeature` is moderately expensive. On targets with many
- // features (e.g. x86) these calls take a non-trivial fraction of runtime
- // when compiling very small programs.
- if !unsafe { llvm::LLVMRustHasFeature(target_machine.raw(), cstr.as_ptr()) } {
- return false;
- }
+
+ let (unstable_target_features, target_features) = cfg_target_feature(sess, |feature| {
+ if let Some(feat) = to_llvm_features(sess, feature) {
+ // All the LLVM features this expands to must be enabled.
+ for llvm_feature in feat {
+ let cstr = SmallCStr::new(llvm_feature);
+ // `LLVMRustHasFeature` is moderately expensive. On targets with many
+ // features (e.g. x86) these calls take a non-trivial fraction of runtime
+ // when compiling very small programs.
+ if !unsafe { llvm::LLVMRustHasFeature(target_machine.raw(), cstr.as_ptr()) } {
+ return false;
}
- true
- } else {
- false
}
- })
- .map(|(feature, _, _)| Symbol::intern(feature))
- .collect();
-
- // Add enabled and remove disabled features.
- for (enabled, feature) in
- sess.opts.cg.target_feature.split(',').filter_map(|s| match s.chars().next() {
- Some('+') => Some((true, Symbol::intern(&s[1..]))),
- Some('-') => Some((false, Symbol::intern(&s[1..]))),
- _ => None,
- })
- {
- if enabled {
- // Also add all transitively implied features.
-
- // We don't care about the order in `features` since the only thing we use it for is the
- // `features.contains` below.
- #[allow(rustc::potential_query_instability)]
- features.extend(
- sess.target
- .implied_target_features(feature.as_str())
- .iter()
- .map(|s| Symbol::intern(s)),
- );
+ true
} else {
- // Remove transitively reverse-implied features.
-
- // We don't care about the order in `features` since the only thing we use it for is the
- // `features.contains` below.
- #[allow(rustc::potential_query_instability)]
- features.retain(|f| {
- if sess.target.implied_target_features(f.as_str()).contains(&feature.as_str()) {
- // If `f` if implies `feature`, then `!feature` implies `!f`, so we have to
- // remove `f`. (This is the standard logical contraposition principle.)
- false
- } else {
- // We can keep `f`.
- true
- }
- });
+ false
}
- }
+ });
- // Filter enabled features based on feature gates.
- let f = |allow_unstable| {
- sess.target
- .rust_target_features()
- .iter()
- .filter_map(|(feature, gate, _)| {
- // The `allow_unstable` set is used by rustc internally to determined which target
- // features are truly available, so we want to return even perma-unstable
- // "forbidden" features.
- if allow_unstable
- || (gate.in_cfg()
- && (sess.is_nightly_build() || gate.requires_nightly().is_none()))
- {
- Some(Symbol::intern(feature))
- } else {
- None
- }
- })
- .filter(|feature| features.contains(&feature))
- .collect()
- };
-
- let target_features = f(false);
- let unstable_target_features = f(true);
let mut cfg = TargetConfig {
target_features,
unstable_target_features,
@@ -707,10 +622,18 @@ pub(crate) fn target_cpu(sess: &Session) -> &str {
handle_native(cpu_name)
}
-fn llvm_features_by_flags(sess: &Session) -> Vec<&str> {
- let mut features: Vec<&str> = Vec::new();
- retpoline_features_by_flags(sess, &mut features);
- features
+/// The target features for compiler flags other than `-Ctarget-features`.
+fn llvm_features_by_flags(sess: &Session, features: &mut Vec<String>) {
+ target_features::retpoline_features_by_flags(sess, features);
+
+ // -Zfixed-x18
+ if sess.opts.unstable_opts.fixed_x18 {
+ if sess.target.arch != "aarch64" {
+ sess.dcx().emit_fatal(errors::FixedX18InvalidArch { arch: &sess.target.arch });
+ } else {
+ features.push("+reserve-x18".into());
+ }
+ }
}
/// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
@@ -777,6 +700,8 @@ pub(crate) fn global_llvm_features(
.split(',')
.filter(|v| !v.is_empty())
// Drop +v8plus feature introduced in LLVM 20.
+ // (Hard-coded target features do not go through `to_llvm_feature` since they already
+ // are LLVM feature names, hence we need a special case here.)
.filter(|v| *v != "+v8plus" || get_version() >= (20, 0, 0))
.map(String::from),
);
@@ -787,86 +712,23 @@ pub(crate) fn global_llvm_features(
// -Ctarget-features
if !only_base_features {
- let known_features = sess.target.rust_target_features();
- // Will only be filled when `diagnostics` is set!
- let mut featsmap = FxHashMap::default();
-
- // Compute implied features
- let mut all_rust_features = vec![];
- for feature in sess.opts.cg.target_feature.split(',').chain(llvm_features_by_flags(sess)) {
- if let Some(feature) = feature.strip_prefix('+') {
- all_rust_features.extend(
- UnordSet::from(sess.target.implied_target_features(feature))
- .to_sorted_stable_ord()
- .iter()
- .map(|&&s| (true, s)),
- )
- } else if let Some(feature) = feature.strip_prefix('-') {
- // FIXME: Why do we not remove implied features on "-" here?
- // We do the equivalent above in `target_config`.
- // See <https://github.com/rust-lang/rust/issues/134792>.
- all_rust_features.push((false, feature));
- } else if !feature.is_empty() {
- if diagnostics {
- sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature });
- }
- }
- }
- // Remove features that are meant for rustc, not LLVM.
- all_rust_features.retain(|(_, feature)| {
- // Retain if it is not a rustc feature
- !RUSTC_SPECIFIC_FEATURES.contains(feature)
- });
-
- // Check feature validity.
- if diagnostics {
- for &(enable, feature) in &all_rust_features {
- let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
- match feature_state {
- None => {
- let rust_feature =
- known_features.iter().find_map(|&(rust_feature, _, _)| {
- let llvm_features = to_llvm_features(sess, rust_feature)?;
- if llvm_features.contains(feature)
- && !llvm_features.contains(rust_feature)
- {
- Some(rust_feature)
- } else {
- None
- }
- });
- let unknown_feature = if let Some(rust_feature) = rust_feature {
- UnknownCTargetFeature {
- feature,
- rust_feature: PossibleFeature::Some { rust_feature },
- }
- } else {
- UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
- };
- sess.dcx().emit_warn(unknown_feature);
- }
- Some((_, stability, _)) => {
- stability.verify_feature_enabled_by_flag(sess, enable, feature);
- }
- }
-
- // FIXME(nagisa): figure out how to not allocate a full hashset here.
- featsmap.insert(feature, enable);
- }
- }
-
- // Translate this into LLVM features.
- let feats = all_rust_features
- .iter()
- .filter_map(|&(enable, feature)| {
+ target_features::flag_to_backend_features(
+ sess,
+ diagnostics,
+ |feature| {
+ to_llvm_features(sess, feature)
+ .map(|f| SmallVec::<[&str; 2]>::from_iter(f.into_iter()))
+ .unwrap_or_default()
+ },
+ |feature, enable| {
let enable_disable = if enable { '+' } else { '-' };
// We run through `to_llvm_features` when
// passing requests down to LLVM. This means that all in-language
// features also work on the command line instead of having two
// different names when the LLVM name and the Rust name differ.
- let llvm_feature = to_llvm_features(sess, feature)?;
+ let Some(llvm_feature) = to_llvm_features(sess, feature) else { return };
- Some(
+ features.extend(
std::iter::once(format!(
"{}{}",
enable_disable, llvm_feature.llvm_feature_name
@@ -881,27 +743,12 @@ pub(crate) fn global_llvm_features(
},
)),
)
- })
- .flatten();
- features.extend(feats);
-
- if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) {
- sess.dcx().emit_err(rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable {
- features: f,
- span: None,
- missing_features: None,
- });
- }
+ },
+ );
}
- // -Zfixed-x18
- if sess.opts.unstable_opts.fixed_x18 {
- if sess.target.arch != "aarch64" {
- sess.dcx().emit_fatal(FixedX18InvalidArch { arch: &sess.target.arch });
- } else {
- features.push("+reserve-x18".into());
- }
- }
+ // We add this in the "base target" so that these show up in `sess.unstable_target_features`.
+ llvm_features_by_flags(sess, &mut features);
features
}
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 5322fe5..2bd8644 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -48,8 +48,6 @@
codegen_ssa_expected_name_value_pair = expected name value pair
-codegen_ssa_expected_one_argument = expected one argument
-
codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
@@ -68,6 +66,11 @@
codegen_ssa_field_associated_value_expected = associated value expected for `{$name}`
+codegen_ssa_forbidden_ctarget_feature =
+ target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason}
+ .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+codegen_ssa_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
+
codegen_ssa_forbidden_target_feature_attr =
target feature `{$feature}` cannot be enabled with `#[target_feature]`: {$reason}
@@ -86,9 +89,6 @@
codegen_ssa_insufficient_vs_code_product = VS Code is a different product, and is not sufficient.
-codegen_ssa_invalid_argument = invalid argument
- .help = valid inline arguments are `always` and `never`
-
codegen_ssa_invalid_instruction_set = invalid instruction set specified
codegen_ssa_invalid_link_ordinal_nargs = incorrect number of arguments to `#[link_ordinal]`
@@ -368,8 +368,22 @@
codegen_ssa_unknown_archive_kind =
Don't know how to build archive of type: {$kind}
+codegen_ssa_unknown_ctarget_feature =
+ unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}`
+ .note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future
+ .possible_feature = you might have meant: `{$rust_feature}`
+ .consider_filing_feature_request = consider filing a feature request
+
+codegen_ssa_unknown_ctarget_feature_prefix =
+ unknown feature specified for `-Ctarget-feature`: `{$feature}`
+ .note = features must begin with a `+` to enable or `-` to disable it
+
codegen_ssa_unknown_reuse_kind = unknown cgu-reuse-kind `{$kind}` specified
+codegen_ssa_unstable_ctarget_feature =
+ unstable feature specified for `-Ctarget-feature`: `{$feature}`
+ .note = this feature is not stably supported; its behavior can change in the future
+
codegen_ssa_unsupported_instruction_set = target does not support `#[instruction_set]`
codegen_ssa_unsupported_link_self_contained = option `-C link-self-contained` is not supported on this target
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index bbf9cce..c3bfe4c 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -14,10 +14,10 @@
use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard};
use rustc_errors::emitter::Emitter;
-use rustc_errors::translation::Translate;
+use rustc_errors::translation::Translator;
use rustc_errors::{
- Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, FluentBundle, Level, MultiSpan,
- Style, Suggestions,
+ Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, Level, MultiSpan, Style,
+ Suggestions,
};
use rustc_fs_util::link_or_copy;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
@@ -1889,16 +1889,6 @@ fn fatal(&self, msg: &str) {
}
}
-impl Translate for SharedEmitter {
- fn fluent_bundle(&self) -> Option<&FluentBundle> {
- None
- }
-
- fn fallback_fluent_bundle(&self) -> &FluentBundle {
- panic!("shared emitter attempted to translate a diagnostic");
- }
-}
-
impl Emitter for SharedEmitter {
fn emit_diagnostic(
&mut self,
@@ -1932,6 +1922,10 @@ fn emit_diagnostic(
fn source_map(&self) -> Option<&SourceMap> {
None
}
+
+ fn translator(&self) -> &Translator {
+ panic!("shared emitter attempted to translate a diagnostic");
+ }
}
impl SharedEmitterMain {
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 9874225..39818be 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -4,9 +4,8 @@
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
use rustc_attr_data_structures::{
- AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, find_attr,
+ AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, ReprAttr, find_attr,
};
-use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
@@ -18,13 +17,15 @@
use rustc_middle::query::Providers;
use rustc_middle::span_bug;
use rustc_middle::ty::{self as ty, TyCtxt};
+use rustc_session::lint;
use rustc_session::parse::feature_err;
-use rustc_session::{Session, lint};
use rustc_span::{Ident, Span, sym};
use rustc_target::spec::SanitizerSet;
use crate::errors;
-use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr};
+use crate::target_features::{
+ check_target_feature_trait_unsafe, check_tied_features, from_target_feature_attr,
+};
fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
use rustc_middle::mir::mono::Linkage::*;
@@ -109,8 +110,20 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
};
- if let hir::Attribute::Parsed(AttributeKind::Align { align, .. }) = attr {
- codegen_fn_attrs.alignment = Some(*align);
+ if let hir::Attribute::Parsed(p) = attr {
+ match p {
+ AttributeKind::Repr(reprs) => {
+ codegen_fn_attrs.alignment = reprs
+ .iter()
+ .filter_map(
+ |(r, _)| if let ReprAttr::ReprAlign(x) = r { Some(*x) } else { None },
+ )
+ .max();
+ }
+ AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
+ AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
+ _ => {}
+ }
}
let Some(Ident { name, .. }) = attr.ident() else {
@@ -118,7 +131,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
};
match name {
- sym::cold => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
sym::rustc_allocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR,
sym::ffi_pure => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE,
sym::ffi_const => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST,
@@ -455,33 +467,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
codegen_fn_attrs.inline = InlineAttr::Never;
}
- codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::Default, |ia, attr| {
- if !attr.has_name(sym::optimize) {
- return ia;
- }
- if attr.is_word() {
- tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
- return ia;
- }
- let Some(ref items) = attr.meta_item_list() else {
- return OptimizeAttr::Default;
- };
-
- let [item] = &items[..] else {
- tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
- return OptimizeAttr::Default;
- };
- if item.has_name(sym::size) {
- OptimizeAttr::Size
- } else if item.has_name(sym::speed) {
- OptimizeAttr::Speed
- } else if item.has_name(sym::none) {
- OptimizeAttr::DoNotOptimize
- } else {
- tcx.dcx().emit_err(errors::InvalidArgumentOptimize { span: item.span() });
- OptimizeAttr::Default
- }
- });
+ codegen_fn_attrs.optimize =
+ find_attr!(attrs, AttributeKind::Optimize(i, _) => *i).unwrap_or(OptimizeAttr::Default);
// #73631: closures inherit `#[target_feature]` annotations
//
@@ -615,25 +602,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
codegen_fn_attrs
}
-/// Given a map from target_features to whether they are enabled or disabled, ensure only valid
-/// combinations are allowed.
-pub fn check_tied_features(
- sess: &Session,
- features: &FxHashMap<&str, bool>,
-) -> Option<&'static [&'static str]> {
- if !features.is_empty() {
- for tied in sess.target.tied_target_features() {
- // Tied features must be set to the same value, or not set at all
- let mut tied_iter = tied.iter();
- let enabled = features.get(tied_iter.next().unwrap());
- if tied_iter.any(|f| enabled != features.get(f)) {
- return Some(tied);
- }
- }
- }
- None
-}
-
/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller
/// applied to the method prototype.
fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index 5387b2a..72e71b9 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -209,20 +209,6 @@ pub(crate) struct OutOfRangeInteger {
}
#[derive(Diagnostic)]
-#[diag(codegen_ssa_expected_one_argument, code = E0722)]
-pub(crate) struct ExpectedOneArgumentOptimize {
- #[primary_span]
- pub span: Span,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_ssa_invalid_argument, code = E0722)]
-pub(crate) struct InvalidArgumentOptimize {
- #[primary_span]
- pub span: Span,
-}
-
-#[derive(Diagnostic)]
#[diag(codegen_ssa_copy_path_buf)]
pub(crate) struct CopyPathBuf {
pub source_file: PathBuf,
@@ -1217,30 +1203,6 @@ pub(crate) struct ErrorCreatingImportLibrary<'a> {
pub error: String,
}
-pub struct TargetFeatureDisableOrEnable<'a> {
- pub features: &'a [&'a str],
- pub span: Option<Span>,
- pub missing_features: Option<MissingFeatures>,
-}
-
-#[derive(Subdiagnostic)]
-#[help(codegen_ssa_missing_features)]
-pub struct MissingFeatures;
-
-impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
- fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
- let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_target_feature_disable_or_enable);
- if let Some(span) = self.span {
- diag.span(span);
- };
- if let Some(missing_features) = self.missing_features {
- diag.subdiagnostic(missing_features);
- }
- diag.arg("features", self.features.join(", "));
- diag
- }
-}
-
#[derive(Diagnostic)]
#[diag(codegen_ssa_aix_strip_not_used)]
pub(crate) struct AixStripNotUsed;
@@ -1283,3 +1245,68 @@ pub(crate) struct XcrunSdkPathWarning {
#[derive(LintDiagnostic)]
#[diag(codegen_ssa_aarch64_softfloat_neon)]
pub(crate) struct Aarch64SoftfloatNeon;
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_unknown_ctarget_feature_prefix)]
+#[note]
+pub(crate) struct UnknownCTargetFeaturePrefix<'a> {
+ pub feature: &'a str,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum PossibleFeature<'a> {
+ #[help(codegen_ssa_possible_feature)]
+ Some { rust_feature: &'a str },
+ #[help(codegen_ssa_consider_filing_feature_request)]
+ None,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_unknown_ctarget_feature)]
+#[note]
+pub(crate) struct UnknownCTargetFeature<'a> {
+ pub feature: &'a str,
+ #[subdiagnostic]
+ pub rust_feature: PossibleFeature<'a>,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_unstable_ctarget_feature)]
+#[note]
+pub(crate) struct UnstableCTargetFeature<'a> {
+ pub feature: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_forbidden_ctarget_feature)]
+#[note]
+#[note(codegen_ssa_forbidden_ctarget_feature_issue)]
+pub(crate) struct ForbiddenCTargetFeature<'a> {
+ pub feature: &'a str,
+ pub enabled: &'a str,
+ pub reason: &'a str,
+}
+
+pub struct TargetFeatureDisableOrEnable<'a> {
+ pub features: &'a [&'a str],
+ pub span: Option<Span>,
+ pub missing_features: Option<MissingFeatures>,
+}
+
+#[derive(Subdiagnostic)]
+#[help(codegen_ssa_missing_features)]
+pub struct MissingFeatures;
+
+impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
+ fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
+ let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_target_feature_disable_or_enable);
+ if let Some(span) = self.span {
+ diag.span(span);
+ };
+ if let Some(missing_features) = self.missing_features {
+ diag.subdiagnostic(missing_features);
+ }
+ diag.arg("features", self.features.join(", "));
+ diag
+ }
+}
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index 640d197..67ac619 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -1,5 +1,5 @@
use rustc_attr_data_structures::InstructionSetAttr;
-use rustc_data_structures::fx::FxIndexSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::Applicability;
use rustc_hir as hir;
@@ -8,11 +8,12 @@
use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
use rustc_middle::query::Providers;
use rustc_middle::ty::TyCtxt;
-use rustc_session::features::StabilityExt;
+use rustc_session::Session;
use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON;
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, sym};
-use rustc_target::target_features::{self, Stability};
+use rustc_target::target_features::{self, RUSTC_SPECIFIC_FEATURES, Stability};
+use smallvec::SmallVec;
use crate::errors;
@@ -67,7 +68,7 @@ pub(crate) fn from_target_feature_attr(
// Only allow target features whose feature gates have been enabled
// and which are permitted to be toggled.
- if let Err(reason) = stability.is_toggle_permitted(tcx.sess) {
+ if let Err(reason) = stability.toggle_allowed() {
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
span: item.span(),
feature,
@@ -88,7 +89,7 @@ pub(crate) fn from_target_feature_attr(
let feature_sym = Symbol::intern(feature);
for &name in tcx.implied_target_features(feature_sym) {
// But ensure the ABI does not forbid enabling this.
- // Here we do assume that LLVM doesn't add even more implied features
+ // Here we do assume that the backend doesn't add even more implied features
// we don't know about, at least no features that would have ABI effects!
// We skip this logic in rustdoc, where we want to allow all target features of
// all targets, so we can't check their ABI compatibility and anyway we are not
@@ -156,6 +157,276 @@ pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId,
}
}
+/// Parse the value of `-Ctarget-feature`, also expanding implied features,
+/// and call the closure for each (expanded) Rust feature. If the list contains
+/// a syntactically invalid item (not starting with `+`/`-`), the error callback is invoked.
+fn parse_rust_feature_flag<'a>(
+ sess: &'a Session,
+ err_callback: impl Fn(&'a str),
+ mut callback: impl FnMut(
+ /* base_feature */ &'a str,
+ /* with_implied */ FxHashSet<&'a str>,
+ /* enable */ bool,
+ ),
+) {
+ // A cache for the backwards implication map.
+ let mut inverse_implied_features: Option<FxHashMap<&str, FxHashSet<&str>>> = None;
+
+ for feature in sess.opts.cg.target_feature.split(',') {
+ if let Some(base_feature) = feature.strip_prefix('+') {
+ // Skip features that are not target features, but rustc features.
+ if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) {
+ return;
+ }
+
+ callback(base_feature, sess.target.implied_target_features(base_feature), true)
+ } else if let Some(base_feature) = feature.strip_prefix('-') {
+ // Skip features that are not target features, but rustc features.
+ if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) {
+ return;
+ }
+
+ // If `f1` implies `f2`, then `!f2` implies `!f1` -- this is standard logical
+ // contraposition. So we have to find all the reverse implications of `base_feature` and
+ // disable them, too.
+
+ let inverse_implied_features = inverse_implied_features.get_or_insert_with(|| {
+ let mut set: FxHashMap<&str, FxHashSet<&str>> = FxHashMap::default();
+ for (f, _, is) in sess.target.rust_target_features() {
+ for i in is.iter() {
+ set.entry(i).or_default().insert(f);
+ }
+ }
+ set
+ });
+
+ // Inverse implied target features have their own inverse implied target features, so we
+ // traverse the map until there are no more features to add.
+ let mut features = FxHashSet::default();
+ let mut new_features = vec![base_feature];
+ while let Some(new_feature) = new_features.pop() {
+ if features.insert(new_feature) {
+ if let Some(implied_features) = inverse_implied_features.get(&new_feature) {
+ new_features.extend(implied_features)
+ }
+ }
+ }
+
+ callback(base_feature, features, false)
+ } else if !feature.is_empty() {
+ err_callback(feature)
+ }
+ }
+}
+
+/// Utility function for a codegen backend to compute `cfg(target_feature)`, or more specifically,
+/// to populate `sess.unstable_target_features` and `sess.target_features` (these are the first and
+/// 2nd component of the return value, respectively).
+///
+/// `target_base_has_feature` should check whether the given feature (a Rust feature name!) is
+/// enabled in the "base" target machine, i.e., without applying `-Ctarget-feature`.
+///
+/// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled elsewhere.
+pub fn cfg_target_feature(
+ sess: &Session,
+ mut target_base_has_feature: impl FnMut(&str) -> bool,
+) -> (Vec<Symbol>, Vec<Symbol>) {
+ // Compute which of the known target features are enabled in the 'base' target machine. We only
+ // consider "supported" features; "forbidden" features are not reflected in `cfg` as of now.
+ let mut features: UnordSet<Symbol> = sess
+ .target
+ .rust_target_features()
+ .iter()
+ .filter(|(feature, _, _)| target_base_has_feature(feature))
+ .map(|(feature, _, _)| Symbol::intern(feature))
+ .collect();
+
+ // Add enabled and remove disabled features.
+ parse_rust_feature_flag(
+ sess,
+ /* err_callback */
+ |_| {
+ // Errors are already emitted in `flag_to_backend_features`; avoid duplicates.
+ },
+ |_base_feature, new_features, enabled| {
+ // Iteration order is irrelevant since this only influences an `UnordSet`.
+ #[allow(rustc::potential_query_instability)]
+ if enabled {
+ features.extend(new_features.into_iter().map(|f| Symbol::intern(f)));
+ } else {
+ // Remove `new_features` from `features`.
+ for new in new_features {
+ features.remove(&Symbol::intern(new));
+ }
+ }
+ },
+ );
+
+ // Filter enabled features based on feature gates.
+ let f = |allow_unstable| {
+ sess.target
+ .rust_target_features()
+ .iter()
+ .filter_map(|(feature, gate, _)| {
+ // The `allow_unstable` set is used by rustc internally to determine which target
+ // features are truly available, so we want to return even perma-unstable
+ // "forbidden" features.
+ if allow_unstable
+ || (gate.in_cfg()
+ && (sess.is_nightly_build() || gate.requires_nightly().is_none()))
+ {
+ Some(Symbol::intern(feature))
+ } else {
+ None
+ }
+ })
+ .filter(|feature| features.contains(&feature))
+ .collect()
+ };
+
+ (f(true), f(false))
+}
+
+/// Given a map from target_features to whether they are enabled or disabled, ensure only valid
+/// combinations are allowed.
+pub fn check_tied_features(
+ sess: &Session,
+ features: &FxHashMap<&str, bool>,
+) -> Option<&'static [&'static str]> {
+ if !features.is_empty() {
+ for tied in sess.target.tied_target_features() {
+ // Tied features must be set to the same value, or not set at all
+ let mut tied_iter = tied.iter();
+ let enabled = features.get(tied_iter.next().unwrap());
+ if tied_iter.any(|f| enabled != features.get(f)) {
+ return Some(tied);
+ }
+ }
+ }
+ None
+}
+
+/// Translates the `-Ctarget-feature` flag into a backend target feature list.
+///
+/// `to_backend_features` converts a Rust feature name into a list of backend feature names; this is
+/// used for diagnostic purposes only.
+///
+/// `extend_backend_features` extends the set of backend features (assumed to be in mutable state
+/// accessible by that closure) to enable/disable the given Rust feature name.
+pub fn flag_to_backend_features<'a, const N: usize>(
+ sess: &'a Session,
+ diagnostics: bool,
+ to_backend_features: impl Fn(&'a str) -> SmallVec<[&'a str; N]>,
+ mut extend_backend_features: impl FnMut(&'a str, /* enable */ bool),
+) {
+ let known_features = sess.target.rust_target_features();
+
+ // Compute implied features
+ let mut rust_features = vec![];
+ parse_rust_feature_flag(
+ sess,
+ /* err_callback */
+ |feature| {
+ if diagnostics {
+ sess.dcx().emit_warn(errors::UnknownCTargetFeaturePrefix { feature });
+ }
+ },
+ |base_feature, new_features, enable| {
+ rust_features.extend(
+ UnordSet::from(new_features).to_sorted_stable_ord().iter().map(|&&s| (enable, s)),
+ );
+ // Check feature validity.
+ if diagnostics {
+ let feature_state = known_features.iter().find(|&&(v, _, _)| v == base_feature);
+ match feature_state {
+ None => {
+ // This is definitely not a valid Rust feature name. Maybe it is a backend
+ // feature name? If so, give a better error message.
+ let rust_feature =
+ known_features.iter().find_map(|&(rust_feature, _, _)| {
+ let backend_features = to_backend_features(rust_feature);
+ if backend_features.contains(&base_feature)
+ && !backend_features.contains(&rust_feature)
+ {
+ Some(rust_feature)
+ } else {
+ None
+ }
+ });
+ let unknown_feature = if let Some(rust_feature) = rust_feature {
+ errors::UnknownCTargetFeature {
+ feature: base_feature,
+ rust_feature: errors::PossibleFeature::Some { rust_feature },
+ }
+ } else {
+ errors::UnknownCTargetFeature {
+ feature: base_feature,
+ rust_feature: errors::PossibleFeature::None,
+ }
+ };
+ sess.dcx().emit_warn(unknown_feature);
+ }
+ Some((_, stability, _)) => {
+ if let Err(reason) = stability.toggle_allowed() {
+ sess.dcx().emit_warn(errors::ForbiddenCTargetFeature {
+ feature: base_feature,
+ enabled: if enable { "enabled" } else { "disabled" },
+ reason,
+ });
+ } else if stability.requires_nightly().is_some() {
+ // An unstable feature. Warn about using it. It makes little sense
+ // to hard-error here since we just warn about fully unknown
+ // features above.
+ sess.dcx().emit_warn(errors::UnstableCTargetFeature {
+ feature: base_feature,
+ });
+ }
+ }
+ }
+ }
+ },
+ );
+
+ if diagnostics {
+ // FIXME(nagisa): figure out how to not allocate a full hashmap here.
+ if let Some(f) = check_tied_features(
+ sess,
+ &FxHashMap::from_iter(rust_features.iter().map(|&(enable, feature)| (feature, enable))),
+ ) {
+ sess.dcx().emit_err(errors::TargetFeatureDisableOrEnable {
+ features: f,
+ span: None,
+ missing_features: None,
+ });
+ }
+ }
+
+ // Add this to the backend features.
+ for (enable, feature) in rust_features {
+ extend_backend_features(feature, enable);
+ }
+}
+
+/// Computes the backend target features to be added to account for retpoline flags.
+/// Used by both LLVM and GCC since their target features are, conveniently, the same.
+pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec<String>) {
+ // -Zretpoline without -Zretpoline-external-thunk enables
+ // retpoline-indirect-branches and retpoline-indirect-calls target features
+ let unstable_opts = &sess.opts.unstable_opts;
+ if unstable_opts.retpoline && !unstable_opts.retpoline_external_thunk {
+ features.push("+retpoline-indirect-branches".into());
+ features.push("+retpoline-indirect-calls".into());
+ }
+ // -Zretpoline-external-thunk (maybe, with -Zretpoline too) enables
+ // retpoline-external-thunk, retpoline-indirect-branches and
+ // retpoline-indirect-calls target features
+ if unstable_opts.retpoline_external_thunk {
+ features.push("+retpoline-external-thunk".into());
+ features.push("+retpoline-indirect-branches".into());
+ features.push("+retpoline-indirect-calls".into());
+ }
+}
+
pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers {
rust_target_features: |tcx, cnum| {
@@ -182,7 +453,8 @@ pub(crate) fn provide(providers: &mut Providers) {
Stability::Unstable { .. } | Stability::Forbidden { .. },
)
| (Stability::Forbidden { .. }, Stability::Forbidden { .. }) => {
- // The stability in the entry is at least as good as the new one, just keep it.
+ // The stability in the entry is at least as good as the new
+ // one, just keep it.
}
_ => {
// Overwrite stabilite.
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 4f252f3..576b174 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -463,12 +463,6 @@ pub fn check_drop_terminator(
);
}
- fn crate_inject_span(&self) -> Option<Span> {
- self.tcx.hir_crate_items(()).definitions().next().and_then(|id| {
- self.tcx.crate_level_attribute_injection_span(self.tcx.local_def_id_to_hir_id(id))
- })
- }
-
/// Check the const stability of the given item (fn or trait).
fn check_callee_stability(&mut self, def_id: DefId) {
match self.tcx.lookup_const_stability(def_id) {
@@ -543,7 +537,6 @@ fn check_callee_stability(&mut self, def_id: DefId) {
feature,
feature_enabled,
safe_to_expose_on_stable: callee_safe_to_expose_on_stable,
- suggestion_span: self.crate_inject_span(),
is_function_call: self.tcx.def_kind(def_id) != DefKind::Trait,
});
}
@@ -919,7 +912,6 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location
name: intrinsic.name,
feature,
const_stable_indirect: is_const_stable,
- suggestion: self.crate_inject_span(),
});
}
Some(attrs::ConstStability {
diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs
index 9c30dbf..887275e 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -1,8 +1,8 @@
//! Concrete error types for all operations which may be invalid in a certain const context.
use hir::{ConstContext, LangItem};
+use rustc_errors::Diag;
use rustc_errors::codes::*;
-use rustc_errors::{Applicability, Diag};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::TyCtxtInferExt;
@@ -384,7 +384,6 @@ pub(crate) struct CallUnstable {
/// expose on stable.
pub feature_enabled: bool,
pub safe_to_expose_on_stable: bool,
- pub suggestion_span: Option<Span>,
/// true if `def_id` is the function we are calling, false if `def_id` is an unstable trait.
pub is_function_call: bool,
}
@@ -412,20 +411,7 @@ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
def_path: ccx.tcx.def_path_str(self.def_id),
})
};
- // FIXME: make this translatable
- let msg = format!("add `#![feature({})]` to the crate attributes to enable", self.feature);
- #[allow(rustc::untranslatable_diagnostic)]
- if let Some(span) = self.suggestion_span {
- err.span_suggestion_verbose(
- span,
- msg,
- format!("#![feature({})]\n", self.feature),
- Applicability::MachineApplicable,
- );
- } else {
- err.help(msg);
- }
-
+ ccx.tcx.disabled_nightly_features(&mut err, [(String::new(), self.feature)]);
err
}
}
@@ -452,7 +438,6 @@ pub(crate) struct IntrinsicUnstable {
pub name: Symbol,
pub feature: Symbol,
pub const_stable_indirect: bool,
- pub suggestion: Option<Span>,
}
impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
@@ -472,8 +457,7 @@ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
span,
name: self.name,
feature: self.feature,
- suggestion: self.suggestion,
- help: self.suggestion.is_none(),
+ suggestion: ccx.tcx.crate_level_attribute_injection_span(),
})
}
}
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 037cbf7..69c71ae 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -136,9 +136,7 @@ pub(crate) struct UnstableIntrinsic {
code = "#![feature({feature})]\n",
applicability = "machine-applicable"
)]
- pub suggestion: Option<Span>,
- #[help(const_eval_unstable_intrinsic_suggestion)]
- pub help: bool,
+ pub suggestion: Span,
}
#[derive(Diagnostic)]
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 99a4bc1..36d1a41 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -1412,8 +1412,13 @@ pub fn mem_copy_repeatedly(
let src_alloc = self.get_alloc_raw(src_alloc_id)?;
let src_range = alloc_range(src_offset, size);
assert!(!self.memory.validation_in_progress.get(), "we can't be copying during validation");
- // For the overlapping case, it is crucial that we trigger the read hook
+
+ // Trigger read hooks.
+ // For the overlapping case, it is crucial that we trigger the read hooks
// before the write hook -- the aliasing model cares about the order.
+ if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(src, size.bytes() as i64) {
+ M::before_alloc_read(self, alloc_id)?;
+ }
M::before_memory_read(
tcx,
&self.machine,
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index d53126d..daeca43 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -38,6 +38,7 @@
};
use rustc_errors::emitter::stderr_destination;
use rustc_errors::registry::Registry;
+use rustc_errors::translation::Translator;
use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown};
use rustc_feature::find_gated_cfg;
// This avoids a false positive with `-Wunused_crate_dependencies`.
@@ -109,6 +110,10 @@ pub(super) fn install() {}
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
+pub fn default_translator() -> Translator {
+ Translator::with_fallback_bundle(DEFAULT_LOCALE_RESOURCES.to_vec(), false)
+}
+
pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
// tidy-alphabetical-start
crate::DEFAULT_LOCALE_RESOURCE,
@@ -1413,11 +1418,10 @@ fn report_ice(
extra_info: fn(&DiagCtxt),
using_internal_features: &AtomicBool,
) {
- let fallback_bundle =
- rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
+ let translator = default_translator();
let emitter = Box::new(rustc_errors::emitter::HumanEmitter::new(
stderr_destination(rustc_errors::ColorConfig::Auto),
- fallback_bundle,
+ translator,
));
let dcx = rustc_errors::DiagCtxt::new(emitter);
let dcx = dcx.handle();
diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs
index ec77043..688307a 100644
--- a/compiler/rustc_driver_impl/src/pretty.rs
+++ b/compiler/rustc_driver_impl/src/pretty.rs
@@ -292,7 +292,11 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
}
HirTree => {
debug!("pretty printing HIR tree");
- format!("{:#?}", ex.tcx().hir_crate(()))
+ ex.tcx()
+ .hir_crate_items(())
+ .owners()
+ .map(|owner| format!("{:#?} => {:#?}\n", owner, ex.tcx().hir_owner_nodes(owner)))
+ .collect()
}
Mir => {
let mut out = Vec::new();
diff --git a/compiler/rustc_error_codes/src/error_codes/E0722.md b/compiler/rustc_error_codes/src/error_codes/E0722.md
index 570717a..1799458 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0722.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0722.md
@@ -1,8 +1,14 @@
+#### Note: this error code is no longer emitted by the compiler
+
+This is because it was too specific to the `optimize` attribute.
+Similar diagnostics occur for other attributes too.
+The example here will now emit `E0539`
+
The `optimize` attribute was malformed.
Erroneous code example:
-```compile_fail,E0722
+```compile_fail,E0539
#![feature(optimize_attribute)]
#[optimize(something)] // error: invalid argument
diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs
index 6f5e482..22cc1e8 100644
--- a/compiler/rustc_error_codes/src/lib.rs
+++ b/compiler/rustc_error_codes/src/lib.rs
@@ -686,6 +686,7 @@ macro_rules! error_codes {
// E0707, // multiple elided lifetimes used in arguments of `async fn`
// E0709, // multiple different lifetimes used in arguments of `async fn`
// E0721, // `await` keyword
+// E0722, // replaced with a generic attribute input check
// E0723, // unstable feature in `const` context
// E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
// E0744, // merged into E0728
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index 1d3b5b2..194fc24 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -18,7 +18,7 @@
use fluent_syntax::parser::ParserError;
use icu_provider_adapters::fallback::{LocaleFallbackProvider, LocaleFallbacker};
use intl_memoizer::concurrent::IntlLangMemoizer;
-use rustc_data_structures::sync::IntoDynSyncSend;
+use rustc_data_structures::sync::{DynSend, IntoDynSyncSend};
use rustc_macros::{Decodable, Encodable};
use rustc_span::Span;
use smallvec::SmallVec;
@@ -204,16 +204,16 @@ fn register_functions(bundle: &mut FluentBundle) {
/// Type alias for the result of `fallback_fluent_bundle` - a reference-counted pointer to a lazily
/// evaluated fluent bundle.
-pub type LazyFallbackBundle = Arc<LazyLock<FluentBundle, impl FnOnce() -> FluentBundle>>;
+pub type LazyFallbackBundle =
+ Arc<LazyLock<FluentBundle, Box<dyn FnOnce() -> FluentBundle + DynSend>>>;
/// Return the default `FluentBundle` with standard "en-US" diagnostic messages.
#[instrument(level = "trace", skip(resources))]
-#[define_opaque(LazyFallbackBundle)]
pub fn fallback_fluent_bundle(
resources: Vec<&'static str>,
with_directionality_markers: bool,
) -> LazyFallbackBundle {
- Arc::new(LazyLock::new(move || {
+ Arc::new(LazyLock::new(Box::new(move || {
let mut fallback_bundle = new_bundle(vec![langid!("en-US")]);
register_functions(&mut fallback_bundle);
@@ -228,7 +228,7 @@ pub fn fallback_fluent_bundle(
}
fallback_bundle
- }))
+ })))
}
/// Identifier for the Fluent message/attribute corresponding to a diagnostic message.
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index f3aeb8d..2eb3c23 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -15,17 +15,15 @@
use crate::emitter::FileWithAnnotatedLines;
use crate::registry::Registry;
use crate::snippet::Line;
-use crate::translation::{Translate, to_fluent_args};
+use crate::translation::{Translator, to_fluent_args};
use crate::{
- CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, FluentBundle, LazyFallbackBundle,
- Level, MultiSpan, Style, Subdiag,
+ CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, Level, MultiSpan, Style, Subdiag,
};
/// Generates diagnostics using annotate-snippet
pub struct AnnotateSnippetEmitter {
source_map: Option<Arc<SourceMap>>,
- fluent_bundle: Option<Arc<FluentBundle>>,
- fallback_bundle: LazyFallbackBundle,
+ translator: Translator,
/// If true, hides the longer explanation text
short_message: bool,
@@ -35,16 +33,6 @@ pub struct AnnotateSnippetEmitter {
macro_backtrace: bool,
}
-impl Translate for AnnotateSnippetEmitter {
- fn fluent_bundle(&self) -> Option<&FluentBundle> {
- self.fluent_bundle.as_deref()
- }
-
- fn fallback_fluent_bundle(&self) -> &FluentBundle {
- &self.fallback_bundle
- }
-}
-
impl Emitter for AnnotateSnippetEmitter {
/// The entry point for the diagnostics generation
fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) {
@@ -78,6 +66,10 @@ fn source_map(&self) -> Option<&SourceMap> {
fn should_show_explain(&self) -> bool {
!self.short_message
}
+
+ fn translator(&self) -> &Translator {
+ &self.translator
+ }
}
/// Provides the source string for the given `line` of `file`
@@ -104,19 +96,11 @@ fn annotation_level_for_level(level: Level) -> annotate_snippets::Level {
impl AnnotateSnippetEmitter {
pub fn new(
source_map: Option<Arc<SourceMap>>,
- fluent_bundle: Option<Arc<FluentBundle>>,
- fallback_bundle: LazyFallbackBundle,
+ translator: Translator,
short_message: bool,
macro_backtrace: bool,
) -> Self {
- Self {
- source_map,
- fluent_bundle,
- fallback_bundle,
- short_message,
- ui_testing: false,
- macro_backtrace,
- }
+ Self { source_map, translator, short_message, ui_testing: false, macro_backtrace }
}
/// Allows to modify `Self` to enable or disable the `ui_testing` flag.
@@ -137,7 +121,7 @@ fn emit_messages_default(
_children: &[Subdiag],
_suggestions: &[CodeSuggestion],
) {
- let message = self.translate_messages(messages, args);
+ let message = self.translator.translate_messages(messages, args);
if let Some(source_map) = &self.source_map {
// Make sure our primary file comes first
let primary_lo = if let Some(primary_span) = msp.primary_span().as_ref() {
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 6ab6f96..e333de4 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -35,10 +35,10 @@
};
use crate::styled_buffer::StyledBuffer;
use crate::timings::TimingRecord;
-use crate::translation::{Translate, to_fluent_args};
+use crate::translation::{Translator, to_fluent_args};
use crate::{
- CodeSuggestion, DiagInner, DiagMessage, ErrCode, FluentBundle, LazyFallbackBundle, Level,
- MultiSpan, Subdiag, SubstitutionHighlight, SuggestionStyle, TerminalUrl,
+ CodeSuggestion, DiagInner, DiagMessage, ErrCode, Level, MultiSpan, Subdiag,
+ SubstitutionHighlight, SuggestionStyle, TerminalUrl,
};
/// Default column width, used in tests and when terminal dimensions cannot be determined.
@@ -175,7 +175,7 @@ pub enum TimingEvent {
pub type DynEmitter = dyn Emitter + DynSend;
/// Emitter trait for emitting errors and other structured information.
-pub trait Emitter: Translate {
+pub trait Emitter {
/// Emit a structured diagnostic.
fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry);
@@ -212,6 +212,8 @@ fn supports_color(&self) -> bool {
fn source_map(&self) -> Option<&SourceMap>;
+ fn translator(&self) -> &Translator;
+
/// Formats the substitutions of the primary_span
///
/// There are a lot of conditions to this method, but in short:
@@ -224,13 +226,17 @@ fn supports_color(&self) -> bool {
/// * If the current `DiagInner` has multiple suggestions,
/// we leave `primary_span` and the suggestions untouched.
fn primary_span_formatted(
- &mut self,
+ &self,
primary_span: &mut MultiSpan,
suggestions: &mut Vec<CodeSuggestion>,
fluent_args: &FluentArgs<'_>,
) {
if let Some((sugg, rest)) = suggestions.split_first() {
- let msg = self.translate_message(&sugg.msg, fluent_args).map_err(Report::new).unwrap();
+ let msg = self
+ .translator()
+ .translate_message(&sugg.msg, fluent_args)
+ .map_err(Report::new)
+ .unwrap();
if rest.is_empty()
// ^ if there is only one suggestion
// don't display multi-suggestions as labels
@@ -491,16 +497,6 @@ fn fix_multispan_in_extern_macros(&self, span: &mut MultiSpan) {
}
}
-impl Translate for HumanEmitter {
- fn fluent_bundle(&self) -> Option<&FluentBundle> {
- self.fluent_bundle.as_deref()
- }
-
- fn fallback_fluent_bundle(&self) -> &FluentBundle {
- &self.fallback_bundle
- }
-}
-
impl Emitter for HumanEmitter {
fn source_map(&self) -> Option<&SourceMap> {
self.sm.as_deref()
@@ -538,25 +534,41 @@ fn should_show_explain(&self) -> bool {
fn supports_color(&self) -> bool {
self.dst.supports_color()
}
+
+ fn translator(&self) -> &Translator {
+ &self.translator
+ }
}
/// An emitter that does nothing when emitting a non-fatal diagnostic.
/// Fatal diagnostics are forwarded to `fatal_emitter` to avoid silent
/// failures of rustc, as witnessed e.g. in issue #89358.
-pub struct SilentEmitter {
+pub struct FatalOnlyEmitter {
pub fatal_emitter: Box<dyn Emitter + DynSend>,
pub fatal_note: Option<String>,
- pub emit_fatal_diagnostic: bool,
}
-impl Translate for SilentEmitter {
- fn fluent_bundle(&self) -> Option<&FluentBundle> {
+impl Emitter for FatalOnlyEmitter {
+ fn source_map(&self) -> Option<&SourceMap> {
None
}
- fn fallback_fluent_bundle(&self) -> &FluentBundle {
- self.fatal_emitter.fallback_fluent_bundle()
+ fn emit_diagnostic(&mut self, mut diag: DiagInner, registry: &Registry) {
+ if diag.level == Level::Fatal {
+ if let Some(fatal_note) = &self.fatal_note {
+ diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new());
+ }
+ self.fatal_emitter.emit_diagnostic(diag, registry);
+ }
}
+
+ fn translator(&self) -> &Translator {
+ self.fatal_emitter.translator()
+ }
+}
+
+pub struct SilentEmitter {
+ pub translator: Translator,
}
impl Emitter for SilentEmitter {
@@ -564,13 +576,10 @@ fn source_map(&self) -> Option<&SourceMap> {
None
}
- fn emit_diagnostic(&mut self, mut diag: DiagInner, registry: &Registry) {
- if self.emit_fatal_diagnostic && diag.level == Level::Fatal {
- if let Some(fatal_note) = &self.fatal_note {
- diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new());
- }
- self.fatal_emitter.emit_diagnostic(diag, registry);
- }
+ fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {}
+
+ fn translator(&self) -> &Translator {
+ &self.translator
}
}
@@ -615,9 +624,8 @@ pub struct HumanEmitter {
#[setters(skip)]
dst: IntoDynSyncSend<Destination>,
sm: Option<Arc<SourceMap>>,
- fluent_bundle: Option<Arc<FluentBundle>>,
#[setters(skip)]
- fallback_bundle: LazyFallbackBundle,
+ translator: Translator,
short_message: bool,
ui_testing: bool,
ignored_directories_in_source_blocks: Vec<String>,
@@ -637,12 +645,11 @@ pub(crate) struct FileWithAnnotatedLines {
}
impl HumanEmitter {
- pub fn new(dst: Destination, fallback_bundle: LazyFallbackBundle) -> HumanEmitter {
+ pub fn new(dst: Destination, translator: Translator) -> HumanEmitter {
HumanEmitter {
dst: IntoDynSyncSend(dst),
sm: None,
- fluent_bundle: None,
- fallback_bundle,
+ translator,
short_message: false,
ui_testing: false,
ignored_directories_in_source_blocks: Vec::new(),
@@ -1433,7 +1440,7 @@ fn style_or_override(style: Style, override_: Option<Style>) -> Style {
// very *weird* formats
// see?
for (text, style) in msgs.iter() {
- let text = self.translate_message(text, args).map_err(Report::new).unwrap();
+ let text = self.translator.translate_message(text, args).map_err(Report::new).unwrap();
let text = &normalize_whitespace(&text);
let lines = text.split('\n').collect::<Vec<_>>();
if lines.len() > 1 {
@@ -1528,7 +1535,8 @@ fn emit_messages_default_inner(
}
let mut line = 0;
for (text, style) in msgs.iter() {
- let text = self.translate_message(text, args).map_err(Report::new).unwrap();
+ let text =
+ self.translator.translate_message(text, args).map_err(Report::new).unwrap();
// Account for newlines to align output to its label.
for text in normalize_whitespace(&text).lines() {
buffer.append(
@@ -1560,7 +1568,7 @@ fn emit_messages_default_inner(
.into_iter()
.filter_map(|label| match label.label {
Some(msg) if label.is_primary => {
- let text = self.translate_message(&msg, args).ok()?;
+ let text = self.translator.translate_message(&msg, args).ok()?;
if !text.trim().is_empty() { Some(text.to_string()) } else { None }
}
_ => None,
@@ -3104,7 +3112,11 @@ fn add_annotation_to_file(
let label = label.as_ref().map(|m| {
normalize_whitespace(
- &emitter.translate_message(m, args).map_err(Report::new).unwrap(),
+ &emitter
+ .translator()
+ .translate_message(m, args)
+ .map_err(Report::new)
+ .unwrap(),
)
});
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index d67e2ba..6d600f8 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -32,11 +32,8 @@
};
use crate::registry::Registry;
use crate::timings::{TimingRecord, TimingSection};
-use crate::translation::{Translate, to_fluent_args};
-use crate::{
- CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, Subdiag, Suggestions,
- TerminalUrl,
-};
+use crate::translation::{Translator, to_fluent_args};
+use crate::{CodeSuggestion, MultiSpan, SpanLabel, Subdiag, Suggestions, TerminalUrl};
#[cfg(test)]
mod tests;
@@ -47,9 +44,8 @@ pub struct JsonEmitter {
dst: IntoDynSyncSend<Box<dyn Write + Send>>,
#[setters(skip)]
sm: Option<Arc<SourceMap>>,
- fluent_bundle: Option<Arc<FluentBundle>>,
#[setters(skip)]
- fallback_bundle: LazyFallbackBundle,
+ translator: Translator,
#[setters(skip)]
pretty: bool,
ui_testing: bool,
@@ -67,7 +63,7 @@ impl JsonEmitter {
pub fn new(
dst: Box<dyn Write + Send>,
sm: Option<Arc<SourceMap>>,
- fallback_bundle: LazyFallbackBundle,
+ translator: Translator,
pretty: bool,
json_rendered: HumanReadableErrorType,
color_config: ColorConfig,
@@ -75,8 +71,7 @@ pub fn new(
JsonEmitter {
dst: IntoDynSyncSend(dst),
sm,
- fluent_bundle: None,
- fallback_bundle,
+ translator,
pretty,
ui_testing: false,
ignored_directories_in_source_blocks: Vec::new(),
@@ -110,16 +105,6 @@ enum EmitTyped<'a> {
UnusedExtern(UnusedExterns<'a>),
}
-impl Translate for JsonEmitter {
- fn fluent_bundle(&self) -> Option<&FluentBundle> {
- self.fluent_bundle.as_deref()
- }
-
- fn fallback_fluent_bundle(&self) -> &FluentBundle {
- &self.fallback_bundle
- }
-}
-
impl Emitter for JsonEmitter {
fn emit_diagnostic(&mut self, diag: crate::DiagInner, registry: &Registry) {
let data = Diagnostic::from_errors_diagnostic(diag, self, registry);
@@ -194,6 +179,10 @@ fn source_map(&self) -> Option<&SourceMap> {
fn should_show_explain(&self) -> bool {
!self.json_rendered.short()
}
+
+ fn translator(&self) -> &Translator {
+ &self.translator
+ }
}
// The following data types are provided just for serialisation.
@@ -324,7 +313,7 @@ fn from_errors_diagnostic(
let args = to_fluent_args(diag.args.iter());
let sugg_to_diag = |sugg: &CodeSuggestion| {
let translated_message =
- je.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap();
+ je.translator.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap();
Diagnostic {
message: translated_message.to_string(),
code: None,
@@ -368,7 +357,7 @@ fn reset(&mut self) -> io::Result<()> {
}
}
- let translated_message = je.translate_messages(&diag.messages, &args);
+ let translated_message = je.translator.translate_messages(&diag.messages, &args);
let code = if let Some(code) = diag.code {
Some(DiagnosticCode {
@@ -396,10 +385,9 @@ fn reset(&mut self) -> io::Result<()> {
ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)),
ColorConfig::Never => {}
}
- HumanEmitter::new(dst, Arc::clone(&je.fallback_bundle))
+ HumanEmitter::new(dst, je.translator.clone())
.short_message(short)
.sm(je.sm.clone())
- .fluent_bundle(je.fluent_bundle.clone())
.diagnostic_width(je.diagnostic_width)
.macro_backtrace(je.macro_backtrace)
.track_diagnostics(je.track_diagnostics)
@@ -430,7 +418,7 @@ fn from_sub_diagnostic(
args: &FluentArgs<'_>,
je: &JsonEmitter,
) -> Diagnostic {
- let translated_message = je.translate_messages(&subdiag.messages, args);
+ let translated_message = je.translator.translate_messages(&subdiag.messages, args);
Diagnostic {
message: translated_message.to_string(),
code: None,
@@ -454,7 +442,7 @@ fn from_span_label(
span.is_primary,
span.label
.as_ref()
- .map(|m| je.translate_message(m, args).unwrap())
+ .map(|m| je.translator.translate_message(m, args).unwrap())
.map(|m| m.to_string()),
suggestion,
je,
diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs
index 40973e8..8cf81f4 100644
--- a/compiler/rustc_errors/src/json/tests.rs
+++ b/compiler/rustc_errors/src/json/tests.rs
@@ -41,14 +41,14 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
rustc_span::create_default_session_globals_then(|| {
let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned());
- let fallback_bundle =
- crate::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
+ let translator =
+ Translator::with_fallback_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
let output = Arc::new(Mutex::new(Vec::new()));
let je = JsonEmitter::new(
Box::new(Shared { data: output.clone() }),
Some(sm),
- fallback_bundle,
+ translator,
true, // pretty
HumanReadableErrorType::Short,
ColorConfig::Never,
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 0bd2593..207aed8 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -748,40 +748,10 @@ pub fn new(emitter: Box<DynEmitter>) -> Self {
Self { inner: Lock::new(DiagCtxtInner::new(emitter)) }
}
- pub fn make_silent(&self, fatal_note: Option<String>, emit_fatal_diagnostic: bool) {
- // An empty type that implements `Emitter` to temporarily swap in place of the real one,
- // which will be used in constructing its replacement.
- struct FalseEmitter;
-
- impl Emitter for FalseEmitter {
- fn emit_diagnostic(&mut self, _: DiagInner, _: &Registry) {
- unimplemented!("false emitter must only used during `make_silent`")
- }
-
- fn source_map(&self) -> Option<&SourceMap> {
- unimplemented!("false emitter must only used during `make_silent`")
- }
- }
-
- impl translation::Translate for FalseEmitter {
- fn fluent_bundle(&self) -> Option<&FluentBundle> {
- unimplemented!("false emitter must only used during `make_silent`")
- }
-
- fn fallback_fluent_bundle(&self) -> &FluentBundle {
- unimplemented!("false emitter must only used during `make_silent`")
- }
- }
-
+ pub fn make_silent(&self) {
let mut inner = self.inner.borrow_mut();
- let mut prev_emitter = Box::new(FalseEmitter) as Box<dyn Emitter + DynSend>;
- std::mem::swap(&mut inner.emitter, &mut prev_emitter);
- let new_emitter = Box::new(emitter::SilentEmitter {
- fatal_emitter: prev_emitter,
- fatal_note,
- emit_fatal_diagnostic,
- });
- inner.emitter = new_emitter;
+ let translator = inner.emitter.translator().clone();
+ inner.emitter = Box::new(emitter::SilentEmitter { translator });
}
pub fn set_emitter(&self, emitter: Box<dyn Emitter + DynSend>) {
@@ -1771,7 +1741,12 @@ fn eagerly_translate_to_string<'a>(
args: impl Iterator<Item = DiagArg<'a>>,
) -> String {
let args = crate::translation::to_fluent_args(args);
- self.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string()
+ self.emitter
+ .translator()
+ .translate_message(&message, &args)
+ .map_err(Report::new)
+ .unwrap()
+ .to_string()
}
fn eagerly_translate_for_subdiag(
diff --git a/compiler/rustc_errors/src/tests.rs b/compiler/rustc_errors/src/tests.rs
index 376fd24..34ebac0 100644
--- a/compiler/rustc_errors/src/tests.rs
+++ b/compiler/rustc_errors/src/tests.rs
@@ -1,3 +1,5 @@
+use std::sync::{Arc, LazyLock};
+
use rustc_data_structures::sync::IntoDynSyncSend;
use rustc_error_messages::fluent_bundle::resolver::errors::{ReferenceKind, ResolverError};
use rustc_error_messages::{DiagMessage, langid};
@@ -5,23 +7,9 @@
use crate::FluentBundle;
use crate::error::{TranslateError, TranslateErrorKind};
use crate::fluent_bundle::*;
-use crate::translation::Translate;
+use crate::translation::Translator;
-struct Dummy {
- bundle: FluentBundle,
-}
-
-impl Translate for Dummy {
- fn fluent_bundle(&self) -> Option<&FluentBundle> {
- None
- }
-
- fn fallback_fluent_bundle(&self) -> &FluentBundle {
- &self.bundle
- }
-}
-
-fn make_dummy(ftl: &'static str) -> Dummy {
+fn make_translator(ftl: &'static str) -> Translator {
let resource = FluentResource::try_new(ftl.into()).expect("Failed to parse an FTL string.");
let langid_en = langid!("en-US");
@@ -33,12 +21,15 @@ fn make_dummy(ftl: &'static str) -> Dummy {
bundle.add_resource(resource).expect("Failed to add FTL resources to the bundle.");
- Dummy { bundle }
+ Translator {
+ fluent_bundle: None,
+ fallback_fluent_bundle: Arc::new(LazyLock::new(Box::new(|| bundle))),
+ }
}
#[test]
fn wellformed_fluent() {
- let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value
+ let translator = make_translator("mir_build_borrow_of_moved_value = borrow of moved value
.label = value moved into `{$name}` here
.occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement the `Copy` trait
.value_borrowed_label = value borrowed here after move
@@ -54,7 +45,7 @@ fn wellformed_fluent() {
);
assert_eq!(
- dummy.translate_message(&message, &args).unwrap(),
+ translator.translate_message(&message, &args).unwrap(),
"borrow this binding in the pattern to avoid moving the value"
);
}
@@ -66,7 +57,7 @@ fn wellformed_fluent() {
);
assert_eq!(
- dummy.translate_message(&message, &args).unwrap(),
+ translator.translate_message(&message, &args).unwrap(),
"value borrowed here after move"
);
}
@@ -78,7 +69,7 @@ fn wellformed_fluent() {
);
assert_eq!(
- dummy.translate_message(&message, &args).unwrap(),
+ translator.translate_message(&message, &args).unwrap(),
"move occurs because `\u{2068}Foo\u{2069}` has type `\u{2068}std::string::String\u{2069}` which does not implement the `Copy` trait"
);
@@ -89,7 +80,7 @@ fn wellformed_fluent() {
);
assert_eq!(
- dummy.translate_message(&message, &args).unwrap(),
+ translator.translate_message(&message, &args).unwrap(),
"value moved into `\u{2068}Foo\u{2069}` here"
);
}
@@ -98,7 +89,7 @@ fn wellformed_fluent() {
#[test]
fn misformed_fluent() {
- let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value
+ let translator = make_translator("mir_build_borrow_of_moved_value = borrow of moved value
.label = value moved into `{name}` here
.occurs_because_label = move occurs because `{$oops}` has type `{$ty}` which does not implement the `Copy` trait
.suggestion = borrow this binding in the pattern to avoid moving the value");
@@ -112,7 +103,7 @@ fn misformed_fluent() {
Some("value_borrowed_label".into()),
);
- let err = dummy.translate_message(&message, &args).unwrap_err();
+ let err = translator.translate_message(&message, &args).unwrap_err();
assert!(
matches!(
&err,
@@ -141,7 +132,7 @@ fn misformed_fluent() {
Some("label".into()),
);
- let err = dummy.translate_message(&message, &args).unwrap_err();
+ let err = translator.translate_message(&message, &args).unwrap_err();
if let TranslateError::Two {
primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. },
fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. },
@@ -168,7 +159,7 @@ fn misformed_fluent() {
Some("occurs_because_label".into()),
);
- let err = dummy.translate_message(&message, &args).unwrap_err();
+ let err = translator.translate_message(&message, &args).unwrap_err();
if let TranslateError::Two {
primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. },
fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. },
diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs
index 156f5e5..c0bcec0 100644
--- a/compiler/rustc_errors/src/translation.rs
+++ b/compiler/rustc_errors/src/translation.rs
@@ -1,8 +1,9 @@
use std::borrow::Cow;
use std::env;
use std::error::Report;
+use std::sync::Arc;
-pub use rustc_error_messages::FluentArgs;
+pub use rustc_error_messages::{FluentArgs, LazyFallbackBundle};
use tracing::{debug, trace};
use crate::error::{TranslateError, TranslateErrorKind};
@@ -28,19 +29,33 @@ pub fn to_fluent_args<'iter>(iter: impl Iterator<Item = DiagArg<'iter>>) -> Flue
args
}
-pub trait Translate {
- /// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no
- /// language was requested by the user then this will be `None` and `fallback_fluent_bundle`
- /// should be used.
- fn fluent_bundle(&self) -> Option<&FluentBundle>;
-
+#[derive(Clone)]
+pub struct Translator {
+ /// Localized diagnostics for the locale requested by the user. If no language was requested by
+ /// the user then this will be `None` and `fallback_fluent_bundle` should be used.
+ pub fluent_bundle: Option<Arc<FluentBundle>>,
/// Return `FluentBundle` with localized diagnostics for the default locale of the compiler.
/// Used when the user has not requested a specific language or when a localized diagnostic is
/// unavailable for the requested locale.
- fn fallback_fluent_bundle(&self) -> &FluentBundle;
+ pub fallback_fluent_bundle: LazyFallbackBundle,
+}
+
+impl Translator {
+ pub fn with_fallback_bundle(
+ resources: Vec<&'static str>,
+ with_directionality_markers: bool,
+ ) -> Translator {
+ Translator {
+ fluent_bundle: None,
+ fallback_fluent_bundle: crate::fallback_fluent_bundle(
+ resources,
+ with_directionality_markers,
+ ),
+ }
+ }
/// Convert `DiagMessage`s to a string, performing translation if necessary.
- fn translate_messages(
+ pub fn translate_messages(
&self,
messages: &[(DiagMessage, Style)],
args: &FluentArgs<'_>,
@@ -54,7 +69,7 @@ fn translate_messages(
}
/// Convert a `DiagMessage` to a string, performing translation if necessary.
- fn translate_message<'a>(
+ pub fn translate_message<'a>(
&'a self,
message: &'a DiagMessage,
args: &'a FluentArgs<'_>,
@@ -91,7 +106,7 @@ fn translate_message<'a>(
};
try {
- match self.fluent_bundle().map(|b| translate_with_bundle(b)) {
+ match self.fluent_bundle.as_ref().map(|b| translate_with_bundle(b)) {
// The primary bundle was present and translation succeeded
Some(Ok(t)) => t,
@@ -102,7 +117,7 @@ fn translate_message<'a>(
primary @ TranslateError::One {
kind: TranslateErrorKind::MessageMissing, ..
},
- )) => translate_with_bundle(self.fallback_fluent_bundle())
+ )) => translate_with_bundle(&self.fallback_fluent_bundle)
.map_err(|fallback| primary.and(fallback))?,
// Always yeet out for errors on debug (unless
@@ -118,11 +133,11 @@ fn translate_message<'a>(
// ..otherwise, for end users, an error about this wouldn't be useful or actionable, so
// just hide it and try with the fallback bundle.
- Some(Err(primary)) => translate_with_bundle(self.fallback_fluent_bundle())
+ Some(Err(primary)) => translate_with_bundle(&self.fallback_fluent_bundle)
.map_err(|fallback| primary.and(fallback))?,
// The primary bundle is missing, proceed to the fallback bundle
- None => translate_with_bundle(self.fallback_fluent_bundle())
+ None => translate_with_bundle(&self.fallback_fluent_bundle)
.map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?,
}
}
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index 2d3fd77..0520be5 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -9,7 +9,7 @@
use rustc_errors::{Diag, DiagCtxtHandle, PResult, pluralize};
use rustc_parse::lexer::nfc_normalize;
use rustc_parse::parser::ParseNtResult;
-use rustc_session::parse::{ParseSess, SymbolGallery};
+use rustc_session::parse::ParseSess;
use rustc_span::hygiene::{LocalExpnId, Transparency};
use rustc_span::{
Ident, MacroRulesNormalizedIdent, Span, Symbol, SyntaxContext, sym, with_metavar_spans,
@@ -25,20 +25,77 @@
use crate::mbe::metavar_expr::{MetaVarExprConcatElem, RAW_IDENT_ERR};
use crate::mbe::{self, KleeneOp, MetaVarExpr};
-// A Marker adds the given mark to the syntax context.
-struct Marker(LocalExpnId, Transparency, FxHashMap<SyntaxContext, SyntaxContext>);
+/// Context needed to perform transcription of metavariable expressions.
+struct TranscrCtx<'psess, 'itp> {
+ psess: &'psess ParseSess,
+
+ /// Map from metavars to matched tokens
+ interp: &'itp FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
+
+ /// Allow marking spans.
+ marker: Marker,
+
+ /// The stack of things yet to be completely expanded.
+ ///
+ /// We descend into the RHS (`src`), expanding things as we go. This stack contains the things
+ /// we have yet to expand/are still expanding. We start the stack off with the whole RHS. The
+ /// choice of spacing values doesn't matter.
+ stack: SmallVec<[Frame<'itp>; 1]>,
+
+ /// A stack of where we are in the repeat expansion.
+ ///
+ /// As we descend in the RHS, we will need to be able to match nested sequences of matchers.
+ /// `repeats` keeps track of where we are in matching at each level, with the last element
+ /// being the most deeply nested sequence. This is used as a stack.
+ repeats: Vec<(usize, usize)>,
+
+ /// The resulting token stream from the `TokenTree` we just finished processing.
+ ///
+ /// At the end, this will contain the full result of transcription, but at arbitrary points
+ /// during `transcribe`, `result` will contain subsets of the final result.
+ ///
+ /// Specifically, as we descend into each TokenTree, we will push the existing results onto the
+ /// `result_stack` and clear `results`. We will then produce the results of transcribing the
+ /// TokenTree into `results`. Then, as we unwind back out of the `TokenTree`, we will pop the
+ /// `result_stack` and append `results` too it to produce the new `results` up to that point.
+ ///
+ /// Thus, if we try to pop the `result_stack` and it is empty, we have reached the top-level
+ /// again, and we are done transcribing.
+ result: Vec<TokenTree>,
+
+ /// The in-progress `result` lives at the top of this stack. Each entered `TokenTree` adds a
+ /// new entry.
+ result_stack: Vec<Vec<TokenTree>>,
+}
+
+impl<'psess> TranscrCtx<'psess, '_> {
+ /// Span marked with the correct expansion and transparency.
+ fn visited_dspan(&mut self, dspan: DelimSpan) -> Span {
+ let mut span = dspan.entire();
+ self.marker.mark_span(&mut span);
+ span
+ }
+}
+
+/// A Marker adds the given mark to the syntax context.
+struct Marker {
+ expand_id: LocalExpnId,
+ transparency: Transparency,
+ cache: FxHashMap<SyntaxContext, SyntaxContext>,
+}
impl Marker {
+ /// Mark a span with the stored expansion ID and transparency.
fn mark_span(&mut self, span: &mut Span) {
// `apply_mark` is a relatively expensive operation, both due to taking hygiene lock, and
// by itself. All tokens in a macro body typically have the same syntactic context, unless
// it's some advanced case with macro-generated macros. So if we cache the marked version
// of that context once, we'll typically have a 100% cache hit rate after that.
- let Marker(expn_id, transparency, ref mut cache) = *self;
*span = span.map_ctxt(|ctxt| {
- *cache
+ *self
+ .cache
.entry(ctxt)
- .or_insert_with(|| ctxt.apply_mark(expn_id.to_expn_id(), transparency))
+ .or_insert_with(|| ctxt.apply_mark(self.expand_id.to_expn_id(), self.transparency))
});
}
}
@@ -116,52 +173,36 @@ pub(super) fn transcribe<'a>(
return Ok(TokenStream::default());
}
- // We descend into the RHS (`src`), expanding things as we go. This stack contains the things
- // we have yet to expand/are still expanding. We start the stack off with the whole RHS. The
- // choice of spacing values doesn't matter.
- let mut stack: SmallVec<[Frame<'_>; 1]> = smallvec![Frame::new_delimited(
- src,
- src_span,
- DelimSpacing::new(Spacing::Alone, Spacing::Alone)
- )];
+ let mut tscx = TranscrCtx {
+ psess,
+ interp,
+ marker: Marker { expand_id, transparency, cache: Default::default() },
+ repeats: Vec::new(),
+ stack: smallvec![Frame::new_delimited(
+ src,
+ src_span,
+ DelimSpacing::new(Spacing::Alone, Spacing::Alone)
+ )],
+ result: Vec::new(),
+ result_stack: Vec::new(),
+ };
- // As we descend in the RHS, we will need to be able to match nested sequences of matchers.
- // `repeats` keeps track of where we are in matching at each level, with the last element being
- // the most deeply nested sequence. This is used as a stack.
- let mut repeats: Vec<(usize, usize)> = Vec::new();
-
- // `result` contains resulting token stream from the TokenTree we just finished processing. At
- // the end, this will contain the full result of transcription, but at arbitrary points during
- // `transcribe`, `result` will contain subsets of the final result.
- //
- // Specifically, as we descend into each TokenTree, we will push the existing results onto the
- // `result_stack` and clear `results`. We will then produce the results of transcribing the
- // TokenTree into `results`. Then, as we unwind back out of the `TokenTree`, we will pop the
- // `result_stack` and append `results` too it to produce the new `results` up to that point.
- //
- // Thus, if we try to pop the `result_stack` and it is empty, we have reached the top-level
- // again, and we are done transcribing.
- let mut result: Vec<TokenTree> = Vec::new();
- let mut result_stack = Vec::new();
- let mut marker = Marker(expand_id, transparency, Default::default());
-
- let dcx = psess.dcx();
loop {
// Look at the last frame on the stack.
// If it still has a TokenTree we have not looked at yet, use that tree.
- let Some(tree) = stack.last_mut().unwrap().next() else {
+ let Some(tree) = tscx.stack.last_mut().unwrap().next() else {
// This else-case never produces a value for `tree` (it `continue`s or `return`s).
// Otherwise, if we have just reached the end of a sequence and we can keep repeating,
// go back to the beginning of the sequence.
- let frame = stack.last_mut().unwrap();
+ let frame = tscx.stack.last_mut().unwrap();
if let FrameKind::Sequence { sep, .. } = &frame.kind {
- let (repeat_idx, repeat_len) = repeats.last_mut().unwrap();
+ let (repeat_idx, repeat_len) = tscx.repeats.last_mut().unwrap();
*repeat_idx += 1;
if repeat_idx < repeat_len {
frame.idx = 0;
if let Some(sep) = sep {
- result.push(TokenTree::Token(*sep, Spacing::Alone));
+ tscx.result.push(TokenTree::Token(*sep, Spacing::Alone));
}
continue;
}
@@ -170,10 +211,10 @@ pub(super) fn transcribe<'a>(
// We are done with the top of the stack. Pop it. Depending on what it was, we do
// different things. Note that the outermost item must be the delimited, wrapped RHS
// that was passed in originally to `transcribe`.
- match stack.pop().unwrap().kind {
+ match tscx.stack.pop().unwrap().kind {
// Done with a sequence. Pop from repeats.
FrameKind::Sequence { .. } => {
- repeats.pop();
+ tscx.repeats.pop();
}
// We are done processing a Delimited. If this is the top-level delimited, we are
@@ -185,15 +226,16 @@ pub(super) fn transcribe<'a>(
if delim == Delimiter::Bracket {
spacing.close = Spacing::Alone;
}
- if result_stack.is_empty() {
+ if tscx.result_stack.is_empty() {
// No results left to compute! We are back at the top-level.
- return Ok(TokenStream::new(result));
+ return Ok(TokenStream::new(tscx.result));
}
// Step back into the parent Delimited.
- let tree = TokenTree::Delimited(span, spacing, delim, TokenStream::new(result));
- result = result_stack.pop().unwrap();
- result.push(tree);
+ let tree =
+ TokenTree::Delimited(span, spacing, delim, TokenStream::new(tscx.result));
+ tscx.result = tscx.result_stack.pop().unwrap();
+ tscx.result.push(tree);
}
}
continue;
@@ -202,223 +244,19 @@ pub(super) fn transcribe<'a>(
// At this point, we know we are in the middle of a TokenTree (the last one on `stack`).
// `tree` contains the next `TokenTree` to be processed.
match tree {
- // We are descending into a sequence. We first make sure that the matchers in the RHS
- // and the matches in `interp` have the same shape. Otherwise, either the caller or the
- // macro writer has made a mistake.
+ // Replace the sequence with its expansion.
seq @ mbe::TokenTree::Sequence(_, seq_rep) => {
- match lockstep_iter_size(seq, interp, &repeats) {
- LockstepIterSize::Unconstrained => {
- return Err(dcx.create_err(NoSyntaxVarsExprRepeat { span: seq.span() }));
- }
-
- LockstepIterSize::Contradiction(msg) => {
- // FIXME: this really ought to be caught at macro definition time... It
- // happens when two meta-variables are used in the same repetition in a
- // sequence, but they come from different sequence matchers and repeat
- // different amounts.
- return Err(
- dcx.create_err(MetaVarsDifSeqMatchers { span: seq.span(), msg })
- );
- }
-
- LockstepIterSize::Constraint(len, _) => {
- // We do this to avoid an extra clone above. We know that this is a
- // sequence already.
- let mbe::TokenTree::Sequence(sp, seq) = seq else { unreachable!() };
-
- // Is the repetition empty?
- if len == 0 {
- if seq.kleene.op == KleeneOp::OneOrMore {
- // FIXME: this really ought to be caught at macro definition
- // time... It happens when the Kleene operator in the matcher and
- // the body for the same meta-variable do not match.
- return Err(dcx.create_err(MustRepeatOnce { span: sp.entire() }));
- }
- } else {
- // 0 is the initial counter (we have done 0 repetitions so far). `len`
- // is the total number of repetitions we should generate.
- repeats.push((0, len));
-
- // The first time we encounter the sequence we push it to the stack. It
- // then gets reused (see the beginning of the loop) until we are done
- // repeating.
- stack.push(Frame::new_sequence(
- seq_rep,
- seq.separator.clone(),
- seq.kleene.op,
- ));
- }
- }
- }
+ transcribe_sequence(&mut tscx, seq, seq_rep)?;
}
// Replace the meta-var with the matched token tree from the invocation.
- &mbe::TokenTree::MetaVar(mut sp, mut original_ident) => {
- // Find the matched nonterminal from the macro invocation, and use it to replace
- // the meta-var.
- //
- // We use `Spacing::Alone` everywhere here, because that's the conservative choice
- // and spacing of declarative macros is tricky. E.g. in this macro:
- // ```
- // macro_rules! idents {
- // ($($a:ident,)*) => { stringify!($($a)*) }
- // }
- // ```
- // `$a` has no whitespace after it and will be marked `JointHidden`. If you then
- // call `idents!(x,y,z,)`, each of `x`, `y`, and `z` will be marked as `Joint`. So
- // if you choose to use `$x`'s spacing or the identifier's spacing, you'll end up
- // producing "xyz", which is bad because it effectively merges tokens.
- // `Spacing::Alone` is the safer option. Fortunately, `space_between` will avoid
- // some of the unnecessary whitespace.
- let ident = MacroRulesNormalizedIdent::new(original_ident);
- if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) {
- // We wrap the tokens in invisible delimiters, unless they are already wrapped
- // in invisible delimiters with the same `MetaVarKind`. Because some proc
- // macros can't handle multiple layers of invisible delimiters of the same
- // `MetaVarKind`. This loses some span info, though it hopefully won't matter.
- let mut mk_delimited = |mk_span, mv_kind, mut stream: TokenStream| {
- if stream.len() == 1 {
- let tree = stream.iter().next().unwrap();
- if let TokenTree::Delimited(_, _, delim, inner) = tree
- && let Delimiter::Invisible(InvisibleOrigin::MetaVar(mvk)) = delim
- && mv_kind == *mvk
- {
- stream = inner.clone();
- }
- }
-
- // Emit as a token stream within `Delimiter::Invisible` to maintain
- // parsing priorities.
- marker.mark_span(&mut sp);
- with_metavar_spans(|mspans| mspans.insert(mk_span, sp));
- // Both the open delim and close delim get the same span, which covers the
- // `$foo` in the decl macro RHS.
- TokenTree::Delimited(
- DelimSpan::from_single(sp),
- DelimSpacing::new(Spacing::Alone, Spacing::Alone),
- Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind)),
- stream,
- )
- };
- let tt = match cur_matched {
- MatchedSingle(ParseNtResult::Tt(tt)) => {
- // `tt`s are emitted into the output stream directly as "raw tokens",
- // without wrapping them into groups. Other variables are emitted into
- // the output stream as groups with `Delimiter::Invisible` to maintain
- // parsing priorities.
- maybe_use_metavar_location(psess, &stack, sp, tt, &mut marker)
- }
- MatchedSingle(ParseNtResult::Ident(ident, is_raw)) => {
- marker.mark_span(&mut sp);
- with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
- let kind = token::NtIdent(*ident, *is_raw);
- TokenTree::token_alone(kind, sp)
- }
- MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => {
- marker.mark_span(&mut sp);
- with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
- let kind = token::NtLifetime(*ident, *is_raw);
- TokenTree::token_alone(kind, sp)
- }
- MatchedSingle(ParseNtResult::Item(item)) => {
- mk_delimited(item.span, MetaVarKind::Item, TokenStream::from_ast(item))
- }
- MatchedSingle(ParseNtResult::Block(block)) => mk_delimited(
- block.span,
- MetaVarKind::Block,
- TokenStream::from_ast(block),
- ),
- MatchedSingle(ParseNtResult::Stmt(stmt)) => {
- let stream = if let StmtKind::Empty = stmt.kind {
- // FIXME: Properly collect tokens for empty statements.
- TokenStream::token_alone(token::Semi, stmt.span)
- } else {
- TokenStream::from_ast(stmt)
- };
- mk_delimited(stmt.span, MetaVarKind::Stmt, stream)
- }
- MatchedSingle(ParseNtResult::Pat(pat, pat_kind)) => mk_delimited(
- pat.span,
- MetaVarKind::Pat(*pat_kind),
- TokenStream::from_ast(pat),
- ),
- MatchedSingle(ParseNtResult::Expr(expr, kind)) => {
- let (can_begin_literal_maybe_minus, can_begin_string_literal) =
- match &expr.kind {
- ExprKind::Lit(_) => (true, true),
- ExprKind::Unary(UnOp::Neg, e)
- if matches!(&e.kind, ExprKind::Lit(_)) =>
- {
- (true, false)
- }
- _ => (false, false),
- };
- mk_delimited(
- expr.span,
- MetaVarKind::Expr {
- kind: *kind,
- can_begin_literal_maybe_minus,
- can_begin_string_literal,
- },
- TokenStream::from_ast(expr),
- )
- }
- MatchedSingle(ParseNtResult::Literal(lit)) => {
- mk_delimited(lit.span, MetaVarKind::Literal, TokenStream::from_ast(lit))
- }
- MatchedSingle(ParseNtResult::Ty(ty)) => {
- let is_path = matches!(&ty.kind, TyKind::Path(None, _path));
- mk_delimited(
- ty.span,
- MetaVarKind::Ty { is_path },
- TokenStream::from_ast(ty),
- )
- }
- MatchedSingle(ParseNtResult::Meta(attr_item)) => {
- let has_meta_form = attr_item.meta_kind().is_some();
- mk_delimited(
- attr_item.span(),
- MetaVarKind::Meta { has_meta_form },
- TokenStream::from_ast(attr_item),
- )
- }
- MatchedSingle(ParseNtResult::Path(path)) => {
- mk_delimited(path.span, MetaVarKind::Path, TokenStream::from_ast(path))
- }
- MatchedSingle(ParseNtResult::Vis(vis)) => {
- mk_delimited(vis.span, MetaVarKind::Vis, TokenStream::from_ast(vis))
- }
- MatchedSeq(..) => {
- // We were unable to descend far enough. This is an error.
- return Err(dcx.create_err(VarStillRepeating { span: sp, ident }));
- }
- };
- result.push(tt)
- } else {
- // If we aren't able to match the meta-var, we push it back into the result but
- // with modified syntax context. (I believe this supports nested macros).
- marker.mark_span(&mut sp);
- marker.mark_span(&mut original_ident.span);
- result.push(TokenTree::token_joint_hidden(token::Dollar, sp));
- result.push(TokenTree::Token(
- Token::from_ast_ident(original_ident),
- Spacing::Alone,
- ));
- }
+ &mbe::TokenTree::MetaVar(sp, original_ident) => {
+ transcribe_metavar(&mut tscx, sp, original_ident)?;
}
// Replace meta-variable expressions with the result of their expansion.
- mbe::TokenTree::MetaVarExpr(sp, expr) => {
- transcribe_metavar_expr(
- dcx,
- expr,
- interp,
- &mut marker,
- &repeats,
- &mut result,
- sp,
- &psess.symbol_gallery,
- )?;
+ mbe::TokenTree::MetaVarExpr(dspan, expr) => {
+ transcribe_metavar_expr(&mut tscx, *dspan, expr)?;
}
// If we are entering a new delimiter, we push its contents to the `stack` to be
@@ -427,21 +265,21 @@ pub(super) fn transcribe<'a>(
// jump back out of the Delimited, pop the result_stack and add the new results back to
// the previous results (from outside the Delimited).
&mbe::TokenTree::Delimited(mut span, ref spacing, ref delimited) => {
- marker.mark_span(&mut span.open);
- marker.mark_span(&mut span.close);
- stack.push(Frame::new_delimited(delimited, span, *spacing));
- result_stack.push(mem::take(&mut result));
+ tscx.marker.mark_span(&mut span.open);
+ tscx.marker.mark_span(&mut span.close);
+ tscx.stack.push(Frame::new_delimited(delimited, span, *spacing));
+ tscx.result_stack.push(mem::take(&mut tscx.result));
}
// Nothing much to do here. Just push the token to the result, being careful to
// preserve syntax context.
&mbe::TokenTree::Token(mut token) => {
- marker.mark_span(&mut token.span);
+ tscx.marker.mark_span(&mut token.span);
if let token::NtIdent(ident, _) | token::NtLifetime(ident, _) = &mut token.kind {
- marker.mark_span(&mut ident.span);
+ tscx.marker.mark_span(&mut ident.span);
}
let tt = TokenTree::Token(token, Spacing::Alone);
- result.push(tt);
+ tscx.result.push(tt);
}
// There should be no meta-var declarations in the invocation of a macro.
@@ -450,6 +288,305 @@ pub(super) fn transcribe<'a>(
}
}
+/// Turn `$(...)*` sequences into tokens.
+fn transcribe_sequence<'tx, 'itp>(
+ tscx: &mut TranscrCtx<'tx, 'itp>,
+ seq: &mbe::TokenTree,
+ seq_rep: &'itp mbe::SequenceRepetition,
+) -> PResult<'tx, ()> {
+ let dcx = tscx.psess.dcx();
+
+ // We are descending into a sequence. We first make sure that the matchers in the RHS
+ // and the matches in `interp` have the same shape. Otherwise, either the caller or the
+ // macro writer has made a mistake.
+ match lockstep_iter_size(seq, tscx.interp, &tscx.repeats) {
+ LockstepIterSize::Unconstrained => {
+ return Err(dcx.create_err(NoSyntaxVarsExprRepeat { span: seq.span() }));
+ }
+
+ LockstepIterSize::Contradiction(msg) => {
+ // FIXME: this really ought to be caught at macro definition time... It
+ // happens when two meta-variables are used in the same repetition in a
+ // sequence, but they come from different sequence matchers and repeat
+ // different amounts.
+ return Err(dcx.create_err(MetaVarsDifSeqMatchers { span: seq.span(), msg }));
+ }
+
+ LockstepIterSize::Constraint(len, _) => {
+ // We do this to avoid an extra clone above. We know that this is a
+ // sequence already.
+ let mbe::TokenTree::Sequence(sp, seq) = seq else { unreachable!() };
+
+ // Is the repetition empty?
+ if len == 0 {
+ if seq.kleene.op == KleeneOp::OneOrMore {
+ // FIXME: this really ought to be caught at macro definition
+ // time... It happens when the Kleene operator in the matcher and
+ // the body for the same meta-variable do not match.
+ return Err(dcx.create_err(MustRepeatOnce { span: sp.entire() }));
+ }
+ } else {
+ // 0 is the initial counter (we have done 0 repetitions so far). `len`
+ // is the total number of repetitions we should generate.
+ tscx.repeats.push((0, len));
+
+ // The first time we encounter the sequence we push it to the stack. It
+ // then gets reused (see the beginning of the loop) until we are done
+ // repeating.
+ tscx.stack.push(Frame::new_sequence(seq_rep, seq.separator.clone(), seq.kleene.op));
+ }
+ }
+ }
+
+ Ok(())
+}
+
+/// Find the matched nonterminal from the macro invocation, and use it to replace
+/// the meta-var.
+///
+/// We use `Spacing::Alone` everywhere here, because that's the conservative choice
+/// and spacing of declarative macros is tricky. E.g. in this macro:
+/// ```
+/// macro_rules! idents {
+/// ($($a:ident,)*) => { stringify!($($a)*) }
+/// }
+/// ```
+/// `$a` has no whitespace after it and will be marked `JointHidden`. If you then
+/// call `idents!(x,y,z,)`, each of `x`, `y`, and `z` will be marked as `Joint`. So
+/// if you choose to use `$x`'s spacing or the identifier's spacing, you'll end up
+/// producing "xyz", which is bad because it effectively merges tokens.
+/// `Spacing::Alone` is the safer option. Fortunately, `space_between` will avoid
+/// some of the unnecessary whitespace.
+fn transcribe_metavar<'tx>(
+ tscx: &mut TranscrCtx<'tx, '_>,
+ mut sp: Span,
+ mut original_ident: Ident,
+) -> PResult<'tx, ()> {
+ let dcx = tscx.psess.dcx();
+
+ let ident = MacroRulesNormalizedIdent::new(original_ident);
+ let Some(cur_matched) = lookup_cur_matched(ident, tscx.interp, &tscx.repeats) else {
+ // If we aren't able to match the meta-var, we push it back into the result but
+ // with modified syntax context. (I believe this supports nested macros).
+ tscx.marker.mark_span(&mut sp);
+ tscx.marker.mark_span(&mut original_ident.span);
+ tscx.result.push(TokenTree::token_joint_hidden(token::Dollar, sp));
+ tscx.result.push(TokenTree::Token(Token::from_ast_ident(original_ident), Spacing::Alone));
+ return Ok(());
+ };
+
+ // We wrap the tokens in invisible delimiters, unless they are already wrapped
+ // in invisible delimiters with the same `MetaVarKind`. Because some proc
+ // macros can't handle multiple layers of invisible delimiters of the same
+ // `MetaVarKind`. This loses some span info, though it hopefully won't matter.
+ let mut mk_delimited = |mk_span, mv_kind, mut stream: TokenStream| {
+ if stream.len() == 1 {
+ let tree = stream.iter().next().unwrap();
+ if let TokenTree::Delimited(_, _, delim, inner) = tree
+ && let Delimiter::Invisible(InvisibleOrigin::MetaVar(mvk)) = delim
+ && mv_kind == *mvk
+ {
+ stream = inner.clone();
+ }
+ }
+
+ // Emit as a token stream within `Delimiter::Invisible` to maintain
+ // parsing priorities.
+ tscx.marker.mark_span(&mut sp);
+ with_metavar_spans(|mspans| mspans.insert(mk_span, sp));
+ // Both the open delim and close delim get the same span, which covers the
+ // `$foo` in the decl macro RHS.
+ TokenTree::Delimited(
+ DelimSpan::from_single(sp),
+ DelimSpacing::new(Spacing::Alone, Spacing::Alone),
+ Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind)),
+ stream,
+ )
+ };
+
+ let tt = match cur_matched {
+ MatchedSingle(ParseNtResult::Tt(tt)) => {
+ // `tt`s are emitted into the output stream directly as "raw tokens",
+ // without wrapping them into groups. Other variables are emitted into
+ // the output stream as groups with `Delimiter::Invisible` to maintain
+ // parsing priorities.
+ maybe_use_metavar_location(tscx.psess, &tscx.stack, sp, tt, &mut tscx.marker)
+ }
+ MatchedSingle(ParseNtResult::Ident(ident, is_raw)) => {
+ tscx.marker.mark_span(&mut sp);
+ with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
+ let kind = token::NtIdent(*ident, *is_raw);
+ TokenTree::token_alone(kind, sp)
+ }
+ MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => {
+ tscx.marker.mark_span(&mut sp);
+ with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
+ let kind = token::NtLifetime(*ident, *is_raw);
+ TokenTree::token_alone(kind, sp)
+ }
+ MatchedSingle(ParseNtResult::Item(item)) => {
+ mk_delimited(item.span, MetaVarKind::Item, TokenStream::from_ast(item))
+ }
+ MatchedSingle(ParseNtResult::Block(block)) => {
+ mk_delimited(block.span, MetaVarKind::Block, TokenStream::from_ast(block))
+ }
+ MatchedSingle(ParseNtResult::Stmt(stmt)) => {
+ let stream = if let StmtKind::Empty = stmt.kind {
+ // FIXME: Properly collect tokens for empty statements.
+ TokenStream::token_alone(token::Semi, stmt.span)
+ } else {
+ TokenStream::from_ast(stmt)
+ };
+ mk_delimited(stmt.span, MetaVarKind::Stmt, stream)
+ }
+ MatchedSingle(ParseNtResult::Pat(pat, pat_kind)) => {
+ mk_delimited(pat.span, MetaVarKind::Pat(*pat_kind), TokenStream::from_ast(pat))
+ }
+ MatchedSingle(ParseNtResult::Expr(expr, kind)) => {
+ let (can_begin_literal_maybe_minus, can_begin_string_literal) = match &expr.kind {
+ ExprKind::Lit(_) => (true, true),
+ ExprKind::Unary(UnOp::Neg, e) if matches!(&e.kind, ExprKind::Lit(_)) => {
+ (true, false)
+ }
+ _ => (false, false),
+ };
+ mk_delimited(
+ expr.span,
+ MetaVarKind::Expr {
+ kind: *kind,
+ can_begin_literal_maybe_minus,
+ can_begin_string_literal,
+ },
+ TokenStream::from_ast(expr),
+ )
+ }
+ MatchedSingle(ParseNtResult::Literal(lit)) => {
+ mk_delimited(lit.span, MetaVarKind::Literal, TokenStream::from_ast(lit))
+ }
+ MatchedSingle(ParseNtResult::Ty(ty)) => {
+ let is_path = matches!(&ty.kind, TyKind::Path(None, _path));
+ mk_delimited(ty.span, MetaVarKind::Ty { is_path }, TokenStream::from_ast(ty))
+ }
+ MatchedSingle(ParseNtResult::Meta(attr_item)) => {
+ let has_meta_form = attr_item.meta_kind().is_some();
+ mk_delimited(
+ attr_item.span(),
+ MetaVarKind::Meta { has_meta_form },
+ TokenStream::from_ast(attr_item),
+ )
+ }
+ MatchedSingle(ParseNtResult::Path(path)) => {
+ mk_delimited(path.span, MetaVarKind::Path, TokenStream::from_ast(path))
+ }
+ MatchedSingle(ParseNtResult::Vis(vis)) => {
+ mk_delimited(vis.span, MetaVarKind::Vis, TokenStream::from_ast(vis))
+ }
+ MatchedSeq(..) => {
+ // We were unable to descend far enough. This is an error.
+ return Err(dcx.create_err(VarStillRepeating { span: sp, ident }));
+ }
+ };
+
+ tscx.result.push(tt);
+ Ok(())
+}
+
+/// Turn `${expr(...)}` metavariable expressionss into tokens.
+fn transcribe_metavar_expr<'tx>(
+ tscx: &mut TranscrCtx<'tx, '_>,
+ dspan: DelimSpan,
+ expr: &MetaVarExpr,
+) -> PResult<'tx, ()> {
+ let dcx = tscx.psess.dcx();
+ let tt = match *expr {
+ MetaVarExpr::Concat(ref elements) => metavar_expr_concat(tscx, dspan, elements)?,
+ MetaVarExpr::Count(original_ident, depth) => {
+ let matched = matched_from_ident(dcx, original_ident, tscx.interp)?;
+ let count = count_repetitions(dcx, depth, matched, &tscx.repeats, &dspan)?;
+ TokenTree::token_alone(
+ TokenKind::lit(token::Integer, sym::integer(count), None),
+ tscx.visited_dspan(dspan),
+ )
+ }
+ MetaVarExpr::Ignore(original_ident) => {
+ // Used to ensure that `original_ident` is present in the LHS
+ let _ = matched_from_ident(dcx, original_ident, tscx.interp)?;
+ return Ok(());
+ }
+ MetaVarExpr::Index(depth) => match tscx.repeats.iter().nth_back(depth) {
+ Some((index, _)) => TokenTree::token_alone(
+ TokenKind::lit(token::Integer, sym::integer(*index), None),
+ tscx.visited_dspan(dspan),
+ ),
+ None => {
+ return Err(out_of_bounds_err(dcx, tscx.repeats.len(), dspan.entire(), "index"));
+ }
+ },
+ MetaVarExpr::Len(depth) => match tscx.repeats.iter().nth_back(depth) {
+ Some((_, length)) => TokenTree::token_alone(
+ TokenKind::lit(token::Integer, sym::integer(*length), None),
+ tscx.visited_dspan(dspan),
+ ),
+ None => {
+ return Err(out_of_bounds_err(dcx, tscx.repeats.len(), dspan.entire(), "len"));
+ }
+ },
+ };
+ tscx.result.push(tt);
+ Ok(())
+}
+
+/// Handle the `${concat(...)}` metavariable expression.
+fn metavar_expr_concat<'tx>(
+ tscx: &mut TranscrCtx<'tx, '_>,
+ dspan: DelimSpan,
+ elements: &[MetaVarExprConcatElem],
+) -> PResult<'tx, TokenTree> {
+ let dcx = tscx.psess.dcx();
+ let mut concatenated = String::new();
+ for element in elements.into_iter() {
+ let symbol = match element {
+ MetaVarExprConcatElem::Ident(elem) => elem.name,
+ MetaVarExprConcatElem::Literal(elem) => *elem,
+ MetaVarExprConcatElem::Var(ident) => {
+ match matched_from_ident(dcx, *ident, tscx.interp)? {
+ NamedMatch::MatchedSeq(named_matches) => {
+ let Some((curr_idx, _)) = tscx.repeats.last() else {
+ return Err(dcx.struct_span_err(dspan.entire(), "invalid syntax"));
+ };
+ match &named_matches[*curr_idx] {
+ // FIXME(c410-f3r) Nested repetitions are unimplemented
+ MatchedSeq(_) => unimplemented!(),
+ MatchedSingle(pnr) => extract_symbol_from_pnr(dcx, pnr, ident.span)?,
+ }
+ }
+ NamedMatch::MatchedSingle(pnr) => {
+ extract_symbol_from_pnr(dcx, pnr, ident.span)?
+ }
+ }
+ }
+ };
+ concatenated.push_str(symbol.as_str());
+ }
+ let symbol = nfc_normalize(&concatenated);
+ let concatenated_span = tscx.visited_dspan(dspan);
+ if !rustc_lexer::is_ident(symbol.as_str()) {
+ return Err(dcx.struct_span_err(
+ concatenated_span,
+ "`${concat(..)}` is not generating a valid identifier",
+ ));
+ }
+ tscx.psess.symbol_gallery.insert(symbol, concatenated_span);
+
+ // The current implementation marks the span as coming from the macro regardless of
+ // contexts of the concatenated identifiers but this behavior may change in the
+ // future.
+ Ok(TokenTree::Token(
+ Token::from_ast_ident(Ident::new(symbol, concatenated_span)),
+ Spacing::Alone,
+ ))
+}
+
/// Store the metavariable span for this original span into a side table.
/// FIXME: Try to put the metavariable span into `SpanData` instead of a side table (#118517).
/// An optimal encoding for inlined spans will need to be selected to minimize regressions.
@@ -671,13 +808,13 @@ fn lockstep_iter_size(
/// * `[ $( ${count(foo, 0)} ),* ]` will be the same as `[ $( ${count(foo)} ),* ]`
/// * `[ $( ${count(foo, 1)} ),* ]` will return an error because `${count(foo, 1)}` is
/// declared inside a single repetition and the index `1` implies two nested repetitions.
-fn count_repetitions<'a>(
- dcx: DiagCtxtHandle<'a>,
+fn count_repetitions<'dx>(
+ dcx: DiagCtxtHandle<'dx>,
depth_user: usize,
mut matched: &NamedMatch,
repeats: &[(usize, usize)],
sp: &DelimSpan,
-) -> PResult<'a, usize> {
+) -> PResult<'dx, usize> {
// Recursively count the number of matches in `matched` at given depth
// (or at the top-level of `matched` if no depth is given).
fn count<'a>(depth_curr: usize, depth_max: usize, matched: &NamedMatch) -> PResult<'a, usize> {
@@ -762,102 +899,6 @@ fn out_of_bounds_err<'a>(dcx: DiagCtxtHandle<'a>, max: usize, span: Span, ty: &s
dcx.struct_span_err(span, msg)
}
-fn transcribe_metavar_expr<'a>(
- dcx: DiagCtxtHandle<'a>,
- expr: &MetaVarExpr,
- interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
- marker: &mut Marker,
- repeats: &[(usize, usize)],
- result: &mut Vec<TokenTree>,
- sp: &DelimSpan,
- symbol_gallery: &SymbolGallery,
-) -> PResult<'a, ()> {
- let mut visited_span = || {
- let mut span = sp.entire();
- marker.mark_span(&mut span);
- span
- };
- match *expr {
- MetaVarExpr::Concat(ref elements) => {
- let mut concatenated = String::new();
- for element in elements.into_iter() {
- let symbol = match element {
- MetaVarExprConcatElem::Ident(elem) => elem.name,
- MetaVarExprConcatElem::Literal(elem) => *elem,
- MetaVarExprConcatElem::Var(ident) => {
- match matched_from_ident(dcx, *ident, interp)? {
- NamedMatch::MatchedSeq(named_matches) => {
- let Some((curr_idx, _)) = repeats.last() else {
- return Err(dcx.struct_span_err(sp.entire(), "invalid syntax"));
- };
- match &named_matches[*curr_idx] {
- // FIXME(c410-f3r) Nested repetitions are unimplemented
- MatchedSeq(_) => unimplemented!(),
- MatchedSingle(pnr) => {
- extract_symbol_from_pnr(dcx, pnr, ident.span)?
- }
- }
- }
- NamedMatch::MatchedSingle(pnr) => {
- extract_symbol_from_pnr(dcx, pnr, ident.span)?
- }
- }
- }
- };
- concatenated.push_str(symbol.as_str());
- }
- let symbol = nfc_normalize(&concatenated);
- let concatenated_span = visited_span();
- if !rustc_lexer::is_ident(symbol.as_str()) {
- return Err(dcx.struct_span_err(
- concatenated_span,
- "`${concat(..)}` is not generating a valid identifier",
- ));
- }
- symbol_gallery.insert(symbol, concatenated_span);
- // The current implementation marks the span as coming from the macro regardless of
- // contexts of the concatenated identifiers but this behavior may change in the
- // future.
- result.push(TokenTree::Token(
- Token::from_ast_ident(Ident::new(symbol, concatenated_span)),
- Spacing::Alone,
- ));
- }
- MetaVarExpr::Count(original_ident, depth) => {
- let matched = matched_from_ident(dcx, original_ident, interp)?;
- let count = count_repetitions(dcx, depth, matched, repeats, sp)?;
- let tt = TokenTree::token_alone(
- TokenKind::lit(token::Integer, sym::integer(count), None),
- visited_span(),
- );
- result.push(tt);
- }
- MetaVarExpr::Ignore(original_ident) => {
- // Used to ensure that `original_ident` is present in the LHS
- let _ = matched_from_ident(dcx, original_ident, interp)?;
- }
- MetaVarExpr::Index(depth) => match repeats.iter().nth_back(depth) {
- Some((index, _)) => {
- result.push(TokenTree::token_alone(
- TokenKind::lit(token::Integer, sym::integer(*index), None),
- visited_span(),
- ));
- }
- None => return Err(out_of_bounds_err(dcx, repeats.len(), sp.entire(), "index")),
- },
- MetaVarExpr::Len(depth) => match repeats.iter().nth_back(depth) {
- Some((_, length)) => {
- result.push(TokenTree::token_alone(
- TokenKind::lit(token::Integer, sym::integer(*length), None),
- visited_span(),
- ));
- }
- None => return Err(out_of_bounds_err(dcx, repeats.len(), sp.entire(), "len")),
- },
- }
- Ok(())
-}
-
/// Extracts an metavariable symbol that can be an identifier, a token tree or a literal.
fn extract_symbol_from_pnr<'a>(
dcx: DiagCtxtHandle<'a>,
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index ca24d5a..bf2d4f6 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -311,9 +311,7 @@ fn default_body_is_unstable(
reason: reason_str,
});
- let inject_span = item_did
- .as_local()
- .and_then(|id| tcx.crate_level_attribute_injection_span(tcx.local_def_id_to_hir_id(id)));
+ let inject_span = item_did.is_local().then(|| tcx.crate_level_attribute_injection_span());
rustc_session::parse::add_feature_diagnostics_for_issue(
&mut err,
&tcx.sess,
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 20d0e87..13f9502 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1064,7 +1064,7 @@ fn ty_is_local(ty: Ty<'_>) -> bool {
Ok(..) => Some(vec![(adt_const_params_feature_string, sym::adt_const_params)]),
};
if let Some(features) = may_suggest_feature {
- tcx.disabled_nightly_features(&mut diag, Some(param.hir_id), features);
+ tcx.disabled_nightly_features(&mut diag, features);
}
Err(diag.emit())
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index abb8cdc..5f59b3a 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -30,8 +30,6 @@ pub(crate) struct BaseExpressionDoubleDot {
)]
pub default_field_values_suggestion: Option<Span>,
#[subdiagnostic]
- pub default_field_values_help: Option<BaseExpressionDoubleDotEnableDefaultFieldValues>,
- #[subdiagnostic]
pub add_expr: Option<BaseExpressionDoubleDotAddExpr>,
#[subdiagnostic]
pub remove_dots: Option<BaseExpressionDoubleDotRemove>,
@@ -61,10 +59,6 @@ pub(crate) struct BaseExpressionDoubleDotAddExpr {
pub span: Span,
}
-#[derive(Subdiagnostic)]
-#[help(hir_typeck_base_expression_double_dot_enable_default_field_values)]
-pub(crate) struct BaseExpressionDoubleDotEnableDefaultFieldValues;
-
#[derive(Diagnostic)]
#[diag(hir_typeck_field_multiply_specified_in_initializer, code = E0062)]
pub(crate) struct FieldMultiplySpecifiedInInitializer {
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 55c39d9..eca9c5f 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -43,10 +43,9 @@
use crate::coercion::{CoerceMany, DynamicCoerceMany};
use crate::errors::{
AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr,
- BaseExpressionDoubleDotEnableDefaultFieldValues, BaseExpressionDoubleDotRemove,
- CantDereference, FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct,
- HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnType, NoFieldOnVariant,
- ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive,
+ BaseExpressionDoubleDotRemove, CantDereference, FieldMultiplySpecifiedInInitializer,
+ FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnType,
+ NoFieldOnVariant, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive,
TypeMismatchFruTypo, YieldExprOutsideOfCoroutine,
};
use crate::{
@@ -2158,7 +2157,7 @@ fn check_expr_struct_fields(
}
}
if !self.tcx.features().default_field_values() {
- let sugg = self.tcx.crate_level_attribute_injection_span(expr.hir_id);
+ let sugg = self.tcx.crate_level_attribute_injection_span();
self.dcx().emit_err(BaseExpressionDoubleDot {
span: span.shrink_to_hi(),
// We only mention enabling the feature if this is a nightly rustc *and* the
@@ -2166,18 +2165,8 @@ fn check_expr_struct_fields(
default_field_values_suggestion: if self.tcx.sess.is_nightly_build()
&& missing_mandatory_fields.is_empty()
&& !missing_optional_fields.is_empty()
- && sugg.is_some()
{
- sugg
- } else {
- None
- },
- default_field_values_help: if self.tcx.sess.is_nightly_build()
- && missing_mandatory_fields.is_empty()
- && !missing_optional_fields.is_empty()
- && sugg.is_none()
- {
- Some(BaseExpressionDoubleDotEnableDefaultFieldValues)
+ Some(sugg)
} else {
None
},
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index a3fdf20..589dbb5 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -1727,7 +1727,6 @@ pub(crate) fn maybe_emit_unstable_name_collision_hint(
}
tcx.disabled_nightly_features(
lint,
- Some(scope_expr_id),
self.unstable_candidates.iter().map(|(candidate, feature)| {
(format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature)
}),
diff --git a/compiler/rustc_infer/src/infer/canonical/instantiate.rs b/compiler/rustc_infer/src/infer/canonical/instantiate.rs
index 67f1319..2385c68 100644
--- a/compiler/rustc_infer/src/infer/canonical/instantiate.rs
+++ b/compiler/rustc_infer/src/infer/canonical/instantiate.rs
@@ -7,8 +7,11 @@
//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
use rustc_macros::extension;
-use rustc_middle::bug;
-use rustc_middle::ty::{self, FnMutDelegate, GenericArgKind, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{
+ self, DelayedMap, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable,
+ TypeVisitableExt, TypeVisitor,
+};
+use rustc_type_ir::TypeVisitable;
use crate::infer::canonical::{Canonical, CanonicalVarValues};
@@ -58,23 +61,169 @@ pub(super) fn instantiate_value<'tcx, T>(
T: TypeFoldable<TyCtxt<'tcx>>,
{
if var_values.var_values.is_empty() {
- value
- } else {
- let delegate = FnMutDelegate {
- regions: &mut |br: ty::BoundRegion| match var_values[br.var].kind() {
- GenericArgKind::Lifetime(l) => l,
- r => bug!("{:?} is a region but value is {:?}", br, r),
- },
- types: &mut |bound_ty: ty::BoundTy| match var_values[bound_ty.var].kind() {
- GenericArgKind::Type(ty) => ty,
- r => bug!("{:?} is a type but value is {:?}", bound_ty, r),
- },
- consts: &mut |bound_ct: ty::BoundVar| match var_values[bound_ct].kind() {
- GenericArgKind::Const(ct) => ct,
- c => bug!("{:?} is a const but value is {:?}", bound_ct, c),
- },
- };
-
- tcx.replace_escaping_bound_vars_uncached(value, delegate)
+ return value;
}
+
+ value.fold_with(&mut CanonicalInstantiator {
+ tcx,
+ current_index: ty::INNERMOST,
+ var_values: var_values.var_values,
+ cache: Default::default(),
+ })
+}
+
+/// Replaces the bound vars in a canonical binder with var values.
+struct CanonicalInstantiator<'tcx> {
+ tcx: TyCtxt<'tcx>,
+
+ // The values that the bound vars are are being instantiated with.
+ var_values: ty::GenericArgsRef<'tcx>,
+
+ /// As with `BoundVarReplacer`, represents the index of a binder *just outside*
+ /// the ones we have visited.
+ current_index: ty::DebruijnIndex,
+
+ // Instantiation is a pure function of `DebruijnIndex` and `Ty`.
+ cache: DelayedMap<(ty::DebruijnIndex, Ty<'tcx>), Ty<'tcx>>,
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for CanonicalInstantiator<'tcx> {
+ fn cx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+ &mut self,
+ t: ty::Binder<'tcx, T>,
+ ) -> ty::Binder<'tcx, T> {
+ self.current_index.shift_in(1);
+ let t = t.super_fold_with(self);
+ self.current_index.shift_out(1);
+ t
+ }
+
+ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+ match *t.kind() {
+ ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => {
+ self.var_values[bound_ty.var.as_usize()].expect_ty()
+ }
+ _ => {
+ if !t.has_vars_bound_at_or_above(self.current_index) {
+ t
+ } else if let Some(&t) = self.cache.get(&(self.current_index, t)) {
+ t
+ } else {
+ let res = t.super_fold_with(self);
+ assert!(self.cache.insert((self.current_index, t), res));
+ res
+ }
+ }
+ }
+ }
+
+ fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+ match r.kind() {
+ ty::ReBound(debruijn, br) if debruijn == self.current_index => {
+ self.var_values[br.var.as_usize()].expect_region()
+ }
+ _ => r,
+ }
+ }
+
+ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+ match ct.kind() {
+ ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => {
+ self.var_values[bound_const.as_usize()].expect_const()
+ }
+ _ => ct.super_fold_with(self),
+ }
+ }
+
+ fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+ if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+ }
+
+ fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> {
+ if !c.has_vars_bound_at_or_above(self.current_index) {
+ return c;
+ }
+
+ // Since instantiation is a function of `DebruijnIndex`, we don't want
+ // to have to cache more copies of clauses when we're inside of binders.
+ // Since we currently expect to only have clauses in the outermost
+ // debruijn index, we just fold if we're inside of a binder.
+ if self.current_index > ty::INNERMOST {
+ return c.super_fold_with(self);
+ }
+
+ // Our cache key is `(clauses, var_values)`, but we also don't care about
+ // var values that aren't named in the clauses, since they can change without
+ // affecting the output. Since `ParamEnv`s are cached first, we compute the
+ // last var value that is mentioned in the clauses, and cut off the list so
+ // that we have more hits in the cache.
+
+ // We also cache the computation of "highest var named by clauses" since that
+ // is both expensive (depending on the size of the clauses) and a pure function.
+ let index = *self
+ .tcx
+ .highest_var_in_clauses_cache
+ .lock()
+ .entry(c)
+ .or_insert_with(|| highest_var_in_clauses(c));
+ let c_args = &self.var_values[..=index];
+
+ if let Some(c) = self.tcx.clauses_cache.lock().get(&(c, c_args)) {
+ c
+ } else {
+ let folded = c.super_fold_with(self);
+ self.tcx.clauses_cache.lock().insert((c, c_args), folded);
+ folded
+ }
+ }
+}
+
+fn highest_var_in_clauses<'tcx>(c: ty::Clauses<'tcx>) -> usize {
+ struct HighestVarInClauses {
+ max_var: usize,
+ current_index: ty::DebruijnIndex,
+ }
+ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HighestVarInClauses {
+ fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
+ &mut self,
+ t: &ty::Binder<'tcx, T>,
+ ) -> Self::Result {
+ self.current_index.shift_in(1);
+ let t = t.super_visit_with(self);
+ self.current_index.shift_out(1);
+ t
+ }
+ fn visit_ty(&mut self, t: Ty<'tcx>) {
+ if let ty::Bound(debruijn, bound_ty) = *t.kind()
+ && debruijn == self.current_index
+ {
+ self.max_var = self.max_var.max(bound_ty.var.as_usize());
+ } else if t.has_vars_bound_at_or_above(self.current_index) {
+ t.super_visit_with(self);
+ }
+ }
+ fn visit_region(&mut self, r: ty::Region<'tcx>) {
+ if let ty::ReBound(debruijn, bound_region) = r.kind()
+ && debruijn == self.current_index
+ {
+ self.max_var = self.max_var.max(bound_region.var.as_usize());
+ }
+ }
+ fn visit_const(&mut self, ct: ty::Const<'tcx>) {
+ if let ty::ConstKind::Bound(debruijn, bound_const) = ct.kind()
+ && debruijn == self.current_index
+ {
+ self.max_var = self.max_var.max(bound_const.as_usize());
+ } else if ct.has_vars_bound_at_or_above(self.current_index) {
+ ct.super_visit_with(self);
+ }
+ }
+ }
+ let mut visitor = HighestVarInClauses { max_var: 0, current_index: ty::INNERMOST };
+ c.visit_with(&mut visitor);
+ visitor.max_var
}
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index e824e9d..d62bf7f 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -52,10 +52,9 @@ pub struct Compiler {
pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg {
cfgs.into_iter()
.map(|s| {
- let psess = ParseSess::with_silent_emitter(
+ let psess = ParseSess::with_fatal_emitter(
vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
format!("this error occurred on the command line: `--cfg={s}`"),
- true,
);
let filename = FileName::cfg_spec_source_code(&s);
@@ -116,10 +115,9 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() };
for s in specs {
- let psess = ParseSess::with_silent_emitter(
+ let psess = ParseSess::with_fatal_emitter(
vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
format!("this error occurred on the command line: `--check-cfg={s}`"),
- true,
);
let filename = FileName::cfg_spec_source_code(&s);
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 02d1ebd..70ae914 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -1011,8 +1011,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
// Prefetch this to prevent multiple threads from blocking on it later.
// This is needed since the `hir_id_validator::check_crate` call above is not guaranteed
- // to use `hir_crate`.
- tcx.ensure_done().hir_crate(());
+ // to use `hir_crate_items`.
+ tcx.ensure_done().hir_crate_items(());
let sess = tcx.sess;
sess.time("misc_checking_1", || {
diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs
index 4c2b82a..481e116 100644
--- a/compiler/rustc_lint/src/expect.rs
+++ b/compiler/rustc_lint/src/expect.rs
@@ -1,5 +1,4 @@
use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::CRATE_OWNER_ID;
use rustc_middle::lint::LintExpectation;
use rustc_middle::query::Providers;
use rustc_middle::ty::TyCtxt;
@@ -18,7 +17,7 @@ fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExp
let mut expectations = Vec::new();
- for owner in std::iter::once(CRATE_OWNER_ID).chain(krate.owners()) {
+ for owner in krate.owners() {
let lints = tcx.shallow_lint_levels_on(owner);
expectations.extend_from_slice(&lints.expectations);
}
diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs
index e5e1ae5..03bb970 100644
--- a/compiler/rustc_middle/src/hir/map.rs
+++ b/compiler/rustc_middle/src/hir/map.rs
@@ -328,8 +328,7 @@ pub fn hir_body_const_context(self, def_id: LocalDefId) -> Option<ConstContext>
}
/// Returns an iterator of the `DefId`s for all body-owners in this
- /// crate. If you would prefer to iterate over the bodies
- /// themselves, you can do `self.hir_crate(()).body_ids.iter()`.
+ /// crate.
#[inline]
pub fn hir_body_owners(self) -> impl Iterator<Item = LocalDefId> {
self.hir_crate_items(()).body_owners.iter().copied()
@@ -396,12 +395,11 @@ pub fn hir_walk_attributes<V>(self, visitor: &mut V) -> V::Result
where
V: Visitor<'tcx>,
{
- let krate = self.hir_crate(());
- for info in krate.owners.iter() {
- if let MaybeOwner::Owner(info) = info {
- for attrs in info.attrs.map.values() {
- walk_list!(visitor, visit_attribute, *attrs);
- }
+ let krate = self.hir_crate_items(());
+ for owner in krate.owners() {
+ let attrs = self.hir_attr_map(owner);
+ for attrs in attrs.map.values() {
+ walk_list!(visitor, visit_attribute, *attrs);
}
}
V::Result::output()
@@ -1225,6 +1223,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod
..
} = collector;
ModuleItems {
+ add_root: false,
submodules: submodules.into_boxed_slice(),
free_items: items.into_boxed_slice(),
trait_items: trait_items.into_boxed_slice(),
@@ -1260,6 +1259,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems {
} = collector;
ModuleItems {
+ add_root: true,
submodules: submodules.into_boxed_slice(),
free_items: items.into_boxed_slice(),
trait_items: trait_items.into_boxed_slice(),
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index 9f79ed4..d7a8dce 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -24,6 +24,9 @@
/// bodies. The Ids are in visitor order. This is used to partition a pass between modules.
#[derive(Debug, HashStable, Encodable, Decodable)]
pub struct ModuleItems {
+ /// Whether this represents the whole crate, in which case we need to add `CRATE_OWNER_ID` to
+ /// the iterators if we want to account for the crate root.
+ add_root: bool,
submodules: Box<[OwnerId]>,
free_items: Box<[ItemId]>,
trait_items: Box<[TraitItemId]>,
@@ -66,9 +69,10 @@ pub fn foreign_items(&self) -> impl Iterator<Item = ForeignItemId> {
}
pub fn owners(&self) -> impl Iterator<Item = OwnerId> {
- self.free_items
- .iter()
- .map(|id| id.owner_id)
+ self.add_root
+ .then_some(CRATE_OWNER_ID)
+ .into_iter()
+ .chain(self.free_items.iter().map(|id| id.owner_id))
.chain(self.trait_items.iter().map(|id| id.owner_id))
.chain(self.impl_items.iter().map(|id| id.owner_id))
.chain(self.foreign_items.iter().map(|id| id.owner_id))
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index f1395c2..4f8cfd8 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -1479,6 +1479,12 @@ pub struct GlobalCtxt<'tcx> {
pub canonical_param_env_cache: CanonicalParamEnvCache<'tcx>,
+ /// Caches the index of the highest bound var in clauses in a canonical binder.
+ pub highest_var_in_clauses_cache: Lock<FxHashMap<ty::Clauses<'tcx>, usize>>,
+ /// Caches the instantiation of a canonical binder given a set of args.
+ pub clauses_cache:
+ Lock<FxHashMap<(ty::Clauses<'tcx>, &'tcx [ty::GenericArg<'tcx>]), ty::Clauses<'tcx>>>,
+
/// Data layout specification for the current target.
pub data_layout: TargetDataLayout,
@@ -1727,6 +1733,8 @@ pub fn create_global_ctxt<T>(
new_solver_evaluation_cache: Default::default(),
new_solver_canonical_param_env_cache: Default::default(),
canonical_param_env_cache: Default::default(),
+ highest_var_in_clauses_cache: Default::default(),
+ clauses_cache: Default::default(),
data_layout,
alloc_map: interpret::AllocMap::new(),
current_gcx,
@@ -2113,7 +2121,7 @@ pub fn def_path_hash_to_def_index_map(
) -> &'tcx rustc_hir::def_path_hash_map::DefPathHashMap {
// Create a dependency to the crate to be sure we re-execute this when the amount of
// definitions change.
- self.ensure_ok().hir_crate(());
+ self.ensure_ok().hir_crate_items(());
// Freeze definitions once we start iterating on them, to prevent adding new ones
// while iterating. If some query needs to add definitions, it should be `ensure`d above.
self.untracked.definitions.freeze().def_path_hash_to_def_index_map()
@@ -3160,42 +3168,33 @@ pub fn node_span_lint(
lint_level(self.sess, lint, level, Some(span.into()), decorate);
}
- /// Find the crate root and the appropriate span where `use` and outer attributes can be
- /// inserted at.
- pub fn crate_level_attribute_injection_span(self, hir_id: HirId) -> Option<Span> {
- for (_hir_id, node) in self.hir_parent_iter(hir_id) {
- if let hir::Node::Crate(m) = node {
- return Some(m.spans.inject_use_span.shrink_to_lo());
- }
- }
- None
+ /// Find the appropriate span where `use` and outer attributes can be inserted at.
+ pub fn crate_level_attribute_injection_span(self) -> Span {
+ let node = self.hir_node(hir::CRATE_HIR_ID);
+ let hir::Node::Crate(m) = node else { bug!() };
+ m.spans.inject_use_span.shrink_to_lo()
}
pub fn disabled_nightly_features<E: rustc_errors::EmissionGuarantee>(
self,
diag: &mut Diag<'_, E>,
- hir_id: Option<HirId>,
features: impl IntoIterator<Item = (String, Symbol)>,
) {
if !self.sess.is_nightly_build() {
return;
}
- let span = hir_id.and_then(|id| self.crate_level_attribute_injection_span(id));
+ let span = self.crate_level_attribute_injection_span();
for (desc, feature) in features {
// FIXME: make this string translatable
let msg =
format!("add `#![feature({feature})]` to the crate attributes to enable{desc}");
- if let Some(span) = span {
- diag.span_suggestion_verbose(
- span,
- msg,
- format!("#![feature({feature})]\n"),
- Applicability::MaybeIncorrect,
- );
- } else {
- diag.help(msg);
- }
+ diag.span_suggestion_verbose(
+ span,
+ msg,
+ format!("#![feature({feature})]\n"),
+ Applicability::MaybeIncorrect,
+ );
}
}
diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs
index 2a44c90..15679d2 100644
--- a/compiler/rustc_parse/src/parser/tests.rs
+++ b/compiler/rustc_parse/src/parser/tests.rs
@@ -14,6 +14,7 @@
use rustc_ast::{self as ast, PatKind, visit};
use rustc_ast_pretty::pprust::item_to_string;
use rustc_errors::emitter::{HumanEmitter, OutputTheme};
+use rustc_errors::translation::Translator;
use rustc_errors::{DiagCtxt, MultiSpan, PResult};
use rustc_session::parse::ParseSess;
use rustc_span::source_map::{FilePathMapping, SourceMap};
@@ -41,9 +42,8 @@ fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> {
fn create_test_handler(theme: OutputTheme) -> (DiagCtxt, Arc<SourceMap>, Arc<Mutex<Vec<u8>>>) {
let output = Arc::new(Mutex::new(Vec::new()));
let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
- let fallback_bundle =
- rustc_errors::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
- let mut emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), fallback_bundle)
+ let translator = Translator::with_fallback_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
+ let mut emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), translator)
.sm(Some(source_map.clone()))
.diagnostic_width(Some(140));
emitter = emitter.theme(theme);
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index a12215a..ed1737b 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -291,6 +291,8 @@ fn emit_malformed_attribute(
| sym::repr
| sym::align
| sym::deprecated
+ | sym::optimize
+ | sym::cold
) {
return;
}
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 50d6c5d..f25c9f3 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -128,6 +128,9 @@ fn check_attributes(
Attribute::Parsed(AttributeKind::Inline(kind, attr_span)) => {
self.check_inline(hir_id, *attr_span, span, kind, target)
}
+ Attribute::Parsed(AttributeKind::Optimize(_, attr_span)) => {
+ self.check_optimize(hir_id, *attr_span, span, target)
+ }
Attribute::Parsed(AttributeKind::AllowInternalUnstable(syms)) => self
.check_allow_internal_unstable(
hir_id,
@@ -146,10 +149,12 @@ fn check_attributes(
}
Attribute::Parsed(AttributeKind::Repr(_)) => { /* handled below this loop and elsewhere */
}
+ Attribute::Parsed(AttributeKind::Cold(attr_span)) => {
+ self.check_cold(hir_id, *attr_span, span, target)
+ }
Attribute::Parsed(AttributeKind::Align { align, span: repr_span }) => {
self.check_align(span, target, *align, *repr_span)
}
-
Attribute::Parsed(
AttributeKind::BodyStability { .. }
| AttributeKind::ConstStabilityIndirect
@@ -167,7 +172,6 @@ fn check_attributes(
self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target)
}
[sym::coverage, ..] => self.check_coverage(attr, span, target),
- [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target),
[sym::no_sanitize, ..] => {
self.check_no_sanitize(attr, span, target)
}
@@ -243,7 +247,6 @@ fn check_attributes(
[sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target),
[sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
[sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target),
- [sym::cold, ..] => self.check_cold(hir_id, attr, span, target),
[sym::link, ..] => self.check_link(hir_id, attr, span, target),
[sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target),
[sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target),
@@ -529,7 +532,7 @@ fn check_coverage(&self, attr: &Attribute, target_span: Span, target: Target) {
/// Checks that `#[optimize(..)]` is applied to a function/closure/method,
/// or to an impl block or module.
- fn check_optimize(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
+ fn check_optimize(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) {
let is_valid = matches!(
target,
Target::Fn
@@ -538,7 +541,7 @@ fn check_optimize(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Ta
);
if !is_valid {
self.dcx().emit_err(errors::OptimizeInvalidTarget {
- attr_span: attr.span(),
+ attr_span,
defn_span: span,
on_crate: hir_id == CRATE_HIR_ID,
});
@@ -649,8 +652,6 @@ fn check_naked(
sym::repr,
sym::align,
sym::rustc_std_internal_symbol,
- // code generation
- sym::cold,
// documentation
sym::doc,
];
@@ -686,7 +687,8 @@ fn check_naked(
Attribute::Parsed(
AttributeKind::Deprecation { .. }
| AttributeKind::Repr { .. }
- | AttributeKind::Align { .. },
+ | AttributeKind::Align { .. }
+ | AttributeKind::Cold(..),
) => {
continue;
}
@@ -1635,7 +1637,7 @@ fn check_may_dangle(&self, hir_id: HirId, attr: &Attribute) {
}
/// Checks if `#[cold]` is applied to a non-function.
- fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
+ fn check_cold(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) {
match target {
Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
// FIXME(#80564): We permit struct fields, match arms and macro defs to have an
@@ -1643,7 +1645,7 @@ fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
- self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "cold");
+ self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "cold");
}
_ => {
// FIXME: #[cold] was previously allowed on non-functions and some crates used
@@ -1651,7 +1653,7 @@ fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
- attr.span(),
+ attr_span,
errors::Cold { span, on_crate: hir_id == CRATE_HIR_ID },
);
}
diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index 6195361..528c52e 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -40,11 +40,6 @@
session_file_write_fail = failed to write `{$path}` due to error `{$err}`
-session_forbidden_ctarget_feature =
- target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason}
- .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-session_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
-
session_function_return_requires_x86_or_x86_64 = `-Zfunction-return` (except `keep`) is only supported on x86 and x86_64
session_function_return_thunk_extern_requires_non_large_code_model = `-Zfunction-return=thunk-extern` is only supported on non-large code models
@@ -137,9 +132,6 @@
session_unleashed_feature_help_named = skipping check for `{$gate}` feature
session_unleashed_feature_help_unnamed = skipping check that does not even have a feature gate
-session_unstable_ctarget_feature =
- unstable feature specified for `-Ctarget-feature`: `{$feature}`
- .note = this feature is not stably supported; its behavior can change in the future
session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` requires `-Clto`
session_unsupported_crate_type_for_target =
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index 9c591dc..bf95014 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -501,20 +501,3 @@ pub(crate) struct FailedToCreateProfiler {
#[note]
#[note(session_soft_float_deprecated_issue)]
pub(crate) struct SoftFloatDeprecated;
-
-#[derive(Diagnostic)]
-#[diag(session_forbidden_ctarget_feature)]
-#[note]
-#[note(session_forbidden_ctarget_feature_issue)]
-pub(crate) struct ForbiddenCTargetFeature<'a> {
- pub feature: &'a str,
- pub enabled: &'a str,
- pub reason: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(session_unstable_ctarget_feature)]
-#[note]
-pub(crate) struct UnstableCTargetFeature<'a> {
- pub feature: &'a str,
-}
diff --git a/compiler/rustc_session/src/features.rs b/compiler/rustc_session/src/features.rs
deleted file mode 100644
index 70a088a..0000000
--- a/compiler/rustc_session/src/features.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use rustc_target::target_features::Stability;
-
-use crate::Session;
-use crate::errors::{ForbiddenCTargetFeature, UnstableCTargetFeature};
-
-pub trait StabilityExt {
- /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`.
- /// Otherwise, some features also may only be enabled by flag (target modifier).
- /// (It might still be nightly-only even if this returns `true`, so make sure to also check
- /// `requires_nightly`.)
- fn is_toggle_permitted(&self, sess: &Session) -> Result<(), &'static str>;
-
- /// Check that feature is correctly enabled/disabled by command line flag (emits warnings)
- fn verify_feature_enabled_by_flag(&self, sess: &Session, enable: bool, feature: &str);
-}
-
-impl StabilityExt for Stability {
- fn is_toggle_permitted(&self, sess: &Session) -> Result<(), &'static str> {
- match self {
- Stability::Forbidden { reason } => Err(reason),
- Stability::TargetModifierOnly { reason, flag } => {
- if !sess.opts.target_feature_flag_enabled(*flag) { Err(reason) } else { Ok(()) }
- }
- _ => Ok(()),
- }
- }
- fn verify_feature_enabled_by_flag(&self, sess: &Session, enable: bool, feature: &str) {
- if let Err(reason) = self.is_toggle_permitted(sess) {
- sess.dcx().emit_warn(ForbiddenCTargetFeature {
- feature,
- enabled: if enable { "enabled" } else { "disabled" },
- reason,
- });
- } else if self.requires_nightly().is_some() {
- // An unstable feature. Warn about using it. It makes little sense
- // to hard-error here since we just warn about fully unknown
- // features above.
- sess.dcx().emit_warn(UnstableCTargetFeature { feature });
- }
- }
-}
-
-pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec<&str>) {
- // -Zretpoline without -Zretpoline-external-thunk enables
- // retpoline-indirect-branches and retpoline-indirect-calls target features
- let unstable_opts = &sess.opts.unstable_opts;
- if unstable_opts.retpoline && !unstable_opts.retpoline_external_thunk {
- features.push("+retpoline-indirect-branches");
- features.push("+retpoline-indirect-calls");
- }
- // -Zretpoline-external-thunk (maybe, with -Zretpoline too) enables
- // retpoline-external-thunk, retpoline-indirect-branches and
- // retpoline-indirect-calls target features
- if unstable_opts.retpoline_external_thunk {
- features.push("+retpoline-external-thunk");
- features.push("+retpoline-indirect-branches");
- features.push("+retpoline-indirect-calls");
- }
-}
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index 4added1..5e5872e 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -29,7 +29,6 @@
pub mod output;
pub use getopts;
-pub mod features;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 31b4237..7fef942 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -290,14 +290,6 @@ pub fn gather_target_modifiers(&self) -> Vec<TargetModifier> {
mods.sort_by(|a, b| a.opt.cmp(&b.opt));
mods
}
-
- pub fn target_feature_flag_enabled(&self, flag: &str) -> bool {
- match flag {
- "retpoline" => self.unstable_opts.retpoline,
- "retpoline-external-thunk" => self.unstable_opts.retpoline_external_thunk,
- _ => false,
- }
- }
}
);
}
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 87c848c..0118cdb 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -8,10 +8,11 @@
use rustc_ast::node_id::NodeId;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
use rustc_data_structures::sync::{AppendOnlyVec, Lock};
-use rustc_errors::emitter::{HumanEmitter, SilentEmitter, stderr_destination};
+use rustc_errors::emitter::{FatalOnlyEmitter, HumanEmitter, stderr_destination};
+use rustc_errors::translation::Translator;
use rustc_errors::{
ColorConfig, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, EmissionGuarantee, MultiSpan,
- StashKey, fallback_fluent_bundle,
+ StashKey,
};
use rustc_feature::{GateIssue, UnstableFeatures, find_feature_issue};
use rustc_span::edition::Edition;
@@ -242,10 +243,10 @@ pub struct ParseSess {
impl ParseSess {
/// Used for testing.
pub fn new(locale_resources: Vec<&'static str>) -> Self {
- let fallback_bundle = fallback_fluent_bundle(locale_resources, false);
+ let translator = Translator::with_fallback_bundle(locale_resources, false);
let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
let emitter = Box::new(
- HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle)
+ HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator)
.sm(Some(Arc::clone(&sm))),
);
let dcx = DiagCtxt::new(emitter);
@@ -274,19 +275,14 @@ pub fn with_dcx(dcx: DiagCtxt, source_map: Arc<SourceMap>) -> Self {
}
}
- pub fn with_silent_emitter(
- locale_resources: Vec<&'static str>,
- fatal_note: String,
- emit_fatal_diagnostic: bool,
- ) -> Self {
- let fallback_bundle = fallback_fluent_bundle(locale_resources, false);
+ pub fn with_fatal_emitter(locale_resources: Vec<&'static str>, fatal_note: String) -> Self {
+ let translator = Translator::with_fallback_bundle(locale_resources, false);
let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
let fatal_emitter =
- Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle));
- let dcx = DiagCtxt::new(Box::new(SilentEmitter {
+ Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator));
+ let dcx = DiagCtxt::new(Box::new(FatalOnlyEmitter {
fatal_emitter,
fatal_note: Some(fatal_note),
- emit_fatal_diagnostic,
}))
.disable_warnings();
ParseSess::with_dcx(dcx, sm)
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index ca42c5a..ad58c3c 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -19,9 +19,10 @@
};
use rustc_errors::json::JsonEmitter;
use rustc_errors::timings::TimingSectionHandler;
+use rustc_errors::translation::Translator;
use rustc_errors::{
Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort,
- FluentBundle, LazyFallbackBundle, TerminalUrl, fallback_fluent_bundle,
+ TerminalUrl, fallback_fluent_bundle,
};
use rustc_macros::HashStable_Generic;
pub use rustc_span::def_id::StableCrateId;
@@ -948,8 +949,7 @@ pub fn apple_deployment_target(&self) -> apple::OSVersion {
fn default_emitter(
sopts: &config::Options,
source_map: Arc<SourceMap>,
- bundle: Option<Arc<FluentBundle>>,
- fallback_bundle: LazyFallbackBundle,
+ translator: Translator,
) -> Box<DynEmitter> {
let macro_backtrace = sopts.unstable_opts.macro_backtrace;
let track_diagnostics = sopts.unstable_opts.track_diagnostics;
@@ -974,17 +974,11 @@ fn default_emitter(
let short = kind.short();
if let HumanReadableErrorType::AnnotateSnippet = kind {
- let emitter = AnnotateSnippetEmitter::new(
- source_map,
- bundle,
- fallback_bundle,
- short,
- macro_backtrace,
- );
+ let emitter =
+ AnnotateSnippetEmitter::new(source_map, translator, short, macro_backtrace);
Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
} else {
- let emitter = HumanEmitter::new(stderr_destination(color_config), fallback_bundle)
- .fluent_bundle(bundle)
+ let emitter = HumanEmitter::new(stderr_destination(color_config), translator)
.sm(source_map)
.short_message(short)
.diagnostic_width(sopts.diagnostic_width)
@@ -1006,12 +1000,11 @@ fn default_emitter(
JsonEmitter::new(
Box::new(io::BufWriter::new(io::stderr())),
source_map,
- fallback_bundle,
+ translator,
pretty,
json_rendered,
color_config,
)
- .fluent_bundle(bundle)
.ui_testing(sopts.unstable_opts.ui_testing)
.ignored_directories_in_source_blocks(
sopts.unstable_opts.ignore_directory_in_diagnostics_source_blocks.clone(),
@@ -1030,7 +1023,7 @@ fn default_emitter(
pub fn build_session(
sopts: config::Options,
io: CompilerIO,
- bundle: Option<Arc<rustc_errors::FluentBundle>>,
+ fluent_bundle: Option<Arc<rustc_errors::FluentBundle>>,
registry: rustc_errors::registry::Registry,
fluent_resources: Vec<&'static str>,
driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
@@ -1052,12 +1045,15 @@ pub fn build_session(
let cap_lints_allow = sopts.lint_cap.is_some_and(|cap| cap == lint::Allow);
let can_emit_warnings = !(warnings_allow || cap_lints_allow);
- let fallback_bundle = fallback_fluent_bundle(
- fluent_resources,
- sopts.unstable_opts.translate_directionality_markers,
- );
+ let translator = Translator {
+ fluent_bundle,
+ fallback_fluent_bundle: fallback_fluent_bundle(
+ fluent_resources,
+ sopts.unstable_opts.translate_directionality_markers,
+ ),
+ };
let source_map = rustc_span::source_map::get_source_map().unwrap();
- let emitter = default_emitter(&sopts, Arc::clone(&source_map), bundle, fallback_bundle);
+ let emitter = default_emitter(&sopts, Arc::clone(&source_map), translator);
let mut dcx = DiagCtxt::new(emitter)
.with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings))
@@ -1500,13 +1496,13 @@ pub fn early_struct_warn(&self, msg: impl Into<DiagMessage>) -> Diag<'_, ()> {
fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {
// FIXME(#100717): early errors aren't translated at the moment, so this is fine, but it will
// need to reference every crate that might emit an early error for translation to work.
- let fallback_bundle =
- fallback_fluent_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false);
+ let translator =
+ Translator::with_fallback_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false);
let emitter: Box<DynEmitter> = match output {
config::ErrorOutputType::HumanReadable { kind, color_config } => {
let short = kind.short();
Box::new(
- HumanEmitter::new(stderr_destination(color_config), fallback_bundle)
+ HumanEmitter::new(stderr_destination(color_config), translator)
.theme(if let HumanReadableErrorType::Unicode = kind {
OutputTheme::Unicode
} else {
@@ -1519,7 +1515,7 @@ fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {
Box::new(JsonEmitter::new(
Box::new(io::BufWriter::new(io::stderr())),
Some(Arc::new(SourceMap::new(FilePathMapping::empty()))),
- fallback_bundle,
+ translator,
pretty,
json_rendered,
color_config,
diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs
index a1eac1f..3eea1e0 100644
--- a/compiler/rustc_target/src/target_features.rs
+++ b/compiler/rustc_target/src/target_features.rs
@@ -11,11 +11,6 @@
/// These exist globally and are not in the target-specific lists below.
pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"];
-/// Features that require special handling when passing to LLVM:
-/// these are target-specific (i.e., must also be listed in the target-specific list below)
-/// but do not correspond to an LLVM target feature.
-pub const RUSTC_SPECIAL_FEATURES: &[&str] = &["backchain"];
-
/// Stability information for target features.
#[derive(Debug, Copy, Clone)]
pub enum Stability {
@@ -34,9 +29,6 @@ pub enum Stability {
/// particular for features are actually ABI configuration flags (not all targets are as nice as
/// RISC-V and have an explicit way to set the ABI separate from target features).
Forbidden { reason: &'static str },
- /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be set
- /// by target modifier flag. Target modifier flags are tracked to be consistent in linked modules.
- TargetModifierOnly { reason: &'static str, flag: &'static str },
}
use Stability::*;
@@ -52,7 +44,6 @@ fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
Stability::Forbidden { reason } => {
reason.hash_stable(hcx, hasher);
}
- Stability::TargetModifierOnly { .. } => {}
}
}
}
@@ -62,7 +53,7 @@ impl Stability {
/// (It might still be nightly-only even if this returns `true`, so make sure to also check
/// `requires_nightly`.)
pub fn in_cfg(&self) -> bool {
- !matches!(self, Stability::Forbidden { .. })
+ matches!(self, Stability::Stable | Stability::Unstable { .. })
}
/// Returns the nightly feature that is required to toggle this target feature via
@@ -78,7 +69,16 @@ pub fn requires_nightly(&self) -> Option<Symbol> {
Stability::Unstable(nightly_feature) => Some(nightly_feature),
Stability::Stable { .. } => None,
Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"),
- Stability::TargetModifierOnly { .. } => None,
+ }
+ }
+
+ /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`.
+ /// (It might still be nightly-only even if this returns `true`, so make sure to also check
+ /// `requires_nightly`.)
+ pub fn toggle_allowed(&self) -> Result<(), &'static str> {
+ match self {
+ Stability::Unstable(_) | Stability::Stable { .. } => Ok(()),
+ Stability::Forbidden { reason } => Err(reason),
}
}
}
@@ -270,12 +270,7 @@ pub fn requires_nightly(&self) -> Option<Symbol> {
("rcpc3", Unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]),
// FEAT_RDM
("rdm", Stable, &["neon"]),
- // This is needed for inline assembly, but shouldn't be stabilized as-is
- // since it should be enabled globally using -Zfixed-x18, not
- // #[target_feature].
- // Note that cfg(target_feature = "reserve-x18") is currently not set for
- // targets that reserve x18 by default.
- ("reserve-x18", Unstable(sym::aarch64_unstable_target_feature), &[]),
+ ("reserve-x18", Forbidden { reason: "use `-Zfixed-x18` compiler flag instead" }, &[]),
// FEAT_SB
("sb", Stable, &[]),
// FEAT_SHA1 & FEAT_SHA256
@@ -450,26 +445,17 @@ pub fn requires_nightly(&self) -> Option<Symbol> {
("rdseed", Stable, &[]),
(
"retpoline-external-thunk",
- Stability::TargetModifierOnly {
- reason: "use `retpoline-external-thunk` target modifier flag instead",
- flag: "retpoline-external-thunk",
- },
+ Stability::Forbidden { reason: "use `-Zretpoline-external-thunk` compiler flag instead" },
&[],
),
(
"retpoline-indirect-branches",
- Stability::TargetModifierOnly {
- reason: "use `retpoline` target modifier flag instead",
- flag: "retpoline",
- },
+ Stability::Forbidden { reason: "use `-Zretpoline` compiler flag instead" },
&[],
),
(
"retpoline-indirect-calls",
- Stability::TargetModifierOnly {
- reason: "use `retpoline` target modifier flag instead",
- flag: "retpoline",
- },
+ Stability::Forbidden { reason: "use `-Zretpoline` compiler flag instead" },
&[],
),
("rtm", Unstable(sym::rtm_target_feature), &[]),
@@ -732,6 +718,7 @@ pub fn requires_nightly(&self) -> Option<Symbol> {
#[rustfmt::skip]
const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-start
+ // For "backchain", https://github.com/rust-lang/rust/issues/142412 is a stabilization blocker
("backchain", Unstable(sym::s390x_target_feature), &[]),
("concurrent-functions", Unstable(sym::s390x_target_feature), &[]),
("deflate-conversion", Unstable(sym::s390x_target_feature), &[]),
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index ee5a5b2..6d07ae0 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -3575,11 +3575,7 @@ pub(super) fn note_obligation_cause_code<G: EmissionGuarantee, T>(
}
ObligationCauseCode::TrivialBound => {
err.help("see issue #48214");
- tcx.disabled_nightly_features(
- err,
- Some(tcx.local_def_id_to_hir_id(body_id)),
- [(String::new(), sym::trivial_bounds)],
- );
+ tcx.disabled_nightly_features(err, [(String::new(), sym::trivial_bounds)]);
}
ObligationCauseCode::OpaqueReturnType(expr_info) => {
let (expr_ty, expr) = if let Some((expr_ty, hir_id)) = expr_info {
diff --git a/library/Cargo.lock b/library/Cargo.lock
index 34012d6..4f0e7ae 100644
--- a/library/Cargo.lock
+++ b/library/Cargo.lock
@@ -219,21 +219,19 @@
[[package]]
name = "r-efi"
-version = "5.2.0"
+version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
dependencies = [
- "compiler_builtins",
"rustc-std-workspace-core",
]
[[package]]
name = "r-efi-alloc"
-version = "2.0.0"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e43c53ff1a01d423d1cb762fd991de07d32965ff0ca2e4f80444ac7804198203"
+checksum = "dc2f58ef3ca9bb0f9c44d9aa8537601bcd3df94cc9314a40178cadf7d4466354"
dependencies = [
- "compiler_builtins",
"r-efi",
"rustc-std-workspace-core",
]
diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs
index 410e67d..3d6c740 100644
--- a/library/alloc/src/collections/linked_list/tests.rs
+++ b/library/alloc/src/collections/linked_list/tests.rs
@@ -1,4 +1,3 @@
-use std::cell::Cell;
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::thread;
@@ -6,6 +5,7 @@
use super::*;
use crate::testing::crash_test::{CrashTestDummy, Panic};
+use crate::testing::macros::struct_with_counted_drop;
use crate::vec::Vec;
#[test]
@@ -1010,22 +1010,6 @@ fn extract_if_drop_panic_leak() {
assert_eq!(d7.dropped(), 1);
}
-macro_rules! struct_with_counted_drop {
- ($struct_name:ident$(($elt_ty:ty))?, $drop_counter:ident $(=> $drop_stmt:expr)?) => {
- thread_local! {static $drop_counter: Cell<u32> = Cell::new(0);}
-
- struct $struct_name$(($elt_ty))?;
-
- impl Drop for $struct_name {
- fn drop(&mut self) {
- $drop_counter.set($drop_counter.get() + 1);
-
- $($drop_stmt(self))?
- }
- }
- };
-}
-
#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn extract_if_pred_panic_leak() {
diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs
index c90679f..ad76cb1 100644
--- a/library/alloc/src/collections/vec_deque/tests.rs
+++ b/library/alloc/src/collections/vec_deque/tests.rs
@@ -1,9 +1,7 @@
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#![allow(static_mut_refs)]
-
use core::iter::TrustedLen;
use super::*;
+use crate::testing::macros::struct_with_counted_drop;
#[bench]
fn bench_push_back_100(b: &mut test::Bencher) {
@@ -1086,36 +1084,24 @@ fn test_clone_from() {
#[test]
fn test_vec_deque_truncate_drop() {
- static mut DROPS: u32 = 0;
- #[derive(Clone)]
- struct Elem(#[allow(dead_code)] i32);
- impl Drop for Elem {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
+ struct_with_counted_drop!(Elem, DROPS);
- let v = vec![Elem(1), Elem(2), Elem(3), Elem(4), Elem(5)];
- for push_front in 0..=v.len() {
- let v = v.clone();
- let mut tester = VecDeque::with_capacity(5);
- for (index, elem) in v.into_iter().enumerate() {
+ const LEN: usize = 5;
+ for push_front in 0..=LEN {
+ let mut tester = VecDeque::with_capacity(LEN);
+ for index in 0..LEN {
if index < push_front {
- tester.push_front(elem);
+ tester.push_front(Elem);
} else {
- tester.push_back(elem);
+ tester.push_back(Elem);
}
}
- assert_eq!(unsafe { DROPS }, 0);
+ assert_eq!(DROPS.get(), 0);
tester.truncate(3);
- assert_eq!(unsafe { DROPS }, 2);
+ assert_eq!(DROPS.get(), 2);
tester.truncate(0);
- assert_eq!(unsafe { DROPS }, 5);
- unsafe {
- DROPS = 0;
- }
+ assert_eq!(DROPS.get(), 5);
+ DROPS.set(0);
}
}
diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs
index 7c71594..5197e40 100644
--- a/library/alloc/src/string.rs
+++ b/library/alloc/src/string.rs
@@ -2877,6 +2877,7 @@ fn spec_to_string(&self) -> String {
i32, u32,
i64, u64,
isize, usize,
+ i128, u128,
}
#[cfg(not(no_global_oom_handling))]
diff --git a/library/alloctests/testing/macros.rs b/library/alloctests/testing/macros.rs
new file mode 100644
index 0000000..2433e53
--- /dev/null
+++ b/library/alloctests/testing/macros.rs
@@ -0,0 +1,37 @@
+macro_rules! struct_with_counted_drop {
+ ($struct_name:ident $(( $( $elt_ty:ty ),+ ))?, $drop_counter:ident $( => $drop_stmt:expr )? ) => {
+ thread_local! {static $drop_counter: ::core::cell::Cell<u32> = ::core::cell::Cell::new(0);}
+
+ #[derive(Clone, Debug, PartialEq)]
+ struct $struct_name $(( $( $elt_ty ),+ ))?;
+
+ impl ::std::ops::Drop for $struct_name {
+ fn drop(&mut self) {
+ $drop_counter.set($drop_counter.get() + 1);
+
+ $($drop_stmt(self))?
+ }
+ }
+ };
+ ($struct_name:ident $(( $( $elt_ty:ty ),+ ))?, $drop_counter:ident[ $drop_key:expr,$key_ty:ty ] $( => $drop_stmt:expr )? ) => {
+ thread_local! {
+ static $drop_counter: ::core::cell::RefCell<::std::collections::HashMap<$key_ty, u32>> =
+ ::core::cell::RefCell::new(::std::collections::HashMap::new());
+ }
+
+ #[derive(Clone, Debug, PartialEq)]
+ struct $struct_name $(( $( $elt_ty ),+ ))?;
+
+ impl ::std::ops::Drop for $struct_name {
+ fn drop(&mut self) {
+ $drop_counter.with_borrow_mut(|counter| {
+ *counter.entry($drop_key(self)).or_default() += 1;
+ });
+
+ $($drop_stmt(self))?
+ }
+ }
+ };
+}
+
+pub(crate) use struct_with_counted_drop;
diff --git a/library/alloctests/testing/mod.rs b/library/alloctests/testing/mod.rs
index c8457da..66a4f66 100644
--- a/library/alloctests/testing/mod.rs
+++ b/library/alloctests/testing/mod.rs
@@ -1,3 +1,4 @@
pub(crate) mod crash_test;
+pub(crate) mod macros;
pub(crate) mod ord_chaos;
pub(crate) mod rng;
diff --git a/library/alloctests/tests/fmt.rs b/library/alloctests/tests/fmt.rs
index dbcf0c3..0989a56 100644
--- a/library/alloctests/tests/fmt.rs
+++ b/library/alloctests/tests/fmt.rs
@@ -1,6 +1,4 @@
#![deny(warnings)]
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#![allow(static_mut_refs)]
#![allow(unnecessary_transmutes)]
use std::cell::RefCell;
@@ -285,19 +283,32 @@ fn test_format_args() {
t!(s, "args were: hello world");
}
+macro_rules! counter_fn {
+ ($name:ident) => {
+ fn $name() -> u32 {
+ thread_local! {static COUNTER: ::core::cell::Cell<u32> = ::core::cell::Cell::new(0);}
+
+ COUNTER.set(COUNTER.get() + 1);
+ COUNTER.get()
+ }
+ };
+}
+
#[test]
fn test_order() {
- // Make sure format!() arguments are always evaluated in a left-to-right
- // ordering
- fn foo() -> isize {
- static mut FOO: isize = 0;
- unsafe {
- FOO += 1;
- FOO
- }
- }
+ // Make sure format!() arguments are always evaluated in a left-to-right ordering
+ counter_fn!(count);
+
assert_eq!(
- format!("{} {} {a} {b} {} {c}", foo(), foo(), foo(), a = foo(), b = foo(), c = foo()),
+ format!(
+ "{} {} {a} {b} {} {c}",
+ count(),
+ count(),
+ count(),
+ a = count(),
+ b = count(),
+ c = count()
+ ),
"1 2 4 5 3 6".to_string()
);
}
@@ -306,14 +317,9 @@ fn foo() -> isize {
fn test_once() {
// Make sure each argument are evaluated only once even though it may be
// formatted multiple times
- fn foo() -> isize {
- static mut FOO: isize = 0;
- unsafe {
- FOO += 1;
- FOO
- }
- }
- assert_eq!(format!("{0} {0} {0} {a} {a} {a}", foo(), a = foo()), "1 1 1 2 2 2".to_string());
+ counter_fn!(count);
+
+ assert_eq!(format!("{0} {0} {0} {a} {a} {a}", count(), a = count()), "1 1 1 2 2 2".to_string());
}
#[test]
diff --git a/library/alloctests/tests/num.rs b/library/alloctests/tests/num.rs
index 3c76e68..a169bbe 100644
--- a/library/alloctests/tests/num.rs
+++ b/library/alloctests/tests/num.rs
@@ -52,6 +52,8 @@ fn $fn_name() {
i32,
test_i64_to_string,
i64,
+ test_isize_to_string,
+ isize,
test_i128_to_string,
i128,
);
@@ -64,6 +66,8 @@ fn $fn_name() {
u32,
test_u64_to_string,
u64,
+ test_usize_to_string,
+ usize,
test_u128_to_string,
u128,
);
diff --git a/library/alloctests/tests/testing/mod.rs b/library/alloctests/tests/testing/mod.rs
index 30275a5..08856e3 100644
--- a/library/alloctests/tests/testing/mod.rs
+++ b/library/alloctests/tests/testing/mod.rs
@@ -1,2 +1,4 @@
#[path = "../../testing/crash_test.rs"]
pub mod crash_test;
+#[path = "../../testing/macros.rs"]
+pub mod macros;
diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs
index 51b49b8..00f640c 100644
--- a/library/alloctests/tests/vec.rs
+++ b/library/alloctests/tests/vec.rs
@@ -1,6 +1,3 @@
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#![allow(static_mut_refs)]
-
use core::alloc::{Allocator, Layout};
use core::num::NonZero;
use core::ptr::NonNull;
@@ -20,6 +17,8 @@
use std::sync::atomic::{AtomicU32, Ordering};
use std::vec::{Drain, IntoIter};
+use crate::testing::macros::struct_with_counted_drop;
+
struct DropCounter<'a> {
count: &'a mut u32,
}
@@ -548,32 +547,25 @@ fn test_cmp() {
#[test]
fn test_vec_truncate_drop() {
- static mut DROPS: u32 = 0;
- struct Elem(#[allow(dead_code)] i32);
- impl Drop for Elem {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
+ struct_with_counted_drop!(Elem(i32), DROPS);
let mut v = vec![Elem(1), Elem(2), Elem(3), Elem(4), Elem(5)];
- assert_eq!(unsafe { DROPS }, 0);
+
+ assert_eq!(DROPS.get(), 0);
v.truncate(3);
- assert_eq!(unsafe { DROPS }, 2);
+ assert_eq!(DROPS.get(), 2);
v.truncate(0);
- assert_eq!(unsafe { DROPS }, 5);
+ assert_eq!(DROPS.get(), 5);
}
#[test]
#[should_panic]
fn test_vec_truncate_fail() {
struct BadElem(i32);
+
impl Drop for BadElem {
fn drop(&mut self) {
- let BadElem(ref mut x) = *self;
- if *x == 0xbadbeef {
+ if let BadElem(0xbadbeef) = self {
panic!("BadElem panic: 0xbadbeef")
}
}
@@ -812,22 +804,7 @@ fn test_drain_end_overflow() {
#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_drain_leak() {
- static mut DROPS: i32 = 0;
-
- #[derive(Debug, PartialEq)]
- struct D(u32, bool);
-
- impl Drop for D {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
-
- if self.1 {
- panic!("panic in `drop`");
- }
- }
- }
+ struct_with_counted_drop!(D(u32, bool), DROPS => |this: &D| if this.1 { panic!("panic in `drop`"); });
let mut v = vec![
D(0, false),
@@ -844,7 +821,7 @@ fn drop(&mut self) {
}))
.ok();
- assert_eq!(unsafe { DROPS }, 4);
+ assert_eq!(DROPS.get(), 4);
assert_eq!(v, vec![D(0, false), D(1, false), D(6, false),]);
}
@@ -1057,27 +1034,13 @@ fn iter_equal<I: Iterator<Item = i32>>(it: I, slice: &[i32]) {
#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_into_iter_leak() {
- static mut DROPS: i32 = 0;
-
- struct D(bool);
-
- impl Drop for D {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
-
- if self.0 {
- panic!("panic in `drop`");
- }
- }
- }
+ struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); });
let v = vec![D(false), D(true), D(false)];
catch_unwind(move || drop(v.into_iter())).ok();
- assert_eq!(unsafe { DROPS }, 3);
+ assert_eq!(DROPS.get(), 3);
}
#[test]
@@ -1274,55 +1237,31 @@ fn test_from_iter_specialization_panic_during_iteration_drops() {
#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#[allow(static_mut_refs)]
fn test_from_iter_specialization_panic_during_drop_doesnt_leak() {
- static mut DROP_COUNTER_OLD: [usize; 5] = [0; 5];
- static mut DROP_COUNTER_NEW: [usize; 2] = [0; 2];
-
- #[derive(Debug)]
- struct Old(usize);
-
- impl Drop for Old {
- fn drop(&mut self) {
- unsafe {
- DROP_COUNTER_OLD[self.0] += 1;
+ struct_with_counted_drop!(
+ Old(usize), DROP_COUNTER_OLD[|this: &Old| this.0, usize] =>
+ |this: &Old| {
+ if this.0 == 3 { panic!(); } println!("Dropped Old: {}", this.0)
}
-
- if self.0 == 3 {
- panic!();
- }
-
- println!("Dropped Old: {}", self.0);
- }
- }
-
- #[derive(Debug)]
- struct New(usize);
-
- impl Drop for New {
- fn drop(&mut self) {
- unsafe {
- DROP_COUNTER_NEW[self.0] += 1;
- }
-
- println!("Dropped New: {}", self.0);
- }
- }
+ );
+ struct_with_counted_drop!(
+ New(usize), DROP_COUNTER_NEW[|this: &New| this.0, usize] =>
+ |this: &New| println!("Dropped New: {}", this.0)
+ );
let _ = std::panic::catch_unwind(AssertUnwindSafe(|| {
let v = vec![Old(0), Old(1), Old(2), Old(3), Old(4)];
let _ = v.into_iter().map(|x| New(x.0)).take(2).collect::<Vec<_>>();
}));
- assert_eq!(unsafe { DROP_COUNTER_OLD[0] }, 1);
- assert_eq!(unsafe { DROP_COUNTER_OLD[1] }, 1);
- assert_eq!(unsafe { DROP_COUNTER_OLD[2] }, 1);
- assert_eq!(unsafe { DROP_COUNTER_OLD[3] }, 1);
- assert_eq!(unsafe { DROP_COUNTER_OLD[4] }, 1);
+ DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&0), Some(&1)));
+ DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&1), Some(&1)));
+ DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&2), Some(&1)));
+ DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&3), Some(&1)));
+ DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&4), Some(&1)));
- assert_eq!(unsafe { DROP_COUNTER_NEW[0] }, 1);
- assert_eq!(unsafe { DROP_COUNTER_NEW[1] }, 1);
+ DROP_COUNTER_NEW.with_borrow(|c| assert_eq!(c.get(&0), Some(&1)));
+ DROP_COUNTER_NEW.with_borrow(|c| assert_eq!(c.get(&1), Some(&1)));
}
// regression test for issue #85322. Peekable previously implemented InPlaceIterable,
diff --git a/library/alloctests/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs
index b77ea3a..a82906d 100644
--- a/library/alloctests/tests/vec_deque.rs
+++ b/library/alloctests/tests/vec_deque.rs
@@ -1,6 +1,3 @@
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#![allow(static_mut_refs)]
-
use core::num::NonZero;
use std::assert_matches::assert_matches;
use std::collections::TryReserveErrorKind::*;
@@ -14,6 +11,7 @@
use Taggypar::*;
use crate::hash;
+use crate::testing::macros::struct_with_counted_drop;
#[test]
fn test_simple() {
@@ -719,15 +717,7 @@ fn test_show() {
#[test]
fn test_drop() {
- static mut DROPS: i32 = 0;
- struct Elem;
- impl Drop for Elem {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
+ struct_with_counted_drop!(Elem, DROPS);
let mut ring = VecDeque::new();
ring.push_back(Elem);
@@ -736,20 +726,12 @@ fn drop(&mut self) {
ring.push_front(Elem);
drop(ring);
- assert_eq!(unsafe { DROPS }, 4);
+ assert_eq!(DROPS.get(), 4);
}
#[test]
fn test_drop_with_pop() {
- static mut DROPS: i32 = 0;
- struct Elem;
- impl Drop for Elem {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
+ struct_with_counted_drop!(Elem, DROPS);
let mut ring = VecDeque::new();
ring.push_back(Elem);
@@ -759,23 +741,15 @@ fn drop(&mut self) {
drop(ring.pop_back());
drop(ring.pop_front());
- assert_eq!(unsafe { DROPS }, 2);
+ assert_eq!(DROPS.get(), 2);
drop(ring);
- assert_eq!(unsafe { DROPS }, 4);
+ assert_eq!(DROPS.get(), 4);
}
#[test]
fn test_drop_clear() {
- static mut DROPS: i32 = 0;
- struct Elem;
- impl Drop for Elem {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
+ struct_with_counted_drop!(Elem, DROPS);
let mut ring = VecDeque::new();
ring.push_back(Elem);
@@ -783,30 +757,16 @@ fn drop(&mut self) {
ring.push_back(Elem);
ring.push_front(Elem);
ring.clear();
- assert_eq!(unsafe { DROPS }, 4);
+ assert_eq!(DROPS.get(), 4);
drop(ring);
- assert_eq!(unsafe { DROPS }, 4);
+ assert_eq!(DROPS.get(), 4);
}
#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_drop_panic() {
- static mut DROPS: i32 = 0;
-
- struct D(bool);
-
- impl Drop for D {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
-
- if self.0 {
- panic!("panic in `drop`");
- }
- }
- }
+ struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } );
let mut q = VecDeque::new();
q.push_back(D(false));
@@ -820,7 +780,7 @@ fn drop(&mut self) {
catch_unwind(move || drop(q)).ok();
- assert_eq!(unsafe { DROPS }, 8);
+ assert_eq!(DROPS.get(), 8);
}
#[test]
@@ -1655,21 +1615,7 @@ fn test_try_rfold_moves_iter() {
#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn truncate_leak() {
- static mut DROPS: i32 = 0;
-
- struct D(bool);
-
- impl Drop for D {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
-
- if self.0 {
- panic!("panic in `drop`");
- }
- }
- }
+ struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } );
let mut q = VecDeque::new();
q.push_back(D(false));
@@ -1683,27 +1629,13 @@ fn drop(&mut self) {
catch_unwind(AssertUnwindSafe(|| q.truncate(1))).ok();
- assert_eq!(unsafe { DROPS }, 7);
+ assert_eq!(DROPS.get(), 7);
}
#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn truncate_front_leak() {
- static mut DROPS: i32 = 0;
-
- struct D(bool);
-
- impl Drop for D {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
-
- if self.0 {
- panic!("panic in `drop`");
- }
- }
- }
+ struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } );
let mut q = VecDeque::new();
q.push_back(D(false));
@@ -1717,28 +1649,13 @@ fn drop(&mut self) {
catch_unwind(AssertUnwindSafe(|| q.truncate_front(1))).ok();
- assert_eq!(unsafe { DROPS }, 7);
+ assert_eq!(DROPS.get(), 7);
}
#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_drain_leak() {
- static mut DROPS: i32 = 0;
-
- #[derive(Debug, PartialEq)]
- struct D(u32, bool);
-
- impl Drop for D {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
-
- if self.1 {
- panic!("panic in `drop`");
- }
- }
- }
+ struct_with_counted_drop!(D(u32, bool), DROPS => |this: &D| if this.1 { panic!("panic in `drop`"); } );
let mut v = VecDeque::new();
v.push_back(D(4, false));
@@ -1754,10 +1671,10 @@ fn drop(&mut self) {
}))
.ok();
- assert_eq!(unsafe { DROPS }, 4);
+ assert_eq!(DROPS.get(), 4);
assert_eq!(v.len(), 3);
drop(v);
- assert_eq!(unsafe { DROPS }, 7);
+ assert_eq!(DROPS.get(), 7);
}
#[test]
diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs
index ce878f2..13cd7f7 100644
--- a/library/core/src/fmt/num.rs
+++ b/library/core/src/fmt/num.rs
@@ -565,123 +565,134 @@ mod imp {
}
impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128);
+const U128_MAX_DEC_N: usize = u128::MAX.ilog(10) as usize + 1;
+
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Display for u128 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt_u128(*self, true, f)
+ let mut buf = [MaybeUninit::<u8>::uninit(); U128_MAX_DEC_N];
+
+ f.pad_integral(true, "", self._fmt(&mut buf))
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Display for i128 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt_u128(self.unsigned_abs(), *self >= 0, f)
+ // This is not a typo, we use the maximum number of digits of `u128`, hence why we use
+ // `U128_MAX_DEC_N`.
+ let mut buf = [MaybeUninit::<u8>::uninit(); U128_MAX_DEC_N];
+
+ let is_nonnegative = *self >= 0;
+ f.pad_integral(is_nonnegative, "", self.unsigned_abs()._fmt(&mut buf))
}
}
-/// Format optimized for u128. Computation of 128 bits is limited by proccessing
-/// in batches of 16 decimals at a time.
-fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- // Optimize common-case zero, which would also need special treatment due to
- // its "leading" zero.
- if n == 0 {
- return f.pad_integral(true, "", "0");
- }
-
- // U128::MAX has 39 significant-decimals.
- const MAX_DEC_N: usize = u128::MAX.ilog(10) as usize + 1;
- // Buffer decimals with right alignment.
- let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
-
- // Take the 16 least-significant decimals.
- let (quot_1e16, mod_1e16) = div_rem_1e16(n);
- let (mut remain, mut offset) = if quot_1e16 == 0 {
- (mod_1e16, MAX_DEC_N)
- } else {
- // Write digits at buf[23..39].
- enc_16lsd::<{ MAX_DEC_N - 16 }>(&mut buf, mod_1e16);
-
- // Take another 16 decimals.
- let (quot2, mod2) = div_rem_1e16(quot_1e16);
- if quot2 == 0 {
- (mod2, MAX_DEC_N - 16)
- } else {
- // Write digits at buf[7..23].
- enc_16lsd::<{ MAX_DEC_N - 32 }>(&mut buf, mod2);
- // Quot2 has at most 7 decimals remaining after two 1e16 divisions.
- (quot2 as u64, MAX_DEC_N - 32)
+impl u128 {
+ /// Format optimized for u128. Computation of 128 bits is limited by proccessing
+ /// in batches of 16 decimals at a time.
+ #[doc(hidden)]
+ #[unstable(
+ feature = "fmt_internals",
+ reason = "specialized method meant to only be used by `SpecToString` implementation",
+ issue = "none"
+ )]
+ pub fn _fmt<'a>(self, buf: &'a mut [MaybeUninit<u8>]) -> &'a str {
+ // Optimize common-case zero, which would also need special treatment due to
+ // its "leading" zero.
+ if self == 0 {
+ return "0";
}
- };
- // Format per four digits from the lookup table.
- while remain > 999 {
- // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
- // and the while condition ensures at least 4 more decimals.
- unsafe { core::hint::assert_unchecked(offset >= 4) }
- // SAFETY: The offset counts down from its initial buf.len()
- // without underflow due to the previous precondition.
- unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
- offset -= 4;
+ // Take the 16 least-significant decimals.
+ let (quot_1e16, mod_1e16) = div_rem_1e16(self);
+ let (mut remain, mut offset) = if quot_1e16 == 0 {
+ (mod_1e16, U128_MAX_DEC_N)
+ } else {
+ // Write digits at buf[23..39].
+ enc_16lsd::<{ U128_MAX_DEC_N - 16 }>(buf, mod_1e16);
- // pull two pairs
- let quad = remain % 1_00_00;
- remain /= 1_00_00;
- let pair1 = (quad / 100) as usize;
- let pair2 = (quad % 100) as usize;
- buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
- buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
- buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
- buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
+ // Take another 16 decimals.
+ let (quot2, mod2) = div_rem_1e16(quot_1e16);
+ if quot2 == 0 {
+ (mod2, U128_MAX_DEC_N - 16)
+ } else {
+ // Write digits at buf[7..23].
+ enc_16lsd::<{ U128_MAX_DEC_N - 32 }>(buf, mod2);
+ // Quot2 has at most 7 decimals remaining after two 1e16 divisions.
+ (quot2 as u64, U128_MAX_DEC_N - 32)
+ }
+ };
+
+ // Format per four digits from the lookup table.
+ while remain > 999 {
+ // SAFETY: All of the decimals fit in buf due to U128_MAX_DEC_N
+ // and the while condition ensures at least 4 more decimals.
+ unsafe { core::hint::assert_unchecked(offset >= 4) }
+ // SAFETY: The offset counts down from its initial buf.len()
+ // without underflow due to the previous precondition.
+ unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
+ offset -= 4;
+
+ // pull two pairs
+ let quad = remain % 1_00_00;
+ remain /= 1_00_00;
+ let pair1 = (quad / 100) as usize;
+ let pair2 = (quad % 100) as usize;
+ buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
+ buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
+ buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
+ buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
+ }
+
+ // Format per two digits from the lookup table.
+ if remain > 9 {
+ // SAFETY: All of the decimals fit in buf due to U128_MAX_DEC_N
+ // and the if condition ensures at least 2 more decimals.
+ unsafe { core::hint::assert_unchecked(offset >= 2) }
+ // SAFETY: The offset counts down from its initial buf.len()
+ // without underflow due to the previous precondition.
+ unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
+ offset -= 2;
+
+ let pair = (remain % 100) as usize;
+ remain /= 100;
+ buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
+ buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
+ }
+
+ // Format the last remaining digit, if any.
+ if remain != 0 {
+ // SAFETY: All of the decimals fit in buf due to U128_MAX_DEC_N
+ // and the if condition ensures (at least) 1 more decimals.
+ unsafe { core::hint::assert_unchecked(offset >= 1) }
+ // SAFETY: The offset counts down from its initial buf.len()
+ // without underflow due to the previous precondition.
+ unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
+ offset -= 1;
+
+ // Either the compiler sees that remain < 10, or it prevents
+ // a boundary check up next.
+ let last = (remain & 15) as usize;
+ buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
+ // not used: remain = 0;
+ }
+
+ // SAFETY: All buf content since offset is set.
+ let written = unsafe { buf.get_unchecked(offset..) };
+ // SAFETY: Writes use ASCII from the lookup table exclusively.
+ unsafe {
+ str::from_utf8_unchecked(slice::from_raw_parts(
+ MaybeUninit::slice_as_ptr(written),
+ written.len(),
+ ))
+ }
}
-
- // Format per two digits from the lookup table.
- if remain > 9 {
- // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
- // and the if condition ensures at least 2 more decimals.
- unsafe { core::hint::assert_unchecked(offset >= 2) }
- // SAFETY: The offset counts down from its initial buf.len()
- // without underflow due to the previous precondition.
- unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
- offset -= 2;
-
- let pair = (remain % 100) as usize;
- remain /= 100;
- buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
- buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
- }
-
- // Format the last remaining digit, if any.
- if remain != 0 {
- // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
- // and the if condition ensures (at least) 1 more decimals.
- unsafe { core::hint::assert_unchecked(offset >= 1) }
- // SAFETY: The offset counts down from its initial buf.len()
- // without underflow due to the previous precondition.
- unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
- offset -= 1;
-
- // Either the compiler sees that remain < 10, or it prevents
- // a boundary check up next.
- let last = (remain & 15) as usize;
- buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
- // not used: remain = 0;
- }
-
- // SAFETY: All buf content since offset is set.
- let written = unsafe { buf.get_unchecked(offset..) };
- // SAFETY: Writes use ASCII from the lookup table exclusively.
- let as_str = unsafe {
- str::from_utf8_unchecked(slice::from_raw_parts(
- MaybeUninit::slice_as_ptr(written),
- written.len(),
- ))
- };
- f.pad_integral(is_nonnegative, "", as_str)
}
/// Encodes the 16 least-significant decimals of n into `buf[OFFSET .. OFFSET +
/// 16 ]`.
-fn enc_16lsd<const OFFSET: usize>(buf: &mut [MaybeUninit<u8>; 39], n: u64) {
+fn enc_16lsd<const OFFSET: usize>(buf: &mut [MaybeUninit<u8>], n: u64) {
// Consume the least-significant decimals from a working copy.
let mut remain = n;
diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs
index 7fd8d3e..fb858a0 100644
--- a/library/core/src/fmt/rt.rs
+++ b/library/core/src/fmt/rt.rs
@@ -183,38 +183,6 @@ pub(super) const fn as_u16(&self) -> Option<u16> {
ArgumentType::Placeholder { .. } => None,
}
}
-
- /// Used by `format_args` when all arguments are gone after inlining,
- /// when using `&[]` would incorrectly allow for a bigger lifetime.
- ///
- /// This fails without format argument inlining, and that shouldn't be different
- /// when the argument is inlined:
- ///
- /// ```compile_fail,E0716
- /// let f = format_args!("{}", "a");
- /// println!("{f}");
- /// ```
- #[inline]
- pub const fn none() -> [Self; 0] {
- []
- }
-}
-
-/// This struct represents the unsafety of constructing an `Arguments`.
-/// It exists, rather than an unsafe function, in order to simplify the expansion
-/// of `format_args!(..)` and reduce the scope of the `unsafe` block.
-#[lang = "format_unsafe_arg"]
-pub struct UnsafeArg {
- _private: (),
-}
-
-impl UnsafeArg {
- /// See documentation where `UnsafeArg` is required to know when it is safe to
- /// create and use `UnsafeArg`.
- #[inline]
- pub const unsafe fn new() -> Self {
- Self { _private: () }
- }
}
/// Used by the format_args!() macro to create a fmt::Arguments object.
@@ -248,8 +216,7 @@ pub fn new_v1<const P: usize, const A: usize>(
/// Specifies nonstandard formatting parameters.
///
- /// An `rt::UnsafeArg` is required because the following invariants must be held
- /// in order for this function to be safe:
+ /// SAFETY: the following invariants must be held:
/// 1. The `pieces` slice must be at least as long as `fmt`.
/// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`.
/// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`.
@@ -261,11 +228,10 @@ pub fn new_v1<const P: usize, const A: usize>(
/// const _: () = if false { panic!("a {:1}", "a") };
/// ```
#[inline]
- pub fn new_v1_formatted(
+ pub unsafe fn new_v1_formatted(
pieces: &'a [&'static str],
args: &'a [rt::Argument<'a>],
fmt: &'a [rt::Placeholder],
- _unsafe_arg: rt::UnsafeArg,
) -> Arguments<'a> {
Arguments { pieces, fmt: Some(fmt), args }
}
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index c26bbad..3a3f44c 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -2764,6 +2764,89 @@ pub fn strip_suffix<P: SlicePattern<Item = T> + ?Sized>(&self, suffix: &P) -> Op
None
}
+ /// Returns a subslice with the optional prefix removed.
+ ///
+ /// If the slice starts with `prefix`, returns the subslice after the prefix. If `prefix`
+ /// is empty or the slice does not start with `prefix`, simply returns the original slice.
+ /// If `prefix` is equal to the original slice, returns an empty slice.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(trim_prefix_suffix)]
+ ///
+ /// let v = &[10, 40, 30];
+ ///
+ /// // Prefix present - removes it
+ /// assert_eq!(v.trim_prefix(&[10]), &[40, 30][..]);
+ /// assert_eq!(v.trim_prefix(&[10, 40]), &[30][..]);
+ /// assert_eq!(v.trim_prefix(&[10, 40, 30]), &[][..]);
+ ///
+ /// // Prefix absent - returns original slice
+ /// assert_eq!(v.trim_prefix(&[50]), &[10, 40, 30][..]);
+ /// assert_eq!(v.trim_prefix(&[10, 50]), &[10, 40, 30][..]);
+ ///
+ /// let prefix : &str = "he";
+ /// assert_eq!(b"hello".trim_prefix(prefix.as_bytes()), b"llo".as_ref());
+ /// ```
+ #[must_use = "returns the subslice without modifying the original"]
+ #[unstable(feature = "trim_prefix_suffix", issue = "142312")]
+ pub fn trim_prefix<P: SlicePattern<Item = T> + ?Sized>(&self, prefix: &P) -> &[T]
+ where
+ T: PartialEq,
+ {
+ // This function will need rewriting if and when SlicePattern becomes more sophisticated.
+ let prefix = prefix.as_slice();
+ let n = prefix.len();
+ if n <= self.len() {
+ let (head, tail) = self.split_at(n);
+ if head == prefix {
+ return tail;
+ }
+ }
+ self
+ }
+
+ /// Returns a subslice with the optional suffix removed.
+ ///
+ /// If the slice ends with `suffix`, returns the subslice before the suffix. If `suffix`
+ /// is empty or the slice does not end with `suffix`, simply returns the original slice.
+ /// If `suffix` is equal to the original slice, returns an empty slice.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(trim_prefix_suffix)]
+ ///
+ /// let v = &[10, 40, 30];
+ ///
+ /// // Suffix present - removes it
+ /// assert_eq!(v.trim_suffix(&[30]), &[10, 40][..]);
+ /// assert_eq!(v.trim_suffix(&[40, 30]), &[10][..]);
+ /// assert_eq!(v.trim_suffix(&[10, 40, 30]), &[][..]);
+ ///
+ /// // Suffix absent - returns original slice
+ /// assert_eq!(v.trim_suffix(&[50]), &[10, 40, 30][..]);
+ /// assert_eq!(v.trim_suffix(&[50, 30]), &[10, 40, 30][..]);
+ /// ```
+ #[must_use = "returns the subslice without modifying the original"]
+ #[unstable(feature = "trim_prefix_suffix", issue = "142312")]
+ pub fn trim_suffix<P: SlicePattern<Item = T> + ?Sized>(&self, suffix: &P) -> &[T]
+ where
+ T: PartialEq,
+ {
+ // This function will need rewriting if and when SlicePattern becomes more sophisticated.
+ let suffix = suffix.as_slice();
+ let (len, n) = (self.len(), suffix.len());
+ if n <= len {
+ let (head, tail) = self.split_at(len - n);
+ if tail == suffix {
+ return head;
+ }
+ }
+ self
+ }
+
/// Binary searches this slice for a given element.
/// If the slice is not sorted, the returned result is unspecified and
/// meaningless.
diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs
index 4183479..5051b22 100644
--- a/library/core/src/str/mod.rs
+++ b/library/core/src/str/mod.rs
@@ -2426,6 +2426,83 @@ pub fn strip_suffix<P: Pattern>(&self, suffix: P) -> Option<&str>
suffix.strip_suffix_of(self)
}
+ /// Returns a string slice with the optional prefix removed.
+ ///
+ /// If the string starts with the pattern `prefix`, returns the substring after the prefix.
+ /// Unlike [`strip_prefix`], this method always returns `&str` for easy method chaining,
+ /// instead of returning [`Option<&str>`].
+ ///
+ /// If the string does not start with `prefix`, returns the original string unchanged.
+ ///
+ /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
+ /// function or closure that determines if a character matches.
+ ///
+ /// [`char`]: prim@char
+ /// [pattern]: self::pattern
+ /// [`strip_prefix`]: Self::strip_prefix
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(trim_prefix_suffix)]
+ ///
+ /// // Prefix present - removes it
+ /// assert_eq!("foo:bar".trim_prefix("foo:"), "bar");
+ /// assert_eq!("foofoo".trim_prefix("foo"), "foo");
+ ///
+ /// // Prefix absent - returns original string
+ /// assert_eq!("foo:bar".trim_prefix("bar"), "foo:bar");
+ ///
+ /// // Method chaining example
+ /// assert_eq!("<https://example.com/>".trim_prefix('<').trim_suffix('>'), "https://example.com/");
+ /// ```
+ #[must_use = "this returns the remaining substring as a new slice, \
+ without modifying the original"]
+ #[unstable(feature = "trim_prefix_suffix", issue = "142312")]
+ pub fn trim_prefix<P: Pattern>(&self, prefix: P) -> &str {
+ prefix.strip_prefix_of(self).unwrap_or(self)
+ }
+
+ /// Returns a string slice with the optional suffix removed.
+ ///
+ /// If the string ends with the pattern `suffix`, returns the substring before the suffix.
+ /// Unlike [`strip_suffix`], this method always returns `&str` for easy method chaining,
+ /// instead of returning [`Option<&str>`].
+ ///
+ /// If the string does not end with `suffix`, returns the original string unchanged.
+ ///
+ /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
+ /// function or closure that determines if a character matches.
+ ///
+ /// [`char`]: prim@char
+ /// [pattern]: self::pattern
+ /// [`strip_suffix`]: Self::strip_suffix
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(trim_prefix_suffix)]
+ ///
+ /// // Suffix present - removes it
+ /// assert_eq!("bar:foo".trim_suffix(":foo"), "bar");
+ /// assert_eq!("foofoo".trim_suffix("foo"), "foo");
+ ///
+ /// // Suffix absent - returns original string
+ /// assert_eq!("bar:foo".trim_suffix("bar"), "bar:foo");
+ ///
+ /// // Method chaining example
+ /// assert_eq!("<https://example.com/>".trim_prefix('<').trim_suffix('>'), "https://example.com/");
+ /// ```
+ #[must_use = "this returns the remaining substring as a new slice, \
+ without modifying the original"]
+ #[unstable(feature = "trim_prefix_suffix", issue = "142312")]
+ pub fn trim_suffix<P: Pattern>(&self, suffix: P) -> &str
+ where
+ for<'a> P::Searcher<'a>: ReverseSearcher<'a>,
+ {
+ suffix.strip_suffix_of(self).unwrap_or(self)
+ }
+
/// Returns a string slice with all suffixes that match a pattern
/// repeatedly removed.
///
diff --git a/library/coretests/tests/atomic.rs b/library/coretests/tests/atomic.rs
index e0c0fe4..b1ab443 100644
--- a/library/coretests/tests/atomic.rs
+++ b/library/coretests/tests/atomic.rs
@@ -228,24 +228,20 @@ fn static_init() {
}
#[test]
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#[allow(static_mut_refs)]
fn atomic_access_bool() {
- static mut ATOMIC: AtomicBool = AtomicBool::new(false);
+ let mut atom = AtomicBool::new(false);
- unsafe {
- assert_eq!(*ATOMIC.get_mut(), false);
- ATOMIC.store(true, SeqCst);
- assert_eq!(*ATOMIC.get_mut(), true);
- ATOMIC.fetch_or(false, SeqCst);
- assert_eq!(*ATOMIC.get_mut(), true);
- ATOMIC.fetch_and(false, SeqCst);
- assert_eq!(*ATOMIC.get_mut(), false);
- ATOMIC.fetch_nand(true, SeqCst);
- assert_eq!(*ATOMIC.get_mut(), true);
- ATOMIC.fetch_xor(true, SeqCst);
- assert_eq!(*ATOMIC.get_mut(), false);
- }
+ assert_eq!(*atom.get_mut(), false);
+ atom.store(true, SeqCst);
+ assert_eq!(*atom.get_mut(), true);
+ atom.fetch_or(false, SeqCst);
+ assert_eq!(*atom.get_mut(), true);
+ atom.fetch_and(false, SeqCst);
+ assert_eq!(*atom.get_mut(), false);
+ atom.fetch_nand(true, SeqCst);
+ assert_eq!(*atom.get_mut(), true);
+ atom.fetch_xor(true, SeqCst);
+ assert_eq!(*atom.get_mut(), false);
}
#[test]
diff --git a/library/coretests/tests/fmt/mod.rs b/library/coretests/tests/fmt/mod.rs
index d9060fe..16f116d 100644
--- a/library/coretests/tests/fmt/mod.rs
+++ b/library/coretests/tests/fmt/mod.rs
@@ -3,6 +3,21 @@
mod num;
#[test]
+fn test_lifetime() {
+ // Trigger all different forms of expansion,
+ // and check that each of them can be stored as a variable.
+ let a = format_args!("hello");
+ let a = format_args!("hello {a}");
+ let a = format_args!("hello {a:1}");
+ let a = format_args!("hello {a} {a:?}");
+ assert_eq!(a.to_string(), "hello hello hello hello hello hello hello");
+
+ // Without arguments, it should also work in consts.
+ const A: std::fmt::Arguments<'static> = format_args!("hello");
+ assert_eq!(A.to_string(), "hello");
+}
+
+#[test]
fn test_format_flags() {
// No residual flags left by pointer formatting
let p = "".as_ptr();
diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs
index 5802b65..78c9572 100644
--- a/library/std/src/os/unix/mod.rs
+++ b/library/std/src/os/unix/mod.rs
@@ -116,6 +116,9 @@ pub mod prelude {
#[stable(feature = "rust1", since = "1.0.0")]
pub use super::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
#[doc(no_inline)]
+ #[unstable(feature = "unix_send_signal", issue = "141975")]
+ pub use super::process::ChildExt;
+ #[doc(no_inline)]
#[stable(feature = "rust1", since = "1.0.0")]
pub use super::process::{CommandExt, ExitStatusExt};
#[doc(no_inline)]
diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs
index 57ce3c5..3f4fe2e 100644
--- a/library/std/src/os/unix/process.rs
+++ b/library/std/src/os/unix/process.rs
@@ -378,6 +378,41 @@ fn into_raw(self) -> i32 {
}
}
+#[unstable(feature = "unix_send_signal", issue = "141975")]
+pub trait ChildExt: Sealed {
+ /// Sends a signal to a child process.
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error if the signal is invalid. The integer values associated
+ /// with signals are implemenation-specific, so it's encouraged to use a crate that provides
+ /// posix bindings.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// #![feature(unix_send_signal)]
+ ///
+ /// use std::{io, os::unix::process::ChildExt, process::{Command, Stdio}};
+ ///
+ /// use libc::SIGTERM;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let child = Command::new("cat").stdin(Stdio::piped()).spawn()?;
+ /// child.send_signal(SIGTERM)?;
+ /// Ok(())
+ /// }
+ /// ```
+ fn send_signal(&self, signal: i32) -> io::Result<()>;
+}
+
+#[unstable(feature = "unix_send_signal", issue = "141975")]
+impl ChildExt for process::Child {
+ fn send_signal(&self, signal: i32) -> io::Result<()> {
+ self.handle.send_signal(signal)
+ }
+}
+
#[stable(feature = "process_extensions", since = "1.2.0")]
impl FromRawFd for process::Stdio {
#[inline]
diff --git a/library/std/src/sys/pal/unix/linux/pidfd.rs b/library/std/src/sys/pal/unix/linux/pidfd.rs
index 2d949ec..47e9a61 100644
--- a/library/std/src/sys/pal/unix/linux/pidfd.rs
+++ b/library/std/src/sys/pal/unix/linux/pidfd.rs
@@ -13,11 +13,15 @@
impl PidFd {
pub fn kill(&self) -> io::Result<()> {
+ self.send_signal(libc::SIGKILL)
+ }
+
+ pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> {
cvt(unsafe {
libc::syscall(
libc::SYS_pidfd_send_signal,
self.0.as_raw_fd(),
- libc::SIGKILL,
+ signal,
crate::ptr::null::<()>(),
0,
)
diff --git a/library/std/src/sys/process/unix/fuchsia.rs b/library/std/src/sys/process/unix/fuchsia.rs
index 017ab91..d71be51 100644
--- a/library/std/src/sys/process/unix/fuchsia.rs
+++ b/library/std/src/sys/process/unix/fuchsia.rs
@@ -152,6 +152,11 @@ pub fn kill(&mut self) -> io::Result<()> {
Ok(())
}
+ pub fn send_signal(&self, _signal: i32) -> io::Result<()> {
+ // Fuchsia doesn't have a direct equivalent for signals
+ unimplemented!()
+ }
+
pub fn wait(&mut self) -> io::Result<ExitStatus> {
let mut proc_info: zx_info_process_t = Default::default();
let mut actual: size_t = 0;
diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs
index 4f595ac..1fe80c1 100644
--- a/library/std/src/sys/process/unix/unix.rs
+++ b/library/std/src/sys/process/unix/unix.rs
@@ -963,9 +963,13 @@ pub fn id(&self) -> u32 {
self.pid as u32
}
- pub fn kill(&mut self) -> io::Result<()> {
+ pub fn kill(&self) -> io::Result<()> {
+ self.send_signal(libc::SIGKILL)
+ }
+
+ pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> {
// If we've already waited on this process then the pid can be recycled
- // and used for another process, and we probably shouldn't be killing
+ // and used for another process, and we probably shouldn't be signaling
// random processes, so return Ok because the process has exited already.
if self.status.is_some() {
return Ok(());
@@ -973,9 +977,9 @@ pub fn kill(&mut self) -> io::Result<()> {
#[cfg(target_os = "linux")]
if let Some(pid_fd) = self.pidfd.as_ref() {
// pidfd_send_signal predates pidfd_open. so if we were able to get an fd then sending signals will work too
- return pid_fd.kill();
+ return pid_fd.send_signal(signal);
}
- cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
+ cvt(unsafe { libc::kill(self.pid, signal) }).map(drop)
}
pub fn wait(&mut self) -> io::Result<ExitStatus> {
diff --git a/library/std/src/sys/process/unix/unsupported.rs b/library/std/src/sys/process/unix/unsupported.rs
index e86561a..87403cd 100644
--- a/library/std/src/sys/process/unix/unsupported.rs
+++ b/library/std/src/sys/process/unix/unsupported.rs
@@ -40,7 +40,11 @@ pub fn id(&self) -> u32 {
0
}
- pub fn kill(&mut self) -> io::Result<()> {
+ pub fn kill(&self) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn send_signal(&self, _signal: i32) -> io::Result<()> {
unsupported()
}
diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs
index f33b4a3..51ae8c5 100644
--- a/library/std/src/sys/process/unix/vxworks.rs
+++ b/library/std/src/sys/process/unix/vxworks.rs
@@ -146,14 +146,18 @@ pub fn id(&self) -> u32 {
self.pid as u32
}
- pub fn kill(&mut self) -> io::Result<()> {
+ pub fn kill(&self) -> io::Result<()> {
+ self.send_signal(libc::SIGKILL)
+ }
+
+ pub fn send_signal(&self, signal: i32) -> io::Result<()> {
// If we've already waited on this process then the pid can be recycled
// and used for another process, and we probably shouldn't be killing
// random processes, so return Ok because the process has exited already.
if self.status.is_some() {
Ok(())
} else {
- cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
+ cvt(unsafe { libc::kill(self.pid, signal) }).map(drop)
}
}
diff --git a/src/bootstrap/build.rs b/src/bootstrap/build.rs
index e0e32d3..d9810e8 100644
--- a/src/bootstrap/build.rs
+++ b/src/bootstrap/build.rs
@@ -1,6 +1,7 @@
use std::env;
fn main() {
+ // this is needed because `HOST` is only available to build scripts.
let host = env::var("HOST").unwrap();
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rustc-env=BUILD_TRIPLE={host}");
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 0088e85..53a5c6b 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -1129,6 +1129,7 @@ macro_rules! tool_extended {
tool_name: $tool_name:expr,
stable: $stable:expr
$( , add_bins_to_sysroot: $add_bins_to_sysroot:expr )?
+ $( , add_features: $add_features:expr )?
$( , )?
}
) => {
@@ -1168,6 +1169,7 @@ fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
$tool_name,
$path,
None $( .or(Some(&$add_bins_to_sysroot)) )?,
+ None $( .or(Some($add_features)) )?,
)
}
}
@@ -1205,7 +1207,13 @@ fn run_tool_build_step(
tool_name: &'static str,
path: &'static str,
add_bins_to_sysroot: Option<&[&str]>,
+ add_features: Option<fn(&Builder<'_>, TargetSelection, &mut Vec<String>)>,
) -> ToolBuildResult {
+ let mut extra_features = Vec::new();
+ if let Some(func) = add_features {
+ func(builder, target, &mut extra_features);
+ }
+
let ToolBuildResult { tool_path, build_compiler, target_compiler } =
builder.ensure(ToolBuild {
compiler,
@@ -1213,7 +1221,7 @@ fn run_tool_build_step(
tool: tool_name,
mode: Mode::ToolRustc,
path,
- extra_features: vec![],
+ extra_features,
source_type: SourceType::InTree,
allow_features: "",
cargo_args: vec![],
@@ -1256,7 +1264,12 @@ fn run_tool_build_step(
path: "src/tools/clippy",
tool_name: "clippy-driver",
stable: true,
- add_bins_to_sysroot: ["clippy-driver"]
+ add_bins_to_sysroot: ["clippy-driver"],
+ add_features: |builder, target, features| {
+ if builder.config.jemalloc(target) {
+ features.push("jemalloc".to_string());
+ }
+ }
});
tool_extended!(Miri {
path: "src/tools/miri",
diff --git a/src/doc/book b/src/doc/book
index 4433c9f..8a6d44e 160000
--- a/src/doc/book
+++ b/src/doc/book
@@ -1 +1 @@
-Subproject commit 4433c9f0cad8460bee05ede040587f8a1fa3f1de
+Subproject commit 8a6d44e45b7b564eeb6bae30507e1fbac439d72d
diff --git a/src/doc/reference b/src/doc/reference
index d4c66b3..50fc162 160000
--- a/src/doc/reference
+++ b/src/doc/reference
@@ -1 +1 @@
-Subproject commit d4c66b346f4b72d29e70390a3fa3ea7d4e064db1
+Subproject commit 50fc1628f36563958399123829c73755fa7a8421
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
index 9baa9e8..05c7d8b 160000
--- a/src/doc/rust-by-example
+++ b/src/doc/rust-by-example
@@ -1 +1 @@
-Subproject commit 9baa9e863116cb9524a177d5a5c475baac18928a
+Subproject commit 05c7d8bae65f23a1837430c5a19be129d414f5ec
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 204f8de..cf3c4ac 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -149,15 +149,12 @@ pub(crate) fn new_dcx(
diagnostic_width: Option<usize>,
unstable_opts: &UnstableOptions,
) -> rustc_errors::DiagCtxt {
- let fallback_bundle = rustc_errors::fallback_fluent_bundle(
- rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
- false,
- );
+ let translator = rustc_driver::default_translator();
let emitter: Box<DynEmitter> = match error_format {
ErrorOutputType::HumanReadable { kind, color_config } => {
let short = kind.short();
Box::new(
- HumanEmitter::new(stderr_destination(color_config), fallback_bundle)
+ HumanEmitter::new(stderr_destination(color_config), translator)
.sm(source_map.map(|sm| sm as _))
.short_message(short)
.diagnostic_width(diagnostic_width)
@@ -178,7 +175,7 @@ pub(crate) fn new_dcx(
JsonEmitter::new(
Box::new(io::BufWriter::new(io::stderr())),
Some(source_map),
- fallback_bundle,
+ translator,
pretty,
json_rendered,
color_config,
@@ -387,8 +384,6 @@ pub(crate) fn run_global_ctxt(
ctxt.external_traits.insert(sized_trait_did, sized_trait);
}
- debug!("crate: {:?}", tcx.hir_crate(()));
-
let mut krate = tcx.sess.time("clean_crate", || clean::krate(&mut ctxt));
if krate.module.doc_value().is_empty() {
diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs
index 3ff6828..f229f77 100644
--- a/src/librustdoc/doctest/make.rs
+++ b/src/librustdoc/doctest/make.rs
@@ -456,16 +456,13 @@ fn parse_source(
let filename = FileName::anon_source_code(&wrapped_source);
let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
- let fallback_bundle = rustc_errors::fallback_fluent_bundle(
- rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
- false,
- );
+ let translator = rustc_driver::default_translator();
info.supports_color =
- HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone())
+ HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator.clone())
.supports_color();
// Any errors in parsing should also appear when the doctest is compiled for real, so just
// send all the errors that the parser emits directly into a `Sink` instead of stderr.
- let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
+ let emitter = HumanEmitter::new(Box::new(io::sink()), translator);
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index b611a3e..a2c4870 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -5394,43 +5394,6 @@
search(true);
}
-// @ts-expect-error
-function initSearch(searchIndx) {
- rawSearchIndex = searchIndx;
- if (typeof window !== "undefined") {
- // @ts-expect-error
- docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState);
- registerSearchEvents();
- // If there's a search term in the URL, execute the search now.
- if (window.searchState.getQueryStringParams().search) {
- search();
- }
- } else if (typeof exports !== "undefined") {
- // @ts-expect-error
- docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState);
- exports.docSearch = docSearch;
- exports.parseQuery = DocSearch.parseQuery;
- }
-}
-
-if (typeof exports !== "undefined") {
- exports.initSearch = initSearch;
-}
-
-if (typeof window !== "undefined") {
- // @ts-expect-error
- window.initSearch = initSearch;
- // @ts-expect-error
- if (window.searchIndex !== undefined) {
- // @ts-expect-error
- initSearch(window.searchIndex);
- }
-} else {
- // Running in Node, not a browser. Run initSearch just to produce the
- // exports.
- initSearch(new Map());
-}
-
// Parts of this code are based on Lucene, which is licensed under the
// Apache/2.0 license.
// More information found here:
@@ -5909,3 +5872,44 @@
Lev1TParametricDescription.prototype.offsetIncrs3 = /*2 bits per value */ new Int32Array([
0xa0fc0000,0x5555ba08,0x55555555,
]);
+
+// ====================
+// WARNING: Nothing should be added below this comment: we need the `initSearch` function to
+// be called ONLY when the whole file has been parsed and loaded.
+
+// @ts-expect-error
+function initSearch(searchIndx) {
+ rawSearchIndex = searchIndx;
+ if (typeof window !== "undefined") {
+ // @ts-expect-error
+ docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState);
+ registerSearchEvents();
+ // If there's a search term in the URL, execute the search now.
+ if (window.searchState.getQueryStringParams().search) {
+ search();
+ }
+ } else if (typeof exports !== "undefined") {
+ // @ts-expect-error
+ docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState);
+ exports.docSearch = docSearch;
+ exports.parseQuery = DocSearch.parseQuery;
+ }
+}
+
+if (typeof exports !== "undefined") {
+ exports.initSearch = initSearch;
+}
+
+if (typeof window !== "undefined") {
+ // @ts-expect-error
+ window.initSearch = initSearch;
+ // @ts-expect-error
+ if (window.searchIndex !== undefined) {
+ // @ts-expect-error
+ initSearch(window.searchIndex);
+ }
+} else {
+ // Running in Node, not a browser. Run initSearch just to produce the
+ // exports.
+ initSearch(new Map());
+}
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 0f62c8b..61493c1 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -18,7 +18,6 @@
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
-use rustc_session::features::StabilityExt;
use rustc_span::def_id::LOCAL_CRATE;
use rustdoc_json_types as types;
// It's important to use the FxHashMap from rustdoc_json_types here, instead of
@@ -148,7 +147,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
.copied()
.filter(|(_, stability, _)| {
// Describe only target features which the user can toggle
- stability.is_toggle_permitted(sess).is_ok()
+ stability.toggle_allowed().is_ok()
})
.map(|(name, stability, implied_features)| {
types::TargetFeature {
@@ -164,7 +163,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
// Imply only target features which the user can toggle
feature_stability
.get(name)
- .map(|stability| stability.is_toggle_permitted(sess).is_ok())
+ .map(|stability| stability.toggle_allowed().is_ok())
.unwrap_or(false)
})
.map(String::from)
diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs
index 9662dd8..91cddbe 100644
--- a/src/librustdoc/passes/lint/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs
@@ -6,8 +6,8 @@
use rustc_data_structures::sync::Lock;
use rustc_errors::emitter::Emitter;
use rustc_errors::registry::Registry;
-use rustc_errors::translation::{Translate, to_fluent_args};
-use rustc_errors::{Applicability, DiagCtxt, DiagInner, LazyFallbackBundle};
+use rustc_errors::translation::{Translator, to_fluent_args};
+use rustc_errors::{Applicability, DiagCtxt, DiagInner};
use rustc_parse::{source_str_to_stream, unwrap_or_emit_fatal};
use rustc_resolve::rustdoc::source_span_for_markdown_range;
use rustc_session::parse::ParseSess;
@@ -36,11 +36,8 @@ fn check_rust_syntax(
code_block: RustCodeBlock,
) {
let buffer = Arc::new(Lock::new(Buffer::default()));
- let fallback_bundle = rustc_errors::fallback_fluent_bundle(
- rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
- false,
- );
- let emitter = BufferEmitter { buffer: Arc::clone(&buffer), fallback_bundle };
+ let translator = rustc_driver::default_translator();
+ let emitter = BufferEmitter { buffer: Arc::clone(&buffer), translator };
let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
@@ -149,17 +146,7 @@ struct Buffer {
struct BufferEmitter {
buffer: Arc<Lock<Buffer>>,
- fallback_bundle: LazyFallbackBundle,
-}
-
-impl Translate for BufferEmitter {
- fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
- None
- }
-
- fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
- &self.fallback_bundle
- }
+ translator: Translator,
}
impl Emitter for BufferEmitter {
@@ -168,6 +155,7 @@ fn emit_diagnostic(&mut self, diag: DiagInner, _registry: &Registry) {
let fluent_args = to_fluent_args(diag.args.iter());
let translated_main_message = self
+ .translator
.translate_message(&diag.messages[0].0, &fluent_args)
.unwrap_or_else(|e| panic!("{e}"));
@@ -180,4 +168,8 @@ fn emit_diagnostic(&mut self, diag: DiagInner, _registry: &Registry) {
fn source_map(&self) -> Option<&SourceMap> {
None
}
+
+ fn translator(&self) -> &Translator {
+ &self.translator
+ }
}
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 1f93895..4d25124 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -37,8 +37,8 @@
// will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line
// are deliberately not in a doc comment, because they need not be in public docs.)
//
-// Latest feature: Pretty printing of inline attributes changed
-pub const FORMAT_VERSION: u32 = 48;
+// Latest feature: Pretty printing of cold attributes changed
+pub const FORMAT_VERSION: u32 = 50;
/// The root of the emitted JSON blob.
///
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 3a76c61..13cf82a 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -58,6 +58,7 @@
[features]
integration = ["dep:tempfile"]
internal = ["dep:clippy_lints_internal", "dep:tempfile"]
+jemalloc = []
[package.metadata.rust-analyzer]
# This package uses #[feature(rustc_private)]
diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
index ec45380..7ba11c2 100644
--- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
@@ -42,9 +42,8 @@ fn check_code_sample(code: String, edition: Edition, ignore: bool) -> (bool, Vec
let mut test_attr_spans = vec![];
let filename = FileName::anon_source_code(&code);
- let fallback_bundle =
- rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
- let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
+ let translator = rustc_driver::default_translator();
+ let emitter = HumanEmitter::new(Box::new(io::sink()), translator);
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
#[expect(clippy::arc_with_non_send_sync)] // `Arc` is expected by with_dcx
let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index 37adb14..426ba87 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -13,6 +13,11 @@
extern crate rustc_session;
extern crate rustc_span;
+// See docs in https://github.com/rust-lang/rust/blob/master/compiler/rustc/src/main.rs
+// about jemalloc.
+#[cfg(feature = "jemalloc")]
+extern crate tikv_jemalloc_sys as jemalloc_sys;
+
use clippy_utils::sym;
use rustc_interface::interface;
use rustc_session::EarlyDiagCtxt;
@@ -181,6 +186,36 @@ fn display_help() {
#[allow(clippy::too_many_lines)]
#[allow(clippy::ignored_unit_patterns)]
pub fn main() {
+ // See docs in https://github.com/rust-lang/rust/blob/master/compiler/rustc/src/main.rs
+ // about jemalloc.
+ #[cfg(feature = "jemalloc")]
+ {
+ use std::os::raw::{c_int, c_void};
+
+ #[used]
+ static _F1: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::calloc;
+ #[used]
+ static _F2: unsafe extern "C" fn(*mut *mut c_void, usize, usize) -> c_int = jemalloc_sys::posix_memalign;
+ #[used]
+ static _F3: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::aligned_alloc;
+ #[used]
+ static _F4: unsafe extern "C" fn(usize) -> *mut c_void = jemalloc_sys::malloc;
+ #[used]
+ static _F5: unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void = jemalloc_sys::realloc;
+ #[used]
+ static _F6: unsafe extern "C" fn(*mut c_void) = jemalloc_sys::free;
+
+ #[cfg(target_os = "macos")]
+ {
+ unsafe extern "C" {
+ fn _rjem_je_zone_register();
+ }
+
+ #[used]
+ static _F7: unsafe extern "C" fn() = _rjem_je_zone_register;
+ }
+ }
+
let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
rustc_driver::init_rustc_env_logger(&early_dcx);
diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
index 5f8a4ce..49595e2 100644
--- a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
+++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
@@ -9,28 +9,35 @@
&& let ExprKind::Call(func, args) = e.kind
&& paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
&& args.len() == 1
- && let ExprKind::Call(func1, args1) = args[0].kind
- && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
- && args1.len() == 2
+ && let ExprKind::Block(block1, None) = args[0].kind
+ && block1.stmts.len() == 1
+ && let StmtKind::Let(local1) = block1.stmts[0].kind
+ && let Some(init1) = local1.init
+ && let ExprKind::Array(elements) = init1.kind
+ && elements.len() == 1
+ && let ExprKind::Call(func1, args1) = elements[0].kind
+ && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
+ && args1.len() == 1
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
- && let ExprKind::Array(elements) = inner.kind
- && elements.len() == 2
- && let ExprKind::Lit(ref lit) = elements[0].kind
+ && let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind
+ && name.as_str() == "args"
+ && let Some(trailing_expr) = block1.expr
+ && let ExprKind::Call(func2, args2) = trailing_expr.kind
+ && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
+ && args2.len() == 2
+ && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind
+ && let ExprKind::Array(elements1) = inner1.kind
+ && elements1.len() == 2
+ && let ExprKind::Lit(ref lit) = elements1[0].kind
&& let LitKind::Str(s, _) = lit.node
&& s.as_str() == ""
- && let ExprKind::Lit(ref lit1) = elements[1].kind
+ && let ExprKind::Lit(ref lit1) = elements1[1].kind
&& let LitKind::Str(s1, _) = lit1.node
&& s1.as_str() == "\n"
- && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args1[1].kind
- && let ExprKind::Array(elements1) = inner1.kind
- && elements1.len() == 1
- && let ExprKind::Call(func2, args2) = elements1[0].kind
- && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
- && args2.len() == 1
- && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
+ && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind
&& block.expr.is_none()
- && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
- && name.as_str() == "print_text"
+ && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind
+ && name1.as_str() == "print_text"
{
// report your lint here
}
diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
index ecc2525..4fc7b49 100644
--- a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
+++ b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
@@ -19,25 +19,32 @@
&& let ExprKind::Call(func, args) = e1.kind
&& paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
&& args.len() == 1
- && let ExprKind::Call(func1, args1) = args[0].kind
- && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
- && args1.len() == 2
+ && let ExprKind::Block(block2, None) = args[0].kind
+ && block2.stmts.len() == 1
+ && let StmtKind::Let(local) = block2.stmts[0].kind
+ && let Some(init) = local.init
+ && let ExprKind::Array(elements) = init.kind
+ && elements.len() == 1
+ && let ExprKind::Call(func1, args1) = elements[0].kind
+ && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
+ && args1.len() == 1
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
- && let ExprKind::Array(elements) = inner.kind
- && elements.len() == 2
- && let ExprKind::Lit(ref lit2) = elements[0].kind
+ && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind
+ && name1.as_str() == "args"
+ && let Some(trailing_expr) = block2.expr
+ && let ExprKind::Call(func2, args2) = trailing_expr.kind
+ && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
+ && args2.len() == 2
+ && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind
+ && let ExprKind::Array(elements1) = inner1.kind
+ && elements1.len() == 2
+ && let ExprKind::Lit(ref lit2) = elements1[0].kind
&& let LitKind::Str(s, _) = lit2.node
&& s.as_str() == ""
- && let ExprKind::Lit(ref lit3) = elements[1].kind
+ && let ExprKind::Lit(ref lit3) = elements1[1].kind
&& let LitKind::Str(s1, _) = lit3.node
&& s1.as_str() == "\n"
- && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args1[1].kind
- && let ExprKind::Array(elements1) = inner1.kind
- && elements1.len() == 1
- && let ExprKind::Call(func2, args2) = elements1[0].kind
- && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
- && args2.len() == 1
- && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
+ && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind
&& block1.expr.is_none()
&& block.expr.is_none()
{
diff --git a/src/tools/clippy/tests/ui/manual_inspect.fixed b/src/tools/clippy/tests/ui/manual_inspect.fixed
index 9b768db..00a1915 100644
--- a/src/tools/clippy/tests/ui/manual_inspect.fixed
+++ b/src/tools/clippy/tests/ui/manual_inspect.fixed
@@ -154,7 +154,6 @@
});
let _ = [0]
- //~^ suspicious_map
.into_iter()
.inspect(|&x| {
//~^ manual_inspect
diff --git a/src/tools/clippy/tests/ui/manual_inspect.rs b/src/tools/clippy/tests/ui/manual_inspect.rs
index e679636..b3b1713 100644
--- a/src/tools/clippy/tests/ui/manual_inspect.rs
+++ b/src/tools/clippy/tests/ui/manual_inspect.rs
@@ -165,7 +165,6 @@ macro_rules! maybe_ret {
});
let _ = [0]
- //~^ suspicious_map
.into_iter()
.map(|x| {
//~^ manual_inspect
diff --git a/src/tools/clippy/tests/ui/manual_inspect.stderr b/src/tools/clippy/tests/ui/manual_inspect.stderr
index 78b085f..70c00c1 100644
--- a/src/tools/clippy/tests/ui/manual_inspect.stderr
+++ b/src/tools/clippy/tests/ui/manual_inspect.stderr
@@ -157,25 +157,8 @@
LL ~ println!("{}", x);
|
-error: this call to `map()` won't have an effect on the call to `count()`
- --> tests/ui/manual_inspect.rs:167:13
- |
-LL | let _ = [0]
- | _____________^
-LL | |
-LL | | .into_iter()
-LL | | .map(|x| {
-... |
-LL | | })
-LL | | .count();
- | |________________^
- |
- = help: make sure you did not confuse `map` with `filter`, `for_each` or `inspect`
- = note: `-D clippy::suspicious-map` implied by `-D warnings`
- = help: to override `-D warnings` add `#[allow(clippy::suspicious_map)]`
-
error: using `map` over `inspect`
- --> tests/ui/manual_inspect.rs:170:10
+ --> tests/ui/manual_inspect.rs:169:10
|
LL | .map(|x| {
| ^^^
@@ -188,7 +171,7 @@
|
error: using `map` over `inspect`
- --> tests/ui/manual_inspect.rs:203:30
+ --> tests/ui/manual_inspect.rs:202:30
|
LL | if let Some(x) = Some(1).map(|x| { println!("{x}");
| ^^^
@@ -200,5 +183,5 @@
LL ~ }) {
|
-error: aborting due to 14 previous errors
+error: aborting due to 13 previous errors
diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs
index afd847f..10e2809 100644
--- a/src/tools/rustfmt/src/parse/session.rs
+++ b/src/tools/rustfmt/src/parse/session.rs
@@ -5,7 +5,7 @@
use rustc_data_structures::sync::IntoDynSyncSend;
use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter, SilentEmitter, stderr_destination};
use rustc_errors::registry::Registry;
-use rustc_errors::translation::Translate;
+use rustc_errors::translation::Translator;
use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel};
use rustc_session::parse::ParseSess as RawParseSess;
use rustc_span::{
@@ -47,16 +47,6 @@ fn handle_non_ignoreable_error(&mut self, diag: DiagInner, registry: &Registry)
}
}
-impl Translate for SilentOnIgnoredFilesEmitter {
- fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
- self.emitter.fluent_bundle()
- }
-
- fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
- self.emitter.fallback_fluent_bundle()
- }
-}
-
impl Emitter for SilentOnIgnoredFilesEmitter {
fn source_map(&self) -> Option<&SourceMap> {
None
@@ -84,6 +74,10 @@ fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry) {
}
self.handle_non_ignoreable_error(diag, registry);
}
+
+ fn translator(&self) -> &Translator {
+ self.emitter.translator()
+ }
}
impl From<Color> for ColorConfig {
@@ -110,23 +104,15 @@ fn default_dcx(
ColorConfig::Never
};
- let fallback_bundle = rustc_errors::fallback_fluent_bundle(
- rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
- false,
- );
- let emitter = Box::new(
- HumanEmitter::new(stderr_destination(emit_color), fallback_bundle)
- .sm(Some(source_map.clone())),
- );
+ let translator = rustc_driver::default_translator();
- let emitter: Box<DynEmitter> = if !show_parse_errors {
- Box::new(SilentEmitter {
- fatal_emitter: emitter,
- fatal_note: None,
- emit_fatal_diagnostic: false,
- })
+ let emitter: Box<DynEmitter> = if show_parse_errors {
+ Box::new(
+ HumanEmitter::new(stderr_destination(emit_color), translator)
+ .sm(Some(source_map.clone())),
+ )
} else {
- emitter
+ Box::new(SilentEmitter { translator })
};
DiagCtxt::new(Box::new(SilentOnIgnoredFilesEmitter {
has_non_ignorable_parser_errors: false,
@@ -205,7 +191,7 @@ pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
}
pub(crate) fn set_silent_emitter(&mut self) {
- self.raw_psess.dcx().make_silent(None, false);
+ self.raw_psess.dcx().make_silent();
}
pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
@@ -335,16 +321,6 @@ struct TestEmitter {
num_emitted_errors: Arc<AtomicU32>,
}
- impl Translate for TestEmitter {
- fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
- None
- }
-
- fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
- panic!("test emitter attempted to translate a diagnostic");
- }
- }
-
impl Emitter for TestEmitter {
fn source_map(&self) -> Option<&SourceMap> {
None
@@ -353,6 +329,10 @@ fn source_map(&self) -> Option<&SourceMap> {
fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {
self.num_emitted_errors.fetch_add(1, Ordering::Release);
}
+
+ fn translator(&self) -> &Translator {
+ panic!("test emitter attempted to translate a diagnostic");
+ }
}
fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> DiagInner {
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index 045f2f06..b3517b2 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -305,7 +305,6 @@
ui/borrowck/issue-10876.rs
ui/borrowck/issue-109271-pass-self-into-closure.rs
ui/borrowck/issue-111554.rs
-ui/borrowck/issue-114374-invalid-help-fmt-args.rs
ui/borrowck/issue-11493.rs
ui/borrowck/issue-115259-suggest-iter-mut.rs
ui/borrowck/issue-119915-bad-clone-suggestion.rs
diff --git a/tests/assembly/s390x-backchain-toggle.rs b/tests/assembly/s390x-backchain-toggle.rs
index 83c7b82..9bae15b 100644
--- a/tests/assembly/s390x-backchain-toggle.rs
+++ b/tests/assembly/s390x-backchain-toggle.rs
@@ -1,5 +1,5 @@
//@ add-core-stubs
-//@ revisions: enable-backchain disable-backchain
+//@ revisions: enable-backchain disable-backchain default-backchain
//@ assembly-output: emit-asm
//@ compile-flags: -Copt-level=3 --crate-type=lib --target=s390x-unknown-linux-gnu
//@ needs-llvm-components: systemz
@@ -26,6 +26,8 @@ extern "C" fn test_backchain() -> i32 {
// enable-backchain: stg [[REG1]], 0(%r15)
// disable-backchain: aghi %r15, -160
// disable-backchain-NOT: stg %r{{.*}}, 0(%r15)
+ // default-backchain: aghi %r15, -160
+ // default-backchain-NOT: stg %r{{.*}}, 0(%r15)
unsafe {
extern_func();
}
@@ -35,6 +37,7 @@ extern "C" fn test_backchain() -> i32 {
// Make sure that the expected return value is written into %r2 (return register):
// enable-backchain-NEXT: lghi %r2, 1
// disable-backchain: lghi %r2, 0
+ // default-backchain: lghi %r2, 0
#[cfg(target_feature = "backchain")]
{
1
diff --git a/tests/codegen/target-feature-negative-implication.rs b/tests/codegen/target-feature-negative-implication.rs
new file mode 100644
index 0000000..36cd82d
--- /dev/null
+++ b/tests/codegen/target-feature-negative-implication.rs
@@ -0,0 +1,20 @@
+//@ add-core-stubs
+//@ needs-llvm-components: x86
+//@ compile-flags: --target=x86_64-unknown-linux-gnu
+//@ compile-flags: -Ctarget-feature=-avx2
+
+#![feature(no_core, lang_items)]
+#![crate_type = "lib"]
+#![no_core]
+
+extern crate minicore;
+use minicore::*;
+
+#[no_mangle]
+pub unsafe fn banana() {
+ // CHECK-LABEL: @banana()
+ // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] {
+}
+
+// CHECK: attributes [[BANANAATTRS]]
+// CHECK-SAME: -avx512
diff --git a/tests/codegen/target-feature-overrides.rs b/tests/codegen/target-feature-overrides.rs
index 0fc1e01..eb19b0d 100644
--- a/tests/codegen/target-feature-overrides.rs
+++ b/tests/codegen/target-feature-overrides.rs
@@ -1,3 +1,4 @@
+// ignore-tidy-linelength
//@ add-core-stubs
//@ revisions: COMPAT INCOMPAT
//@ needs-llvm-components: x86
@@ -39,7 +40,7 @@ pub unsafe fn banana() -> u32 {
// CHECK: attributes [[APPLEATTRS]]
// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}"
-// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx,{{.*}}"
+// INCOMPAT-SAME: "target-features"="{{(-[^,]+,)*}}-avx2{{(,-[^,]+)*}},-avx{{(,-[^,]+)*}},+avx{{(,\+[^,]+)*}}"
// CHECK: attributes [[BANANAATTRS]]
// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}"
-// INCOMPAT-SAME: "target-features"="-avx2,-avx"
+// INCOMPAT-SAME: "target-features"="{{(-[^,]+,)*}}-avx2{{(,-[^,]+)*}},-avx{{(,-[^,]+)*}}"
diff --git a/tests/codegen/tied-features-strength.rs b/tests/codegen/tied-features-strength.rs
index 6be0e21..81499c0 100644
--- a/tests/codegen/tied-features-strength.rs
+++ b/tests/codegen/tied-features-strength.rs
@@ -4,14 +4,23 @@
//@ compile-flags: --crate-type=rlib --target=aarch64-unknown-linux-gnu
//@ needs-llvm-components: aarch64
+// Rust made SVE require neon.
//@ [ENABLE_SVE] compile-flags: -C target-feature=+sve -Copt-level=0
-// ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+sve,?)|(\+neon,?)|(\+fp-armv8,?))*}}" }
+// ENABLE_SVE: attributes #0
+// ENABLE_SVE-SAME: +neon
+// ENABLE_SVE-SAME: +sve
+// However, disabling SVE does not disable neon.
//@ [DISABLE_SVE] compile-flags: -C target-feature=-sve -Copt-level=0
-// DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(-sve,?)|(\+neon,?))*}}" }
+// DISABLE_SVE: attributes #0
+// DISABLE_SVE-NOT: -neon
+// DISABLE_SVE-SAME: -sve
+// OTOH, neon fn `fp-armv8` are fully tied; toggling neon must toggle `fp-armv8` the same way.
//@ [DISABLE_NEON] compile-flags: -C target-feature=-neon -Copt-level=0
-// DISABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(-fp-armv8,?)|(-neon,?))*}}" }
+// DISABLE_NEON: attributes #0
+// DISABLE_NEON-SAME: -neon
+// DISABLE_NEON-SAME: -fp-armv8
//@ [ENABLE_NEON] compile-flags: -C target-feature=+neon -Copt-level=0
// ENABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fp-armv8,?)|(\+neon,?))*}}" }
diff --git a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
index 33f1ad9..e2d3c6c 100644
--- a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
+++ b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
@@ -11,28 +11,29 @@
let _9: ();
let _10: ();
let mut _11: std::fmt::Arguments<'_>;
- let mut _12: &[&str; 3];
- let _13: &[&str; 3];
- let _14: [&str; 3];
- let mut _15: &[core::fmt::rt::Argument<'_>; 2];
- let _16: &[core::fmt::rt::Argument<'_>; 2];
- let _17: [core::fmt::rt::Argument<'_>; 2];
+ let mut _13: &std::boxed::Box<dyn std::fmt::Display>;
+ let mut _14: &u32;
+ let mut _16: core::fmt::rt::Argument<'_>;
+ let mut _17: &std::boxed::Box<dyn std::fmt::Display>;
let mut _18: core::fmt::rt::Argument<'_>;
- let mut _19: &std::boxed::Box<dyn std::fmt::Display>;
- let _20: &std::boxed::Box<dyn std::fmt::Display>;
- let mut _21: core::fmt::rt::Argument<'_>;
- let mut _22: &u32;
- let _23: &u32;
- let mut _25: bool;
- let mut _26: isize;
- let mut _27: isize;
- let mut _28: isize;
-+ let _29: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>;
-+ let _30: u32;
+ let mut _19: &u32;
+ let mut _20: &[&str; 3];
+ let _21: &[&str; 3];
+ let _22: [&str; 3];
+ let mut _23: &[core::fmt::rt::Argument<'_>; 2];
+ let _24: &[core::fmt::rt::Argument<'_>; 2];
+ let mut _26: &std::boxed::Box<dyn std::fmt::Display>;
+ let mut _27: &u32;
+ let mut _28: bool;
+ let mut _29: isize;
+ let mut _30: isize;
+ let mut _31: isize;
++ let _32: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>;
++ let _33: u32;
scope 1 {
- debug foo => _1;
-+ debug ((foo: Foo<T>).0: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>) => _29;
-+ debug ((foo: Foo<T>).1: u32) => _30;
++ debug ((foo: Foo<T>).0: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>) => _32;
++ debug ((foo: Foo<T>).1: u32) => _33;
let _5: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>;
scope 2 {
debug x => _5;
@@ -42,17 +43,29 @@
scope 4 {
debug x => _8;
let _8: std::boxed::Box<dyn std::fmt::Display>;
- let mut _24: &[&str; 3];
+ let _12: (&std::boxed::Box<dyn std::fmt::Display>, &u32);
++ let _34: &std::boxed::Box<dyn std::fmt::Display>;
++ let _35: &u32;
+ scope 5 {
+- debug args => _12;
++ debug ((args: (&Box<dyn std::fmt::Display>, &u32)).0: &std::boxed::Box<dyn std::fmt::Display>) => _34;
++ debug ((args: (&Box<dyn std::fmt::Display>, &u32)).1: &u32) => _35;
+ let _15: [core::fmt::rt::Argument<'_>; 2];
+ scope 6 {
+ debug args => _15;
+ let mut _25: &[&str; 3];
+ }
+ }
}
}
}
}
bb0: {
- _25 = const false;
+ _28 = const false;
- StorageLive(_1);
-+ StorageLive(_29);
-+ StorageLive(_30);
++ StorageLive(_32);
++ StorageLive(_33);
+ nop;
StorageLive(_2);
StorageLive(_3);
@@ -66,77 +79,93 @@
_2 = Result::<Box<dyn std::fmt::Display>, <T as Err>::Err>::Ok(move _3);
StorageDead(_3);
- _1 = Foo::<T> { x: move _2, y: const 7_u32 };
-+ _29 = move _2;
-+ _30 = const 7_u32;
++ _32 = move _2;
++ _33 = const 7_u32;
+ nop;
StorageDead(_2);
StorageLive(_5);
- _25 = const true;
+ _28 = const true;
- _5 = move (_1.0: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>);
-+ _5 = move _29;
++ _5 = move _32;
StorageLive(_6);
- _6 = copy (_1.1: u32);
-+ _6 = copy _30;
++ _6 = copy _33;
_7 = discriminant(_5);
switchInt(move _7) -> [0: bb2, otherwise: bb7];
}
bb2: {
StorageLive(_8);
- _25 = const false;
+ _28 = const false;
_8 = move ((_5 as Ok).0: std::boxed::Box<dyn std::fmt::Display>);
StorageLive(_9);
StorageLive(_10);
StorageLive(_11);
- StorageLive(_12);
+- StorageLive(_12);
++ StorageLive(_34);
++ StorageLive(_35);
++ nop;
StorageLive(_13);
- _24 = const foo::<T>::promoted[0];
- _13 = &(*_24);
- _12 = &(*_13);
+ _13 = &_8;
+ StorageLive(_14);
+ _14 = &_6;
+- _12 = (move _13, move _14);
++ _34 = move _13;
++ _35 = move _14;
++ nop;
+ StorageDead(_14);
+ StorageDead(_13);
StorageLive(_15);
StorageLive(_16);
StorageLive(_17);
- StorageLive(_18);
- StorageLive(_19);
- StorageLive(_20);
- _20 = &_8;
- _19 = &(*_20);
- _18 = core::fmt::rt::Argument::<'_>::new_display::<Box<dyn std::fmt::Display>>(move _19) -> [return: bb3, unwind unreachable];
+- _26 = deref_copy (_12.0: &std::boxed::Box<dyn std::fmt::Display>);
++ _26 = deref_copy _34;
+ _17 = &(*_26);
+ _16 = core::fmt::rt::Argument::<'_>::new_display::<Box<dyn std::fmt::Display>>(move _17) -> [return: bb3, unwind unreachable];
}
bb3: {
- StorageDead(_19);
- StorageLive(_21);
- StorageLive(_22);
- StorageLive(_23);
- _23 = &_6;
- _22 = &(*_23);
- _21 = core::fmt::rt::Argument::<'_>::new_display::<u32>(move _22) -> [return: bb4, unwind unreachable];
+ StorageDead(_17);
+ StorageLive(_18);
+ StorageLive(_19);
+- _27 = deref_copy (_12.1: &u32);
++ _27 = deref_copy _35;
+ _19 = &(*_27);
+ _18 = core::fmt::rt::Argument::<'_>::new_display::<u32>(move _19) -> [return: bb4, unwind unreachable];
}
bb4: {
- StorageDead(_22);
- _17 = [move _18, move _21];
- StorageDead(_21);
+ StorageDead(_19);
+ _15 = [move _16, move _18];
StorageDead(_18);
- _16 = &_17;
- _15 = &(*_16);
- _11 = core::fmt::rt::<impl Arguments<'_>>::new_v1::<3, 2>(move _12, move _15) -> [return: bb5, unwind unreachable];
+ StorageDead(_16);
+ StorageLive(_20);
+ StorageLive(_21);
+ _25 = const foo::<T>::promoted[0];
+ _21 = &(*_25);
+ _20 = &(*_21);
+ StorageLive(_23);
+ StorageLive(_24);
+ _24 = &_15;
+ _23 = &(*_24);
+ _11 = core::fmt::rt::<impl Arguments<'_>>::new_v1::<3, 2>(move _20, move _23) -> [return: bb5, unwind unreachable];
}
bb5: {
- StorageDead(_15);
- StorageDead(_12);
+ StorageDead(_24);
+ StorageDead(_23);
+ StorageDead(_21);
+ StorageDead(_20);
_10 = _eprint(move _11) -> [return: bb6, unwind unreachable];
}
bb6: {
StorageDead(_11);
- StorageDead(_23);
- StorageDead(_20);
- StorageDead(_17);
- StorageDead(_16);
- StorageDead(_13);
+ StorageDead(_15);
+- StorageDead(_12);
++ StorageDead(_34);
++ StorageDead(_35);
++ nop;
StorageDead(_10);
_9 = const ();
StorageDead(_9);
@@ -156,22 +185,22 @@
bb9: {
StorageDead(_6);
- _26 = discriminant(_5);
- switchInt(move _26) -> [0: bb11, otherwise: bb13];
+ _29 = discriminant(_5);
+ switchInt(move _29) -> [0: bb11, otherwise: bb13];
}
bb10: {
- _25 = const false;
+ _28 = const false;
StorageDead(_5);
- StorageDead(_1);
-+ StorageDead(_29);
-+ StorageDead(_30);
++ StorageDead(_32);
++ StorageDead(_33);
+ nop;
return;
}
bb11: {
- switchInt(copy _25) -> [0: bb10, otherwise: bb12];
+ switchInt(copy _28) -> [0: bb10, otherwise: bb12];
}
bb12: {
diff --git a/tests/ui/attributes/expected-word.rs b/tests/ui/attributes/expected-word.rs
new file mode 100644
index 0000000..246aa78
--- /dev/null
+++ b/tests/ui/attributes/expected-word.rs
@@ -0,0 +1,3 @@
+#[cold = true]
+//~^ ERROR malformed `cold` attribute input [E0565]
+fn main() {}
diff --git a/tests/ui/attributes/expected-word.stderr b/tests/ui/attributes/expected-word.stderr
new file mode 100644
index 0000000..dcb10e7
--- /dev/null
+++ b/tests/ui/attributes/expected-word.stderr
@@ -0,0 +1,12 @@
+error[E0565]: malformed `cold` attribute input
+ --> $DIR/expected-word.rs:1:1
+ |
+LL | #[cold = true]
+ | ^^^^^^^------^
+ | | |
+ | | didn't expect any arguments here
+ | help: must be of the form: `#[cold]`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0565`.
diff --git a/tests/ui/borrowck/clone-on-ref.stderr b/tests/ui/borrowck/clone-on-ref.stderr
index 911c136..72580e7 100644
--- a/tests/ui/borrowck/clone-on-ref.stderr
+++ b/tests/ui/borrowck/clone-on-ref.stderr
@@ -30,7 +30,7 @@
| ^ move out of `x` occurs here
LL |
LL | println!("{b}");
- | --- borrow later used here
+ | - borrow later used here
|
help: if `T` implemented `Clone`, you could clone the value
--> $DIR/clone-on-ref.rs:11:8
@@ -57,7 +57,7 @@
| ^ move out of `x` occurs here
LL |
LL | println!("{b:?}");
- | ----- borrow later used here
+ | - borrow later used here
|
note: if `A` implemented `Clone`, you could clone the value
--> $DIR/clone-on-ref.rs:19:1
diff --git a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.rs b/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.rs
deleted file mode 100644
index 4a6c2f9..0000000
--- a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-#![allow(dead_code)]
-
-fn bar<'a>(_: std::fmt::Arguments<'a>) {}
-fn main() {
- let x = format_args!("a {} {} {}.", 1, format_args!("b{}!", 2), 3);
- //~^ ERROR temporary value dropped while borrowed
-
- bar(x);
-
- let foo = format_args!("{}", "hi");
- //~^ ERROR temporary value dropped while borrowed
- bar(foo);
-
- let foo = format_args!("hi"); // no placeholder in arguments, so no error
- bar(foo);
-}
diff --git a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr b/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr
deleted file mode 100644
index 30f292f..0000000
--- a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr
+++ /dev/null
@@ -1,31 +0,0 @@
-error[E0716]: temporary value dropped while borrowed
- --> $DIR/issue-114374-invalid-help-fmt-args.rs:5:13
- |
-LL | let x = format_args!("a {} {} {}.", 1, format_args!("b{}!", 2), 3);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
- | |
- | creates a temporary value which is freed while still in use
-...
-LL | bar(x);
- | - borrow later used here
- |
- = note: the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used
- = note: to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>
-
-error[E0716]: temporary value dropped while borrowed
- --> $DIR/issue-114374-invalid-help-fmt-args.rs:10:15
- |
-LL | let foo = format_args!("{}", "hi");
- | ^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
- | |
- | creates a temporary value which is freed while still in use
-LL |
-LL | bar(foo);
- | --- borrow later used here
- |
- = note: the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used
- = note: to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0716`.
diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr
index f29a41d..f422919 100644
--- a/tests/ui/check-cfg/target_feature.stderr
+++ b/tests/ui/check-cfg/target_feature.stderr
@@ -211,10 +211,6 @@
`reference-types`
`relax`
`relaxed-simd`
-`reserve-x18`
-`retpoline-external-thunk`
-`retpoline-indirect-branches`
-`retpoline-indirect-calls`
`rtm`
`sb`
`scq`
diff --git a/tests/ui/consts/recursive-const-in-impl.stderr b/tests/ui/consts/recursive-const-in-impl.stderr
index 6175112..035d9c2 100644
--- a/tests/ui/consts/recursive-const-in-impl.stderr
+++ b/tests/ui/consts/recursive-const-in-impl.stderr
@@ -1,11 +1,12 @@
error: queries overflow the depth limit!
- --> $DIR/recursive-const-in-impl.rs:11:14
+ --> $DIR/recursive-const-in-impl.rs:11:20
|
LL | println!("{}", Thing::<i32>::X);
- | ^^^^
+ | ^^^^^^^^^^^^^^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "14"]` attribute to your crate (`recursive_const_in_impl`)
= note: query depth increased by 9 when simplifying constant for the type system `main::promoted[1]`
+ = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 1 previous error
diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.rs b/tests/ui/feature-gates/feature-gate-optimize_attribute.rs
index 77cc307..ed5a112 100644
--- a/tests/ui/feature-gates/feature-gate-optimize_attribute.rs
+++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.rs
@@ -11,5 +11,5 @@ fn none() {}
#[optimize(banana)]
//~^ ERROR the `#[optimize]` attribute is an experimental feature
-//~| ERROR E0722
+//~| ERROR malformed `optimize` attribute input [E0539]
fn not_known() {}
diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
index 4e6e4ac..e7e62b4 100644
--- a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
+++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
@@ -38,13 +38,16 @@
= help: add `#![feature(optimize_attribute)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-error[E0722]: invalid argument
- --> $DIR/feature-gate-optimize_attribute.rs:12:12
+error[E0539]: malformed `optimize` attribute input
+ --> $DIR/feature-gate-optimize_attribute.rs:12:1
|
LL | #[optimize(banana)]
- | ^^^^^^
+ | ^^^^^^^^^^^------^^
+ | | |
+ | | valid arguments are `size`, `speed` or `none`
+ | help: must be of the form: `#[optimize(size|speed|none)]`
error: aborting due to 5 previous errors
-Some errors have detailed explanations: E0658, E0722.
-For more information about an error, try `rustc --explain E0658`.
+Some errors have detailed explanations: E0539, E0658.
+For more information about an error, try `rustc --explain E0539`.
diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
index 1c6868d..9280dfd 100644
--- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
+++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
@@ -379,14 +379,6 @@
LL | #![proc_macro_derive()]
| ^^^^^^^^^^^^^^^^^^^^^^^
-warning: attribute should be applied to a function definition
- --> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1
- |
-LL | #![cold]
- | ^^^^^^^^ cannot be applied to crates
- |
- = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-
warning: attribute should be applied to an `extern` block with non-Rust ABI
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:64:1
|
@@ -417,6 +409,14 @@
LL | #![must_use]
| ^^^^^^^^^^^^
+warning: attribute should be applied to a function definition
+ --> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1
+ |
+LL | #![cold]
+ | ^^^^^^^^ cannot be applied to crates
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+
warning: `#[macro_use]` only has an effect on `extern crate` and modules
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:176:5
|
diff --git a/tests/ui/impl-trait/precise-capturing/foreign-2021.stderr b/tests/ui/impl-trait/precise-capturing/foreign-2021.stderr
index cd9ed0f..1dcd800 100644
--- a/tests/ui/impl-trait/precise-capturing/foreign-2021.stderr
+++ b/tests/ui/impl-trait/precise-capturing/foreign-2021.stderr
@@ -8,7 +8,7 @@
| ^^^^^^^^^ mutable borrow occurs here
...
LL | println!("{h}");
- | --- immutable borrow later used here
+ | - immutable borrow later used here
|
note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
--> $DIR/foreign-2021.rs:7:13
diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.stderr b/tests/ui/impl-trait/precise-capturing/migration-note.stderr
index 676b6c1..aa0f640 100644
--- a/tests/ui/impl-trait/precise-capturing/migration-note.stderr
+++ b/tests/ui/impl-trait/precise-capturing/migration-note.stderr
@@ -23,7 +23,7 @@
| ^^^^^^^^^ mutable borrow occurs here
...
LL | println!("{a}");
- | --- immutable borrow later used here
+ | - immutable borrow later used here
|
note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
--> $DIR/migration-note.rs:16:13
@@ -99,7 +99,7 @@
| ^ second mutable borrow occurs here
...
LL | println!("{a}");
- | --- first borrow later used here
+ | - first borrow later used here
|
note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
--> $DIR/migration-note.rs:63:13
@@ -175,7 +175,7 @@
| ^^^^^^^ `s.f` is assigned to here but it was already borrowed
...
LL | println!("{a}");
- | --- borrow later used here
+ | - borrow later used here
|
note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
--> $DIR/migration-note.rs:112:13
@@ -197,7 +197,7 @@
| ^^^^^^^ `s.f` is assigned to here but it was already borrowed
...
LL | println!("{a}");
- | --- borrow later used here
+ | - borrow later used here
|
note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
--> $DIR/migration-note.rs:128:13
@@ -219,7 +219,7 @@
| ^^^ use of borrowed `s.f`
...
LL | println!("{a}");
- | --- borrow later used here
+ | - borrow later used here
|
note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
--> $DIR/migration-note.rs:140:13
diff --git a/tests/ui/issues/issue-43988.stderr b/tests/ui/issues/issue-43988.stderr
index bd4eb8b..fe61e13 100644
--- a/tests/ui/issues/issue-43988.stderr
+++ b/tests/ui/issues/issue-43988.stderr
@@ -38,7 +38,7 @@
| ^^^^^^^
| |
| expected this to be a list
- | help: must be of the form: `#[repr(C)]`
+ | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
error[E0539]: malformed `inline` attribute input
--> $DIR/issue-43988.rs:30:5
@@ -64,7 +64,7 @@
| ^^^^^^^
| |
| expected this to be a list
- | help: must be of the form: `#[repr(C)]`
+ | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
error[E0518]: attribute should be applied to function or closure
--> $DIR/issue-43988.rs:5:5
diff --git a/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr b/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr
index 66f9140..b0d64b5 100644
--- a/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr
+++ b/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr
@@ -6,7 +6,7 @@
| |
| creates a temporary value which is freed while still in use
LL | println!("{a:?} {b:?} {c:?} {d:?} {e:?} {f:?} {g:?}");
- | ----- borrow later used here
+ | - borrow later used here
|
help: consider using a `let` binding to create a longer lived value
|
diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr
index e1c45e8..03ce975 100644
--- a/tests/ui/lint/unused/unused-attr-duplicate.stderr
+++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr
@@ -103,18 +103,6 @@
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: unused attribute
- --> $DIR/unused-attr-duplicate.rs:77:1
- |
-LL | #[cold]
- | ^^^^^^^ help: remove this attribute
- |
-note: attribute also specified here
- --> $DIR/unused-attr-duplicate.rs:76:1
- |
-LL | #[cold]
- | ^^^^^^^
-
-error: unused attribute
--> $DIR/unused-attr-duplicate.rs:79:1
|
LL | #[track_caller]
@@ -289,5 +277,17 @@
| ^^^^^^^^^^^^^^^^^
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+error: unused attribute
+ --> $DIR/unused-attr-duplicate.rs:77:1
+ |
+LL | #[cold]
+ | ^^^^^^^ help: remove this attribute
+ |
+note: attribute also specified here
+ --> $DIR/unused-attr-duplicate.rs:76:1
+ |
+LL | #[cold]
+ | ^^^^^^^
+
error: aborting due to 23 previous errors
diff --git a/tests/ui/macros/format-args-temporaries-in-write.stderr b/tests/ui/macros/format-args-temporaries-in-write.stderr
index e58a433..4899878 100644
--- a/tests/ui/macros/format-args-temporaries-in-write.stderr
+++ b/tests/ui/macros/format-args-temporaries-in-write.stderr
@@ -13,11 +13,6 @@
| -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard`
| |
| `mutex` dropped here while still borrowed
- |
-help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
- |
-LL | write!(Out, "{}", mutex.lock()); /* no semicolon */
- | +
error[E0597]: `mutex` does not live long enough
--> $DIR/format-args-temporaries-in-write.rs:47:29
@@ -34,11 +29,6 @@
| -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard`
| |
| `mutex` dropped here while still borrowed
- |
-help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
- |
-LL | writeln!(Out, "{}", mutex.lock()); /* no semicolon */
- | +
error: aborting due to 2 previous errors
diff --git a/tests/ui/repr/repr.stderr b/tests/ui/repr/repr.stderr
index f3b1139..9e58133 100644
--- a/tests/ui/repr/repr.stderr
+++ b/tests/ui/repr/repr.stderr
@@ -5,7 +5,7 @@
| ^^^^^^^
| |
| expected this to be a list
- | help: must be of the form: `#[repr(C)]`
+ | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
error[E0539]: malformed `repr` attribute input
--> $DIR/repr.rs:4:1
@@ -14,7 +14,7 @@
| ^^^^^^^^^^^^^
| |
| expected this to be a list
- | help: must be of the form: `#[repr(C)]`
+ | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
error[E0539]: malformed `repr` attribute input
--> $DIR/repr.rs:7:1
@@ -23,7 +23,7 @@
| ^^^^^^^^^^^^^
| |
| expected this to be a list
- | help: must be of the form: `#[repr(C)]`
+ | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
error: aborting due to 3 previous errors
diff --git a/tests/ui/sized/unsized-binding.rs b/tests/ui/sized/unsized-binding.rs
index 3b99b0f..ce6c152 100644
--- a/tests/ui/sized/unsized-binding.rs
+++ b/tests/ui/sized/unsized-binding.rs
@@ -1,5 +1,5 @@
fn main() {
let x = *""; //~ ERROR E0277
- println!("{}", x);
- println!("{}", x);
+ drop(x);
+ drop(x);
}
diff --git a/tests/ui/statics/read_before_init.rs b/tests/ui/statics/read_before_init.rs
new file mode 100644
index 0000000..d779ef6
--- /dev/null
+++ b/tests/ui/statics/read_before_init.rs
@@ -0,0 +1,22 @@
+//! This test checks the one code path that does not go through
+//! the regular CTFE memory access (as an optimization). We forgot
+//! to duplicate the static item self-initialization check, allowing
+//! reading from the uninitialized static memory before it was
+//! initialized at the end of the static initializer.
+//!
+//! https://github.com/rust-lang/rust/issues/142532
+
+use std::mem::MaybeUninit;
+
+pub static X: (i32, MaybeUninit<i32>) = (1, foo(&X.0));
+//~^ ERROR: encountered static that tried to initialize itself with itself
+
+const fn foo(x: &i32) -> MaybeUninit<i32> {
+ let mut temp = MaybeUninit::<i32>::uninit();
+ unsafe {
+ std::ptr::copy(x, temp.as_mut_ptr(), 1);
+ }
+ temp
+}
+
+fn main() {}
diff --git a/tests/ui/statics/read_before_init.stderr b/tests/ui/statics/read_before_init.stderr
new file mode 100644
index 0000000..aeebcf7
--- /dev/null
+++ b/tests/ui/statics/read_before_init.stderr
@@ -0,0 +1,17 @@
+error[E0080]: encountered static that tried to initialize itself with itself
+ --> $DIR/read_before_init.rs:11:45
+ |
+LL | pub static X: (i32, MaybeUninit<i32>) = (1, foo(&X.0));
+ | ^^^^^^^^^ evaluation of `X` failed inside this call
+ |
+note: inside `foo`
+ --> $DIR/read_before_init.rs:17:9
+ |
+LL | std::ptr::copy(x, temp.as_mut_ptr(), 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `std::ptr::copy::<i32>`
+ --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/suggestions/issue-97760.stderr b/tests/ui/suggestions/issue-97760.stderr
index c3cf7e1..ddd143b 100644
--- a/tests/ui/suggestions/issue-97760.stderr
+++ b/tests/ui/suggestions/issue-97760.stderr
@@ -1,8 +1,8 @@
error[E0277]: `<impl IntoIterator as IntoIterator>::Item` doesn't implement `std::fmt::Display`
- --> $DIR/issue-97760.rs:4:19
+ --> $DIR/issue-97760.rs:4:20
|
LL | println!("{x}");
- | ^^^ `<impl IntoIterator as IntoIterator>::Item` cannot be formatted with the default formatter
+ | ^ `<impl IntoIterator as IntoIterator>::Item` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `<impl IntoIterator as IntoIterator>::Item`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr
index 2dca0c2..0b2d71f 100644
--- a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr
+++ b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr
@@ -7,13 +7,5 @@
|
= note: this feature is not stably supported; its behavior can change in the future
-warning: unstable feature specified for `-Ctarget-feature`: `f`
- |
- = note: this feature is not stably supported; its behavior can change in the future
-
-warning: unstable feature specified for `-Ctarget-feature`: `zicsr`
- |
- = note: this feature is not stably supported; its behavior can change in the future
-
-warning: 4 warnings emitted
+warning: 2 warnings emitted
diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs
index 302ccec..1006b07 100644
--- a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs
+++ b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs
@@ -24,5 +24,3 @@ pub trait Freeze {}
//~? WARN must be disabled to ensure that the ABI of the current target can be implemented correctly
//~? WARN unstable feature specified for `-Ctarget-feature`
-//[riscv]~? WARN unstable feature specified for `-Ctarget-feature`
-//[riscv]~? WARN unstable feature specified for `-Ctarget-feature`
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr
index 2a0f5f0..79e8982 100644
--- a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr
@@ -1,4 +1,4 @@
-warning: target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `retpoline-external-thunk` target modifier flag instead
+warning: target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `-Zretpoline-external-thunk` compiler flag instead
|
= note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr
index f7b6cb1..f5ff15d 100644
--- a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr
@@ -1,4 +1,4 @@
-warning: target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
+warning: target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `-Zretpoline` compiler flag instead
|
= note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr
index 4f2cd1d..158cca0 100644
--- a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr
@@ -1,4 +1,4 @@
-warning: target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
+warning: target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `-Zretpoline` compiler flag instead
|
= note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.rs b/tests/ui/target-feature/retpoline-target-feature-flag.rs
index de3c44c..05c8586 100644
--- a/tests/ui/target-feature/retpoline-target-feature-flag.rs
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.rs
@@ -16,6 +16,6 @@
#![no_core]
extern crate minicore;
-//[by_feature1]~? WARN target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `retpoline-external-thunk` target modifier flag instead
-//[by_feature2]~? WARN target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
-//[by_feature3]~? WARN target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
+//[by_feature1]~? WARN target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`
+//[by_feature2]~? WARN target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`
+//[by_feature3]~? WARN target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`
diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout
index c20f123..5d6e390 100644
--- a/tests/ui/unpretty/exhaustive.hir.stdout
+++ b/tests/ui/unpretty/exhaustive.hir.stdout
@@ -405,8 +405,10 @@
fn expr_format_args() {
let expr;
format_arguments::new_const(&[]);
- format_arguments::new_v1(&[""],
- &[format_argument::new_display(&expr)]);
+ {
+ super let args = [format_argument::new_display(&expr)];
+ format_arguments::new_v1(&[""], &args)
+ };
}
}
mod items {
diff --git a/tests/ui/unpretty/flattened-format-args.stdout b/tests/ui/unpretty/flattened-format-args.stdout
index a5d9432..4af8292 100644
--- a/tests/ui/unpretty/flattened-format-args.stdout
+++ b/tests/ui/unpretty/flattened-format-args.stdout
@@ -10,7 +10,9 @@
let x = 1;
// Should flatten to println!("a 123 b {x} xyz\n"):
{
- ::std::io::_print(format_arguments::new_v1(&["a 123 b ", " xyz\n"],
- &[format_argument::new_display(&x)]));
+ ::std::io::_print({
+ super let args = [format_argument::new_display(&x)];
+ format_arguments::new_v1(&["a 123 b ", " xyz\n"], &args)
+ });
};
}