Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
d85e28f
powerpc64: don't pass multi-field unions in FPRs on ELFv1
mattst88 Sep 20, 2026
a7543d0
add test
qaijuang Jul 28, 2026
5a9eb1e
preserve explicit register names in inline asm diagnostics
qaijuang Aug 4, 2026
3101cb2
powerpc64: don't pass single-field or nested unions in FPRs on ELFv1
mattst88 Sep 20, 2026
2ed1e37
Fix applicability level for cfg suggestion
chenyukang Sep 24, 2026
89c1934
Correctly check that an item is not `doc(hidden)`, including its pare…
GuillaumeGomez Sep 24, 2026
4de84d7
Correctly handle `impl` items when checking if an item parents have `…
GuillaumeGomez Sep 24, 2026
3026ffe
make SPARC `Complex` ABI GCC-compatible
folkertdev Jul 25, 2026
876f7a0
Tweak Layout::from_size_align_unchecked safety comment
ultrabear Sep 24, 2026
980e0f0
moving div_ceil() to associated items macro
Eleocraft Sep 25, 2026
a88ba94
Rollup merge of #160068 - qaijuang:fix-asm-register-alias-diagnostic,…
jhpratt Sep 25, 2026
c683145
Rollup merge of #163239 - chenyukang:yukang-fix-cfg-tail-expression-s…
jhpratt Sep 25, 2026
c4274f2
Rollup merge of #163025 - folkertdev:complex-sparc, r=beetrees
jhpratt Sep 25, 2026
b48bdb1
Rollup merge of #163076 - mattst88:powerpc64-elfv1-union-abi, r=folke…
jhpratt Sep 25, 2026
3246802
Rollup merge of #163268 - GuillaumeGomez:jump-to-def-hidden, r=Urgau
jhpratt Sep 25, 2026
a8c7176
Rollup merge of #163288 - ultrabear:ultrabear/layout-from-size-align,…
jhpratt Sep 25, 2026
d7f0af0
Rollup merge of #163292 - Eleocraft:move-div-ceil, r=clarfonthey
jhpratt Sep 25, 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
4 changes: 4 additions & 0 deletions compiler/rustc_abi/src/callconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
///
/// Note: We generally ignore 1-ZST fields when computing this value (see #56877).
///
/// Note: The fields of a union are merged as if it were a struct, so the result does not
/// distinguish the two. `powerpc64::is_or_contains_union` in `rustc_target` walks the layout
/// again to do that, mirroring the array handling here.
///
/// This is public so that it can be used in unit tests, but
/// should generally only be relevant to the ABI details of
/// specific targets.
Expand Down
43 changes: 22 additions & 21 deletions compiler/rustc_ast_lowering/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
.iter()
.map(|(op, op_sp)| {
let lower_reg = |&reg: &_| match reg {
InlineAsmRegOrRegClass::Reg(reg) => {
asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch {
InlineAsmRegOrRegClass::Reg(reg) => hir::InlineAsmRegOrRegClass::Reg {
reg: if let Some(asm_arch) = asm_arch {
asm::InlineAsmReg::parse(asm_arch, reg).unwrap_or_else(|error| {
self.dcx().emit_err(InvalidRegister {
op_span: *op_sp,
Expand All @@ -152,10 +152,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
})
} else {
asm::InlineAsmReg::Err
})
}
},
source_name: Some(reg),
},
InlineAsmRegOrRegClass::RegClass(reg_class) => {
asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch {
hir::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch {
asm::InlineAsmRegClass::parse(asm_arch, reg_class).unwrap_or_else(
|supported_register_classes| {
self.dcx().emit_err(InvalidRegisterClass {
Expand Down Expand Up @@ -344,7 +345,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}

// Check for conflicts between explicit register operands.
if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg {
if let hir::InlineAsmRegOrRegClass::Reg { reg, .. } = reg {
let (input, output) = match op {
hir::InlineAsmOperand::In { .. } => (true, false),

Expand Down Expand Up @@ -393,24 +394,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
_ => None,
};
let reg_str = |idx| -> &str {
// HIR asm doesn't preserve the original alias string of the explicit register,
// so we have to retrieve it from AST
let (op, _): &(InlineAsmOperand, Span) = &asm.operands[idx];
if let Some(ast::InlineAsmRegOrRegClass::Reg(reg_sym)) =
op.reg()
{
reg_sym.as_str()
} else {
unreachable!("{op:?} is not a register operand");
}
};
let reg1_name = op
.reg()
.and_then(|reg| reg.source_name())
.expect("{op:?} has no source register name");

let reg2_name = op2
.reg()
.and_then(|reg| reg.source_name())
.expect("{op2:?} has no source register name");

self.dcx().emit_err(RegisterConflict {
op_span1: op_sp,
op_span2: op_sp2,
reg1_name: reg_str(idx),
reg2_name: reg_str(idx2),
reg1_name: reg1_name.as_str(),
reg2_name: reg2_name.as_str(),
in_out,
});
}
Expand Down Expand Up @@ -457,7 +455,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
if !output_used {
operands.push((
hir::InlineAsmOperand::Out {
reg: asm::InlineAsmRegOrRegClass::Reg(clobber),
reg: hir::InlineAsmRegOrRegClass::Reg {
reg: clobber,
source_name: None,
},
late: true,
expr: None,
},
Expand Down
42 changes: 40 additions & 2 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use rustc_span::{
BytePos, DUMMY_SP, DesugaringKind, ErrorGuaranteed, Ident, LocalExpnId, Span, Spanned, Symbol,
kw, sym,
};
use rustc_target::asm::InlineAsmRegOrRegClass;
use rustc_target::asm;
use tracing::debug;

use crate::def::{CtorKind, DefKind, MacroKinds, PerNS, Res};
Expand Down Expand Up @@ -3611,6 +3611,44 @@ pub enum TyKind<'hir, Unambig = ()> {
Infer(Unambig),
}

/// Stores explicit register name from source
/// for diagnostics only.
#[derive(Debug, Clone, Copy, StableHash)]
pub enum InlineAsmRegOrRegClass {
Reg { reg: asm::InlineAsmReg, source_name: Option<Symbol> },
RegClass(asm::InlineAsmRegClass),
}

impl InlineAsmRegOrRegClass {
// For `rustc_mir_build` and `clippy_utils`
pub fn as_target(self) -> asm::InlineAsmRegOrRegClass {
match self {
Self::Reg { reg, .. } => asm::InlineAsmRegOrRegClass::Reg(reg),
Self::RegClass(reg_class) => asm::InlineAsmRegOrRegClass::RegClass(reg_class),
}
}

// For `rustc_ast_lowering`
pub fn reg_class(self) -> asm::InlineAsmRegClass {
self.as_target().reg_class()
}

// For `rustc_ast_lowering`
pub fn source_name(self) -> Option<Symbol> {
match self {
Self::Reg { source_name, .. } => source_name,
Self::RegClass(_) => None,
}
}
}

// For `rustc_hir_pretty`
impl fmt::Display for InlineAsmRegOrRegClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_target().fmt(f)
}
}

#[derive(Debug, Clone, Copy, StableHash)]
pub enum InlineAsmOperand<'hir> {
In {
Expand Down Expand Up @@ -3665,7 +3703,7 @@ impl<'hir> InlineAsmOperand<'hir> {
pub fn is_clobber(&self) -> bool {
matches!(
self,
InlineAsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(_), late: _, expr: None }
InlineAsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg { .. }, expr: None, .. }
)
}
}
Expand Down
13 changes: 8 additions & 5 deletions compiler/rustc_hir_typeck/src/inline_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ use rustc_middle::ty::{
use rustc_span::def_id::LocalDefId;
use rustc_span::{ErrorGuaranteed, Span, Symbol, bug, sym};
use rustc_target::asm::{
InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmSize, InlineAsmType,
ModifierInfo,
InlineAsmReg, InlineAsmRegClass, InlineAsmSize, InlineAsmType, ModifierInfo,
};
use rustc_trait_selection::infer::InferCtxtExt;

Expand Down Expand Up @@ -191,7 +190,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
fn check_asm_operand_type(
&self,
idx: usize,
reg: InlineAsmRegOrRegClass,
reg: hir::InlineAsmRegOrRegClass,
expr: &'tcx hir::Expr<'tcx>,
template: &[InlineAsmTemplatePiece],
is_input: bool,
Expand Down Expand Up @@ -477,7 +476,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
if let Some(reg) = op.reg() {
// Some explicit registers cannot be used depending on the
// target. Reject those here.
if let InlineAsmRegOrRegClass::Reg(reg) = reg {
if let hir::InlineAsmRegOrRegClass::Reg { reg, source_name } = reg {
if let InlineAsmReg::Err = reg {
// `validate` will panic on `Err`, as an error must
// already have been reported.
Expand All @@ -490,7 +489,11 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
&self.tcx().sess.target,
op.is_clobber(),
) {
let msg = format!("cannot use register `{}`: {}", reg.name(), msg);
let reg_name = match source_name {
Some(ref reg_name) => reg_name.as_str(),
None => &reg.name(),
};
let msg = format!("cannot use register `{reg_name}`: {msg}");
self.fcx.dcx().span_err(op_sp, msg);
continue;
}
Expand Down
17 changes: 11 additions & 6 deletions compiler/rustc_mir_build/src/thir/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -827,22 +827,27 @@ impl<'tcx> ThirBuildCx<'tcx> {
.operands
.iter()
.map(|(op, _op_sp)| match *op {
hir::InlineAsmOperand::In { reg, expr } => {
InlineAsmOperand::In { reg, expr: self.mirror_expr(expr) }
}
hir::InlineAsmOperand::In { reg, expr } => InlineAsmOperand::In {
reg: reg.as_target(),
expr: self.mirror_expr(expr),
},
hir::InlineAsmOperand::Out { reg, late, ref expr } => {
InlineAsmOperand::Out {
reg,
reg: reg.as_target(),
late,
expr: expr.map(|expr| self.mirror_expr(expr)),
}
}
hir::InlineAsmOperand::InOut { reg, late, expr } => {
InlineAsmOperand::InOut { reg, late, expr: self.mirror_expr(expr) }
InlineAsmOperand::InOut {
reg: reg.as_target(),
late,
expr: self.mirror_expr(expr),
}
}
hir::InlineAsmOperand::SplitInOut { reg, late, in_expr, ref out_expr } => {
InlineAsmOperand::SplitInOut {
reg,
reg: reg.as_target(),
late,
in_expr: self.mirror_expr(in_expr),
out_expr: out_expr.map(|expr| self.mirror_expr(expr)),
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_parse/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1656,8 +1656,8 @@ pub(crate) enum ExpectedSemiSugg {
style = "short"
)]
ChangeToSemi(#[primary_span] Span),
#[suggestion("add `;` here", code = ";", applicability = "machine-applicable", style = "short")]
AddSemi(#[primary_span] Span),
#[suggestion("add `;` here", code = ";", style = "short")]
AddSemi(#[primary_span] Span, #[applicability] Applicability),
}

#[derive(Diagnostic)]
Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ impl<'a> Parser<'a> {
span,
token: self.token,
unexpected_token_label: Some(self.token.span),
sugg: ExpectedSemiSugg::AddSemi(span),
sugg: ExpectedSemiSugg::AddSemi(span, Applicability::MachineApplicable),
});
return Ok(guar);
}
Expand Down Expand Up @@ -723,7 +723,8 @@ impl<'a> Parser<'a> {
span,
token: self.token,
unexpected_token_label: Some(self.token.span),
sugg: ExpectedSemiSugg::AddSemi(span),
// A semicolon may discard the intended return value of a cfg-gated tail expression.
sugg: ExpectedSemiSugg::AddSemi(span, Applicability::MaybeIncorrect),
});
let attr_span = match &expr.attrs[..] {
[] => unreachable!(),
Expand Down Expand Up @@ -755,7 +756,7 @@ impl<'a> Parser<'a> {
(expr.span.shrink_to_lo(), "{ ".to_string()),
(expr.span.shrink_to_hi(), " }".to_string()),
],
Applicability::MachineApplicable,
Applicability::MaybeIncorrect,
);

// Special handling for `#[cfg(...)]` chains
Expand Down Expand Up @@ -811,7 +812,8 @@ impl<'a> Parser<'a> {
"it seems like you are trying to provide different expressions depending on \
`cfg`, consider using `if cfg!(..)`",
sugg,
Applicability::MachineApplicable,
// Unlike `#[cfg]`, `if cfg!` still checks disabled branches.
Applicability::MaybeIncorrect,
);
}
}
Expand Down
40 changes: 38 additions & 2 deletions compiler/rustc_target/src/callconv/powerpc64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Alignment of 128 bit types is not currently handled, this will
// need to be fixed when PowerPC vector support is added.

use rustc_abi::{HasDataLayout, Integer, Numeric, TyAbiInterface};
use rustc_abi::{FieldsShape, HasDataLayout, Integer, Numeric, TyAbiInterface, TyAndLayout};

use crate::callconv::{Align, ArgAbi, CastTarget, FnAbi, Reg, RegKind, Uniform};
use crate::spec::{HasTargetSpec, LlvmAbi, Os};
Expand All @@ -15,6 +15,41 @@ enum ABI {
}
use ABI::*;

/// Whether `layout` is or contains a union.
///
/// `homogeneous_aggregate` merges the fields of a union, so it cannot tell a union from a
/// structure. This walks the layout a second time; keep the array handling here in sync with
/// `homogeneous_aggregate`. ZSTs are ignored entirely, as both GCC and Clang do.
///
/// This does not look at enums that are represented as unions at the ABI level (e.g. a
/// `#[repr(C)]` enum with fields). That does not matter here: enums can only have integer
/// discriminants, and `is_homogeneous_aggregate` rejects anything containing an integer, so an
/// enum can never be a float or vector homogeneous aggregate.
fn is_or_contains_union<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout,
{
if layout.is_zst() {
return false;
}
match layout.fields {
FieldsShape::Primitive => false,
// A `repr(transparent)` union is guaranteed to be ABI-compatible with its single
// non-1-ZST field, so look through it instead of rejecting it.
FieldsShape::Union(_) => {
match layout.non_1zst_field(cx).filter(|_| layout.is_transparent()) {
Some((_, field)) => is_or_contains_union(cx, field),
None => true,
}
}
FieldsShape::Array { .. } => is_or_contains_union(cx, layout.field(cx, 0)),
FieldsShape::Arbitrary { .. } => {
(0..layout.fields.count()).any(|i| is_or_contains_union(cx, layout.field(cx, i)))
}
}
}

fn is_homogeneous_aggregate<'a, Ty, C>(
cx: &C,
arg: &mut ArgAbi<'a, Ty>,
Expand All @@ -27,7 +62,8 @@ where
arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
// ELFv1 and AIX only passes one-member aggregates transparently.
// ELFv2 passes up to eight uniquely addressable members.
if ((abi == ELFv1 || abi == AIX) && arg.layout.size > unit.size)
if ((abi == ELFv1 || abi == AIX)
&& (arg.layout.size > unit.size || is_or_contains_union(cx, arg.layout)))
|| arg.layout.size > unit.size.checked_mul(8, cx).unwrap()
{
return None;
Expand Down
Loading
Loading