Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a7f8b2f
remove unnecessary uses of paths()
jyn514 Jan 31, 2026
43ee28a
cmse: don't use `BackendRepr` when checking return type
folkertdev Jan 24, 2026
df0bdea
cmse: additional argument passing tests
folkertdev Jan 28, 2026
0bbc2f2
Remove USE_XCODE_CLANG
madsmtm Feb 2, 2026
19a7bd9
Remove AR=ar env var
madsmtm Feb 2, 2026
3882573
Always use default Clang on macOS
madsmtm Feb 2, 2026
eb9d515
extract `TyAndLayout::peel_transparent_wrappers` helper
folkertdev Feb 5, 2026
4394742
Extend `check_args_compatible` to handle IACs
lapla-cogito Feb 5, 2026
c450ecf
Transform args for inherent type_consts in `instantiate_value_path`
lapla-cogito Feb 5, 2026
d5957f5
Add test for inherent type_const normalization
lapla-cogito Feb 5, 2026
22885d2
Reorganize tests that no longer crash
lapla-cogito Feb 5, 2026
d0aa337
feat: Add `NonZero::<T>::from_str_radix`
sorairolake Feb 26, 2025
0436efe
Get rid of `paths` function
jyn514 Jan 31, 2026
ee2c39a
re-add TaKO8Ki to triagebot review queue
TaKO8Ki Feb 6, 2026
9f0eba2
Rollup merge of #151590 - folkertdev:cmse-unwrap-transparent, r=david…
JonathanBrouwer Feb 6, 2026
f509250
Rollup merge of #151945 - sorairolake:feature/nonzero-from-str-radix,…
JonathanBrouwer Feb 6, 2026
a9b4c7c
Rollup merge of #152000 - lapla-cogito:ice_151027, r=BoxyUwU
JonathanBrouwer Feb 6, 2026
5372fab
Rollup merge of #152192 - madsmtm:remove-use-xcode-clang, r=shepmaster
JonathanBrouwer Feb 6, 2026
ad5b312
Rollup merge of #152196 - jyn514:remove-paths, r=Zalathar
JonathanBrouwer Feb 6, 2026
4ae29f9
Rollup merge of #152222 - TaKO8Ki:add-tako8ki-to-triagebot, r=davidtwco
JonathanBrouwer Feb 6, 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
16 changes: 14 additions & 2 deletions compiler/rustc_abi/src/layout/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,19 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
/// function call isn't allowed (a.k.a. `va_list`).
///
/// This function handles transparent types automatically.
pub fn pass_indirectly_in_non_rustic_abis<C>(mut self, cx: &C) -> bool
pub fn pass_indirectly_in_non_rustic_abis<C>(self, cx: &C) -> bool
where
Ty: TyAbiInterface<'a, C> + Copy,
{
let base = self.peel_transparent_wrappers(cx);
Ty::is_pass_indirectly_in_non_rustic_abis_flag_set(base)
}

/// Recursively peel away transparent wrappers, returning the inner value.
///
/// The return value is not `repr(transparent)` and/or does
/// not have a non-1zst field.
pub fn peel_transparent_wrappers<C>(mut self, cx: &C) -> Self
where
Ty: TyAbiInterface<'a, C> + Copy,
{
Expand All @@ -300,7 +312,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
self = field;
}

Ty::is_pass_indirectly_in_non_rustic_abis_flag_set(self)
self
}

/// Finds the one field that is not a 1-ZST.
Expand Down
26 changes: 11 additions & 15 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use rustc_abi::{BackendRepr, ExternAbi, Float, Integer, Primitive, Scalar};
use rustc_abi::ExternAbi;
use rustc_errors::{DiagCtxtHandle, E0781, struct_span_code_err};
use rustc_hir::{self as hir, HirId};
use rustc_middle::bug;
use rustc_middle::ty::layout::{LayoutError, TyAndLayout};
use rustc_middle::ty::layout::{LayoutCx, LayoutError, TyAndLayout};
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
use rustc_span::Span;

Expand Down Expand Up @@ -150,34 +150,30 @@ fn is_valid_cmse_output<'tcx>(

let typing_env = ty::TypingEnv::fully_monomorphized();
let layout = tcx.layout_of(typing_env.as_query_input(return_type))?;
let layout_cx = LayoutCx::new(tcx, typing_env);

if !is_valid_cmse_output_layout(layout) {
if !is_valid_cmse_output_layout(layout_cx, layout) {
dcx.emit_err(errors::CmseOutputStackSpill { span: fn_decl.output.span(), abi });
}

Ok(())
}

/// Returns whether the output will fit into the available registers
fn is_valid_cmse_output_layout<'tcx>(layout: TyAndLayout<'tcx>) -> bool {
fn is_valid_cmse_output_layout<'tcx>(cx: LayoutCx<'tcx>, layout: TyAndLayout<'tcx>) -> bool {
let size = layout.layout.size().bytes();

if size <= 4 {
return true;
} else if size > 8 {
} else if size != 8 {
return false;
}

// Accept scalar 64-bit types.
let BackendRepr::Scalar(scalar) = layout.layout.backend_repr else {
return false;
};

let Scalar::Initialized { value, .. } = scalar else {
return false;
};

matches!(value, Primitive::Int(Integer::I64, _) | Primitive::Float(Float::F64))
// Accept (transparently wrapped) scalar 64-bit primitives.
matches!(
layout.peel_transparent_wrappers(&cx).ty.kind(),
ty::Int(ty::IntTy::I64) | ty::Uint(ty::UintTy::U64) | ty::Float(ty::FloatTy::F64)
)
}

fn should_emit_layout_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError<'tcx>) -> bool {
Expand Down
47 changes: 46 additions & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,38 @@ use crate::method::{self, MethodCallee};
use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy};

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Transform generic args for inherent associated type constants (IACs).
///
/// IACs have a different generic parameter structure than regular associated constants:
/// - Regular assoc const: parent (impl) generic params + own generic params
/// - IAC (type_const): Self type + own generic params
pub(crate) fn transform_args_for_inherent_type_const(
&self,
def_id: DefId,
args: GenericArgsRef<'tcx>,
) -> GenericArgsRef<'tcx> {
let tcx = self.tcx;
if !tcx.is_type_const(def_id) {
return args;
}
let Some(assoc_item) = tcx.opt_associated_item(def_id) else {
return args;
};
if !matches!(assoc_item.container, ty::AssocContainer::InherentImpl) {
return args;
}

let impl_def_id = assoc_item.container_id(tcx);
let generics = tcx.generics_of(def_id);
let impl_args = &args[..generics.parent_count];
let self_ty = tcx.type_of(impl_def_id).instantiate(tcx, impl_args);
// Build new args: [Self, own_args...]
let own_args = &args[generics.parent_count..];
tcx.mk_args_from_iter(
std::iter::once(ty::GenericArg::from(self_ty)).chain(own_args.iter().copied()),
)
}

/// Produces warning on the given node, if the current point in the
/// function is unreachable, and there hasn't been another warning.
pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) {
Expand Down Expand Up @@ -1281,8 +1313,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
});

let args_for_user_type = if let Res::Def(DefKind::AssocConst, def_id) = res {
self.transform_args_for_inherent_type_const(def_id, args_raw)
} else {
args_raw
};

// First, store the "user args" for later.
self.write_user_type_annotation_from_args(hir_id, def_id, args_raw, user_self_ty);
self.write_user_type_annotation_from_args(hir_id, def_id, args_for_user_type, user_self_ty);

// Normalize only after registering type annotations.
let args = self.normalize(span, args_raw);
Expand Down Expand Up @@ -1322,6 +1360,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_instantiated);

let args = if let Res::Def(DefKind::AssocConst, def_id) = res {
self.transform_args_for_inherent_type_const(def_id, args)
} else {
args
};

self.write_args(hir_id, args);

(ty_instantiated, res)
Expand Down
24 changes: 15 additions & 9 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2920,12 +2920,15 @@ impl<'tcx> TyCtxt<'tcx> {
) -> bool {
let generics = self.generics_of(def_id);

// IATs themselves have a weird arg setup (self + own args), but nested items *in* IATs
// (namely: opaques, i.e. ATPITs) do not.
let own_args = if !nested
&& let DefKind::AssocTy = self.def_kind(def_id)
&& let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id))
{
// IATs and IACs (inherent associated types/consts with #[type_const]) themselves have a
// weird arg setup (self + own args), but nested items *in* IATs (namely: opaques, i.e.
// ATPITs) do not.
let is_inherent_assoc_ty = matches!(self.def_kind(def_id), DefKind::AssocTy)
&& matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false });
let is_inherent_assoc_type_const = matches!(self.def_kind(def_id), DefKind::AssocConst)
&& matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false })
&& self.is_type_const(def_id);
let own_args = if !nested && (is_inherent_assoc_ty || is_inherent_assoc_type_const) {
if generics.own_params.len() + 1 != args.len() {
return false;
}
Expand Down Expand Up @@ -2967,9 +2970,12 @@ impl<'tcx> TyCtxt<'tcx> {
/// and print out the args if not.
pub fn debug_assert_args_compatible(self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>]) {
if cfg!(debug_assertions) && !self.check_args_compatible(def_id, args) {
if let DefKind::AssocTy = self.def_kind(def_id)
&& let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id))
{
let is_inherent_assoc_ty = matches!(self.def_kind(def_id), DefKind::AssocTy)
&& matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false });
let is_inherent_assoc_type_const = matches!(self.def_kind(def_id), DefKind::AssocConst)
&& matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false })
&& self.is_type_const(def_id);
if is_inherent_assoc_ty || is_inherent_assoc_type_const {
bug!(
"args not compatible with generics for {}: args={:#?}, generics={:#?}",
self.def_path_str(def_id),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
use rustc_infer::traits::Obligation;
use rustc_middle::traits::query::NoSolution;
Expand Down Expand Up @@ -96,6 +97,26 @@ fn relate_mir_and_user_args<'tcx>(
let tcx = ocx.infcx.tcx;
let cause = ObligationCause::dummy_with_span(span);

// For IACs, the user args are in the format [SelfTy, GAT_args...] but type_of expects [impl_args..., GAT_args...].
// We need to infer the impl args by equating the impl's self type with the user-provided self type.
let is_inherent_assoc_const = tcx.def_kind(def_id) == DefKind::AssocConst
&& tcx.def_kind(tcx.parent(def_id)) == DefKind::Impl { of_trait: false }
&& tcx.is_type_const(def_id);

let args = if is_inherent_assoc_const {
let impl_def_id = tcx.parent(def_id);
let impl_args = ocx.infcx.fresh_args_for_item(span, impl_def_id);
let impl_self_ty =
ocx.normalize(&cause, param_env, tcx.type_of(impl_def_id).instantiate(tcx, impl_args));
let user_self_ty = ocx.normalize(&cause, param_env, args[0].expect_ty());
ocx.eq(&cause, param_env, impl_self_ty, user_self_ty)?;

let gat_args = &args[1..];
tcx.mk_args_from_iter(impl_args.iter().chain(gat_args.iter().copied()))
} else {
args
};

let ty = tcx.type_of(def_id).instantiate(tcx, args);
let ty = ocx.normalize(&cause, param_env, ty);
debug!("relate_type_and_user_type: ty of def-id is {:?}", ty);
Expand Down
70 changes: 66 additions & 4 deletions library/core/src/num/nonzero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1240,16 +1240,78 @@ macro_rules! nonzero_integer {
// So the result cannot be zero.
unsafe { Self::new_unchecked(self.get().saturating_pow(other)) }
}

/// Parses a non-zero integer from a string slice with digits in a given base.
///
/// The string is expected to be an optional
#[doc = sign_dependent_expr!{
$signedness ?
if signed {
" `+` or `-` "
}
if unsigned {
" `+` "
}
}]
/// sign followed by only digits. Leading and trailing non-digit characters (including
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
/// also represent an error.
///
/// Digits are a subset of these characters, depending on `radix`:
///
/// - `0-9`
/// - `a-z`
/// - `A-Z`
///
/// # Panics
///
/// This method panics if `radix` is not in the range from 2 to 36.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(nonzero_from_str_radix)]
///
/// # use std::num::NonZero;
/// #
/// # fn main() { test().unwrap(); }
/// # fn test() -> Option<()> {
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::from_str_radix(\"A\", 16), Ok(NonZero::new(10)?));")]
/// # Some(())
/// # }
/// ```
///
/// Trailing space returns error:
///
/// ```
/// #![feature(nonzero_from_str_radix)]
///
/// # use std::num::NonZero;
/// #
#[doc = concat!("assert!(NonZero::<", stringify!($Int), ">::from_str_radix(\"1 \", 10).is_err());")]
/// ```
#[unstable(feature = "nonzero_from_str_radix", issue = "152193")]
#[inline]
pub const fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
let n = match <$Int>::from_str_radix(src, radix) {
Ok(n) => n,
Err(err) => return Err(err),
};
if let Some(n) = Self::new(n) {
Ok(n)
} else {
Err(ParseIntError { kind: IntErrorKind::Zero })
}
}
}

#[stable(feature = "nonzero_parse", since = "1.35.0")]
impl FromStr for NonZero<$Int> {
type Err = ParseIntError;
fn from_str(src: &str) -> Result<Self, Self::Err> {
Self::new(<$Int>::from_str_radix(src, 10)?)
.ok_or(ParseIntError {
kind: IntErrorKind::Zero
})
Self::from_str_radix(src, 10)
}
}

Expand Down
1 change: 1 addition & 0 deletions library/coretests/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
#![feature(new_range_api)]
#![feature(next_index)]
#![feature(non_exhaustive_omitted_patterns_lint)]
#![feature(nonzero_from_str_radix)]
#![feature(numfmt)]
#![feature(one_sided_range)]
#![feature(option_reduce)]
Expand Down
35 changes: 35 additions & 0 deletions library/coretests/tests/nonzero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,41 @@ fn test_from_signed_nonzero() {
assert_eq!(num, 1i32);
}

#[test]
fn test_from_str_radix() {
assert_eq!(NonZero::<u8>::from_str_radix("123", 10), Ok(NonZero::new(123).unwrap()));
assert_eq!(NonZero::<u8>::from_str_radix("1001", 2), Ok(NonZero::new(9).unwrap()));
assert_eq!(NonZero::<u8>::from_str_radix("123", 8), Ok(NonZero::new(83).unwrap()));
assert_eq!(NonZero::<u16>::from_str_radix("123", 16), Ok(NonZero::new(291).unwrap()));
assert_eq!(NonZero::<u16>::from_str_radix("ffff", 16), Ok(NonZero::new(65535).unwrap()));
assert_eq!(NonZero::<u8>::from_str_radix("z", 36), Ok(NonZero::new(35).unwrap()));
assert_eq!(
NonZero::<u8>::from_str_radix("0", 10).err().map(|e| e.kind().clone()),
Some(IntErrorKind::Zero)
);
assert_eq!(
NonZero::<u8>::from_str_radix("-1", 10).err().map(|e| e.kind().clone()),
Some(IntErrorKind::InvalidDigit)
);
assert_eq!(
NonZero::<i8>::from_str_radix("-129", 10).err().map(|e| e.kind().clone()),
Some(IntErrorKind::NegOverflow)
);
assert_eq!(
NonZero::<u8>::from_str_radix("257", 10).err().map(|e| e.kind().clone()),
Some(IntErrorKind::PosOverflow)
);

assert_eq!(
NonZero::<u8>::from_str_radix("Z", 10).err().map(|e| e.kind().clone()),
Some(IntErrorKind::InvalidDigit)
);
assert_eq!(
NonZero::<u8>::from_str_radix("_", 2).err().map(|e| e.kind().clone()),
Some(IntErrorKind::InvalidDigit)
);
}

#[test]
fn test_from_str() {
assert_eq!("123".parse::<NonZero<u8>>(), Ok(NonZero::new(123).unwrap()));
Expand Down
2 changes: 1 addition & 1 deletion src/bootstrap/src/core/build_steps/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ macro_rules! tool_check_step {
const IS_HOST: bool = true;

fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.paths(&[ $path, $( $alt_path ),* ])
run.path($path) $( .path( $alt_path ) )*
}

fn is_default_step(_builder: &Builder<'_>) -> bool {
Expand Down
6 changes: 3 additions & 3 deletions src/bootstrap/src/core/build_steps/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3156,7 +3156,7 @@ impl Step for CrateRustdoc {
const IS_HOST: bool = true;

fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.paths(&["src/librustdoc", "src/tools/rustdoc"])
run.path("src/librustdoc").path("src/tools/rustdoc")
}

fn is_default_step(_builder: &Builder<'_>) -> bool {
Expand Down Expand Up @@ -3817,7 +3817,7 @@ impl Step for CodegenCranelift {
const IS_HOST: bool = true;

fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.paths(&["compiler/rustc_codegen_cranelift"])
run.path("compiler/rustc_codegen_cranelift")
}

fn is_default_step(_builder: &Builder<'_>) -> bool {
Expand Down Expand Up @@ -3938,7 +3938,7 @@ impl Step for CodegenGCC {
const IS_HOST: bool = true;

fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.paths(&["compiler/rustc_codegen_gcc"])
run.path("compiler/rustc_codegen_gcc")
}

fn is_default_step(_builder: &Builder<'_>) -> bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,5 @@ expression: bench
- Set({bench::compiler/rustc_windows_rc})
[Bench] test::CrateRustdoc
targets: [x86_64-unknown-linux-gnu]
- Set({bench::src/librustdoc, bench::src/tools/rustdoc})
- Set({bench::src/librustdoc})
- Set({bench::src/tools/rustdoc})
Loading
Loading