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_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 809b8b7f6a74d..5b9f3231fc744 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3336,13 +3336,12 @@ pub enum UseTreeKind { /// use foo::{bar, baz}; /// ^^^^^^^^^^ /// ``` - Nested { items: ThinVec<(UseTree, NodeId)>, span: Span }, + Nested { items: ThinVec, span: Span }, /// `use prefix::*` Glob(Span), } /// A tree of paths sharing common prefixes. -/// Used in `use` items both at top-level and inside of braces in import groups. #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct UseTree { pub prefix: Path, @@ -3384,6 +3383,13 @@ impl UseTree { } } +/// Used in nested `use` trees. +#[derive(Clone, Encodable, Decodable, Debug, Walkable)] +pub struct UseTreeAndId { + pub inner: UseTree, + pub id: NodeId, +} + /// Distinguishes between `Attribute`s that decorate items and Attributes that /// are contained as statements within items. These two cases need to be /// distinguished for pretty-printing. diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index c12f24a7eff87..4270ac0656deb 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -347,7 +347,6 @@ macro_rules! for_each_ast_visit_hook { visit_mac_call(MacCall) => walk_mac; visit_macro_def(MacroDef) => walk_macro_def; visit_mut_restriction(MutRestriction) => walk_mut_restriction; - //visit_nested_use_tree((UseTree, NodeId)) => walk_nested_use_tree; visit_param(Param) => walk_param; visit_param_bound(GenericBound, _ctxt: BoundKind) => walk_param_bound; visit_pat(Pat) => walk_pat; @@ -368,6 +367,7 @@ macro_rules! for_each_ast_visit_hook { visit_ty(Ty) => walk_ty; visit_ty_pat(TyPat) => walk_ty_pat; visit_use_tree(UseTree) => walk_use_tree; + visit_use_tree_and_id(UseTreeAndId) => walk_use_tree_and_id; visit_variant(Variant) => walk_variant; visit_variant_data(VariantData) => walk_variant_data; visit_vis(Visibility) => walk_vis; @@ -477,6 +477,7 @@ macro_rules! common_visitor_and_walkers { ThinVec, ThinVec, ThinVec, + ThinVec, // tidy-alphabetical-end } @@ -681,13 +682,6 @@ macro_rules! common_visitor_and_walkers { fn visit_stmt(&mut self, s: &$lt Stmt) -> Self::Result { walk_stmt(self, s) } - - fn visit_nested_use_tree(&mut self, use_tree: &$lt UseTree, id: NodeId) - -> Self::Result - { - try_visit!(self.visit_id(id)); - self.visit_use_tree(use_tree) - } )? // `MutVisitor`-only methods @@ -812,14 +806,6 @@ macro_rules! common_visitor_and_walkers { ) -> V::Result; } - $(impl_visitable!(|&$lt self: ThinVec<(UseTree, NodeId)>, vis: &mut V| { - for (nested_tree, nested_id) in self { - try_visit!(vis.visit_nested_use_tree(nested_tree, *nested_id)); - } - V::Result::output() - });)? - $(${ignore($mut)} impl_visitable_list!(ThinVec<(UseTree, NodeId)>,);)? - fn walk_item_inner<$($lt,)? K: WalkItemKind, V: $Visitor$(<$lt>)?>( visitor: &mut V, item: &$($lt)? $($mut)? Item, diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index b504afffb1016..a46c37745227f 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -679,7 +679,8 @@ impl<'hir> LoweringContext<'_, 'hir> { let prefix = Path { segments, span }; // Add all the nested `PathListItem`s to the HIR. - for &(ref use_tree, id) in trees { + for use_tree in trees { + let id = use_tree.id; let owner_id = self.owner_id(id); // Each `use` import is an item and thus are owners of the @@ -691,7 +692,8 @@ impl<'hir> LoweringContext<'_, 'hir> { // `prefix` is lowered multiple times, but in different HIR owners. // So each segment gets renewed `HirId` with the same // `ItemLocalId` and the new owner. (See `lower_node_id`) - let kind = this.lower_use_tree(use_tree, &prefix, id, vis_span, attrs); + let kind = + this.lower_use_tree(&use_tree.inner, &prefix, id, vis_span, attrs); if !attrs.is_empty() { this.curr_owner.attrs.insert(hir::ItemLocalId::ZERO, attrs); } @@ -700,7 +702,7 @@ impl<'hir> LoweringContext<'_, 'hir> { owner_id, kind, vis_span, - span: this.lower_span(use_tree.span()), + span: this.lower_span(use_tree.inner.span()), eii: find_attr!(attrs, EiiImpl(..) | EiiDeclaration(..)), }; hir::OwnerNode::Item(this.arena.alloc(item)) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 4cc1fb4943f4a..54f8a3fb06721 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -673,12 +673,13 @@ fn index_ast<'tcx>( match tree.kind { UseTreeKind::Glob(_) | UseTreeKind::Simple(_) => {} UseTreeKind::Nested { items: ref nested_vec, span } => { - for &(ref nested, id) in nested_vec { + for nested in nested_vec { + let id = nested.id; self.insert(id, AstOwner::NestedUseTree(parent)); items.push(self.make_dummy(id, span, ItemKind::MacCall)); let def_id = self.owners[&id].def_id; - self.visit_item_id_use_tree(nested, def_id, items); + self.visit_item_id_use_tree(&nested.inner, def_id, items); } } } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index ae13f627fcbe5..6ce34cecc7e97 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -911,14 +911,15 @@ impl<'a> State<'a> { } if items.is_empty() { self.word("{}"); - } else if let [(item, _)] = items.as_slice() + } else if let [item] = items.as_slice() && !item + .inner .prefix .segments .first() .is_some_and(|seg| seg.ident.name == rustc_span::symbol::kw::SelfLower) { - self.print_use_tree(item); + self.print_use_tree(&item.inner); } else { let cb = self.cbox(INDENT_UNIT); self.word("{"); @@ -926,10 +927,10 @@ impl<'a> State<'a> { let ib = self.ibox(0); for (idx, use_tree) in items.iter().enumerate() { let is_last = idx == items.len() - 1; - self.print_use_tree(&use_tree.0); + self.print_use_tree(&use_tree.inner); if !is_last { self.word(","); - if let ast::UseTreeKind::Nested { .. } = use_tree.0.kind { + if let ast::UseTreeKind::Nested { .. } = use_tree.inner.kind { self.hardbreak(); } else { self.space(); diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 80ea2e3d877fc..ef39ca049b811 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -2,7 +2,7 @@ use rustc_ast::token::{self, Delimiter, IdentIsRaw}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_ast::{ BinOpKind, BorrowKind, DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, ItemKind, MacCall, MethodCall, - Mutability, Path, PathSegment, Stmt, StructRest, UnOp, UseTree, UseTreeKind, + Mutability, Path, PathSegment, Stmt, StructRest, UnOp, UseTree, UseTreeAndId, UseTreeKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; @@ -97,14 +97,12 @@ impl<'cx, 'a> Context<'cx, 'a> { /// /// use ::core::asserting::{ ... }; fn build_initial_imports(&self) -> Stmt { - let nested_tree = |this: &Self, sym| { - ( - UseTree { - prefix: this.cx.path(this.span, vec![Ident::with_dummy_span(sym)]), - kind: UseTreeKind::Simple(None), - }, - DUMMY_NODE_ID, - ) + let nested_tree = |this: &Self, sym| UseTreeAndId { + inner: UseTree { + prefix: this.cx.path(this.span, vec![Ident::with_dummy_span(sym)]), + kind: UseTreeKind::Simple(None), + }, + id: DUMMY_NODE_ID, }; self.cx.stmt_item( self.span, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index c58629111ac00..fc86bdfcf4f18 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1463,8 +1463,8 @@ impl DeclaredIdents for Box { ast::UseTreeKind::Glob(_) => {} ast::UseTreeKind::Simple(_) => idents.push(ut.ident()), ast::UseTreeKind::Nested { items, .. } => { - for (ut, _) in items { - collect_use_tree_leaves(ut, idents); + for tree in items { + collect_use_tree_leaves(&tree.inner, idents); } } } 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_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 7452e3e08ade9..17c078615c411 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1318,17 +1318,17 @@ impl UnusedImportBraces { fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) { if let ast::UseTreeKind::Nested { ref items, .. } = use_tree.kind { // Recursively check nested UseTrees - for (tree, _) in items { - self.check_use_tree(cx, tree, item); + for tree in items { + self.check_use_tree(cx, &tree.inner, item); } // Trigger the lint only if there is one nested item - let [(tree, _)] = items.as_slice() else { return }; + let [tree] = items.as_slice() else { return }; // Trigger the lint if the nested item is a non-self single item - let node_name = match tree.kind { + let node_name = match tree.inner.kind { ast::UseTreeKind::Simple(rename) => { - let orig_ident = tree.prefix.segments.last().unwrap().ident; + let orig_ident = tree.inner.prefix.segments.last().unwrap().ident; if orig_ident.name == kw::SelfLower { return; } 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_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index d4717b88bbb14..2ab012e0ecc2f 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1368,7 +1368,7 @@ impl<'a> Parser<'a> { &mut self, use_token_span: Span, prefix: Option<&'b UsePathList<'b>>, - ) -> PResult<'a, ThinVec<(UseTree, ast::NodeId)>> { + ) -> PResult<'a, ThinVec> { self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), |p| { p.recover_vcs_conflict_marker(); @@ -1386,7 +1386,7 @@ impl<'a> Parser<'a> { p.emit_error_attr_in_use_tree(use_token_span, prefix, use_tree.span(), attr_span); } - Ok((use_tree, DUMMY_NODE_ID)) + Ok(UseTreeAndId { inner: use_tree, id: DUMMY_NODE_ID }) }) .map(|(r, _)| r) } 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_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 14d6ce1f1b695..c596eccb87932 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -582,16 +582,17 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { } } + /// Note: + /// - `item` is the top-level `use` item. + /// - `use_tree` is the particular use tree within the top-level `use` item. fn build_reduced_graph_for_use_tree( &mut self, - // This particular use tree + item: &Item, use_tree: &ast::UseTree, id: NodeId, parent_prefix: &[Segment], nested: bool, list_stem: bool, - // The whole `use` item - item: &Item, vis: Visibility, root_span: Span, feed: TyCtxtFeed<'tcx, LocalDefId>, @@ -752,12 +753,19 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { } } ast::UseTreeKind::Nested { ref items, .. } => { - for &(ref tree, id) in items { + for tree in items { + let id = tree.id; self.with_owner(id, None, DefKind::Use, use_tree.span(), |this, feed| { this.build_reduced_graph_for_use_tree( - // This particular use tree - tree, id, &prefix, true, false, // The whole `use` item - item, vis, root_span, feed, + item, + &tree.inner, + id, + &prefix, + true, + false, + vis, + root_span, + feed, ) }); } @@ -774,20 +782,11 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { prefix: ast::Path::from_ident(Ident::new(kw::SelfLower, new_span)), kind: ast::UseTreeKind::Simple(Some(Ident::new(kw::Underscore, new_span))), }; + let vis = Visibility::Restricted( + self.parent_scope.module.nearest_parent_mod().expect_local(), + ); self.build_reduced_graph_for_use_tree( - // This particular use tree - &tree, - id, - &prefix, - true, - true, - // The whole `use` item - item, - Visibility::Restricted( - self.parent_scope.module.nearest_parent_mod().expect_local(), - ), - root_span, - feed, + item, &tree, id, &prefix, true, true, vis, root_span, feed, ); } } @@ -834,14 +833,12 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { match item.kind { ItemKind::Use(ref use_tree) => { self.build_reduced_graph_for_use_tree( - // This particular use tree + item, use_tree, item.id, &[], false, false, - // The whole `use` item - item, vis, use_tree.span(), feed, diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 8337b2848edc3..fdac03a8f7163 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -148,9 +148,9 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { } } - fn check_imports_as_underscore(&mut self, items: &[(ast::UseTree, ast::NodeId)]) { - for (item, id) in items { - self.check_import_as_underscore(item, *id); + fn check_imports_as_underscore(&mut self, items: &[ast::UseTreeAndId]) { + for use_tree in items { + self.check_import_as_underscore(&use_tree.inner, use_tree.id); } } @@ -247,10 +247,9 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'ra, 'tcx> { fn visit_item(&mut self, item: &'a ast::Item) { self.item_span = item.span_with_attributes(); match &item.kind { - // Ignore is_public import statements because there's no way to be sure - // whether they're used or not. Also ignore imports with a dummy span - // because this means that they were generated in some fashion by the - // compiler and we don't need to consider them. + // Ignore imports with a dummy span because this means that they + // were generated in some fashion by the compiler and we don't need + // to consider them. ast::ItemKind::Use(..) if item.span.is_dummy() => return, // Use the base UseTree's NodeId as the item id // This allows the grouping of all the lints in the same item @@ -276,9 +275,9 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'ra, 'tcx> { visit::walk_item(self, item); } - fn visit_nested_use_tree(&mut self, use_tree: &'a ast::UseTree, id: ast::NodeId) { - self.check_use_tree(use_tree, id); - visit::walk_use_tree(self, use_tree); + fn visit_use_tree_and_id(&mut self, tree: &'a ast::UseTreeAndId) { + self.check_use_tree(&tree.inner, tree.id); + visit::walk_use_tree_and_id(self, tree); } } @@ -321,8 +320,8 @@ fn calc_unused_spans( let mut used_children = 0; let mut contains_self = false; let mut previous_unused = false; - for (pos, (use_tree, use_tree_id)) in nested.iter().enumerate() { - let remove = match calc_unused_spans(unused_import, use_tree, *use_tree_id) { + for (pos, use_tree) in nested.iter().enumerate() { + let remove = match calc_unused_spans(unused_import, &use_tree.inner, use_tree.id) { UnusedSpanResult::Used => { used_children += 1; None @@ -344,10 +343,11 @@ fn calc_unused_spans( } else if pos == nested.len() - 1 || used_children > 0 { // Delete everything from the end of the last import, to delete the // previous comma - nested[pos - 1].0.hi_span().shrink_to_hi().to(use_tree.hi_span()) + nested[pos - 1].inner.hi_span().shrink_to_hi().to(use_tree.inner.hi_span()) } else { // Delete everything until the next import, to delete the trailing commas - use_tree.prefix.span.to(nested[pos + 1].0.prefix.span.shrink_to_lo()) + let inner = &nested[pos + 1].inner; + use_tree.inner.prefix.span.to(inner.prefix.span.shrink_to_lo()) }; // Try to collapse adjacent spans into a single one. This prevents all cases of @@ -359,9 +359,9 @@ fn calc_unused_spans( to_remove.push(remove_span); } } - contains_self |= use_tree.prefix == kw::SelfLower - && matches!(use_tree.kind, ast::UseTreeKind::Simple(_)) - && !unused_import.unused.contains(&use_tree_id); + contains_self |= use_tree.inner.prefix == kw::SelfLower + && matches!(use_tree.inner.kind, ast::UseTreeKind::Simple(_)) + && !unused_import.unused.contains(&use_tree.id); previous_unused = remove.is_some(); } if unused_spans.is_empty() { @@ -386,7 +386,7 @@ fn calc_unused_spans( tree_span.shrink_to_lo().to(nested .first() .unwrap() - .0 + .inner .prefix .span .shrink_to_lo()), @@ -396,7 +396,7 @@ fn calc_unused_spans( nested .last() .unwrap() - .0 + .inner .hi_span() .shrink_to_hi() .to(tree_span.shrink_to_hi()), diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 92598883158fa..fe46287373dee 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -2849,8 +2849,8 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } } } else if let UseTreeKind::Nested { items, .. } = &use_tree.kind { - for (use_tree, _) in items { - self.future_proof_import(use_tree); + for use_tree in items { + self.future_proof_import(&use_tree.inner); } } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index d19566f8f632e..0633fcd253b4f 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1754,7 +1754,7 @@ fn parse_jobs_all( check_upper_limit(frontend, opt_name); frontend } - None => jobs.flatten(), + None => None, // default to 1 thread irrespectively of `jobs` for now }, }; let backend = match matches.opt_str("jobs-backend") { 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/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index f9c6efe35bc48..7073b334c414d 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -2890,10 +2890,10 @@ impl CommandLineStep for Offload { tarball.set_overlay(OverlayKind::Offload); tarball.is_preview(true); - let omp_offload_libdir = builder.out.join(target).join("offload").join("lib"); + let omp_offload_libdir = omp_offload.lib_dir(); for path in omp_offload.artifact_paths_with_symlink_targets() { - let relative = t!(path.strip_prefix(&omp_offload_libdir)); + let relative = t!(path.strip_prefix(omp_offload_libdir)); let destdir = target_libdir.join(relative.parent().unwrap()); tarball.add_file(path, destdir, FileType::NativeLibrary); diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 91a6ecf4d6bb9..f89ccb4c2a6a8 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -1192,10 +1192,7 @@ impl CommandLineStep for RustOffload { let profile = get_llvm_profile(&builder.config); - cfg.out_dir(&out_dir) - .profile(profile) - .env("LLVM_CONFIG_REAL", llvm_output.llvm_config()) - .define("LLVM_DIR", llvm_output.cmake_dir()); + cfg.out_dir(&out_dir).profile(profile).define("LLVM_DIR", llvm_output.cmake_dir()); cfg.build(); @@ -1215,9 +1212,15 @@ impl CommandLineStep for RustOffload { pub struct BuiltOmpOffload { /// Path to the omp and offload dylibs. offload: Vec, + /// Directory the dylibs were installed into. + lib_dir: PathBuf, } impl BuiltOmpOffload { + pub fn lib_dir(&self) -> &Path { + &self.lib_dir + } + pub fn artifact_paths_with_symlink_targets(&self) -> Vec { let mut paths = self.offload.clone(); @@ -1272,48 +1275,21 @@ impl CommandLineStep for OmpOffload { #[allow(unused)] fn run(self, builder: &Builder<'_>) -> Self::Output { if builder.config.dry_run() { - return BuiltOmpOffload { - offload: vec![builder.config.tempdir().join("llvm-offload-dry-run")], - }; + let dry_run = builder.config.tempdir().join("llvm-offload-dry-run"); + return BuiltOmpOffload { offload: vec![dry_run.clone()], lib_dir: dry_run }; } let target = self.target; let llvm_output = builder.ensure(Llvm { target }); - // Running cmake twice in the same folder is known to cause issues, like deleting existing - // binaries. We therefore write our offload artifacts into it's own folder, instead of - // using the llvm build dir. let out_dir = builder.out.join(self.target.triple).join("offload"); - let mut files = vec![]; let lib_ext = std::env::consts::DLL_EXTENSION; - files.push(out_dir.join("lib").join("libLLVMOffload").with_extension(lib_ext)); - files.push(out_dir.join("lib").join("libomp").with_extension(lib_ext)); - files.push(out_dir.join("lib").join("libomptarget").with_extension(lib_ext)); - files.push( - out_dir.join("lib").join("amdgcn-amd-amdhsa").join("libompdevice").with_extension("a"), - ); - files.push( - out_dir - .join("lib") - .join("amdgcn-amd-amdhsa") - .join("libomptarget-amdgpu") - .with_extension("bc"), - ); - files.push( - out_dir - .join("lib") - .join("nvptx64-nvidia-cuda") - .join("libompdevice") - .with_extension("a"), - ); - files.push( - out_dir - .join("lib") - .join("nvptx64-nvidia-cuda") - .join("libomptarget-nvptx") - .with_extension("bc"), - ); + let files = vec![ + out_dir.join("lib").join("libLLVMOffload").with_extension(lib_ext), + out_dir.join("lib").join("libomp").with_extension(lib_ext), + out_dir.join("lib").join("libomptarget").with_extension(lib_ext), + ]; // Offload/OpenMP are just subfolders of LLVM, so we can use the LLVM sha. static STAMP_HASH_MEMO: OnceLock = OnceLock::new(); @@ -1339,7 +1315,7 @@ impl CommandLineStep for OmpOffload { stamp.path().display() )); } - return BuiltOmpOffload { offload: files }; + return BuiltOmpOffload { offload: files, lib_dir: out_dir.join("lib") }; } trace!(?target, "(re)building offload/openmp artifacts"); @@ -1408,79 +1384,61 @@ impl CommandLineStep for OmpOffload { libstdcxx.parent().map(Path::to_path_buf) }); - // In the context of OpenMP offload, some libraries must be compiled for the gpu target, - // some for the host, and others for both. We do not perform a full cross-compilation, since - // we don't want to run rustc on a GPU. - let omp_targets = vec![target.triple.as_ref(), "amdgcn-amd-amdhsa", "nvptx64-nvidia-cuda"]; - for omp_target in omp_targets { - let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/runtimes/")); - - // If we use an external clang as opposed to building our own llvm_clang, than that clang will - // come with it's own set of default include directories, which are based on a potentially older - // LLVM. This can cause issues, so we overwrite it to include headers based on our - // `src/llvm-project` submodule instead. - let mut cflags = CcFlags::default(); - if !builder.config.llvm_clang { - let base = llvm_output.root_dir().join("include"); - let inc_dir = base.display(); - cflags.push_all(format!(" -I {inc_dir}")); - } + let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/runtimes/")); - // Logic copied from `configure_llvm` - // ThinLTO is only available when building with LLVM, enabling LLD is required. - // Apple's linker ld64 supports ThinLTO out of the box though, so don't use LLD on Darwin. - let mut ldflags = LdFlags::default(); - if builder.config.llvm_thin_lto && !target.contains("apple") { - ldflags.push_all("-fuse-ld=lld"); - } - if *omp_target == *target.triple - && let Some(dir) = &cxx_lib_dir - { - ldflags.push_all(format!("-L{}", dir.display())); - } + // If we use an external clang as opposed to building our own llvm_clang, than that clang will + // come with it's own set of default include directories, which are based on a potentially older + // LLVM. This can cause issues, so we overwrite it to include headers based on our + // `src/llvm-project` submodule instead. + let mut cflags = CcFlags::default(); + if !builder.config.llvm_clang { + let base = llvm_output.root_dir().join("include"); + let inc_dir = base.display(); + cflags.push_all(format!(" -I {inc_dir}")); + } - configure_cmake(builder, target, &mut cfg, true, ldflags, cflags, &[]); - - cfg.define("CMAKE_C_COMPILER", &clang) - .define("CMAKE_CXX_COMPILER", &clangxx) - .define("CMAKE_ASM_COMPILER", &clang); - - // Re-use the same flags as llvm to control the level of debug information - // generated for offload. - let profile = get_llvm_profile(&builder.config); - trace!(?profile); - - // FIXME(offload): Once we move from OMP to Offload (Ol) APIs, we should drop the openmp - // runtime to simplify our build. So far, these are still under development. - cfg.out_dir(&out_dir) - .profile(profile) - .env("LLVM_CONFIG_REAL", llvm_output.llvm_config()) - .define("LLVM_ENABLE_ASSERTIONS", "ON") - .define("LLVM_INCLUDE_TESTS", "OFF") - .define("OFFLOAD_INCLUDE_TESTS", "OFF") - .define("LLVM_ROOT", llvm_output.root_dir().join("build")) - .define("LLVM_DIR", llvm_output.cmake_dir()) - .define("LLVM_DEFAULT_TARGET_TRIPLE", omp_target); - if let Some(p) = offload_clang_dir.clone() { - cfg.define("Clang_DIR", p); - } + // Logic copied from `configure_llvm` + // ThinLTO is only available when building with LLVM, enabling LLD is required. + // Apple's linker ld64 supports ThinLTO out of the box though, so don't use LLD on Darwin. + let mut ldflags = LdFlags::default(); + if builder.config.llvm_thin_lto && !target.contains("apple") { + ldflags.push_all("-fuse-ld=lld"); + } - // We don't perform a full cross-compilation of rustc, therefore our target.triple - // will still be a CPU target. - if *omp_target == *target.triple { - // The offload library provides functionality which only makes sense on the host. - cfg.define("LLVM_ENABLE_RUNTIMES", "openmp;offload"); - } else { - // OpenMP provides some device libraries, so we also compile it for all gpu targets. - cfg.define("OPENMP_INSTALL_LIBDIR", Path::new("lib").join(omp_target)); - cfg.define("LLVM_USE_LINKER", "lld"); - cfg.define("LLVM_ENABLE_RUNTIMES", "openmp"); - cfg.define("CMAKE_C_COMPILER_TARGET", omp_target); - cfg.define("CMAKE_CXX_COMPILER_TARGET", omp_target); - } - cfg.build(); + if let Some(dir) = &cxx_lib_dir { + ldflags.push_all(format!("-L{}", dir.display())); + } + + configure_cmake(builder, target, &mut cfg, true, ldflags, cflags, &[]); + + cfg.define("CMAKE_C_COMPILER", &clang) + .define("CMAKE_CXX_COMPILER", &clangxx) + .define("CMAKE_ASM_COMPILER", &clang); + + // Re-use the same flags as llvm to control the level of debug information + // generated for offload. + let profile = get_llvm_profile(&builder.config); + trace!(?profile); + + // FIXME(offload): Once we move from OMP to Offload (Ol) APIs, we should drop the openmp + // runtime to simplify our build. So far, these are still under development. + cfg.out_dir(&out_dir) + .profile(profile) + .define("LLVM_ENABLE_ASSERTIONS", "ON") + .define("LLVM_INCLUDE_TESTS", "OFF") + .define("OFFLOAD_INCLUDE_TESTS", "OFF") + .define("LLVM_ROOT", llvm_output.root_dir().join("build")) + .define("LLVM_DIR", llvm_output.cmake_dir()) + .define("LLVM_DEFAULT_TARGET_TRIPLE", &*target.triple); + if let Some(p) = offload_clang_dir { + cfg.define("Clang_DIR", p); } + // The offload library provides functionality which only makes sense on the host. + cfg.define("LLVM_ENABLE_RUNTIMES", "openmp;offload"); + + cfg.build(); + t!(stamp.write()); for p in &files { @@ -1494,7 +1452,7 @@ impl CommandLineStep for OmpOffload { helpers::exit_process(1); } } - BuiltOmpOffload { offload: files } + BuiltOmpOffload { offload: files, lib_dir: out_dir.join("lib") } } } @@ -1623,7 +1581,6 @@ impl CommandLineStep for Enzyme { cfg.out_dir(&out_dir) .profile(profile) - .env("LLVM_CONFIG_REAL", llvm_output.llvm_config()) .define("LLVM_ENABLE_ASSERTIONS", "ON") .define("ENZYME_EXTERNAL_SHARED_LIB", "ON") .define("ENZYME_BC_LOADER", "OFF") 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/src/doc/rustc-dev-guide/src/offload/installation.md b/src/doc/rustc-dev-guide/src/offload/installation.md index ab8e7984d5b4a..8422d072bca3b 100644 --- a/src/doc/rustc-dev-guide/src/offload/installation.md +++ b/src/doc/rustc-dev-guide/src/offload/installation.md @@ -12,6 +12,10 @@ cd rust ./configure --enable-llvm-link-shared --release-channel=nightly --enable-llvm-assertions --enable-llvm-offload --enable-llvm-enzyme --enable-clang --enable-lld --enable-option-checking --enable-ninja --disable-docs ``` +If you would rather reuse an existing clang than build one, drop `--enable-clang` and pass +`--enable-llvm-offload-clang-dir=` +instead. It should match the (major version of the) LLVM in `src/llvm-project`. + Afterwards you can build rustc using: ```console ./x build --stage 1 library diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 52dc58f65bb8b..ba4cbb01bcf52 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -498,8 +498,8 @@ more specific `jobs-*` options cannot specify larger values. Parallelism used by compilation stages from lexing to generation of backend IR (e.g. LLVM IR). - If `jobs-frontend` is passed, then it is used as the limit, -- otherwise if `jobs` is passed, then it is used as the limit, -- otherwise `1` is used as the limit (parallelism is disabled), this default may change. +- otherwise `1` is used as the limit (parallelism is disabled), this default may change, + but if it's changed to a larger value the limit from `jobs` will still be respected. In any case the parallelism here may be additionally limited dynamically by jobserver passed from a higher level build system like cargo. @@ -510,8 +510,7 @@ Parallelism used by compilation stages converting backend IR to object files. - If `jobs-backend` is passed, then it is used as the limit, - otherwise if `jobs` is passed, then it is used as the limit, -- otherwise `32` is used as the limit or there's no limit in case of an inherited jobserver, - this default may change. +- otherwise the number of available logical CPUs is used as the limit, this default may change. In any case the parallelism here may be additionally limited dynamically by jobserver passed from a higher level build system like cargo. diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs index 837aefc767c8c..dfcab8c74434a 100644 --- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs +++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs @@ -209,15 +209,15 @@ impl SingleComponentPathImports { // keep track of `use {some_module, some_other_module};` usages if let UseTreeKind::Nested { items, .. } = &use_tree.kind { for tree in items { - let segments = &tree.0.prefix.segments; + let segments = &tree.inner.prefix.segments; if segments.len() == 1 - && let UseTreeKind::Simple(None) = tree.0.kind + && let UseTreeKind::Simple(None) = tree.inner.kind { let name = segments[0].ident.name; if !macros.contains(&name) { single_use_usages.push(SingleUse { name, - span: tree.0.span(), + span: tree.inner.span(), item_id: item.id, can_suggest: false, }); @@ -237,7 +237,7 @@ impl SingleComponentPathImports { // nested case such as `use self::{module1::Struct1, module2::Struct2}` if let UseTreeKind::Nested { items, .. } = &use_tree.kind { for tree in items { - let segments = &tree.0.prefix.segments; + let segments = &tree.inner.prefix.segments; if !segments.is_empty() { imports_reused_with_self.push(segments[0].ident.name); } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs index 677a459a03c7b..cd31d728ead14 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs @@ -104,18 +104,18 @@ struct SelfImport<'a> { fn for_each_self_import<'a>(tree: &'a UseTree, emit_lint: impl Fn(SelfImport<'a>) + Copy) { fn inner<'a>(tree: &'a UseTree, emit_lint: impl Fn(SelfImport<'a>) + Copy, is_toplevel: bool) { if let UseTreeKind::Nested { items, .. } = &tree.kind { - if let [(self_tree, _)] = &**items - && let [self_seg] = &*self_tree.prefix.segments + if let [self_tree] = &**items + && let [self_seg] = &*self_tree.inner.prefix.segments && self_seg.ident.name == kw::SelfLower { emit_lint(SelfImport { tree, - self_tree, + self_tree: &self_tree.inner, is_toplevel, }); } else { - for (subtree, _) in &**items { - inner(subtree, emit_lint, false); + for subtree in &**items { + inner(&subtree.inner, emit_lint, false); } } } diff --git a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs index 8756a09d56b47..20b5d36b8d5f3 100644 --- a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs +++ b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs @@ -52,8 +52,8 @@ fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) { }, UseTreeKind::Simple(None) | UseTreeKind::Glob(_) => {}, UseTreeKind::Nested { ref items, .. } => { - for (use_tree, _) in items { - check_use_tree(use_tree, cx, span); + for use_tree in items { + check_use_tree(&use_tree.inner, cx, span); } }, } diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index c340c56781082..74be29dbb38c0 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -778,7 +778,9 @@ fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { match (l, r) { (Glob(_), Glob(_)) => true, (Simple(l), Simple(r)) => both(l.as_ref(), r.as_ref(), |l, r| eq_id(*l, *r)), - (Nested { items: l, .. }, Nested { items: r, .. }) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)), + (Nested { items: l, .. }, Nested { items: r, .. }) => { + over(l, r, |l, r| eq_use_tree(&l.inner, &r.inner)) + } _ => false, } } diff --git a/src/tools/rustfmt/src/imports.rs b/src/tools/rustfmt/src/imports.rs index c5a2a5de2f175..f062eaa332d92 100644 --- a/src/tools/rustfmt/src/imports.rs +++ b/src/tools/rustfmt/src/imports.rs @@ -477,7 +477,7 @@ impl UseTree { // This needs to be done before sorting use items. let items = itemize_list( context.snippet_provider, - list.iter().map(|(tree, _)| tree), + list.iter().map(|tree| &tree.inner), "}", ",", |tree| tree.prefix.span.lo(), @@ -501,7 +501,7 @@ impl UseTree { list.iter() .zip(items) .map(|(t, list_item)| { - Self::from_ast(context, &t.0, Some(list_item), None, None, None) + Self::from_ast(context, &t.inner, Some(list_item), None, None, None) }) .collect(), ); 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`.