diff --git a/compiler/rustc_abi/src/callconv/reg.rs b/compiler/rustc_abi/src/callconv/reg.rs index 745a2ecfc6159..126f5bfa4ebf6 100644 --- a/compiler/rustc_abi/src/callconv/reg.rs +++ b/compiler/rustc_abi/src/callconv/reg.rs @@ -16,6 +16,15 @@ pub enum RegKind { }, } +impl RegKind { + pub fn from_primitive(primitive: Primitive) -> Self { + match primitive { + Primitive::Int(..) | Primitive::Pointer(_) => RegKind::Integer, + Primitive::Float(_) => RegKind::Float, + } + } +} + #[cfg_attr(feature = "nightly", derive(StableHash))] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct Reg { diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index 27ec34f519870..3b3a58697b205 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -116,6 +116,7 @@ pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug + std::fmt::Display { offset: Size, ) -> Option; fn is_adt(this: TyAndLayout<'a, Self>) -> bool; + fn is_enum(this: TyAndLayout<'a, Self>) -> bool; fn is_never(this: TyAndLayout<'a, Self>) -> bool; fn is_tuple(this: TyAndLayout<'a, Self>) -> bool; fn is_unit(this: TyAndLayout<'a, Self>) -> bool; @@ -200,6 +201,13 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { Ty::is_adt(self) } + pub fn is_enum(self) -> bool + where + Ty: TyAbiInterface<'a, C>, + { + Ty::is_enum(self) + } + pub fn is_never(self) -> bool where Ty: TyAbiInterface<'a, C>, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 46415cabc5963..267c36652656d 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1459,6 +1459,20 @@ impl<'tcx> InferCtxt<'tcx> { value.fold_with(&mut r) } + /// Where possible, replaces type/const/region variables in `value` with their final value. + /// If a type/const/region variable has not (yet) been unified, it is left as is. + /// + /// This is an idempotent operation that does not affect inference state in any way, + /// which means it's safe to call this function at will. + pub fn deeply_resolve_via_unification_table(&self, value: T) -> T + where + T: TypeFoldable>, + { + use rustc_middle::ty::InferCtxtLike; + #[allow(rustc::usage_of_type_ir_traits)] + InferCtxtLike::deeply_resolve_via_unification_table(self, value) + } + pub fn resolve_numeric_literals_with_default(&self, value: T) -> T where T: TypeFoldable>, diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index fdcd41a975246..14f534a3e7eb0 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -335,7 +335,6 @@ impl<'tcx> InferCtxt<'tcx> { /// right before lexical region resolution. #[instrument(level = "debug", skip(self, outlives_env))] pub fn process_registered_region_obligations(&self, outlives_env: &OutlivesEnvironment<'tcx>) { - use rustc_type_ir::InferCtxtLike; assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot"); if self.tcx.assumptions_on_binders() { diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 850464f9f4e47..904ae6e12ea9f 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -67,57 +67,6 @@ impl<'a, 'tcx> TypeFolder> for DeepResolverIgnoringRegions<'a, 'tcx } } -/// The region resolver resolves region variables to the variable with the -/// least variable id. It is used when normalizing projections to avoid -/// hitting the recursion limit by creating many versions of a predicate -/// for types that in the end have to unify. -/// -/// If you want to resolve type and const variables as well, call -/// [InferCtxt::deeply_resolve_ignoring_regions] first. -pub struct DeepRegionResolver<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, -} - -impl<'a, 'tcx> DeepRegionResolver<'a, 'tcx> { - pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { - DeepRegionResolver { infcx } - } -} - -impl<'a, 'tcx> TypeFolder> for DeepRegionResolver<'a, 'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !t.has_infer_regions() { - t // micro-optimize -- if there is nothing in this type that this fold affects... - } else { - t.super_fold_with(self) - } - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match r.kind() { - ty::ReVar(vid) => self - .infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .shallow_resolve_region_var(TypeFolder::cx(self), vid), - _ => r, - } - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - if !ct.has_infer_regions() { - ct // micro-optimize -- if there is nothing in this const that this fold affects... - } else { - ct.super_fold_with(self) - } - } -} - /////////////////////////////////////////////////////////////////////////// // FULL TYPE RESOLUTION diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index c816c5a34d3e8..61af59cc47aa0 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -667,12 +667,6 @@ impl<'a, 'tcx> Decodable> for SpanData { } } -impl<'a, 'tcx> Decodable> for &'tcx [(ty::Clause<'tcx>, Span)] { - fn decode(d: &mut MetadataDecodeContext<'a, 'tcx>) -> Self { - ty::codec::RefDecodable::decode(d) - } -} - impl Decodable for LazyValue { fn decode(decoder: &mut D) -> Self { decoder.read_lazy() diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index bfaef6157d02c..ee71d4d46e48f 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -2,9 +2,11 @@ //! `Copy` type, and any `!Copy` type explicitly listed below. use rustc_serialize::Decodable; +use rustc_span::{Span, Spanned}; +use crate::mono::MonoItem; use crate::ty::codec::{RefDecodable, TyDecoder}; -use crate::ty::{Ty, TyCtxt}; +use crate::ty::{self, Ty, TyCtxt}; // If a type `T` supported by the arena also needs to support decoding into `&'tcx T` // backed by an arena allocation (via `RefDecodable`), add it to the list in @@ -157,8 +159,10 @@ where D: TyDecoder<'tcx>, T: ArenaAllocatable<'tcx, C> + Decodable, { - let values: Vec = Decodable::decode(decoder); - decoder.interner().arena.alloc_from_iter(values) + // The decoder for slices must match the decoder for `Vec`, + // which is a `usize` length followed by that many `T`. + let len = decoder.read_usize(); + decoder.interner().arena.alloc_from_iter((0..len).map(|_| T::decode(decoder))) } macro_rules! impl_ref_decodable_into_arena { @@ -168,16 +172,16 @@ macro_rules! impl_ref_decodable_into_arena { )* ) => { $( - impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for $ty { + impl<'tcx> RefDecodable<'tcx> for $ty { #[inline] - fn decode(decoder: &mut D) -> &'tcx Self { + fn decode(decoder: &mut impl TyDecoder<'tcx>) -> &'tcx Self { decode_arena_allocatable(decoder) } } - impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [$ty] { + impl<'tcx> RefDecodable<'tcx> for [$ty] { #[inline] - fn decode(decoder: &mut D) -> &'tcx Self { + fn decode(decoder: &mut impl TyDecoder<'tcx>) -> &'tcx Self { decode_arena_allocatable_slice(decoder) } } @@ -190,9 +194,14 @@ macro_rules! impl_ref_decodable_into_arena { // // Types in this list must be `ArenaAllocatable`, either because they are `Copy` // or because they are listed in the `declare_arena!` invocation. +// +// Types in this list must also implement `Decodable` for all `D: TyDecoder<'tcx>`. impl_ref_decodable_into_arena! { // tidy-alphabetical-start (rustc_middle::middle::exported_symbols::ExportedSymbol<'tcx>, rustc_middle::middle::exported_symbols::SymbolExportInfo), + (ty::Clause<'tcx>, Span), + (ty::PolyTraitRef<'tcx>, Span), + Spanned>, rustc_ast::InlineAsmTemplatePiece, rustc_ast::tokenstream::TokenStream, rustc_data_structures::unord::UnordMap>>, diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 4e0a7b90ccab6..2ddcfc7ee01a5 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -688,88 +688,44 @@ impl<'a, 'tcx> BlobDecoder for CacheDecoder<'a, 'tcx> { } } -impl<'a, 'tcx> Decodable> for &'tcx UnordSet { - #[inline] - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { - RefDecodable::decode(d) - } -} - -impl<'a, 'tcx> Decodable> - for &'tcx UnordMap>> -{ - #[inline] - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { - RefDecodable::decode(d) - } -} - -impl<'a, 'tcx> Decodable> - for &'tcx IndexVec> -{ - #[inline] - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { - RefDecodable::decode(d) - } -} - -impl<'a, 'tcx> Decodable> for &'tcx [(ty::Clause<'tcx>, Span)] { - #[inline] - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { - RefDecodable::decode(d) - } -} - -impl<'a, 'tcx> Decodable> for &'tcx [rustc_ast::InlineAsmTemplatePiece] { - #[inline] - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { - RefDecodable::decode(d) - } -} - -impl<'a, 'tcx> Decodable> for &'tcx [Spanned>] { - #[inline] - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { - RefDecodable::decode(d) - } -} - -impl<'a, 'tcx> Decodable> - for &'tcx crate::traits::specialization_graph::Graph -{ - #[inline] - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { - RefDecodable::decode(d) - } -} - -impl<'a, 'tcx> Decodable> for &'tcx rustc_ast::tokenstream::TokenStream { - #[inline] - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { - RefDecodable::decode(d) - } -} - -macro_rules! impl_ref_decoder { - (<$tcx:tt> $($ty:ty,)*) => { - $(impl<'a, $tcx> Decodable> for &$tcx [$ty] { - #[inline] - fn decode(d: &mut CacheDecoder<'a, $tcx>) -> Self { - RefDecodable::decode(d) +/// Implements [`Decodable`] for `&'tcx T`, where [`T: RefDecodable`](RefDecodable). +/// +/// Due to orphan-rule restrictions, these foreign impls cannot use a blanket +/// [`D: TyDecoder`](TyDecoder), and must instead specify a specific decoder. +/// +/// For impls on types defined in `rustc_middle`, see +/// `impl_decodable_via_ref_decodable_for_local_type!` instead. +macro_rules! impl_decodable_via_ref_decodable_for_foreign_types { + ( + $( + &'tcx $T:ty, + )* + ) => { + $( + impl<'tcx> Decodable> for &'tcx $T { + fn decode(decoder: &mut CacheDecoder<'_, 'tcx>) -> Self { + RefDecodable::decode(decoder) + } } - })* - }; -} - -impl_ref_decoder! {<'tcx> - Span, - rustc_hir::Attribute, - rustc_span::Ident, - ty::Variance, - rustc_span::def_id::DefId, - rustc_span::def_id::LocalDefId, - (rustc_middle::middle::exported_symbols::ExportedSymbol<'tcx>, rustc_middle::middle::exported_symbols::SymbolExportInfo), - rustc_middle::middle::deduced_param_attrs::DeducedParamAttrs, + )* + } +} + +impl_decodable_via_ref_decodable_for_foreign_types! { + // tidy-alphabetical-start + &'tcx IndexVec>, + &'tcx UnordMap>>, + &'tcx UnordSet, + &'tcx [( + rustc_middle::middle::exported_symbols::ExportedSymbol<'tcx>, + rustc_middle::middle::exported_symbols::SymbolExportInfo, + )], + &'tcx [(ty::Clause<'tcx>, Span)], + &'tcx [DefId], + &'tcx [Spanned>], + &'tcx [ty::Variance], + &'tcx rustc_ast::tokenstream::TokenStream, + // tidy-alphabetical-end } //- ENCODING ------------------------------------------------------------------- diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 537015a2560dd..1a63c05e06ced 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -8,20 +8,19 @@ use std::hash::Hash; use std::intrinsics; -use std::marker::{DiscriminantKind, PointeeSized}; +use std::marker::DiscriminantKind; -use rustc_abi::FieldIdx; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def_id::LocalDefId; -use rustc_middle::ty::Const; use rustc_serialize::{Decodable, Encodable}; -use rustc_span::{Span, SpanDecoder, SpanEncoder, Spanned}; +use rustc_span::{SpanDecoder, SpanEncoder}; +pub use self::ref_decodable::RefDecodable; use crate::infer::canonical::{CanonicalVarKind, CanonicalVarKinds}; +use crate::mir; use crate::mir::interpret::{AllocId, ConstAllocation, CtfeProvenance}; -use crate::mono::MonoItem; use crate::ty::{self, AdtDef, GenericArgsRef, Ty, TyCtxt}; -use crate::{mir, traits}; + +mod ref_decodable; /// The shorthand encoding uses an enum's variant index `usize` /// and is offset by this value so it never matches a real variant. @@ -84,23 +83,6 @@ impl<'tcx, E: TyEncoder<'tcx>> EncodableWithShorthand<'tcx, E> for ty::Predicate } } -/// Trait for decoding to a reference. -/// -/// This is a separate trait from `Decodable` so that we can implement it for -/// upstream types, such as `FxHashSet`. -/// -/// The `TyDecodable` derive macro will use this trait for fields that are -/// references (and don't use a type alias to hide that). -/// -/// `Decodable` can still be implemented in cases where `Decodable` is required -/// by a trait bound. -/// -/// Implementations of this trait will typically allocate into an arena or interner, -/// e.g. see `impl_ref_decodable_into_arena!`. -pub trait RefDecodable<'tcx, D: TyDecoder<'tcx>>: PointeeSized { - fn decode(d: &mut D) -> &'tcx Self; -} - /// Encode the given value or a previously cached shorthand. pub fn encode_with_shorthand<'tcx, E, T, M>(encoder: &mut E, value: &T, cache: M) where @@ -310,36 +292,6 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::ParamEnv<'tcx> { } } -macro_rules! impl_decodable_via_ref { - ($($t:ty,)+) => { - $(impl<'tcx, D: TyDecoder<'tcx>> Decodable for $t { - fn decode(decoder: &mut D) -> Self { - RefDecodable::decode(decoder) - } - })* - } -} - -impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> { - fn decode(decoder: &mut D) -> &'tcx Self { - let len = decoder.read_usize(); - decoder - .interner() - .mk_type_list_from_iter((0..len).map::, _>(|_| Decodable::decode(decoder))) - } -} - -impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> - for ty::List> -{ - fn decode(decoder: &mut D) -> &'tcx Self { - let len = decoder.read_usize(); - decoder.interner().mk_poly_existential_predicates_from_iter( - (0..len).map::, _>(|_| Decodable::decode(decoder)), - ) - } -} - impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Const<'tcx> { fn decode(decoder: &mut D) -> Self { let kind: ty::ConstKind<'tcx> = Decodable::decode(decoder); @@ -371,107 +323,6 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable for AdtDef<'tcx> { } } -impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [(ty::Clause<'tcx>, Span)] { - fn decode(decoder: &mut D) -> &'tcx Self { - decoder - .interner() - .arena - .alloc_from_iter((0..decoder.read_usize()).map(|_| Decodable::decode(decoder))) - } -} - -impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [(ty::PolyTraitRef<'tcx>, Span)] { - fn decode(decoder: &mut D) -> &'tcx Self { - decoder - .interner() - .arena - .alloc_from_iter((0..decoder.read_usize()).map(|_| Decodable::decode(decoder))) - } -} - -impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [Spanned>] { - fn decode(decoder: &mut D) -> &'tcx Self { - decoder - .interner() - .arena - .alloc_from_iter((0..decoder.read_usize()).map(|_| Decodable::decode(decoder))) - } -} - -impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> { - fn decode(decoder: &mut D) -> &'tcx Self { - let len = decoder.read_usize(); - decoder.interner().mk_bound_variable_kinds_from_iter( - (0..len).map::, _>(|_| Decodable::decode(decoder)), - ) - } -} - -impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> { - fn decode(decoder: &mut D) -> &'tcx Self { - let len = decoder.read_usize(); - decoder.interner().mk_patterns_from_iter( - (0..len).map::, _>(|_| Decodable::decode(decoder)), - ) - } -} - -impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> { - fn decode(decoder: &mut D) -> &'tcx Self { - let len = decoder.read_usize(); - decoder.interner().mk_const_list_from_iter( - (0..len).map::, _>(|_| Decodable::decode(decoder)), - ) - } -} - -impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> - for ty::ListWithCachedTypeInfo> -{ - fn decode(decoder: &mut D) -> &'tcx Self { - let len = decoder.read_usize(); - decoder.interner().mk_clauses_from_iter( - (0..len).map::, _>(|_| Decodable::decode(decoder)), - ) - } -} - -impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List { - fn decode(decoder: &mut D) -> &'tcx Self { - let len = decoder.read_usize(); - decoder - .interner() - .mk_fields_from_iter((0..len).map::(|_| Decodable::decode(decoder))) - } -} - -impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List { - fn decode(decoder: &mut D) -> &'tcx Self { - let len = decoder.read_usize(); - decoder.interner().mk_local_def_ids_from_iter( - (0..len).map::(|_| Decodable::decode(decoder)), - ) - } -} - -impl<'tcx, D: TyDecoder<'tcx>> Decodable for &'tcx ty::List { - fn decode(d: &mut D) -> Self { - RefDecodable::decode(d) - } -} - -impl_decodable_via_ref! { - &'tcx ty::TypeckResults<'tcx>, - &'tcx ty::List>, - &'tcx ty::List>, - &'tcx traits::ImplSource<'tcx, ()>, - &'tcx mir::Body<'tcx>, - &'tcx ty::List>, - &'tcx ty::List>, - &'tcx ty::ListWithCachedTypeInfo>, - &'tcx ty::List>, -} - #[macro_export] macro_rules! __impl_decoder_methods { ($($name:ident -> $ty:ty;)*) => { diff --git a/compiler/rustc_middle/src/ty/codec/ref_decodable.rs b/compiler/rustc_middle/src/ty/codec/ref_decodable.rs new file mode 100644 index 0000000000000..9f37f9687da51 --- /dev/null +++ b/compiler/rustc_middle/src/ty/codec/ref_decodable.rs @@ -0,0 +1,124 @@ +use std::marker::PointeeSized; + +use rustc_serialize::Decodable; + +use crate::ty::codec::TyDecoder; +use crate::ty::{self, Ty}; +use crate::{mir, traits}; + +/// Trait for decoding to a reference. +/// +/// This is a separate trait from [`Decodable`] so that we can easily implement it for +/// upstream types, such as `FxHashSet`. +/// +/// The [`TyDecodable`](rustc_macros::TyDecodable) derive macro will use this +/// trait for fields that are references (and don't use a type alias to hide that). +/// +/// [`Decodable`] can still be implemented in cases where `Decodable` is required +/// by a trait bound; see `impl_decodable_via_ref_decodable_for_local_types!` for examples. +/// +/// Implementations of this trait will typically allocate into an arena or interner, +/// e.g. see `impl_ref_decodable_into_arena!` in [`rustc_middle::arena`]. +#[diagnostic::on_unimplemented( + note = "consider adding `{Self}` to the list in `impl_ref_decodable_into_arena!`" +)] +pub trait RefDecodable<'tcx>: PointeeSized { + fn decode(d: &mut impl TyDecoder<'tcx>) -> &'tcx Self; +} + +impl<'tcx> RefDecodable<'tcx> for ty::List> { + fn decode(decoder: &mut impl TyDecoder<'tcx>) -> &'tcx Self { + let len = decoder.read_usize(); + decoder + .interner() + .mk_type_list_from_iter((0..len).map::, _>(|_| Decodable::decode(decoder))) + } +} + +impl<'tcx> RefDecodable<'tcx> for ty::List> { + fn decode(decoder: &mut impl TyDecoder<'tcx>) -> &'tcx Self { + let len = decoder.read_usize(); + decoder.interner().mk_poly_existential_predicates_from_iter( + (0..len).map::, _>(|_| Decodable::decode(decoder)), + ) + } +} + +impl<'tcx> RefDecodable<'tcx> for ty::List> { + fn decode(decoder: &mut impl TyDecoder<'tcx>) -> &'tcx Self { + let len = decoder.read_usize(); + decoder.interner().mk_bound_variable_kinds_from_iter( + (0..len).map::, _>(|_| Decodable::decode(decoder)), + ) + } +} + +impl<'tcx> RefDecodable<'tcx> for ty::List> { + fn decode(decoder: &mut impl TyDecoder<'tcx>) -> &'tcx Self { + let len = decoder.read_usize(); + decoder.interner().mk_patterns_from_iter( + (0..len).map::, _>(|_| Decodable::decode(decoder)), + ) + } +} + +impl<'tcx> RefDecodable<'tcx> for ty::List> { + fn decode(decoder: &mut impl TyDecoder<'tcx>) -> &'tcx Self { + let len = decoder.read_usize(); + decoder.interner().mk_const_list_from_iter( + (0..len).map::, _>(|_| Decodable::decode(decoder)), + ) + } +} + +impl<'tcx> RefDecodable<'tcx> for ty::ListWithCachedTypeInfo> { + fn decode(decoder: &mut impl TyDecoder<'tcx>) -> &'tcx Self { + let len = decoder.read_usize(); + decoder.interner().mk_clauses_from_iter( + (0..len).map::, _>(|_| Decodable::decode(decoder)), + ) + } +} + +/// Implements [`Decodable`] for `&'tcx T`, where [`T: RefDecodable`](RefDecodable) +/// and T is defined in this crate (`rustc_middle`). +/// +/// For locally-defined types, we can use a blanket impl over any [`D: TyDecoder`](TyDecoder). +/// +/// ## Note on implementing [`Decodable`] for references to non-local types +/// +/// For references to types not defined in this crate, including slices/tuples/collections +/// of local types, [`Decodable`] cannot use a blanket impl and must be implemented for +/// specific decoders instead. +/// +/// See invocations of `impl_decodable_via_ref_decodable_for_foreign_types!` for examples. +macro_rules! impl_decodable_via_ref_decodable_for_local_types { + ( + $( + &'tcx $T:ty, + )* + ) => { + $( + impl<'tcx, D: TyDecoder<'tcx>> Decodable for &'tcx $T { + fn decode(decoder: &mut D) -> Self { + RefDecodable::decode(decoder) + } + } + )* + } +} + +impl_decodable_via_ref_decodable_for_local_types! { + // tidy-alphabetical-start + &'tcx mir::Body<'tcx>, + &'tcx traits::ImplSource<'tcx, ()>, + &'tcx traits::specialization_graph::Graph, + &'tcx ty::List>, + &'tcx ty::List>, + &'tcx ty::List>, + &'tcx ty::List>, + &'tcx ty::List>, + &'tcx ty::ListWithCachedTypeInfo>, + &'tcx ty::TypeckResults<'tcx>, + // tidy-alphabetical-end +} diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 3b2cd3f603bbc..22fbb306849da 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1196,6 +1196,10 @@ where matches!(this.ty.kind(), ty::Adt(..)) } + fn is_enum(this: TyAndLayout<'tcx>) -> bool { + matches!(this.ty.kind(), ty::Adt(def, _) if def.is_enum()) + } + fn is_never(this: TyAndLayout<'tcx>) -> bool { matches!(this.ty.kind(), ty::Never) } diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 910f4a5745a7d..b760ed98c7111 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -8,7 +8,7 @@ use crate::compiler_interface::with; use crate::mir::FieldIdx; use crate::target::{MachineInfo, MachineSize as Size}; use crate::ty::{Align, Ty, VariantIdx, index_impl}; -use crate::{Error, Opaque, ThreadLocalIndex, error}; +use crate::{Error, ThreadLocalIndex, error}; /// A function ABI definition. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] @@ -19,9 +19,11 @@ pub struct FnAbi { /// The expected return type. pub ret: ArgAbi, - /// The count of non-variadic arguments. + /// The count of declared arguments (excluding variadic and implicit arguments). /// - /// Should only be different from `args.len()` when a function is a C variadic function. + /// This may be less than `args.len()` for C variadic functions (which have + /// additional variadic arguments) or `#[track_caller]` functions (which have + /// an implicit caller location argument). pub fixed_count: u32, /// The ABI convention. @@ -40,24 +42,166 @@ pub struct ArgAbi { } /// How a function argument should be passed in to the target function. +/// +/// The pass mode is determined by the platform's calling convention and the +/// argument's type layout. The same Rust type may use different pass modes +/// on different targets or when register availability changes. +/// +/// Note: for the Rust ABI, pass modes may not correspond to any valid C +/// calling convention (e.g., using more return registers than the platform +/// C ABI allows). Further processing may be needed depending on the target. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum PassMode { /// Ignore the argument. /// - /// The argument is either uninhabited or a ZST. + /// The argument is either uninhabited or a ZST (zero-sized type). Ignore, - /// Pass the argument directly. + /// Pass the argument directly in a single register. + /// + /// Used for primitive types and small values that fit in one register. + Direct(ArgAttributes), + /// Pass the argument directly in two registers. /// - /// The argument has a layout abi of `Scalar` or `Vector`. - Direct(Opaque), - /// Pass a pair's elements directly in two arguments. + /// Used for types represented as a pair of values (e.g., a fat pointer + /// consisting of a data pointer and a length/vtable pointer). + Pair(ArgAttributes, ArgAttributes), + /// Pass the argument after reinterpreting it as a different register layout. /// - /// The argument has a layout abi of `ScalarPair`. - Pair(Opaque, Opaque), - /// Pass the argument after casting it. - Cast { pad_i32_count: u8, cast: Opaque }, - /// Pass the argument indirectly via a hidden pointer. - Indirect { attrs: Opaque, meta_attrs: Opaque, on_stack: bool }, + /// Used for aggregates (structs, tuples) that the platform ABI passes in + /// registers. The argument's bytes are reinterpreted as the register + /// sequence described by [`CastTarget`]. See its documentation for details. + Cast { pad_i32_count: u8, cast: CastTarget }, + /// Pass the argument indirectly via a pointer. + /// + /// The caller places the value in memory and passes a pointer to it. + /// When `on_stack` is true, the value is placed at a fixed stack offset + /// rather than passed as a regular pointer argument. + Indirect { + attrs: ArgAttributes, + /// Attributes for the metadata pointer (vtable or length) of unsized arguments. + /// Only present for unsized types (e.g., `dyn Trait`, `[T]`). + meta_attrs: Option, + on_stack: bool, + }, +} + +/// Attributes of a function argument that affect its ABI. +/// +/// Not all internal compiler attributes are exposed here, as some are +/// LLVM-specific optimization hints. The internal representation is kept +/// private so it can be expanded in the future. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct ArgAttributes { + pub(crate) arg_ext: ArgExtension, + pub(crate) pointee_size: Size, + pub(crate) pointee_align: Option, +} + +impl ArgAttributes { + /// Return how this argument should be extended when passed in a register. + /// + /// Relevant for integer arguments smaller than the register width. + pub fn arg_extension(&self) -> ArgExtension { + self.arg_ext + } + + /// Return the minimum alignment of the pointee, if applicable. + /// + /// This is relevant for `PassMode::Indirect` arguments where the pointer + /// must satisfy a particular alignment. + pub fn pointee_align(&self) -> Option { + self.pointee_align + } + + /// Return the minimum dereferenceable size of the pointee, if known. + pub fn pointee_size(&self) -> Size { + self.pointee_size + } +} + +/// How a small integer argument should be extended to fill a register. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub enum ArgExtension { + /// No extension required. + None, + /// Zero-extend to the register width. + Zext, + /// Sign-extend to the register width. + Sext, +} + +/// Describes the ABI type that an argument is transmuted to for `PassMode::Cast`. +/// +/// When an argument is "cast," its raw bytes are reinterpreted as a sequence of +/// register-sized values for passing. This struct describes that target layout: +/// +/// 1. The `prefix` registers are laid out first, like fields of a `repr(C)` struct +/// (i.e., with alignment padding between them). +/// 2. After the prefix, `rest.unit` is repeated enough times to cover `rest.total`, +/// starting at `rest_offset` (or immediately after the prefix if `None`). +/// +/// For example, on x86_64 a `struct { i32, f64 }` might be cast to a prefix of +/// `[Reg::i64()]` followed by a rest of `Reg::f64()` — placing the first 8 bytes +/// in an integer register and the second 8 bytes in a floating-point register. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct CastTarget { + /// Leading registers of potentially different types, laid out with `repr(C)` padding. + pub prefix: Vec, + /// The byte offset where `rest` begins, if explicitly set. + /// When `None`, `rest` starts immediately after the prefix. + pub rest_offset: Option, + /// The repeated trailing register type filling the remainder of the value. + pub rest: Uniform, +} + +impl CastTarget { + /// Return the total size of the ABI type this argument is cast to. + pub fn size(&self) -> Size { + let prefix_size: usize = self.prefix.iter().map(|r| r.size.bits()).sum(); + Size::from_bits(prefix_size + self.rest.total.bits()) + } +} + +/// A sequence of registers of the same kind used to pass an argument. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct Uniform { + /// The type of register used. + pub unit: Reg, + /// The total size of the argument, which can be: + /// * equal to `unit.size` (one scalar/vector), + /// * a multiple of `unit.size` (an array of scalar/vectors), + /// * if `unit.kind` is `Integer`, the last element can be shorter, i.e., `{ i64, i64, i32 }` + /// for 64-bit integers with a total size of 20 bytes. When the argument is actually passed, + /// this size will be rounded up to the nearest multiple of `unit.size`. + pub total: Size, + /// Whether the argument is consecutive: either all values are passed in registers, or all on + /// the stack with no additional padding between elements. + pub is_consecutive: bool, +} + +impl Uniform { + /// Return the number of registers needed to cover `total`. + pub fn reg_count(&self) -> usize { + if self.unit.size.bits() == 0 { + return 0; + } + (self.total.bits() + self.unit.size.bits() - 1) / self.unit.size.bits() + } +} + +/// A register type used in ABI calling conventions. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct Reg { + pub kind: RegKind, + pub size: Size, +} + +/// The kind of a register used in calling conventions. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] +pub enum RegKind { + Integer, + Float, + Vector, } /// The layout of a type, alongside the type itself. @@ -67,7 +211,7 @@ pub struct TyAndLayout { pub layout: Layout, } -/// The layout of a type in memory. +/// The layout of a type, including its size, alignment, field offsets, and backend representation. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub struct LayoutShape { /// The fields location within the layout @@ -81,8 +225,8 @@ pub struct LayoutShape { /// must be taken into account. pub variants: VariantsShape, - /// The `abi` defines how this data is passed between functions. - pub abi: ValueAbi, + /// A hint for how backends should represent this type: as a scalar, vector, or aggregate. + pub value_repr: ValueRepr, /// The ABI mandated alignment in bytes. pub abi_align: Align, @@ -95,12 +239,12 @@ impl LayoutShape { /// Returns `true` if the layout corresponds to an unsized type. #[inline] pub fn is_unsized(&self) -> bool { - self.abi.is_unsized() + self.value_repr.is_unsized() } #[inline] pub fn is_sized(&self) -> bool { - !self.abi.is_unsized() + !self.value_repr.is_unsized() } /// Returns `true` if the type is sized and a 1-ZST (meaning it has size 0 and alignment 1). @@ -119,7 +263,7 @@ impl Layout { } } -/// Describes how the fields of a type are shaped in memory. +/// Describes the number and position of fields within a type's layout. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum FieldsShape { /// Scalar primitives and `!`, which never have fields. @@ -232,49 +376,54 @@ pub enum TagEncoding { }, } -/// How many scalable vectors are in a `ValueAbi::ScalableVector`? +/// The number of scalable vectors in a [`ValueRepr::ScalableVector`]. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub struct NumScalableVectors(pub(crate) u8); -/// Describes how values of the type are passed by target ABIs, -/// in terms of categories of C types there are ABI rules for. +/// A hint for how backends should represent values of this type. +/// +/// Distinguishes between types representable as scalars, pairs of scalars, +/// SIMD vectors, or aggregates. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub enum ValueAbi { +pub enum ValueRepr { Scalar(Scalar), ScalarPair { a: Scalar, b: Scalar, b_offset: Size, }, + /// A fixed-length SIMD vector. Vector { element: Scalar, count: u64, }, + /// A scalable SIMD vector (e.g., ARM SVE). ScalableVector { element: Scalar, count: u64, number_of_vectors: NumScalableVectors, }, + /// The type is not representable as a scalar or vector (e.g., aggregates, unsized types). Aggregate { /// If true, the size is exact, otherwise it's only a lower bound. sized: bool, }, } -impl ValueAbi { +impl ValueRepr { /// Returns `true` if the layout corresponds to an unsized type. pub fn is_unsized(&self) -> bool { match *self { - ValueAbi::Scalar(_) - | ValueAbi::ScalarPair { .. } - | ValueAbi::Vector { .. } + ValueRepr::Scalar(_) + | ValueRepr::ScalarPair { .. } + | ValueRepr::Vector { .. } // FIXME(rustc_scalable_vector): Scalable vectors are `Sized` while the // `sized_hierarchy` feature is not yet fully implemented. After `sized_hierarchy` is // fully implemented, scalable vectors will remain `Sized`, they just won't be // `const Sized` - whether `is_unsized` continues to return `false` at that point will // need to be revisited and will depend on what `is_unsized` is used for. - | ValueAbi::ScalableVector { .. } => false, - ValueAbi::Aggregate { sized } => !sized, + | ValueRepr::ScalableVector { .. } => false, + ValueRepr::Aggregate { sized } => !sized, } } } @@ -291,9 +440,8 @@ pub enum Scalar { }, Union { /// Unions never have niches, so there is no `valid_range`. - /// Even for unions, we need to use the correct registers for the kind of - /// values inside the union, so we keep the `Primitive` type around. - /// It is also used to compute the size of the scalar. + /// The `Primitive` type is kept to inform the backend representation + /// and to compute the size of the scalar. value: Primitive, }, } @@ -309,23 +457,18 @@ impl Scalar { } } -/// Fundamental unit of memory access and layout. +/// A primitive scalar type: integer, float, or pointer. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize)] pub enum Primitive { - /// The `bool` is the signedness of the `Integer` type. + /// An integer type with a given length and signedness. /// - /// One would think we would not care about such details this low down, - /// but some ABIs are described in terms of C types and ISAs where the - /// integer arithmetic is done on {sign,zero}-extended registers, e.g. - /// a negative integer passed by zero-extension will appear positive in - /// the callee, and most operations on it will produce the wrong values. - Int { - length: IntegerLength, - signed: bool, - }, - Float { - length: FloatLength, - }, + /// Signedness matters because some calling conventions require small integers + /// to be sign-extended or zero-extended when passed, and using the wrong + /// extension produces incorrect values in the callee. + Int { length: IntegerLength, signed: bool }, + /// A floating-point type with a given length. + Float { length: FloatLength }, + /// A pointer in the given address space. Pointer(AddressSpace), } diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index 4bb00b4c04394..766c522958db7 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -8,17 +8,17 @@ use rustc_public_bridge::Tables; use rustc_public_bridge::context::CompilerCtxt; use rustc_target::callconv; +use crate::IndexedVal; use crate::abi::{ - AddressSpace, ArgAbi, CallConvention, FieldsShape, FloatLength, FnAbi, IntegerLength, - IntegerType, Layout, LayoutShape, NumScalableVectors, PassMode, Primitive, ReprFlags, - ReprOptions, Scalar, TagEncoding, TyAndLayout, ValueAbi, VariantFields, VariantsShape, - WrappingRange, + AddressSpace, ArgAbi, ArgAttributes, ArgExtension, CallConvention, CastTarget, FieldsShape, + FloatLength, FnAbi, IntegerLength, IntegerType, Layout, LayoutShape, NumScalableVectors, + PassMode, Primitive, Reg, RegKind, ReprFlags, ReprOptions, Scalar, TagEncoding, TyAndLayout, + Uniform, ValueRepr, VariantFields, VariantsShape, WrappingRange, }; use crate::compiler_interface::BridgeTys; use crate::target::MachineSize as Size; use crate::ty::{Align, VariantIdx}; use crate::unstable::Stable; -use crate::{IndexedVal, opaque}; impl<'tcx> Stable<'tcx> for rustc_abi::VariantIdx { type T = VariantIdx; @@ -73,7 +73,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::LayoutData Stable<'tcx> for CanonAbi { impl<'tcx> Stable<'tcx> for callconv::PassMode { type T = PassMode; - fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { + fn stable<'cx>( + &self, + tables: &mut Tables<'cx, BridgeTys>, + cx: &CompilerCtxt<'cx, BridgeTys>, + ) -> Self::T { match self { callconv::PassMode::Ignore => PassMode::Ignore, - callconv::PassMode::Direct(attr) => PassMode::Direct(opaque(attr)), + callconv::PassMode::Direct(attr) => PassMode::Direct(attr.stable(tables, cx)), callconv::PassMode::Pair(first, second) => { - PassMode::Pair(opaque(first), opaque(second)) + PassMode::Pair(first.stable(tables, cx), second.stable(tables, cx)) } callconv::PassMode::Cast { pad_i32_count, cast } => { - PassMode::Cast { pad_i32_count: *pad_i32_count, cast: opaque(cast) } + PassMode::Cast { pad_i32_count: *pad_i32_count, cast: cast.stable(tables, cx) } } callconv::PassMode::Indirect { attrs, meta_attrs, on_stack } => PassMode::Indirect { - attrs: opaque(attrs), - meta_attrs: opaque(meta_attrs), + attrs: attrs.stable(tables, cx), + meta_attrs: meta_attrs.map(|a| a.stable(tables, cx)), on_stack: *on_stack, }, } } } +impl<'tcx> Stable<'tcx> for callconv::CastTarget { + type T = CastTarget; + + fn stable<'cx>( + &self, + tables: &mut Tables<'cx, BridgeTys>, + cx: &CompilerCtxt<'cx, BridgeTys>, + ) -> Self::T { + CastTarget { + prefix: self.prefix.iter().map(|reg| reg.stable(tables, cx)).collect(), + rest_offset: self.rest_offset.map(|offset| Size::from_bits(offset.bits_usize())), + rest: self.rest.stable(tables, cx), + } + } +} + +impl<'tcx> Stable<'tcx> for callconv::Uniform { + type T = Uniform; + + fn stable<'cx>( + &self, + tables: &mut Tables<'cx, BridgeTys>, + cx: &CompilerCtxt<'cx, BridgeTys>, + ) -> Self::T { + Uniform { + unit: self.unit.stable(tables, cx), + total: Size::from_bits(self.total.bits_usize()), + is_consecutive: self.is_consecutive, + } + } +} + +impl<'tcx> Stable<'tcx> for rustc_abi::Reg { + type T = Reg; + + fn stable<'cx>( + &self, + _: &mut Tables<'cx, BridgeTys>, + _: &CompilerCtxt<'cx, BridgeTys>, + ) -> Self::T { + Reg { + kind: match self.kind { + rustc_abi::RegKind::Integer => RegKind::Integer, + rustc_abi::RegKind::Float => RegKind::Float, + rustc_abi::RegKind::Vector { .. } => RegKind::Vector, + }, + size: Size::from_bits(self.size.bits_usize()), + } + } +} + +impl<'tcx> Stable<'tcx> for callconv::ArgAttributes { + type T = ArgAttributes; + + fn stable<'cx>( + &self, + _: &mut Tables<'cx, BridgeTys>, + _: &CompilerCtxt<'cx, BridgeTys>, + ) -> Self::T { + ArgAttributes { + arg_ext: match self.arg_ext { + callconv::ArgExtension::None => ArgExtension::None, + callconv::ArgExtension::Zext => ArgExtension::Zext, + callconv::ArgExtension::Sext => ArgExtension::Sext, + }, + pointee_size: Size::from_bits(self.pointee_size.bits_usize()), + pointee_align: self.pointee_align.map(|a| a.bytes()), + } + } +} + impl<'tcx> Stable<'tcx> for rustc_abi::FieldsShape { type T = FieldsShape; @@ -274,7 +349,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::BackendLaneCount { } impl<'tcx> Stable<'tcx> for rustc_abi::BackendRepr { - type T = ValueAbi; + type T = ValueRepr; fn stable<'cx>( &self, @@ -282,26 +357,26 @@ impl<'tcx> Stable<'tcx> for rustc_abi::BackendRepr { cx: &CompilerCtxt<'cx, BridgeTys>, ) -> Self::T { match *self { - rustc_abi::BackendRepr::Scalar(scalar) => ValueAbi::Scalar(scalar.stable(tables, cx)), + rustc_abi::BackendRepr::Scalar(scalar) => ValueRepr::Scalar(scalar.stable(tables, cx)), rustc_abi::BackendRepr::ScalarPair { a: first, b: second, b_offset: second_offset } => { - ValueAbi::ScalarPair { + ValueRepr::ScalarPair { a: first.stable(tables, cx), b: second.stable(tables, cx), b_offset: second_offset.stable(tables, cx), } } - rustc_abi::BackendRepr::SimdVector { element, count } => ValueAbi::Vector { + rustc_abi::BackendRepr::SimdVector { element, count } => ValueRepr::Vector { element: element.stable(tables, cx), count: count.stable(tables, cx), }, rustc_abi::BackendRepr::SimdScalableVector { element, count, number_of_vectors } => { - ValueAbi::ScalableVector { + ValueRepr::ScalableVector { element: element.stable(tables, cx), count: count.stable(tables, cx), number_of_vectors: number_of_vectors.stable(tables, cx), } } - rustc_abi::BackendRepr::Memory { sized } => ValueAbi::Aggregate { sized }, + rustc_abi::BackendRepr::Memory { sized } => ValueRepr::Aggregate { sized }, } } } diff --git a/compiler/rustc_target/src/callconv/wasm.rs b/compiler/rustc_target/src/callconv/wasm.rs index b84bcd5903257..3706caa6b6f44 100644 --- a/compiler/rustc_target/src/callconv/wasm.rs +++ b/compiler/rustc_target/src/callconv/wasm.rs @@ -1,6 +1,6 @@ use rustc_abi::{ - BackendRepr, Float, HasDataLayout, Integer, Primitive, Reg, RegKind, TyAbiInterface, - TyAndLayout, + BackendRepr, Float, HasDataLayout, Integer, Primitive, Reg, RegKind, TagEncoding, + TyAbiInterface, TyAndLayout, Variants, }; use crate::callconv::{ArgAbi, FnAbi}; @@ -11,15 +11,27 @@ where C: HasDataLayout, { // The base case: a single scalar is a singleton scalar. - if !layout.is_aggregate() { + if !(layout.is_aggregate() || layout.peel_transparent_wrappers(cx).is_enum()) { let BackendRepr::Scalar(scalar) = layout.backend_repr else { return None; }; - let kind = match scalar.primitive() { - Primitive::Int(..) | Primitive::Pointer(_) => RegKind::Integer, - Primitive::Float(_) => RegKind::Float, - }; - return Some(Reg { kind, size: layout.size }); + return Some(Reg { kind: RegKind::from_primitive(scalar.primitive()), size: layout.size }); + } + + // Enums that are represented as scalars need special care: + // + // - `#[repr(u8)] enum { A, B }` is a singleton scalar + // - `#[repr(u8)] enum { A(()), B }` is not + // + // To rust their representation is the same, but clang looks at the syntax. + // Niches have custom behavior too, so `Option<&i32>` is a singleton scalar. + if let Variants::Multiple { tag, tag_encoding: TagEncoding::Direct, variants, .. } = + &layout.variants + { + if variants.iter().all(|x| x.field_offsets.is_empty()) { + return Some(Reg { kind: RegKind::from_primitive(tag.primitive()), size: layout.size }); + } + return None; } let mut found = None; @@ -39,17 +51,32 @@ where found.filter(|scalar| scalar.size == layout.size) } -fn unwrap_trivial_aggregate<'a, Ty, C>(cx: &C, val: &mut ArgAbi<'a, Ty>) -> bool +/// Return whether the value should be passed as an aggregate (i.e. indirectly). +/// +/// - Enums with integer layout and variants with only zst members are passed as aggregates +/// - Aggregate wrappers around a single scalar are passed as scalars +fn is_aggregate_for_abi<'a, Ty, C>(cx: &C, val: &mut ArgAbi<'a, Ty>) -> bool where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - let Some(scalar) = singleton_scalar(cx, val.layout) else { + // An enum that is represented as an integer is not an aggregate to rust, but may still + // need to be passed as one if its variants have any (even ZST) fields. + if !(val.layout.is_aggregate() || val.layout.peel_transparent_wrappers(cx).is_enum()) { return false; + } + + let Some(scalar) = singleton_scalar(cx, val.layout) else { + return true; }; + // This is an enum with integer layout, no need to cast. + if !val.layout.is_aggregate() { + return false; + } + val.cast_to(scalar); - true + false } fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) @@ -57,19 +84,20 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - ret.extend_integer_width_to(32); - if ret.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, ret) { + // `long double`, `__int128_t` and `__uint128_t` use an indirect return + if let BackendRepr::Scalar(scalar) = ret.layout.backend_repr + && matches!( + scalar.primitive(), + Primitive::Int(Integer::I128, _) | Primitive::Float(Float::F128) + ) + { ret.make_indirect(); + return; } - // `long double`, `__int128_t` and `__uint128_t` use an indirect return - if let BackendRepr::Scalar(scalar) = ret.layout.backend_repr { - match scalar.primitive() { - Primitive::Int(Integer::I128, _) | Primitive::Float(Float::F128) => { - ret.make_indirect(); - } - _ => {} - } + ret.extend_integer_width_to(32); + if is_aggregate_for_abi(cx, ret) { + ret.make_indirect(); } } @@ -87,7 +115,7 @@ where return; } arg.extend_integer_width_to(32); - if arg.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, arg) { + if is_aggregate_for_abi(cx, arg) { arg.make_indirect(); } } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 80fec03698519..63b65cca16c55 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -142,8 +142,6 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { fields(goal = ?self.goal.goal, steps = ?self.steps) )] pub fn instantiate_impl_args(&self, span: Span) -> ty::GenericArgsRef<'tcx> { - use rustc_middle::ty::InferCtxtLike; - let infcx = self.goal.infcx; let mut orig_values = self.goal.orig_values.clone(); @@ -166,9 +164,6 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { self.final_state, ); - // We *want* this folder to live in `rustc_type_ir`. Our best way to call into it is - // through `InferCtxtLike` and it is not defined as an inherent method on `InferCtxt`. - #[allow(rustc::usage_of_type_ir_traits)] return infcx.deeply_resolve_via_unification_table(impl_args); } inspect::ProbeStep::AddGoal(..) => {} @@ -341,8 +336,6 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { root: inspect::GoalEvaluation>, source: GoalSource, ) -> Self { - use rustc_middle::ty::InferCtxtLike; - let infcx = <&SolverDelegate<'tcx>>::from(infcx); let prev_universe = infcx.universe(); @@ -362,9 +355,6 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { depth, orig_values, prev_universe, - // We *want* this folder to live in `rustc_type_ir`. Our best way to call into it is - // through `InferCtxtLike` and it is not defined as an inherent method on `InferCtxt`. - #[allow(rustc::usage_of_type_ir_traits)] goal: infcx.deeply_resolve_via_unification_table(uncanonicalized_goal), result, final_revision, diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 8a464bfcf1024..fad89d17cbf44 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -1,11 +1,10 @@ use rustc_infer::infer::InferOk; use rustc_infer::infer::canonical::QueryRegionConstraint; -use rustc_infer::infer::resolve::DeepRegionResolver; use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_macros::extension; use rustc_middle::infer::canonical::{OriginalQueryValues, QueryRegionConstraints}; pub use rustc_middle::traits::query::OutlivesBound; -use rustc_middle::ty::{self, ParamEnv, Ty, TypeFolder, TypeVisitableExt}; +use rustc_middle::ty::{self, ParamEnv, Ty, TypeVisitableExt}; use rustc_span::def_id::LocalDefId; use tracing::instrument; @@ -39,8 +38,7 @@ fn implied_outlives_bounds<'a, 'tcx>( ty: Ty<'tcx>, disable_implied_bounds_hack: bool, ) -> Vec> { - let ty = infcx.deeply_resolve_ignoring_regions(ty); - let ty = DeepRegionResolver::new(infcx).fold_ty(ty); + let ty = infcx.deeply_resolve_via_unification_table(ty); // We do not expect existential variables in implied bounds. // We may however encounter unconstrained lifetime variables diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 37a376cfd805a..f1a6c002dc584 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -7,14 +7,12 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def_id::DefId; use rustc_infer::infer::DefineOpaqueTypes; -use rustc_infer::infer::resolve::DeepRegionResolver; use rustc_infer::traits::{ObligationCauseCode, PredicateObligations}; use rustc_middle::traits::select::OverflowError; use rustc_middle::traits::{BuiltinImplSource, ImplSource, ImplSourceUserDefinedData}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{ - self, FieldInfo, Term, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingMode, Unnormalized, - Upcast, + self, FieldInfo, Term, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized, Upcast, }; use rustc_span::{bug, span_bug, sym}; use tracing::{debug, instrument}; @@ -1325,7 +1323,7 @@ fn confirm_candidate<'cx, 'tcx>( if let Ok(Projected::Progress(progress)) = &mut result && progress.term.has_infer_regions() { - progress.term = progress.term.fold_with(&mut DeepRegionResolver::new(selcx.infcx)); + progress.term = selcx.infcx.deeply_resolve_via_unification_table(progress.term); } result diff --git a/compiler/rustc_traits/src/coroutine_witnesses.rs b/compiler/rustc_traits/src/coroutine_witnesses.rs index c3e4bfdb45e3a..624db7747d1f0 100644 --- a/compiler/rustc_traits/src/coroutine_witnesses.rs +++ b/compiler/rustc_traits/src/coroutine_witnesses.rs @@ -1,9 +1,8 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::canonical::QueryRegionConstraint; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; -use rustc_infer::infer::resolve::DeepRegionResolver; use rustc_infer::traits::{Obligation, ObligationCause}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, fold_regions}; use rustc_span::def_id::DefId; use rustc_trait_selection::traits::{ObligationCtxt, with_replaced_escaping_bound_vars}; @@ -80,13 +79,14 @@ fn compute_assumptions<'tcx>( let region_assumptions = infcx.take_registered_region_assumptions(); let region_constraints = infcx.take_and_reset_region_constraints(); - let constraints = make_query_region_constraints( - region_obligations, - ®ion_constraints, - region_assumptions, - ) - .constraints - .fold_with(&mut DeepRegionResolver::new(&infcx)); + let constraints = infcx.deeply_resolve_via_unification_table( + make_query_region_constraints( + region_obligations, + ®ion_constraints, + region_assumptions, + ) + .constraints, + ); tcx.mk_outlives_from_iter( constraints diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index e6acf3081c890..331c367314e63 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -2,6 +2,7 @@ use super::{ FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, TrustedStep, }; use crate::ascii::Char as AsciiChar; +use crate::marker::Destruct; use crate::mem; use crate::net::{Ipv4Addr, Ipv6Addr}; use crate::num::NonZero; @@ -1000,7 +1001,7 @@ macro_rules! range_incl_exact_iter_impl { } /// Specialization implementations for `Range`. -trait RangeIteratorImpl { +const trait RangeIteratorImpl { type Item; // Iterator @@ -1014,7 +1015,8 @@ trait RangeIteratorImpl { fn spec_advance_back_by(&mut self, n: usize) -> Result<(), NonZero>; } -impl RangeIteratorImpl for ops::Range { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +const impl RangeIteratorImpl for ops::Range { type Item = A; #[inline] @@ -1094,7 +1096,8 @@ impl RangeIteratorImpl for ops::Range { } } -impl RangeIteratorImpl for ops::Range { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +const impl RangeIteratorImpl for ops::Range { #[inline] fn spec_next(&mut self) -> Option { if self.start < self.end { @@ -1177,7 +1180,8 @@ impl RangeIteratorImpl for ops::Range { } #[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for ops::Range { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +const impl Iterator for ops::Range { type Item = A; #[inline] @@ -1230,7 +1234,10 @@ impl Iterator for ops::Range { } #[inline] - fn is_sorted(self) -> bool { + fn is_sorted(self) -> bool + where + Self: [const] Destruct, + { true } @@ -1310,7 +1317,8 @@ range_incl_exact_iter_impl! { } #[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for ops::Range { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +const impl DoubleEndedIterator for ops::Range { #[inline] fn next_back(&mut self) -> Option { self.spec_next_back() diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index 3df765c3da709..a7c8ec9319a07 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -185,8 +185,10 @@ pub const trait DoubleEndedIterator: [const] Iterator { /// [`Err(k)`]: Err #[inline] #[unstable(feature = "iter_advance_by", issue = "77404")] - #[rustc_non_const_trait_method] - fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> + where + Self::Item: [const] Destruct, + { for i in 0..n { if self.next_back().is_none() { // SAFETY: `i` is always less than `n`. @@ -239,8 +241,10 @@ pub const trait DoubleEndedIterator: [const] Iterator { /// ``` #[inline] #[stable(feature = "iter_nth_back", since = "1.37.0")] - #[rustc_non_const_trait_method] - fn nth_back(&mut self, n: usize) -> Option { + fn nth_back(&mut self, n: usize) -> Option + where + Self::Item: [const] Destruct, + { if self.advance_back_by(n).is_err() { return None; } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 0748274c401fc..cc4077d0ee26f 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -306,14 +306,22 @@ pub const trait Iterator { /// ``` #[inline] #[unstable(feature = "iter_advance_by", issue = "77404")] - #[rustc_non_const_trait_method] - fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> + where + Self::Item: [const] Destruct, + { /// Helper trait to specialize `advance_by` via `try_fold` for `Sized` iterators. - trait SpecAdvanceBy { + + #[rustc_const_unstable(feature = "const_iter", issue = "92476")] + const trait SpecAdvanceBy { fn spec_advance_by(&mut self, n: usize) -> Result<(), NonZero>; } - impl SpecAdvanceBy for I { + #[rustc_const_unstable(feature = "const_iter", issue = "92476")] + const impl SpecAdvanceBy for I + where + I::Item: [const] Destruct, + { default fn spec_advance_by(&mut self, n: usize) -> Result<(), NonZero> { for i in 0..n { if self.next().is_none() { @@ -325,13 +333,17 @@ pub const trait Iterator { } } - impl SpecAdvanceBy for I { + #[rustc_const_unstable(feature = "const_iter", issue = "92476")] + const impl SpecAdvanceBy for I + where + I::Item: [const] Destruct, + { fn spec_advance_by(&mut self, n: usize) -> Result<(), NonZero> { let Some(n) = NonZero::new(n) else { return Ok(()); }; - let res = self.try_fold(n, |n, _| NonZero::new(n.get() - 1)); + let res = self.try_fold(n, const |n, _| NonZero::new(n.get() - 1)); match res { None => Ok(()), @@ -384,8 +396,10 @@ pub const trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_non_const_trait_method] - fn nth(&mut self, n: usize) -> Option { + fn nth(&mut self, n: usize) -> Option + where + Self::Item: [const] Destruct, + { self.advance_by(n).ok()?; self.next() } diff --git a/library/core/src/iter/traits/marker.rs b/library/core/src/iter/traits/marker.rs index 1e6704fe524a9..fd96424566bcc 100644 --- a/library/core/src/iter/traits/marker.rs +++ b/library/core/src/iter/traits/marker.rs @@ -115,4 +115,5 @@ pub unsafe trait InPlaceIterable { /// for details. Consumers are free to rely on the invariants in unsafe code. #[unstable(feature = "trusted_step", issue = "85731")] #[rustc_specialization_trait] -pub unsafe trait TrustedStep: Step + Copy {} +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +pub const unsafe trait TrustedStep: [const] Step + Copy {} diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index cd3fc889ecdd5..fcdde758fa7df 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -126,14 +126,11 @@ where // Implemented as explicit indexing rather // than zipped iterators for performance reasons. // See PR https://github.com/rust-lang/rust/pull/116846 - // FIXME(const_hack): make this a `for idx in 0..len` loop. - let mut idx = 0; - while idx < len { + for idx in 0..len { // SAFETY: idx < len, so both are in-bounds and readable if unsafe { *lhs.add(idx) != *rhs.add(idx) } { return false; } - idx += 1; } true @@ -224,11 +221,8 @@ const fn chaining_impl<'l, 'r, A: PartialOrd, B, C>( let lhs = &left[..l]; let rhs = &right[..l]; - // FIXME(const-hack): revert this to `for i in 0..l` once `impl const Iterator for Range` - let mut i: usize = 0; - while i < l { + for i in 0..l { elem_chain(&lhs[i], &rhs[i])?; - i += 1; } len_chain(&left.len(), &right.len()) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index f787b7994ba9d..6efc9e4f28a16 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -5628,11 +5628,8 @@ where // But since it can't be relied on we also have an explicit specialization for T: Copy. let len = self.len(); let src = &src[..len]; - // FIXME(const_hack): make this a `for idx in 0..self.len()` loop. - let mut idx = 0; - while idx < self.len() { - self[idx].clone_from(&src[idx]); - idx += 1; + for i in 0..len { + self[i].clone_from(&src[i]); } } } diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile index 9b1684bbd2ace..b7d281cc2dbe7 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile @@ -1,8 +1,14 @@ -FROM ubuntu:22.04 +FROM ubuntu:26.04 COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + clang-22 \ + llvm-22 \ + lld-22 + COPY scripts/crosstool-ng-git.sh /scripts/ COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng-git.sh @@ -18,13 +24,26 @@ RUN /scripts/crosstool-ng-build.sh COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -ENV PATH=$PATH:/x-tools/loongarch64-unknown-linux-gnu/bin +ENV PATH=/usr/lib/llvm-22/bin:$PATH:/x-tools/loongarch64-unknown-linux-gnu/bin + +# --no-rosegment keeps read-only code in the first LOAD segment, matching the +# segment layout produced by GNU ld. This allows Linux to make better use of +# file-backed PMD mappings and reduces iTLB misses. +ENV CLANG_FLAGS="--target=loongarch64-unknown-linux-gnu -fuse-ld=lld \ + --gcc-toolchain=/x-tools/loongarch64-unknown-linux-gnu \ + --sysroot=/x-tools/loongarch64-unknown-linux-gnu/loongarch64-unknown-linux-gnu/sysroot \ + -Wl,--no-rosegment" -ENV CC_loongarch64_unknown_linux_gnu=loongarch64-unknown-linux-gnu-gcc \ - AR_loongarch64_unknown_linux_gnu=loongarch64-unknown-linux-gnu-ar \ - CXX_loongarch64_unknown_linux_gnu=loongarch64-unknown-linux-gnu-g++ \ - CFLAGS_loongarch64_unknown_linux_gnu="-mcmodel=medium" \ - CXXFLAGS_loongarch64_unknown_linux_gnu="-mcmodel=medium" +ENV CC=clang \ + CXX=clang++ \ + CC_loongarch64_unknown_linux_gnu=clang \ + CXX_loongarch64_unknown_linux_gnu=clang++ \ + CFLAGS_loongarch64_unknown_linux_gnu="$CLANG_FLAGS -mcmodel=medium" \ + CXXFLAGS_loongarch64_unknown_linux_gnu="$CLANG_FLAGS -mcmodel=medium" \ + AR_loongarch64_unknown_linux_gnu=llvm-ar \ + RANLIB_loongarch64_unknown_linux_gnu=llvm-ranlib \ + CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER=clang \ + CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-Clink-arg=${CLANG_FLAGS// / -Clink-arg=}" # We re-use the Linux toolchain for bare-metal, because upstream bare-metal # target support for LoongArch is only available from GCC 14+. @@ -64,6 +83,8 @@ ENV RUST_CONFIGURE_ARGS="--enable-extended \ --enable-sanitizers \ --disable-docs \ --set build.allocator=jemalloc \ + --set llvm.link-shared=true \ + --set llvm.thin-lto=true \ --set rust.lto=thin \ --set rust.codegen-units=1" diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile index f9eac213e5060..582a22231b33c 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile @@ -1,8 +1,14 @@ -FROM ubuntu:22.04 +FROM ubuntu:26.04 COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + clang-22 \ + llvm-22 \ + lld-22 + COPY scripts/crosstool-ng-git.sh /scripts/ COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ RUN sh /scripts/crosstool-ng-git.sh @@ -18,13 +24,26 @@ RUN /scripts/crosstool-ng-build.sh COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -ENV PATH=$PATH:/x-tools/loongarch64-unknown-linux-musl/bin - -ENV CC_loongarch64_unknown_linux_musl=loongarch64-unknown-linux-musl-gcc \ - AR_loongarch64_unknown_linux_musl=loongarch64-unknown-linux-musl-ar \ - CXX_loongarch64_unknown_linux_musl=loongarch64-unknown-linux-musl-g++ \ - CFLAGS_loongarch64_unknown_linux_musl="-mcmodel=medium" \ - CXXFLAGS_loongarch64_unknown_linux_musl="-mcmodel=medium" +ENV PATH=/usr/lib/llvm-22/bin:$PATH:/x-tools/loongarch64-unknown-linux-musl/bin + +# --no-rosegment keeps read-only code in the first LOAD segment, matching the +# segment layout produced by GNU ld. This allows Linux to make better use of +# file-backed PMD mappings and reduces iTLB misses. +ENV CLANG_FLAGS="--target=loongarch64-unknown-linux-musl -fuse-ld=lld \ + --gcc-toolchain=/x-tools/loongarch64-unknown-linux-musl \ + --sysroot=/x-tools/loongarch64-unknown-linux-musl/loongarch64-unknown-linux-musl/sysroot \ + -Wl,--no-rosegment" + +ENV CC=clang \ + CXX=clang++ \ + CC_loongarch64_unknown_linux_musl=clang \ + CXX_loongarch64_unknown_linux_musl=clang++ \ + CFLAGS_loongarch64_unknown_linux_musl="$CLANG_FLAGS -mcmodel=medium" \ + CXXFLAGS_loongarch64_unknown_linux_musl="$CLANG_FLAGS -mcmodel=medium" \ + AR_loongarch64_unknown_linux_musl=llvm-ar \ + RANLIB_loongarch64_unknown_linux_musl=llvm-ranlib \ + CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_MUSL_LINKER=clang \ + CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_MUSL_RUSTFLAGS="-Clink-arg=${CLANG_FLAGS// / -Clink-arg=}" ENV HOSTS=loongarch64-unknown-linux-musl @@ -34,6 +53,8 @@ ENV RUST_CONFIGURE_ARGS="--enable-extended \ --enable-sanitizers \ --disable-docs \ --set build.allocator=jemalloc \ + --set llvm.link-shared=true \ + --set llvm.thin-lto=true \ --set rust.lto=thin \ --set rust.codegen-units=1 \ --set target.loongarch64-unknown-linux-musl.crt-static=false \ diff --git a/tests/codegen-llvm/array-cmp.rs b/tests/codegen-llvm/array-cmp.rs index 5b0a802f4097e..2eb498d08f254 100644 --- a/tests/codegen-llvm/array-cmp.rs +++ b/tests/codegen-llvm/array-cmp.rs @@ -42,6 +42,16 @@ pub fn array_of_tuple_le(a: &[(i16, u16); 2], b: &[(i16, u16); 2]) -> bool { // CHECK: %[[EQ00:.+]] = icmp eq i16 %[[A00]], %[[B00]] // CHECK-NEXT: br i1 %[[EQ00]], label %[[L01:.+]], label %[[EXIT_S:.+]] + // CHECK: [[L01]]: + // CHECK: %[[PA01:.+]] = getelementptr{{.+}}i8, ptr %a, {{i32|i64}} 2 + // CHECK: %[[PB01:.+]] = getelementptr{{.+}}i8, ptr %b, {{i32|i64}} 2 + // CHECK: %[[A01:.+]] = load i16, ptr %[[PA01]] + // CHECK: %[[B01:.+]] = load i16, ptr %[[PB01]] + // CHECK-NOT: cmp + // CHECK: %[[EQ01:.+]] = icmp eq i16 %[[A01]], %[[B01]] + // CHECK-NEXT: br i1 %[[EQ01]], label %[[L10:.+]], label %[[EXIT_U:.+]] + + // CHECK: [[L10]]: // CHECK: %[[PA10:.+]] = getelementptr{{.+}}i8, ptr %a, {{i32|i64}} 4 // CHECK: %[[PB10:.+]] = getelementptr{{.+}}i8, ptr %b, {{i32|i64}} 4 // CHECK: %[[A10:.+]] = load i16, ptr %[[PA10]] @@ -57,16 +67,7 @@ pub fn array_of_tuple_le(a: &[(i16, u16); 2], b: &[(i16, u16); 2]) -> bool { // CHECK: %[[B11:.+]] = load i16, ptr %[[PB11]] // CHECK-NOT: cmp // CHECK: %[[EQ11:.+]] = icmp eq i16 %[[A11]], %[[B11]] - // CHECK-NEXT: br i1 %[[EQ11]], label %[[DONE:.+]], label %[[EXIT_U:.+]] - - // CHECK: [[L01]]: - // CHECK: %[[PA01:.+]] = getelementptr{{.+}}i8, ptr %a, {{i32|i64}} 2 - // CHECK: %[[PB01:.+]] = getelementptr{{.+}}i8, ptr %b, {{i32|i64}} 2 - // CHECK: %[[A01:.+]] = load i16, ptr %[[PA01]] - // CHECK: %[[B01:.+]] = load i16, ptr %[[PB01]] - // CHECK-NOT: cmp - // CHECK: %[[EQ01:.+]] = icmp eq i16 %[[A01]], %[[B01]] - // CHECK-NEXT: br i1 %[[EQ01]], label %{{.+}}, label %[[EXIT_U]] + // CHECK-NEXT: br i1 %[[EQ11]], label %[[DONE:.+]], label %[[EXIT_U]] // CHECK: [[DONE]]: // LLVM22: %[[RET:.+]] = phi i1 [ %{{.+}}, %[[EXIT_S]] ], [ %{{.+}}, %[[EXIT_U]] ], [ true, %[[L11]] ] diff --git a/tests/codegen-llvm/wasm-abi/singleton-scalar.rs b/tests/codegen-llvm/wasm-abi/singleton-scalar.rs index 9e8948c617feb..334bf14351410 100644 --- a/tests/codegen-llvm/wasm-abi/singleton-scalar.rs +++ b/tests/codegen-llvm/wasm-abi/singleton-scalar.rs @@ -3,7 +3,7 @@ //@[wasm] compile-flags: --target wasm32-unknown-unknown //@[wasip1] compile-flags: --target wasm32-wasip1 //@ needs-llvm-components: webassembly -//@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled +//@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled -Ctarget-feature=+simd128 #![feature(no_core, rustc_attrs, f128)] #![crate_type = "lib"] #![no_core] @@ -167,6 +167,96 @@ mod pass_i32 { ) -> ReprTransparent> { x } + + #[repr(i32)] + enum CLikeIntEnum { + A, + B, + } + + // CHECK: define{{.*}} i32 @pass_i32_c_like_enum(i32 noundef returned range(i32 0, 2) %[[ARG:.*]]) + #[unsafe(no_mangle)] + extern "C" fn pass_i32_c_like_enum(x: CLikeIntEnum) -> CLikeIntEnum { + x + } + + // CHECK: define{{.*}} i32 @pass_transparent_i32_c_like_enum(i32 noundef returned range(i32 0, 2) %[[ARG:.*]]) + #[unsafe(no_mangle)] + extern "C" fn pass_transparent_i32_c_like_enum( + x: ReprTransparent, + ) -> ReprTransparent { + x + } + + #[repr(i32)] + enum IntEnumZstStructVariants { + A(()), + B(), + } + + // Any field, even a ZST, disqualifies an enum from being passed as a scalar. + // + // CHECK: define{{.*}} void @pass_i32_enum_zst_struct_variants(ptr{{.*}}, ptr{{.*}}) + #[unsafe(no_mangle)] + extern "C" fn pass_i32_enum_zst_struct_variants( + x: IntEnumZstStructVariants, + ) -> IntEnumZstStructVariants { + x + } + + // CHECK: define{{.*}} void @pass_transparent_i32_enum_zst_struct_variants(ptr{{.*}}, ptr{{.*}}) + #[unsafe(no_mangle)] + extern "C" fn pass_transparent_i32_enum_zst_struct_variants( + x: ReprTransparent, + ) -> ReprTransparent { + x + } + + // CHECK: define{{.*}} void @pass_c_i32_enum_zst_struct_variants(ptr{{.*}}, ptr{{.*}}) + #[unsafe(no_mangle)] + extern "C" fn pass_c_i32_enum_zst_struct_variants( + x: ReprC, + ) -> ReprC { + x + } +} + +mod pass_ptr { + use super::*; + + // The layout of `Option<&T>` is guaranteed to match `*const T`. + // + // CHECK: define{{.*}} ptr @pass_option_ref(ptr{{.*}} %[[ARG:.*]]) + #[unsafe(no_mangle)] + extern "C" fn pass_option_ref(x: Option<&'static i32>) -> Option<&'static i32> { + x + } + + // CHECK: define{{.*}} ptr @pass_transparent_option_ref(ptr{{.*}} %[[ARG:.*]]) + #[unsafe(no_mangle)] + extern "C" fn pass_transparent_option_ref( + x: ReprTransparent>, + ) -> ReprTransparent> { + x + } +} + +mod pass_simd { + use super::*; + + // CHECK: define{{.*}} <4 x float> @pass_simd_f32x4(<4 x float> returned %[[ARG:.*]]) + #[unsafe(no_mangle)] + extern "C" fn pass_simd_f32x4(x: simd::f32x4) -> simd::f32x4 { + x + } + + // CHECK: define{{.*}} <4 x float> @pass_transparent_simd_f32x4(<4 x float> returned %[[ARG:.*]]) + #[unsafe(no_mangle)] + extern "C" fn pass_transparent_simd_f32x4( + x: ReprTransparent, + ) -> ReprTransparent { + x + } } mod pass_i128 { diff --git a/tests/ui-fulldeps/rustc_public/check_abi.rs b/tests/ui-fulldeps/rustc_public/check_abi.rs index 4cf79b1ac8005..f6c95fb745409 100644 --- a/tests/ui-fulldeps/rustc_public/check_abi.rs +++ b/tests/ui-fulldeps/rustc_public/check_abi.rs @@ -15,8 +15,8 @@ extern crate rustc_middle; extern crate rustc_public; use rustc_public::abi::{ - ArgAbi, CallConvention, FieldsShape, IntegerLength, PassMode, Primitive, Scalar, ValueAbi, - VariantsShape, + ArgAbi, ArgExtension, CallConvention, FieldsShape, IntegerLength, PassMode, Primitive, Scalar, + ValueRepr, VariantsShape, }; use rustc_public::mir::MirVisitor; use rustc_public::mir::mono::Instance; @@ -106,7 +106,13 @@ fn check_ignore(abi: &ArgAbi) { /// Check the primitive argument: `primitive: char`. fn check_primitive(abi: &ArgAbi) { assert!(abi.ty.kind().is_char()); - assert_matches!(abi.mode, PassMode::Direct(_)); + let PassMode::Direct(ref attrs) = abi.mode else { + panic!("Expected PassMode::Direct for char, got: {:?}", abi.mode); + }; + // A char (32-bit) doesn't need sign/zero extension on most platforms. + assert_eq!(attrs.arg_extension(), ArgExtension::None); + // Direct arguments are not pointers, so no pointee alignment. + assert_eq!(attrs.pointee_align(), None); let layout = abi.layout.shape(); assert!(layout.is_sized()); assert!(!layout.is_1zst()); @@ -116,7 +122,14 @@ fn check_primitive(abi: &ArgAbi) { /// Check the return value: `Result`. fn check_result(abi: &ArgAbi) { assert!(abi.ty.kind().is_enum()); - assert_matches!(abi.mode, PassMode::Indirect { .. }); + let PassMode::Indirect { ref attrs, ref meta_attrs, on_stack } = abi.mode else { + panic!("Expected PassMode::Indirect for Result, got: {:?}", abi.mode); + }; + // Indirect arguments have a pointee alignment (the pointer must be aligned). + assert!(attrs.pointee_align().is_some()); + // Result is a sized type, so no metadata pointer. + assert!(meta_attrs.is_none()); + assert!(!on_stack); let layout = abi.layout.shape(); assert!(layout.is_sized()); assert_matches!(layout.fields, FieldsShape::Arbitrary { .. }); @@ -131,7 +144,7 @@ fn check_niche(abi: &ArgAbi) { assert!(layout.is_sized()); assert_eq!(layout.size.bytes(), 1); - let ValueAbi::Scalar(scalar) = layout.abi else { unreachable!() }; + let ValueRepr::Scalar(scalar) = layout.value_repr else { unreachable!() }; assert!(scalar.has_niche(&MachineInfo::target()), "Opps: {:?}", scalar); let Scalar::Initialized { value, valid_range } = scalar else { unreachable!() }; diff --git a/tests/ui-fulldeps/rustc_public/check_abi_cast.rs b/tests/ui-fulldeps/rustc_public/check_abi_cast.rs new file mode 100644 index 0000000000000..0bd4ac684066e --- /dev/null +++ b/tests/ui-fulldeps/rustc_public/check_abi_cast.rs @@ -0,0 +1,218 @@ +//@ run-pass +//! Test that `PassMode::Cast` exposes the `CastTarget` structure for arguments and returns. +//! +//! When a platform ABI requires an aggregate to be passed in registers, rustc represents +//! this as `PassMode::Cast` with a `CastTarget` describing the register layout. This test +//! verifies that the public API exposes the register kinds, sizes, and that register +//! exhaustion correctly transitions arguments from `Cast` to `Indirect { on_stack: true }`. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +//@ only-x86_64-unknown-linux-gnu + +#![feature(rustc_private)] + +extern crate rustc_driver; +extern crate rustc_interface; +extern crate rustc_middle; +#[macro_use] +extern crate rustc_public; + +use std::convert::TryFrom; +use std::io::Write; +use std::ops::ControlFlow; + +use rustc_public::abi::{CallConvention, PassMode, RegKind}; +use rustc_public::mir::mono::Instance; +use rustc_public::{CrateDef, ItemKind}; + +const CRATE_NAME: &str = "input"; + +fn test_abi_cast() -> ControlFlow<()> { + let items = rustc_public::all_local_items(); + + // Test Cast on argument: a small struct passed in registers. + let cast_arg_fn = items + .iter() + .find(|item| item.kind() == ItemKind::Fn && item.name() == "input::cast_arg") + .expect("missing cast_arg"); + + let instance = Instance::try_from(*cast_arg_fn).unwrap(); + let abi = instance.fn_abi().unwrap(); + assert_eq!(abi.conv, CallConvention::C); + match &abi.args[0].mode { + PassMode::Cast { pad_i32_count, cast } => { + assert_eq!(*pad_i32_count, 0); + assert_eq!(cast.rest.unit.kind, RegKind::Integer); + assert!(cast.rest.total.bits() > 0); + } + other => panic!("Expected PassMode::Cast for struct arg, got: {:?}", other), + } + + // Test Cast on return: a small struct returned via registers. + let cast_ret_fn = items + .iter() + .find(|item| item.kind() == ItemKind::Fn && item.name() == "input::cast_ret") + .expect("missing cast_ret"); + + let instance = Instance::try_from(*cast_ret_fn).unwrap(); + let abi = instance.fn_abi().unwrap(); + match &abi.ret.mode { + PassMode::Cast { pad_i32_count, cast } => { + assert_eq!(*pad_i32_count, 0); + // A 16-byte struct returned via integer registers. + assert!( + cast.rest.unit.kind == RegKind::Integer + || cast.prefix.iter().any(|r| r.kind == RegKind::Integer), + "Expected integer registers for return, got: {:?}", + cast + ); + } + other => panic!("Expected PassMode::Cast for struct return, got: {:?}", other), + } + + // Test Cast with mixed register kinds: struct with int + float fields. + let cast_mixed_fn = items + .iter() + .find(|item| item.kind() == ItemKind::Fn && item.name() == "input::cast_mixed") + .expect("missing cast_mixed"); + + let instance = Instance::try_from(*cast_mixed_fn).unwrap(); + let abi = instance.fn_abi().unwrap(); + match &abi.args[0].mode { + PassMode::Cast { pad_i32_count, cast } => { + assert_eq!(*pad_i32_count, 0); + // On x86_64 SysV, a struct { i64, f64 } uses prefix [Int] + rest Sse, + // or similar split. Just verify we have register info exposed. + let has_int = cast.prefix.iter().any(|r| r.kind == RegKind::Integer) + || cast.rest.unit.kind == RegKind::Integer; + let has_float = cast.prefix.iter().any(|r| r.kind == RegKind::Float) + || cast.rest.unit.kind == RegKind::Float; + assert!( + has_int && has_float, + "Expected both integer and float registers, got: {:?}", + cast + ); + } + other => panic!("Expected PassMode::Cast for mixed struct arg, got: {:?}", other), + } + + // Test multiple cast arguments in one function. + let cast_multi_fn = items + .iter() + .find(|item| item.kind() == ItemKind::Fn && item.name() == "input::cast_multi") + .expect("missing cast_multi"); + + let instance = Instance::try_from(*cast_multi_fn).unwrap(); + let abi = instance.fn_abi().unwrap(); + assert_eq!(abi.conv, CallConvention::C); + assert_eq!(abi.args.len(), 3); + // First arg: SmallStruct → Cast + assert!(matches!(&abi.args[0].mode, PassMode::Cast { .. })); + // Second arg: u64 → Direct (scalar) + assert!(matches!(&abi.args[1].mode, PassMode::Direct(_))); + // Third arg: MixedStruct → Cast with both int and float registers + match &abi.args[2].mode { + PassMode::Cast { cast, .. } => { + let has_int = cast.prefix.iter().any(|r| r.kind == RegKind::Integer) + || cast.rest.unit.kind == RegKind::Integer; + let has_float = cast.prefix.iter().any(|r| r.kind == RegKind::Float) + || cast.rest.unit.kind == RegKind::Float; + assert!(has_int && has_float, "Expected mixed registers, got: {:?}", cast); + } + other => panic!("Expected PassMode::Cast for third arg, got: {:?}", other), + } + + // Test stack spill: same type can have different PassModes when registers are exhausted. + // On x86_64 SysV, integer args use up to 6 registers (rdi, rsi, rdx, rcx, r8, r9). + // TwoWords uses 2 registers each, so the 4th one spills to the stack. + let cast_spill_fn = items + .iter() + .find(|item| item.kind() == ItemKind::Fn && item.name() == "input::cast_spill") + .expect("missing cast_spill"); + + let instance = Instance::try_from(*cast_spill_fn).unwrap(); + let abi = instance.fn_abi().unwrap(); + assert_eq!(abi.conv, CallConvention::C); + assert_eq!(abi.args.len(), 4); + // First three TwoWords fit in registers (2 regs each = 6 total) → Cast + for i in 0..3 { + assert!( + matches!(&abi.args[i].mode, PassMode::Cast { .. }), + "Expected arg {} to be Cast, got: {:?}", + i, + abi.args[i].mode + ); + } + // Fourth TwoWords has no registers left → Indirect (on stack) + assert!( + matches!(&abi.args[3].mode, PassMode::Indirect { on_stack: true, .. }), + "Expected arg 3 to be Indirect on stack, got: {:?}", + abi.args[3].mode + ); + + ControlFlow::Continue(()) +} + +fn main() { + let path = "pass_mode_input.rs"; + generate_input(&path).unwrap(); + let args = &[ + "rustc".to_string(), + "-Cpanic=abort".to_string(), + "--crate-type=lib".to_string(), + "--crate-name".to_string(), + CRATE_NAME.to_string(), + path.to_string(), + ]; + run!(args, test_abi_cast).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + #[repr(C)] + pub struct SmallStruct {{ + pub a: u8, + pub b: u16, + pub c: u32, + }} + + #[repr(C)] + pub struct TwoWords {{ + pub a: u64, + pub b: u64, + }} + + #[repr(C)] + pub struct MixedStruct {{ + pub i: i64, + pub f: f64, + }} + + pub extern "C" fn cast_arg(s: SmallStruct) -> u64 {{ + (s.a as u64) + (s.b as u64) + (s.c as u64) + }} + + pub extern "C" fn cast_ret(x: u64) -> TwoWords {{ + TwoWords {{ a: x, b: x + 1 }} + }} + + pub extern "C" fn cast_mixed(s: MixedStruct) -> f64 {{ + (s.i as f64) + s.f + }} + + pub extern "C" fn cast_multi(s: SmallStruct, x: u64, m: MixedStruct) -> u64 {{ + (s.a as u64) + x + (m.i as u64) + }} + + pub extern "C" fn cast_spill(a: TwoWords, b: TwoWords, c: TwoWords, d: TwoWords) -> u64 {{ + a.a + b.a + c.a + d.a + }} + "# + )?; + Ok(()) +} diff --git a/tests/ui/consts/const-for-feature-gate.rs b/tests/ui/consts/const-for-feature-gate.rs index b643e63c09690..1024beace3d89 100644 --- a/tests/ui/consts/const-for-feature-gate.rs +++ b/tests/ui/consts/const-for-feature-gate.rs @@ -3,7 +3,9 @@ const _: () = { for _ in 0..5 {} //~^ ERROR cannot use `for` + //~| ERROR `IntoIterator` is not yet stable //~| ERROR cannot use `for` + //~| ERROR `Iterator` is not yet stable }; fn main() {} diff --git a/tests/ui/consts/const-for-feature-gate.stderr b/tests/ui/consts/const-for-feature-gate.stderr index 29db5d24ac866..5876f1476341c 100644 --- a/tests/ui/consts/const-for-feature-gate.stderr +++ b/tests/ui/consts/const-for-feature-gate.stderr @@ -1,20 +1,48 @@ -error[E0015]: cannot use `for` loop on `std::ops::Range` in constants +error[E0658]: cannot use `for` loop on `std::ops::Range` in constants --> $DIR/const-for-feature-gate.rs:4:14 | LL | for _ in 0..5 {} | ^^^^ | = note: calls in constants are limited to constant functions, tuple structs and tuple variants + = note: see issue #143874 for more information + = help: add `#![feature(const_trait_impl)]` 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[E0015]: cannot use `for` loop on `std::ops::Range` in constants +error: `IntoIterator` is not yet stable as a const trait + --> $DIR/const-for-feature-gate.rs:4:14 + | +LL | for _ in 0..5 {} + | ^^^^ + | +help: add `#![feature(const_iter)]` to the crate attributes to enable + | +LL + #![feature(const_iter)] + | + +error[E0658]: cannot use `for` loop on `std::ops::Range` in constants --> $DIR/const-for-feature-gate.rs:4:14 | LL | for _ in 0..5 {} | ^^^^ | = note: calls in constants are limited to constant functions, tuple structs and tuple variants + = note: see issue #143874 for more information + = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 2 previous errors +error: `Iterator` is not yet stable as a const trait + --> $DIR/const-for-feature-gate.rs:4:14 + | +LL | for _ in 0..5 {} + | ^^^^ + | +help: add `#![feature(const_iter)]` to the crate attributes to enable + | +LL + #![feature(const_iter)] + | + +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0015`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/consts/const-for.rs b/tests/ui/consts/const-for.rs deleted file mode 100644 index 6f7895457c53d..0000000000000 --- a/tests/ui/consts/const-for.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![feature(const_for)] - -const _: () = { - for _ in 0..5 {} - //~^ ERROR cannot use `for` - //~| ERROR cannot use `for` -}; - -fn main() {} diff --git a/tests/ui/consts/const-for.stderr b/tests/ui/consts/const-for.stderr deleted file mode 100644 index d1308a8dedc85..0000000000000 --- a/tests/ui/consts/const-for.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error[E0015]: cannot use `for` loop on `std::ops::Range` in constants - --> $DIR/const-for.rs:4:14 - | -LL | for _ in 0..5 {} - | ^^^^ - | - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - -error[E0015]: cannot use `for` loop on `std::ops::Range` in constants - --> $DIR/const-for.rs:4:14 - | -LL | for _ in 0..5 {} - | ^^^^ - | - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/consts/control-flow/loop.rs b/tests/ui/consts/control-flow/loop.rs index b02c31c4c25b5..7da88dfd2ac89 100644 --- a/tests/ui/consts/control-flow/loop.rs +++ b/tests/ui/consts/control-flow/loop.rs @@ -1,3 +1,6 @@ +//@ check-pass +#![feature(const_iter,const_trait_impl)] + const _: () = loop { break (); }; static FOO: i32 = loop { break 4; }; @@ -51,14 +54,10 @@ const _: i32 = { let mut x = 0; for i in 0..4 { - //~^ ERROR: cannot use `for` - //~| ERROR: cannot use `for` x += i; } for i in 0..4 { - //~^ ERROR: cannot use `for` - //~| ERROR: cannot use `for` x += i; } diff --git a/tests/ui/consts/control-flow/loop.stderr b/tests/ui/consts/control-flow/loop.stderr deleted file mode 100644 index b91371f9dc218..0000000000000 --- a/tests/ui/consts/control-flow/loop.stderr +++ /dev/null @@ -1,37 +0,0 @@ -error[E0015]: cannot use `for` loop on `std::ops::Range` in constants - --> $DIR/loop.rs:53:14 - | -LL | for i in 0..4 { - | ^^^^ - | - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - -error[E0015]: cannot use `for` loop on `std::ops::Range` in constants - --> $DIR/loop.rs:53:14 - | -LL | for i in 0..4 { - | ^^^^ - | - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0015]: cannot use `for` loop on `std::ops::Range` in constants - --> $DIR/loop.rs:59:14 - | -LL | for i in 0..4 { - | ^^^^ - | - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - -error[E0015]: cannot use `for` loop on `std::ops::Range` in constants - --> $DIR/loop.rs:59:14 - | -LL | for i in 0..4 { - | ^^^^ - | - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/traits/next-solver/rpit-in-trait-malformed-bound-globally-156100.rs b/tests/ui/traits/next-solver/rpit-in-trait-malformed-bound-globally-156100.rs new file mode 100644 index 0000000000000..162e291e463df --- /dev/null +++ b/tests/ui/traits/next-solver/rpit-in-trait-malformed-bound-globally-156100.rs @@ -0,0 +1,16 @@ +//@ compile-flags: -Znext-solver=globally + +// Regression test for . + +trait X { + fn into_iter(&self) -> impl Iterator { + //~^ ERROR trait takes 0 generic arguments but 1 generic argument was supplied + //~| ERROR trait takes 0 generic arguments but 1 generic argument was supplied + //~| ERROR trait takes 0 generic arguments but 1 generic argument was supplied + //~| ERROR trait takes 0 generic arguments but 1 generic argument was supplied + //~| ERROR overflow evaluating the requirement + todo!() + } +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/rpit-in-trait-malformed-bound-globally-156100.stderr b/tests/ui/traits/next-solver/rpit-in-trait-malformed-bound-globally-156100.stderr new file mode 100644 index 0000000000000..e3b4c32047af7 --- /dev/null +++ b/tests/ui/traits/next-solver/rpit-in-trait-malformed-bound-globally-156100.stderr @@ -0,0 +1,59 @@ +error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/rpit-in-trait-malformed-bound-globally-156100.rs:6:33 + | +LL | fn into_iter(&self) -> impl Iterator { + | ^^^^^^^^ expected 0 generic arguments + | +help: turn the generic argument into an associated item binding + | +LL | fn into_iter(&self) -> impl Iterator { + | ++++++ + +error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/rpit-in-trait-malformed-bound-globally-156100.rs:6:33 + | +LL | fn into_iter(&self) -> impl Iterator { + | ^^^^^^^^ expected 0 generic arguments + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: turn the generic argument into an associated item binding + | +LL | fn into_iter(&self) -> impl Iterator { + | ++++++ + +error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/rpit-in-trait-malformed-bound-globally-156100.rs:6:33 + | +LL | fn into_iter(&self) -> impl Iterator { + | ^^^^^^^^ expected 0 generic arguments + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: turn the generic argument into an associated item binding + | +LL | fn into_iter(&self) -> impl Iterator { + | ++++++ + +error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/rpit-in-trait-malformed-bound-globally-156100.rs:6:33 + | +LL | fn into_iter(&self) -> impl Iterator { + | ^^^^^^^^ expected 0 generic arguments + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: turn the generic argument into an associated item binding + | +LL | fn into_iter(&self) -> impl Iterator { + | ++++++ + +error[E0275]: overflow evaluating the requirement `impl Iterator == _` + --> $DIR/rpit-in-trait-malformed-bound-globally-156100.rs:6:28 + | +LL | fn into_iter(&self) -> impl Iterator { + | ^^^^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`rpit_in_trait_malformed_bound_globally_156100`) + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0107, E0275. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/typeck/typeck_type_placeholder_item.rs b/tests/ui/typeck/typeck_type_placeholder_item.rs index 7616e391a35a9..2eda0c5863471 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.rs +++ b/tests/ui/typeck/typeck_type_placeholder_item.rs @@ -239,5 +239,6 @@ fn evens_squared(n: usize) -> _ { const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); //~^ ERROR the placeholder +//~| ERROR `Iterator` is not yet stable //~| ERROR cannot call //~| ERROR cannot call diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr index 2772d55f953a8..469c41b286a75 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item.stderr @@ -678,13 +678,27 @@ LL | fn map(_: fn() -> Option<&'static T>) -> Option { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: calls in constants are limited to constant functions, tuple structs and tuple variants -error[E0015]: cannot call non-const method ` as Iterator>::filter::<{closure@$DIR/typeck_type_placeholder_item.rs:240:29: 240:32}>` in constants +error[E0658]: cannot call conditionally-const method ` as Iterator>::filter::<{closure@$DIR/typeck_type_placeholder_item.rs:240:29: 240:32}>` in constants --> $DIR/typeck_type_placeholder_item.rs:240:22 | LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^^^^^^^^^^^^^^^^^^^^^^ | = note: calls in constants are limited to constant functions, tuple structs and tuple variants + = note: see issue #143874 for more information + = help: add `#![feature(const_trait_impl)]` 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: `Iterator` is not yet stable as a const trait + --> $DIR/typeck_type_placeholder_item.rs:240:14 + | +LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add `#![feature(const_iter)]` to the crate attributes to enable + | +LL + #![feature(const_iter)] + | error[E0015]: cannot call non-const method `, {closure@$DIR/typeck_type_placeholder_item.rs:240:29: 240:32}> as Iterator>::map::` in constants --> $DIR/typeck_type_placeholder_item.rs:240:45 @@ -694,7 +708,7 @@ LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | = note: calls in constants are limited to constant functions, tuple structs and tuple variants -error: aborting due to 83 previous errors +error: aborting due to 84 previous errors -Some errors have detailed explanations: E0015, E0046, E0121, E0282, E0403. +Some errors have detailed explanations: E0015, E0046, E0121, E0282, E0403, E0658. For more information about an error, try `rustc --explain E0015`.