diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs index 4fda4735b613c..1bab82667232b 100644 --- a/compiler/rustc_abi/src/callconv.rs +++ b/compiler/rustc_abi/src/callconv.rs @@ -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. diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index fd3a00d56fe0b..71bcdb0e27e8f 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -140,8 +140,8 @@ impl<'hir> LoweringContext<'_, 'hir> { .iter() .map(|(op, op_sp)| { let lower_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, @@ -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 { @@ -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), @@ -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, }); } @@ -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, }, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 96af15b29b807..a613b992367e2 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -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}; @@ -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 }, + 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 { + 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 { @@ -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, .. } ) } } diff --git a/compiler/rustc_hir_typeck/src/inline_asm.rs b/compiler/rustc_hir_typeck/src/inline_asm.rs index 52f3957d6f512..04613cc33e54d 100644 --- a/compiler/rustc_hir_typeck/src/inline_asm.rs +++ b/compiler/rustc_hir_typeck/src/inline_asm.rs @@ -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; @@ -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, @@ -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. @@ -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 => ®.name(), + }; + let msg = format!("cannot use register `{reg_name}`: {msg}"); self.fcx.dcx().span_err(op_sp, msg); continue; } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index feef8d24bd9ca..5bad7bec531c9 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -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)), diff --git a/compiler/rustc_parse/src/diagnostics.rs b/compiler/rustc_parse/src/diagnostics.rs index c080fc9e95df7..54d91eca01327 100644 --- a/compiler/rustc_parse/src/diagnostics.rs +++ b/compiler/rustc_parse/src/diagnostics.rs @@ -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)] diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 0630a65e56cb4..bb8be3c9c509f 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -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); } @@ -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!(), @@ -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 @@ -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, ); } } diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index d4617d8e7d693..ca79a52d2874f 100644 --- a/compiler/rustc_target/src/callconv/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs @@ -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}; @@ -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>, @@ -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; diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index dc4d4a7f51225..d367efaf746f0 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -1,20 +1,42 @@ -use rustc_abi::{BackendRepr, Float, HasDataLayout, Primitive, TyAbiInterface}; +use rustc_abi::{ + BackendRepr, Float, HasDataLayout, Integer, Numeric, Primitive, RegKind, TyAbiInterface, +}; -use crate::callconv::{ArgAbi, FnAbi}; +use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Reg}; -fn classify<'a, Ty>(arg: &mut ArgAbi<'a, Ty>) { - if arg.layout.is_aggregate() { - arg.make_indirect(); - } else if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr +fn classify_shared<'a, Ty>(val: &mut ArgAbi<'a, Ty>) { + if val.layout.is_aggregate() { + val.make_indirect(); + } else if let BackendRepr::Scalar(scalar) = val.layout.backend_repr && scalar.primitive() == Primitive::Float(Float::F128) { - // Always pass f128 indirectly. - arg.make_indirect(); + // f128 is passed and returned indirectly. + val.make_indirect(); } else { - arg.extend_integer_width_to(32); + val.extend_integer_width_to(32); } } +fn classify_complex_ret<'a, Ty>(ret: &mut ArgAbi<'a, Ty>, component: Numeric) { + let reg = Reg { kind: component.reg_kind(), size: component.size() }; + let mut cast = CastTarget::pair(reg, reg); + + match component { + Numeric::Float(Float::F128) => { + // Mark `_Complex long double` as inreg to get the right behavior, + // consistent with clang `SparcV8ABIInfo::classifyReturnType`. + cast.attrs.set(ArgAttribute::InReg); + } + Numeric::Int(Integer::I8 | Integer::I16 | Integer::I32, _) => { + let size = ret.layout.size; + cast = CastTarget::from(Reg { kind: RegKind::Integer, size }); + } + _ => { /* default behavior */ } + } + + ret.cast_to(cast); +} + fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, @@ -29,7 +51,16 @@ where return; } - classify(arg); + if let Some(component) = arg.layout.complex_number(cx) { + if let Numeric::Int(Integer::I8 | Integer::I16 | Integer::I32, _) = component { + arg.cast_to(Reg { kind: RegKind::Integer, size: 2 * component.size() }); + } else { + arg.make_indirect(); + } + return; + } + + classify_shared(arg) } pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) @@ -38,7 +69,11 @@ where C: HasDataLayout, { if !fn_abi.ret.is_ignore() { - classify(&mut fn_abi.ret); + if let Some(component) = fn_abi.ret.layout.complex_number(cx) { + classify_complex_ret(&mut fn_abi.ret, component); + } else { + classify_shared(&mut fn_abi.ret); + }; } for arg in fn_abi.args.iter_mut() { diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index edf266174bd4e..0ae297ad294e4 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -122,7 +122,7 @@ impl Layout { /// /// # Safety /// - /// This function is unsafe as it does not verify the preconditions from + /// The arguments passed must meet the preconditions from /// [`Layout::from_size_align`]. #[stable(feature = "alloc_layout", since = "1.28.0")] #[rustc_const_stable(feature = "const_alloc_layout_unchecked", since = "1.36.0")] diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 595f3d2248190..e12e8c7dffc43 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1573,38 +1573,6 @@ macro_rules! nonzero_integer_signedness_dependent_impls { *self = *self % other; } } - - impl NonZero<$Int> { - /// Calculates the quotient of `self` and `rhs`, rounding the result towards positive infinity. - /// - /// The result is guaranteed to be non-zero. - /// - /// # Examples - /// - /// ``` - /// # use std::num::NonZero; - #[doc = concat!("let one = NonZero::new(1", stringify!($Int), ").unwrap();")] - #[doc = concat!("let max = NonZero::new(", stringify!($Int), "::MAX).unwrap();")] - /// assert_eq!(one.div_ceil(max), one); - /// - #[doc = concat!("let two = NonZero::new(2", stringify!($Int), ").unwrap();")] - #[doc = concat!("let three = NonZero::new(3", stringify!($Int), ").unwrap();")] - /// assert_eq!(three.div_ceil(two), two); - /// ``` - #[stable(feature = "unsigned_nonzero_div_ceil", since = "1.92.0")] - #[rustc_const_stable(feature = "unsigned_nonzero_div_ceil", since = "1.92.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn div_ceil(self, rhs: Self) -> Self { - // An implementation of the function without calculating the remainder. - // It is better than the implementation for normal integers, but it can only - // be used here because of the possibility to subtract by one without overflow. - let v = (self.get() - 1) / rhs.get() + 1; - // SAFETY: ceiled division of two positive integers can never be zero. - unsafe { Self::new_unchecked(v) } - } - } }; // Impls for signed nonzero types only. (signed $Int:ty) => { @@ -1772,6 +1740,36 @@ macro_rules! nonzero_integer_signedness_dependent_methods { unsafe { Self::new_unchecked(self.get().unchecked_add(other)) } } + /// Calculates the quotient of `self` and `rhs`, rounding the result towards positive infinity. + /// + /// The result is guaranteed to be non-zero. + /// + /// # Examples + /// + /// ``` + /// # use std::num::NonZero; + #[doc = concat!("let one = NonZero::new(1", stringify!($Int), ").unwrap();")] + #[doc = concat!("let max = NonZero::new(", stringify!($Int), "::MAX).unwrap();")] + /// assert_eq!(one.div_ceil(max), one); + /// + #[doc = concat!("let two = NonZero::new(2", stringify!($Int), ").unwrap();")] + #[doc = concat!("let three = NonZero::new(3", stringify!($Int), ").unwrap();")] + /// assert_eq!(three.div_ceil(two), two); + /// ``` + #[stable(feature = "unsigned_nonzero_div_ceil", since = "1.92.0")] + #[rustc_const_stable(feature = "unsigned_nonzero_div_ceil", since = "1.92.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn div_ceil(self, rhs: Self) -> Self { + // An implementation of the function without calculating the remainder. + // It is better than the implementation for normal integers, but it can only + // be used here because of the possibility to subtract by one without overflow. + let v = (self.get() - 1) / rhs.get() + 1; + // SAFETY: ceiled division of two positive integers can never be zero. + unsafe { Self::new_unchecked(v) } + } + /// Returns the smallest power of two greater than or equal to `self`. /// Checks for overflow and returns [`None`] /// if the next power of two is greater than the type’s maximum value. diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index cadfd37f9f35f..4ee91d411903a 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -19,7 +19,7 @@ use rustc_hir::def::{DefKind, MacroKinds}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::{ConstStability, StabilityLevel, StableSince}; use rustc_metadata::creader::CStore; -use rustc_middle::ty::{self, TyCtxt, TypingMode}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode}; use rustc_span::symbol::kw; use rustc_span::{Ident, Symbol}; use tracing::{debug, trace}; @@ -410,17 +410,28 @@ fn generate_macro_def_id_path( Ok(HrefInfo { url, kind: item_type, rust_path: path }) } +/// Takes an impl `DefId` and return the self `Ty` of the impl. +fn impl_self_ty(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Ty<'_> { + use rustc_middle::traits::ObligationCause; + use rustc_middle::ty; + use rustc_trait_selection::infer::TyCtxtInferExt; + use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; + + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let ty = tcx.type_of(impl_def_id); + infcx + .at(&ObligationCause::dummy(), tcx.param_env(impl_def_id)) + .query_normalize(ty::Binder::dummy(ty.instantiate_identity().skip_norm_wip())) + .map(|resolved| infcx.deeply_resolve_ignoring_regions(resolved.value).skip_binder()) + .unwrap_or(ty.skip_binder()) +} + fn generate_item_def_id_path( mut def_id: DefId, original_def_id: DefId, cx: &Context<'_>, root_path: Option<&str>, ) -> Result { - use rustc_middle::traits::ObligationCause; - use rustc_middle::ty; - use rustc_trait_selection::infer::TyCtxtInferExt; - use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; - let tcx = cx.tcx(); let crate_name = tcx.crate_name(def_id.krate); let mut prim = None; @@ -428,13 +439,7 @@ fn generate_item_def_id_path( // No need to try to infer the actual parent item if it's not an associated item from the `impl` // block. if def_id != original_def_id && matches!(tcx.def_kind(def_id), DefKind::Impl { .. }) { - let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - let ty = tcx.type_of(def_id); - let ty = infcx - .at(&ObligationCause::dummy(), tcx.param_env(def_id)) - .query_normalize(ty::Binder::dummy(ty.instantiate_identity().skip_norm_wip())) - .map(|resolved| infcx.deeply_resolve_ignoring_regions(resolved.value).skip_binder()) - .unwrap_or(ty.skip_binder()); + let ty = impl_self_ty(tcx, def_id); // If this is a dyn trait, we want to get the actual trait from which the method comes from. // Since a `dyn trait` (as of 2026) can only be composed of a trait plus auto traits, we // look for the trait and ignore auto traits. @@ -552,6 +557,47 @@ fn make_href( url_parts.finish() } +fn ty_inherits_doc_hidden<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + // If this is a dyn trait, we want to get the actual trait from which the method comes from. + // Since a `dyn trait` (as of 2026) can only be composed of a trait plus auto traits, we + // look for the trait and ignore auto traits. + ty::Dynamic(traits, _) => traits + .iter() + .find_map(|trait_| match trait_.skip_binder() { + ty::ExistentialPredicate::Trait(t) => Some(inherits_doc_hidden(tcx, t.def_id)), + ty::ExistentialPredicate::Projection(p) => { + Some(inherits_doc_hidden(tcx, p.trait_ref(tcx).def_id)) + } + ty::ExistentialPredicate::AutoTrait(_) => None, + }) + .unwrap_or(false), + ty::Adt(adt, _) => inherits_doc_hidden(tcx, adt.did()), + ty::Foreign(def_id) => inherits_doc_hidden(tcx, *def_id), + ty::Ref(_, ty, _) => ty_inherits_doc_hidden(tcx, *ty), + // For now we consider that everything doesn't inherit `#[doc(hidden)]`. To be confirmed + // later. + _ => false, + } +} + +fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut def_id: DefId) -> bool { + loop { + if tcx.is_doc_hidden(def_id) { + return true; + } else if def_id.is_crate_root() { + return false; + } else if let DefKind::Impl { of_trait } = tcx.def_kind(def_id) { + // `impl` blocks stand a bit on their own: unless they have `#[doc(hidden)]` directly + // on them, they don't inherit it from the parent context. + // Instead we check that the `Self` item and the trait aren't hidden. + return ty_inherits_doc_hidden(tcx, impl_self_ty(tcx, def_id)) + || (of_trait && inherits_doc_hidden(tcx, tcx.impl_trait_id(def_id))); + } + def_id = tcx.parent(def_id); + } +} + pub(crate) fn href_with_root_path( original_did: DefId, cx: &Context<'_>, @@ -590,7 +636,7 @@ pub(crate) fn href_with_root_path( // If we are generating an href for the "jump to def" feature, then the only case we want // to ignore is if the item is `doc(hidden)` because we can't link to it. if root_path.is_some() { - if tcx.is_doc_hidden(original_did) { + if inherits_doc_hidden(tcx, original_did) { return Err(HrefError::Private); } } else if !cache.effective_visibilities.is_directly_public(tcx, did) diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 7e49702de8632..04ace1727c82c 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1280,18 +1280,18 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { for (op, _op_sp) in asm.operands { match op { InlineAsmOperand::In { reg, expr } => { - reg.hash(&mut self.s); + reg.as_target().hash(&mut self.s); self.hash_expr(expr); }, InlineAsmOperand::Out { reg, late, expr } => { - reg.hash(&mut self.s); + reg.as_target().hash(&mut self.s); late.hash(&mut self.s); if let Some(expr) = expr { self.hash_expr(expr); } }, InlineAsmOperand::InOut { reg, late, expr } => { - reg.hash(&mut self.s); + reg.as_target().hash(&mut self.s); late.hash(&mut self.s); self.hash_expr(expr); }, @@ -1301,7 +1301,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { in_expr, out_expr, } => { - reg.hash(&mut self.s); + reg.as_target().hash(&mut self.s); late.hash(&mut self.s); self.hash_expr(in_expr); if let Some(out_expr) = out_expr { diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index c900c3debd504..3235627e9ca5f 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -61,9 +61,11 @@ //@ [CSKY] compile-flags: --target csky-unknown-linux-gnuabiv2 //@ [CSKY] needs-llvm-components: csky -//@ revisions: SPARC64 +//@ revisions: SPARC64 SPARC //@ [SPARC64] compile-flags: --target sparc64-unknown-linux-gnu //@ [SPARC64] needs-llvm-components: sparc +//@ [SPARC] compile-flags: --target sparc-unknown-linux-gnu +//@ [SPARC] needs-llvm-components: sparc //@ revisions: POWERPC64LE POWERPC64 AIX //@ [POWERPC64LE] compile-flags: --target powerpc64le-unknown-linux-gnu @@ -75,10 +77,6 @@ // FIXME: the below revisions are deliberately disabled for now. -// revisions: SPARC -// [SPARC] compile-flags: --target sparc-unknown-linux-gnu -// [SPARC] needs-llvm-components: sparc - // revisions: POWERPC // [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu // [POWERPC] needs-llvm-components: powerpc @@ -155,7 +153,7 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { // RISCV64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) // S390X: define{{.*}} void @cplx_f32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) - // SPARC: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval({ float, float }) {{.*}}) + // SPARC: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} dereferenceable(8) {{.*}}) // WASM32: define{{.*}} void @cplx_f32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @cplx_f32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} i64 @cplx_f32(ptr {{.*}} byval([8 x i8]) {{.*}}) @@ -189,7 +187,7 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { // RISCV64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) // S390X: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) - // SPARC: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval({ double, double }) {{.*}}) + // SPARC: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} dereferenceable(16) {{.*}}) // WASM32: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) @@ -204,6 +202,7 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { pub extern "C" fn cplx_f128(x: Complex) -> Complex { // AARCH64: define{{.*}} [2 x fp128] {{.*}}) // I686: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}}) + // SPARC: define{{.*}} inreg { fp128, fp128 } @cplx_f128(ptr {{.*}} dereferenceable(32) {{.*}}) // WASM32: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}}) @@ -237,7 +236,7 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { // RISCV64: define{{.*}} i64 @cplx_i8(i64{{.*}}) // S390X: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i16 @cplx_i8(i16{{.*}}) - // SPARC: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval({ i8, i8 }) {{.*}}) + // SPARC: define{{.*}} i16 @cplx_i8(i16{{.*}}) // WASM32: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} i16 @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) @@ -271,7 +270,7 @@ pub extern "C" fn cplx_i16(x: Complex) -> Complex { // RISCV64: define{{.*}} i64 @cplx_i16(i64{{.*}}) // S390X: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i32 @cplx_i16(i32{{.*}}) - // SPARC: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval({ i16, i16 }) {{.*}}) + // SPARC: define{{.*}} i32 @cplx_i16(i32{{.*}}) // WASM32: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} i32 @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) @@ -305,7 +304,7 @@ pub extern "C" fn cplx_i32(x: Complex) -> Complex { // RISCV64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) // S390X: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) - // SPARC: define{{.*}} { i32, i32 } @cplx_i32(ptr {{.*}} byval({ i32, i32 }) {{.*}}) + // SPARC: define{{.*}} i64 @cplx_i32(i64 {{.*}}) // WASM32: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} i64 @cplx_i32(ptr {{.*}} byval([8 x i8]) {{.*}}) @@ -339,7 +338,7 @@ pub extern "C" fn cplx_i64(x: Complex) -> Complex { // RISCV64: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) // S390X: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} { i64, i64 } @cplx_i64({ i64, i64 } {{.*}}) - // SPARC: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) + // SPARC: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} dereferenceable(16) {{.*}}) // WASM32: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) @@ -378,7 +377,7 @@ pub extern "C" fn wrapper_cplx_i64( // RISCV64: define{{.*}} [2 x i64] @wrapper_cplx_i64([2 x i64] {{.*}}) // S390X: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define { i64, i64 } @wrapper_cplx_i64({ i64, i64 } {{.*}}) - // SPARC: define{{.*}} { i64, i64 } @wrapper_cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) + // SPARC: define{{.*}} { i64, i64 } @wrapper_cplx_i64(ptr {{.*}} dereferenceable(16) {{.*}}) // WASM32: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) diff --git a/tests/codegen-llvm/powerpc64-abi/homogeneous-aggregate.rs b/tests/codegen-llvm/powerpc64-abi/homogeneous-aggregate.rs index dd053411aa4e5..58dd9c837603f 100644 --- a/tests/codegen-llvm/powerpc64-abi/homogeneous-aggregate.rs +++ b/tests/codegen-llvm/powerpc64-abi/homogeneous-aggregate.rs @@ -94,3 +94,131 @@ pub struct Hfa4V2F64 { pub extern "C" fn test_hfa_4_f64x2(a: Hfa4V2F64) { hint::black_box(a); } + +// A one-member aggregate containing a float is passed in a floating point register. +#[repr(C)] +pub struct OneFloatStruct { + pub a: f32, +} +impl Copy for OneFloatStruct {} + +// ppc64: define void @test_struct_one_float(float %0) +// ppc64_vsx: define void @test_struct_one_float(float %0) +// ppc64le: define void @test_struct_one_float(float %0) +#[unsafe(no_mangle)] +pub extern "C" fn test_struct_one_float(a: OneFloatStruct) { + hint::black_box(a); +} + +// A union with several members is not a one-member aggregate, so on ELFv1 it is not passed in a +// floating point register. See https://github.com/rust-lang/rust/issues/162011. +#[repr(C)] +pub union TwoFloats { + pub a: f32, + pub b: f32, +} + +// ppc64: define void @test_union_two_floats(i32 %0) +// ppc64_vsx: define void @test_union_two_floats(i32 %0) +// ppc64le: define void @test_union_two_floats(float %0) +#[unsafe(no_mangle)] +pub extern "C" fn test_union_two_floats(a: TwoFloats) { + hint::black_box(a); +} + +// A zero-sized union contributes no data, so it doesn't disqualify the aggregate. +#[repr(C)] +pub union ZstUnion { + pub a: (), +} + +#[repr(C)] +pub struct OneFloatAndZstUnion { + pub a: f32, + pub u: ZstUnion, +} + +// ppc64: define void @test_struct_one_float_and_zst_union(float %0) +// ppc64_vsx: define void @test_struct_one_float_and_zst_union(float %0) +// ppc64le: define void @test_struct_one_float_and_zst_union(float %0) +#[unsafe(no_mangle)] +pub extern "C" fn test_struct_one_float_and_zst_union(a: OneFloatAndZstUnion) { + hint::black_box(a); +} + +// On ELFv1 unions are not considered aggregates for the ABI, so they are never passed in a +// floating point register, not even when they have a single field or are nested within an +// aggregate. See https://github.com/rust-lang/rust/issues/162011. +#[repr(C)] +pub union OneFloat { + pub a: f32, +} + +// ppc64: define void @test_union_one_float(i32 %0) +// ppc64_vsx: define void @test_union_one_float(i32 %0) +// ppc64le: define void @test_union_one_float(float %0) +#[unsafe(no_mangle)] +pub extern "C" fn test_union_one_float(a: OneFloat) { + hint::black_box(a); +} + +#[repr(C)] +pub struct StructOfUnion { + pub u: OneFloat, +} + +// ppc64: define void @test_struct_of_union(i32 %0) +// ppc64_vsx: define void @test_struct_of_union(i32 %0) +// ppc64le: define void @test_struct_of_union(float %0) +#[unsafe(no_mangle)] +pub extern "C" fn test_struct_of_union(a: StructOfUnion) { + hint::black_box(a); +} + +#[repr(C)] +pub union UnionOfStruct { + pub s: OneFloatStruct, +} + +// ppc64: define void @test_union_of_struct(i32 %0) +// ppc64_vsx: define void @test_union_of_struct(i32 %0) +// ppc64le: define void @test_union_of_struct(float %0) +#[unsafe(no_mangle)] +pub extern "C" fn test_union_of_struct(a: UnionOfStruct) { + hint::black_box(a); +} + +// A `repr(transparent)` union like `MaybeUninit` is guaranteed to be ABI-compatible with its single +// non-1-ZST field, so it is passed in a floating point register like the field would be. +// +// ppc64: define void @test_transparent_union_one_float(float %a) +// ppc64_vsx: define void @test_transparent_union_one_float(float %a) +// ppc64le: define void @test_transparent_union_one_float(float %a) +#[unsafe(no_mangle)] +pub extern "C" fn test_transparent_union_one_float(a: MaybeUninit) { + hint::black_box(a); +} + +#[repr(C)] +pub struct StructOfTransparentUnion { + pub u: MaybeUninit, +} + +// ppc64: define void @test_struct_of_transparent_union(float %0) +// ppc64_vsx: define void @test_struct_of_transparent_union(float %0) +// ppc64le: define void @test_struct_of_transparent_union(float %0) +#[unsafe(no_mangle)] +pub extern "C" fn test_struct_of_transparent_union(a: StructOfTransparentUnion) { + hint::black_box(a); +} + +// A `repr(transparent)` union whose field is itself a union is still not passed in a floating +// point register on ELFv1. +// +// ppc64: define void @test_transparent_union_of_union(i32 %0) +// ppc64_vsx: define void @test_transparent_union_of_union(i32 %0) +// ppc64le: define void @test_transparent_union_of_union(float %0) +#[unsafe(no_mangle)] +pub extern "C" fn test_transparent_union_of_union(a: MaybeUninit) { + hint::black_box(a); +} diff --git a/tests/rustdoc-html/jump-to-def/auxiliary/hidden-links.rs b/tests/rustdoc-html/jump-to-def/auxiliary/hidden-links.rs new file mode 100644 index 0000000000000..342a4765f4f7d --- /dev/null +++ b/tests/rustdoc-html/jump-to-def/auxiliary/hidden-links.rs @@ -0,0 +1,19 @@ +#[doc(hidden)] +pub mod error { + pub struct Foo; + + pub trait HiddenTrait { + fn hidden(&self) {} + } +} + +pub struct Bar; +pub trait PubTrait { + fn public(&self) {} +} + +impl crate::error::HiddenTrait for crate::error::Foo {} +impl crate::error::HiddenTrait for Bar {} + +impl PubTrait for crate::error::Foo {} +impl PubTrait for Bar {} diff --git a/tests/rustdoc-html/jump-to-def/hidden-links.rs b/tests/rustdoc-html/jump-to-def/hidden-links.rs new file mode 100644 index 0000000000000..848265f38f514 --- /dev/null +++ b/tests/rustdoc-html/jump-to-def/hidden-links.rs @@ -0,0 +1,43 @@ +// This checks that if the item is not directly `doc(hidden)` but instead a parent +// is, we don't generate a link as it would lead to non-existing files. + +//@ compile-flags: -Zunstable-options --generate-link-to-definition +//@ aux-build:hidden-links.rs +//@ build-aux-docs + +#![crate_name = "foo"] + +extern crate hidden_links; + +//@ has 'src/foo/hidden-links.rs.html' + +// Because our XPath handler is very bad, I can't look for `not(@id)` so instead, +// we count how many `` we have in total (line numbers are ``). +//@ count - '//pre/code/a' 21 +// Then we count the `` of lines. The diff should be the number of non-line `` (no +// better way to test that either..). +//@ count - '//pre/code/a[@id]' 13 + +//@ has - '//pre/code/a[@href="../../hidden_links/trait.PubTrait.html"]' 'PubTrait' +use hidden_links::{PubTrait, error::HiddenTrait}; + +fn foo() { + // In here, only `Bar` should be linked as `Foo` has a `doc(hidden)` parent and + // is not reexported. So there should be only one link. + + // This one should not generate any link. + let x = hidden_links::error::Foo; + //@ has - '//pre/code/a[@href="../../hidden_links/struct.Bar.html"]' 'Bar' + let y = hidden_links::Bar; + + // `x` and `y` generate a link every time... + //@ count - '//pre/code/a[@href="#29"]' 2 + //@ count - '//pre/code/a[@href="#31"]' 2 + // FIXME(GuillaumeGomez): This is a bug, it should link the trait method on the item's page, + // not on the trait's (and so `x.public()`) should not link. + //@ count - '//pre/code/a[@href="../../hidden_links/trait.PubTrait.html#method.public"]' 2 + x.hidden(); + y.hidden(); + x.public(); + y.public(); +} diff --git a/tests/ui/asm/sparc/bad-reg.rs b/tests/ui/asm/sparc/bad-reg.rs index 8cac5eb6b4ad0..74c31279656d1 100644 --- a/tests/ui/asm/sparc/bad-reg.rs +++ b/tests/ui/asm/sparc/bad-reg.rs @@ -28,7 +28,7 @@ fn f() { asm!("", out("g3") _); asm!("", out("g4") _); asm!("", out("g5") _); - //[sparc,sparcv8plus]~^ ERROR cannot use register `r5`: g5 is reserved for system on SPARC32 + //[sparc,sparcv8plus]~^ ERROR cannot use register `g5`: g5 is reserved for system on SPARC32 asm!("", out("g6") _); //~^ ERROR invalid register `g6`: reserved for system and cannot be used as an operand for inline asm asm!("", out("g7") _); diff --git a/tests/ui/asm/sparc/bad-reg.sparc.stderr b/tests/ui/asm/sparc/bad-reg.sparc.stderr index 06b607ff007ac..ac2ee667d4eea 100644 --- a/tests/ui/asm/sparc/bad-reg.sparc.stderr +++ b/tests/ui/asm/sparc/bad-reg.sparc.stderr @@ -64,7 +64,7 @@ error: register class `yreg` can only be used as a clobber, not as an input or o LL | asm!("/* {} */", out(yreg) _); | ^^^^^^^^^^^ -error: cannot use register `r5`: g5 is reserved for system on SPARC32 +error: cannot use register `g5`: g5 is reserved for system on SPARC32 --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("g5") _); diff --git a/tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr b/tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr index e0580ad3232f5..be931f9a2aad4 100644 --- a/tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr +++ b/tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr @@ -64,7 +64,7 @@ error: register class `yreg` can only be used as a clobber, not as an input or o LL | asm!("/* {} */", out(yreg) _); | ^^^^^^^^^^^ -error: cannot use register `r5`: g5 is reserved for system on SPARC32 +error: cannot use register `g5`: g5 is reserved for system on SPARC32 --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("g5") _); diff --git a/tests/ui/asm/x86-explicit-register-alias-diagnostic.rs b/tests/ui/asm/x86-explicit-register-alias-diagnostic.rs new file mode 100644 index 0000000000000..4a67827d3c84e --- /dev/null +++ b/tests/ui/asm/x86-explicit-register-alias-diagnostic.rs @@ -0,0 +1,51 @@ +// Check that inline assembly diagnostics use the explicit register name from the source. +// +// On 32-bit x86, vector registers 8 and higher are unavailable. Registers 16 through 31 +// share a canonical `zmm` representation, but diagnostics must still use the `xmm`, `ymm`, +// or `zmm` name that the user wrote. +// +// Regression test for . + +//@ add-minicore +//@ revisions: x86 x86_64 +//@[x86] compile-flags: --target i686-unknown-linux-gnu +//@[x86] needs-llvm-components: x86 +//@[x86] check-fail +//@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu +//@[x86_64] needs-llvm-components: x86 +//@[x86_64] check-pass +//@ ignore-backends: gcc + +#![feature(no_core)] +#![no_core] + +extern crate minicore; +use minicore::*; + +fn main() { + unsafe { + // Register 15 is the last register with separate canonical representations. + asm!("", lateout("xmm15") _); + //[x86]~^ ERROR cannot use register `xmm15` + asm!("", lateout("ymm15") _); + //[x86]~^ ERROR cannot use register `ymm15` + asm!("", lateout("zmm15") _); + //[x86]~^ ERROR cannot use register `zmm15` + + // Register 16 is the first register whose aliases share the canonical `zmm` representation. + asm!("", lateout("xmm16") _); + //[x86]~^ ERROR cannot use register `xmm16` + asm!("", lateout("ymm16") _); + //[x86]~^ ERROR cannot use register `ymm16` + asm!("", lateout("zmm16") _); + //[x86]~^ ERROR cannot use register `zmm16` + + // Register 31 is the upper boundary of the affected register range. + asm!("", lateout("xmm31") _); + //[x86]~^ ERROR cannot use register `xmm31` + asm!("", lateout("ymm31") _); + //[x86]~^ ERROR cannot use register `ymm31` + asm!("", lateout("zmm31") _); + //[x86]~^ ERROR cannot use register `zmm31` + } +} diff --git a/tests/ui/asm/x86-explicit-register-alias-diagnostic.x86.stderr b/tests/ui/asm/x86-explicit-register-alias-diagnostic.x86.stderr new file mode 100644 index 0000000000000..81718243841aa --- /dev/null +++ b/tests/ui/asm/x86-explicit-register-alias-diagnostic.x86.stderr @@ -0,0 +1,56 @@ +error: cannot use register `xmm15`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:28:18 + | +LL | asm!("", lateout("xmm15") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `ymm15`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:30:18 + | +LL | asm!("", lateout("ymm15") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `zmm15`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:32:18 + | +LL | asm!("", lateout("zmm15") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `xmm16`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:36:18 + | +LL | asm!("", lateout("xmm16") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `ymm16`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:38:18 + | +LL | asm!("", lateout("ymm16") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `zmm16`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:40:18 + | +LL | asm!("", lateout("zmm16") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `xmm31`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:44:18 + | +LL | asm!("", lateout("xmm31") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `ymm31`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:46:18 + | +LL | asm!("", lateout("ymm31") _); + | ^^^^^^^^^^^^^^^^^^ + +error: cannot use register `zmm31`: register is only available on x86_64 + --> $DIR/x86-explicit-register-alias-diagnostic.rs:48:18 + | +LL | asm!("", lateout("zmm31") _); + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors +