Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3277a9c
rustc_target: Use +spe for powerpcspe targets
taiki-e Sep 21, 2025
a5b1187
Region values: use an enum as opposed to two `Option`s
amandasystems May 27, 2026
936f0ce
Restore simpler Encode/Decode impls for u32 and (on 64bit systems) usize
bjorn3 Jun 2, 2026
381cea9
fix
bb1yd Jun 1, 2026
b5acf95
Render `impl` restriction
CoCo-Japan-pan Jun 2, 2026
5358c2f
Add tests for `impl` restriction rendering
CoCo-Japan-pan Jun 2, 2026
49f80b3
Remove unnecessary arm
mu001999 Jun 2, 2026
5521a56
Remove `skip_arg` attribute from `Diagnostic` and `Subdiagnostic` pro…
GuillaumeGomez May 28, 2026
551940e
Remove `#[skip_arg]` references from rustc-dev-guide
GuillaumeGomez May 28, 2026
579831c
Fix compilation error because of `potential_query_instability` in `di…
GuillaumeGomez Jun 2, 2026
62fbbec
Add test for undefined EII static error
bjorn3 Jun 2, 2026
87a08b1
Integrate target checking for #[repr] into its attribute parser
JonathanBrouwer Jun 2, 2026
b40c43d
Add specific arguments to the diagnostic message
JonathanBrouwer Jun 2, 2026
a78d218
Re-add hints for `rustc_align`
JonathanBrouwer May 31, 2026
80d33c7
Add debug assertion to ensure target checking is not missed
JonathanBrouwer May 31, 2026
79e666b
Add regression test for repr on mac call
JonathanBrouwer May 31, 2026
466699e
Rollup merge of #157035 - amandasystems:region-values-either, r=jackh726
JonathanBrouwer Jun 2, 2026
9ca6103
Rollup merge of #157182 - bjorn3:fix_perf_regression, r=petrochenkov
JonathanBrouwer Jun 2, 2026
c93a807
Rollup merge of #157310 - CoCo-Japan-pan:render-impl-restriction, r=G…
JonathanBrouwer Jun 2, 2026
a703f75
Rollup merge of #157070 - GuillaumeGomez:rm-skip_arg, r=JonathanBrouwer
JonathanBrouwer Jun 2, 2026
25a125d
Rollup merge of #157137 - taiki-e:powerpcspe, r=RalfJung
JonathanBrouwer Jun 2, 2026
22ea858
Rollup merge of #157215 - JonathanBrouwer:repr-target-checking3, r=mejrs
JonathanBrouwer Jun 2, 2026
1aabc75
Rollup merge of #157235 - bb1yd:improve-colon-suggestion, r=mejrs
JonathanBrouwer Jun 2, 2026
7f2e2bd
Rollup merge of #157321 - mu001999-contrib:nit, r=JonathanBrouwer
JonathanBrouwer Jun 2, 2026
51c415a
Rollup merge of #157324 - bjorn3:eii_error_test, r=jdonszelmann
JonathanBrouwer Jun 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 107 additions & 28 deletions compiler/rustc_attr_parsing/src/attributes/repr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ impl CombineAttributeParser for ReprParser {
};

if list.is_empty() {
cx.check_target(
"()",
&AllowedTargets::AllowList(&[
Allow(Target::Struct),
Allow(Target::Enum),
Allow(Target::Union),
Warn(Target::MacroCall),
]),
);

let attr_span = cx.attr_span;
cx.adcx().warn_empty_attribute(attr_span);
return vec![];
Expand All @@ -53,51 +63,120 @@ impl CombineAttributeParser for ReprParser {
reprs
}

//FIXME Still checked fully in `check_attr.rs`
//This one is slightly more complicated because the allowed targets depend on the arguments
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::ManuallyChecked;
const STABILITY: AttributeStability = AttributeStability::Stable;
}

fn parse_repr(cx: &mut AcceptContext<'_, '_>, param: &MetaItemParser) -> Option<ReprAttr> {
use ReprAttr::*;

macro_rules! no_args {
($constructor: expr) => {{
macro_rules! repr_int {
($arg: ident, $constructor: expr) => {{
cx.check_target(
concat!("(", stringify!($arg), ")"),
&AllowedTargets::AllowList(&[Allow(Target::Enum), Warn(Target::MacroCall)]),
);
cx.expect_no_args(param.args())?;
Some($constructor)
}};
}

match param.path().word_sym() {
Some(sym::align) => {
cx.check_target(
"(align(...))",
&AllowedTargets::AllowList(&[
Allow(Target::Struct),
Allow(Target::Enum),
Allow(Target::Union),
Warn(Target::MacroCall),
]),
);
let l = cx.expect_list(param.args(), param.span())?;
parse_repr_align(cx, l, AlignKind::Align)
}
Some(sym::packed) => match param.args() {
ArgParser::NoArgs => Some(ReprPacked(Align::ONE)),
ArgParser::List(l) => parse_repr_align(cx, l, AlignKind::Packed),
ArgParser::NameValue(_) => {
cx.adcx().expected_list_or_no_args(param.span());
None
Some(sym::packed) => {
cx.check_target(
"(packed)",
&AllowedTargets::AllowList(&[
Allow(Target::Struct),
Allow(Target::Union),
Warn(Target::MacroCall),
]),
);
match param.args() {
ArgParser::NoArgs => Some(ReprPacked(Align::ONE)),
ArgParser::List(l) => parse_repr_align(cx, l, AlignKind::Packed),
ArgParser::NameValue(_) => {
cx.adcx().expected_list_or_no_args(param.span());
None
}
}
},
Some(sym::Rust) => no_args!(ReprRust),
Some(sym::C) => no_args!(ReprC),
Some(sym::simd) => no_args!(ReprSimd),
Some(sym::transparent) => no_args!(ReprTransparent),
Some(sym::i8) => no_args!(ReprInt(SignedInt(IntTy::I8))),
Some(sym::u8) => no_args!(ReprInt(UnsignedInt(UintTy::U8))),
Some(sym::i16) => no_args!(ReprInt(SignedInt(IntTy::I16))),
Some(sym::u16) => no_args!(ReprInt(UnsignedInt(UintTy::U16))),
Some(sym::i32) => no_args!(ReprInt(SignedInt(IntTy::I32))),
Some(sym::u32) => no_args!(ReprInt(UnsignedInt(UintTy::U32))),
Some(sym::i64) => no_args!(ReprInt(SignedInt(IntTy::I64))),
Some(sym::u64) => no_args!(ReprInt(UnsignedInt(UintTy::U64))),
Some(sym::i128) => no_args!(ReprInt(SignedInt(IntTy::I128))),
Some(sym::u128) => no_args!(ReprInt(UnsignedInt(UintTy::U128))),
Some(sym::isize) => no_args!(ReprInt(SignedInt(IntTy::Isize))),
Some(sym::usize) => no_args!(ReprInt(UnsignedInt(UintTy::Usize))),
}

Some(sym::Rust) => {
cx.check_target(
"(Rust)",
&AllowedTargets::AllowList(&[
Allow(Target::Struct),
Allow(Target::Enum),
Allow(Target::Union),
Warn(Target::MacroCall),
]),
);
cx.expect_no_args(param.args())?;
Some(ReprRust)
}
Some(sym::C) => {
cx.check_target(
"(C)",
&AllowedTargets::AllowList(&[
Allow(Target::Struct),
Allow(Target::Enum),
Allow(Target::Union),
Warn(Target::MacroCall),
]),
);
cx.expect_no_args(param.args())?;
Some(ReprC)
}
Some(sym::simd) => {
cx.check_target(
"(simd)",
&AllowedTargets::AllowList(&[
Allow(Target::Struct), // Feature gated in `rustc_ast_passes`
Warn(Target::MacroCall), // FIXME: This is not feature gated (!!)
]),
);
cx.expect_no_args(param.args())?;
Some(ReprSimd)
}
Some(sym::transparent) => {
cx.check_target(
"(transparent)",
&AllowedTargets::AllowList(&[
Allow(Target::Struct),
Allow(Target::Enum),
Allow(Target::Union), // Feature gated in `rustc_hir_analysis`
Warn(Target::MacroCall),
]),
);
cx.expect_no_args(param.args())?;
Some(ReprTransparent)
}

Some(sym::i8) => repr_int!(i8, ReprInt(SignedInt(IntTy::I8))),
Some(sym::u8) => repr_int!(u8, ReprInt(UnsignedInt(UintTy::U8))),
Some(sym::i16) => repr_int!(i16, ReprInt(SignedInt(IntTy::I16))),
Some(sym::u16) => repr_int!(u16, ReprInt(UnsignedInt(UintTy::U16))),
Some(sym::i32) => repr_int!(i32, ReprInt(SignedInt(IntTy::I32))),
Some(sym::u32) => repr_int!(u32, ReprInt(UnsignedInt(UintTy::U32))),
Some(sym::i64) => repr_int!(i64, ReprInt(SignedInt(IntTy::I64))),
Some(sym::u64) => repr_int!(u64, ReprInt(UnsignedInt(UintTy::U64))),
Some(sym::i128) => repr_int!(i128, ReprInt(SignedInt(IntTy::I128))),
Some(sym::u128) => repr_int!(u128, ReprInt(UnsignedInt(UintTy::U128))),
Some(sym::isize) => repr_int!(isize, ReprInt(SignedInt(IntTy::Isize))),
Some(sym::usize) => repr_int!(usize, ReprInt(UnsignedInt(UintTy::Usize))),
_ => {
cx.adcx().expected_specific_argument(
param.span(),
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,10 @@ pub struct AcceptContext<'f, 'sess> {

/// The name of the attribute we're currently accepting.
pub(crate) attr_path: AttrPath,

/// Used for `AllowedTargets::ManuallyChecked`, to assert that the manual target check has been done
#[cfg(debug_assertions)]
pub(crate) has_target_been_checked: bool,
}

impl<'f, 'sess: 'f> SharedContext<'f, 'sess> {
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_attr_parsing/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ impl<'sess> AttributeParser<'sess> {
template,
attr_safety: attr_safety.unwrap_or(Safety::Default),
attr_path,
#[cfg(debug_assertions)]
has_target_been_checked: false,
};
parse_fn(&mut cx, args)
}
Expand Down Expand Up @@ -410,12 +412,14 @@ impl<'sess> AttributeParser<'sess> {
template: &accept.template,
attr_safety: n.item.unsafety,
attr_path: attr_path.clone(),
#[cfg(debug_assertions)]
has_target_been_checked: false,
};

(accept.accept_fn)(&mut cx, &args);
finalizers.push(accept.finalizer);

Self::check_target(&accept.allowed_targets, &mut cx);
Self::check_target(&accept.allowed_targets, "", &mut cx);
#[cfg(debug_assertions)]
if !cx.shared.has_lint_been_emitted.load(Ordering::Relaxed) {
cx.shared.cx.check_args_used(&attr, &args)
Expand Down
15 changes: 13 additions & 2 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,8 +331,8 @@ pub(crate) struct EmptyConfusables {
}

#[derive(Diagnostic)]
#[help("`#[{$name}]` can {$only}be applied to {$applied}")]
#[diag("`#[{$name}]` attribute cannot be used on {$target}")]
#[help("`#[{$name}{$attribute_args}]` can {$only}be applied to {$applied}")]
#[diag("`#[{$name}{$attribute_args}]` attribute cannot be used on {$target}")]
pub(crate) struct InvalidTarget {
#[primary_span]
#[suggestion(
Expand All @@ -346,12 +346,23 @@ pub(crate) struct InvalidTarget {
pub target: &'static str,
pub applied: DiagArgValue,
pub only: &'static str,
pub attribute_args: &'static str,
#[subdiagnostic]
pub help: Option<InvalidTargetHelp>,
#[warning(
"this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!"
)]
pub previously_accepted: bool,
}

#[derive(Subdiagnostic)]
pub(crate) enum InvalidTargetHelp {
#[help("use `#[rustc_align(...)]` instead")]
UseRustcAlign,
#[help("use `#[rustc_align_static(...)]` instead")]
UseRustcAlignStatic,
}

#[derive(Diagnostic)]
#[diag("invalid alignment value: {$error_part}", code = E0589)]
pub(crate) struct InvalidAlignmentValue {
Expand Down
71 changes: 51 additions & 20 deletions compiler/rustc_attr_parsing/src/target_checking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@ use crate::context::AcceptContext;
use crate::errors::{
InvalidAttrAtCrateLevel, ItemFollowingInnerAttr, UnsupportedAttributesInWhere,
};
use crate::session_diagnostics::InvalidTarget;
use crate::session_diagnostics::{InvalidTarget, InvalidTargetHelp};
use crate::target_checking::Policy::Allow;
use crate::{AttributeParser, ShouldEmit};

#[derive(Debug)]
pub(crate) enum AllowedTargets {
AllowList(&'static [Policy]),
AllowListWarnRest(&'static [Policy]),
/// This is useful for argument-dependent target checking.
/// If debug assertions are enabled,
/// this emits a delayed bug if the `cx.check_target(...)` method is not called during attribute parsing.
ManuallyChecked,
}

pub(crate) enum AllowedResult {
Expand Down Expand Up @@ -52,13 +56,15 @@ impl AllowedTargets {
AllowedResult::Warn
}
}
AllowedTargets::ManuallyChecked => unreachable!(),
}
}

pub(crate) fn allowed_targets(&self) -> Vec<Target> {
match self {
AllowedTargets::AllowList(list) => list,
AllowedTargets::AllowListWarnRest(list) => list,
AllowedTargets::ManuallyChecked => unreachable!(),
}
.iter()
.filter_map(|target| match target {
Expand Down Expand Up @@ -89,38 +95,29 @@ pub(crate) enum Policy {
impl<'sess> AttributeParser<'sess> {
pub(crate) fn check_target(
allowed_targets: &AllowedTargets,
attribute_args: &'static str,
cx: &mut AcceptContext<'_, 'sess>,
) {
if matches!(cx.should_emit, ShouldEmit::Nothing) {
return;
}

if let AllowedTargets::ManuallyChecked = allowed_targets {
#[cfg(debug_assertions)]
if !cx.has_target_been_checked {
cx.dcx().delayed_bug("Attribute target has not been checked");
}

return;
}

// For crate-level attributes we emit a specific set of lints to warn
// people about accidentally not using them on the crate.
if let &AllowedTargets::AllowList(&[Allow(Target::Crate)]) = allowed_targets {
Self::check_crate_level(cx);
return;
}

if matches!(cx.attr_path.segments.as_ref(), [sym::repr]) && cx.target == Target::Crate {
// The allowed targets of `repr` depend on its arguments. They can't be checked using
// the `AttributeParser` code.
let span = cx.attr_span;
let item =
cx.cx.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span });

let pound_to_opening_bracket = cx.attr_span.until(cx.inner_span);

cx.dcx()
.create_err(InvalidAttrAtCrateLevel {
span,
pound_to_opening_bracket,
name: sym::repr,
item,
})
.emit();
}

let result = allowed_targets.is_allowed(cx.target);
if matches!(result, AllowedResult::Allowed) {
return;
Expand All @@ -134,6 +131,8 @@ impl<'sess> AttributeParser<'sess> {
target: cx.target.plural_name(),
only: if only { "only " } else { "" },
applied: DiagArgValue::StrListSepByAnd(applied.into_iter().map(Cow::Owned).collect()),
attribute_args,
help: Self::target_checking_help(attribute_args, cx),
previously_accepted: matches!(result, AllowedResult::Warn),
};

Expand Down Expand Up @@ -164,6 +163,24 @@ impl<'sess> AttributeParser<'sess> {
}
}

fn target_checking_help(
attribute_args: &'static str,
cx: &AcceptContext<'_, '_>,
) -> Option<InvalidTargetHelp> {
match &*cx.attr_path.segments {
[sym::repr] if attribute_args == "(align(...))" => match cx.target {
Target::Fn | Target::Method(..) if cx.features().fn_align() => {
Some(InvalidTargetHelp::UseRustcAlign)
}
Target::Static if cx.features().static_align() => {
Some(InvalidTargetHelp::UseRustcAlignStatic)
}
_ => None,
},
_ => None,
}
}

pub(crate) fn check_crate_level(cx: &mut AcceptContext<'_, 'sess>) {
if cx.target == Target::Crate {
return;
Expand Down Expand Up @@ -399,6 +416,20 @@ fn filter_targets(
added_fake_targets.push(target_group_name);
}

impl<'f, 'sess> AcceptContext<'f, 'sess> {
pub(crate) fn check_target(
&mut self,
attribute_args: &'static str,
allowed_targets: &AllowedTargets,
) {
#[cfg(debug_assertions)]
{
self.has_target_been_checked = true;
}
AttributeParser::check_target(allowed_targets, attribute_args, self);
}
}

/// This is the list of all targets to which a attribute can be applied
/// This is used for:
/// - `rustc_dummy`, which can be applied to all targets
Expand Down
Loading
Loading