diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 003c1e5d7ebbe..2fca71716c18c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,6 +104,18 @@ jobs: with: fetch-depth: 2 + # Free up disk space on Linux by removing preinstalled components that + # we do not need. We do this to enable some of the less resource + # intensive jobs to run on free runners, which however also have + # less disk space. + - name: free up disk space + uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be + if: contains(matrix.os, 'ubuntu') + with: + # Removing packages with APT saves ~5 GiB, but takes several + # minutes (and potentially removes important packages). + large-packages: false + # Rust Log Analyzer can't currently detect the PR number of a GitHub # Actions build on its own, so a hint in the log message is needed to # point it in the right direction. @@ -194,6 +206,11 @@ jobs: - name: create github artifacts run: src/ci/scripts/create-doc-artifacts.sh + - name: print disk usage + run: | + echo "disk usage:" + df -h + - name: upload artifacts to github uses: actions/upload-artifact@v4 with: diff --git a/.gitignore b/.gitignore index b170dca88fa95..948133cd76e4c 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,8 @@ build/ /src/tools/x/target # Created by default with `src/ci/docker/run.sh` /obj/ +# Created by nix dev shell / .envrc +src/tools/nix-dev-shell/flake.lock ## ICE reports rustc-ice-*.txt diff --git a/.mailmap b/.mailmap index bdc34c52aa768..56490ca5059b0 100644 --- a/.mailmap +++ b/.mailmap @@ -256,6 +256,7 @@ Jakub Adam Wieczorek Jakub Adam Wieczorek Jakub Adam Wieczorek Jakub Adam Wieczorek +Jakub Beránek James [Undefined] James Deng James Hinshelwood diff --git a/Cargo.lock b/Cargo.lock index 5f81a5a84966a..4d54b5aeb4e1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -377,7 +377,7 @@ dependencies = [ "cargo_metadata", "directories", "rustc-build-sysroot", - "rustc_tools_util 0.4.0", + "rustc_tools_util", "rustc_version", "serde", "serde_json", @@ -537,7 +537,7 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "clippy" -version = "0.1.83" +version = "0.1.84" dependencies = [ "anstream", "cargo_metadata", @@ -550,9 +550,11 @@ dependencies = [ "if_chain", "itertools", "parking_lot", + "pulldown-cmark 0.11.3", "quote", "regex", - "rustc_tools_util 0.3.0", + "rinja", + "rustc_tools_util", "serde", "serde_json", "syn 2.0.79", @@ -566,7 +568,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.83" +version = "0.1.84" dependencies = [ "itertools", "serde", @@ -582,20 +584,19 @@ dependencies = [ "clap", "indoc", "itertools", - "opener 0.6.1", + "opener", "shell-escape", "walkdir", ] [[package]] name = "clippy_lints" -version = "0.1.83" +version = "0.1.84" dependencies = [ "arrayvec", "cargo_metadata", "clippy_config", "clippy_utils", - "declare_clippy_lint", "itertools", "quine-mc_cluskey", "regex", @@ -613,7 +614,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.83" +version = "0.1.84" dependencies = [ "arrayvec", "clippy_config", @@ -919,15 +920,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "declare_clippy_lint" -version = "0.1.83" -dependencies = [ - "itertools", - "quote", - "syn 2.0.79", -] - [[package]] name = "deranged" version = "0.3.11" @@ -1915,7 +1907,7 @@ dependencies = [ "anyhow", "clap", "fs-err", - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", "rustdoc-json-types", "serde", "serde_json", @@ -2163,7 +2155,7 @@ dependencies = [ "log", "memchr", "once_cell", - "opener 0.7.2", + "opener", "pulldown-cmark 0.10.3", "regex", "serde", @@ -2501,17 +2493,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "opener" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c62dcb6174f9cb326eac248f07e955d5d559c272730b6c03e396b443b562788" -dependencies = [ - "bstr", - "normpath", - "winapi", -] - [[package]] name = "opener" version = "0.7.2" @@ -2879,6 +2860,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625" dependencies = [ "bitflags 2.6.0", + "getopts", "memchr", "pulldown-cmark-escape 0.11.0", "unicase", @@ -3532,7 +3514,7 @@ dependencies = [ "memmap2", "parking_lot", "portable-atomic", - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", "rustc-rayon", "rustc-stable-hash", "rustc_arena", @@ -4229,7 +4211,7 @@ dependencies = [ name = "rustc_pattern_analysis" version = "0.0.0" dependencies = [ - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", "rustc_apfloat", "rustc_arena", "rustc_data_structures", @@ -4460,12 +4442,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "rustc_tools_util" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba09476327c4b70ccefb6180f046ef588c26a24cf5d269a9feba316eb4f029f" - [[package]] name = "rustc_tools_util" version = "0.4.0" @@ -4608,6 +4584,7 @@ dependencies = [ "rustdoc-json-types", "serde", "serde_json", + "sha2", "smallvec", "tempfile", "threadpool", @@ -4632,7 +4609,7 @@ name = "rustdoc-json-types" version = "0.1.0" dependencies = [ "bincode", - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", "serde", "serde_json", ] @@ -5262,7 +5239,7 @@ dependencies = [ "ignore", "miropt-test-tools", "regex", - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", "semver", "similar", "termcolor", @@ -5593,13 +5570,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "unicode-bdd" -version = "0.1.0" -dependencies = [ - "ucd-parse", -] - [[package]] name = "unicode-bidi" version = "0.3.15" @@ -5649,6 +5619,13 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-table-generator" +version = "0.1.0" +dependencies = [ + "ucd-parse", +] + [[package]] name = "unicode-width" version = "0.1.14" diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs index 2ecac8a9df1c6..872cae59a4e03 100644 --- a/compiler/rustc_abi/src/callconv.rs +++ b/compiler/rustc_abi/src/callconv.rs @@ -3,18 +3,23 @@ mod abi { pub(crate) use crate::Variants; } +#[cfg(feature = "nightly")] use rustc_macros::HashStable_Generic; -use crate::{Abi, Align, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; +#[cfg(feature = "nightly")] +use crate::{Abi, FieldsShape, TyAbiInterface, TyAndLayout}; +use crate::{Align, HasDataLayout, Size}; -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +#[cfg_attr(feature = "nightly", derive(HashStable_Generic))] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum RegKind { Integer, Float, Vector, } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +#[cfg_attr(feature = "nightly", derive(HashStable_Generic))] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct Reg { pub kind: RegKind, pub size: Size, @@ -108,15 +113,8 @@ impl HomogeneousAggregate { } } +#[cfg(feature = "nightly")] impl<'a, Ty> TyAndLayout<'a, Ty> { - /// Returns `true` if this is an aggregate type (including a ScalarPair!) - pub fn is_aggregate(&self) -> bool { - match self.abi { - Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false, - Abi::ScalarPair(..) | Abi::Aggregate { .. } => true, - } - } - /// Returns `Homogeneous` if this layout is an aggregate containing fields of /// only a single type (e.g., `(u32, u32)`). Such aggregates are often /// special-cased in ABIs. diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 6e1299944a09d..0340d1bd6bcd1 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -11,8 +11,10 @@ use crate::{ Variants, WrappingRange, }; +#[cfg(feature = "nightly")] mod ty; +#[cfg(feature = "nightly")] pub use ty::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx}; // A variant is absent if it's uninhabited and only has ZST fields. @@ -39,7 +41,7 @@ enum NicheBias { End, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum LayoutCalculatorError { /// An unsized type was found in a location where a sized type was expected. /// @@ -54,6 +56,36 @@ pub enum LayoutCalculatorError { /// A union had no fields. EmptyUnion, + + /// The fields or variants have irreconcilable reprs + ReprConflict, +} + +impl LayoutCalculatorError { + pub fn without_payload(&self) -> LayoutCalculatorError<()> { + match self { + LayoutCalculatorError::UnexpectedUnsized(_) => { + LayoutCalculatorError::UnexpectedUnsized(()) + } + LayoutCalculatorError::SizeOverflow => LayoutCalculatorError::SizeOverflow, + LayoutCalculatorError::EmptyUnion => LayoutCalculatorError::EmptyUnion, + LayoutCalculatorError::ReprConflict => LayoutCalculatorError::ReprConflict, + } + } + + /// Format an untranslated diagnostic for this type + /// + /// Intended for use by rust-analyzer, as neither it nor `rustc_abi` depend on fluent infra. + pub fn fallback_fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + LayoutCalculatorError::UnexpectedUnsized(_) => { + "an unsized type was found where a sized type was expected" + } + LayoutCalculatorError::SizeOverflow => "size overflow", + LayoutCalculatorError::EmptyUnion => "type is a union with no fields", + LayoutCalculatorError::ReprConflict => "type has an invalid repr", + }) + } } type LayoutCalculatorResult = @@ -489,6 +521,10 @@ impl LayoutCalculator { } let dl = self.cx.data_layout(); + // bail if the enum has an incoherent repr that cannot be computed + if repr.packed() { + return Err(LayoutCalculatorError::ReprConflict); + } let calculate_niche_filling_layout = || -> Option> { if dont_niche_optimize_enum { diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 84d756b6d517c..8e90130da4c01 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -29,14 +29,14 @@ mod layout; mod tests; pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind}; -pub use layout::{ - FIRST_VARIANT, FieldIdx, Layout, LayoutCalculator, LayoutCalculatorError, TyAbiInterface, - TyAndLayout, VariantIdx, -}; +#[cfg(feature = "nightly")] +pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx}; +pub use layout::{LayoutCalculator, LayoutCalculatorError}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro /// instead of implementing everything in `rustc_middle`. +#[cfg(feature = "nightly")] pub trait HashStableContext {} #[derive(Clone, Copy, PartialEq, Eq, Default)] @@ -1644,6 +1644,14 @@ pub struct LayoutS { } impl LayoutS { + /// Returns `true` if this is an aggregate type (including a ScalarPair!) + pub fn is_aggregate(&self) -> bool { + match self.abi { + Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false, + Abi::ScalarPair(..) | Abi::Aggregate { .. } => true, + } + } + pub fn scalar(cx: &C, scalar: Scalar) -> Self { let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar); let size = scalar.size(cx); diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index cecf223b96193..4d8525989cc59 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -23,7 +23,6 @@ #![feature(maybe_uninit_slice)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] -#![feature(strict_provenance)] #![warn(unreachable_pub)] // tidy-alphabetical-end diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index ce744cc56e1ab..7416a1e39eb53 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1574,7 +1574,7 @@ impl<'hir> LoweringContext<'_, 'hir> { .collect(); // Introduce extra lifetimes if late resolution tells us to. - let extra_lifetimes = self.resolver.take_extra_lifetime_params(parent_node_id); + let extra_lifetimes = self.resolver.extra_lifetime_params(parent_node_id); params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| { self.lifetime_res_to_generic_param( ident, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 00eafeb4d844d..4d8d22e09d922 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -268,8 +268,8 @@ impl ResolverAstLowering { /// /// The extra lifetimes that appear from the parenthesized `Fn`-trait desugaring /// should appear at the enclosing `PolyTraitRef`. - fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> { - self.extra_lifetime_params_map.remove(&id).unwrap_or_default() + fn extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> { + self.extra_lifetime_params_map.get(&id).cloned().unwrap_or_default() } } @@ -885,7 +885,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let mut generic_params: Vec<_> = self .lower_generic_params_mut(generic_params, hir::GenericParamSource::Binder) .collect(); - let extra_lifetimes = self.resolver.take_extra_lifetime_params(binder); + let extra_lifetimes = self.resolver.extra_lifetime_params(binder); debug!(?extra_lifetimes); generic_params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| { self.lifetime_res_to_generic_param(ident, node_id, res, hir::GenericParamSource::Binder) @@ -1495,62 +1495,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // frequently opened issues show. let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None); - let captured_lifetimes_to_duplicate = if let Some(args) = - // We only look for one `use<...>` syntax since we syntactially reject more than one. - bounds.iter().find_map( - |bound| match bound { - ast::GenericBound::Use(a, _) => Some(a), - _ => None, - }, - ) { - // We'll actually validate these later on; all we need is the list of - // lifetimes to duplicate during this portion of lowering. - args.iter() - .filter_map(|arg| match arg { - PreciseCapturingArg::Lifetime(lt) => Some(*lt), - PreciseCapturingArg::Arg(..) => None, - }) - // Add in all the lifetimes mentioned in the bounds. We will error - // them out later, but capturing them here is important to make sure - // they actually get resolved in resolve_bound_vars. - .chain(lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)) - .collect() - } else { - match origin { - hir::OpaqueTyOrigin::TyAlias { .. } => { - // type alias impl trait and associated type position impl trait were - // decided to capture all in-scope lifetimes, which we collect for - // all opaques during resolution. - self.resolver - .take_extra_lifetime_params(opaque_ty_node_id) - .into_iter() - .map(|(ident, id, _)| Lifetime { id, ident }) - .collect() - } - hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. } => { - if in_trait_or_impl.is_some() - || self.tcx.features().lifetime_capture_rules_2024 - || span.at_least_rust_2024() - { - // return-position impl trait in trait was decided to capture all - // in-scope lifetimes, which we collect for all opaques during resolution. - self.resolver - .take_extra_lifetime_params(opaque_ty_node_id) - .into_iter() - .map(|(ident, id, _)| Lifetime { id, ident }) - .collect() - } else { - // in fn return position, like the `fn test<'a>() -> impl Debug + 'a` - // example, we only need to duplicate lifetimes that appear in the - // bounds, since those are the only ones that are captured by the opaque. - lifetime_collector::lifetimes_in_bounds(self.resolver, bounds) - } - } - hir::OpaqueTyOrigin::AsyncFn { .. } => { - unreachable!("should be using `lower_async_fn_ret_ty`") - } + // Whether this opaque always captures lifetimes in scope. + // Right now, this is all RPITIT and TAITs, and when `lifetime_capture_rules_2024` + // is enabled. We don't check the span of the edition, since this is done + // on a per-opaque basis to account for nested opaques. + let always_capture_in_scope = match origin { + _ if self.tcx.features().lifetime_capture_rules_2024 => true, + hir::OpaqueTyOrigin::TyAlias { .. } => true, + hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. } => in_trait_or_impl.is_some(), + hir::OpaqueTyOrigin::AsyncFn { .. } => { + unreachable!("should be using `lower_coroutine_fn_ret_ty`") } }; + let captured_lifetimes_to_duplicate = lifetime_collector::lifetimes_for_opaque( + self.resolver, + always_capture_in_scope, + opaque_ty_node_id, + bounds, + span, + ); debug!(?captured_lifetimes_to_duplicate); // Feature gate for RPITIT + use<..> @@ -1920,7 +1883,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let captured_lifetimes = self .resolver - .take_extra_lifetime_params(opaque_ty_node_id) + .extra_lifetime_params(opaque_ty_node_id) .into_iter() .map(|(ident, id, _)| Lifetime { id, ident }) .collect(); diff --git a/compiler/rustc_ast_lowering/src/lifetime_collector.rs b/compiler/rustc_ast_lowering/src/lifetime_collector.rs index fe64160fb4dcf..8d47c856bdd24 100644 --- a/compiler/rustc_ast_lowering/src/lifetime_collector.rs +++ b/compiler/rustc_ast_lowering/src/lifetime_collector.rs @@ -1,5 +1,7 @@ use rustc_ast::visit::{self, BoundKind, LifetimeCtxt, Visitor}; -use rustc_ast::{GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind}; +use rustc_ast::{ + GenericBound, GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind, +}; use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def::{DefKind, LifetimeRes, Res}; use rustc_middle::span_bug; @@ -10,14 +12,41 @@ use rustc_span::symbol::{Ident, kw}; use super::ResolverAstLoweringExt; struct LifetimeCollectVisitor<'ast> { - resolver: &'ast ResolverAstLowering, + resolver: &'ast mut ResolverAstLowering, + always_capture_in_scope: bool, current_binders: Vec, collected_lifetimes: FxIndexSet, } impl<'ast> LifetimeCollectVisitor<'ast> { - fn new(resolver: &'ast ResolverAstLowering) -> Self { - Self { resolver, current_binders: Vec::new(), collected_lifetimes: FxIndexSet::default() } + fn new(resolver: &'ast mut ResolverAstLowering, always_capture_in_scope: bool) -> Self { + Self { + resolver, + always_capture_in_scope, + current_binders: Vec::new(), + collected_lifetimes: FxIndexSet::default(), + } + } + + fn visit_opaque(&mut self, opaque_ty_node_id: NodeId, bounds: &'ast GenericBounds, span: Span) { + // If we're edition 2024 or within a TAIT or RPITIT, *and* there is no + // `use<>` statement to override the default capture behavior, then + // capture all of the in-scope lifetimes. + if (self.always_capture_in_scope || span.at_least_rust_2024()) + && bounds.iter().all(|bound| !matches!(bound, GenericBound::Use(..))) + { + for (ident, id, _) in self.resolver.extra_lifetime_params(opaque_ty_node_id) { + self.record_lifetime_use(Lifetime { id, ident }); + } + } + + // We also recurse on the bounds to make sure we capture all the lifetimes + // mentioned in the bounds. These may disagree with the `use<>` list, in which + // case we will error on these later. We will also recurse to visit any + // nested opaques, which may *implicitly* capture lifetimes. + for bound in bounds { + self.visit_param_bound(bound, BoundKind::Bound); + } } fn record_lifetime_use(&mut self, lifetime: Lifetime) { @@ -99,6 +128,9 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> { self.record_elided_anchor(t.id, t.span); visit::walk_ty(self, t); } + TyKind::ImplTrait(opaque_ty_node_id, bounds) => { + self.visit_opaque(*opaque_ty_node_id, bounds, t.span) + } _ => { visit::walk_ty(self, t); } @@ -106,13 +138,14 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> { } } -pub(crate) fn lifetimes_in_bounds( - resolver: &ResolverAstLowering, +pub(crate) fn lifetimes_for_opaque( + resolver: &mut ResolverAstLowering, + always_capture_in_scope: bool, + opaque_ty_node_id: NodeId, bounds: &GenericBounds, + span: Span, ) -> FxIndexSet { - let mut visitor = LifetimeCollectVisitor::new(resolver); - for bound in bounds { - visitor.visit_param_bound(bound, BoundKind::Bound); - } + let mut visitor = LifetimeCollectVisitor::new(resolver, always_capture_in_scope); + visitor.visit_opaque(opaque_ty_node_id, bounds, span); visitor.collected_lifetimes } diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 40a6d506ffa63..2437a43bd5a36 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -3,12 +3,16 @@ use std::rc::Rc; use rustc_errors::Diag; use rustc_hir::def_id::LocalDefId; -use rustc_infer::infer::canonical::Canonical; +use rustc_infer::infer::canonical::CanonicalQueryInput; use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; use rustc_infer::infer::{ InferCtxt, RegionResolutionError, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt as _, }; use rustc_infer::traits::ObligationCause; +use rustc_infer::traits::query::{ + CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpNormalizeGoal, + CanonicalTypeOpProvePredicateGoal, +}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{ self, RePlaceholder, Region, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex, @@ -95,9 +99,7 @@ impl<'tcx> ToUniverseInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tc } } -impl<'tcx> ToUniverseInfo<'tcx> - for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>> -{ +impl<'tcx> ToUniverseInfo<'tcx> for CanonicalTypeOpProvePredicateGoal<'tcx> { fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(PredicateQuery { canonical_query: self, @@ -107,7 +109,7 @@ impl<'tcx> ToUniverseInfo<'tcx> } impl<'tcx, T: Copy + fmt::Display + TypeFoldable> + 'tcx> ToUniverseInfo<'tcx> - for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize>> + for CanonicalTypeOpNormalizeGoal<'tcx, T> { fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(NormalizeQuery { @@ -117,9 +119,7 @@ impl<'tcx, T: Copy + fmt::Display + TypeFoldable> + 'tcx> ToUnivers } } -impl<'tcx> ToUniverseInfo<'tcx> - for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>> -{ +impl<'tcx> ToUniverseInfo<'tcx> for CanonicalTypeOpAscribeUserTypeGoal<'tcx> { fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(AscribeUserTypeQuery { canonical_query: self, @@ -128,7 +128,7 @@ impl<'tcx> ToUniverseInfo<'tcx> } } -impl<'tcx, F> ToUniverseInfo<'tcx> for Canonical<'tcx, type_op::custom::CustomTypeOp> { +impl<'tcx, F> ToUniverseInfo<'tcx> for CanonicalQueryInput<'tcx, type_op::custom::CustomTypeOp> { fn to_universe_info(self, _base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { // We can't rerun custom type ops. UniverseInfo::other() @@ -211,8 +211,7 @@ trait TypeOpInfo<'tcx> { } struct PredicateQuery<'tcx> { - canonical_query: - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>>, + canonical_query: CanonicalTypeOpProvePredicateGoal<'tcx>, base_universe: ty::UniverseIndex, } @@ -220,7 +219,7 @@ impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> { fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> { tcx.dcx().create_err(HigherRankedLifetimeError { cause: Some(HigherRankedErrorCause::CouldNotProve { - predicate: self.canonical_query.value.value.predicate.to_string(), + predicate: self.canonical_query.canonical.value.value.predicate.to_string(), }), span, }) @@ -253,7 +252,7 @@ impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> { } struct NormalizeQuery<'tcx, T> { - canonical_query: Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize>>, + canonical_query: CanonicalTypeOpNormalizeGoal<'tcx, T>, base_universe: ty::UniverseIndex, } @@ -264,7 +263,7 @@ where fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> { tcx.dcx().create_err(HigherRankedLifetimeError { cause: Some(HigherRankedErrorCause::CouldNotNormalize { - value: self.canonical_query.value.value.value.to_string(), + value: self.canonical_query.canonical.value.value.value.to_string(), }), span, }) @@ -306,7 +305,7 @@ where } struct AscribeUserTypeQuery<'tcx> { - canonical_query: Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>>, + canonical_query: CanonicalTypeOpAscribeUserTypeGoal<'tcx>, base_universe: ty::UniverseIndex, } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 20ecc665b1eed..a5f3298b02cc2 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1146,6 +1146,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } // don't create labels for compiler-generated spans Some(_) => None, + // don't create labels for the span not from user's code + None if opt_assignment_rhs_span + .is_some_and(|span| self.infcx.tcx.sess.source_map().is_imported(span)) => + { + None + } None => { let (has_sugg, decl_span, sugg) = if name != kw::SelfLower { suggest_ampmut( @@ -1198,18 +1204,21 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { sugg.push(s); } - err.multipart_suggestion_verbose( - format!( - "consider changing this to be a mutable {pointer_desc}{}", - if is_trait_sig { - " in the `impl` method and the `trait` definition" - } else { - "" - } - ), - sugg, - Applicability::MachineApplicable, - ); + if sugg.iter().all(|(span, _)| !self.infcx.tcx.sess.source_map().is_imported(*span)) + { + err.multipart_suggestion_verbose( + format!( + "consider changing this to be a mutable {pointer_desc}{}", + if is_trait_sig { + " in the `impl` method and the `trait` definition" + } else { + "" + } + ), + sugg, + Applicability::MachineApplicable, + ); + } } Some((false, err_label_span, message, _)) => { let def_id = self.body.source.def_id(); diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index cbf8aa313c517..698fdafc93616 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -187,10 +187,10 @@ fn do_mir_borrowck<'tcx>( let location_table = LocationTable::new(body); - let move_data = MoveData::gather_moves(body, tcx, param_env, |_| true); + let move_data = MoveData::gather_moves(body, tcx, |_| true); let promoted_move_data = promoted .iter_enumerated() - .map(|(idx, body)| (idx, MoveData::gather_moves(body, tcx, param_env, |_| true))); + .map(|(idx, body)| (idx, MoveData::gather_moves(body, tcx, |_| true))); let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) .into_engine(tcx, body) diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 0c6f8cd7b5bfe..fde68615cc089 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -137,7 +137,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let _: Result<_, ErrorGuaranteed> = self.fully_perform_op( locations, category, - param_env.and(type_op::prove_predicate::ProvePredicate::new(predicate)), + param_env.and(type_op::prove_predicate::ProvePredicate { predicate }), ); } @@ -162,7 +162,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let result: Result<_, ErrorGuaranteed> = self.fully_perform_op( location.to_locations(), category, - param_env.and(type_op::normalize::Normalize::new(value)), + param_env.and(type_op::normalize::Normalize { value }), ); result.unwrap_or(value) } @@ -223,7 +223,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let _: Result<_, ErrorGuaranteed> = self.fully_perform_op( Locations::All(span), ConstraintCategory::Boring, - self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(mir_ty, user_ty)), + self.param_env.and(type_op::ascribe_user_type::AscribeUserType { mir_ty, user_ty }), ); } diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index cded9935f971a..8e1faf025e25c 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -280,7 +280,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = self .param_env - .and(type_op::normalize::Normalize::new(ty)) + .and(type_op::normalize::Normalize { value: ty }) .fully_perform(self.infcx, span) .unwrap_or_else(|guar| TypeOpOutput { output: Ty::new_error(self.infcx.tcx, guar), @@ -318,7 +318,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) { let result: Result<_, ErrorGuaranteed> = self .param_env - .and(type_op::normalize::Normalize::new(ty)) + .and(type_op::normalize::Normalize { value: ty }) .fully_perform(self.infcx, span); let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else { continue; @@ -373,7 +373,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { ) -> Option<&'tcx QueryRegionConstraints<'tcx>> { let TypeOpOutput { output: bounds, constraints, .. } = self .param_env - .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty }) + .and(type_op::ImpliedOutlivesBounds { ty }) .fully_perform(self.infcx, span) .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty)) .ok()?; diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index a5175e653d8fe..35963228181f7 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -11,8 +11,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex}; use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; use rustc_span::DUMMY_SP; -use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives; -use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; +use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, TypeOpOutput}; use tracing::debug; use crate::location::RichLocation; @@ -632,7 +631,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { match typeck .param_env - .and(DropckOutlives::new(dropped_ty)) + .and(DropckOutlives { dropped_ty }) .fully_perform(typeck.infcx, DUMMY_SP) { Ok(TypeOpOutput { output, constraints, .. }) => { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 238d7d0749a5d..7b60a632c3052 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1128,7 +1128,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } let projected_ty = curr_projected_ty.projection_ty_core( tcx, - self.param_env, proj, |this, field, ()| { let ty = this.field_ty(tcx, field); @@ -1919,7 +1918,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // than 1. // If the length is larger than 1, the repeat expression will need to copy the // element, so we require the `Copy` trait. - if len.try_eval_target_usize(tcx, self.param_env).map_or(true, |len| len > 1) { + if len.try_to_target_usize(tcx).is_none_or(|len| len > 1) { match operand { Operand::Copy(..) | Operand::Constant(..) => { // These are always okay: direct use of a const, or a value that can evidently be copied. diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index cbe411d78d5fd..e7f9f894381cd 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -133,6 +133,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( .expect_const() .try_to_valtree() .expect("expected monomorphic const in codegen") + .0 .unwrap_branch(); assert_eq!(x.layout(), y.layout()); @@ -806,8 +807,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => m.load_scalar(fx), ty::Array(elem, len) if matches!(elem.kind(), ty::Uint(ty::UintTy::U8)) - && len.try_eval_target_usize(fx.tcx, ty::ParamEnv::reveal_all()) - == Some(expected_bytes) => + && len + .try_to_target_usize(fx.tcx) + .expect("expected monomorphic const in codegen") + == expected_bytes => { m.force_stack(fx).0.load( fx, @@ -907,8 +910,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => {} ty::Array(elem, len) if matches!(elem.kind(), ty::Uint(ty::UintTy::U8)) - && len.try_eval_target_usize(fx.tcx, ty::ParamEnv::reveal_all()) - == Some(expected_bytes) => {} + && len + .try_to_target_usize(fx.tcx) + .expect("expected monomorphic const in codegen") + == expected_bytes => {} _ => { fx.tcx.dcx().span_fatal( span, diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 5c297ebfadbfd..336934354e11b 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -34,7 +34,9 @@ pub(crate) fn unsized_info<'tcx>( { let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); - if data_a.principal_def_id() == data_b.principal_def_id() { + let b_principal_def_id = data_b.principal_def_id(); + if data_a.principal_def_id() == b_principal_def_id || b_principal_def_id.is_none() { + // A NOP cast that doesn't actually change anything, should be allowed even with invalid vtables. debug_assert!( validate_trivial_unsize(fx.tcx, data_a, data_b), "NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}" diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 4e1b99fdebfdf..43dbfafa87184 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -76,8 +76,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(), ty::Array(elem, len) if matches!(*elem.kind(), ty::Uint(ty::UintTy::U8)) - && len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all()) - == Some(expected_bytes) => + && len + .try_to_target_usize(bx.tcx) + .expect("expected monomorphic const in codegen") + == expected_bytes => { let place = PlaceRef::alloca(bx, args[0].layout); args[0].val.store(bx, place); @@ -696,8 +698,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( } ty::Array(elem, len) if matches!(*elem.kind(), ty::Uint(ty::UintTy::U8)) - && len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all()) - == Some(expected_bytes) => + && len + .try_to_target_usize(bx.tcx) + .expect("expected monomorphic const in codegen") + == expected_bytes => { // Zero-extend iN to the array length: let ze = bx.zext(result, bx.type_ix(expected_bytes * 8)); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index c2c261da79b68..cee704a9c220b 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -1,3 +1,5 @@ +use std::ffi::CStr; + use itertools::Itertools as _; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; @@ -132,7 +134,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { .collect::>(); let initializer = cx.const_array(cx.type_ptr(), &name_globals); - let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), "__llvm_coverage_names"); + let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), c"__llvm_coverage_names"); llvm::set_global_constant(array, true); llvm::set_linkage(array, llvm::Linkage::InternalLinkage); llvm::set_initializer(array, initializer); @@ -305,7 +307,7 @@ fn generate_coverage_map<'ll>( /// specific, well-known section and name. fn save_function_record( cx: &CodegenCx<'_, '_>, - covfun_section_name: &str, + covfun_section_name: &CStr, mangled_function_name: &str, source_hash: u64, filenames_ref: u64, diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index d7d29eebf8593..484a4d00c13ee 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -1,4 +1,5 @@ use std::cell::RefCell; +use std::ffi::{CStr, CString}; use libc::c_uint; use rustc_codegen_ssa::traits::{ @@ -12,6 +13,7 @@ use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::Instance; use rustc_middle::ty::layout::HasTyCtxt; use rustc_target::abi::{Align, Size}; +use rustc_target::spec::HasTargetSpec; use tracing::{debug, instrument}; use crate::builder::Builder; @@ -284,16 +286,16 @@ pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, cov_data_val: &'ll llvm::Value, ) { - let covmap_var_name = llvm::build_string(|s| unsafe { + let covmap_var_name = CString::new(llvm::build_byte_buffer(|s| unsafe { llvm::LLVMRustCoverageWriteMappingVarNameToString(s); - }) - .expect("Rust Coverage Mapping var name failed UTF-8 conversion"); + })) + .unwrap(); debug!("covmap var name: {:?}", covmap_var_name); - let covmap_section_name = llvm::build_string(|s| unsafe { + let covmap_section_name = CString::new(llvm::build_byte_buffer(|s| unsafe { llvm::LLVMRustCoverageWriteMapSectionNameToString(cx.llmod, s); - }) - .expect("Rust Coverage section name failed UTF-8 conversion"); + })) + .expect("covmap section name should not contain NUL"); debug!("covmap section name: {:?}", covmap_section_name); let llglobal = llvm::add_global(cx.llmod, cx.val_ty(cov_data_val), &covmap_var_name); @@ -308,7 +310,7 @@ pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>( pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, - covfun_section_name: &str, + covfun_section_name: &CStr, func_name_hash: u64, func_record_val: &'ll llvm::Value, is_used: bool, @@ -322,7 +324,8 @@ pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( // of descriptions play distinct roles in LLVM IR; therefore, assign them different names (by // appending "u" to the end of the function record var name, to prevent `linkonce_odr` merging. let func_record_var_name = - format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" }); + CString::new(format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" })) + .unwrap(); debug!("function record var name: {:?}", func_record_var_name); debug!("function record section name: {:?}", covfun_section_name); @@ -334,7 +337,9 @@ pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( llvm::set_section(llglobal, covfun_section_name); // LLVM's coverage mapping format specifies 8-byte alignment for items in this section. llvm::set_alignment(llglobal, Align::EIGHT); - llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name); + if cx.target_spec().supports_comdat() { + llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name); + } cx.add_used_global(llglobal); } @@ -349,9 +354,9 @@ pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( /// - `__llvm_covfun` on Linux /// - `__LLVM_COV,__llvm_covfun` on macOS (includes `__LLVM_COV,` segment prefix) /// - `.lcovfun$M` on Windows (includes `$M` sorting suffix) -pub(crate) fn covfun_section_name(cx: &CodegenCx<'_, '_>) -> String { - llvm::build_string(|s| unsafe { +pub(crate) fn covfun_section_name(cx: &CodegenCx<'_, '_>) -> CString { + CString::new(llvm::build_byte_buffer(|s| unsafe { llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s); - }) - .expect("Rust Coverage function record section name failed UTF-8 conversion") + })) + .expect("covfun section name should not contain NUL") } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index bfe623e7fc354..c9a17c9852dbc 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -787,7 +787,9 @@ fn codegen_msvc_try<'ll>( let tydesc = bx.declare_global("__rust_panic_type_info", bx.val_ty(type_info)); unsafe { llvm::LLVMRustSetLinkage(tydesc, llvm::Linkage::LinkOnceODRLinkage); - llvm::SetUniqueComdat(bx.llmod, tydesc); + if bx.cx.tcx.sess.target.supports_comdat() { + llvm::SetUniqueComdat(bx.llmod, tydesc); + } llvm::LLVMSetInitializer(tydesc, type_info); } @@ -1177,8 +1179,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(), ty::Array(elem, len) if matches!(elem.kind(), ty::Uint(ty::UintTy::U8)) - && len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all()) - == Some(expected_bytes) => + && len + .try_to_target_usize(bx.tcx) + .expect("expected monomorphic const in codegen") + == expected_bytes => { let place = PlaceRef::alloca(bx, args[0].layout); args[0].val.store(bx, place); @@ -1243,12 +1247,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } if name == sym::simd_shuffle_generic { - let idx = fn_args[2] - .expect_const() - .eval(tcx, ty::ParamEnv::reveal_all(), span) - .unwrap() - .1 - .unwrap_branch(); + let idx = fn_args[2].expect_const().try_to_valtree().unwrap().0.unwrap_branch(); let n = idx.len() as u64; let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn); @@ -1467,8 +1466,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } ty::Array(elem, len) if matches!(elem.kind(), ty::Uint(ty::UintTy::U8)) - && len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all()) - == Some(expected_bytes) => + && len + .try_to_target_usize(bx.tcx) + .expect("expected monomorphic const in codegen") + == expected_bytes => { // Zero-extend iN to the array length: let ze = bx.zext(i_, bx.type_ix(expected_bytes * 8)); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 661debbb9f126..d0034de06c726 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -646,6 +646,7 @@ unsafe extern "C" { pub type Attribute; pub type Metadata; pub type BasicBlock; + pub type Comdat; } #[repr(C)] pub struct Builder<'a>(InvariantOpaque<'a>); @@ -1490,6 +1491,9 @@ unsafe extern "C" { pub fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr); pub fn LLVMIsAConstantInt(value_ref: &Value) -> Option<&ConstantInt>; + + pub fn LLVMGetOrInsertComdat(M: &Module, Name: *const c_char) -> &Comdat; + pub fn LLVMSetComdat(V: &Value, C: &Comdat); } #[link(name = "llvm-wrapper", kind = "static")] @@ -2320,7 +2324,6 @@ unsafe extern "C" { pub fn LLVMRustPositionBuilderAtStart<'a>(B: &Builder<'a>, BB: &'a BasicBlock); - pub fn LLVMRustSetComdat<'a>(M: &'a Module, V: &'a Value, Name: *const c_char, NameLen: size_t); pub fn LLVMRustSetModulePICLevel(M: &Module); pub fn LLVMRustSetModulePIELevel(M: &Module); pub fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel); diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index d0db350a149e8..e837022044ee0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -178,10 +178,10 @@ pub fn SetFunctionCallConv(fn_: &Value, cc: CallConv) { // function. // For more details on COMDAT sections see e.g., https://www.airs.com/blog/archives/52 pub fn SetUniqueComdat(llmod: &Module, val: &Value) { - unsafe { - let name = get_value_name(val); - LLVMRustSetComdat(llmod, val, name.as_ptr().cast(), name.len()); - } + let name_buf = get_value_name(val).to_vec(); + let name = + CString::from_vec_with_nul(name_buf).or_else(|buf| CString::new(buf.into_bytes())).unwrap(); + set_comdat(llmod, val, &name); } pub fn SetUnnamedAddress(global: &Value, unnamed: UnnamedAddr) { @@ -210,15 +210,13 @@ impl MemoryEffects { } } -pub fn set_section(llglobal: &Value, section_name: &str) { - let section_name_cstr = CString::new(section_name).expect("unexpected CString error"); +pub fn set_section(llglobal: &Value, section_name: &CStr) { unsafe { - LLVMSetSection(llglobal, section_name_cstr.as_ptr()); + LLVMSetSection(llglobal, section_name.as_ptr()); } } -pub fn add_global<'a>(llmod: &'a Module, ty: &'a Type, name: &str) -> &'a Value { - let name_cstr = CString::new(name).expect("unexpected CString error"); +pub fn add_global<'a>(llmod: &'a Module, ty: &'a Type, name_cstr: &CStr) -> &'a Value { unsafe { LLVMAddGlobal(llmod, ty, name_cstr.as_ptr()) } } @@ -252,9 +250,14 @@ pub fn set_alignment(llglobal: &Value, align: Align) { } } -pub fn set_comdat(llmod: &Module, llglobal: &Value, name: &str) { +/// Get the `name`d comdat from `llmod` and assign it to `llglobal`. +/// +/// Inserts the comdat into `llmod` if it does not exist. +/// It is an error to call this if the target does not support comdat. +pub fn set_comdat(llmod: &Module, llglobal: &Value, name: &CStr) { unsafe { - LLVMRustSetComdat(llmod, llglobal, name.as_ptr().cast(), name.len()); + let comdat = LLVMGetOrInsertComdat(llmod, name.as_ptr()); + LLVMSetComdat(llglobal, comdat); } } diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 02e1995620bdb..bf6ef219873a5 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -64,7 +64,9 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { unsafe { llvm::LLVMRustSetLinkage(lldecl, base::linkage_to_llvm(linkage)) }; let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); base::set_link_section(lldecl, attrs); - if linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR { + if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR) + && self.tcx.sess.target.supports_comdat() + { llvm::SetUniqueComdat(self.llmod, lldecl); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index d91c0f0790d9d..f3d9a7d37e6cc 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -147,7 +147,7 @@ pub fn validate_trivial_unsize<'tcx>( infcx.leak_check(universe, None).is_ok() }) } - (None, None) => true, + (_, None) => true, _ => false, } } @@ -175,7 +175,8 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( { let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); - if data_a.principal_def_id() == data_b.principal_def_id() { + let b_principal_def_id = data_b.principal_def_id(); + if data_a.principal_def_id() == b_principal_def_id || b_principal_def_id.is_none() { // Codegen takes advantage of the additional assumption, where if the // principal trait def id of what's being casted doesn't change, // then we don't need to adjust the vtable at all. This diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 369ab387bea58..526d2b86d4883 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -23,7 +23,6 @@ use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{ self, ExistentialProjection, GenericArgKind, GenericArgsRef, ParamEnv, Ty, TyCtxt, }; -use rustc_span::DUMMY_SP; use rustc_target::abi::Integer; use smallvec::SmallVec; @@ -685,21 +684,25 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S ty::ConstKind::Param(param) => { write!(output, "{}", param.name) } - ty::ConstKind::Value(ty, _) => { + ty::ConstKind::Value(ty, valtree) => { match ty.kind() { ty::Int(ity) => { // FIXME: directly extract the bits from a valtree instead of evaluating an // already evaluated `Const` in order to get the bits. - let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all()); + let bits = ct + .try_to_bits(tcx, ty::ParamEnv::reveal_all()) + .expect("expected monomorphic const in codegen"); let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; write!(output, "{val}") } ty::Uint(_) => { - let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all()); + let val = ct + .try_to_bits(tcx, ty::ParamEnv::reveal_all()) + .expect("expected monomorphic const in codegen"); write!(output, "{val}") } ty::Bool => { - let val = ct.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap(); + let val = ct.try_to_bool().expect("expected monomorphic const in codegen"); write!(output, "{val}") } _ => { @@ -711,8 +714,9 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S // avoiding collisions and will make the emitted type names shorter. let hash_short = tcx.with_stable_hashing_context(|mut hcx| { let mut hasher = StableHasher::new(); - let ct = ct.eval(tcx, ty::ParamEnv::reveal_all(), DUMMY_SP).unwrap(); - hcx.while_hashing_spans(false, |hcx| ct.hash_stable(hcx, &mut hasher)); + hcx.while_hashing_spans(false, |hcx| { + (ty, valtree).hash_stable(hcx, &mut hasher) + }); hasher.finish::() }); diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index cbd95146294f5..73bfa9dbd10e2 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -11,7 +11,6 @@ #![feature(let_chains)] #![feature(negative_impls)] #![feature(rustdoc_internals)] -#![feature(strict_provenance)] #![feature(trait_alias)] #![feature(try_blocks)] #![warn(unreachable_pub)] diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index a132ca6954050..82fea4c58e191 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -361,12 +361,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty), (Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty), (Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm), - (Pointer(..), Int(..)) => bx.ptrtoint(imm, to_backend_ty), + (Pointer(..), Int(..)) => { + // FIXME: this exposes the provenance, which shouldn't be necessary. + bx.ptrtoint(imm, to_backend_ty) + } (Float(_), Pointer(..)) => { let int_imm = bx.bitcast(imm, bx.cx().type_isize()); bx.ptradd(bx.const_null(bx.type_ptr()), int_imm) } (Pointer(..), Float(_)) => { + // FIXME: this exposes the provenance, which shouldn't be necessary. let int_imm = bx.ptrtoint(imm, bx.cx().type_isize()); bx.bitcast(int_imm, to_backend_ty) } diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 7358a6c6d2256..547030a185435 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -6,13 +6,10 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::LangItem; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::*; -use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty}; use rustc_middle::{bug, mir}; -use rustc_trait_selection::traits::{ - ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext, -}; -use tracing::{instrument, trace}; +use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; +use tracing::instrument; use super::ConstCx; @@ -195,50 +192,8 @@ impl Qualif for NeedsNonConstDrop { return false; } - // FIXME(effects): If `destruct` is not a `const_trait`, - // or effects are disabled in this crate, then give up. - let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span)); - if !cx.tcx.has_host_param(destruct_def_id) || !cx.tcx.features().effects { - return NeedsDrop::in_any_value_of_ty(cx, ty); - } - - let obligation = Obligation::new( - cx.tcx, - ObligationCause::dummy_with_span(cx.body.span), - cx.param_env, - ty::TraitRef::new(cx.tcx, destruct_def_id, [ - ty::GenericArg::from(ty), - ty::GenericArg::from(cx.tcx.expected_host_effect_param_for_body(cx.def_id())), - ]), - ); - - let infcx = cx.tcx.infer_ctxt().build(); - let mut selcx = SelectionContext::new(&infcx); - let Some(impl_src) = selcx.select(&obligation).ok().flatten() else { - // If we couldn't select a const destruct candidate, then it's bad - return true; - }; - - trace!(?impl_src); - - if !matches!( - impl_src, - ImplSource::Builtin(BuiltinImplSource::Misc, _) | ImplSource::Param(_) - ) { - // If our const destruct candidate is not ConstDestruct or implied by the param env, - // then it's bad - return true; - } - - if impl_src.borrow_nested_obligations().is_empty() { - return false; - } - - // If we had any errors, then it's bad - let ocx = ObligationCtxt::new(&infcx); - ocx.register_obligations(impl_src.nested_obligations()); - let errors = ocx.select_all_or_error(); - !errors.is_empty() + // FIXME(effects): Reimplement const drop checking. + NeedsDrop::in_any_value_of_ty(cx, ty) } fn in_adt_inherently<'tcx>( diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index fd05664e2f227..6686413bf0254 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -11,7 +11,8 @@ use rustc_span::{Span, Symbol}; use super::CompileTimeMachine; use crate::errors::{self, FrameNote, ReportErrorExt}; use crate::interpret::{ - ErrorHandled, Frame, InterpError, InterpErrorInfo, MachineStopType, err_inval, err_machine_stop, + ErrorHandled, Frame, InterpErrorInfo, InterpErrorKind, MachineStopType, err_inval, + err_machine_stop, }; /// The CTFE machine has some custom error kinds. @@ -57,7 +58,7 @@ impl MachineStopType for ConstEvalErrKind { } } -/// The errors become [`InterpError::MachineStop`] when being raised. +/// The errors become [`InterpErrorKind::MachineStop`] when being raised. impl<'tcx> Into> for ConstEvalErrKind { fn into(self) -> InterpErrorInfo<'tcx> { err_machine_stop!(self).into() @@ -124,7 +125,7 @@ pub fn get_span_and_frames<'tcx>( /// `get_span_and_frames`. pub(super) fn report<'tcx, C, F, E>( tcx: TyCtxt<'tcx>, - error: InterpError<'tcx>, + error: InterpErrorKind<'tcx>, span: Span, get_span_and_frames: C, mk: F, diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 672353e629d44..7319c251bbd99 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -18,7 +18,7 @@ use tracing::{debug, instrument, trace}; use super::{CanAccessMutGlobal, CompileTimeInterpCx, CompileTimeMachine}; use crate::const_eval::CheckAlignment; use crate::interpret::{ - CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpError, + CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpErrorKind, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup, create_static_alloc, eval_nullary_intrinsic, intern_const_alloc_recursive, interp_ok, throw_exhaust, }; @@ -463,7 +463,7 @@ fn report_validation_error<'tcx>( error: InterpErrorInfo<'tcx>, alloc_id: AllocId, ) -> ErrorHandled { - if !matches!(error.kind(), InterpError::UndefinedBehavior(_)) { + if !matches!(error.kind(), InterpErrorKind::UndefinedBehavior(_)) { // Some other error happened during validation, e.g. an unsupported operation. return report_eval_error(ecx, cid, error); } diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index ba19f642795a9..e2e4754a45c5e 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -43,7 +43,7 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( // We go to `usize` as we cannot allocate anything bigger anyway. let (field_count, variant, down) = match ty.kind() { - ty::Array(_, len) => (len.eval_target_usize(tcx.tcx, param_env) as usize, None, op), + ty::Array(_, len) => (len.try_to_target_usize(tcx.tcx)? as usize, None, op), ty::Adt(def, _) if def.variants().is_empty() => { return None; } diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index c943236affc56..211668cf055c6 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -9,7 +9,7 @@ use rustc_errors::{ use rustc_hir::ConstContext; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::mir::interpret::{ - CheckInAllocMsg, CtfeProvenance, ExpectedKind, InterpError, InvalidMetaKind, + CheckInAllocMsg, CtfeProvenance, ExpectedKind, InterpErrorKind, InvalidMetaKind, InvalidProgramInfo, Misalignment, Pointer, PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo, }; @@ -835,23 +835,23 @@ impl ReportErrorExt for UnsupportedOpInfo { } } -impl<'tcx> ReportErrorExt for InterpError<'tcx> { +impl<'tcx> ReportErrorExt for InterpErrorKind<'tcx> { fn diagnostic_message(&self) -> DiagMessage { match self { - InterpError::UndefinedBehavior(ub) => ub.diagnostic_message(), - InterpError::Unsupported(e) => e.diagnostic_message(), - InterpError::InvalidProgram(e) => e.diagnostic_message(), - InterpError::ResourceExhaustion(e) => e.diagnostic_message(), - InterpError::MachineStop(e) => e.diagnostic_message(), + InterpErrorKind::UndefinedBehavior(ub) => ub.diagnostic_message(), + InterpErrorKind::Unsupported(e) => e.diagnostic_message(), + InterpErrorKind::InvalidProgram(e) => e.diagnostic_message(), + InterpErrorKind::ResourceExhaustion(e) => e.diagnostic_message(), + InterpErrorKind::MachineStop(e) => e.diagnostic_message(), } } fn add_args(self, diag: &mut Diag<'_, G>) { match self { - InterpError::UndefinedBehavior(ub) => ub.add_args(diag), - InterpError::Unsupported(e) => e.add_args(diag), - InterpError::InvalidProgram(e) => e.add_args(diag), - InterpError::ResourceExhaustion(e) => e.add_args(diag), - InterpError::MachineStop(e) => e.add_args(&mut |name, value| { + InterpErrorKind::UndefinedBehavior(ub) => ub.add_args(diag), + InterpErrorKind::Unsupported(e) => e.add_args(diag), + InterpErrorKind::InvalidProgram(e) => e.add_args(diag), + InterpErrorKind::ResourceExhaustion(e) => e.add_args(diag), + InterpErrorKind::MachineStop(e) => e.add_args(&mut |name, value| { diag.arg(name, value); }), } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 4945563f4a4ee..85d99900c6ccd 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -471,7 +471,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Don't forget to mark "initially live" locals as live. self.storage_live_for_always_live_locals()?; }; - res.inspect_err(|_| { + res.inspect_err_kind(|_| { // Don't show the incomplete stack frame in the error stacktrace. self.stack_mut().pop(); }) diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 1def3d08328ac..64b15611316f9 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -391,7 +391,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let ptr = self.read_pointer(src)?; let val = Immediate::new_slice( ptr, - length.eval_target_usize(*self.tcx, self.param_env), + length + .try_to_target_usize(*self.tcx) + .expect("expected monomorphic const in const eval"), self, ); self.write_immediate(val, dest) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 02dd7821ef667..a1c773a4b8005 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -19,7 +19,7 @@ use rustc_trait_selection::traits::ObligationCtxt; use tracing::{debug, instrument, trace}; use super::{ - Frame, FrameInfo, GlobalId, InterpError, InterpErrorInfo, InterpResult, MPlaceTy, Machine, + Frame, FrameInfo, GlobalId, InterpErrorInfo, InterpErrorKind, InterpResult, MPlaceTy, Machine, MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, err_inval, interp_ok, throw_inval, throw_ub, throw_ub_custom, }; @@ -73,7 +73,7 @@ where } impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { - type LayoutOfResult = Result, InterpError<'tcx>>; + type LayoutOfResult = Result, InterpErrorKind<'tcx>>; #[inline] fn layout_tcx_at_span(&self) -> Span { @@ -82,20 +82,25 @@ impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { } #[inline] - fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> InterpError<'tcx> { + fn handle_layout_err( + &self, + err: LayoutError<'tcx>, + _: Span, + _: Ty<'tcx>, + ) -> InterpErrorKind<'tcx> { err_inval!(Layout(err)) } } impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> { - type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpError<'tcx>>; + type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpErrorKind<'tcx>>; fn handle_fn_abi_err( &self, err: FnAbiError<'tcx>, _span: Span, _fn_abi_request: FnAbiRequest<'tcx>, - ) -> InterpError<'tcx> { + ) -> InterpErrorKind<'tcx> { match err { FnAbiError::Layout(err) => err_inval!(Layout(err)), FnAbiError::AdjustForForeignAbi(err) => { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 540898ec6456c..4e603f57c56fb 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -324,13 +324,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { dist.checked_neg().unwrap(), // i64::MIN is impossible as no allocation can be that large CheckInAllocMsg::OffsetFromTest, ) - .map_err(|_| { + .map_err_kind(|_| { // Make the error more specific. err_ub_custom!( fluent::const_eval_offset_from_different_allocations, name = intrinsic_name, ) - .into() })?; // Perform division by size to compute return value. diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 13641ef2bd3f6..b6120ce82fe5f 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -17,8 +17,8 @@ use rustc_hir as hir; use rustc_middle::bug; use rustc_middle::mir::interpret::ValidationErrorKind::{self, *}; use rustc_middle::mir::interpret::{ - ExpectedKind, InterpError, InterpErrorInfo, InvalidMetaKind, Misalignment, PointerKind, - Provenance, UnsupportedOpInfo, ValidationErrorInfo, alloc_range, interp_ok, + ExpectedKind, InterpErrorKind, InvalidMetaKind, Misalignment, PointerKind, Provenance, + UnsupportedOpInfo, ValidationErrorInfo, alloc_range, interp_ok, }; use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; @@ -37,8 +37,8 @@ use super::{ // for the validation errors #[rustfmt::skip] -use super::InterpError::UndefinedBehavior as Ub; -use super::InterpError::Unsupported as Unsup; +use super::InterpErrorKind::UndefinedBehavior as Ub; +use super::InterpErrorKind::Unsupported as Unsup; use super::UndefinedBehaviorInfo::*; use super::UnsupportedOpInfo::*; @@ -97,20 +97,19 @@ macro_rules! try_validation { ($e:expr, $where:expr, $( $( $p:pat_param )|+ => $kind: expr ),+ $(,)? ) => {{ - $e.map_err(|e| { + $e.map_err_kind(|e| { // We catch the error and turn it into a validation failure. We are okay with // allocation here as this can only slow down builds that fail anyway. - let (kind, backtrace) = e.into_parts(); - match kind { + match e { $( $($p)|+ => { err_validation_failure!( $where, $kind - ).into() + ) } ),+, - _ => InterpErrorInfo::from_parts(kind, backtrace), + e => e, } })? }}; @@ -1230,11 +1229,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // No need for an alignment check here, this is not an actual memory access. let alloc = self.ecx.get_ptr_alloc(mplace.ptr(), size)?.expect("we already excluded size 0"); - alloc.get_bytes_strip_provenance().map_err(|err| { + alloc.get_bytes_strip_provenance().map_err_kind(|kind| { // Some error happened, try to provide a more detailed description. // For some errors we might be able to provide extra information. // (This custom logic does not fit the `try_validation!` macro.) - let (kind, backtrace) = err.into_parts(); match kind { Ub(InvalidUninitBytes(Some((_alloc_id, access)))) | Unsup(ReadPointerAsInt(Some((_alloc_id, access)))) => { // Some byte was uninitialized, determine which @@ -1247,14 +1245,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, self.path.push(PathElem::ArrayElem(i)); if matches!(kind, Ub(InvalidUninitBytes(_))) { - err_validation_failure!(self.path, Uninit { expected }).into() + err_validation_failure!(self.path, Uninit { expected }) } else { - err_validation_failure!(self.path, PointerAsInt { expected }).into() + err_validation_failure!(self.path, PointerAsInt { expected }) } } // Propagate upwards (that will also check for unexpected errors). - _ => return InterpErrorInfo::from_parts(kind, backtrace), + err => err, } })?; @@ -1368,12 +1366,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { v.reset_padding(val)?; interp_ok(()) }) - .map_err(|err| { + .map_err_info(|err| { if !matches!( err.kind(), err_ub!(ValidationError { .. }) - | InterpError::InvalidProgram(_) - | InterpError::Unsupported(UnsupportedOpInfo::ExternTypeField) + | InterpErrorKind::InvalidProgram(_) + | InterpErrorKind::Unsupported(UnsupportedOpInfo::ExternTypeField) ) { bug!( "Unexpected error during validation: {}", diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 39e2d3b4ebbb1..0490195caf4f9 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -10,7 +10,6 @@ #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(slice_ptr_get)] -#![feature(strict_provenance)] #![feature(trait_alias)] #![feature(try_blocks)] #![feature(unqualified_local_imports)] diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index d73cf11ee64d6..5a477143a6236 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -13,7 +13,7 @@ ena = "0.14.3" indexmap = { version = "2.4.0" } jobserver_crate = { version = "0.1.28", package = "jobserver" } measureme = "11" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" rustc-rayon = { version = "0.5.0", optional = true } rustc-stable-hash = { version = "0.1.0", features = ["nightly"] } rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index fba2707922bcd..afac08ae6f818 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -33,7 +33,6 @@ #![feature(ptr_alignment_type)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] -#![feature(strict_provenance)] #![feature(test)] #![feature(thread_id_value)] #![feature(type_alias_impl_trait)] diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 0c5fe6e8d8b76..534458046945c 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -353,6 +353,9 @@ declare_features! ( (accepted, repr_packed, "1.33.0", Some(33158)), /// Allows `#[repr(transparent)]` attribute on newtype structs. (accepted, repr_transparent, "1.28.0", Some(43036)), + /// Allows enums like Result to be used across FFI, if T's niche value can + /// be used to describe E or vice-versa. + (accepted, result_ffi_guarantees, "CURRENT_RUSTC_VERSION", Some(110503)), /// Allows return-position `impl Trait` in traits. (accepted, return_position_impl_trait_in_trait, "1.75.0", Some(91611)), /// Allows code like `let x: &'static u32 = &42` to work (RFC 1414). diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 1067156958dc1..f4795c75e4834 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -580,9 +580,6 @@ declare_features! ( (incomplete, repr128, "1.16.0", Some(56071)), /// Allows `repr(simd)` and importing the various simd intrinsics. (unstable, repr_simd, "1.4.0", Some(27731)), - /// Allows enums like Result to be used across FFI, if T's niche value can - /// be used to describe E or vise-versa. - (unstable, result_ffi_guarantees, "1.80.0", Some(110503)), /// Allows bounding the return type of AFIT/RPITIT. (unstable, return_type_notation, "1.70.0", Some(109417)), /// Allows `extern "rust-cold"`. @@ -598,7 +595,7 @@ declare_features! ( /// Allows attributes on expressions and non-item statements. (unstable, stmt_expr_attributes, "1.6.0", Some(15701)), /// Allows lints part of the strict provenance effort. - (unstable, strict_provenance, "1.61.0", Some(95228)), + (unstable, strict_provenance_lints, "1.61.0", Some(130351)), /// Allows string patterns to dereference values to match them. (unstable, string_deref_patterns, "1.67.0", Some(87121)), /// Allows the use of `#[target_feature]` on safe functions. diff --git a/compiler/rustc_fluent_macro/src/fluent.rs b/compiler/rustc_fluent_macro/src/fluent.rs index ead72e2a0e19a..d4604c27e6da3 100644 --- a/compiler/rustc_fluent_macro/src/fluent.rs +++ b/compiler/rustc_fluent_macro/src/fluent.rs @@ -8,6 +8,7 @@ use fluent_syntax::ast::{ Attribute, Entry, Expression, Identifier, InlineExpression, Message, Pattern, PatternElement, }; use fluent_syntax::parser::ParserError; +use proc_macro::tracked_path::path; use proc_macro::{Diagnostic, Level, Span}; use proc_macro2::TokenStream; use quote::quote; @@ -99,8 +100,7 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok let crate_name = Ident::new(&crate_name, resource_str.span()); - // As this macro also outputs an `include_str!` for this file, the macro will always be - // re-executed when the file changes. + path(absolute_ftl_path.to_str().unwrap()); let resource_contents = match read_to_string(absolute_ftl_path) { Ok(resource_contents) => resource_contents, Err(e) => { diff --git a/compiler/rustc_fluent_macro/src/lib.rs b/compiler/rustc_fluent_macro/src/lib.rs index 6e5add24bcc4f..3ad51fa1e64d0 100644 --- a/compiler/rustc_fluent_macro/src/lib.rs +++ b/compiler/rustc_fluent_macro/src/lib.rs @@ -6,6 +6,7 @@ #![feature(proc_macro_diagnostic)] #![feature(proc_macro_span)] #![feature(rustdoc_internals)] +#![feature(track_path)] #![warn(unreachable_pub)] // tidy-alphabetical-end diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index a9f30ffd6dac3..507297ce16216 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -356,12 +356,14 @@ hir_analysis_only_current_traits_arbitrary = only traits defined in the current hir_analysis_only_current_traits_foreign = this is not defined in the current crate because this is a foreign trait -hir_analysis_only_current_traits_label = impl doesn't use only types from inside the current crate - hir_analysis_only_current_traits_name = this is not defined in the current crate because {$name} are always foreign hir_analysis_only_current_traits_note = define and implement a trait or new type instead +hir_analysis_only_current_traits_note_more_info = for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + +hir_analysis_only_current_traits_note_uncovered = impl doesn't have any local type before any uncovered type parameters + hir_analysis_only_current_traits_opaque = type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate hir_analysis_only_current_traits_outside = only traits defined in the current crate can be implemented for types defined outside of the crate diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index 8947e7a22167b..9a2c38e51e2b8 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -9,7 +9,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, Upcast}; use rustc_span::Span; use rustc_span::def_id::DefId; -use crate::hir_ty_lowering::OnlySelfBounds; +use crate::hir_ty_lowering::PredicateFilter; /// Collects together a list of type bounds. These lists of bounds occur in many places /// in Rust's syntax: @@ -51,8 +51,8 @@ impl<'tcx> Bounds<'tcx> { bound_trait_ref: ty::PolyTraitRef<'tcx>, span: Span, polarity: ty::PredicatePolarity, - constness: ty::BoundConstness, - only_self_bounds: OnlySelfBounds, + constness: Option, + predicate_filter: PredicateFilter, ) { let clause = ( bound_trait_ref @@ -72,26 +72,36 @@ impl<'tcx> Bounds<'tcx> { // FIXME(effects): Lift this out of `push_trait_bound`, and move it somewhere else. // Perhaps moving this into `lower_poly_trait_ref`, just like we lower associated // type bounds. - if !tcx.features().effects || only_self_bounds.0 { + if !tcx.features().effects { return; } + match predicate_filter { + PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => { + return; + } + PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { + // Ok. + } + } + // For `T: ~const Tr` or `T: const Tr`, we need to add an additional bound on the // associated type of `` and make sure that the effect is compatible. let compat_val = match (tcx.def_kind(defining_def_id), constness) { // FIXME(effects): revisit the correctness of this - (_, ty::BoundConstness::Const) => tcx.consts.false_, + (_, Some(ty::BoundConstness::Const)) => tcx.consts.false_, // body owners that can have trait bounds - (DefKind::Const | DefKind::Fn | DefKind::AssocFn, ty::BoundConstness::ConstIfConst) => { - tcx.expected_host_effect_param_for_body(defining_def_id) - } + ( + DefKind::Const | DefKind::Fn | DefKind::AssocFn, + Some(ty::BoundConstness::ConstIfConst), + ) => tcx.expected_host_effect_param_for_body(defining_def_id), - (_, ty::BoundConstness::NotConst) => { + (_, None) => { if !tcx.is_const_trait(bound_trait_ref.def_id()) { return; } tcx.consts.true_ } - (DefKind::Trait, ty::BoundConstness::ConstIfConst) => { + (DefKind::Trait, Some(ty::BoundConstness::ConstIfConst)) => { // we are in a trait, where `bound_trait_ref` could be: // (1) a super trait `trait Foo: ~const Bar`. // - This generates `::Effects: TyCompat<::Effects>` @@ -129,7 +139,7 @@ impl<'tcx> Bounds<'tcx> { return; } - (DefKind::Impl { of_trait: true }, ty::BoundConstness::ConstIfConst) => { + (DefKind::Impl { of_trait: true }, Some(ty::BoundConstness::ConstIfConst)) => { // this is a where clause on an impl header. // push `::Effects` into the set for the `Min` bound. let Some(assoc) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) else { @@ -163,12 +173,12 @@ impl<'tcx> Bounds<'tcx> { // // FIXME(effects) this is equality for now, which wouldn't be helpful for a non-const implementor // that uses a `Bar` that implements `Trait` with `Maybe` effects. - (DefKind::AssocTy, ty::BoundConstness::ConstIfConst) => { + (DefKind::AssocTy, Some(ty::BoundConstness::ConstIfConst)) => { // FIXME(effects): implement this return; } // probably illegal in this position. - (_, ty::BoundConstness::ConstIfConst) => { + (_, Some(ty::BoundConstness::ConstIfConst)) => { tcx.dcx().span_delayed_bug(span, "invalid `~const` encountered"); return; } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 94da3d4ea8406..2aeeb9450ce9b 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -22,7 +22,7 @@ use rustc_middle::ty::{ AdtDef, GenericArgKind, ParamEnv, RegionKind, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; -use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS}; +use rustc_session::lint::builtin::UNINHABITED_STATIC; use rustc_target::abi::FieldIdx; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; @@ -36,36 +36,25 @@ use super::compare_impl_item::{check_type_bounds, compare_impl_method, compare_i use super::*; use crate::check::intrinsicck::InlineAsmCtxt; -pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) { - match tcx.sess.target.is_abi_supported(abi) { - Some(true) => (), - Some(false) => { - struct_span_code_err!( - tcx.dcx(), - span, - E0570, - "`{abi}` is not a supported ABI for the current target", - ) - .emit(); - } - None => { - tcx.node_span_lint(UNSUPPORTED_CALLING_CONVENTIONS, hir_id, span, |lint| { - lint.primary_message("use of calling convention not supported on this target"); - }); - } +pub fn check_abi(tcx: TyCtxt<'_>, span: Span, abi: Abi) { + if !tcx.sess.target.is_abi_supported(abi) { + struct_span_code_err!( + tcx.dcx(), + span, + E0570, + "`{abi}` is not a supported ABI for the current target", + ) + .emit(); } } pub fn check_abi_fn_ptr(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) { - match tcx.sess.target.is_abi_supported(abi) { - Some(true) => (), - Some(false) | None => { - tcx.node_span_lint(UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS, hir_id, span, |lint| { - lint.primary_message(format!( - "the calling convention {abi} is not supported on this target" - )); - }); - } + if !tcx.sess.target.is_abi_supported(abi) { + tcx.node_span_lint(UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS, hir_id, span, |lint| { + lint.primary_message(format!( + "the calling convention {abi} is not supported on this target" + )); + }); } } @@ -705,7 +694,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { let hir::ItemKind::ForeignMod { abi, items } = it.kind else { return; }; - check_abi(tcx, it.hir_id(), it.span, abi); + check_abi(tcx, it.span, abi); match abi { Abi::RustIntrinsic => { @@ -1037,7 +1026,11 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { return; } - if let Some(len) = len_const.try_eval_target_usize(tcx, tcx.param_env(def.did())) { + // FIXME(repr_simd): This check is nice, but perhaps unnecessary due to the fact + // we do not expect users to implement their own `repr(simd)` types. If they could, + // this check is easily side-steppable by hiding the const behind normalization. + // The consequence is that the error is, in general, only observable post-mono. + if let Some(len) = len_const.try_to_target_usize(tcx) { if len == 0 { struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit(); return; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index 71eb368185eb8..bbff00cd3b3c2 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -76,9 +76,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { let (size, ty) = match elem_ty.kind() { ty::Array(ty, len) => { - if let Some(len) = - len.try_eval_target_usize(self.tcx, self.tcx.param_env(adt.did())) - { + if let Some(len) = len.try_to_target_usize(self.tcx) { (len, *ty) } else { return None; diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 4346504450d0b..69ebd3a928a3a 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -13,6 +13,7 @@ use tracing::{debug, instrument}; use super::ItemCtxt; use super::predicates_of::assert_only_contains_predicates_from; +use crate::bounds::Bounds; use crate::hir_ty_lowering::{HirTyLowerer, PredicateFilter}; /// For associated types we include both bounds written on the type @@ -36,7 +37,8 @@ fn associated_type_bounds<'tcx>( ); let icx = ItemCtxt::new(tcx, assoc_item_def_id); - let mut bounds = icx.lowerer().lower_mono_bounds(item_ty, hir_bounds, filter); + let mut bounds = Bounds::default(); + icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter); // Associated types are implicitly sized unless a `?Sized` bound is found icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span); @@ -303,7 +305,8 @@ fn opaque_type_bounds<'tcx>( ) -> &'tcx [(ty::Clause<'tcx>, Span)] { ty::print::with_reduced_queries!({ let icx = ItemCtxt::new(tcx, opaque_def_id); - let mut bounds = icx.lowerer().lower_mono_bounds(item_ty, hir_bounds, filter); + let mut bounds = Bounds::default(); + icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter); // Opaque types are implicitly sized unless a `?Sized` bound is found icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span); debug!(?bounds); diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index a87b29b3093d2..9bd8c70dcfe79 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -16,7 +16,7 @@ use crate::bounds::Bounds; use crate::collect::ItemCtxt; use crate::constrained_generic_params as cgp; use crate::delegation::inherit_predicates_for_delegation_item; -use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter, RegionInferReason}; +use crate::hir_ty_lowering::{HirTyLowerer, PredicateFilter, RegionInferReason}; /// Returns a list of all type predicates (explicit and implicit) for the definition with /// ID `def_id`. This includes all predicates returned by `explicit_predicates_of`, plus @@ -181,9 +181,12 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // on a trait we must also consider the bounds that follow the trait's name, // like `trait Foo: A + B + C`. if let Some(self_bounds) = is_trait { - let bounds = icx.lowerer().lower_mono_bounds( + let mut bounds = Bounds::default(); + icx.lowerer().lower_bounds( tcx.types.self_param, self_bounds, + &mut bounds, + ty::List::empty(), PredicateFilter::All, ); predicates.extend(bounds.clauses(tcx)); @@ -265,12 +268,12 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } let mut bounds = Bounds::default(); - icx.lowerer().lower_poly_bounds( + icx.lowerer().lower_bounds( ty, - bound_pred.bounds.iter(), + bound_pred.bounds, &mut bounds, bound_vars, - OnlySelfBounds(false), + PredicateFilter::All, ); predicates.extend(bounds.clauses(tcx)); effects_min_tys.extend(bounds.effects_min_tys()); @@ -626,7 +629,7 @@ pub(super) fn implied_predicates_with_filter<'tcx>( bug!("trait_def_id {trait_def_id:?} is not an item"); }; - let (generics, bounds) = match item.kind { + let (generics, superbounds) = match item.kind { hir::ItemKind::Trait(.., generics, supertraits, _) => (generics, supertraits), hir::ItemKind::TraitAlias(generics, supertraits) => (generics, supertraits), _ => span_bug!(item.span, "super_predicates invoked on non-trait"), @@ -635,7 +638,8 @@ pub(super) fn implied_predicates_with_filter<'tcx>( let icx = ItemCtxt::new(tcx, trait_def_id); let self_param_ty = tcx.types.self_param; - let superbounds = icx.lowerer().lower_mono_bounds(self_param_ty, bounds, filter); + let mut bounds = Bounds::default(); + icx.lowerer().lower_bounds(self_param_ty, superbounds, &mut bounds, ty::List::empty(), filter); let where_bounds_that_match = icx.probe_ty_param_bounds_in_generics( generics, @@ -646,7 +650,7 @@ pub(super) fn implied_predicates_with_filter<'tcx>( // Combine the two lists to form the complete set of superbounds: let implied_bounds = - &*tcx.arena.alloc_from_iter(superbounds.clauses(tcx).chain(where_bounds_that_match)); + &*tcx.arena.alloc_from_iter(bounds.clauses(tcx).chain(where_bounds_that_match)); debug!(?implied_bounds); // Now require that immediate supertraits are lowered, which will, in @@ -825,20 +829,6 @@ impl<'tcx> ItemCtxt<'tcx> { continue; }; - // Subtle: If we're collecting `SelfAndAssociatedTypeBounds`, then we - // want to only consider predicates with `Self: ...`, but we don't want - // `OnlySelfBounds(true)` since we want to collect the nested associated - // type bound as well. - let (only_self_bounds, assoc_name) = match filter { - PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { - (OnlySelfBounds(false), None) - } - PredicateFilter::SelfOnly => (OnlySelfBounds(true), None), - PredicateFilter::SelfThatDefines(assoc_name) => { - (OnlySelfBounds(true), Some(assoc_name)) - } - }; - let bound_ty = if predicate.is_param_bound(param_def_id.to_def_id()) { ty } else if matches!(filter, PredicateFilter::All) { @@ -848,33 +838,15 @@ impl<'tcx> ItemCtxt<'tcx> { }; let bound_vars = self.tcx.late_bound_vars(predicate.hir_id); - self.lowerer().lower_poly_bounds( + self.lowerer().lower_bounds( bound_ty, - predicate.bounds.iter().filter(|bound| { - assoc_name - .map_or(true, |assoc_name| self.bound_defines_assoc_item(bound, assoc_name)) - }), + predicate.bounds, &mut bounds, bound_vars, - only_self_bounds, + filter, ); } bounds.clauses(self.tcx).collect() } - - #[instrument(level = "trace", skip(self))] - fn bound_defines_assoc_item(&self, b: &hir::GenericBound<'_>, assoc_name: Ident) -> bool { - match b { - hir::GenericBound::Trait(poly_trait_ref) => { - let trait_ref = &poly_trait_ref.trait_ref; - if let Some(trait_did) = trait_ref.trait_def_id() { - self.tcx.trait_may_define_assoc_item(trait_did, assoc_name) - } else { - false - } - } - _ => false, - } - } } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index af4445a7fd48a..77e81af3ca9a1 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1434,24 +1434,27 @@ pub(crate) enum OnlyCurrentTraits { #[diag(hir_analysis_only_current_traits_outside, code = E0117)] Outside { #[primary_span] - #[label(hir_analysis_only_current_traits_label)] span: Span, + #[note(hir_analysis_only_current_traits_note_uncovered)] + #[note(hir_analysis_only_current_traits_note_more_info)] #[note(hir_analysis_only_current_traits_note)] note: (), }, #[diag(hir_analysis_only_current_traits_primitive, code = E0117)] Primitive { #[primary_span] - #[label(hir_analysis_only_current_traits_label)] span: Span, + #[note(hir_analysis_only_current_traits_note_uncovered)] + #[note(hir_analysis_only_current_traits_note_more_info)] #[note(hir_analysis_only_current_traits_note)] note: (), }, #[diag(hir_analysis_only_current_traits_arbitrary, code = E0117)] Arbitrary { #[primary_span] - #[label(hir_analysis_only_current_traits_label)] span: Span, + #[note(hir_analysis_only_current_traits_note_uncovered)] + #[note(hir_analysis_only_current_traits_note_more_info)] #[note(hir_analysis_only_current_traits_note)] note: (), }, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 8f7ca089c9184..4721a3a0cf5ad 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -19,9 +19,7 @@ use tracing::{debug, instrument}; use super::errors::GenericsArgsErrExtend; use crate::bounds::Bounds; use crate::errors; -use crate::hir_ty_lowering::{ - AssocItemQSelf, HirTyLowerer, OnlySelfBounds, PredicateFilter, RegionInferReason, -}; +use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer, PredicateFilter, RegionInferReason}; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Add a `Sized` bound to the `bounds` if appropriate. @@ -144,31 +142,44 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// There is an implied binder around `param_ty` and `hir_bounds`. /// See `lower_poly_trait_ref` for more details. #[instrument(level = "debug", skip(self, hir_bounds, bounds))] - pub(crate) fn lower_poly_bounds<'hir, I: Iterator>>( + pub(crate) fn lower_bounds<'hir, I: IntoIterator>>( &self, param_ty: Ty<'tcx>, hir_bounds: I, bounds: &mut Bounds<'tcx>, bound_vars: &'tcx ty::List, - only_self_bounds: OnlySelfBounds, + predicate_filter: PredicateFilter, ) where 'tcx: 'hir, { for hir_bound in hir_bounds { + // In order to avoid cycles, when we're lowering `SelfThatDefines`, + // we skip over any traits that don't define the given associated type. + + if let PredicateFilter::SelfThatDefines(assoc_name) = predicate_filter { + if let Some(trait_ref) = hir_bound.trait_ref() + && let Some(trait_did) = trait_ref.trait_def_id() + && self.tcx().trait_may_define_assoc_item(trait_did, assoc_name) + { + // Okay + } else { + continue; + } + } + match hir_bound { hir::GenericBound::Trait(poly_trait_ref) => { let (constness, polarity) = match poly_trait_ref.modifiers { hir::TraitBoundModifier::Const => { - (ty::BoundConstness::Const, ty::PredicatePolarity::Positive) - } - hir::TraitBoundModifier::MaybeConst => { - (ty::BoundConstness::ConstIfConst, ty::PredicatePolarity::Positive) - } - hir::TraitBoundModifier::None => { - (ty::BoundConstness::NotConst, ty::PredicatePolarity::Positive) + (Some(ty::BoundConstness::Const), ty::PredicatePolarity::Positive) } + hir::TraitBoundModifier::MaybeConst => ( + Some(ty::BoundConstness::ConstIfConst), + ty::PredicatePolarity::Positive, + ), + hir::TraitBoundModifier::None => (None, ty::PredicatePolarity::Positive), hir::TraitBoundModifier::Negative => { - (ty::BoundConstness::NotConst, ty::PredicatePolarity::Negative) + (None, ty::PredicatePolarity::Negative) } hir::TraitBoundModifier::Maybe => continue, }; @@ -179,7 +190,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { polarity, param_ty, bounds, - only_self_bounds, + predicate_filter, ); } hir::GenericBound::Outlives(lifetime) => { @@ -200,56 +211,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - /// Lower HIR bounds into `bounds` given the self type `param_ty` and *no* overarching late-bound vars. - /// - /// ### Example - /// - /// ```ignore (illustrative) - /// fn foo() { } - /// // ^ ^^^^^^^^^ hir_bounds - /// // param_ty - /// ``` - pub(crate) fn lower_mono_bounds( - &self, - param_ty: Ty<'tcx>, - hir_bounds: &[hir::GenericBound<'tcx>], - filter: PredicateFilter, - ) -> Bounds<'tcx> { - let mut bounds = Bounds::default(); - - let only_self_bounds = match filter { - PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { - OnlySelfBounds(false) - } - PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => OnlySelfBounds(true), - }; - - self.lower_poly_bounds( - param_ty, - hir_bounds.iter().filter(|bound| match filter { - PredicateFilter::All - | PredicateFilter::SelfOnly - | PredicateFilter::SelfAndAssociatedTypeBounds => true, - PredicateFilter::SelfThatDefines(assoc_name) => { - if let Some(trait_ref) = bound.trait_ref() - && let Some(trait_did) = trait_ref.trait_def_id() - && self.tcx().trait_may_define_assoc_item(trait_did, assoc_name) - { - true - } else { - false - } - } - }), - &mut bounds, - ty::List::empty(), - only_self_bounds, - ); - debug!(?bounds); - - bounds - } - /// Lower an associated item constraint from the HIR into `bounds`. /// /// ### A Note on Binders @@ -267,7 +228,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bounds: &mut Bounds<'tcx>, duplicates: &mut FxIndexMap, path_span: Span, - only_self_bounds: OnlySelfBounds, + predicate_filter: PredicateFilter, ) -> Result<(), ErrorGuaranteed> { let tcx = self.tcx(); @@ -444,21 +405,23 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Lower a constraint like `Item: Debug` as found in HIR bound `T: Iterator` // to a bound involving a projection: `::Item: Debug`. hir::AssocItemConstraintKind::Bound { bounds: hir_bounds } => { - // NOTE: If `only_self_bounds` is true, do NOT expand this associated type bound into - // a trait predicate, since we only want to add predicates for the `Self` type. - if !only_self_bounds.0 { - let projection_ty = projection_term - .map_bound(|projection_term| projection_term.expect_ty(self.tcx())); - // Calling `skip_binder` is okay, because `lower_bounds` expects the `param_ty` - // parameter to have a skipped binder. - let param_ty = Ty::new_alias(tcx, ty::Projection, projection_ty.skip_binder()); - self.lower_poly_bounds( - param_ty, - hir_bounds.iter(), - bounds, - projection_ty.bound_vars(), - only_self_bounds, - ); + match predicate_filter { + PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => {} + PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { + let projection_ty = projection_term + .map_bound(|projection_term| projection_term.expect_ty(self.tcx())); + // Calling `skip_binder` is okay, because `lower_bounds` expects the `param_ty` + // parameter to have a skipped binder. + let param_ty = + Ty::new_alias(tcx, ty::Projection, projection_ty.skip_binder()); + self.lower_bounds( + param_ty, + hir_bounds, + bounds, + projection_ty.bound_vars(), + predicate_filter, + ); + } } } } @@ -516,7 +479,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty, trait_segment, false, - ty::BoundConstness::NotConst, ); // SUBTLE: As noted at the end of `try_append_return_type_notation_params` diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index 98822eec2ac4d..a1ee120e855fd 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -20,7 +20,7 @@ use tracing::{debug, instrument}; use super::HirTyLowerer; use crate::bounds::Bounds; use crate::hir_ty_lowering::{ - GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds, RegionInferReason, + GenericArgCountMismatch, GenericArgCountResult, PredicateFilter, RegionInferReason, }; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { @@ -51,13 +51,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } = self.lower_poly_trait_ref( &trait_bound.trait_ref, trait_bound.span, - ty::BoundConstness::NotConst, + None, ty::PredicatePolarity::Positive, dummy_self, &mut bounds, - // True so we don't populate `bounds` with associated type bounds, even - // though they're disallowed from object types. - OnlySelfBounds(true), + PredicateFilter::SelfOnly, ) { potential_assoc_types.extend(cur_potential_assoc_types); } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index d760acf53bdd8..fe0cd572609fa 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -64,9 +64,6 @@ use crate::require_c_abi_if_c_variadic; #[derive(Debug)] pub struct GenericPathSegment(pub DefId, pub usize); -#[derive(Copy, Clone, Debug)] -pub struct OnlySelfBounds(pub bool); - #[derive(Copy, Clone, Debug)] pub enum PredicateFilter { /// All predicates may be implied by the trait. @@ -76,7 +73,8 @@ pub enum PredicateFilter { SelfOnly, /// Only traits that reference `Self: ..` and define an associated type - /// with the given ident are implied by the trait. + /// with the given ident are implied by the trait. This mode exists to + /// side-step query cycles when lowering associated types. SelfThatDefines(Ident), /// Only traits that reference `Self: ..` and their associated type bounds. @@ -336,14 +334,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { def_id: DefId, item_segment: &hir::PathSegment<'tcx>, ) -> GenericArgsRef<'tcx> { - let (args, _) = self.lower_generic_args_of_path( - span, - def_id, - &[], - item_segment, - None, - ty::BoundConstness::NotConst, - ); + let (args, _) = self.lower_generic_args_of_path(span, def_id, &[], item_segment, None); if let Some(c) = item_segment.args().constraints.first() { prohibit_assoc_item_constraint(self, c, Some((def_id, item_segment, span))); } @@ -392,7 +383,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { parent_args: &[ty::GenericArg<'tcx>], segment: &hir::PathSegment<'tcx>, self_ty: Option>, - constness: ty::BoundConstness, ) -> (GenericArgsRef<'tcx>, GenericArgCountResult) { // If the type is parameterized by this region, then replace this // region with the current anon region binding (in other words, @@ -415,7 +405,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assert!(self_ty.is_none()); } - let mut arg_count = check_generic_arg_count( + let arg_count = check_generic_arg_count( self, def_id, segment, @@ -573,16 +563,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } } - if let ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst = constness - && generics.has_self - && !tcx.is_const_trait(def_id) - { - let reported = self.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait { - span, - modifier: constness.as_str(), - }); - arg_count.correct = Err(GenericArgCountMismatch { reported, invalid_args: vec![] }); - } let mut args_ctx = GenericArgsCtxt { lowerer: self, @@ -614,14 +594,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { parent_args: GenericArgsRef<'tcx>, ) -> GenericArgsRef<'tcx> { debug!(?span, ?item_def_id, ?item_segment); - let (args, _) = self.lower_generic_args_of_path( - span, - item_def_id, - parent_args, - item_segment, - None, - ty::BoundConstness::NotConst, - ); + let (args, _) = + self.lower_generic_args_of_path(span, item_def_id, parent_args, item_segment, None); if let Some(c) = item_segment.args().constraints.first() { prohibit_assoc_item_constraint(self, c, Some((item_def_id, item_segment, span))); } @@ -647,7 +621,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty, trait_ref.path.segments.last().unwrap(), true, - ty::BoundConstness::NotConst, ) } @@ -679,11 +652,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, trait_ref: &hir::TraitRef<'tcx>, span: Span, - constness: ty::BoundConstness, + constness: Option, polarity: ty::PredicatePolarity, self_ty: Ty<'tcx>, bounds: &mut Bounds<'tcx>, - only_self_bounds: OnlySelfBounds, + predicate_filter: PredicateFilter, ) -> GenericArgCountResult { let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()); let trait_segment = trait_ref.path.segments.last().unwrap(); @@ -700,9 +673,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &[], trait_segment, Some(self_ty), - constness, ); + if let Some(constness) = constness + && !self.tcx().is_const_trait(trait_def_id) + { + self.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait { + span: trait_ref.path.span, + modifier: constness.as_str(), + }); + } + let tcx = self.tcx(); let bound_vars = tcx.late_bound_vars(trait_ref.hir_ref_id); debug!(?bound_vars); @@ -720,7 +701,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span, polarity, constness, - only_self_bounds, + predicate_filter, ); let mut dup_constraints = FxIndexMap::default(); @@ -744,7 +725,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bounds, &mut dup_constraints, constraint.span, - only_self_bounds, + predicate_filter, ); // Okay to ignore `Err` because of `ErrorGuaranteed` (see above). } @@ -762,19 +743,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty: Ty<'tcx>, trait_segment: &hir::PathSegment<'tcx>, is_impl: bool, - // FIXME(effects): Move all host param things in HIR ty lowering to AST lowering. - constness: ty::BoundConstness, ) -> ty::TraitRef<'tcx> { self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl); - let (generic_args, _) = self.lower_generic_args_of_path( - span, - trait_def_id, - &[], - trait_segment, - Some(self_ty), - constness, - ); + let (generic_args, _) = + self.lower_generic_args_of_path(span, trait_def_id, &[], trait_segment, Some(self_ty)); if let Some(c) = trait_segment.args().constraints.first() { prohibit_assoc_item_constraint(self, c, Some((trait_def_id, trait_segment, span))); } @@ -1542,7 +1515,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { item_def_id: DefId, trait_segment: &hir::PathSegment<'tcx>, item_segment: &hir::PathSegment<'tcx>, - constness: ty::BoundConstness, ) -> Ty<'tcx> { let tcx = self.tcx(); @@ -1555,7 +1527,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { debug!(?self_ty); let trait_ref = - self.lower_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false, constness); + self.lower_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false); debug!(?trait_ref); let item_args = @@ -1918,7 +1890,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { def_id, &path.segments[path.segments.len() - 2], path.segments.last().unwrap(), - ty::BoundConstness::NotConst, ) } Res::PrimTy(prim_ty) => { @@ -2151,7 +2122,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &[], &hir::PathSegment::invalid(), None, - ty::BoundConstness::NotConst, ); tcx.at(span).type_of(def_id).instantiate(tcx, args) } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 13ba615d4c927..fa6a86c691188 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -537,40 +537,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // This check is here because there is currently no way to express a trait bound for `FnDef` types only. if let ty::FnDef(def_id, _args) = *arg_ty.kind() { - let fn_once_def_id = - self.tcx.require_lang_item(hir::LangItem::FnOnce, Some(span)); - let fn_once_output_def_id = - self.tcx.require_lang_item(hir::LangItem::FnOnceOutput, Some(span)); - if self.tcx.has_host_param(fn_once_def_id) { - let const_param: ty::GenericArg<'tcx> = - ([self.tcx.consts.false_, self.tcx.consts.true_])[idx].into(); - self.register_predicate(traits::Obligation::new( - self.tcx, - self.misc(span), - self.param_env, - ty::TraitRef::new(self.tcx, fn_once_def_id, [ - arg_ty.into(), - fn_sig.inputs()[0].into(), - const_param, - ]), - )); - - self.register_predicate(traits::Obligation::new( - self.tcx, - self.misc(span), - self.param_env, - ty::ProjectionPredicate { - projection_term: ty::AliasTerm::new( - self.tcx, - fn_once_output_def_id, - [arg_ty.into(), fn_sig.inputs()[0].into(), const_param], - ), - term: fn_sig.output().into(), - }, - )); - - self.select_obligations_where_possible(|_| {}); - } else if idx == 0 && !self.tcx.is_const_fn_raw(def_id) { + if idx == 0 && !self.tcx.is_const_fn_raw(def_id) { self.dcx().emit_err(errors::ConstSelectMustBeConst { span }); } } else { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 7bdd3c95ad181..f4d7b59e9c865 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1320,84 +1320,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let expected_ty = expected.coercion_target_type(self, expr.span); if expected_ty == self.tcx.types.bool { - // The expected type is `bool` but this will result in `()` so we can reasonably - // say that the user intended to write `lhs == rhs` instead of `lhs = rhs`. - // The likely cause of this is `if foo = bar { .. }`. - let actual_ty = self.tcx.types.unit; - let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap_err(); - let lhs_ty = self.check_expr(lhs); - let rhs_ty = self.check_expr(rhs); - let refs_can_coerce = |lhs: Ty<'tcx>, rhs: Ty<'tcx>| { - let lhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, lhs.peel_refs()); - let rhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, rhs.peel_refs()); - self.may_coerce(rhs, lhs) - }; - let (applicability, eq) = if self.may_coerce(rhs_ty, lhs_ty) { - (Applicability::MachineApplicable, true) - } else if refs_can_coerce(rhs_ty, lhs_ty) { - // The lhs and rhs are likely missing some references in either side. Subsequent - // suggestions will show up. - (Applicability::MaybeIncorrect, true) - } else if let ExprKind::Binary( - Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, - _, - rhs_expr, - ) = lhs.kind - { - // if x == 1 && y == 2 { .. } - // + - let actual_lhs_ty = self.check_expr(rhs_expr); - ( - Applicability::MaybeIncorrect, - self.may_coerce(rhs_ty, actual_lhs_ty) - || refs_can_coerce(rhs_ty, actual_lhs_ty), - ) - } else if let ExprKind::Binary( - Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, - lhs_expr, - _, - ) = rhs.kind - { - // if x == 1 && y == 2 { .. } - // + - let actual_rhs_ty = self.check_expr(lhs_expr); - ( - Applicability::MaybeIncorrect, - self.may_coerce(actual_rhs_ty, lhs_ty) - || refs_can_coerce(actual_rhs_ty, lhs_ty), - ) - } else { - (Applicability::MaybeIncorrect, false) - }; - if !lhs.is_syntactic_place_expr() - && lhs.is_approximately_pattern() - && !matches!(lhs.kind, hir::ExprKind::Lit(_)) - { - // Do not suggest `if let x = y` as `==` is way more likely to be the intention. - if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) = - self.tcx.parent_hir_node(expr.hir_id) - { - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "you might have meant to use pattern matching", - "let ", - applicability, - ); - }; - } - if eq { - err.span_suggestion_verbose( - span.shrink_to_hi(), - "you might have meant to compare for equality", - '=', - applicability, - ); - } - - // If the assignment expression itself is ill-formed, don't - // bother emitting another error - let reported = err.emit_unless(lhs_ty.references_error() || rhs_ty.references_error()); - return Ty::new_error(self.tcx, reported); + let guar = self.expr_assign_expected_bool_error(expr, lhs, rhs, span); + return Ty::new_error(self.tcx, guar); } let lhs_ty = self.check_expr_with_needs(lhs, Needs::MutPlace); @@ -1450,6 +1374,88 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// The expected type is `bool` but this will result in `()` so we can reasonably + /// say that the user intended to write `lhs == rhs` instead of `lhs = rhs`. + /// The likely cause of this is `if foo = bar { .. }`. + fn expr_assign_expected_bool_error( + &self, + expr: &'tcx hir::Expr<'tcx>, + lhs: &'tcx hir::Expr<'tcx>, + rhs: &'tcx hir::Expr<'tcx>, + span: Span, + ) -> ErrorGuaranteed { + let actual_ty = self.tcx.types.unit; + let expected_ty = self.tcx.types.bool; + let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap_err(); + let lhs_ty = self.check_expr(lhs); + let rhs_ty = self.check_expr(rhs); + let refs_can_coerce = |lhs: Ty<'tcx>, rhs: Ty<'tcx>| { + let lhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, lhs.peel_refs()); + let rhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, rhs.peel_refs()); + self.may_coerce(rhs, lhs) + }; + let (applicability, eq) = if self.may_coerce(rhs_ty, lhs_ty) { + (Applicability::MachineApplicable, true) + } else if refs_can_coerce(rhs_ty, lhs_ty) { + // The lhs and rhs are likely missing some references in either side. Subsequent + // suggestions will show up. + (Applicability::MaybeIncorrect, true) + } else if let ExprKind::Binary( + Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, + _, + rhs_expr, + ) = lhs.kind + { + // if x == 1 && y == 2 { .. } + // + + let actual_lhs = self.check_expr(rhs_expr); + let may_eq = self.may_coerce(rhs_ty, actual_lhs) || refs_can_coerce(rhs_ty, actual_lhs); + (Applicability::MaybeIncorrect, may_eq) + } else if let ExprKind::Binary( + Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, + lhs_expr, + _, + ) = rhs.kind + { + // if x == 1 && y == 2 { .. } + // + + let actual_rhs = self.check_expr(lhs_expr); + let may_eq = self.may_coerce(actual_rhs, lhs_ty) || refs_can_coerce(actual_rhs, lhs_ty); + (Applicability::MaybeIncorrect, may_eq) + } else { + (Applicability::MaybeIncorrect, false) + }; + + if !lhs.is_syntactic_place_expr() + && lhs.is_approximately_pattern() + && !matches!(lhs.kind, hir::ExprKind::Lit(_)) + { + // Do not suggest `if let x = y` as `==` is way more likely to be the intention. + if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) = + self.tcx.parent_hir_node(expr.hir_id) + { + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "you might have meant to use pattern matching", + "let ", + applicability, + ); + }; + } + if eq { + err.span_suggestion_verbose( + span.shrink_to_hi(), + "you might have meant to compare for equality", + '=', + applicability, + ); + } + + // If the assignment expression itself is ill-formed, don't + // bother emitting another error + err.emit_unless(lhs_ty.references_error() || rhs_ty.references_error()) + } + pub(super) fn check_expr_let( &self, let_expr: &'tcx hir::LetExpr<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index eccb18ad6c443..2bc84d1ac6735 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1487,7 +1487,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } else if self.tcx.features().generic_const_exprs { - ct.normalize(self.tcx, self.param_env) + ct.normalize_internal(self.tcx, self.param_env) } else { ct } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index bf8cc462189fb..f29d6199a875e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1097,6 +1097,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut only_extras_so_far = errors .peek() .is_some_and(|first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0)); + let mut prev_extra_idx = None; let mut suggestions = vec![]; while let Some(error) = errors.next() { only_extras_so_far &= matches!(error, Error::Extra(_)); @@ -1165,11 +1166,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // fn f() {} // - f(0, 1,) // + f() - if only_extras_so_far - && !errors - .peek() - .is_some_and(|next_error| matches!(next_error, Error::Extra(_))) - { + let trim_next_comma = match errors.peek() { + Some(Error::Extra(provided_idx)) + if only_extras_so_far + && provided_idx.index() > arg_idx.index() + 1 => + // If the next Error::Extra ("next") doesn't next to current ("current"), + // fn foo(_: (), _: u32) {} + // - foo("current", (), 1u32, "next") + // + foo((), 1u32) + // If the previous error is not a `Error::Extra`, then do not trim the next comma + // - foo((), "current", 42u32, "next") + // + foo((), 42u32) + { + prev_extra_idx.map_or(true, |prev_extra_idx| { + prev_extra_idx + 1 == arg_idx.index() + }) + } + // If no error left, we need to delete the next comma + None if only_extras_so_far => true, + // Not sure if other error type need to be handled as well + _ => false, + }; + + if trim_next_comma { let next = provided_arg_tys .get(arg_idx + 1) .map(|&(_, sp)| sp) @@ -1192,6 +1211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { SuggestionText::Remove(_) => SuggestionText::Remove(true), _ => SuggestionText::DidYouMean, }; + prev_extra_idx = Some(arg_idx.index()) } } Error::Missing(expected_idx) => { diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 6b0a897faba94..85ee0a5cf7dab 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -155,7 +155,7 @@ fn typeck_with_fallback<'tcx>( tcx.fn_sig(def_id).instantiate_identity() }; - check_abi(tcx, id, span, fn_sig.abi()); + check_abi(tcx, span, fn_sig.abi()); // Compute the function signature from point of view of inside the fn. let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); @@ -419,7 +419,7 @@ fn report_unexpected_variant_res( } } - err.multipart_suggestion_verbose(descr, suggestion, Applicability::MaybeIncorrect); + err.multipart_suggestion_verbose(descr, suggestion, Applicability::HasPlaceholders); err } Res::Def(DefKind::Variant, _) if expr.is_none() => { diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index ba6bfd3a5e940..1be711887d9fa 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -340,13 +340,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { OP: FnOnce(ProbeContext<'_, 'tcx>) -> Result>, { let mut orig_values = OriginalQueryValues::default(); - let param_env_and_self_ty = self.canonicalize_query( + let query_input = self.canonicalize_query( ParamEnvAnd { param_env: self.param_env, value: self_ty }, &mut orig_values, ); let steps = match mode { - Mode::MethodCall => self.tcx.method_autoderef_steps(param_env_and_self_ty), + Mode::MethodCall => self.tcx.method_autoderef_steps(query_input), Mode::Path => self.probe(|_| { // Mode::Path - the deref steps is "trivial". This turns // our CanonicalQuery into a "trivial" QueryResponse. This @@ -355,11 +355,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let infcx = &self.infcx; let (ParamEnvAnd { param_env: _, value: self_ty }, canonical_inference_vars) = - infcx.instantiate_canonical(span, ¶m_env_and_self_ty); - debug!( - "probe_op: Mode::Path, param_env_and_self_ty={:?} self_ty={:?}", - param_env_and_self_ty, self_ty - ); + infcx.instantiate_canonical(span, &query_input.canonical); + debug!(?self_ty, ?query_input, "probe_op: Mode::Path"); MethodAutoderefStepsResult { steps: infcx.tcx.arena.alloc_from_iter([CandidateStep { self_ty: self.make_query_response_ignoring_pending_obligations( diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 35ea42338250d..e3519dfb02852 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -17,7 +17,8 @@ use tracing::debug; use crate::infer::InferCtxt; use crate::infer::canonical::{ - Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, OriginalQueryValues, + Canonical, CanonicalQueryInput, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, + OriginalQueryValues, }; impl<'tcx> InferCtxt<'tcx> { @@ -40,12 +41,12 @@ impl<'tcx> InferCtxt<'tcx> { &self, value: ty::ParamEnvAnd<'tcx, V>, query_state: &mut OriginalQueryValues<'tcx>, - ) -> Canonical<'tcx, ty::ParamEnvAnd<'tcx, V>> + ) -> CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, V>> where V: TypeFoldable>, { let (param_env, value) = value.into_parts(); - let mut param_env = self.tcx.canonical_param_env_cache.get_or_insert( + let param_env = self.tcx.canonical_param_env_cache.get_or_insert( self.tcx, param_env, query_state, @@ -62,9 +63,7 @@ impl<'tcx> InferCtxt<'tcx> { }, ); - param_env.defining_opaque_types = self.defining_opaque_types; - - Canonicalizer::canonicalize_with_base( + let canonical = Canonicalizer::canonicalize_with_base( param_env, value, Some(self), @@ -72,7 +71,8 @@ impl<'tcx> InferCtxt<'tcx> { &CanonicalizeAllFreeRegions, query_state, ) - .unchecked_map(|(param_env, value)| param_env.and(value)) + .unchecked_map(|(param_env, value)| param_env.and(value)); + CanonicalQueryInput { canonical, defining_opaque_types: self.defining_opaque_types() } } /// Canonicalizes a query *response* `V`. When we canonicalize a @@ -544,7 +544,6 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { max_universe: ty::UniverseIndex::ROOT, variables: List::empty(), value: (), - defining_opaque_types: infcx.map(|i| i.defining_opaque_types).unwrap_or_default(), }; Canonicalizer::canonicalize_with_base( base, @@ -614,15 +613,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { .max() .unwrap_or(ty::UniverseIndex::ROOT); - assert!( - !infcx.is_some_and(|infcx| infcx.defining_opaque_types != base.defining_opaque_types) - ); - Canonical { - max_universe, - variables: canonical_variables, - value: (base.value, out_value), - defining_opaque_types: base.defining_opaque_types, - } + Canonical { max_universe, variables: canonical_variables, value: (base.value, out_value) } } /// Creates a canonical variable replacing `kind` from the input, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 7ef714475fc2a..5afdf3c245402 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -25,7 +25,7 @@ use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_macros::extension; pub use rustc_macros::{TypeFoldable, TypeVisitable}; -use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; +use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues}; use rustc_middle::infer::unify_key::{ ConstVariableOrigin, ConstVariableValue, ConstVidKey, EffectVarValue, EffectVidKey, }; @@ -606,14 +606,14 @@ impl<'tcx> InferCtxtBuilder<'tcx> { pub fn build_with_canonical( mut self, span: Span, - canonical: &Canonical<'tcx, T>, + input: &CanonicalQueryInput<'tcx, T>, ) -> (InferCtxt<'tcx>, T, CanonicalVarValues<'tcx>) where T: TypeFoldable>, { - self.defining_opaque_types = canonical.defining_opaque_types; + self.defining_opaque_types = input.defining_opaque_types; let infcx = self.build(); - let (value, args) = infcx.instantiate_canonical(span, canonical); + let (value, args) = infcx.instantiate_canonical(span, &input.canonical); (infcx, value, args) } @@ -899,6 +899,13 @@ impl<'tcx> InferCtxt<'tcx> { ty::Const::new_var(self.tcx, vid) } + fn next_effect_var(&self) -> ty::Const<'tcx> { + let effect_vid = + self.inner.borrow_mut().effect_unification_table().new_key(EffectVarValue::Unknown).vid; + + ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(effect_vid)) + } + pub fn next_int_var(&self) -> Ty<'tcx> { let next_int_var_id = self.inner.borrow_mut().int_unification_table().new_key(ty::IntVarValue::Unknown); @@ -1001,15 +1008,13 @@ impl<'tcx> InferCtxt<'tcx> { } pub fn var_for_effect(&self, param: &ty::GenericParamDef) -> GenericArg<'tcx> { - let effect_vid = - self.inner.borrow_mut().effect_unification_table().new_key(EffectVarValue::Unknown).vid; let ty = self .tcx .type_of(param.def_id) .no_bound_vars() .expect("const parameter types cannot be generic"); debug_assert_eq!(self.tcx.types.bool, ty); - ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(effect_vid)).into() + self.next_effect_var().into() } /// Given a set of generics defined on a type or impl, returns the generic parameters mapping diff --git a/compiler/rustc_infer/src/infer/snapshot/fudge.rs b/compiler/rustc_infer/src/infer/snapshot/fudge.rs index 8e330a084c65d..613cebc266dd9 100644 --- a/compiler/rustc_infer/src/infer/snapshot/fudge.rs +++ b/compiler/rustc_infer/src/infer/snapshot/fudge.rs @@ -4,9 +4,12 @@ use rustc_data_structures::{snapshot_vec as sv, unify as ut}; use rustc_middle::infer::unify_key::{ConstVariableValue, ConstVidKey}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid}; +use rustc_type_ir::EffectVid; +use rustc_type_ir::visit::TypeVisitableExt; use tracing::instrument; use ut::UnifyKey; +use super::VariableLengths; use crate::infer::type_variable::TypeVariableOrigin; use crate::infer::{ConstVariableOrigin, InferCtxt, RegionVariableOrigin, UnificationTable}; @@ -40,26 +43,7 @@ fn const_vars_since_snapshot<'tcx>( ) } -struct VariableLengths { - type_var_len: usize, - const_var_len: usize, - int_var_len: usize, - float_var_len: usize, - region_constraints_len: usize, -} - impl<'tcx> InferCtxt<'tcx> { - fn variable_lengths(&self) -> VariableLengths { - let mut inner = self.inner.borrow_mut(); - VariableLengths { - type_var_len: inner.type_variables().num_vars(), - const_var_len: inner.const_unification_table().len(), - int_var_len: inner.int_unification_table().len(), - float_var_len: inner.float_unification_table().len(), - region_constraints_len: inner.unwrap_region_constraints().num_region_vars(), - } - } - /// This rather funky routine is used while processing expected /// types. What happens here is that we want to propagate a /// coercion through the return type of a fn to its @@ -106,78 +90,94 @@ impl<'tcx> InferCtxt<'tcx> { T: TypeFoldable>, { let variable_lengths = self.variable_lengths(); - let (mut fudger, value) = self.probe(|_| { - match f() { - Ok(value) => { - let value = self.resolve_vars_if_possible(value); - - // At this point, `value` could in principle refer - // to inference variables that have been created during - // the snapshot. Once we exit `probe()`, those are - // going to be popped, so we will have to - // eliminate any references to them. - - let mut inner = self.inner.borrow_mut(); - let type_vars = - inner.type_variables().vars_since_snapshot(variable_lengths.type_var_len); - let int_vars = vars_since_snapshot( - &inner.int_unification_table(), - variable_lengths.int_var_len, - ); - let float_vars = vars_since_snapshot( - &inner.float_unification_table(), - variable_lengths.float_var_len, - ); - let region_vars = inner - .unwrap_region_constraints() - .vars_since_snapshot(variable_lengths.region_constraints_len); - let const_vars = const_vars_since_snapshot( - &mut inner.const_unification_table(), - variable_lengths.const_var_len, - ); - - let fudger = InferenceFudger { - infcx: self, - type_vars, - int_vars, - float_vars, - region_vars, - const_vars, - }; - - Ok((fudger, value)) - } - Err(e) => Err(e), - } + let (snapshot_vars, value) = self.probe(|_| { + let value = f()?; + // At this point, `value` could in principle refer + // to inference variables that have been created during + // the snapshot. Once we exit `probe()`, those are + // going to be popped, so we will have to + // eliminate any references to them. + let snapshot_vars = SnapshotVarData::new(self, variable_lengths); + Ok((snapshot_vars, self.resolve_vars_if_possible(value))) })?; // At this point, we need to replace any of the now-popped // type/region variables that appear in `value` with a fresh // variable of the appropriate kind. We can't do this during // the probe because they would just get popped then too. =) + Ok(self.fudge_inference(snapshot_vars, value)) + } + fn fudge_inference>>( + &self, + snapshot_vars: SnapshotVarData, + value: T, + ) -> T { // Micro-optimization: if no variables have been created, then // `value` can't refer to any of them. =) So we can just return it. - if fudger.type_vars.0.is_empty() - && fudger.int_vars.is_empty() - && fudger.float_vars.is_empty() - && fudger.region_vars.0.is_empty() - && fudger.const_vars.0.is_empty() - { - Ok(value) + if snapshot_vars.is_empty() { + value } else { - Ok(value.fold_with(&mut fudger)) + value.fold_with(&mut InferenceFudger { infcx: self, snapshot_vars }) } } } -struct InferenceFudger<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, +struct SnapshotVarData { + region_vars: (Range, Vec), type_vars: (Range, Vec), int_vars: Range, float_vars: Range, - region_vars: (Range, Vec), const_vars: (Range, Vec), + effect_vars: Range, +} + +impl SnapshotVarData { + fn new(infcx: &InferCtxt<'_>, vars_pre_snapshot: VariableLengths) -> SnapshotVarData { + let mut inner = infcx.inner.borrow_mut(); + let region_vars = inner + .unwrap_region_constraints() + .vars_since_snapshot(vars_pre_snapshot.region_constraints_len); + let type_vars = inner.type_variables().vars_since_snapshot(vars_pre_snapshot.type_var_len); + let int_vars = + vars_since_snapshot(&inner.int_unification_table(), vars_pre_snapshot.int_var_len); + let float_vars = + vars_since_snapshot(&inner.float_unification_table(), vars_pre_snapshot.float_var_len); + + let const_vars = const_vars_since_snapshot( + &mut inner.const_unification_table(), + vars_pre_snapshot.const_var_len, + ); + let effect_vars = vars_since_snapshot( + &inner.effect_unification_table(), + vars_pre_snapshot.effect_var_len, + ); + let effect_vars = effect_vars.start.vid..effect_vars.end.vid; + + SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars, effect_vars } + } + + fn is_empty(&self) -> bool { + let SnapshotVarData { + region_vars, + type_vars, + int_vars, + float_vars, + const_vars, + effect_vars, + } = self; + region_vars.0.is_empty() + && type_vars.0.is_empty() + && int_vars.is_empty() + && float_vars.is_empty() + && const_vars.0.is_empty() + && effect_vars.is_empty() + } +} + +struct InferenceFudger<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, + snapshot_vars: SnapshotVarData, } impl<'a, 'tcx> TypeFolder> for InferenceFudger<'a, 'tcx> { @@ -186,68 +186,93 @@ impl<'a, 'tcx> TypeFolder> for InferenceFudger<'a, 'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match *ty.kind() { - ty::Infer(ty::InferTy::TyVar(vid)) => { - if self.type_vars.0.contains(&vid) { - // This variable was created during the fudging. - // Recreate it with a fresh variable here. - let idx = vid.as_usize() - self.type_vars.0.start.as_usize(); - let origin = self.type_vars.1[idx]; - self.infcx.next_ty_var_with_origin(origin) - } else { - // This variable was created before the - // "fudging". Since we refresh all type - // variables to their binding anyhow, we know - // that it is unbound, so we can just return - // it. - debug_assert!( - self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown() - ); - ty + if let &ty::Infer(infer_ty) = ty.kind() { + match infer_ty { + ty::TyVar(vid) => { + if self.snapshot_vars.type_vars.0.contains(&vid) { + // This variable was created during the fudging. + // Recreate it with a fresh variable here. + let idx = vid.as_usize() - self.snapshot_vars.type_vars.0.start.as_usize(); + let origin = self.snapshot_vars.type_vars.1[idx]; + self.infcx.next_ty_var_with_origin(origin) + } else { + // This variable was created before the + // "fudging". Since we refresh all type + // variables to their binding anyhow, we know + // that it is unbound, so we can just return + // it. + debug_assert!( + self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown() + ); + ty + } } - } - ty::Infer(ty::InferTy::IntVar(vid)) => { - if self.int_vars.contains(&vid) { - self.infcx.next_int_var() - } else { - ty + ty::IntVar(vid) => { + if self.snapshot_vars.int_vars.contains(&vid) { + self.infcx.next_int_var() + } else { + ty + } } - } - ty::Infer(ty::InferTy::FloatVar(vid)) => { - if self.float_vars.contains(&vid) { - self.infcx.next_float_var() - } else { - ty + ty::FloatVar(vid) => { + if self.snapshot_vars.float_vars.contains(&vid) { + self.infcx.next_float_var() + } else { + ty + } + } + ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => { + unreachable!("unexpected fresh infcx var") } } - _ => ty.super_fold_with(self), + } else if ty.has_infer() { + ty.super_fold_with(self) + } else { + ty } } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - if let ty::ReVar(vid) = *r - && self.region_vars.0.contains(&vid) - { - let idx = vid.index() - self.region_vars.0.start.index(); - let origin = self.region_vars.1[idx]; - return self.infcx.next_region_var(origin); + if let ty::ReVar(vid) = r.kind() { + if self.snapshot_vars.region_vars.0.contains(&vid) { + let idx = vid.index() - self.snapshot_vars.region_vars.0.start.index(); + let origin = self.snapshot_vars.region_vars.1[idx]; + self.infcx.next_region_var(origin) + } else { + r + } + } else { + r } - r } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.kind() { - if self.const_vars.0.contains(&vid) { - // This variable was created during the fudging. - // Recreate it with a fresh variable here. - let idx = vid.index() - self.const_vars.0.start.index(); - let origin = self.const_vars.1[idx]; - self.infcx.next_const_var_with_origin(origin) - } else { - ct + if let ty::ConstKind::Infer(infer_ct) = ct.kind() { + match infer_ct { + ty::InferConst::Var(vid) => { + if self.snapshot_vars.const_vars.0.contains(&vid) { + let idx = vid.index() - self.snapshot_vars.const_vars.0.start.index(); + let origin = self.snapshot_vars.const_vars.1[idx]; + self.infcx.next_const_var_with_origin(origin) + } else { + ct + } + } + ty::InferConst::EffectVar(vid) => { + if self.snapshot_vars.effect_vars.contains(&vid) { + self.infcx.next_effect_var() + } else { + ct + } + } + ty::InferConst::Fresh(_) => { + unreachable!("unexpected fresh infcx var") + } } - } else { + } else if ct.has_infer() { ct.super_fold_with(self) + } else { + ct } } } diff --git a/compiler/rustc_infer/src/infer/snapshot/mod.rs b/compiler/rustc_infer/src/infer/snapshot/mod.rs index 07a482c2f9ac3..fa813500c544e 100644 --- a/compiler/rustc_infer/src/infer/snapshot/mod.rs +++ b/compiler/rustc_infer/src/infer/snapshot/mod.rs @@ -17,7 +17,28 @@ pub struct CombinedSnapshot<'tcx> { universe: ty::UniverseIndex, } +struct VariableLengths { + region_constraints_len: usize, + type_var_len: usize, + int_var_len: usize, + float_var_len: usize, + const_var_len: usize, + effect_var_len: usize, +} + impl<'tcx> InferCtxt<'tcx> { + fn variable_lengths(&self) -> VariableLengths { + let mut inner = self.inner.borrow_mut(); + VariableLengths { + region_constraints_len: inner.unwrap_region_constraints().num_region_vars(), + type_var_len: inner.type_variables().num_vars(), + int_var_len: inner.int_unification_table().len(), + float_var_len: inner.float_unification_table().len(), + const_var_len: inner.const_unification_table().len(), + effect_var_len: inner.effect_unification_table().len(), + } + } + pub fn in_snapshot(&self) -> bool { UndoLogs::>::in_snapshot(&self.inner.borrow_mut().undo_log) } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 8bd9c899a6286..ef8100da9e2e7 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -2601,7 +2601,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { ty.tuple_fields().iter().find_map(|field| ty_find_init_error(cx, field, init)) } Array(ty, len) => { - if matches!(len.try_eval_target_usize(cx.tcx, cx.param_env), Some(v) if v > 0) { + if matches!(len.try_to_target_usize(cx.tcx), Some(v) if v > 0) { // Array length known at array non-empty -- recurse. ty_find_init_error(cx, *ty, init) } else { diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 89a67fc0d8988..95a8e7625ff48 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -493,7 +493,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { // // This means that this only errors if we're truly lowering the lint // level from forbid. - if self.lint_added_lints && level != Level::Forbid && old_level == Level::Forbid { + if self.lint_added_lints && level == Level::Deny && old_level == Level::Forbid { + // Having a deny inside a forbid is fine and is ignored, so we skip this check. + return; + } else if self.lint_added_lints && level != Level::Forbid && old_level == Level::Forbid { // Backwards compatibility check: // // We used to not consider `forbid(lint_group)` diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 81352af3d48fa..a7faab0868d82 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -598,6 +598,7 @@ fn register_builtins(store: &mut LintStore) { "converted into hard error, see PR #125380 \ for more information", ); + store.register_removed("unsupported_calling_conventions", "converted into hard error"); } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index db4413149a49f..60ff925b40e0d 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -18,6 +18,8 @@ use rustc_target::spec::abi::Abi as SpecAbi; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; +mod improper_ctypes; + use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, @@ -728,7 +730,6 @@ fn is_niche_optimization_candidate<'tcx>( /// can, return the type that `ty` can be safely converted to, otherwise return `None`. /// Currently restricted to function pointers, boxes, references, `core::num::NonZero`, /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes. -/// FIXME: This duplicates code in codegen. pub(crate) fn repr_nullable_ptr<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -741,10 +742,6 @@ pub(crate) fn repr_nullable_ptr<'tcx>( [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) { ([], [field]) | ([field], []) => field.ty(tcx, args), ([field1], [field2]) => { - if !tcx.features().result_ffi_guarantees { - return None; - } - let ty1 = field1.ty(tcx, args); let ty2 = field2.ty(tcx, args); @@ -983,15 +980,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Empty enums are okay... although sort of useless. return FfiSafe; } - - if def.is_variant_list_non_exhaustive() && !def.did().is_local() { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_non_exhaustive, - help: None, - }; - } - // Check for a repr() attribute to specify the size of the // discriminant. if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() @@ -1010,21 +998,23 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }; } + use improper_ctypes::{ + check_non_exhaustive_variant, non_local_and_non_exhaustive, + }; + + let non_local_def = non_local_and_non_exhaustive(def); // Check the contained variants. - for variant in def.variants() { - let is_non_exhaustive = variant.is_field_list_non_exhaustive(); - if is_non_exhaustive && !variant.def_id.is_local() { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_non_exhaustive_variant, - help: None, - }; - } + let ret = def.variants().iter().try_for_each(|variant| { + check_non_exhaustive_variant(non_local_def, variant) + .map_break(|reason| FfiUnsafe { ty, reason, help: None })?; match self.check_variant_for_ffi(acc, ty, def, variant, args) { - FfiSafe => (), - r => return r, + FfiSafe => ControlFlow::Continue(()), + r => ControlFlow::Break(r), } + }); + if let ControlFlow::Break(result) = ret { + return result; } FfiSafe diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs new file mode 100644 index 0000000000000..1030101c545da --- /dev/null +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -0,0 +1,51 @@ +use std::ops::ControlFlow; + +use rustc_errors::DiagMessage; +use rustc_hir::def::CtorKind; +use rustc_middle::ty; + +use crate::fluent_generated as fluent; + +/// Check a variant of a non-exhaustive enum for improper ctypes +/// +/// We treat `#[non_exhaustive] enum` as "ensure that code will compile if new variants are added". +/// This includes linting, on a best-effort basis. There are valid additions that are unlikely. +/// +/// Adding a data-carrying variant to an existing C-like enum that is passed to C is "unlikely", +/// so we don't need the lint to account for it. +/// e.g. going from enum Foo { A, B, C } to enum Foo { A, B, C, D(u32) }. +pub(crate) fn check_non_exhaustive_variant( + non_local_def: bool, + variant: &ty::VariantDef, +) -> ControlFlow { + // non_exhaustive suggests it is possible that someone might break ABI + // see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344 + // so warn on complex enums being used outside their crate + if non_local_def { + // which is why we only warn about really_tagged_union reprs from https://rust.tf/rfc2195 + // with an enum like `#[repr(u8)] enum Enum { A(DataA), B(DataB), }` + // but exempt enums with unit ctors like C's (e.g. from rust-bindgen) + if variant_has_complex_ctor(variant) { + return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive); + } + } + + let non_exhaustive_variant_fields = variant.is_field_list_non_exhaustive(); + if non_exhaustive_variant_fields && !variant.def_id.is_local() { + return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive_variant); + } + + ControlFlow::Continue(()) +} + +fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool { + // CtorKind::Const means a "unit" ctor + !matches!(variant.ctor_kind(), Some(CtorKind::Const)) +} + +// non_exhaustive suggests it is possible that someone might break ABI +// see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344 +// so warn on complex enums being used outside their crate +pub(crate) fn non_local_and_non_exhaustive(def: ty::AdtDef<'_>) -> bool { + def.is_variant_list_non_exhaustive() && !def.did().is_local() +} diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 1a00725096148..ddc18c755a89b 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -346,7 +346,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { None } } - ty::Array(ty, len) => match len.try_eval_target_usize(cx.tcx, cx.param_env) { + ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) { // If the array is empty we don't lint, to avoid false positives Some(0) | None => None, // If the array is definitely non-empty, we can do `#[must_use]` checking. diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 45a5ce0ca20e9..a4c49a1590535 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -123,7 +123,6 @@ declare_lint_pass! { UNSAFE_OP_IN_UNSAFE_FN, UNSTABLE_NAME_COLLISIONS, UNSTABLE_SYNTAX_PRE_EXPANSION, - UNSUPPORTED_CALLING_CONVENTIONS, UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS, UNUSED_ASSIGNMENTS, UNUSED_ASSOCIATED_TYPE_BOUNDS, @@ -156,7 +155,7 @@ declare_lint! { /// /// ```rust /// #![forbid(warnings)] - /// #![deny(bad_style)] + /// #![warn(bad_style)] /// /// fn main() {} /// ``` @@ -2667,7 +2666,6 @@ declare_lint! { /// ### Example /// /// ```rust - /// #![feature(strict_provenance)] /// #![warn(fuzzy_provenance_casts)] /// /// fn main() { @@ -2701,7 +2699,7 @@ declare_lint! { pub FUZZY_PROVENANCE_CASTS, Allow, "a fuzzy integer to pointer cast is used", - @feature_gate = strict_provenance; + @feature_gate = strict_provenance_lints; } declare_lint! { @@ -2711,7 +2709,6 @@ declare_lint! { /// ### Example /// /// ```rust - /// #![feature(strict_provenance)] /// #![warn(lossy_provenance_casts)] /// /// fn main() { @@ -2747,7 +2744,7 @@ declare_lint! { pub LOSSY_PROVENANCE_CASTS, Allow, "a lossy pointer to integer cast is used", - @feature_gate = strict_provenance; + @feature_gate = strict_provenance_lints; } declare_lint! { @@ -3789,53 +3786,6 @@ declare_lint! { crate_level_only } -declare_lint! { - /// The `unsupported_calling_conventions` lint is output whenever there is a use of the - /// `stdcall`, `fastcall`, `thiscall`, `vectorcall` calling conventions (or their unwind - /// variants) on targets that cannot meaningfully be supported for the requested target. - /// - /// For example `stdcall` does not make much sense for a x86_64 or, more apparently, powerpc - /// code, because this calling convention was never specified for those targets. - /// - /// Historically MSVC toolchains have fallen back to the regular C calling convention for - /// targets other than x86, but Rust doesn't really see a similar need to introduce a similar - /// hack across many more targets. - /// - /// ### Example - /// - /// ```rust,ignore (needs specific targets) - /// extern "stdcall" fn stdcall() {} - /// ``` - /// - /// This will produce: - /// - /// ```text - /// warning: use of calling convention not supported on this target - /// --> $DIR/unsupported.rs:39:1 - /// | - /// LL | extern "stdcall" fn stdcall() {} - /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - /// | - /// = note: `#[warn(unsupported_calling_conventions)]` on by default - /// = warning: this was previously accepted by the compiler but is being phased out; - /// it will become a hard error in a future release! - /// = note: for more information, see issue ... - /// ``` - /// - /// ### Explanation - /// - /// On most of the targets the behaviour of `stdcall` and similar calling conventions is not - /// defined at all, but was previously accepted due to a bug in the implementation of the - /// compiler. - pub UNSUPPORTED_CALLING_CONVENTIONS, - Warn, - "use of unsupported calling convention", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, - reference: "issue #87678 ", - }; -} - declare_lint! { /// The `unsupported_fn_ptr_calling_conventions` lint is output whenever there is a use of /// a target dependent calling convention on a target that does not support this calling diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 8f0b1b8127657..4b303511dbc05 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -490,13 +490,13 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0'); auto Arg0 = std::string(ArgsCstrBuff); buffer_offset = Arg0.size() + 1; - auto ArgsCppStr = - std::string(ArgsCstrBuff + buffer_offset, ArgsCstrBuffLen - 1); + auto ArgsCppStr = std::string(ArgsCstrBuff + buffer_offset, + ArgsCstrBuffLen - buffer_offset); auto i = 0; while (i != std::string::npos) { i = ArgsCppStr.find('\0', i + 1); if (i != std::string::npos) - ArgsCppStr.replace(i, i + 1, " "); + ArgsCppStr.replace(i, 1, " "); } Options.MCOptions.Argv0 = Arg0; Options.MCOptions.CommandlineArgs = ArgsCppStr; diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 72b03fa0560e6..910c27da95463 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1658,16 +1658,6 @@ extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B, unwrap(B)->SetInsertPoint(unwrap(BB), Point); } -extern "C" void LLVMRustSetComdat(LLVMModuleRef M, LLVMValueRef V, - const char *Name, size_t NameLen) { - Triple TargetTriple = Triple(unwrap(M)->getTargetTriple()); - GlobalObject *GV = unwrap(V); - if (TargetTriple.supportsCOMDAT()) { - StringRef NameRef(Name, NameLen); - GV->setComdat(unwrap(M)->getOrInsertComdat(NameRef)); - } -} - enum class LLVMRustLinkage { ExternalLinkage = 0, AvailableExternallyLinkage = 1, diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 99c673b021a90..a4a69ae95144e 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -499,8 +499,11 @@ impl<'a> CrateLocator<'a> { dylibs: FxIndexMap, ) -> Result, CrateError> { let mut slot = None; - // Order here matters, rmeta should come first. See comment in - // `extract_one` below. + // Order here matters, rmeta should come first. + // + // Make sure there's at most one rlib and at most one dylib. + // + // See comment in `extract_one` below. let source = CrateSource { rmeta: self.extract_one(rmetas, CrateFlavor::Rmeta, &mut slot)?, rlib: self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot)?, @@ -706,54 +709,58 @@ impl<'a> CrateLocator<'a> { let mut rmetas = FxIndexMap::default(); let mut dylibs = FxIndexMap::default(); for loc in &self.exact_paths { - if !loc.canonicalized().exists() { - return Err(CrateError::ExternLocationNotExist( - self.crate_name, - loc.original().clone(), - )); + let loc_canon = loc.canonicalized(); + let loc_orig = loc.original(); + if !loc_canon.exists() { + return Err(CrateError::ExternLocationNotExist(self.crate_name, loc_orig.clone())); } - if !loc.original().is_file() { - return Err(CrateError::ExternLocationNotFile( - self.crate_name, - loc.original().clone(), - )); + if !loc_orig.is_file() { + return Err(CrateError::ExternLocationNotFile(self.crate_name, loc_orig.clone())); } - let Some(file) = loc.original().file_name().and_then(|s| s.to_str()) else { - return Err(CrateError::ExternLocationNotFile( - self.crate_name, - loc.original().clone(), - )); + // Note to take care and match against the non-canonicalized name: + // some systems save build artifacts into content-addressed stores + // that do not preserve extensions, and then link to them using + // e.g. symbolic links. If we canonicalize too early, we resolve + // the symlink, the file type is lost and we might treat rlibs and + // rmetas as dylibs. + let Some(file) = loc_orig.file_name().and_then(|s| s.to_str()) else { + return Err(CrateError::ExternLocationNotFile(self.crate_name, loc_orig.clone())); }; - - if file.starts_with("lib") && (file.ends_with(".rlib") || file.ends_with(".rmeta")) - || file.starts_with(self.target.dll_prefix.as_ref()) - && file.ends_with(self.target.dll_suffix.as_ref()) - { - // Make sure there's at most one rlib and at most one dylib. - // Note to take care and match against the non-canonicalized name: - // some systems save build artifacts into content-addressed stores - // that do not preserve extensions, and then link to them using - // e.g. symbolic links. If we canonicalize too early, we resolve - // the symlink, the file type is lost and we might treat rlibs and - // rmetas as dylibs. - let loc_canon = loc.canonicalized().clone(); - let loc = loc.original(); - if loc.file_name().unwrap().to_str().unwrap().ends_with(".rlib") { - rlibs.insert(loc_canon, PathKind::ExternFlag); - } else if loc.file_name().unwrap().to_str().unwrap().ends_with(".rmeta") { - rmetas.insert(loc_canon, PathKind::ExternFlag); - } else { - dylibs.insert(loc_canon, PathKind::ExternFlag); + // FnMut cannot return reference to captured value, so references + // must be taken outside the closure. + let rlibs = &mut rlibs; + let rmetas = &mut rmetas; + let dylibs = &mut dylibs; + let type_via_filename = (|| { + if file.starts_with("lib") { + if file.ends_with(".rlib") { + return Some(rlibs); + } + if file.ends_with(".rmeta") { + return Some(rmetas); + } + } + let dll_prefix = self.target.dll_prefix.as_ref(); + let dll_suffix = self.target.dll_suffix.as_ref(); + if file.starts_with(dll_prefix) && file.ends_with(dll_suffix) { + return Some(dylibs); + } + None + })(); + match type_via_filename { + Some(type_via_filename) => { + type_via_filename.insert(loc_canon.clone(), PathKind::ExternFlag); + } + None => { + self.crate_rejections + .via_filename + .push(CrateMismatch { path: loc_orig.clone(), got: String::new() }); } - } else { - self.crate_rejections - .via_filename - .push(CrateMismatch { path: loc.original().clone(), got: String::new() }); } } // Extract the dylib/rlib/rmeta triple. - Ok(self.extract_lib(rlibs, rmetas, dylibs)?.map(|(_, lib)| lib)) + self.extract_lib(rlibs, rmetas, dylibs).map(|opt| opt.map(|(_, lib)| lib)) } pub(crate) fn into_error(self, root: Option) -> CrateError { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 69707fdbe8fa1..7bb40996d58f2 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -437,9 +437,6 @@ provide! { tcx, def_id, other, cdata, pub(in crate::rmeta) fn provide(providers: &mut Providers) { provide_cstore_hooks(providers); - // FIXME(#44234) - almost all of these queries have no sub-queries and - // therefore no actual inputs, they're just reading tables calculated in - // resolve! Does this work? Unsure! That's what the issue is about providers.queries = rustc_middle::query::Providers { allocator_kind: |tcx, ()| CStore::from_tcx(tcx).allocator_kind(), alloc_error_handler_kind: |tcx, ()| CStore::from_tcx(tcx).alloc_error_handler_kind(), diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index d431497881919..ac55497f8b3d4 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -34,6 +34,7 @@ use crate::infer::MemberConstraint; use crate::mir::ConstraintCategory; use crate::ty::{self, GenericArg, List, Ty, TyCtxt, TypeFlags, TypeVisitableExt}; +pub type CanonicalQueryInput<'tcx, V> = ir::CanonicalQueryInput, V>; pub type Canonical<'tcx, V> = ir::Canonical, V>; pub type CanonicalVarInfo<'tcx> = ir::CanonicalVarInfo>; pub type CanonicalVarValues<'tcx> = ir::CanonicalVarValues>; @@ -182,7 +183,6 @@ impl<'tcx> CanonicalParamEnvCache<'tcx> { max_universe: ty::UniverseIndex::ROOT, variables: List::empty(), value: key, - defining_opaque_types: ty::List::empty(), }; } diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index e9b73d25ba208..04a06ba7464cb 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -56,7 +56,6 @@ #![feature(ptr_alignment_type)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] -#![feature(strict_provenance)] #![feature(trait_upcasting)] #![feature(trusted_len)] #![feature(try_blocks)] diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 4262460d928b6..465aa0eee0344 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -1,5 +1,6 @@ use std::fmt::{self, Debug, Display, Formatter}; +use either::Either; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_session::RemapFileNameExt; @@ -320,8 +321,14 @@ impl<'tcx> Const<'tcx> { Const::Ty(_, c) => { // We want to consistently have a "clean" value for type system constants (i.e., no // data hidden in the padding), so we always go through a valtree here. - let (ty, val) = c.eval(tcx, param_env, span)?; - Ok(tcx.valtree_to_const_val((ty, val))) + match c.eval_valtree(tcx, param_env, span) { + Ok((ty, val)) => Ok(tcx.valtree_to_const_val((ty, val))), + Err(Either::Left(_bad_ty)) => Err(tcx + .dcx() + .delayed_bug("`mir::Const::eval` called on a non-valtree-compatible type") + .into()), + Err(Either::Right(e)) => Err(e), + } } Const::Unevaluated(uneval, _) => { // FIXME: We might want to have a `try_eval`-like function on `Unevaluated` diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 04d035e27ba3e..ac3baf74ca7c4 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -18,9 +18,9 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_target::abi::{Align, HasDataLayout, Size}; use super::{ - AllocId, BadBytesAccess, CtfeProvenance, InterpError, InterpResult, Pointer, PointerArithmetic, - Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, UndefinedBehaviorInfo, - UnsupportedOpInfo, interp_ok, read_target_uint, write_target_uint, + AllocId, BadBytesAccess, CtfeProvenance, InterpErrorKind, InterpResult, Pointer, + PointerArithmetic, Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, + UndefinedBehaviorInfo, UnsupportedOpInfo, interp_ok, read_target_uint, write_target_uint, }; use crate::ty; @@ -199,22 +199,22 @@ impl From for AllocError { } impl AllocError { - pub fn to_interp_error<'tcx>(self, alloc_id: AllocId) -> InterpError<'tcx> { + pub fn to_interp_error<'tcx>(self, alloc_id: AllocId) -> InterpErrorKind<'tcx> { use AllocError::*; match self { ScalarSizeMismatch(s) => { - InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ScalarSizeMismatch(s)) + InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ScalarSizeMismatch(s)) } - ReadPointerAsInt(info) => InterpError::Unsupported( + ReadPointerAsInt(info) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPointerAsInt(info.map(|b| (alloc_id, b))), ), - OverwritePartialPointer(offset) => InterpError::Unsupported( + OverwritePartialPointer(offset) => InterpErrorKind::Unsupported( UnsupportedOpInfo::OverwritePartialPointer(Pointer::new(alloc_id, offset)), ), - ReadPartialPointer(offset) => InterpError::Unsupported( + ReadPartialPointer(offset) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPartialPointer(Pointer::new(alloc_id, offset)), ), - InvalidUninitBytes(info) => InterpError::UndefinedBehavior( + InvalidUninitBytes(info) => InterpErrorKind::UndefinedBehavior( UndefinedBehaviorInfo::InvalidUninitBytes(info.map(|b| (alloc_id, b))), ), } @@ -318,7 +318,7 @@ impl Allocation { pub fn try_uninit<'tcx>(size: Size, align: Align) -> InterpResult<'tcx, Self> { Self::uninit_inner(size, align, || { ty::tls::with(|tcx| tcx.dcx().delayed_bug("exhausted memory during interpretation")); - InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) + InterpErrorKind::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) }) .into() } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index fcb87e1943505..b520f21ce20c9 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -113,7 +113,7 @@ pub struct InterpErrorInfo<'tcx>(Box>); #[derive(Debug)] struct InterpErrorInfoInner<'tcx> { - kind: InterpError<'tcx>, + kind: InterpErrorKind<'tcx>, backtrace: InterpErrorBacktrace, } @@ -154,21 +154,21 @@ impl InterpErrorBacktrace { } impl<'tcx> InterpErrorInfo<'tcx> { - pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) { + pub fn into_parts(self) -> (InterpErrorKind<'tcx>, InterpErrorBacktrace) { let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self; (kind, backtrace) } - pub fn into_kind(self) -> InterpError<'tcx> { + pub fn into_kind(self) -> InterpErrorKind<'tcx> { self.0.kind } - pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self { + pub fn from_parts(kind: InterpErrorKind<'tcx>, backtrace: InterpErrorBacktrace) -> Self { Self(Box::new(InterpErrorInfoInner { kind, backtrace })) } #[inline] - pub fn kind(&self) -> &InterpError<'tcx> { + pub fn kind(&self) -> &InterpErrorKind<'tcx> { &self.0.kind } } @@ -179,13 +179,13 @@ fn print_backtrace(backtrace: &Backtrace) { impl From for InterpErrorInfo<'_> { fn from(err: ErrorGuaranteed) -> Self { - InterpError::InvalidProgram(InvalidProgramInfo::AlreadyReported(err.into())).into() + InterpErrorKind::InvalidProgram(InvalidProgramInfo::AlreadyReported(err.into())).into() } } impl From for InterpErrorInfo<'_> { fn from(err: ErrorHandled) -> Self { - InterpError::InvalidProgram(match err { + InterpErrorKind::InvalidProgram(match err { ErrorHandled::Reported(r, _span) => InvalidProgramInfo::AlreadyReported(r), ErrorHandled::TooGeneric(_span) => InvalidProgramInfo::TooGeneric, }) @@ -193,8 +193,8 @@ impl From for InterpErrorInfo<'_> { } } -impl<'tcx> From> for InterpErrorInfo<'tcx> { - fn from(kind: InterpError<'tcx>) -> Self { +impl<'tcx> From> for InterpErrorInfo<'tcx> { + fn from(kind: InterpErrorKind<'tcx>) -> Self { InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace: InterpErrorBacktrace::new(), @@ -590,7 +590,7 @@ impl dyn MachineStopType { } #[derive(Debug)] -pub enum InterpError<'tcx> { +pub enum InterpErrorKind<'tcx> { /// The program caused undefined behavior. UndefinedBehavior(UndefinedBehaviorInfo<'tcx>), /// The program did something the interpreter does not support (some of these *might* be UB @@ -606,25 +606,25 @@ pub enum InterpError<'tcx> { MachineStop(Box), } -impl InterpError<'_> { +impl InterpErrorKind<'_> { /// Some errors do string formatting even if the error is never printed. /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors, /// so this method lets us detect them and `bug!` on unexpected errors. pub fn formatted_string(&self) -> bool { matches!( self, - InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { .. }) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) + InterpErrorKind::Unsupported(UnsupportedOpInfo::Unsupported(_)) + | InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { .. }) + | InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) ) } } -// Macros for constructing / throwing `InterpError` +// Macros for constructing / throwing `InterpErrorKind` #[macro_export] macro_rules! err_unsup { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::Unsupported( + $crate::mir::interpret::InterpErrorKind::Unsupported( $crate::mir::interpret::UnsupportedOpInfo::$($tt)* ) }; @@ -638,7 +638,7 @@ macro_rules! err_unsup_format { #[macro_export] macro_rules! err_inval { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::InvalidProgram( + $crate::mir::interpret::InterpErrorKind::InvalidProgram( $crate::mir::interpret::InvalidProgramInfo::$($tt)* ) }; @@ -647,7 +647,7 @@ macro_rules! err_inval { #[macro_export] macro_rules! err_ub { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::UndefinedBehavior( + $crate::mir::interpret::InterpErrorKind::UndefinedBehavior( $crate::mir::interpret::UndefinedBehaviorInfo::$($tt)* ) }; @@ -680,7 +680,7 @@ macro_rules! err_ub_custom { #[macro_export] macro_rules! err_exhaust { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::ResourceExhaustion( + $crate::mir::interpret::InterpErrorKind::ResourceExhaustion( $crate::mir::interpret::ResourceExhaustionInfo::$($tt)* ) }; @@ -689,7 +689,7 @@ macro_rules! err_exhaust { #[macro_export] macro_rules! err_machine_stop { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::MachineStop(Box::new($($tt)*)) + $crate::mir::interpret::InterpErrorKind::MachineStop(Box::new($($tt)*)) }; } @@ -792,9 +792,9 @@ impl<'tcx, T> ops::FromResidual for InterpResult_<'tcx, T> { } // Allow `yeet`ing `InterpError` in functions returning `InterpResult_`. -impl<'tcx, T> ops::FromResidual>> for InterpResult_<'tcx, T> { +impl<'tcx, T> ops::FromResidual>> for InterpResult_<'tcx, T> { #[inline] - fn from_residual(ops::Yeet(e): ops::Yeet>) -> Self { + fn from_residual(ops::Yeet(e): ops::Yeet>) -> Self { Self::new(Err(e.into())) } } @@ -856,7 +856,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> { } #[inline] - pub fn map_err( + pub fn map_err_info( self, f: impl FnOnce(InterpErrorInfo<'tcx>) -> InterpErrorInfo<'tcx>, ) -> InterpResult<'tcx, T> { @@ -864,8 +864,19 @@ impl<'tcx, T> InterpResult_<'tcx, T> { } #[inline] - pub fn inspect_err(self, f: impl FnOnce(&InterpErrorInfo<'tcx>)) -> InterpResult<'tcx, T> { - InterpResult_::new(self.disarm().inspect_err(f)) + pub fn map_err_kind( + self, + f: impl FnOnce(InterpErrorKind<'tcx>) -> InterpErrorKind<'tcx>, + ) -> InterpResult<'tcx, T> { + InterpResult_::new(self.disarm().map_err(|mut e| { + e.0.kind = f(e.0.kind); + e + })) + } + + #[inline] + pub fn inspect_err_kind(self, f: impl FnOnce(&InterpErrorKind<'tcx>)) -> InterpResult<'tcx, T> { + InterpResult_::new(self.disarm().inspect_err(|e| f(&e.0.kind))) } #[inline] diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 115bcdbc58987..790ff3e2fe030 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -36,7 +36,7 @@ pub use self::allocation::{ pub use self::error::{ BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, - InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, InvalidProgramInfo, + InterpErrorInfo, InterpErrorKind, InterpResult, InvalidMetaKind, InvalidProgramInfo, MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind, interp_ok, diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 8d57d0d865469..476e352ed9227 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -55,7 +55,7 @@ impl<'tcx> PlaceTy<'tcx> { /// `PlaceElem`, where we can just use the `Ty` that is already /// stored inline on field projection elems. pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> { - self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty, |_, ty| ty) + self.projection_ty_core(tcx, &elem, |_, _, ty| ty, |_, ty| ty) } /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })` @@ -66,7 +66,6 @@ impl<'tcx> PlaceTy<'tcx> { pub fn projection_ty_core( self, tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, elem: &ProjectionElem, mut handle_field: impl FnMut(&Self, FieldIdx, T) -> Ty<'tcx>, mut handle_opaque_cast_and_subtype: impl FnMut(&Self, T) -> Ty<'tcx>, @@ -93,7 +92,9 @@ impl<'tcx> PlaceTy<'tcx> { ty::Slice(..) => self.ty, ty::Array(inner, _) if !from_end => Ty::new_array(tcx, *inner, to - from), ty::Array(inner, size) if from_end => { - let size = size.eval_target_usize(tcx, param_env); + let size = size + .try_to_target_usize(tcx) + .expect("expected subslice projection on fixed-size array"); let len = size - from - to; Ty::new_array(tcx, *inner, len) } diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 80adbe74fe702..ba7b57c891c52 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -7,7 +7,7 @@ use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{DUMMY_SP, Span}; use rustc_target::abi; -use crate::infer::canonical::Canonical; +use crate::infer::canonical::CanonicalQueryInput; use crate::ty::fast_reject::SimplifiedType; use crate::ty::layout::{TyAndLayout, ValidityRequirement}; use crate::ty::{self, GenericArg, GenericArgsRef, Ty, TyCtxt}; @@ -485,7 +485,7 @@ impl Key for Option { /// Canonical query goals correspond to abstract trait operations that /// are not tied to any crate in particular. -impl<'tcx, T: Clone> Key for Canonical<'tcx, T> { +impl<'tcx, T: Clone> Key for CanonicalQueryInput<'tcx, T> { type Cache = DefaultCache; fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index f0be70e00dfca..f8ba606e087c8 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -65,10 +65,11 @@ use crate::query::plumbing::{ CyclePlaceholder, DynamicQuery, query_ensure, query_ensure_error_guaranteed, query_get_at, }; use crate::traits::query::{ - CanonicalAliasGoal, CanonicalPredicateGoal, CanonicalTyGoal, - CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpNormalizeGoal, - CanonicalTypeOpProvePredicateGoal, DropckConstraint, DropckOutlivesResult, - MethodAutoderefStepsResult, NoSolution, NormalizationResult, OutlivesBound, + CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, + CanonicalPredicateGoal, CanonicalTyGoal, CanonicalTypeOpAscribeUserTypeGoal, + CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, DropckConstraint, + DropckOutlivesResult, MethodAutoderefStepsResult, NoSolution, NormalizationResult, + OutlivesBound, }; use crate::traits::{ CodegenObligationError, DynCompatibilityViolation, EvaluationResult, ImplSource, @@ -569,6 +570,7 @@ rustc_queries! { /// either `#[coverage(on)]` or no coverage attribute was found. query coverage_attr_on(key: LocalDefId) -> bool { desc { |tcx| "checking for `#[coverage(..)]` on `{}`", tcx.def_path_str(key) } + feedable } /// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass @@ -2010,7 +2012,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution, > { - desc { "normalizing `{}`", goal.value.value } + desc { "normalizing `{}`", goal.canonical.value.value } } ///
@@ -2024,7 +2026,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution, > { - desc { "normalizing `{}`", goal.value.value } + desc { "normalizing `{}`", goal.canonical.value.value } } ///
@@ -2038,7 +2040,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution, > { - desc { "normalizing `{}`", goal.value.value } + desc { "normalizing `{}`", goal.canonical.value.value } } /// Do not call this query directly: invoke `try_normalize_erasing_regions` instead. @@ -2049,32 +2051,32 @@ rustc_queries! { } query implied_outlives_bounds_compat( - goal: CanonicalTyGoal<'tcx> + goal: CanonicalImpliedOutlivesBoundsGoal<'tcx> ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, NoSolution, > { - desc { "computing implied outlives bounds for `{}`", goal.value.value } + desc { "computing implied outlives bounds for `{}`", goal.canonical.value.value.ty } } query implied_outlives_bounds( - goal: CanonicalTyGoal<'tcx> + goal: CanonicalImpliedOutlivesBoundsGoal<'tcx> ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, NoSolution, > { - desc { "computing implied outlives bounds v2 for `{}`", goal.value.value } + desc { "computing implied outlives bounds v2 for `{}`", goal.canonical.value.value.ty } } /// Do not call this query directly: /// invoke `DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx)` instead. query dropck_outlives( - goal: CanonicalTyGoal<'tcx> + goal: CanonicalDropckOutlivesGoal<'tcx> ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, NoSolution, > { - desc { "computing dropck types for `{}`", goal.value.value } + desc { "computing dropck types for `{}`", goal.canonical.value.value.dropped_ty } } /// Do not call this query directly: invoke `infcx.predicate_may_hold()` or @@ -2082,7 +2084,7 @@ rustc_queries! { query evaluate_obligation( goal: CanonicalPredicateGoal<'tcx> ) -> Result { - desc { "evaluating trait selection obligation `{}`", goal.value.value } + desc { "evaluating trait selection obligation `{}`", goal.canonical.value.value } } /// Do not call this query directly: part of the `Eq` type-op @@ -2092,7 +2094,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, NoSolution, > { - desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal.value.value } + desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal.canonical.value.value } } /// Do not call this query directly: part of the `ProvePredicate` type-op @@ -2102,7 +2104,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, NoSolution, > { - desc { "evaluating `type_op_prove_predicate` `{:?}`", goal.value.value } + desc { "evaluating `type_op_prove_predicate` `{:?}`", goal.canonical.value.value } } /// Do not call this query directly: part of the `Normalize` type-op @@ -2112,7 +2114,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Ty<'tcx>>>, NoSolution, > { - desc { "normalizing `{}`", goal.value.value.value } + desc { "normalizing `{}`", goal.canonical.value.value.value } } /// Do not call this query directly: part of the `Normalize` type-op @@ -2122,7 +2124,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::Clause<'tcx>>>, NoSolution, > { - desc { "normalizing `{:?}`", goal.value.value.value } + desc { "normalizing `{:?}`", goal.canonical.value.value.value } } /// Do not call this query directly: part of the `Normalize` type-op @@ -2132,7 +2134,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::PolyFnSig<'tcx>>>, NoSolution, > { - desc { "normalizing `{:?}`", goal.value.value.value } + desc { "normalizing `{:?}`", goal.canonical.value.value.value } } /// Do not call this query directly: part of the `Normalize` type-op @@ -2142,7 +2144,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::FnSig<'tcx>>>, NoSolution, > { - desc { "normalizing `{:?}`", goal.value.value.value } + desc { "normalizing `{:?}`", goal.canonical.value.value.value } } query instantiate_and_check_impossible_predicates(key: (DefId, GenericArgsRef<'tcx>)) -> bool { @@ -2163,7 +2165,7 @@ rustc_queries! { query method_autoderef_steps( goal: CanonicalTyGoal<'tcx> ) -> MethodAutoderefStepsResult<'tcx> { - desc { "computing autoderef types for `{}`", goal.value.value } + desc { "computing autoderef types for `{}`", goal.canonical.value.value } } query supported_target_features(_: CrateNum) -> &'tcx UnordMap> { diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 81a543e647a7e..eeed5118592bc 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -11,16 +11,13 @@ use rustc_span::Span; pub use rustc_type_ir::solve::NoSolution; use crate::error::DropCheckOverflow; -use crate::infer::canonical::{Canonical, QueryResponse}; +use crate::infer::canonical::{Canonical, CanonicalQueryInput, QueryResponse}; use crate::ty::{self, GenericArg, Ty, TyCtxt}; pub mod type_op { - use std::fmt; - use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; - use crate::ty::fold::TypeFoldable; - use crate::ty::{Predicate, Ty, TyCtxt, UserType}; + use crate::ty::{Predicate, Ty, UserType}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] pub struct AscribeUserType<'tcx> { @@ -28,12 +25,6 @@ pub mod type_op { pub user_ty: UserType<'tcx>, } - impl<'tcx> AscribeUserType<'tcx> { - pub fn new(mir_ty: Ty<'tcx>, user_ty: UserType<'tcx>) -> Self { - Self { mir_ty, user_ty } - } - } - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] pub struct Eq<'tcx> { pub a: Ty<'tcx>, @@ -51,46 +42,50 @@ pub mod type_op { pub predicate: Predicate<'tcx>, } - impl<'tcx> ProvePredicate<'tcx> { - pub fn new(predicate: Predicate<'tcx>) -> Self { - ProvePredicate { predicate } - } - } - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] pub struct Normalize { pub value: T, } - impl<'tcx, T> Normalize - where - T: fmt::Debug + TypeFoldable>, - { - pub fn new(value: T) -> Self { - Self { value } - } + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] + pub struct ImpliedOutlivesBounds<'tcx> { + pub ty: Ty<'tcx>, + } + + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] + pub struct DropckOutlives<'tcx> { + pub dropped_ty: Ty<'tcx>, } } -pub type CanonicalAliasGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>; +pub type CanonicalAliasGoal<'tcx> = + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>; -pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>; +pub type CanonicalTyGoal<'tcx> = CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>; -pub type CanonicalPredicateGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>; +pub type CanonicalPredicateGoal<'tcx> = + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>; pub type CanonicalTypeOpAscribeUserTypeGoal<'tcx> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>>; + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>>; -pub type CanonicalTypeOpEqGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Eq<'tcx>>>; +pub type CanonicalTypeOpEqGoal<'tcx> = + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::Eq<'tcx>>>; pub type CanonicalTypeOpSubtypeGoal<'tcx> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Subtype<'tcx>>>; + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::Subtype<'tcx>>>; pub type CanonicalTypeOpProvePredicateGoal<'tcx> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::ProvePredicate<'tcx>>>; + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::ProvePredicate<'tcx>>>; pub type CanonicalTypeOpNormalizeGoal<'tcx, T> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize>>; + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize>>; + +pub type CanonicalImpliedOutlivesBoundsGoal<'tcx> = + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::ImpliedOutlivesBounds<'tcx>>>; + +pub type CanonicalDropckOutlivesGoal<'tcx> = + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::DropckOutlives<'tcx>>>; #[derive(Clone, Debug, Default, HashStable, TypeFoldable, TypeVisitable)] pub struct DropckOutlivesResult<'tcx> { diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 66035464fa55a..05556ae38a8e2 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -161,9 +161,7 @@ pub enum SelectionCandidate<'tcx> { /// Implementation of a `Fn`-family trait by one of the anonymous /// types generated for a fn pointer type (e.g., `fn(int) -> int`) - FnPointerCandidate { - fn_host_effect: ty::Const<'tcx>, - }, + FnPointerCandidate, TraitAliasCandidate, @@ -180,9 +178,6 @@ pub enum SelectionCandidate<'tcx> { BuiltinObjectCandidate, BuiltinUnsizeCandidate, - - /// Implementation of `const Destruct`, optionally from a custom `impl const Drop`. - ConstDestructCandidate(Option), } /// The result of trait evaluation. The order is important diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 389d20f315ff0..eb715be22b1c8 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -398,133 +398,65 @@ impl<'tcx> Const<'tcx> { } } - /// Returns the evaluated constant - #[inline] - pub fn eval( - self, - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - span: Span, - ) -> Result<(Ty<'tcx>, ValTree<'tcx>), ErrorHandled> { - self.eval_valtree(tcx, param_env, span).map_err(|err| { - match err { - Either::Right(err) => err, - Either::Left(_bad_ty) => { - // This can happen when we run on ill-typed code. - let e = tcx.dcx().span_delayed_bug( - span, - "`ty::Const::eval` called on a non-valtree-compatible type", - ); - e.into() - } - } - }) - } - /// Normalizes the constant to a value or an error if possible. #[inline] - pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { - match self.eval(tcx, param_env, DUMMY_SP) { + pub fn normalize_internal(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { + match self.eval_valtree(tcx, param_env, DUMMY_SP) { Ok((ty, val)) => Self::new_value(tcx, val, ty), - Err(ErrorHandled::Reported(r, _span)) => Self::new_error(tcx, r.into()), - Err(ErrorHandled::TooGeneric(_span)) => self, + Err(Either::Left(_bad_ty)) => { + // This can happen when we run on ill-typed code. + Self::new_error( + tcx, + tcx.dcx() + .delayed_bug("`ty::Const::eval` called on a non-valtree-compatible type"), + ) + } + Err(Either::Right(ErrorHandled::Reported(r, _span))) => Self::new_error(tcx, r.into()), + Err(Either::Right(ErrorHandled::TooGeneric(_span))) => self, } } - #[inline] - pub fn try_eval_scalar( - self, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> Option<(Ty<'tcx>, Scalar)> { - let (ty, val) = self.eval(tcx, param_env, DUMMY_SP).ok()?; - let val = val.try_to_scalar()?; - Some((ty, val)) - } - - #[inline] - /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of - /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it - /// contains const generic parameters or pointers). - pub fn try_eval_scalar_int( - self, - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - ) -> Option<(Ty<'tcx>, ScalarInt)> { - let (ty, scalar) = self.try_eval_scalar(tcx, param_env)?; - let val = scalar.try_to_scalar_int().ok()?; - Some((ty, val)) - } - - #[inline] - /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of - /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it - /// contains const generic parameters or pointers). - pub fn try_eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { - let (ty, scalar) = self.try_eval_scalar_int(tcx, param_env)?; - let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size; - // if `ty` does not depend on generic parameters, use an empty param_env - Some(scalar.to_bits(size)) - } - - #[inline] - /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type. - pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u128 { - self.try_eval_bits(tcx, param_env) - .unwrap_or_else(|| bug!("failed to evalate {:#?} to bits", self)) - } - - #[inline] - pub fn try_eval_target_usize( - self, - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - ) -> Option { - let (_, scalar) = self.try_eval_scalar_int(tcx, param_env)?; - Some(scalar.to_target_usize(tcx)) - } - - #[inline] - pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { - let (_, scalar) = self.try_eval_scalar_int(tcx, param_env)?; - scalar.try_into().ok() - } - - #[inline] - /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`. - pub fn eval_target_usize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u64 { - self.try_eval_target_usize(tcx, param_env) - .unwrap_or_else(|| bug!("expected usize, got {:#?}", self)) - } - /// Panics if self.kind != ty::ConstKind::Value - pub fn to_valtree(self) -> ty::ValTree<'tcx> { + pub fn to_valtree(self) -> (ty::ValTree<'tcx>, Ty<'tcx>) { match self.kind() { - ty::ConstKind::Value(_, valtree) => valtree, + ty::ConstKind::Value(ty, valtree) => (valtree, ty), _ => bug!("expected ConstKind::Value, got {:?}", self.kind()), } } /// Attempts to convert to a `ValTree` - pub fn try_to_valtree(self) -> Option> { + pub fn try_to_valtree(self) -> Option<(ty::ValTree<'tcx>, Ty<'tcx>)> { match self.kind() { - ty::ConstKind::Value(_, valtree) => Some(valtree), + ty::ConstKind::Value(ty, valtree) => Some((valtree, ty)), _ => None, } } #[inline] - pub fn try_to_scalar(self) -> Option { - self.try_to_valtree()?.try_to_scalar() + pub fn try_to_scalar(self) -> Option<(Scalar, Ty<'tcx>)> { + let (valtree, ty) = self.try_to_valtree()?; + Some((valtree.try_to_scalar()?, ty)) } pub fn try_to_bool(self) -> Option { - self.try_to_valtree()?.try_to_scalar_int()?.try_to_bool().ok() + self.try_to_valtree()?.0.try_to_scalar_int()?.try_to_bool().ok() } #[inline] pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option { - self.try_to_valtree()?.try_to_target_usize(tcx) + self.try_to_valtree()?.0.try_to_target_usize(tcx) + } + + #[inline] + /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of + /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it + /// contains const generic parameters or pointers). + pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { + let (scalar, ty) = self.try_to_scalar()?; + let scalar = scalar.try_to_scalar_int().ok()?; + let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size; + // if `ty` does not depend on generic parameters, use an empty param_env + Some(scalar.to_bits(size)) } pub fn is_ct_infer(self) -> bool { diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs index 54b8507babfa9..bf741f63a3d4f 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs @@ -85,7 +85,7 @@ impl<'tcx> InhabitedPredicate<'tcx> { match self { Self::False => Ok(false), Self::True => Ok(true), - Self::ConstIsZero(const_) => match const_.try_eval_target_usize(tcx, param_env) { + Self::ConstIsZero(const_) => match const_.try_to_target_usize(tcx) { None | Some(0) => Ok(true), Some(1..) => Ok(false), }, diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 6c12b691c26c0..3670b6bbc772a 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -397,7 +397,7 @@ impl<'tcx> SizeSkeleton<'tcx> { } } ty::Array(inner, len) if tcx.features().transmute_generic_consts => { - let len_eval = len.try_eval_target_usize(tcx, param_env); + let len_eval = len.try_to_target_usize(tcx); if len_eval == Some(0) { return Ok(SizeSkeleton::Known(Size::from_bytes(0), None)); } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 7ada5fd93ba7d..b5495fa282b65 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1956,7 +1956,6 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { define_scoped_cx!(self); match constness { - ty::BoundConstness::NotConst => {} ty::BoundConstness::Const => { p!("const "); } @@ -2948,7 +2947,10 @@ impl<'tcx> ty::TraitPredicate<'tcx> { } #[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift)] -pub struct TraitPredPrintWithBoundConstness<'tcx>(ty::TraitPredicate<'tcx>, ty::BoundConstness); +pub struct TraitPredPrintWithBoundConstness<'tcx>( + ty::TraitPredicate<'tcx>, + Option, +); impl<'tcx> fmt::Debug for TraitPredPrintWithBoundConstness<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -2966,7 +2968,7 @@ impl<'tcx> ty::PolyTraitPredicate<'tcx> { fn print_with_bound_constness( self, - constness: ty::BoundConstness, + constness: Option, ) -> ty::Binder<'tcx, TraitPredPrintWithBoundConstness<'tcx>> { self.map_bound(|trait_pred| TraitPredPrintWithBoundConstness(trait_pred, constness)) } @@ -3206,7 +3208,9 @@ define_print_and_forward_display! { TraitPredPrintWithBoundConstness<'tcx> { p!(print(self.0.trait_ref.self_ty()), ": "); - p!(pretty_print_bound_constness(self.1)); + if let Some(constness) = self.1 { + p!(pretty_print_bound_constness(constness)); + } if let ty::PredicatePolarity::Negative = self.0.polarity { p!("!"); } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 3f00458d195c4..74de378c4d78e 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1117,7 +1117,12 @@ impl<'tcx> Ty<'tcx> { // The way we evaluate the `N` in `[T; N]` here only works since we use // `simd_size_and_type` post-monomorphization. It will probably start to ICE // if we use it in generic code. See the `simd-array-trait` ui test. - (f0_len.eval_target_usize(tcx, ParamEnv::empty()), *f0_elem_ty) + ( + f0_len + .try_to_target_usize(tcx) + .expect("expected SIMD field to have definite array size"), + *f0_elem_ty, + ) } #[inline] diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 0a917120b3ba5..9a3049f3be6db 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -907,24 +907,6 @@ impl<'tcx> TyCtxt<'tcx> { } } - /// Constructs generic args for an item, optionally appending a const effect param type - pub fn with_opt_host_effect_param( - self, - caller_def_id: LocalDefId, - callee_def_id: DefId, - args: impl IntoIterator>>, - ) -> ty::GenericArgsRef<'tcx> { - let generics = self.generics_of(callee_def_id); - assert_eq!(generics.parent, None); - - let opt_const_param = generics - .host_effect_index - .is_some() - .then(|| ty::GenericArg::from(self.expected_host_effect_param_for_body(caller_def_id))); - - self.mk_args_from_iter(args.into_iter().map(|arg| arg.into()).chain(opt_const_param)) - } - /// Expand any [weak alias types][weak] contained within the given `value`. /// /// This should be used over other normalization routines in situations where diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 020c202f96503..7170aebee7d22 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -454,12 +454,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, Some(source_info.span)); - let method = trait_method( - self.tcx, - eq_def_id, - sym::eq, - self.tcx.with_opt_host_effect_param(self.def_id, eq_def_id, [compare_ty, compare_ty]), - ); + let method = trait_method(self.tcx, eq_def_id, sym::eq, [compare_ty, compare_ty]); let bool_ty = self.tcx.types.bool; let eq_result = self.temp(bool_ty, source_info.span); diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 2c3611afca9a7..0dfa9168f7c80 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -43,7 +43,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } struct ConstToPat<'tcx> { - id: hir::HirId, span: Span, param_env: ty::ParamEnv<'tcx>, @@ -62,7 +61,6 @@ impl<'tcx> ConstToPat<'tcx> { ) -> Self { trace!(?pat_ctxt.typeck_results.hir_owner); ConstToPat { - id, span, infcx, param_env: pat_ctxt.param_env, @@ -149,15 +147,7 @@ impl<'tcx> ConstToPat<'tcx> { tcx, ObligationCause::dummy(), self.param_env, - ty::TraitRef::new_from_args( - tcx, - partial_eq_trait_id, - tcx.with_opt_host_effect_param( - tcx.hir().enclosing_body_owner(self.id), - partial_eq_trait_id, - [ty, ty], - ), - ), + ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]), ); // This *could* accept a type that isn't actually `PartialEq`, because region bounds get diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index a3b117a3f1966..7f2a07e2f5e0f 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -863,7 +863,7 @@ where ty::Adt(def, args) => self.open_drop_for_adt(*def, args), ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind), ty::Array(ety, size) => { - let size = size.try_eval_target_usize(self.tcx(), self.elaborator.param_env()); + let size = size.try_to_target_usize(self.tcx()); self.open_drop_for_array(*ety, size) } ty::Slice(ety) => self.drop_loop_pair(*ety), diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 162245cb95060..fd8e403ebc26e 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -18,18 +18,12 @@ struct MoveDataBuilder<'a, 'tcx, F> { body: &'a Body<'tcx>, loc: Location, tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, data: MoveData<'tcx>, filter: F, } impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { - fn new( - body: &'a Body<'tcx>, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - filter: F, - ) -> Self { + fn new(body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, filter: F) -> Self { let mut move_paths = IndexVec::new(); let mut path_map = IndexVec::new(); let mut init_path_map = IndexVec::new(); @@ -59,7 +53,6 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { body, loc: Location::START, tcx, - param_env, data: MoveData { moves: IndexVec::new(), loc_map: LocationMap::new(body), @@ -308,10 +301,9 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { pub(super) fn gather_moves<'tcx>( body: &Body<'tcx>, tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, filter: impl Fn(Ty<'tcx>) -> bool, ) -> MoveData<'tcx> { - let mut builder = MoveDataBuilder::new(body, tcx, param_env, filter); + let mut builder = MoveDataBuilder::new(body, tcx, filter); builder.gather_args(); @@ -550,7 +542,9 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { }; let base_ty = base_place.ty(self.body, self.tcx).ty; let len: u64 = match base_ty.kind() { - ty::Array(_, size) => size.eval_target_usize(self.tcx, self.param_env), + ty::Array(_, size) => size + .try_to_target_usize(self.tcx) + .expect("expected subslice projection on fixed-size array"), _ => bug!("from_end: false slice pattern of non-array type"), }; for offset in from..to { diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index bc1177976b5de..926bd187431e6 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -4,7 +4,7 @@ use std::ops::{Index, IndexMut}; use rustc_data_structures::fx::FxHashMap; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::*; -use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_span::Span; use smallvec::SmallVec; @@ -352,10 +352,9 @@ impl<'tcx> MoveData<'tcx> { pub fn gather_moves( body: &Body<'tcx>, tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, filter: impl Fn(Ty<'tcx>) -> bool, ) -> MoveData<'tcx> { - builder::gather_moves(body, tcx, param_env, filter) + builder::gather_moves(body, tcx, filter) } /// For the move path `mpi`, returns the root local variable that starts the path. diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 75732b19cd0b2..5727517bd612a 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -40,8 +40,7 @@ pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); } - let param_env = tcx.param_env(def_id); - let move_data = MoveData::gather_moves(body, tcx, param_env, |_| true); + let move_data = MoveData::gather_moves(body, tcx, |_| true); if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() { let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs index d889bc90c9d74..84c8a91b08252 100644 --- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs +++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs @@ -51,11 +51,20 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { // This will filter to functions with `extern "C-unwind"` ABIs, for // example. for block in body.basic_blocks.as_mut() { + let Some(terminator) = &mut block.terminator else { continue }; + let span = terminator.source_info.span; + + // If we see an `UnwindResume` terminator inside a function that cannot unwind, we need + // to replace it with `UnwindTerminate`. + if let TerminatorKind::UnwindResume = &terminator.kind + && !body_can_unwind + { + terminator.kind = TerminatorKind::UnwindTerminate(UnwindTerminateReason::Abi); + } + if block.is_cleanup { continue; } - let Some(terminator) = &block.terminator else { continue }; - let span = terminator.source_info.span; let call_can_unwind = match &terminator.kind { TerminatorKind::Call { func, .. } => { @@ -87,14 +96,18 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { if !call_can_unwind { // If this function call can't unwind, then there's no need for it // to have a landing pad. This means that we can remove any cleanup - // registered for it. + // registered for it (and turn it into `UnwindAction::Unreachable`). let cleanup = block.terminator_mut().unwind_mut().unwrap(); *cleanup = UnwindAction::Unreachable; - } else if !body_can_unwind { + } else if !body_can_unwind + && matches!(terminator.unwind(), Some(UnwindAction::Continue)) + { // Otherwise if this function can unwind, then if the outer function // can also unwind there's nothing to do. If the outer function - // can't unwind, however, we need to change the landing pad for this - // function call to one that aborts. + // can't unwind, however, we need to ensure that any `UnwindAction::Continue` + // is replaced with terminate. For those with `UnwindAction::Cleanup`, + // cleanup will still happen, and terminate will happen afterwards handled by + // the `UnwindResume` -> `UnwindTerminate` terminator replacement. let cleanup = block.terminator_mut().unwind_mut().unwrap(); *cleanup = UnwindAction::Terminate(UnwindTerminateReason::Abi); } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 8f032728f6be0..0a413d68020dc 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1957,7 +1957,8 @@ fn check_must_not_suspend_ty<'tcx>( let descr_pre = &format!("{}array{} of ", data.descr_pre, plural_suffix); check_must_not_suspend_ty(tcx, ty, hir_id, param_env, SuspendCheckData { descr_pre, - plural_len: len.try_eval_target_usize(tcx, param_env).unwrap_or(0) as usize + 1, + // FIXME(must_not_suspend): This is wrong. We should handle printing unevaluated consts. + plural_len: len.try_to_target_usize(tcx).unwrap_or(0) as usize + 1, ..data }) } diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index cc4b7689d407c..2c622b1927e52 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -223,6 +223,7 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( // Inherited from the by-ref coroutine. body_def.codegen_fn_attrs(tcx.codegen_fn_attrs(coroutine_def_id).clone()); + body_def.coverage_attr_on(tcx.coverage_attr_on(coroutine_def_id)); body_def.constness(tcx.constness(coroutine_def_id)); body_def.coroutine_kind(tcx.coroutine_kind(coroutine_def_id)); body_def.def_ident_span(tcx.def_ident_span(coroutine_def_id)); diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index d0f30314e79a9..2e4c503f3ce5b 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -524,6 +524,11 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir // FIXME(#79625): Consider improving MIR to provide the information needed, to avoid going back // to HIR for it. + // HACK: For synthetic MIR bodies (async closures), use the def id of the HIR body. + if tcx.is_synthetic_mir(def_id) { + return extract_hir_info(tcx, tcx.local_parent(def_id)); + } + let hir_node = tcx.hir_node_by_def_id(def_id); let fn_body_id = hir_node.body_id().expect("HIR node is a function with body"); let hir_body = tcx.hir().body(fn_body_id); diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index f4ac4d9fee6fb..30e1ac05e039c 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -58,8 +58,7 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops { let param_env = tcx.param_env_reveal_all_normalized(def_id); // For types that do not need dropping, the behaviour is trivial. So we only need to track // init/uninit for types that do need dropping. - let move_data = - MoveData::gather_moves(body, tcx, param_env, |ty| ty.needs_drop(tcx, param_env)); + let move_data = MoveData::gather_moves(body, tcx, |ty| ty.needs_drop(tcx, param_env)); let elaborate_patch = { let env = MoveDataParamEnv { move_data, param_env }; diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 8f490094d6030..08923748eb275 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -232,7 +232,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { F: FnOnce(&mut Self) -> InterpResult<'tcx, T>, { f(self) - .map_err(|err| { + .map_err_info(|err| { trace!("InterpCx operation failed: {:?}", err); // Some errors shouldn't come up because creating them causes // an allocation, which we should avoid. When that happens, @@ -602,7 +602,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { Len(place) => { let len = if let ty::Array(_, n) = place.ty(self.local_decls(), self.tcx).ty.kind() { - n.try_eval_target_usize(self.tcx, self.param_env)? + n.try_to_target_usize(self.tcx)? } else { match self.get_const(place)? { Value::Immediate(src) => src.len(&self.ecx).discard_err()?, diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index d963ca5c48502..86c4b241a2b4d 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -329,7 +329,7 @@ impl<'tcx> Validator<'_, 'tcx> { // Determine the type of the thing we are indexing. && let ty::Array(_, len) = place_base.ty(self.body, self.tcx).ty.kind() // It's an array; determine its length. - && let Some(len) = len.try_eval_target_usize(self.tcx, self.param_env) + && let Some(len) = len.try_to_target_usize(self.tcx) // If the index is in-bounds, go ahead. && idx < len { @@ -407,7 +407,7 @@ impl<'tcx> Validator<'_, 'tcx> { // mutably without consequences. However, only &mut [] // is allowed right now. if let ty::Array(_, len) = ty.kind() { - match len.try_eval_target_usize(self.tcx, self.param_env) { + match len.try_to_target_usize(self.tcx) { Some(0) => {} _ => return Err(Unpromotable), } diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index e6647edf3f50d..09969a4c7cc7f 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -19,8 +19,7 @@ pub(super) struct RemoveUninitDrops; impl<'tcx> crate::MirPass<'tcx> for RemoveUninitDrops { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let param_env = tcx.param_env(body.source.def_id()); - let move_data = - MoveData::gather_moves(body, tcx, param_env, |ty| ty.needs_drop(tcx, param_env)); + let move_data = MoveData::gather_moves(body, tcx, |ty| ty.needs_drop(tcx, param_env)); let mut maybe_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) .into_engine(tcx, body) diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 0bf9d7b924952..23634d35c07fe 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -83,8 +83,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { let (max_universe, variables) = canonicalizer.finalize(); - let defining_opaque_types = delegate.defining_opaque_types(); - Canonical { defining_opaque_types, max_universe, variables, value } + Canonical { max_universe, variables, value } } fn get_or_insert_bound_var( diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 6a3d58b59066b..4da1e7fa711b5 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -4,9 +4,8 @@ use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::solve::{Certainty, Goal, NoSolution, SolverMode}; use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; -pub trait SolverDelegate: - Deref::Interner>> + Sized -{ +pub trait SolverDelegate: Deref::Infcx> + Sized { + type Infcx: InferCtxtLike::Interner>; type Interner: Interner; fn cx(&self) -> Self::Interner { (**self).cx() @@ -17,7 +16,7 @@ pub trait SolverDelegate: fn build_with_canonical( cx: Self::Interner, solver_mode: SolverMode, - canonical: &ty::Canonical, + canonical: &ty::CanonicalQueryInput, ) -> (Self, V, ty::CanonicalVarValues) where V: TypeFoldable; diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index fdefec33eeb5d..71ce0cce77224 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -16,7 +16,7 @@ use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::{self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner}; -use tracing::{instrument, trace}; +use tracing::{debug, instrument, trace}; use crate::canonicalizer::{CanonicalizeMode, Canonicalizer}; use crate::delegate::SolverDelegate; @@ -60,7 +60,7 @@ where (goal, opaque_types).fold_with(&mut EagerResolver::new(self.delegate)); let mut orig_values = Default::default(); - let canonical_goal = Canonicalizer::canonicalize( + let canonical = Canonicalizer::canonicalize( self.delegate, CanonicalizeMode::Input, &mut orig_values, @@ -71,7 +71,11 @@ where .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), }, ); - (orig_values, canonical_goal) + let query_input = ty::CanonicalQueryInput { + canonical, + defining_opaque_types: self.delegate.defining_opaque_types(), + }; + (orig_values, query_input) } /// To return the constraints of a canonical query to the caller, we canonicalize: @@ -161,12 +165,22 @@ where // HACK: We bail with overflow if the response would have too many non-region // inference variables. This tends to only happen if we encounter a lot of // ambiguous alias types which get replaced with fresh inference variables - // during generalization. This prevents a hang in nalgebra. - let num_non_region_vars = canonical.variables.iter().filter(|c| !c.is_region()).count(); - if num_non_region_vars > self.cx().recursion_limit() { - return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow { - suggest_increasing_limit: true, - })); + // during generalization. This prevents hangs caused by an exponential blowup, + // see tests/ui/traits/next-solver/coherence-alias-hang.rs. + // + // We don't do so for `NormalizesTo` goals as we erased the expected term and + // bailing with overflow here would prevent us from detecting a type-mismatch, + // causing a coherence error in diesel, see #131969. We still bail with verflow + // when later returning from the parent AliasRelate goal. + if !self.is_normalizes_to_goal { + let num_non_region_vars = + canonical.variables.iter().filter(|c| !c.is_region() && c.is_existential()).count(); + if num_non_region_vars > self.cx().recursion_limit() { + debug!(?num_non_region_vars, "too many inference variables -> overflow"); + return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow { + suggest_increasing_limit: true, + })); + } } Ok(canonical) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 0f8b796d602a7..cbefc826fb7d8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -283,11 +283,11 @@ where let mut ecx = EvalCtxt { delegate, - variables: canonical_input.variables, + variables: canonical_input.canonical.variables, var_values, is_normalizes_to_goal: false, predefined_opaques_in_body: input.predefined_opaques_in_body, - max_input_universe: canonical_input.max_universe, + max_input_universe: canonical_input.canonical.max_universe, search_graph, nested_goals: NestedGoals::new(), tainted: Ok(()), diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 8fe39bb4ee163..a475a58e483b0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -295,6 +295,37 @@ where Ok(ty) } } + + /// Normalize a const for when it is structurally matched on, or more likely + /// when it needs `.try_to_*` called on it (e.g. to turn it into a usize). + /// + /// This function is necessary in nearly all cases before matching on a const. + /// Not doing so is likely to be incomplete and therefore unsound during + /// coherence. + #[instrument(level = "trace", skip(self, param_env), ret)] + fn structurally_normalize_const( + &mut self, + param_env: I::ParamEnv, + ct: I::Const, + ) -> Result { + if let ty::ConstKind::Unevaluated(..) = ct.kind() { + let normalized_ct = self.next_const_infer(); + let alias_relate_goal = Goal::new( + self.cx(), + param_env, + ty::PredicateKind::AliasRelate( + ct.into(), + normalized_ct.into(), + ty::AliasRelationDirection::Equate, + ), + ); + self.add_goal(GoalSource::Misc, alias_relate_goal); + self.try_evaluate_added_goals()?; + Ok(self.resolve_vars_if_possible(normalized_ct)) + } else { + Ok(ct) + } + } } fn response_no_constraints_raw( @@ -313,6 +344,5 @@ fn response_no_constraints_raw( external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()), certainty, }, - defining_opaque_types: Default::default(), } } diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 0e3f179b0c846..843200ca184ea 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -96,14 +96,19 @@ where } fn step_is_coinductive(cx: I, input: CanonicalInput) -> bool { - input.value.goal.predicate.is_coinductive(cx) + input.canonical.value.goal.predicate.is_coinductive(cx) } } fn response_no_constraints( cx: I, - goal: CanonicalInput, + input: CanonicalInput, certainty: Certainty, ) -> QueryResult { - Ok(super::response_no_constraints_raw(cx, goal.max_universe, goal.variables, certainty)) + Ok(super::response_no_constraints_raw( + cx, + input.canonical.max_universe, + input.canonical.variables, + certainty, + )) } diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 5828b2ecf34d5..a8d6536baadde 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -627,11 +627,16 @@ where } ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + let assume = ecx.structurally_normalize_const( + goal.param_env, + goal.predicate.trait_ref.args.const_at(2), + )?; + let certainty = ecx.is_transmutable( goal.param_env, goal.predicate.trait_ref.args.type_at(0), goal.predicate.trait_ref.args.type_at(1), - goal.predicate.trait_ref.args.const_at(2), + assume, )?; ecx.evaluate_added_goals_and_make_canonical_response(certainty) }) @@ -785,7 +790,8 @@ where let mut responses = vec![]; // If the principal def ids match (or are both none), then we're not doing // trait upcasting. We're just removing auto traits (or shortening the lifetime). - if a_data.principal_def_id() == b_data.principal_def_id() { + let b_principal_def_id = b_data.principal_def_id(); + if a_data.principal_def_id() == b_principal_def_id || b_principal_def_id.is_none() { responses.extend(self.consider_builtin_upcast_to_principal( goal, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index e5a14f6a15658..3613b7b862d7f 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -553,9 +553,9 @@ passes_only_has_effect_on = *[unspecified] (unspecified--this is a compiler bug) } -passes_optimize_not_fn_or_closure = - attribute should be applied to function or closure - .label = not a function or closure +passes_optimize_invalid_target = + attribute applied to an invalid target + .label = invalid target passes_outer_crate_level_attr = crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4516ea94cad71..8b30546d5ccec 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -124,7 +124,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } [sym::inline, ..] => self.check_inline(hir_id, attr, span, target), [sym::coverage, ..] => self.check_coverage(attr, span, target), - [sym::optimize, ..] => self.check_optimize(hir_id, attr, target), + [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target), [sym::no_sanitize, ..] => { self.check_applied_to_fn_or_method(hir_id, attr, span, target) } @@ -433,23 +433,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Checks that `#[optimize(..)]` is applied to a function/closure/method, /// or to an impl block or module. - // FIXME(#128488): this should probably be elevated to an error? - fn check_optimize(&self, hir_id: HirId, attr: &Attribute, target: Target) { - match target { + fn check_optimize(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + let is_valid = matches!( + target, Target::Fn - | Target::Closure - | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) - | Target::Impl - | Target::Mod => {} - - _ => { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr.span, - errors::OptimizeNotFnOrClosure, - ); - } + | Target::Closure + | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) + ); + if !is_valid { + self.dcx().emit_err(errors::OptimizeInvalidTarget { + attr_span: attr.span, + defn_span: span, + on_crate: hir_id == CRATE_HIR_ID, + }); } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 6dc3dfba58f06..f01ddbd47efe8 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -76,9 +76,15 @@ pub(crate) struct CoverageNotFnOrClosure { pub defn_span: Span, } -#[derive(LintDiagnostic)] -#[diag(passes_optimize_not_fn_or_closure)] -pub(crate) struct OptimizeNotFnOrClosure; +#[derive(Diagnostic)] +#[diag(passes_optimize_invalid_target)] +pub(crate) struct OptimizeInvalidTarget { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, + pub on_crate: bool, +} #[derive(Diagnostic)] #[diag(passes_should_be_applied_to_fn)] diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index a4c3d789176c3..27714a0fdcca0 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -122,7 +122,9 @@ impl<'k> StatCollector<'k> { // We will soon sort, so the initial order does not matter. #[allow(rustc::potential_query_instability)] let mut nodes: Vec<_> = self.nodes.iter().collect(); - nodes.sort_by_key(|(_, node)| node.stats.count * node.stats.size); + nodes.sort_by_cached_key(|(label, node)| { + (node.stats.count * node.stats.size, label.to_owned()) + }); let total_size = nodes.iter().map(|(_, node)| node.stats.count * node.stats.size).sum(); diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml index 0cb47e03441ba..34fb1bdf6fa79 100644 --- a/compiler/rustc_pattern_analysis/Cargo.toml +++ b/compiler/rustc_pattern_analysis/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena", optional = true } rustc_data_structures = { path = "../rustc_data_structures", optional = true } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 0145ffd3a4b7c..0ca6bb8c07d9d 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1193,7 +1193,11 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { if !ident.as_str().starts_with('_') { self.r.unused_macros.insert(def_id, (node_id, ident)); for (rule_i, rule_span) in &self.r.macro_map[&def_id.to_def_id()].rule_spans { - self.r.unused_macro_rules.insert((def_id, *rule_i), (ident, *rule_span)); + self.r + .unused_macro_rules + .entry(def_id) + .or_default() + .insert(*rule_i, (ident, *rule_span)); } } } diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 3080a0c9892dd..e36055e85755e 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -184,11 +184,11 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { // If the extern crate isn't in the extern prelude, // there is no way it can be written as a `use`. - if !self + if self .r .extern_prelude .get(&extern_crate.ident) - .is_some_and(|entry| !entry.introduced_by_item) + .is_none_or(|entry| entry.introduced_by_item) { continue; } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index b84cbf9c62941..98db36b12be67 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -4011,6 +4011,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let instead = res.is_some(); let suggestion = if let Some((start, end)) = this.diag_metadata.in_range && path[0].ident.span.lo() == end.span.lo() + && !matches!(start.kind, ExprKind::Lit(_)) { let mut sugg = "."; let mut span = start.span.between(end.span); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 24a0c252e55ad..35d491cfc18e7 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1122,7 +1122,8 @@ pub struct Resolver<'ra, 'tcx> { local_macro_def_scopes: FxHashMap>, ast_transform_scopes: FxHashMap>, unused_macros: FxHashMap, - unused_macro_rules: FxHashMap<(LocalDefId, usize), (Ident, Span)>, + /// A map from the macro to all its potentially unused arms. + unused_macro_rules: FxIndexMap>, proc_macro_stubs: FxHashSet, /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index fa2be8216c73f..e43c871866581 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -340,7 +340,9 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { fn record_macro_rule_usage(&mut self, id: NodeId, rule_i: usize) { let did = self.local_def_id(id); - self.unused_macro_rules.remove(&(did, rule_i)); + if let Some(rules) = self.unused_macro_rules.get_mut(&did) { + rules.remove(&rule_i); + } } fn check_unused_macros(&mut self) { @@ -352,18 +354,24 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { BuiltinLintDiag::UnusedMacroDefinition(ident.name), ); } - for (&(def_id, arm_i), &(ident, rule_span)) in self.unused_macro_rules.iter() { - if self.unused_macros.contains_key(&def_id) { - // We already lint the entire macro as unused - continue; + + for (&def_id, unused_arms) in self.unused_macro_rules.iter() { + let mut unused_arms = unused_arms.iter().collect::>(); + unused_arms.sort_by_key(|&(&arm_i, _)| arm_i); + + for (&arm_i, &(ident, rule_span)) in unused_arms { + if self.unused_macros.contains_key(&def_id) { + // We already lint the entire macro as unused + continue; + } + let node_id = self.def_id_to_node_id[def_id]; + self.lint_buffer.buffer_lint( + UNUSED_MACRO_RULES, + node_id, + rule_span, + BuiltinLintDiag::MacroRuleNeverUsed(arm_i, ident.name), + ); } - let node_id = self.def_id_to_node_id[def_id]; - self.lint_buffer.buffer_lint( - UNUSED_MACRO_RULES, - node_id, - rule_span, - BuiltinLintDiag::MacroRuleNeverUsed(arm_i, ident.name), - ); } } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 53834198f6328..ca75952fe3d43 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -133,7 +133,9 @@ fn encode_const<'tcx>( // bool value false is encoded as 0 and true as 1. match ct_ty.kind() { ty::Int(ity) => { - let bits = c.eval_bits(tcx, ty::ParamEnv::reveal_all()); + let bits = c + .try_to_bits(tcx, ty::ParamEnv::reveal_all()) + .expect("expected monomorphic const in cfi"); let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; if val < 0 { s.push('n'); @@ -141,7 +143,9 @@ fn encode_const<'tcx>( let _ = write!(s, "{val}"); } ty::Uint(_) => { - let val = c.eval_bits(tcx, ty::ParamEnv::reveal_all()); + let val = c + .try_to_bits(tcx, ty::ParamEnv::reveal_all()) + .expect("expected monomorphic const in cfi"); let _ = write!(s, "{val}"); } ty::Bool => { diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index ed5d2d1e799e1..8fa8f2ac4021e 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -406,7 +406,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let tcx = tables.tcx; let mir_const = cnst.internal(&mut *tables, tcx); mir_const - .try_eval_target_usize(tables.tcx, ParamEnv::empty()) + .try_to_target_usize(tables.tcx) .ok_or_else(|| Error::new(format!("Const `{cnst:?}` cannot be encoded as u64"))) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 6f62b4f82d76e..3ab482072b8f4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1913,7 +1913,7 @@ symbols! { str_trim, str_trim_end, str_trim_start, - strict_provenance, + strict_provenance_lints, string_as_mut_str, string_as_str, string_deref_patterns, diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index f3da7ff1ca7b0..d092fa8f0820f 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -555,7 +555,6 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> { // We only mangle a typed value if the const can be evaluated. - let ct = ct.normalize(self.tcx, ty::ParamEnv::reveal_all()); let (ct_ty, valtree) = match ct.kind() { ty::ConstKind::Value(ty, val) => (ty, val), @@ -592,7 +591,9 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => { ct_ty.print(self)?; - let mut bits = ct.eval_bits(self.tcx, ty::ParamEnv::reveal_all()); + let mut bits = ct + .try_to_bits(self.tcx, ty::ParamEnv::reveal_all()) + .expect("expected const to be monomorphic"); // Negative integer values are mangled using `n` as a "sign prefix". if let ty::Int(ity) = ct_ty.kind() { diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 82e11a3afce32..24f5646a9ea4b 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2514,6 +2514,13 @@ fn add_link_args(link_args: &mut LinkArgs, flavor: LinkerFlavor, args: &[&'stati add_link_args_iter(link_args, flavor, args.iter().copied().map(Cow::Borrowed)) } +impl TargetOptions { + pub fn supports_comdat(&self) -> bool { + // XCOFF and MachO don't support COMDAT. + !self.is_like_aix && !self.is_like_osx + } +} + impl TargetOptions { fn link_args(flavor: LinkerFlavor, args: &[&'static str]) -> LinkArgs { let mut link_args = LinkArgs::new(); @@ -2750,10 +2757,9 @@ impl Target { } } - /// Returns a None if the UNSUPPORTED_CALLING_CONVENTIONS lint should be emitted - pub fn is_abi_supported(&self, abi: Abi) -> Option { + pub fn is_abi_supported(&self, abi: Abi) -> bool { use Abi::*; - Some(match abi { + match abi { Rust | C { .. } | System { .. } @@ -2812,9 +2818,9 @@ impl Target { // architectures for which these calling conventions are actually well defined. Stdcall { .. } | Fastcall { .. } if self.arch == "x86" => true, Vectorcall { .. } if ["x86", "x86_64"].contains(&&self.arch[..]) => true, - // Return a `None` for other cases so that we know to emit a future compat lint. - Stdcall { .. } | Fastcall { .. } | Vectorcall { .. } => return None, - }) + // Reject these calling conventions everywhere else. + Stdcall { .. } | Fastcall { .. } | Vectorcall { .. } => false, + } } /// Minimum integer size in bits that this target can perform atomic diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs index e869314d4d8b9..68d5119356446 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs @@ -23,6 +23,7 @@ pub(crate) fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD, + supports_xray: true, direct_access_external_data: Some(false), ..base::linux_gnu::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs index 70e8bf633a9a6..25d3559d920bd 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs @@ -24,6 +24,8 @@ pub(crate) fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD, + supports_xray: true, + direct_access_external_data: Some(false), ..base::linux_musl::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs index 90bcd9a45cf42..12e026294cf4f 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs @@ -1,4 +1,4 @@ -use crate::spec::{SanitizerSet, Target, TargetOptions, base}; +use crate::spec::{CodeModel, SanitizerSet, Target, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,6 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), arch: "loongarch64".into(), options: TargetOptions { + code_model: Some(CodeModel::Medium), cpu: "generic".into(), features: "+f,+d".into(), llvm_abiname: "lp64d".into(), @@ -22,6 +23,8 @@ pub(crate) fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD, + supports_xray: true, + direct_access_external_data: Some(false), ..base::linux_ohos::opts() }, } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 5076152dbff8f..549beafd1969c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -290,7 +290,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Drop) - && matches!(predicate_constness, ty::BoundConstness::ConstIfConst | ty::BoundConstness::Const) + && matches!(predicate_constness, Some(ty::BoundConstness::ConstIfConst | ty::BoundConstness::Const)) { err.note("`~const Drop` was renamed to `~const Destruct`"); err.note("See for more details"); @@ -1277,19 +1277,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let normalized_term = ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term); - let is_normalized_term_expected = !matches!( - obligation.cause.code().peel_derives(), - ObligationCauseCode::WhereClause(..) - | ObligationCauseCode::WhereClauseInExpr(..) - | ObligationCauseCode::Coercion { .. } - ); - - let (expected, actual) = if is_normalized_term_expected { - (normalized_term, data.term) - } else { - (data.term, normalized_term) - }; - // constrain inference variables a bit more to nested obligations from normalize so // we can have more helpful errors. // @@ -1298,12 +1285,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let _ = ocx.select_where_possible(); if let Err(new_err) = - ocx.eq(&obligation.cause, obligation.param_env, expected, actual) + ocx.eq(&obligation.cause, obligation.param_env, data.term, normalized_term) { ( Some(( data.projection_term, - is_normalized_term_expected, + false, self.resolve_vars_if_possible(normalized_term), data.term, )), @@ -1444,12 +1431,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &mut diag, &obligation.cause, secondary_span, - values.map(|(_, is_normalized_ty_expected, normalized_ty, expected_ty)| { - infer::ValuePairs::Terms(ExpectedFound::new( - is_normalized_ty_expected, - normalized_ty, - expected_ty, - )) + values.map(|(_, _, normalized_ty, expected_ty)| { + infer::ValuePairs::Terms(ExpectedFound::new(true, expected_ty, normalized_ty)) }), err, false, @@ -2209,7 +2192,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, trait_predicate: ty::PolyTraitPredicate<'tcx>, message: Option, - predicate_constness: ty::BoundConstness, + predicate_constness: Option, append_const_msg: Option, post_message: String, ) -> String { @@ -2217,19 +2200,21 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .and_then(|cannot_do_this| { match (predicate_constness, append_const_msg) { // do nothing if predicate is not const - (ty::BoundConstness::NotConst, _) => Some(cannot_do_this), + (None, _) => Some(cannot_do_this), // suggested using default post message ( - ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, + Some(ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst), Some(AppendConstMessage::Default), ) => Some(format!("{cannot_do_this} in const contexts")), // overridden post message ( - ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, + Some(ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst), Some(AppendConstMessage::Custom(custom_msg, _)), ) => Some(format!("{cannot_do_this}{custom_msg}")), // fallback to generic message - (ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, None) => None, + (Some(ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst), None) => { + None + } } }) .unwrap_or_else(|| { @@ -2247,124 +2232,143 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span: Span, ) -> GetSafeTransmuteErrorAndReason { use rustc_transmute::Answer; + self.probe(|_| { + // We don't assemble a transmutability candidate for types that are generic + // and we should have ambiguity for types that still have non-region infer. + if obligation.predicate.has_non_region_param() || obligation.has_non_region_infer() { + return GetSafeTransmuteErrorAndReason::Default; + } - // We don't assemble a transmutability candidate for types that are generic - // and we should have ambiguity for types that still have non-region infer. - if obligation.predicate.has_non_region_param() || obligation.has_non_region_infer() { - return GetSafeTransmuteErrorAndReason::Default; - } + // Erase regions because layout code doesn't particularly care about regions. + let trait_ref = + self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref)); - // Erase regions because layout code doesn't particularly care about regions. - let trait_ref = - self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref)); + let src_and_dst = rustc_transmute::Types { + dst: trait_ref.args.type_at(0), + src: trait_ref.args.type_at(1), + }; - let src_and_dst = rustc_transmute::Types { - dst: trait_ref.args.type_at(0), - src: trait_ref.args.type_at(1), - }; - let Some(assume) = rustc_transmute::Assume::from_const( - self.infcx.tcx, - obligation.param_env, - trait_ref.args.const_at(2), - ) else { - self.dcx().span_delayed_bug( - span, - "Unable to construct rustc_transmute::Assume where it was previously possible", - ); - return GetSafeTransmuteErrorAndReason::Silent; - }; + let ocx = ObligationCtxt::new(self); + let Ok(assume) = ocx.structurally_normalize_const( + &obligation.cause, + obligation.param_env, + trait_ref.args.const_at(2), + ) else { + self.dcx().span_delayed_bug( + span, + "Unable to construct rustc_transmute::Assume where it was previously possible", + ); + return GetSafeTransmuteErrorAndReason::Silent; + }; - let dst = trait_ref.args.type_at(0); - let src = trait_ref.args.type_at(1); + let Some(assume) = + rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, assume) + else { + self.dcx().span_delayed_bug( + span, + "Unable to construct rustc_transmute::Assume where it was previously possible", + ); + return GetSafeTransmuteErrorAndReason::Silent; + }; - let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`"); + let dst = trait_ref.args.type_at(0); + let src = trait_ref.args.type_at(1); + let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`"); - match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( - obligation.cause, - src_and_dst, - assume, - ) { - Answer::No(reason) => { - let safe_transmute_explanation = match reason { - rustc_transmute::Reason::SrcIsNotYetSupported => { - format!("analyzing the transmutability of `{src}` is not yet supported") - } + match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( + obligation.cause, + src_and_dst, + assume, + ) { + Answer::No(reason) => { + let safe_transmute_explanation = match reason { + rustc_transmute::Reason::SrcIsNotYetSupported => { + format!("analyzing the transmutability of `{src}` is not yet supported") + } - rustc_transmute::Reason::DstIsNotYetSupported => { - format!("analyzing the transmutability of `{dst}` is not yet supported") - } + rustc_transmute::Reason::DstIsNotYetSupported => { + format!("analyzing the transmutability of `{dst}` is not yet supported") + } - rustc_transmute::Reason::DstIsBitIncompatible => { - format!("at least one value of `{src}` isn't a bit-valid value of `{dst}`") - } + rustc_transmute::Reason::DstIsBitIncompatible => { + format!( + "at least one value of `{src}` isn't a bit-valid value of `{dst}`" + ) + } - rustc_transmute::Reason::DstUninhabited => { - format!("`{dst}` is uninhabited") - } + rustc_transmute::Reason::DstUninhabited => { + format!("`{dst}` is uninhabited") + } - rustc_transmute::Reason::DstMayHaveSafetyInvariants => { - format!("`{dst}` may carry safety invariants") - } - rustc_transmute::Reason::DstIsTooBig => { - format!("the size of `{src}` is smaller than the size of `{dst}`") - } - rustc_transmute::Reason::DstRefIsTooBig { src, dst } => { - let src_size = src.size; - let dst_size = dst.size; - format!( - "the referent size of `{src}` ({src_size} bytes) is smaller than that of `{dst}` ({dst_size} bytes)" - ) - } - rustc_transmute::Reason::SrcSizeOverflow => { - format!( - "values of the type `{src}` are too big for the target architecture" - ) - } - rustc_transmute::Reason::DstSizeOverflow => { - format!( - "values of the type `{dst}` are too big for the target architecture" - ) - } - rustc_transmute::Reason::DstHasStricterAlignment { - src_min_align, - dst_min_align, - } => { - format!( - "the minimum alignment of `{src}` ({src_min_align}) should be greater than that of `{dst}` ({dst_min_align})" - ) - } - rustc_transmute::Reason::DstIsMoreUnique => { - format!("`{src}` is a shared reference, but `{dst}` is a unique reference") - } - // Already reported by rustc - rustc_transmute::Reason::TypeError => { - return GetSafeTransmuteErrorAndReason::Silent; - } - rustc_transmute::Reason::SrcLayoutUnknown => { - format!("`{src}` has an unknown layout") - } - rustc_transmute::Reason::DstLayoutUnknown => { - format!("`{dst}` has an unknown layout") + rustc_transmute::Reason::DstMayHaveSafetyInvariants => { + format!("`{dst}` may carry safety invariants") + } + rustc_transmute::Reason::DstIsTooBig => { + format!("the size of `{src}` is smaller than the size of `{dst}`") + } + rustc_transmute::Reason::DstRefIsTooBig { src, dst } => { + let src_size = src.size; + let dst_size = dst.size; + format!( + "the referent size of `{src}` ({src_size} bytes) \ + is smaller than that of `{dst}` ({dst_size} bytes)" + ) + } + rustc_transmute::Reason::SrcSizeOverflow => { + format!( + "values of the type `{src}` are too big for the target architecture" + ) + } + rustc_transmute::Reason::DstSizeOverflow => { + format!( + "values of the type `{dst}` are too big for the target architecture" + ) + } + rustc_transmute::Reason::DstHasStricterAlignment { + src_min_align, + dst_min_align, + } => { + format!( + "the minimum alignment of `{src}` ({src_min_align}) should \ + be greater than that of `{dst}` ({dst_min_align})" + ) + } + rustc_transmute::Reason::DstIsMoreUnique => { + format!( + "`{src}` is a shared reference, but `{dst}` is a unique reference" + ) + } + // Already reported by rustc + rustc_transmute::Reason::TypeError => { + return GetSafeTransmuteErrorAndReason::Silent; + } + rustc_transmute::Reason::SrcLayoutUnknown => { + format!("`{src}` has an unknown layout") + } + rustc_transmute::Reason::DstLayoutUnknown => { + format!("`{dst}` has an unknown layout") + } + }; + GetSafeTransmuteErrorAndReason::Error { + err_msg, + safe_transmute_explanation: Some(safe_transmute_explanation), } - }; - GetSafeTransmuteErrorAndReason::Error { - err_msg, - safe_transmute_explanation: Some(safe_transmute_explanation), } + // Should never get a Yes at this point! We already ran it before, and did not get a Yes. + Answer::Yes => span_bug!( + span, + "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes", + ), + // Reached when a different obligation (namely `Freeze`) causes the + // transmutability analysis to fail. In this case, silence the + // transmutability error message in favor of that more specific + // error. + Answer::If(_) => GetSafeTransmuteErrorAndReason::Error { + err_msg, + safe_transmute_explanation: None, + }, } - // Should never get a Yes at this point! We already ran it before, and did not get a Yes. - Answer::Yes => span_bug!( - span, - "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes", - ), - // Reached when a different obligation (namely `Freeze`) causes the - // transmutability analysis to fail. In this case, silence the - // transmutability error message in favor of that more specific - // error. - Answer::If(_) => { - GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation: None } - } - } + }) } /// For effects predicates such as `::Effects: Compat`, pretend that the @@ -2375,26 +2379,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { p: ty::PolyTraitPredicate<'tcx>, leaf: ty::PolyTraitPredicate<'tcx>, span: Span, - ) -> (ty::PolyTraitPredicate<'tcx>, ty::PolyTraitPredicate<'tcx>, ty::BoundConstness) { + ) -> (ty::PolyTraitPredicate<'tcx>, ty::PolyTraitPredicate<'tcx>, Option) + { let trait_ref = p.to_poly_trait_ref(); if !self.tcx.is_lang_item(trait_ref.def_id(), LangItem::EffectsCompat) { - return (p, leaf, ty::BoundConstness::NotConst); + return (p, leaf, None); } let Some(ty::Alias(ty::AliasTyKind::Projection, projection)) = trait_ref.self_ty().no_bound_vars().map(Ty::kind) else { - return (p, leaf, ty::BoundConstness::NotConst); + return (p, leaf, None); }; let constness = trait_ref.skip_binder().args.const_at(1); let constness = if constness == self.tcx.consts.true_ || constness.is_ct_infer() { - ty::BoundConstness::NotConst + None } else if constness == self.tcx.consts.false_ { - ty::BoundConstness::Const + Some(ty::BoundConstness::Const) } else if matches!(constness.kind(), ty::ConstKind::Param(_)) { - ty::BoundConstness::ConstIfConst + Some(ty::BoundConstness::ConstIfConst) } else { self.dcx().span_bug(span, format!("Unknown constness argument: {constness:?}")); }; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index cd41ab9fa6cb0..7f42c932fcf57 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -231,7 +231,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]` if let ty::Array(aty, len) = self_ty.kind() { flags.push((sym::_Self, Some("[]".to_string()))); - let len = len.try_to_valtree().and_then(|v| v.try_to_target_usize(self.tcx)); + let len = len.try_to_target_usize(self.tcx); flags.push((sym::_Self, Some(format!("[{aty}; _]")))); if let Some(n) = len { flags.push((sym::_Self, Some(format!("[{aty}; {n}]")))); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 733baaa99e52a..677905a3085b5 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3701,12 +3701,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } _ => None, }; - // Also add host param, if present - let host = self.tcx.generics_of(trait_pred.def_id()).host_effect_index.map(|idx| trait_pred.skip_binder().trait_ref.args[idx]); let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate { trait_ref: ty::TraitRef::new(self.tcx, trait_pred.def_id(), - [field_ty].into_iter().chain(trait_args).chain(host), + [field_ty].into_iter().chain(trait_args), ), ..*tr }); diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index f232a896f9698..bacb3b1b1b861 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -5,7 +5,9 @@ use rustc_hir::lang_items::LangItem; pub use rustc_infer::infer::*; use rustc_macros::extension; use rustc_middle::arena::ArenaAllocatable; -use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse}; +use rustc_middle::infer::canonical::{ + Canonical, CanonicalQueryInput, CanonicalQueryResponse, QueryResponse, +}; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast}; use rustc_span::DUMMY_SP; @@ -132,7 +134,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// `K: TypeFoldable>`.) fn enter_canonical_trait_query( self, - canonical_key: &Canonical<'tcx, K>, + canonical_key: &CanonicalQueryInput<'tcx, K>, operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result, ) -> Result, NoSolution> where diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index df9ac2b80fded..5793ac2fc313c 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -4,7 +4,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::{ - Canonical, CanonicalExt as _, CanonicalVarInfo, CanonicalVarValues, + Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarValues, }; use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::solve::Goal; @@ -36,6 +36,7 @@ impl<'tcx> Deref for SolverDelegate<'tcx> { } impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<'tcx> { + type Infcx = InferCtxt<'tcx>; type Interner = TyCtxt<'tcx>; fn cx(&self) -> TyCtxt<'tcx> { @@ -47,7 +48,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< fn build_with_canonical( interner: TyCtxt<'tcx>, solver_mode: SolverMode, - canonical: &Canonical<'tcx, V>, + canonical: &CanonicalQueryInput<'tcx, V>, ) -> (Self, V, CanonicalVarValues<'tcx>) where V: TypeFoldable>, diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index f4a2483cebf87..f8fb297e36c4b 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -298,6 +298,7 @@ fn equate_impl_headers<'tcx>( } /// The result of [fn impl_intersection_has_impossible_obligation]. +#[derive(Debug)] enum IntersectionHasImpossibleObligations<'tcx> { Yes, No { @@ -328,6 +329,7 @@ enum IntersectionHasImpossibleObligations<'tcx> { /// of the two impls above to be empty. /// /// Importantly, this works even if there isn't a `impl !Error for MyLocalType`. +#[instrument(level = "debug", skip(selcx), ret)] fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligations: &'a [PredicateObligation<'tcx>], diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index a45ec8b3c4490..5e270b62b0081 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -329,4 +329,15 @@ where .at(cause, param_env) .structurally_normalize(value, &mut **self.engine.borrow_mut()) } + + pub fn structurally_normalize_const( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: ty::Const<'tcx>, + ) -> Result, Vec> { + self.infcx + .at(cause, param_env) + .structurally_normalize_const(value, &mut **self.engine.borrow_mut()) + } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 0fb795fc18401..cffb9b5984167 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -368,7 +368,7 @@ pub fn normalize_param_env_or_error<'tcx>( // should actually be okay since without `feature(generic_const_exprs)` the only // const arguments that have a non-empty param env are array repeat counts. These // do not appear in the type system though. - c.normalize(self.0, ty::ParamEnv::empty()) + c.normalize_internal(self.0, ty::ParamEnv::empty()) } } @@ -562,11 +562,20 @@ fn is_impossible_associated_item( let generics = tcx.generics_of(trait_item_def_id); let predicates = tcx.predicates_of(trait_item_def_id); + + // Be conservative in cases where we have `W` and a method like `Self: Sized`, + // since that method *may* have some substitutions where the predicates hold. + // + // This replicates the logic we use in coherence. + let infcx = + tcx.infer_ctxt().ignoring_regions().with_next_trait_solver(true).intercrate(true).build(); + let param_env = ty::ParamEnv::empty(); + let fresh_args = infcx.fresh_args_for_item(tcx.def_span(impl_def_id), impl_def_id); + let impl_trait_ref = tcx .impl_trait_ref(impl_def_id) .expect("expected impl to correspond to trait") - .instantiate_identity(); - let param_env = tcx.param_env(impl_def_id); + .instantiate(tcx, fresh_args); let mut visitor = ReferencesOnlyParentGenerics { tcx, generics, trait_item_def_id }; let predicates_for_trait = predicates.predicates.iter().filter_map(|(pred, span)| { @@ -580,16 +589,9 @@ fn is_impossible_associated_item( }) }); - let infcx = tcx.infer_ctxt().ignoring_regions().build(); - for obligation in predicates_for_trait { - // Ignore overflow error, to be conservative. - if let Ok(result) = infcx.evaluate_obligation(&obligation) - && !result.may_apply() - { - return true; - } - } - false + let ocx = ObligationCtxt::new(&infcx); + ocx.register_obligations(predicates_for_trait); + !ocx.select_where_possible().is_empty() } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 7eac355934889..d246d37f74865 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -413,7 +413,7 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx self.selcx.infcx, &mut self.universes, constant, - |constant| constant.normalize(tcx, self.param_env), + |constant| constant.normalize_internal(tcx, self.param_env), ) } } diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 339e4bf1f22c5..88c11e55b7a7e 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -1,6 +1,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_infer::infer::InferOk; use rustc_infer::infer::resolve::OpportunisticRegionResolver; +use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_macros::extension; use rustc_middle::infer::canonical::{OriginalQueryValues, QueryRegionConstraints}; use rustc_middle::span_bug; @@ -54,11 +55,12 @@ fn implied_outlives_bounds<'a, 'tcx>( assert!(!ty.has_non_region_infer()); let mut canonical_var_values = OriginalQueryValues::default(); - let canonical_ty = infcx.canonicalize_query(param_env.and(ty), &mut canonical_var_values); + let input = ImpliedOutlivesBounds { ty }; + let canonical = infcx.canonicalize_query(param_env.and(input), &mut canonical_var_values); let implied_bounds_result = if compat { - infcx.tcx.implied_outlives_bounds_compat(canonical_ty) + infcx.tcx.implied_outlives_bounds_compat(canonical) } else { - infcx.tcx.implied_outlives_bounds(canonical_ty) + infcx.tcx.implied_outlives_bounds(canonical) }; let Ok(canonical_result) = implied_bounds_result else { return vec![]; diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 7f7c9bced1859..cc634b65a0bf5 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1642,24 +1642,9 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>( sig, ); - let host_effect_param = match *fn_type.kind() { - ty::FnDef(def_id, args) => tcx - .generics_of(def_id) - .host_effect_index - .map_or(tcx.consts.true_, |idx| args.const_at(idx)), - ty::FnPtr(..) => tcx.consts.true_, - _ => unreachable!("only expected FnPtr or FnDef in `confirm_fn_pointer_candidate`"), - }; - - confirm_callable_candidate( - selcx, - obligation, - sig, - util::TupleArgumentsFlag::Yes, - host_effect_param, - ) - .with_addl_obligations(nested) - .with_addl_obligations(obligations) + confirm_callable_candidate(selcx, obligation, sig, util::TupleArgumentsFlag::Yes) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) } fn confirm_closure_candidate<'cx, 'tcx>( @@ -1739,16 +1724,9 @@ fn confirm_closure_candidate<'cx, 'tcx>( debug!(?obligation, ?closure_sig, ?obligations, "confirm_closure_candidate"); - confirm_callable_candidate( - selcx, - obligation, - closure_sig, - util::TupleArgumentsFlag::No, - // FIXME(effects): This doesn't handle const closures correctly! - selcx.tcx().consts.true_, - ) - .with_addl_obligations(nested) - .with_addl_obligations(obligations) + confirm_callable_candidate(selcx, obligation, closure_sig, util::TupleArgumentsFlag::No) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) } fn confirm_callable_candidate<'cx, 'tcx>( @@ -1756,7 +1734,6 @@ fn confirm_callable_candidate<'cx, 'tcx>( obligation: &ProjectionTermObligation<'tcx>, fn_sig: ty::PolyFnSig<'tcx>, flag: util::TupleArgumentsFlag, - fn_host_effect: ty::Const<'tcx>, ) -> Progress<'tcx> { let tcx = selcx.tcx(); @@ -1771,7 +1748,6 @@ fn confirm_callable_candidate<'cx, 'tcx>( obligation.predicate.self_ty(), fn_sig, flag, - fn_host_effect, ) .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate { projection_term: ty::AliasTerm::new_from_args(tcx, fn_once_output_def_id, trait_ref.args), diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index c70fe13fc69a2..4ff0910c9b969 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -1,4 +1,5 @@ use rustc_data_structures::fx::FxHashSet; +use rustc_infer::traits::query::type_op::DropckOutlives; use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult}; use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, Ty, TyCtxt}; use rustc_span::{DUMMY_SP, Span}; @@ -88,10 +89,10 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { pub fn compute_dropck_outlives_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, - goal: ParamEnvAnd<'tcx, Ty<'tcx>>, + goal: ParamEnvAnd<'tcx, DropckOutlives<'tcx>>, ) -> Result, NoSolution> { let tcx = ocx.infcx.tcx; - let ParamEnvAnd { param_env, value: for_ty } = goal; + let ParamEnvAnd { param_env, value: DropckOutlives { dropped_ty } } = goal; let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] }; @@ -99,7 +100,7 @@ pub fn compute_dropck_outlives_inner<'tcx>( // something from the stack and invoke // `dtorck_constraint_for_ty_inner`. This may produce new types that // have to be pushed on the stack. This continues until we have explored - // all the reachable types from the type `for_ty`. + // all the reachable types from the type `dropped_ty`. // // Example: Imagine that we have the following code: // @@ -129,7 +130,7 @@ pub fn compute_dropck_outlives_inner<'tcx>( // lead to us trying to push `A` a second time -- to prevent // infinite recursion, we notice that `A` was already pushed // once and stop. - let mut ty_stack = vec![(for_ty, 0)]; + let mut ty_stack = vec![(dropped_ty, 0)]; // Set used to detect infinite recursion. let mut ty_set = FxHashSet::default(); diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 18412b844ff71..01e6516302c2b 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -340,7 +340,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { self.infcx, &mut self.universes, constant, - |constant| constant.normalize(self.infcx.tcx, self.param_env), + |constant| constant.normalize_internal(self.infcx.tcx, self.param_env), ); debug!(?constant, ?self.param_env); constant.try_super_fold_with(self) diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index c84c3147a3842..dc3f5544613ad 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -7,7 +7,7 @@ use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, UserArgs, UserSelfTy, User use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; -use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { @@ -22,7 +22,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result, NoSolution> { tcx.type_op_ascribe_user_type(canonicalized) } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index bab038af9ed2a..dfd0cab69058b 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -1,7 +1,7 @@ -use rustc_infer::infer::canonical::Canonical; +use rustc_infer::infer::canonical::CanonicalQueryInput; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::query::OutlivesBound; -use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; +use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_middle::infer::canonical::CanonicalQueryResponse; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt}; @@ -14,11 +14,6 @@ use tracing::debug; use crate::traits::query::NoSolution; use crate::traits::{ObligationCtxt, wf}; -#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] -pub struct ImpliedOutlivesBounds<'tcx> { - pub ty: Ty<'tcx>, -} - impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { type QueryResponse = Vec>; @@ -38,16 +33,8 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result, NoSolution> { - // FIXME this `unchecked_map` is only necessary because the - // query is defined as taking a `ParamEnvAnd`; it should - // take an `ImpliedOutlivesBounds` instead - let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| { - let ImpliedOutlivesBounds { ty } = value; - param_env.and(ty) - }); - if tcx.sess.opts.unstable_opts.no_implied_bounds_compat { tcx.implied_outlives_bounds(canonicalized) } else { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 5ae8c87ec027f..a618d96ce9507 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -1,7 +1,6 @@ use std::fmt; use rustc_errors::ErrorGuaranteed; -use rustc_infer::infer::canonical::Certainty; use rustc_infer::traits::PredicateObligations; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::fold::TypeFoldable; @@ -9,7 +8,8 @@ use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use rustc_span::Span; use crate::infer::canonical::{ - Canonical, CanonicalQueryResponse, OriginalQueryValues, QueryRegionConstraints, + CanonicalQueryInput, CanonicalQueryResponse, Certainty, OriginalQueryValues, + QueryRegionConstraints, }; use crate::infer::{InferCtxt, InferOk}; use crate::traits::{ObligationCause, ObligationCtxt}; @@ -80,7 +80,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable> + 't /// not captured in the return value. fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result, NoSolution>; /// In the new trait solver, we already do caching in the solver itself, @@ -102,7 +102,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable> + 't ) -> Result< ( Self::QueryResponse, - Option>>, + Option>>, PredicateObligations<'tcx>, Certainty, ), @@ -135,7 +135,7 @@ where Q: QueryTypeOp<'tcx>, { type Output = Q::QueryResponse; - type ErrorInfo = Canonical<'tcx, ParamEnvAnd<'tcx, Q>>; + type ErrorInfo = CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Q>>; fn fully_perform( self, diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index 62d5655922b09..94df222932efe 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -6,7 +6,7 @@ pub use rustc_middle::traits::query::type_op::Normalize; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; -use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; impl<'tcx, T> super::QueryTypeOp<'tcx> for Normalize @@ -21,7 +21,7 @@ where fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result, NoSolution> { T::type_op_method(tcx, canonicalized) } @@ -40,14 +40,14 @@ pub trait Normalizable<'tcx>: { fn type_op_method( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize>>, ) -> Result, NoSolution>; } impl<'tcx> Normalizable<'tcx> for Ty<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize>>, ) -> Result, NoSolution> { tcx.type_op_normalize_ty(canonicalized) } @@ -56,7 +56,7 @@ impl<'tcx> Normalizable<'tcx> for Ty<'tcx> { impl<'tcx> Normalizable<'tcx> for ty::Clause<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize>>, ) -> Result, NoSolution> { tcx.type_op_normalize_clause(canonicalized) } @@ -65,7 +65,7 @@ impl<'tcx> Normalizable<'tcx> for ty::Clause<'tcx> { impl<'tcx> Normalizable<'tcx> for ty::PolyFnSig<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize>>, ) -> Result, NoSolution> { tcx.type_op_normalize_poly_fn_sig(canonicalized) } @@ -74,7 +74,7 @@ impl<'tcx> Normalizable<'tcx> for ty::PolyFnSig<'tcx> { impl<'tcx> Normalizable<'tcx> for ty::FnSig<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize>>, ) -> Result, NoSolution> { tcx.type_op_normalize_fn_sig(canonicalized) } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs index d891d4ca06f9d..fa05f901f663d 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs @@ -1,23 +1,12 @@ -use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution}; -use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; +use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; -use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; use crate::traits::query::dropck_outlives::{ compute_dropck_outlives_inner, trivial_dropck_outlives, }; - -#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] -pub struct DropckOutlives<'tcx> { - dropped_ty: Ty<'tcx>, -} - -impl<'tcx> DropckOutlives<'tcx> { - pub fn new(dropped_ty: Ty<'tcx>) -> Self { - DropckOutlives { dropped_ty } - } -} +use crate::traits::query::type_op::DropckOutlives; impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { type QueryResponse = DropckOutlivesResult<'tcx>; @@ -31,16 +20,8 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result, NoSolution> { - // FIXME convert to the type expected by the `dropck_outlives` - // query. This should eventually be fixed by changing the - // *underlying query*. - let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| { - let DropckOutlives { dropped_ty } = value; - param_env.and(dropped_ty) - }); - tcx.dropck_outlives(canonicalized) } @@ -48,6 +29,6 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, ) -> Result { - compute_dropck_outlives_inner(ocx, key.param_env.and(key.value.dropped_ty)) + compute_dropck_outlives_inner(ocx, key.param_env.and(key.value)) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 7cdb9ee691e4b..b2dab379262f5 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -5,7 +5,7 @@ use rustc_middle::traits::query::NoSolution; pub use rustc_middle::traits::query::type_op::ProvePredicate; use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt}; -use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { @@ -49,7 +49,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result, NoSolution> { tcx.type_op_prove_predicate(canonicalized) } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 52048ca79f92f..ac38c9e24a290 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -543,23 +543,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Provide an impl, but only for suitable `fn` pointers. ty::FnPtr(sig_tys, hdr) => { if sig_tys.with(hdr).is_fn_trait_compatible() { - candidates - .vec - .push(FnPointerCandidate { fn_host_effect: self.tcx().consts.true_ }); + candidates.vec.push(FnPointerCandidate); } } // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). - ty::FnDef(def_id, args) => { + ty::FnDef(def_id, _args) => { let tcx = self.tcx(); if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible() && tcx.codegen_fn_attrs(def_id).target_features.is_empty() { - candidates.vec.push(FnPointerCandidate { - fn_host_effect: tcx - .generics_of(def_id) - .host_effect_index - .map_or(tcx.consts.true_, |idx| args.const_at(idx)), - }); + candidates.vec.push(FnPointerCandidate); } } _ => {} @@ -1018,7 +1011,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // #2 (region bounds). let principal_def_id_a = a_data.principal_def_id(); let principal_def_id_b = b_data.principal_def_id(); - if principal_def_id_a == principal_def_id_b { + if principal_def_id_a == principal_def_id_b || principal_def_id_b.is_none() { // We may upcast to auto traits that are either explicitly listed in // the object type's bounds, or implied by the principal trait ref's // supertraits. @@ -1170,103 +1163,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn assemble_const_destruct_candidates( &mut self, - obligation: &PolyTraitObligation<'tcx>, + _obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - // If the predicate is `~const Destruct` in a non-const environment, we don't actually need - // to check anything. We'll short-circuit checking any obligations in confirmation, too. - let Some(host_effect_index) = - self.tcx().generics_of(obligation.predicate.def_id()).host_effect_index - else { - candidates.vec.push(BuiltinCandidate { has_nested: false }); - return; - }; - // If the obligation has `host = true`, then the obligation is non-const and it's always - // trivially implemented. - if obligation.predicate.skip_binder().trait_ref.args.const_at(host_effect_index) - == self.tcx().consts.true_ - { - candidates.vec.push(BuiltinCandidate { has_nested: false }); - return; - } - - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); - match self_ty.kind() { - ty::Alias(..) - | ty::Dynamic(..) - | ty::Error(_) - | ty::Bound(..) - | ty::Param(_) - | ty::Placeholder(_) => { - // We don't know if these are `~const Destruct`, at least - // not structurally... so don't push a candidate. - } - - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::Str - | ty::RawPtr(_, _) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Never - | ty::Foreign(_) - | ty::Array(..) - | ty::Pat(..) - | ty::Slice(_) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::Tuple(_) - | ty::CoroutineWitness(..) => { - // These are built-in, and cannot have a custom `impl const Destruct`. - candidates.vec.push(ConstDestructCandidate(None)); - } - - ty::Adt(..) => { - let mut relevant_impl = None; - self.tcx().for_each_relevant_impl( - self.tcx().require_lang_item(LangItem::Drop, None), - obligation.predicate.skip_binder().trait_ref.self_ty(), - |impl_def_id| { - if let Some(old_impl_def_id) = relevant_impl { - self.tcx() - .dcx() - .struct_span_err( - self.tcx().def_span(impl_def_id), - "multiple drop impls found", - ) - .with_span_note( - self.tcx().def_span(old_impl_def_id), - "other impl here", - ) - .delay_as_bug(); - } - - relevant_impl = Some(impl_def_id); - }, - ); - - if let Some(impl_def_id) = relevant_impl { - // Check that `impl Drop` is actually const, if there is a custom impl - if self.tcx().constness(impl_def_id) == hir::Constness::Const { - candidates.vec.push(ConstDestructCandidate(Some(impl_def_id))); - } - } else { - // Otherwise check the ADT like a built-in type (structurally) - candidates.vec.push(ConstDestructCandidate(None)); - } - } - - ty::Infer(_) => { - candidates.ambiguous = true; - } - } + // FIXME(effects): Destruct is not const yet, and it is implemented + // by all types today in non-const setting. + candidates.vec.push(BuiltinCandidate { has_nested: false }); } fn assemble_candidate_for_tuple( diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index cc5c7532b5053..9a49fd7e77e9c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -28,9 +28,9 @@ use super::{BuiltinImplConditions, PredicateObligations, SelectionContext}; use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::util::{self, closure_trait_ref_and_return_type}; use crate::traits::{ - ImplDerivedCause, ImplSource, ImplSourceUserDefinedData, Normalized, Obligation, - ObligationCause, PolyTraitObligation, PredicateObligation, Selection, SelectionError, - SignatureMismatch, TraitDynIncompatible, TraitObligation, Unimplemented, + ImplSource, ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, + PolyTraitObligation, PredicateObligation, Selection, SelectionError, SignatureMismatch, + TraitDynIncompatible, TraitObligation, Unimplemented, }; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { @@ -109,8 +109,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator) } - FnPointerCandidate { fn_host_effect } => { - let data = self.confirm_fn_pointer_candidate(obligation, fn_host_effect)?; + FnPointerCandidate => { + let data = self.confirm_fn_pointer_candidate(obligation)?; ImplSource::Builtin(BuiltinImplSource::Misc, data) } @@ -131,11 +131,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { TraitUpcastingUnsizeCandidate(idx) => { self.confirm_trait_upcasting_unsize_candidate(obligation, idx)? } - - ConstDestructCandidate(def_id) => { - let data = self.confirm_const_destruct_candidate(obligation, def_id)?; - ImplSource::Builtin(BuiltinImplSource::Misc, data) - } }; // The obligations returned by confirmation are recursively evaluated @@ -405,11 +400,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let predicate = obligation.predicate.skip_binder(); - let Some(assume) = rustc_transmute::Assume::from_const( - self.infcx.tcx, - obligation.param_env, - predicate.trait_ref.args.const_at(2), - ) else { + let mut assume = predicate.trait_ref.args.const_at(2); + // FIXME(min_generic_const_exprs): We should shallowly normalize this. + if self.tcx().features().generic_const_exprs { + assume = assume.normalize_internal(self.tcx(), obligation.param_env); + } + let Some(assume) = + rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, assume) + else { return Err(Unimplemented); }; @@ -708,7 +706,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_fn_pointer_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, - fn_host_effect: ty::Const<'tcx>, ) -> Result, SelectionError<'tcx>> { debug!(?obligation, "confirm_fn_pointer_candidate"); let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); @@ -722,7 +719,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self_ty, sig, util::TupleArgumentsFlag::Yes, - fn_host_effect, ) .map_bound(|(trait_ref, _)| trait_ref); @@ -904,11 +900,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let self_ty: Ty<'_> = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let trait_ref = match *self_ty.kind() { - ty::Closure(..) => self.closure_trait_ref_unnormalized( - self_ty, - obligation.predicate.def_id(), - self.tcx().consts.true_, - ), + ty::Closure(..) => { + self.closure_trait_ref_unnormalized(self_ty, obligation.predicate.def_id()) + } ty::CoroutineClosure(_, args) => { args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| { ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [ @@ -1153,6 +1147,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`. let iter = data_a .principal() + .filter(|_| { + // optionally drop the principal, if we're unsizing to no principal + data_b.principal().is_some() + }) .map(|b| b.map_bound(ty::ExistentialPredicate::Trait)) .into_iter() .chain( @@ -1337,170 +1335,4 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => bug!("source: {source}, target: {target}"), }) } - - fn confirm_const_destruct_candidate( - &mut self, - obligation: &PolyTraitObligation<'tcx>, - impl_def_id: Option, - ) -> Result, SelectionError<'tcx>> { - let Some(host_effect_index) = - self.tcx().generics_of(obligation.predicate.def_id()).host_effect_index - else { - bug!() - }; - let host_effect_param: ty::GenericArg<'tcx> = - obligation.predicate.skip_binder().trait_ref.args.const_at(host_effect_index).into(); - - let drop_trait = self.tcx().require_lang_item(LangItem::Drop, None); - - let tcx = self.tcx(); - let self_ty = obligation.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty)); - - let mut nested = PredicateObligations::new(); - let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived); - - // If we have a custom `impl const Drop`, then - // first check it like a regular impl candidate. - // This is copied from confirm_impl_candidate but remaps the predicate to `~const Drop` beforehand. - if let Some(impl_def_id) = impl_def_id { - let mut new_obligation = obligation.clone(); - new_obligation.predicate = new_obligation.predicate.map_bound(|mut trait_pred| { - trait_pred.trait_ref.def_id = drop_trait; - trait_pred - }); - let args = self.rematch_impl(impl_def_id, &new_obligation); - debug!(?args, "impl args"); - - let cause = obligation.derived_cause(|derived| { - ObligationCauseCode::ImplDerived(Box::new(ImplDerivedCause { - derived, - impl_or_alias_def_id: impl_def_id, - impl_def_predicate_index: None, - span: obligation.cause.span, - })) - }); - let obligations = ensure_sufficient_stack(|| { - self.vtable_impl( - impl_def_id, - args, - &cause, - new_obligation.recursion_depth + 1, - new_obligation.param_env, - obligation.predicate, - ) - }); - nested.extend(obligations.nested); - } - - // We want to confirm the ADT's fields if we have an ADT - let mut stack = match *self_ty.skip_binder().kind() { - ty::Adt(def, args) => def.all_fields().map(|f| f.ty(tcx, args)).collect(), - _ => vec![self_ty.skip_binder()], - }; - - while let Some(nested_ty) = stack.pop() { - match *nested_ty.kind() { - // We know these types are trivially drop - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::Str - | ty::RawPtr(_, _) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Never - | ty::Foreign(_) => {} - - // `ManuallyDrop` is trivially drop - ty::Adt(def, _) if def.is_manually_drop() => {} - - // These types are built-in, so we can fast-track by registering - // nested predicates for their constituent type(s) - ty::Array(ty, _) | ty::Slice(ty) | ty::Pat(ty, _) => { - stack.push(ty); - } - ty::Tuple(tys) => { - stack.extend(tys.iter()); - } - ty::Closure(_, args) => { - stack.push(args.as_closure().tupled_upvars_ty()); - } - ty::Coroutine(_, args) => { - let coroutine = args.as_coroutine(); - stack.extend([coroutine.tupled_upvars_ty(), coroutine.witness()]); - } - ty::CoroutineWitness(def_id, args) => { - let tcx = self.tcx(); - stack.extend(tcx.bound_coroutine_hidden_types(def_id).map(|bty| { - self.infcx.enter_forall_and_leak_universe(bty.instantiate(tcx, args)) - })) - } - - // If we have a projection type, make sure to normalize it so we replace it - // with a fresh infer variable - ty::Alias(ty::Projection | ty::Inherent, ..) => { - let predicate = normalize_with_depth_to( - self, - obligation.param_env, - cause.clone(), - obligation.recursion_depth + 1, - self_ty.rebind(ty::TraitPredicate { - trait_ref: ty::TraitRef::new( - self.tcx(), - self.tcx().require_lang_item(LangItem::Destruct, Some(cause.span)), - [nested_ty.into(), host_effect_param], - ), - polarity: ty::PredicatePolarity::Positive, - }), - &mut nested, - ); - - nested.push(Obligation::with_depth( - tcx, - cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - predicate, - )); - } - - // If we have any other type (e.g. an ADT), just register a nested obligation - // since it's either not `const Drop` (and we raise an error during selection), - // or it's an ADT (and we need to check for a custom impl during selection) - ty::Error(_) - | ty::Dynamic(..) - | ty::CoroutineClosure(..) - | ty::Param(_) - | ty::Bound(..) - | ty::Adt(..) - | ty::Alias(ty::Opaque | ty::Weak, _) - | ty::Infer(_) - | ty::Placeholder(_) => { - let predicate = self_ty.rebind(ty::TraitPredicate { - trait_ref: ty::TraitRef::new( - self.tcx(), - self.tcx().require_lang_item(LangItem::Destruct, Some(cause.span)), - [nested_ty.into(), host_effect_param], - ), - polarity: ty::PredicatePolarity::Positive, - }); - - nested.push(Obligation::with_depth( - tcx, - cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - predicate, - )); - } - } - } - - Ok(nested) - } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 621babe910426..4757430a21cba 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1831,12 +1831,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No, // (*) - (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => { - DropVictim::Yes - } - (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => { - DropVictim::No - } + (BuiltinCandidate { has_nested: false }, _) => DropVictim::Yes, + (_, BuiltinCandidate { has_nested: false }) => DropVictim::No, (ParamCandidate(other), ParamCandidate(victim)) => { let same_except_bound_vars = other.skip_binder().trait_ref @@ -1855,11 +1851,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } - // Drop otherwise equivalent non-const fn pointer candidates - (FnPointerCandidate { .. }, FnPointerCandidate { fn_host_effect }) => { - DropVictim::drop_if(*fn_host_effect == self.tcx().consts.true_) - } - ( ParamCandidate(other_cand), ImplCandidate(..) @@ -2766,7 +2757,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { &mut self, self_ty: Ty<'tcx>, fn_trait_def_id: DefId, - fn_host_effect: ty::Const<'tcx>, ) -> ty::PolyTraitRef<'tcx> { let ty::Closure(_, args) = *self_ty.kind() else { bug!("expected closure, found {self_ty}"); @@ -2779,7 +2769,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { self_ty, closure_sig, util::TupleArgumentsFlag::No, - fn_host_effect, ) .map_bound(|(trait_ref, _)| trait_ref) } diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index 3814f8112e92e..f8d98eb856e8e 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -82,6 +82,8 @@ impl<'tcx> At<'_, 'tcx> { } Ok(self.infcx.resolve_vars_if_possible(new_infer_ct)) + } else if self.infcx.tcx.features().generic_const_exprs { + Ok(ct.normalize_internal(self.infcx.tcx, self.param_env)) } else { Ok(self.normalize(ct).into_value_registering_obligations(self.infcx, fulfill_cx)) } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index aed2e3d61aa66..b7a2f20b769d2 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -215,22 +215,13 @@ pub(crate) fn closure_trait_ref_and_return_type<'tcx>( self_ty: Ty<'tcx>, sig: ty::PolyFnSig<'tcx>, tuple_arguments: TupleArgumentsFlag, - fn_host_effect: ty::Const<'tcx>, ) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> { assert!(!self_ty.has_escaping_bound_vars()); let arguments_tuple = match tuple_arguments { TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()), }; - let trait_ref = if tcx.has_host_param(fn_trait_def_id) { - ty::TraitRef::new(tcx, fn_trait_def_id, [ - ty::GenericArg::from(self_ty), - ty::GenericArg::from(arguments_tuple), - ty::GenericArg::from(fn_host_effect), - ]) - } else { - ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]) - }; + let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]); sig.map_bound(|sig| (trait_ref, sig.output())) } diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 6e6f948a2cdc4..ed221e2a1835f 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -154,18 +154,17 @@ fn prepare_vtable_segments_inner<'tcx, T>( // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level. while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() { + let has_entries = + has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id()); + segment_visitor(VtblSegment::TraitOwnEntries { trait_ref: inner_most_trait_ref, - emit_vptr: emit_vptr && !tcx.sess.opts.unstable_opts.no_trait_vptr, + emit_vptr: emit_vptr && has_entries && !tcx.sess.opts.unstable_opts.no_trait_vptr, })?; // If we've emitted (fed to `segment_visitor`) a trait that has methods present in the vtable, // we'll need to emit vptrs from now on. - if !emit_vptr_on_new_entry - && has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id()) - { - emit_vptr_on_new_entry = true; - } + emit_vptr_on_new_entry |= has_entries; if let Some(next_inner_most_trait_ref) = siblings.find(|&sibling| visited.insert(sibling.upcast(tcx))) diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index 0d052ecf0dfd2..4e5309eea287b 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -10,7 +10,7 @@ use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::dropck_outlives::{ compute_dropck_outlives_inner, dtorck_constraint_for_ty_inner, }; -use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution}; +use rustc_trait_selection::traits::query::{CanonicalDropckOutlivesGoal, NoSolution}; use tracing::debug; pub(crate) fn provide(p: &mut Providers) { @@ -19,7 +19,7 @@ pub(crate) fn provide(p: &mut Providers) { fn dropck_outlives<'tcx>( tcx: TyCtxt<'tcx>, - canonical_goal: CanonicalTyGoal<'tcx>, + canonical_goal: CanonicalDropckOutlivesGoal<'tcx>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, NoSolution> { debug!("dropck_outlives(goal={:#?})", canonical_goal); diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index f9e1db567c2ba..a51eefd908cc7 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -5,13 +5,14 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::canonical::{self, Canonical}; use rustc_infer::traits::query::OutlivesBound; +use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::{ compute_implied_outlives_bounds_compat_inner, compute_implied_outlives_bounds_inner, }; -use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution}; +use rustc_trait_selection::traits::query::{CanonicalImpliedOutlivesBoundsGoal, NoSolution}; pub(crate) fn provide(p: &mut Providers) { *p = Providers { implied_outlives_bounds_compat, ..*p }; @@ -20,26 +21,26 @@ pub(crate) fn provide(p: &mut Providers) { fn implied_outlives_bounds_compat<'tcx>( tcx: TyCtxt<'tcx>, - goal: CanonicalTyGoal<'tcx>, + goal: CanonicalImpliedOutlivesBoundsGoal<'tcx>, ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, NoSolution, > { tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { - let (param_env, ty) = key.into_parts(); + let (param_env, ImpliedOutlivesBounds { ty }) = key.into_parts(); compute_implied_outlives_bounds_compat_inner(ocx, param_env, ty) }) } fn implied_outlives_bounds<'tcx>( tcx: TyCtxt<'tcx>, - goal: CanonicalTyGoal<'tcx>, + goal: CanonicalImpliedOutlivesBoundsGoal<'tcx>, ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, NoSolution, > { tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { - let (param_env, ty) = key.into_parts(); + let (param_env, ImpliedOutlivesBounds { ty }) = key.into_parts(); compute_implied_outlives_bounds_inner(ocx, param_env, ty) }) } diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index c982cd66bca12..71088a598bb93 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -1,7 +1,7 @@ use std::fmt; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::infer::canonical::{Canonical, QueryResponse}; +use rustc_infer::infer::canonical::{Canonical, CanonicalQueryInput, QueryResponse}; use rustc_middle::query::Providers; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{Clause, FnSig, ParamEnvAnd, PolyFnSig, Ty, TyCtxt, TypeFoldable}; @@ -28,7 +28,7 @@ pub(crate) fn provide(p: &mut Providers) { fn type_op_ascribe_user_type<'tcx>( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, AscribeUserType<'tcx>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, AscribeUserType<'tcx>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |ocx, key| { type_op_ascribe_user_type_with_span(ocx, key, None) @@ -51,35 +51,35 @@ where fn type_op_normalize_ty<'tcx>( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, type_op_normalize) } fn type_op_normalize_clause<'tcx>( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, Clause<'tcx>>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, type_op_normalize) } fn type_op_normalize_fn_sig<'tcx>( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, FnSig<'tcx>>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, type_op_normalize) } fn type_op_normalize_poly_fn_sig<'tcx>( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, PolyFnSig<'tcx>>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, type_op_normalize) } fn type_op_prove_predicate<'tcx>( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, ProvePredicate<'tcx>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, ProvePredicate<'tcx>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |ocx, key| { type_op_prove_predicate_with_cause(ocx, key, ObligationCause::dummy()); diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index c1d0b704ab287..f7651e49cdd15 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -85,7 +85,6 @@ mod rustc { use rustc_macros::TypeVisitable; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{Const, ParamEnv, Ty, TyCtxt, ValTree}; - use rustc_span::DUMMY_SP; use super::*; @@ -134,13 +133,8 @@ mod rustc { use rustc_middle::ty::ScalarInt; use rustc_span::symbol::sym; - let Ok((ty, cv)) = c.eval(tcx, param_env, DUMMY_SP) else { - return Some(Self { - alignment: true, - lifetimes: true, - safety: true, - validity: true, - }); + let Some((cv, ty)) = c.try_to_valtree() else { + return None; }; let adt_def = ty.ty_adt_def()?; diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 7354ea5fb6a22..13691204c96a1 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -728,6 +728,49 @@ fn fn_abi_adjust_for_abi<'tcx>( }; } + if arg_idx.is_none() && arg.layout.size > Pointer(AddressSpace::DATA).size(cx) * 2 { + // Return values larger than 2 registers using a return area + // pointer. LLVM and Cranelift disagree about how to return + // values that don't fit in the registers designated for return + // values. LLVM will force the entire return value to be passed + // by return area pointer, while Cranelift will look at each IR level + // return value independently and decide to pass it in a + // register or not, which would result in the return value + // being passed partially in registers and partially through a + // return area pointer. + // + // While Cranelift may need to be fixed as the LLVM behavior is + // generally more correct with respect to the surface language, + // forcing this behavior in rustc itself makes it easier for + // other backends to conform to the Rust ABI and for the C ABI + // rustc already handles this behavior anyway. + // + // In addition LLVM's decision to pass the return value in + // registers or using a return area pointer depends on how + // exactly the return type is lowered to an LLVM IR type. For + // example `Option` can be lowered as `{ i128, i128 }` + // in which case the x86_64 backend would use a return area + // pointer, or it could be passed as `{ i32, i128 }` in which + // case the x86_64 backend would pass it in registers by taking + // advantage of an LLVM ABI extension that allows using 3 + // registers for the x86_64 sysv call conv rather than the + // officially specified 2 registers. + // + // FIXME: Technically we should look at the amount of available + // return registers rather than guessing that there are 2 + // registers for return values. In practice only a couple of + // architectures have less than 2 return registers. None of + // which supported by Cranelift. + // + // NOTE: This adjustment is only necessary for the Rust ABI as + // for other ABI's the calling convention implementations in + // rustc_target already ensure any return value which doesn't + // fit in the available amount of return registers is passed in + // the right way for the current target. + arg.make_indirect(); + return; + } + match arg.layout.abi { Abi::Aggregate { .. } => {} diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index afdfa2e80c1f6..e755e90aa65db 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -30,7 +30,8 @@ use {rustc_abi as abi, rustc_hir as hir}; use crate::errors::{ MultipleArrayFieldsSimdType, NonPrimitiveSimdType, OversizedSimdType, ZeroLengthSimdType, }; -use crate::layout_sanity_check::sanity_check_layout; + +mod invariant; pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { layout_of, ..*providers }; @@ -79,7 +80,7 @@ fn layout_of<'tcx>( record_layout_for_printing(&cx, layout); } - sanity_check_layout(&cx, &layout); + invariant::partially_check_layout(&cx, &layout); Ok(layout) } @@ -115,6 +116,11 @@ fn map_error<'tcx>( cx.tcx().dcx().delayed_bug(format!("computed layout of empty union: {ty:?}")); LayoutError::Unknown(ty) } + LayoutCalculatorError::ReprConflict => { + // packed enums are the only known trigger of this, but others might arise + cx.tcx().dcx().delayed_bug(format!("computed impossible repr (packed enum?): {ty:?}")); + LayoutError::Unknown(ty) + } }; error(cx, err) } @@ -170,12 +176,12 @@ fn layout_of_uncached<'tcx>( if let Abi::Scalar(scalar) | Abi::ScalarPair(scalar, _) = &mut layout.abi { if let Some(start) = start { scalar.valid_range_mut().start = start - .try_eval_bits(tcx, param_env) + .try_to_bits(tcx, param_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; } if let Some(end) = end { let mut end = end - .try_eval_bits(tcx, param_env) + .try_to_bits(tcx, param_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; if !include_end { end = end.wrapping_sub(1); @@ -315,7 +321,7 @@ fn layout_of_uncached<'tcx>( } let count = count - .try_eval_target_usize(tcx, param_env) + .try_to_target_usize(tcx) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; let element = cx.layout_of(element)?; let size = element diff --git a/compiler/rustc_ty_utils/src/layout_sanity_check.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs similarity index 99% rename from compiler/rustc_ty_utils/src/layout_sanity_check.rs rename to compiler/rustc_ty_utils/src/layout/invariant.rs index be0a7c5ee890a..6cf114b74c13d 100644 --- a/compiler/rustc_ty_utils/src/layout_sanity_check.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -5,7 +5,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout}; use rustc_target::abi::*; /// Enforce some basic invariants on layouts. -pub(super) fn sanity_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { +pub(super) fn partially_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { let tcx = cx.tcx(); // Type-level uninhabitedness should always imply ABI uninhabitedness. diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index dc5303317a8a0..8be1611bb9ac2 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -29,7 +29,6 @@ mod errors; mod implied_bounds; mod instance; mod layout; -mod layout_sanity_check; mod needs_drop; mod opaque_types; mod representability; diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index d609e5add14b9..07cb8b037ecf7 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -10,6 +10,18 @@ use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Gen use crate::inherent::*; use crate::{self as ty, Interner, UniverseIndex}; +#[derive_where(Clone; I: Interner, V: Clone)] +#[derive_where(Hash; I: Interner, V: Hash)] +#[derive_where(PartialEq; I: Interner, V: PartialEq)] +#[derive_where(Eq; I: Interner, V: Eq)] +#[derive_where(Debug; I: Interner, V: fmt::Debug)] +#[derive_where(Copy; I: Interner, V: Copy)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +pub struct CanonicalQueryInput { + pub canonical: Canonical, + pub defining_opaque_types: I::DefiningOpaqueTypes, +} + /// A "canonicalized" type `V` is one where all free inference /// variables have been rewritten to "canonical vars". These are /// numbered starting from 0 in order of first appearance. @@ -24,8 +36,6 @@ use crate::{self as ty, Interner, UniverseIndex}; pub struct Canonical { pub value: V, pub max_universe: UniverseIndex, - // FIXME(lcnr, oli-obk): try moving this into the query inputs instead - pub defining_opaque_types: I::DefiningOpaqueTypes, pub variables: I::CanonicalVars, } @@ -54,27 +64,17 @@ impl Canonical { /// let b: Canonical)> = a.unchecked_map(|v| (v, ty)); /// ``` pub fn unchecked_map(self, map_op: impl FnOnce(V) -> W) -> Canonical { - let Canonical { defining_opaque_types, max_universe, variables, value } = self; - Canonical { defining_opaque_types, max_universe, variables, value: map_op(value) } - } - - /// Allows you to map the `value` of a canonical while keeping the same set of - /// bound variables. - /// - /// **WARNING:** This function is very easy to mis-use, hence the name! See - /// the comment of [Canonical::unchecked_map] for more details. - pub fn unchecked_rebind(self, value: W) -> Canonical { - let Canonical { defining_opaque_types, max_universe, variables, value: _ } = self; - Canonical { defining_opaque_types, max_universe, variables, value } + let Canonical { max_universe, variables, value } = self; + Canonical { max_universe, variables, value: map_op(value) } } } impl fmt::Display for Canonical { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { value, max_universe, variables, defining_opaque_types } = self; + let Self { value, max_universe, variables } = self; write!( f, - "Canonical {{ value: {value}, max_universe: {max_universe:?}, variables: {variables:?}, defining_opaque_types: {defining_opaque_types:?} }}", + "Canonical {{ value: {value}, max_universe: {max_universe:?}, variables: {variables:?} }}", ) } } diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 8146181df6c17..b613505f826cf 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -726,9 +726,9 @@ pub struct CoercePredicate { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))] pub enum BoundConstness { - /// `Type: Trait` - NotConst, /// `Type: const Trait` + /// + /// A bound is required to be unconditionally const, even in a runtime function. Const, /// `Type: ~const Trait` /// @@ -739,7 +739,6 @@ pub enum BoundConstness { impl BoundConstness { pub fn as_str(self) -> &'static str { match self { - Self::NotConst => "", Self::Const => "const", Self::ConstIfConst => "~const", } @@ -749,7 +748,6 @@ impl BoundConstness { impl fmt::Display for BoundConstness { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::NotConst => f.write_str("normal"), Self::Const => f.write_str("const"), Self::ConstIfConst => f.write_str("~const"), } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index f4fb03562de9c..3fd2bb61ba583 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -714,7 +714,7 @@ impl, X: Cx> SearchGraph { // current goal is already part of the same cycle. This check could be // improved but seems to be good enough for now. let last = self.stack.raw.last().unwrap(); - if !last.heads.opt_lowest_cycle_head().is_some_and(|lowest| lowest <= head) { + if last.heads.opt_lowest_cycle_head().is_none_or(|lowest| lowest > head) { continue; } } diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index f02c7a3207193..b3f8390bbf062 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -71,7 +71,8 @@ pub enum SolverMode { Coherence, } -pub type CanonicalInput::Predicate> = Canonical>; +pub type CanonicalInput::Predicate> = + ty::CanonicalQueryInput>; pub type CanonicalResponse = Canonical>; /// The result of evaluating a canonical query. /// diff --git a/config.example.toml b/config.example.toml index 4b591b949b36d..168ac353cff75 100644 --- a/config.example.toml +++ b/config.example.toml @@ -414,6 +414,11 @@ # Specify the location of the Android NDK. Used when targeting Android. #android-ndk = "/path/to/android-ndk-r26d" +# Number of parallel jobs to be used for building and testing. If set to `0` or +# omitted, it will be automatically determined. This is the `-j`/`--jobs` flag +# passed to cargo invocations. +#jobs = 0 + # ============================================================================= # General install configuration options # ============================================================================= diff --git a/library/Cargo.lock b/library/Cargo.lock index 59b76d8d4427d..db60a48408157 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.133" +version = "0.1.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab10bf45b2ed1b4f4c25401527a61684142c042b3c86ace7da7ea6881e26741b" +checksum = "2f743e6f7410a78c261505c729f389583de40eec62332cc8cdf2c8b9bf73049a" dependencies = [ "cc", "rustc-std-workspace-core", @@ -124,9 +124,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.30.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e1d97fbe9722ba9bbd0c97051c2956e726562b61f86a25a4360398a40edfc9" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", @@ -158,9 +158,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.159" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" dependencies = [ "rustc-std-workspace-core", ] @@ -340,7 +340,6 @@ dependencies = [ "object", "panic_abort", "panic_unwind", - "profiler_builtins", "r-efi", "r-efi-alloc", "rand", @@ -368,6 +367,7 @@ name = "sysroot" version = "0.0.0" dependencies = [ "proc_macro", + "profiler_builtins", "std", "test", ] @@ -406,12 +406,12 @@ dependencies = [ [[package]] name = "unwinding" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc55842d0db6329a669d55a623c674b02d677b16bfb2d24857d4089d41eba882" +checksum = "637d511437df708cee34bdec7ba2f1548d256b7acf3ff20e0a1c559f9bf3a987" dependencies = [ "compiler_builtins", - "gimli 0.30.0", + "gimli 0.31.1", "rustc-std-workspace-core", ] diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 259a3ed2bebde..6301ade2775dc 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "0.1.133", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "0.1.134", features = ['rustc-dep-of-std'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } diff --git a/library/alloc/benches/lib.rs b/library/alloc/benches/lib.rs index ae9608ec7bd5c..c1907361f93e1 100644 --- a/library/alloc/benches/lib.rs +++ b/library/alloc/benches/lib.rs @@ -4,7 +4,8 @@ #![feature(iter_next_chunk)] #![feature(repr_simd)] #![feature(slice_partition_dedup)] -#![feature(strict_provenance)] +#![cfg_attr(bootstrap, feature(strict_provenance))] +#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![feature(test)] #![deny(fuzzy_provenance_casts)] diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 0cd410c0fb7c1..ca0ea1ec8b2ba 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -1082,7 +1082,7 @@ impl LinkedList { /// Retains only the elements specified by the predicate. /// - /// In other words, remove all elements `e` for which `f(&e)` returns false. + /// In other words, remove all elements `e` for which `f(&mut e)` returns false. /// This method operates in place, visiting each element exactly once in the /// original order, and preserves the order of the retained elements. /// diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 54739c50d1d84..cf51a84bb6f24 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -2122,7 +2122,7 @@ impl VecDeque { /// Retains only the elements specified by the predicate. /// - /// In other words, remove all elements `e` for which `f(&e)` returns false. + /// In other words, remove all elements `e` for which `f(&mut e)` returns false. /// This method operates in place, visiting each element exactly once in the /// original order, and preserves the order of the retained elements. /// diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 0a4a5160d827a..50a5f3c5b1e04 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -112,7 +112,6 @@ #![feature(const_eval_select)] #![feature(const_heap)] #![feature(const_maybe_uninit_write)] -#![feature(const_pin)] #![feature(const_size_of_val)] #![feature(const_vec_string_slice)] #![feature(core_intrinsics)] @@ -148,7 +147,6 @@ #![feature(slice_range)] #![feature(std_internals)] #![feature(str_internals)] -#![feature(strict_provenance)] #![feature(trusted_fused)] #![feature(trusted_len)] #![feature(trusted_random_access)] @@ -163,6 +161,8 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(strict_provenance))] +#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![cfg_attr(not(test), feature(coroutine_trait))] #![cfg_attr(test, feature(panic_update_hook))] #![cfg_attr(test, feature(test))] diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 42501f9c3159a..52ceb8b45f91c 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -20,7 +20,7 @@ pub use core::str::SplitInclusive; pub use core::str::SplitWhitespace; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::pattern; -use core::str::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; +use core::str::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher, Utf8Pattern}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::{Bytes, CharIndices, Chars, from_utf8, from_utf8_mut}; #[stable(feature = "str_escape", since = "1.34.0")] @@ -269,6 +269,18 @@ impl str { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn replace(&self, from: P, to: &str) -> String { + // Fast path for ASCII to ASCII case. + + if let Some(from_byte) = match from.as_utf8_pattern() { + Some(Utf8Pattern::StringPattern([from_byte])) => Some(*from_byte), + Some(Utf8Pattern::CharPattern(c)) => c.as_ascii().map(|ascii_char| ascii_char.to_u8()), + _ => None, + } { + if let [to_byte] = to.as_bytes() { + return unsafe { replace_ascii(self.as_bytes(), from_byte, *to_byte) }; + } + } + let mut result = String::new(); let mut last_end = 0; for (start, part) in self.match_indices(from) { @@ -686,3 +698,14 @@ pub fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, &str) { (ascii_string, rest) } } +#[inline] +#[cfg(not(test))] +#[cfg(not(no_global_oom_handling))] +#[allow(dead_code)] +/// Faster implementation of string replacement for ASCII to ASCII cases. +/// Should produce fast vectorized code. +unsafe fn replace_ascii(utf8_bytes: &[u8], from: u8, to: u8) -> String { + let result: Vec = utf8_bytes.iter().map(|b| if *b == from { to } else { *b }).collect(); + // SAFETY: We replaced ascii with ascii on valid utf8 strings. + unsafe { String::from_utf8_unchecked(result) } +} diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 82dbf0306083c..b042720933b6d 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -53,7 +53,7 @@ use core::ops::AddAssign; #[cfg(not(no_global_oom_handling))] use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{self, Range, RangeBounds}; -use core::str::pattern::Pattern; +use core::str::pattern::{Pattern, Utf8Pattern}; use core::{fmt, hash, ptr, slice}; #[cfg(not(no_global_oom_handling))] @@ -2436,6 +2436,11 @@ impl<'b> Pattern for &'b String { { self[..].strip_suffix_of(haystack) } + + #[inline] + fn as_utf8_pattern(&self) -> Option> { + Some(Utf8Pattern::StringPattern(self.as_bytes())) + } } macro_rules! impl_eq { diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 301126b5d4d34..699a8e6776e6d 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -32,7 +32,8 @@ #![feature(panic_update_hook)] #![feature(pointer_is_aligned_to)] #![feature(thin_box)] -#![feature(strict_provenance)] +#![cfg_attr(bootstrap, feature(strict_provenance))] +#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![feature(drain_keep_rest)] #![feature(local_waker)] #![feature(vec_pop_if)] diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 69ad4f415196c..97e727633c5fa 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2794,7 +2794,6 @@ where /// #![feature(is_val_statically_known)] /// #![feature(core_intrinsics)] /// # #![allow(internal_features)] -/// #![feature(strict_provenance)] /// use std::intrinsics::is_val_statically_known; /// /// fn foo(x: &i32) -> bool { @@ -3138,7 +3137,7 @@ pub const fn ptr_metadata + ?Sized, M>(_ptr: *cons /// [violate memory safety][read-ownership]. /// /// Note that even if the effectively copied size (`count * size_of::()`) is -/// `0`, the pointers must be non-null and properly aligned. +/// `0`, the pointers must be properly aligned. /// /// [`read`]: crate::ptr::read /// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value @@ -3261,7 +3260,7 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us /// [violate memory safety][read-ownership]. /// /// Note that even if the effectively copied size (`count * size_of::()`) is -/// `0`, the pointers must be non-null and properly aligned. +/// `0`, the pointers must be properly aligned. /// /// [`read`]: crate::ptr::read /// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value @@ -3342,7 +3341,7 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { /// * `dst` must be properly aligned. /// /// Note that even if the effectively copied size (`count * size_of::()`) is -/// `0`, the pointer must be non-null and properly aligned. +/// `0`, the pointer must be properly aligned. /// /// Additionally, note that changing `*dst` in this way can easily lead to undefined behavior (UB) /// later if the written bytes are not a valid representation of some `T`. For instance, the diff --git a/library/core/src/iter/sources/repeat_n.rs b/library/core/src/iter/sources/repeat_n.rs index 7e162ff387baf..cc089c617c0e3 100644 --- a/library/core/src/iter/sources/repeat_n.rs +++ b/library/core/src/iter/sources/repeat_n.rs @@ -8,9 +8,7 @@ use crate::num::NonZero; /// The `repeat_n()` function repeats a single value exactly `n` times. /// /// This is very similar to using [`repeat()`] with [`Iterator::take()`], -/// but there are two differences: -/// - `repeat_n()` can return the original value, rather than always cloning. -/// - `repeat_n()` produces an [`ExactSizeIterator`]. +/// but `repeat_n()` can return the original value, rather than always cloning. /// /// [`repeat()`]: crate::iter::repeat /// diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 6ed9ccaa694b4..168775667651d 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -129,7 +129,7 @@ #![feature(const_nonnull_new)] #![feature(const_num_midpoint)] #![feature(const_option_ext)] -#![feature(const_pin)] +#![feature(const_pin_2)] #![feature(const_pointer_is_aligned)] #![feature(const_ptr_is_null)] #![feature(const_ptr_sub_ptr)] @@ -163,7 +163,6 @@ #![feature(str_internals)] #![feature(str_split_inclusive_remainder)] #![feature(str_split_remainder)] -#![feature(strict_provenance)] #![feature(ub_checks)] #![feature(unchecked_neg)] #![feature(unchecked_shifts)] @@ -174,6 +173,8 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(strict_provenance))] +#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 6a4f2af10efc6..771c2d31b60e0 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1550,7 +1550,7 @@ pub(crate) mod builtin { /// MODE is any of Forward, Reverse, ForwardFirst, ReverseFirst. /// INPUT_ACTIVITIES consists of one valid activity for each input parameter. /// OUTPUT_ACTIVITY must not be set if we implicitely return nothing (or explicitely return - /// `-> ()`. Otherwise it must be set to one of the allowed activities. + /// `-> ()`). Otherwise it must be set to one of the allowed activities. #[unstable(feature = "autodiff", issue = "124509")] #[allow_internal_unstable(rustc_attrs)] #[rustc_builtin_macro] diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs index 64214eae377dd..dce3514a1595b 100644 --- a/library/core/src/ops/index_range.rs +++ b/library/core/src/ops/index_range.rs @@ -45,7 +45,8 @@ impl IndexRange { #[inline] pub const fn len(&self) -> usize { // SAFETY: By invariant, this cannot wrap - unsafe { self.end.unchecked_sub(self.start) } + // Using the intrinsic because a UB check here impedes LLVM optimization. (#131563) + unsafe { crate::intrinsics::unchecked_sub(self.end, self.start) } } /// # Safety @@ -82,7 +83,8 @@ impl IndexRange { let mid = if n <= self.len() { // SAFETY: We just checked that this will be between start and end, // and thus the addition cannot overflow. - unsafe { self.start.unchecked_add(n) } + // Using the intrinsic avoids a superfluous UB check. + unsafe { crate::intrinsics::unchecked_add(self.start, n) } } else { self.end }; @@ -100,8 +102,9 @@ impl IndexRange { pub fn take_suffix(&mut self, n: usize) -> Self { let mid = if n <= self.len() { // SAFETY: We just checked that this will be between start and end, - // and thus the addition cannot overflow. - unsafe { self.end.unchecked_sub(n) } + // and thus the subtraction cannot overflow. + // Using the intrinsic avoids a superfluous UB check. + unsafe { crate::intrinsics::unchecked_sub(self.end, n) } } else { self.start }; diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index fac789dbd99fb..5d5733d38fca1 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1186,7 +1186,7 @@ impl> Pin { /// let mut pinned: Pin<&mut u8> = Pin::new(&mut val); /// ``` #[inline(always)] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin", since = "1.33.0")] pub const fn new(pointer: Ptr) -> Pin { // SAFETY: the value pointed to is `Unpin`, and so has no requirements @@ -1214,7 +1214,7 @@ impl> Pin { /// assert_eq!(*r, 5); /// ``` #[inline(always)] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_unstable(feature = "const_pin_2", issue = "76654")] #[stable(feature = "pin_into_inner", since = "1.39.0")] pub const fn into_inner(pin: Pin) -> Ptr { pin.__pointer @@ -1351,7 +1351,7 @@ impl Pin { /// [`pin` module docs]: self #[lang = "new_unchecked"] #[inline(always)] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin", since = "1.33.0")] pub const unsafe fn new_unchecked(pointer: Ptr) -> Pin { Pin { __pointer: pointer } @@ -1503,7 +1503,7 @@ impl Pin { /// If the underlying data is [`Unpin`], [`Pin::into_inner`] should be used /// instead. #[inline(always)] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_unstable(feature = "const_pin_2", issue = "76654")] #[stable(feature = "pin_into_inner", since = "1.39.0")] pub const unsafe fn into_inner_unchecked(pin: Pin) -> Ptr { pin.__pointer @@ -1559,7 +1559,7 @@ impl<'a, T: ?Sized> Pin<&'a T> { /// ["pinning projections"]: self#projections-and-structural-pinning #[inline(always)] #[must_use] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin", since = "1.33.0")] pub const fn get_ref(self) -> &'a T { self.__pointer @@ -1570,7 +1570,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { /// Converts this `Pin<&mut T>` into a `Pin<&T>` with the same lifetime. #[inline(always)] #[must_use = "`self` will be dropped if the result is not used"] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin", since = "1.33.0")] pub const fn into_ref(self) -> Pin<&'a T> { Pin { __pointer: self.__pointer } @@ -1588,7 +1588,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { #[inline(always)] #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "pin", since = "1.33.0")] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] pub const fn get_mut(self) -> &'a mut T where T: Unpin, @@ -1609,7 +1609,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { #[inline(always)] #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "pin", since = "1.33.0")] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn get_unchecked_mut(self) -> &'a mut T { self.__pointer } @@ -1652,7 +1652,7 @@ impl Pin<&'static T> { /// This is safe because `T` is borrowed immutably for the `'static` lifetime, which /// never ends. #[stable(feature = "pin_static_ref", since = "1.61.0")] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] pub const fn static_ref(r: &'static T) -> Pin<&'static T> { // SAFETY: The 'static borrow guarantees the data will not be // moved/invalidated until it gets dropped (which is never). @@ -1666,7 +1666,7 @@ impl Pin<&'static mut T> { /// This is safe because `T` is borrowed for the `'static` lifetime, which /// never ends. #[stable(feature = "pin_static_ref", since = "1.61.0")] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] pub const fn static_mut(r: &'static mut T) -> Pin<&'static mut T> { // SAFETY: The 'static borrow guarantees the data will not be // moved/invalidated until it gets dropped (which is never). diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 89936dc12ac36..95fa6c9c9507c 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -505,9 +505,11 @@ impl () {} /// /// *[See also the `std::ptr` module](ptr).* /// -/// Working with raw pointers in Rust is uncommon, typically limited to a few patterns. -/// Raw pointers can be unaligned or [`null`]. However, when a raw pointer is -/// dereferenced (using the `*` operator), it must be non-null and aligned. +/// Working with raw pointers in Rust is uncommon, typically limited to a few patterns. Raw pointers +/// can be out-of-bounds, unaligned, or [`null`]. However, when loading from or storing to a raw +/// pointer, it must be [valid] for the given access and aligned. When using a field expression, +/// tuple index expression, or array/slice index expression on a raw pointer, it follows the rules +/// of [in-bounds pointer arithmetic][`offset`]. /// /// Storing through a raw pointer using `*ptr = data` calls `drop` on the old value, so /// [`write`] must be used if the type has drop glue and memory is not already @@ -613,6 +615,7 @@ impl () {} /// [`offset`]: pointer::offset /// [`into_raw`]: ../std/boxed/struct.Box.html#method.into_raw /// [`write`]: ptr::write +/// [valid]: ptr#safety #[stable(feature = "rust1", since = "1.0.0")] mod prim_pointer {} @@ -1781,9 +1784,11 @@ mod prim_ref {} /// unique field that doesn't have size 0 and alignment 1 (if there is such a field). /// - `i32` is ABI-compatible with `NonZero`, and similar for all other integer types. /// - If `T` is guaranteed to be subject to the [null pointer -/// optimization](option/index.html#representation), then `T` and `Option` are ABI-compatible. -/// Furthermore, if `U` satisfies the requirements [outlined here](result/index.html#representation), -/// then `T` and `Result` and `Result` are all ABI-compatible. +/// optimization](option/index.html#representation), and `E` is an enum satisfying the following +/// requirements, then `T` and `E` are ABI-compatible. Such an enum `E` is called "option-like". +/// - The enum `E` has exactly two variants. +/// - One variant has exactly one field, of type `T`. +/// - All fields of the other variant are zero-sized with 1-byte alignment. /// /// Furthermore, ABI compatibility satisfies the following general properties: /// diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 72f4bc2c9dae8..9ee0fb5948e16 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -137,10 +137,11 @@ impl *const T { /// Gets the "address" portion of the pointer. /// - /// This is similar to `self as usize`, which semantically discards *provenance* and - /// *address-space* information. However, unlike `self as usize`, casting the returned address - /// back to a pointer yields a [pointer without provenance][without_provenance], which is undefined behavior to dereference. To - /// properly restore the lost information and obtain a dereferenceable pointer, use + /// This is similar to `self as usize`, except that the [provenance][crate::ptr#provenance] of + /// the pointer is discarded and not [exposed][crate::ptr#exposed-provenance]. This means that + /// casting the returned address back to a pointer yields a [pointer without + /// provenance][without_provenance], which is undefined behavior to dereference. To properly + /// restore the lost information and obtain a dereferenceable pointer, use /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr]. /// /// If using those APIs is not possible because there is no way to preserve a pointer with the @@ -155,90 +156,81 @@ impl *const T { /// perform a change of representation to produce a value containing only the address /// portion of the pointer. What that means is up to the platform to define. /// - /// This API and its claimed semantics are part of the Strict Provenance experiment, and as such - /// might change in the future (including possibly weakening this so it becomes wholly - /// equivalent to `self as usize`). See the [module documentation][crate::ptr] for details. + /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline(always)] - #[unstable(feature = "strict_provenance", issue = "95228")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn addr(self) -> usize { - // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + // A pointer-to-integer transmute currently has exactly the right semantics: it returns the + // address without exposing the provenance. Note that this is *not* a stable guarantee about + // transmute semantics, it relies on sysroot crates having special status. // SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the // provenance). unsafe { mem::transmute(self.cast::<()>()) } } - /// Exposes the "provenance" part of the pointer for future use in - /// [`with_exposed_provenance`][] and returns the "address" portion. + /// Exposes the ["provenance"][crate::ptr#provenance] part of the pointer for future use in + /// [`with_exposed_provenance`] and returns the "address" portion. /// - /// This is equivalent to `self as usize`, which semantically discards *provenance* and - /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit - /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can - /// later call [`with_exposed_provenance`][] to reconstitute the original pointer including its - /// provenance. (Reconstructing address space information, if required, is your responsibility.) + /// This is equivalent to `self as usize`, which semantically discards provenance information. + /// Furthermore, this (like the `as` cast) has the implicit side-effect of marking the + /// provenance as 'exposed', so on platforms that support it you can later call + /// [`with_exposed_provenance`] to reconstitute the original pointer including its provenance. /// - /// Using this method means that code is *not* following [Strict - /// Provenance][super#strict-provenance] rules. Supporting - /// [`with_exposed_provenance`][] complicates specification and reasoning and may not be supported by - /// tools that help you to stay conformant with the Rust memory model, so it is recommended to - /// use [`addr`][pointer::addr] wherever possible. + /// Due to its inherent ambiguity, [`with_exposed_provenance`] may not be supported by tools + /// that help you to stay conformant with the Rust memory model. It is recommended to use + /// [Strict Provenance][crate::ptr#strict-provenance] APIs such as [`with_addr`][pointer::with_addr] + /// wherever possible, in which case [`addr`][pointer::addr] should be used instead of `expose_provenance`. /// /// On most platforms this will produce a value with the same bytes as the original pointer, /// because all the bytes are dedicated to describing the address. Platforms which need to store /// additional information in the pointer may not support this operation, since the 'expose' - /// side-effect which is required for [`with_exposed_provenance`][] to work is typically not + /// side-effect which is required for [`with_exposed_provenance`] to work is typically not /// available. /// - /// It is unclear whether this method can be given a satisfying unambiguous specification. This - /// API and its claimed semantics are part of [Exposed Provenance][super#exposed-provenance]. + /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. /// /// [`with_exposed_provenance`]: with_exposed_provenance #[must_use] #[inline(always)] - #[unstable(feature = "exposed_provenance", issue = "95228")] + #[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn expose_provenance(self) -> usize { - // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. self.cast::<()>() as usize } - /// Creates a new pointer with the given address. + /// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of + /// `self`. /// - /// This performs the same operation as an `addr as ptr` cast, but copies - /// the *address-space* and *provenance* of `self` to the new pointer. - /// This allows us to dynamically preserve and propagate this important - /// information in a way that is otherwise impossible with a unary cast. + /// This is similar to a `addr as *const T` cast, but copies + /// the *provenance* of `self` to the new pointer. + /// This avoids the inherent ambiguity of the unary cast. /// /// This is equivalent to using [`wrapping_offset`][pointer::wrapping_offset] to offset /// `self` to the given address, and therefore has all the same capabilities and restrictions. /// - /// This API and its claimed semantics are part of the Strict Provenance experiment, - /// see the [module documentation][crate::ptr] for details. + /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[unstable(feature = "strict_provenance", issue = "95228")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn with_addr(self, addr: usize) -> Self { - // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. - // - // In the mean-time, this operation is defined to be "as if" it was - // a wrapping_offset, so we can emulate it as such. This should properly - // restore pointer provenance even under today's compiler. + // This should probably be an intrinsic to avoid doing any sort of arithmetic, but + // meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's + // provenance. let self_addr = self.addr() as isize; let dest_addr = addr as isize; let offset = dest_addr.wrapping_sub(self_addr); - - // This is the canonical desugaring of this operation self.wrapping_byte_offset(offset) } - /// Creates a new pointer by mapping `self`'s address to a new one. + /// Creates a new pointer by mapping `self`'s address to a new one, preserving the + /// [provenance][crate::ptr#provenance] of `self`. /// /// This is a convenience for [`with_addr`][pointer::with_addr], see that method for details. /// - /// This API and its claimed semantics are part of the Strict Provenance experiment, - /// see the [module documentation][crate::ptr] for details. + /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[unstable(feature = "strict_provenance", issue = "95228")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self { self.with_addr(f(self.addr())) } @@ -379,7 +371,7 @@ impl *const T { /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without /// "wrapping around"), must fit in an `isize`. /// - /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in /// bounds of that allocated object. In particular, this range must not "wrap around" the edge /// of the address space. @@ -560,7 +552,7 @@ impl *const T { /// ## Examples /// /// ``` - /// #![feature(ptr_mask, strict_provenance)] + /// #![feature(ptr_mask)] /// let v = 17_u32; /// let ptr: *const u32 = &v; /// @@ -611,7 +603,7 @@ impl *const T { /// * `self` and `origin` must either /// /// * point to the same address, or - /// * both be *derived from* a pointer to the same [allocated object], and the memory range between + /// * both be [derived from][crate::ptr#provenance] a pointer to the same [allocated object], and the memory range between /// the two pointers must be in bounds of that object. (See below for an example.) /// /// * The distance between the pointers, in bytes, must be an exact multiple @@ -871,7 +863,7 @@ impl *const T { /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without /// "wrapping around"), must fit in an `isize`. /// - /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in /// bounds of that allocated object. In particular, this range must not "wrap around" the edge /// of the address space. @@ -978,7 +970,7 @@ impl *const T { /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without /// "wrapping around"), must fit in an `isize`. /// - /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in /// bounds of that allocated object. In particular, this range must not "wrap around" the edge /// of the address space. diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 09ff7f8cab173..f769b5158771c 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -18,10 +18,11 @@ //! * For operations of [size zero][zst], *every* pointer is valid, including the [null] pointer. //! The following points are only concerned with non-zero-sized accesses. //! * A [null] pointer is *never* valid. -//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer -//! be *dereferenceable*: the memory range of the given size starting at the pointer must all be -//! within the bounds of a single allocated object. Note that in Rust, -//! every (stack-allocated) variable is considered a separate allocated object. +//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer be +//! *dereferenceable*. The [provenance] of the pointer is used to determine which [allocated +//! object] it is derived from; a pointer is dereferenceable if the memory range of the given size +//! starting at the pointer is entirely contained within the bounds of that allocated object. Note +//! that in Rust, every (stack-allocated) variable is considered a separate allocated object. //! * All accesses performed by functions in this module are *non-atomic* in the sense //! of [atomic operations] used to synchronize between threads. This means it is //! undefined behavior to perform two concurrent accesses to the same location from different @@ -130,123 +131,130 @@ //! //! [`null()`]: null //! -//! # Strict Provenance -//! -//! **The following text is non-normative, insufficiently formal, and is an extremely strict -//! interpretation of provenance. It's ok if your code doesn't strictly conform to it.** -//! -//! [Strict Provenance][] is an experimental set of APIs that help tools that try -//! to validate the memory-safety of your program's execution. Notably this includes [Miri][] -//! and [CHERI][], which can detect when you access out of bounds memory or otherwise violate -//! Rust's memory model. -//! -//! Provenance must exist in some form for any programming -//! language compiled for modern computer architectures, but specifying a model for provenance -//! in a way that is useful to both compilers and programmers is an ongoing challenge. -//! The [Strict Provenance][] experiment seeks to explore the question: *what if we just said you -//! couldn't do all the nasty operations that make provenance so messy?* -//! -//! What APIs would have to be removed? What APIs would have to be added? How much would code -//! have to change, and is it worse or better now? Would any patterns become truly inexpressible? -//! Could we carve out special exceptions for those patterns? Should we? -//! -//! A secondary goal of this project is to see if we can disambiguate the many functions of -//! pointer<->integer casts enough for the definition of `usize` to be loosened so that it -//! isn't *pointer*-sized but address-space/offset/allocation-sized (we'll probably continue -//! to conflate these notions). This would potentially make it possible to more efficiently -//! target platforms where pointers are larger than offsets, such as CHERI and maybe some -//! segmented architectures. -//! -//! ## Provenance -//! -//! **This section is *non-normative* and is part of the [Strict Provenance][] experiment.** +//! # Provenance //! //! Pointers are not *simply* an "integer" or "address". For instance, it's uncontroversial //! to say that a Use After Free is clearly Undefined Behaviour, even if you "get lucky" //! and the freed memory gets reallocated before your read/write (in fact this is the //! worst-case scenario, UAFs would be much less concerning if this didn't happen!). -//! To rationalize this claim, pointers need to somehow be *more* than just their addresses: -//! they must have provenance. +//! As another example, consider that [`wrapping_offset`] is documented to "remember" +//! the allocated object that the original pointer points to, even if it is offset far +//! outside the memory range occupied by that allocated object. +//! To rationalize claims like this, pointers need to somehow be *more* than just their addresses: +//! they must have **provenance**. //! -//! When an allocation is created, that allocation has a unique Original Pointer. For alloc -//! APIs this is literally the pointer the call returns, and for local variables and statics, -//! this is the name of the variable/static. This is mildly overloading the term "pointer" -//! for the sake of brevity/exposition. +//! A pointer value in Rust semantically contains the following information: +//! +//! * The **address** it points to, which can be represented by a `usize`. +//! * The **provenance** it has, defining the memory it has permission to access. Provenance can be +//! absent, in which case the pointer does not have permission to access any memory. //! -//! The Original Pointer for an allocation is guaranteed to have unique access to the entire -//! allocation and *only* that allocation. In this sense, an allocation can be thought of -//! as a "sandbox" that cannot be broken into or out of. *Provenance* is the permission -//! to access an allocation's sandbox and has both a *spatial* and *temporal* component: +//! The exact structure of provenance is not yet specified, but the permission defined by a +//! pointer's provenance have a *spatial* component, a *temporal* component, and a *mutability* +//! component: //! -//! * Spatial: A range of bytes that the pointer is allowed to access. -//! * Temporal: The lifetime (of the allocation) that access to these bytes is tied to. +//! * Spatial: The set of memory addresses that the pointer is allowed to access. +//! * Temporal: The timespan during which the pointer is allowed to access those memory addresses. +//! * Mutability: Whether the pointer may only access the memory for reads, or also access it for +//! writes. Note that this can interact with the other components, e.g. a pointer might permit +//! mutation only for a subset of addresses, or only for a subset of its maximal timespan. //! -//! Spatial provenance makes sure you don't go beyond your sandbox, while temporal provenance -//! makes sure that you can't "get lucky" after your permission to access some memory -//! has been revoked (either through deallocations or borrows expiring). +//! When an [allocated object] is created, it has a unique Original Pointer. For alloc +//! APIs this is literally the pointer the call returns, and for local variables and statics, +//! this is the name of the variable/static. (This is mildly overloading the term "pointer" +//! for the sake of brevity/exposition.) +//! +//! The Original Pointer for an allocated object has provenance that constrains the *spatial* +//! permissions of this pointer to the memory range of the allocation, and the *temporal* +//! permissions to the lifetime of the allocation. Provenance is implicitly inherited by all +//! pointers transitively derived from the Original Pointer through operations like [`offset`], +//! borrowing, and pointer casts. Some operations may *shrink* the permissions of the derived +//! provenance, limiting how much memory it can access or how long it's valid for (i.e. borrowing a +//! subfield and subslicing can shrink the spatial component of provenance, and all borrowing can +//! shrink the temporal component of provenance). However, no operation can ever *grow* the +//! permissions of the derived provenance: even if you "know" there is a larger allocation, you +//! can't derive a pointer with a larger provenance. Similarly, you cannot "recombine" two +//! contiguous provenances back into one (i.e. with a `fn merge(&[T], &[T]) -> &[T]`). +//! +//! A reference to a place always has provenance over at least the memory that place occupies. +//! A reference to a slice always has provenance over at least the range that slice describes. +//! Whether and when exactly the provenance of a reference gets "shrunk" to *exactly* fit +//! the memory it points to is not yet determined. +//! +//! A *shared* reference only ever has provenance that permits reading from memory, +//! and never permits writes, except inside [`UnsafeCell`]. +//! +//! Provenance can affect whether a program has undefined behavior: +//! +//! * It is undefined behavior to access memory through a pointer that does not have provenance over +//! that memory. Note that a pointer "at the end" of its provenance is not actually outside its +//! provenance, it just has 0 bytes it can load/store. Zero-sized accesses do not require any +//! provenance since they access an empty range of memory. +//! +//! * It is undefined behavior to [`offset`] a pointer across a memory range that is not contained +//! in the allocated object it is derived from, or to [`offset_from`] two pointers not derived +//! from the same allocated object. Provenance is used to say what exactly "derived from" even +//! means: the lineage of a pointer is traced back to the Original Pointer it descends from, and +//! that identifies the relevant allocated object. In particular, it's always UB to offset a +//! pointer derived from something that is now deallocated, except if the offset is 0. //! -//! Provenance is implicitly shared with all pointers transitively derived from -//! The Original Pointer through operations like [`offset`], borrowing, and pointer casts. -//! Some operations may *shrink* the derived provenance, limiting how much memory it can -//! access or how long it's valid for (i.e. borrowing a subfield and subslicing). +//! But it *is* still sound to: //! -//! Shrinking provenance cannot be undone: even if you "know" there is a larger allocation, you -//! can't derive a pointer with a larger provenance. Similarly, you cannot "recombine" -//! two contiguous provenances back into one (i.e. with a `fn merge(&[T], &[T]) -> &[T]`). +//! * Create a pointer without provenance from just an address (see [`ptr::dangling`]). Such a +//! pointer cannot be used for memory accesses (except for zero-sized accesses). This can still be +//! useful for sentinel values like `null` *or* to represent a tagged pointer that will never be +//! dereferenceable. In general, it is always sound for an integer to pretend to be a pointer "for +//! fun" as long as you don't use operations on it which require it to be valid (non-zero-sized +//! offset, read, write, etc). //! -//! A reference to a value always has provenance over exactly the memory that field occupies. -//! A reference to a slice always has provenance over exactly the range that slice describes. +//! * Forge an allocation of size zero at any sufficiently aligned non-null address. +//! i.e. the usual "ZSTs are fake, do what you want" rules apply. //! -//! If an allocation is deallocated, all pointers with provenance to that allocation become -//! invalidated, and effectively lose their provenance. +//! * [`wrapping_offset`] a pointer outside its provenance. This includes pointers +//! which have "no" provenance. In particular, this makes it sound to do pointer tagging tricks. //! -//! The strict provenance experiment is mostly only interested in exploring stricter *spatial* -//! provenance. In this sense it can be thought of as a subset of the more ambitious and -//! formal [Stacked Borrows][] research project, which is what tools like [Miri][] are based on. -//! In particular, Stacked Borrows is necessary to properly describe what borrows are allowed -//! to do and when they become invalidated. This necessarily involves much more complex -//! *temporal* reasoning than simply identifying allocations. Adjusting APIs and code -//! for the strict provenance experiment will also greatly help Stacked Borrows. +//! * Compare arbitrary pointers by address. Pointer comparison ignores provenance and addresses +//! *are* just integers, so there is always a coherent answer, even if the pointers are dangling +//! or from different provenances. Note that if you get "lucky" and notice that a pointer at the +//! end of one allocated object is the "same" address as the start of another allocated object, +//! anything you do with that fact is *probably* going to be gibberish. The scope of that +//! gibberish is kept under control by the fact that the two pointers *still* aren't allowed to +//! access the other's allocation (bytes), because they still have different provenance. //! +//! Note that the full definition of provenance in Rust is not decided yet, as this interacts +//! with the as-yet undecided [aliasing] rules. //! -//! ## Pointer Vs Addresses +//! ## Pointers Vs Integers //! -//! **This section is *non-normative* and is part of the [Strict Provenance][] experiment.** +//! From this discussion, it becomes very clear that a `usize` *cannot* accurately represent a pointer, +//! and converting from a pointer to a `usize` is generally an operation which *only* extracts the +//! address. Converting this address back into pointer requires somehow answering the question: +//! which provenance should the resulting pointer have? //! -//! One of the largest historical issues with trying to define provenance is that programmers -//! freely convert between pointers and integers. Once you allow for this, it generally becomes -//! impossible to accurately track and preserve provenance information, and you need to appeal -//! to very complex and unreliable heuristics. But of course, converting between pointers and -//! integers is very useful, so what can we do? +//! Rust provides two ways of dealing with this situation: *Strict Provenance* and *Exposed Provenance*. //! -//! Also did you know WASM is actually a "Harvard Architecture"? As in function pointers are -//! handled completely differently from data pointers? And we kind of just shipped Rust on WASM -//! without really addressing the fact that we let you freely convert between function pointers -//! and data pointers, because it mostly Just Works? Let's just put that on the "pointer casts -//! are dubious" pile. +//! Note that a pointer *can* represent a `usize` (via [`without_provenance`]), so the right type to +//! use in situations where a value is "sometimes a pointer and sometimes a bare `usize`" is a +//! pointer type. //! -//! Strict Provenance attempts to square these circles by decoupling Rust's traditional conflation -//! of pointers and `usize` (and `isize`), and defining a pointer to semantically contain the -//! following information: +//! ## Strict Provenance //! -//! * The **address-space** it is part of (e.g. "data" vs "code" in WASM). -//! * The **address** it points to, which can be represented by a `usize`. -//! * The **provenance** it has, defining the memory it has permission to access. -//! Provenance can be absent, in which case the pointer does not have permission to access any memory. +//! "Strict Provenance" refers to a set of APIs designed to make working with provenance more +//! explicit. They are intended as substitutes for casting a pointer to an integer and back. //! -//! Under Strict Provenance, a `usize` *cannot* accurately represent a pointer, and converting from -//! a pointer to a `usize` is generally an operation which *only* extracts the address. It is -//! therefore *impossible* to construct a valid pointer from a `usize` because there is no way -//! to restore the address-space and provenance. In other words, pointer-integer-pointer -//! roundtrips are not possible (in the sense that the resulting pointer is not dereferenceable). +//! Entirely avoiding integer-to-pointer casts successfully side-steps the inherent ambiguity of +//! that operation. This benefits compiler optimizations, and it is pretty much a requirement for +//! using tools like [Miri] and architectures like [CHERI] that aim to detect and diagnose pointer +//! misuse. //! -//! The key insight to making this model *at all* viable is the [`with_addr`][] method: +//! The key insight to making programming without integer-to-pointer casts *at all* viable is the +//! [`with_addr`] method: //! //! ```text //! /// Creates a new pointer with the given address. //! /// //! /// This performs the same operation as an `addr as ptr` cast, but copies -//! /// the *address-space* and *provenance* of `self` to the new pointer. +//! /// the *provenance* of `self` to the new pointer. //! /// This allows us to dynamically preserve and propagate this important //! /// information in a way that is otherwise impossible with a unary cast. //! /// @@ -257,23 +265,21 @@ //! //! So you're still able to drop down to the address representation and do whatever //! clever bit tricks you want *as long as* you're able to keep around a pointer -//! into the allocation you care about that can "reconstitute" the other parts of the pointer. +//! into the allocation you care about that can "reconstitute" the provenance. //! Usually this is very easy, because you only are taking a pointer, messing with the address, //! and then immediately converting back to a pointer. To make this use case more ergonomic, -//! we provide the [`map_addr`][] method. +//! we provide the [`map_addr`] method. //! //! To help make it clear that code is "following" Strict Provenance semantics, we also provide an -//! [`addr`][] method which promises that the returned address is not part of a -//! pointer-usize-pointer roundtrip. In the future we may provide a lint for pointer<->integer +//! [`addr`] method which promises that the returned address is not part of a +//! pointer-integer-pointer roundtrip. In the future we may provide a lint for pointer<->integer //! casts to help you audit if your code conforms to strict provenance. //! -//! -//! ## Using Strict Provenance +//! ### Using Strict Provenance //! //! Most code needs no changes to conform to strict provenance, as the only really concerning -//! operation that *wasn't* obviously already Undefined Behaviour is casts from usize to a -//! pointer. For code which *does* cast a `usize` to a pointer, the scope of the change depends -//! on exactly what you're doing. +//! operation is casts from usize to a pointer. For code which *does* cast a `usize` to a pointer, +//! the scope of the change depends on exactly what you're doing. //! //! In general, you just need to make sure that if you want to convert a `usize` address to a //! pointer and then use that pointer to read/write memory, you need to keep around a pointer @@ -284,8 +290,6 @@ //! represent the tagged pointer as an actual pointer and not a `usize`*. For instance: //! //! ``` -//! #![feature(strict_provenance)] -//! //! unsafe { //! // A flag we want to pack into our pointer //! static HAS_DATA: usize = 0x1; @@ -314,122 +318,65 @@ //! be using AtomicPtr instead. If that messes up the way you atomically manipulate pointers, //! we would like to know why, and what needs to be done to fix it.) //! -//! Something more complicated and just generally *evil* like an XOR-List requires more significant -//! changes like allocating all nodes in a pre-allocated Vec or Arena and using a pointer -//! to the whole allocation to reconstitute the XORed addresses. -//! //! Situations where a valid pointer *must* be created from just an address, such as baremetal code -//! accessing a memory-mapped interface at a fixed address, are an open question on how to support. -//! These situations *will* still be allowed, but we might require some kind of "I know what I'm -//! doing" annotation to explain the situation to the compiler. It's also possible they need no -//! special attention at all, because they're generally accessing memory outside the scope of -//! "the abstract machine", or already using "I know what I'm doing" annotations like "volatile". -//! -//! Under [Strict Provenance] it is Undefined Behaviour to: -//! -//! * Access memory through a pointer that does not have provenance over that memory. -//! -//! * [`offset`] a pointer to or from an address it doesn't have provenance over. -//! This means it's always UB to offset a pointer derived from something deallocated, -//! even if the offset is 0. Note that a pointer "one past the end" of its provenance -//! is not actually outside its provenance, it just has 0 bytes it can load/store. -//! -//! But it *is* still sound to: -//! -//! * Create a pointer without provenance from just an address (see [`ptr::dangling`][]). Such a -//! pointer cannot be used for memory accesses (except for zero-sized accesses). This can still be -//! useful for sentinel values like `null` *or* to represent a tagged pointer that will never be -//! dereferenceable. In general, it is always sound for an integer to pretend to be a pointer "for -//! fun" as long as you don't use operations on it which require it to be valid (non-zero-sized -//! offset, read, write, etc). -//! -//! * Forge an allocation of size zero at any sufficiently aligned non-null address. -//! i.e. the usual "ZSTs are fake, do what you want" rules apply *but* this only applies -//! for actual forgery (integers cast to pointers). If you borrow some struct's field -//! that *happens* to be zero-sized, the resulting pointer will have provenance tied to -//! that allocation, and it will still get invalidated if the allocation gets deallocated. -//! In the future we may introduce an API to make such a forged allocation explicit. -//! -//! * [`wrapping_offset`][] a pointer outside its provenance. This includes pointers -//! which have "no" provenance. Unfortunately there may be practical limits on this for a -//! particular platform, and it's an open question as to how to specify this (if at all). -//! Notably, [CHERI][] relies on a compression scheme that can't handle a -//! pointer getting offset "too far" out of bounds. If this happens, the address -//! returned by `addr` will be the value you expect, but the provenance will get invalidated -//! and using it to read/write will fault. The details of this are architecture-specific -//! and based on alignment, but the buffer on either side of the pointer's range is pretty -//! generous (think kilobytes, not bytes). -//! -//! * Compare arbitrary pointers by address. Addresses *are* just integers and so there is -//! always a coherent answer, even if the pointers are dangling or from different -//! address-spaces/provenances. Of course, comparing addresses from different address-spaces -//! is generally going to be *meaningless*, but so is comparing Kilograms to Meters, and Rust -//! doesn't prevent that either. Similarly, if you get "lucky" and notice that a pointer -//! one-past-the-end is the "same" address as the start of an unrelated allocation, anything -//! you do with that fact is *probably* going to be gibberish. The scope of that gibberish -//! is kept under control by the fact that the two pointers *still* aren't allowed to access -//! the other's allocation (bytes), because they still have different provenance. -//! -//! * Perform pointer tagging tricks. This falls out of [`wrapping_offset`] but is worth -//! mentioning in more detail because of the limitations of [CHERI][]. Low-bit tagging -//! is very robust, and often doesn't even go out of bounds because types ensure -//! size >= align (and over-aligning actually gives CHERI more flexibility). Anything -//! more complex than this rapidly enters "extremely platform-specific" territory as -//! certain things may or may not be allowed based on specific supported operations. -//! For instance, ARM explicitly supports high-bit tagging, and so CHERI on ARM inherits -//! that and should support it. +//! accessing a memory-mapped interface at a fixed address, cannot currently be handled with strict +//! provenance APIs and should use [exposed provenance](#exposed-provenance). //! //! ## Exposed Provenance //! -//! **This section is *non-normative* and is an extension to the [Strict Provenance] experiment.** -//! -//! As discussed above, pointer-usize-pointer roundtrips are not possible under [Strict Provenance]. +//! As discussed above, integer-to-pointer casts are not possible with Strict Provenance APIs. //! This is by design: the goal of Strict Provenance is to provide a clear specification that we are -//! confident can be formalized unambiguously and can be subject to precise formal reasoning. +//! confident can be formalized unambiguously and can be subject to precise formal reasoning. +//! Integer-to-pointer casts do not (currently) have such a clear specification. //! -//! However, there exist situations where pointer-usize-pointer roundtrips cannot be avoided, or +//! However, there exist situations where integer-to-pointer casts cannot be avoided, or //! where avoiding them would require major refactoring. Legacy platform APIs also regularly assume -//! that `usize` can capture all the information that makes up a pointer. The goal of Strict -//! Provenance is not to rule out such code; the goal is to put all the *other* pointer-manipulating -//! code onto a more solid foundation. Strict Provenance is about improving the situation where -//! possible (all the code that can be written with Strict Provenance) without making things worse -//! for situations where Strict Provenance is insufficient. -//! -//! For these situations, there is a highly experimental extension to Strict Provenance called -//! *Exposed Provenance*. This extension permits pointer-usize-pointer roundtrips. However, its -//! semantics are on much less solid footing than Strict Provenance, and at this point it is not yet -//! clear where a satisfying unambiguous semantics can be defined for Exposed Provenance. -//! Furthermore, Exposed Provenance will not work (well) with tools like [Miri] and [CHERI]. +//! that `usize` can capture all the information that makes up a pointer. +//! Bare-metal platforms can also require the synthesis of a pointer "out of thin air" without +//! anywhere to obtain proper provenance from. +//! +//! Rust's model for dealing with integer-to-pointer casts is called *Exposed Provenance*. However, +//! the semantics of Exposed Provenance are on much less solid footing than Strict Provenance, and +//! at this point it is not yet clear whether a satisfying unambiguous semantics can be defined for +//! Exposed Provenance. (If that sounds bad, be reassured that other popular languages that provide +//! integer-to-pointer casts are not faring any better.) Furthermore, Exposed Provenance will not +//! work (well) with tools like [Miri] and [CHERI]. //! //! Exposed Provenance is provided by the [`expose_provenance`] and [`with_exposed_provenance`] methods, -//! which are meant to replace `as` casts between pointers and integers. [`expose_provenance`] is a lot like -//! [`addr`], but additionally adds the provenance of the pointer to a global list of 'exposed' -//! provenances. (This list is purely conceptual, it exists for the purpose of specifying Rust but -//! is not materialized in actual executions, except in tools like [Miri].) [`with_exposed_provenance`] -//! can be used to construct a pointer with one of these previously 'exposed' provenances. -//! [`with_exposed_provenance`] takes only `addr: usize` as arguments, so unlike in [`with_addr`] there is -//! no indication of what the correct provenance for the returned pointer is -- and that is exactly -//! what makes pointer-usize-pointer roundtrips so tricky to rigorously specify! There is no -//! algorithm that decides which provenance will be used. You can think of this as "guessing" the -//! right provenance, and the guess will be "maximally in your favor", in the sense that if there is -//! any way to avoid undefined behavior, then that is the guess that will be taken. However, if -//! there is *no* previously 'exposed' provenance that justifies the way the returned pointer will -//! be used, the program has undefined behavior. -//! -//! Using [`expose_provenance`] or [`with_exposed_provenance`] (or the `as` casts) means that code is -//! *not* following Strict Provenance rules. The goal of the Strict Provenance experiment is to -//! determine how far one can get in Rust without the use of [`expose_provenance`] and -//! [`with_exposed_provenance`], and to encourage code to be written with Strict Provenance APIs only. -//! Maximizing the amount of such code is a major win for avoiding specification complexity and to -//! facilitate adoption of tools like [CHERI] and [Miri] that can be a big help in increasing the -//! confidence in (unsafe) Rust code. +//! which are equivalent to `as` casts between pointers and integers. +//! - [`expose_provenance`] is a lot like [`addr`], but additionally adds the provenance of the +//! pointer to a global list of 'exposed' provenances. (This list is purely conceptual, it exists +//! for the purpose of specifying Rust but is not materialized in actual executions, except in +//! tools like [Miri].) +//! Memory which is outside the control of the Rust abstract machine (MMIO registers, for example) +//! is always considered to be exposed, so long as this memory is disjoint from memory that will +//! be used by the abstract machine such as the stack, heap, and statics. +//! - [`with_exposed_provenance`] can be used to construct a pointer with one of these previously +//! 'exposed' provenances. [`with_exposed_provenance`] takes only `addr: usize` as arguments, so +//! unlike in [`with_addr`] there is no indication of what the correct provenance for the returned +//! pointer is -- and that is exactly what makes integer-to-pointer casts so tricky to rigorously +//! specify! The compiler will do its best to pick the right provenance for you, but currently we +//! cannot provide any guarantees about which provenance the resulting pointer will have. Only one +//! thing is clear: if there is *no* previously 'exposed' provenance that justifies the way the +//! returned pointer will be used, the program has undefined behavior. +//! +//! If at all possible, we encourage code to be ported to [Strict Provenance] APIs, thus avoiding +//! the need for Exposed Provenance. Maximizing the amount of such code is a major win for avoiding +//! specification complexity and to facilitate adoption of tools like [CHERI] and [Miri] that can be +//! a big help in increasing the confidence in (unsafe) Rust code. However, we acknowledge that this +//! is not always possible, and offer Exposed Provenance as a way to explicit "opt out" of the +//! well-defined semantics of Strict Provenance, and "opt in" to the unclear semantics of +//! integer-to-pointer casts. //! //! [aliasing]: ../../nomicon/aliasing.html +//! [allocated object]: #allocated-object +//! [provenance]: #provenance //! [book]: ../../book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer //! [ub]: ../../reference/behavior-considered-undefined.html //! [zst]: ../../nomicon/exotic-sizes.html#zero-sized-types-zsts //! [atomic operations]: crate::sync::atomic //! [`offset`]: pointer::offset +//! [`offset_from`]: pointer::offset_from //! [`wrapping_offset`]: pointer::wrapping_offset //! [`with_addr`]: pointer::with_addr //! [`map_addr`]: pointer::map_addr @@ -439,8 +386,8 @@ //! [`with_exposed_provenance`]: with_exposed_provenance //! [Miri]: https://github.com/rust-lang/miri //! [CHERI]: https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/ -//! [Strict Provenance]: https://github.com/rust-lang/rust/issues/95228 -//! [Stacked Borrows]: https://plv.mpi-sws.org/rustbelt/stacked-borrows/ +//! [Strict Provenance]: #strict-provenance +//! [`UnsafeCell`]: core::cell::UnsafeCell #![stable(feature = "rust1", since = "1.0.0")] // There are many unsafe functions taking pointers that don't dereference them. @@ -629,7 +576,7 @@ pub const fn null_mut() -> *mut T { from_raw_parts_mut(without_provenance_mut::<()>(0), ()) } -/// Creates a pointer with the given address and no provenance. +/// Creates a pointer with the given address and no [provenance][crate::ptr#provenance]. /// /// This is equivalent to `ptr::null().with_addr(addr)`. /// @@ -641,16 +588,15 @@ pub const fn null_mut() -> *mut T { /// This is different from `addr as *const T`, which creates a pointer that picks up a previously /// exposed provenance. See [`with_exposed_provenance`] for more details on that operation. /// -/// This API and its claimed semantics are part of the Strict Provenance experiment, -/// see the [module documentation][crate::ptr] for details. +/// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[inline(always)] #[must_use] #[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")] -#[unstable(feature = "strict_provenance", issue = "95228")] +#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub const fn without_provenance(addr: usize) -> *const T { - // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. - // We use transmute rather than a cast so tools like Miri can tell that this - // is *not* the same as with_exposed_provenance. + // An int-to-pointer transmute currently has exactly the intended semantics: it creates a + // pointer without provenance. Note that this is *not* a stable guarantee about transmute + // semantics, it relies on sysroot crates having special status. // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that // pointer). unsafe { mem::transmute(addr) } @@ -668,12 +614,12 @@ pub const fn without_provenance(addr: usize) -> *const T { #[inline(always)] #[must_use] #[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")] -#[unstable(feature = "strict_provenance", issue = "95228")] +#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub const fn dangling() -> *const T { without_provenance(mem::align_of::()) } -/// Creates a pointer with the given address and no provenance. +/// Creates a pointer with the given address and no [provenance][crate::ptr#provenance]. /// /// This is equivalent to `ptr::null_mut().with_addr(addr)`. /// @@ -685,16 +631,15 @@ pub const fn dangling() -> *const T { /// This is different from `addr as *mut T`, which creates a pointer that picks up a previously /// exposed provenance. See [`with_exposed_provenance_mut`] for more details on that operation. /// -/// This API and its claimed semantics are part of the Strict Provenance experiment, -/// see the [module documentation][crate::ptr] for details. +/// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[inline(always)] #[must_use] #[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")] -#[unstable(feature = "strict_provenance", issue = "95228")] +#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub const fn without_provenance_mut(addr: usize) -> *mut T { - // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. - // We use transmute rather than a cast so tools like Miri can tell that this - // is *not* the same as with_exposed_provenance. + // An int-to-pointer transmute currently has exactly the intended semantics: it creates a + // pointer without provenance. Note that this is *not* a stable guarantee about transmute + // semantics, it relies on sysroot crates having special status. // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that // pointer). unsafe { mem::transmute(addr) } @@ -712,96 +657,88 @@ pub const fn without_provenance_mut(addr: usize) -> *mut T { #[inline(always)] #[must_use] #[rustc_const_stable(feature = "stable_things_using_strict_provenance", since = "1.61.0")] -#[unstable(feature = "strict_provenance", issue = "95228")] +#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub const fn dangling_mut() -> *mut T { without_provenance_mut(mem::align_of::()) } -/// Converts an address back to a pointer, picking up a previously 'exposed' provenance. +/// Converts an address back to a pointer, picking up some previously 'exposed' +/// [provenance][crate::ptr#provenance]. /// -/// This is a more rigorously specified alternative to `addr as *const T`. The provenance of the -/// returned pointer is that of *any* pointer that was previously exposed by passing it to -/// [`expose_provenance`][pointer::expose_provenance], or a `ptr as usize` cast. In addition, memory which is -/// outside the control of the Rust abstract machine (MMIO registers, for example) is always -/// considered to be exposed, so long as this memory is disjoint from memory that will be used by -/// the abstract machine such as the stack, heap, and statics. +/// This is fully equivalent to `addr as *const T`. The provenance of the returned pointer is that +/// of *some* pointer that was previously exposed by passing it to +/// [`expose_provenance`][pointer::expose_provenance], or a `ptr as usize` cast. In addition, memory +/// which is outside the control of the Rust abstract machine (MMIO registers, for example) is +/// always considered to be accessible with an exposed provenance, so long as this memory is disjoint +/// from memory that will be used by the abstract machine such as the stack, heap, and statics. /// -/// If there is no 'exposed' provenance that justifies the way this pointer will be used, -/// the program has undefined behavior. In particular, the aliasing rules still apply: pointers -/// and references that have been invalidated due to aliasing accesses cannot be used anymore, -/// even if they have been exposed! +/// The exact provenance that gets picked is not specified. The compiler will do its best to pick +/// the "right" provenance for you (whatever that may be), but currently we cannot provide any +/// guarantees about which provenance the resulting pointer will have -- and therefore there +/// is no definite specification for which memory the resulting pointer may access. /// -/// Note that there is no algorithm that decides which provenance will be used. You can think of this -/// as "guessing" the right provenance, and the guess will be "maximally in your favor", in the sense -/// that if there is any way to avoid undefined behavior (while upholding all aliasing requirements), -/// then that is the guess that will be taken. +/// If there is *no* previously 'exposed' provenance that justifies the way the returned pointer +/// will be used, the program has undefined behavior. In particular, the aliasing rules still apply: +/// pointers and references that have been invalidated due to aliasing accesses cannot be used +/// anymore, even if they have been exposed! /// -/// On platforms with multiple address spaces, it is your responsibility to ensure that the -/// address makes sense in the address space that this pointer will be used with. -/// -/// Using this function means that code is *not* following [Strict -/// Provenance][self#strict-provenance] rules. "Guessing" a -/// suitable provenance complicates specification and reasoning and may not be supported by -/// tools that help you to stay conformant with the Rust memory model, so it is recommended to -/// use [`with_addr`][pointer::with_addr] wherever possible. +/// Due to its inherent ambiguity, this operation may not be supported by tools that help you to +/// stay conformant with the Rust memory model. It is recommended to use [Strict +/// Provenance][self#strict-provenance] APIs such as [`with_addr`][pointer::with_addr] wherever +/// possible. /// /// On most platforms this will produce a value with the same bytes as the address. Platforms /// which need to store additional information in a pointer may not support this operation, /// since it is generally not possible to actually *compute* which provenance the returned /// pointer has to pick up. /// -/// It is unclear whether this function can be given a satisfying unambiguous specification. This -/// API and its claimed semantics are part of [Exposed Provenance][self#exposed-provenance]. +/// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. #[must_use] #[inline(always)] -#[unstable(feature = "exposed_provenance", issue = "95228")] +#[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead -pub fn with_exposed_provenance(addr: usize) -> *const T -where - T: Sized, -{ - // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. +pub fn with_exposed_provenance(addr: usize) -> *const T { addr as *const T } -/// Converts an address back to a mutable pointer, picking up a previously 'exposed' provenance. +/// Converts an address back to a mutable pointer, picking up some previously 'exposed' +/// [provenance][crate::ptr#provenance]. +/// +/// This is fully equivalent to `addr as *mut T`. The provenance of the returned pointer is that +/// of *some* pointer that was previously exposed by passing it to +/// [`expose_provenance`][pointer::expose_provenance], or a `ptr as usize` cast. In addition, memory +/// which is outside the control of the Rust abstract machine (MMIO registers, for example) is +/// always considered to be accessible with an exposed provenance, so long as this memory is disjoint +/// from memory that will be used by the abstract machine such as the stack, heap, and statics. /// -/// This is a more rigorously specified alternative to `addr as *mut T`. The provenance of the -/// returned pointer is that of *any* pointer that was previously passed to -/// [`expose_provenance`][pointer::expose_provenance] or a `ptr as usize` cast. If there is no previously -/// 'exposed' provenance that justifies the way this pointer will be used, the program has undefined -/// behavior. Note that there is no algorithm that decides which provenance will be used. You can -/// think of this as "guessing" the right provenance, and the guess will be "maximally in your -/// favor", in the sense that if there is any way to avoid undefined behavior, then that is the -/// guess that will be taken. +/// The exact provenance that gets picked is not specified. The compiler will do its best to pick +/// the "right" provenance for you (whatever that may be), but currently we cannot provide any +/// guarantees about which provenance the resulting pointer will have -- and therefore there +/// is no definite specification for which memory the resulting pointer may access. /// -/// On platforms with multiple address spaces, it is your responsibility to ensure that the -/// address makes sense in the address space that this pointer will be used with. +/// If there is *no* previously 'exposed' provenance that justifies the way the returned pointer +/// will be used, the program has undefined behavior. In particular, the aliasing rules still apply: +/// pointers and references that have been invalidated due to aliasing accesses cannot be used +/// anymore, even if they have been exposed! /// -/// Using this function means that code is *not* following [Strict -/// Provenance][self#strict-provenance] rules. "Guessing" a -/// suitable provenance complicates specification and reasoning and may not be supported by -/// tools that help you to stay conformant with the Rust memory model, so it is recommended to -/// use [`with_addr`][pointer::with_addr] wherever possible. +/// Due to its inherent ambiguity, this operation may not be supported by tools that help you to +/// stay conformant with the Rust memory model. It is recommended to use [Strict +/// Provenance][self#strict-provenance] APIs such as [`with_addr`][pointer::with_addr] wherever +/// possible. /// /// On most platforms this will produce a value with the same bytes as the address. Platforms /// which need to store additional information in a pointer may not support this operation, /// since it is generally not possible to actually *compute* which provenance the returned /// pointer has to pick up. /// -/// It is unclear whether this function can be given a satisfying unambiguous specification. This -/// API and its claimed semantics are part of [Exposed Provenance][self#exposed-provenance]. +/// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. #[must_use] #[inline(always)] -#[unstable(feature = "exposed_provenance", issue = "95228")] +#[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead -pub fn with_exposed_provenance_mut(addr: usize) -> *mut T -where - T: Sized, -{ - // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. +pub fn with_exposed_provenance_mut(addr: usize) -> *mut T { addr as *mut T } @@ -1024,7 +961,7 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { /// /// * Both `x` and `y` must be properly aligned. /// -/// Note that even if `T` has size `0`, the pointers must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointers must be properly aligned. /// /// [valid]: self#safety /// @@ -1110,7 +1047,7 @@ pub const unsafe fn swap(x: *mut T, y: *mut T) { /// beginning at `y` with the same size. /// /// Note that even if the effectively copied size (`count * size_of::()`) is `0`, -/// the pointers must be non-null and properly aligned. +/// the pointers must be properly aligned. /// /// [valid]: self#safety /// @@ -1243,7 +1180,7 @@ const unsafe fn swap_nonoverlapping_simple_untyped(x: *mut T, y: *mut T, coun /// /// * `dst` must point to a properly initialized value of type `T`. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointer must be properly aligned. /// /// [valid]: self#safety /// @@ -1300,7 +1237,7 @@ pub const unsafe fn replace(dst: *mut T, src: T) -> T { /// /// * `src` must point to a properly initialized value of type `T`. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointer must be properly aligned. /// /// # Examples /// @@ -1555,7 +1492,7 @@ pub const unsafe fn read_unaligned(src: *const T) -> T { /// * `dst` must be properly aligned. Use [`write_unaligned`] if this is not the /// case. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointer must be properly aligned. /// /// [valid]: self#safety /// @@ -1774,7 +1711,7 @@ pub const unsafe fn write_unaligned(dst: *mut T, src: T) { /// However, storing non-[`Copy`] types in volatile memory is almost certainly /// incorrect. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointer must be properly aligned. /// /// [valid]: self#safety /// [read-ownership]: read#ownership-of-the-returned-value @@ -1853,7 +1790,7 @@ pub unsafe fn read_volatile(src: *const T) -> T { /// /// * `dst` must be properly aligned. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointer must be properly aligned. /// /// [valid]: self#safety /// diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 613d2c91ac63e..782934fc311db 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -124,12 +124,12 @@ impl *mut T { /// Gets the "address" portion of the pointer. /// - /// This is similar to `self as usize`, which semantically discards *provenance* and - /// *address-space* information. However, unlike `self as usize`, casting the returned address - /// back to a pointer yields a [pointer without provenance][without_provenance_mut], which is undefined - /// behavior to dereference. To properly restore the lost information and obtain a - /// dereferenceable pointer, use [`with_addr`][pointer::with_addr] or - /// [`map_addr`][pointer::map_addr]. + /// This is similar to `self as usize`, except that the [provenance][crate::ptr#provenance] of + /// the pointer is discarded and not [exposed][crate::ptr#exposed-provenance]. This means that + /// casting the returned address back to a pointer yields a [pointer without + /// provenance][without_provenance_mut], which is undefined behavior to dereference. To properly + /// restore the lost information and obtain a dereferenceable pointer, use + /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr]. /// /// If using those APIs is not possible because there is no way to preserve a pointer with the /// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts @@ -143,89 +143,80 @@ impl *mut T { /// perform a change of representation to produce a value containing only the address /// portion of the pointer. What that means is up to the platform to define. /// - /// This API and its claimed semantics are part of the Strict Provenance experiment, and as such - /// might change in the future (including possibly weakening this so it becomes wholly - /// equivalent to `self as usize`). See the [module documentation][crate::ptr] for details. + /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline(always)] - #[unstable(feature = "strict_provenance", issue = "95228")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn addr(self) -> usize { - // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + // A pointer-to-integer transmute currently has exactly the right semantics: it returns the + // address without exposing the provenance. Note that this is *not* a stable guarantee about + // transmute semantics, it relies on sysroot crates having special status. // SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the // provenance). unsafe { mem::transmute(self.cast::<()>()) } } - /// Exposes the "provenance" part of the pointer for future use in - /// [`with_exposed_provenance`][] and returns the "address" portion. + /// Exposes the ["provenance"][crate::ptr#provenance] part of the pointer for future use in + /// [`with_exposed_provenance_mut`] and returns the "address" portion. /// - /// This is equivalent to `self as usize`, which semantically discards *provenance* and - /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit - /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can - /// later call [`with_exposed_provenance_mut`][] to reconstitute the original pointer including its - /// provenance. (Reconstructing address space information, if required, is your responsibility.) + /// This is equivalent to `self as usize`, which semantically discards provenance information. + /// Furthermore, this (like the `as` cast) has the implicit side-effect of marking the + /// provenance as 'exposed', so on platforms that support it you can later call + /// [`with_exposed_provenance_mut`] to reconstitute the original pointer including its provenance. /// - /// Using this method means that code is *not* following [Strict - /// Provenance][super#strict-provenance] rules. Supporting - /// [`with_exposed_provenance_mut`][] complicates specification and reasoning and may not be supported - /// by tools that help you to stay conformant with the Rust memory model, so it is recommended - /// to use [`addr`][pointer::addr] wherever possible. + /// Due to its inherent ambiguity, [`with_exposed_provenance_mut`] may not be supported by tools + /// that help you to stay conformant with the Rust memory model. It is recommended to use + /// [Strict Provenance][crate::ptr#strict-provenance] APIs such as [`with_addr`][pointer::with_addr] + /// wherever possible, in which case [`addr`][pointer::addr] should be used instead of `expose_provenance`. /// /// On most platforms this will produce a value with the same bytes as the original pointer, /// because all the bytes are dedicated to describing the address. Platforms which need to store /// additional information in the pointer may not support this operation, since the 'expose' - /// side-effect which is required for [`with_exposed_provenance_mut`][] to work is typically not + /// side-effect which is required for [`with_exposed_provenance_mut`] to work is typically not /// available. /// - /// It is unclear whether this method can be given a satisfying unambiguous specification. This - /// API and its claimed semantics are part of [Exposed Provenance][super#exposed-provenance]. + /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. /// /// [`with_exposed_provenance_mut`]: with_exposed_provenance_mut #[inline(always)] - #[unstable(feature = "exposed_provenance", issue = "95228")] + #[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn expose_provenance(self) -> usize { - // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. self.cast::<()>() as usize } - /// Creates a new pointer with the given address. + /// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of + /// `self`. /// - /// This performs the same operation as an `addr as ptr` cast, but copies - /// the *address-space* and *provenance* of `self` to the new pointer. - /// This allows us to dynamically preserve and propagate this important - /// information in a way that is otherwise impossible with a unary cast. + /// This is similar to a `addr as *mut T` cast, but copies + /// the *provenance* of `self` to the new pointer. + /// This avoids the inherent ambiguity of the unary cast. /// /// This is equivalent to using [`wrapping_offset`][pointer::wrapping_offset] to offset /// `self` to the given address, and therefore has all the same capabilities and restrictions. /// - /// This API and its claimed semantics are an extension to the Strict Provenance experiment, - /// see the [module documentation][crate::ptr] for details. + /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[unstable(feature = "strict_provenance", issue = "95228")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn with_addr(self, addr: usize) -> Self { - // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. - // - // In the mean-time, this operation is defined to be "as if" it was - // a wrapping_offset, so we can emulate it as such. This should properly - // restore pointer provenance even under today's compiler. + // This should probably be an intrinsic to avoid doing any sort of arithmetic, but + // meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's + // provenance. let self_addr = self.addr() as isize; let dest_addr = addr as isize; let offset = dest_addr.wrapping_sub(self_addr); - - // This is the canonical desugaring of this operation self.wrapping_byte_offset(offset) } - /// Creates a new pointer by mapping `self`'s address to a new one. + /// Creates a new pointer by mapping `self`'s address to a new one, preserving the original + /// pointer's [provenance][crate::ptr#provenance]. /// /// This is a convenience for [`with_addr`][pointer::with_addr], see that method for details. /// - /// This API and its claimed semantics are part of the Strict Provenance experiment, - /// see the [module documentation][crate::ptr] for details. + /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[unstable(feature = "strict_provenance", issue = "95228")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self { self.with_addr(f(self.addr())) } @@ -376,7 +367,7 @@ impl *mut T { /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without /// "wrapping around"), must fit in an `isize`. /// - /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in /// bounds of that allocated object. In particular, this range must not "wrap around" the edge /// of the address space. @@ -558,7 +549,7 @@ impl *mut T { /// ## Examples /// /// ``` - /// #![feature(ptr_mask, strict_provenance)] + /// #![feature(ptr_mask)] /// let mut v = 17_u32; /// let ptr: *mut u32 = &mut v; /// @@ -777,7 +768,7 @@ impl *mut T { /// * `self` and `origin` must either /// /// * point to the same address, or - /// * both be *derived from* a pointer to the same [allocated object], and the memory range between + /// * both be [derived from][crate::ptr#provenance] a pointer to the same [allocated object], and the memory range between /// the two pointers must be in bounds of that object. (See below for an example.) /// /// * The distance between the pointers, in bytes, must be an exact multiple @@ -954,7 +945,7 @@ impl *mut T { /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without /// "wrapping around"), must fit in an `isize`. /// - /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in /// bounds of that allocated object. In particular, this range must not "wrap around" the edge /// of the address space. @@ -1061,7 +1052,7 @@ impl *mut T { /// * The offset in bytes, `count * size_of::()`, computed on mathematical integers (without /// "wrapping around"), must fit in an `isize`. /// - /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in /// bounds of that allocated object. In particular, this range must not "wrap around" the edge /// of the address space. diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 3e4cae2b3cabd..d91bbe1a5a193 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -283,40 +283,39 @@ impl NonNull { /// /// For more details see the equivalent method on a raw pointer, [`pointer::addr`]. /// - /// This API and its claimed semantics are part of the Strict Provenance experiment, - /// see the [`ptr` module documentation][crate::ptr]. + /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[unstable(feature = "strict_provenance", issue = "95228")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn addr(self) -> NonZero { // SAFETY: The pointer is guaranteed by the type to be non-null, // meaning that the address will be non-zero. unsafe { NonZero::new_unchecked(self.pointer.addr()) } } - /// Creates a new pointer with the given address. + /// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of + /// `self`. /// /// For more details see the equivalent method on a raw pointer, [`pointer::with_addr`]. /// - /// This API and its claimed semantics are part of the Strict Provenance experiment, - /// see the [`ptr` module documentation][crate::ptr]. + /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[unstable(feature = "strict_provenance", issue = "95228")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn with_addr(self, addr: NonZero) -> Self { // SAFETY: The result of `ptr::from::with_addr` is non-null because `addr` is guaranteed to be non-zero. unsafe { NonNull::new_unchecked(self.pointer.with_addr(addr.get()) as *mut _) } } - /// Creates a new pointer by mapping `self`'s address to a new one. + /// Creates a new pointer by mapping `self`'s address to a new one, preserving the + /// [provenance][crate::ptr#provenance] of `self`. /// /// For more details see the equivalent method on a raw pointer, [`pointer::map_addr`]. /// - /// This API and its claimed semantics are part of the Strict Provenance experiment, - /// see the [`ptr` module documentation][crate::ptr]. + /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[unstable(feature = "strict_provenance", issue = "95228")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn map_addr(self, f: impl FnOnce(NonZero) -> NonZero) -> Self { self.with_addr(f(self.addr())) } @@ -749,7 +748,6 @@ impl NonNull { /// *Incorrect* usage: /// /// ```rust,no_run - /// #![feature(strict_provenance)] /// use std::ptr::NonNull; /// /// let ptr1 = NonNull::new(Box::into_raw(Box::new(0u8))).unwrap(); diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs index 9f1294d760647..eb60effe8138f 100644 --- a/library/core/src/str/pattern.rs +++ b/library/core/src/str/pattern.rs @@ -160,6 +160,19 @@ pub trait Pattern: Sized { None } } + + /// Returns the pattern as utf-8 bytes if possible. + fn as_utf8_pattern(&self) -> Option>; +} +/// Result of calling [`Pattern::as_utf8_pattern()`]. +/// Can be used for inspecting the contents of a [`Pattern`] in cases +/// where the underlying representation can be represented as UTF-8. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum Utf8Pattern<'a> { + /// Type returned by String and str types. + StringPattern(&'a [u8]), + /// Type returned by char types. + CharPattern(char), } // Searcher @@ -599,6 +612,11 @@ impl Pattern for char { { self.encode_utf8(&mut [0u8; 4]).strip_suffix_of(haystack) } + + #[inline] + fn as_utf8_pattern(&self) -> Option> { + Some(Utf8Pattern::CharPattern(*self)) + } } ///////////////////////////////////////////////////////////////////////////// @@ -657,6 +675,11 @@ impl Pattern for MultiCharEqPattern { fn into_searcher(self, haystack: &str) -> MultiCharEqSearcher<'_, C> { MultiCharEqSearcher { haystack, char_eq: self.0, char_indices: haystack.char_indices() } } + + #[inline] + fn as_utf8_pattern(&self) -> Option> { + None + } } unsafe impl<'a, C: MultiCharEq> Searcher<'a> for MultiCharEqSearcher<'a, C> { @@ -747,6 +770,11 @@ macro_rules! pattern_methods { { ($pmap)(self).strip_suffix_of(haystack) } + + #[inline] + fn as_utf8_pattern(&self) -> Option> { + None + } }; } @@ -1022,6 +1050,11 @@ impl<'b> Pattern for &'b str { None } } + + #[inline] + fn as_utf8_pattern(&self) -> Option> { + Some(Utf8Pattern::StringPattern(self.as_bytes())) + } } ///////////////////////////////////////////////////////////////////////////// diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 42b68e28273e5..17ba18c2a669f 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1758,7 +1758,7 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr, strict_provenance)] + /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let atom = AtomicPtr::::new(core::ptr::null_mut()); @@ -1838,7 +1838,7 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr, strict_provenance)] + /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let atom = AtomicPtr::::new(core::ptr::null_mut()); @@ -1874,7 +1874,7 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr, strict_provenance)] + /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let atom = AtomicPtr::::new(core::ptr::without_provenance_mut(1)); @@ -1919,7 +1919,7 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr, strict_provenance)] + /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let pointer = &mut 3i64 as *mut i64; @@ -1970,7 +1970,7 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr, strict_provenance)] + /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let pointer = &mut 3i64 as *mut i64; @@ -2020,7 +2020,7 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr, strict_provenance)] + /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let pointer = &mut 3i64 as *mut i64; diff --git a/library/core/tests/lazy.rs b/library/core/tests/lazy.rs index 711511eaf4a5a..32d0ac51f0336 100644 --- a/library/core/tests/lazy.rs +++ b/library/core/tests/lazy.rs @@ -114,6 +114,7 @@ fn lazy_type_inference() { } #[test] +#[cfg(panic = "unwind")] #[should_panic = "LazyCell instance has previously been poisoned"] fn lazy_force_mut_panic() { let mut lazy = LazyCell::::new(|| panic!()); diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index bfc0b638b7e6c..5d6921845d0c9 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -1,4 +1,6 @@ // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(strict_provenance))] +#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_match))] #![feature(alloc_layout_extra)] @@ -21,7 +23,7 @@ #![feature(const_likely)] #![feature(const_nonnull_new)] #![feature(const_option_ext)] -#![feature(const_pin)] +#![feature(const_pin_2)] #![feature(const_pointer_is_aligned)] #![feature(const_three_way_compare)] #![feature(const_trait_impl)] @@ -85,7 +87,6 @@ #![feature(std_internals)] #![feature(step_trait)] #![feature(str_internals)] -#![feature(strict_provenance)] #![feature(strict_provenance_atomic_ptr)] #![feature(test)] #![feature(trait_upcasting)] diff --git a/library/core/tests/pin.rs b/library/core/tests/pin.rs index 7a6af46a74323..026d2ca8de26a 100644 --- a/library/core/tests/pin.rs +++ b/library/core/tests/pin.rs @@ -19,6 +19,10 @@ fn pin_const() { const REF: &'static usize = PINNED.get_ref(); assert_eq!(REF, POINTER); + const INT: u8 = 42; + const STATIC_REF: Pin<&'static u8> = Pin::static_ref(&INT); + assert_eq!(*STATIC_REF, INT); + // Note: `pin_mut_const` tests that the methods of `Pin<&mut T>` are usable in a const context. // A const fn is used because `&mut` is not (yet) usable in constants. const fn pin_mut_const() { diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 6cd4dffb8aa30..1981675f40922 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -19,8 +19,6 @@ #![feature(panic_unwind)] #![feature(staged_api)] #![feature(std_internals)] -#![feature(strict_provenance)] -#![feature(exposed_provenance)] #![feature(rustc_attrs)] #![panic_runtime] #![feature(panic_runtime)] diff --git a/library/portable-simd/crates/core_simd/src/lib.rs b/library/portable-simd/crates/core_simd/src/lib.rs index cc6246b4a0d41..992a7705e3c52 100644 --- a/library/portable-simd/crates/core_simd/src/lib.rs +++ b/library/portable-simd/crates/core_simd/src/lib.rs @@ -9,7 +9,6 @@ repr_simd, simd_ffi, staged_api, - strict_provenance, prelude_import, ptr_metadata )] diff --git a/library/portable-simd/crates/core_simd/tests/pointers.rs b/library/portable-simd/crates/core_simd/tests/pointers.rs index 90bfc5d5fd6a5..d7db4e82b3ca2 100644 --- a/library/portable-simd/crates/core_simd/tests/pointers.rs +++ b/library/portable-simd/crates/core_simd/tests/pointers.rs @@ -1,4 +1,4 @@ -#![feature(portable_simd, strict_provenance, exposed_provenance)] +#![feature(portable_simd)] use core_simd::simd::{ ptr::{SimdConstPtr, SimdMutPtr}, diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 5a1086527a127..f6d4825c67b24 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -18,17 +18,10 @@ macro_rules! define_client_handles { $(pub(super) $ity: AtomicU32,)* } - impl HandleCounters { - // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of - // a wrapper `fn` pointer, once `const fn` can reference `static`s. - extern "C" fn get() -> &'static Self { - static COUNTERS: HandleCounters = HandleCounters { - $($oty: AtomicU32::new(1),)* - $($ity: AtomicU32::new(1),)* - }; - &COUNTERS - } - } + static COUNTERS: HandleCounters = HandleCounters { + $($oty: AtomicU32::new(1),)* + $($ity: AtomicU32::new(1),)* + }; $( pub(crate) struct $oty { @@ -259,9 +252,7 @@ pub(crate) fn is_available() -> bool { /// and forcing the use of APIs that take/return `S::TokenStream`, server-side. #[repr(C)] pub struct Client { - // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of - // a wrapper `fn` pointer, once `const fn` can reference `static`s. - pub(super) get_handle_counters: extern "C" fn() -> &'static HandleCounters, + pub(super) handle_counters: &'static HandleCounters, pub(super) run: extern "C" fn(BridgeConfig<'_>) -> Buffer, @@ -346,7 +337,7 @@ fn run_client DecodeMut<'a, 's, ()>, R: Encode<()>>( impl Client { pub const fn expand1(f: impl Fn(crate::TokenStream) -> crate::TokenStream + Copy) -> Self { Client { - get_handle_counters: HandleCounters::get, + handle_counters: &COUNTERS, run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| { run_client(bridge, |input| f(crate::TokenStream(Some(input))).0) }), @@ -360,7 +351,7 @@ impl Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream> { f: impl Fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream + Copy, ) -> Self { Client { - get_handle_counters: HandleCounters::get, + handle_counters: &COUNTERS, run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| { run_client(bridge, |(input, input2)| { f(crate::TokenStream(Some(input)), crate::TokenStream(Some(input2))).0 diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 692b6038a3872..97e5a603c3ac9 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -400,10 +400,10 @@ impl client::Client { S: Server, S::TokenStream: Default, { - let client::Client { get_handle_counters, run, _marker } = *self; + let client::Client { handle_counters, run, _marker } = *self; run_server( strategy, - get_handle_counters(), + handle_counters, server, as Types>::TokenStream::mark(input), run, @@ -426,10 +426,10 @@ impl client::Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream S: Server, S::TokenStream: Default, { - let client::Client { get_handle_counters, run, _marker } = *self; + let client::Client { handle_counters, run, _marker } = *self; run_server( strategy, - get_handle_counters(), + handle_counters, server, ( as Types>::TokenStream::mark(input), diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 72b597a8083ba..ae47bb7adf49a 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -32,7 +32,6 @@ #![feature(restricted_std)] #![feature(rustc_attrs)] #![feature(min_specialization)] -#![feature(strict_provenance)] #![recursion_limit = "256"] #![allow(internal_features)] #![deny(ffi_unwind_calls)] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 358bd25ff1bc6..bfb9df7d78145 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,8 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "0.1.133" } -profiler_builtins = { path = "../profiler_builtins", optional = true } +compiler_builtins = { version = "0.1.134" } unwind = { path = "../unwind" } hashbrown = { version = "0.15", default-features = false, features = [ 'rustc-dep-of-std', @@ -39,7 +38,7 @@ miniz_oxide = { version = "0.7.0", optional = true, default-features = false } addr2line = { version = "0.22.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "0.2.159", default-features = false, features = [ +libc = { version = "0.2.161", default-features = false, features = [ 'rustc-dep-of-std', ], public = true } @@ -98,7 +97,6 @@ backtrace = [ ] panic-unwind = ["panic_unwind"] -profiler = ["profiler_builtins"] compiler-builtins-c = ["alloc/compiler-builtins-c"] compiler-builtins-mem = ["alloc/compiler-builtins-mem"] compiler-builtins-no-asm = ["alloc/compiler-builtins-no-asm"] diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 675140ff18f5f..8a0d2a7f5cfcf 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2618,7 +2618,7 @@ pub fn remove_dir_all>(path: P) -> io::Result<()> { /// # Platform-specific behavior /// /// This function currently corresponds to the `opendir` function on Unix -/// and the `FindFirstFile` function on Windows. Advancing the iterator +/// and the `FindFirstFileEx` function on Windows. Advancing the iterator /// currently corresponds to `readdir` on Unix and `FindNextFile` on Windows. /// Note that, this [may change in the future][changes]. /// diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 453b2708daab9..30d43c8bbfd8c 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -2146,10 +2146,13 @@ mod unsafe_keyword {} #[doc(keyword = "use")] // -/// Import or rename items from other crates or modules. +/// Import or rename items from other crates or modules, or specify precise capturing +/// with `use<..>`. /// -/// Usually a `use` keyword is used to shorten the path required to refer to a module item. -/// The keyword may appear in modules, blocks and even functions, usually at the top. +/// ## Importing items +/// +/// The `use` keyword is employed to shorten the path required to refer to a module item. +/// The keyword may appear in modules, blocks, and even functions, typically at the top. /// /// The most basic usage of the keyword is `use path::to::item;`, /// though a number of convenient shortcuts are supported: @@ -2190,19 +2193,48 @@ mod unsafe_keyword {} /// // Compiles. /// let _ = VariantA; /// -/// // Does not compile ! +/// // Does not compile! /// let n = new(); /// ``` /// -/// For more information on `use` and paths in general, see the [Reference]. +/// For more information on `use` and paths in general, see the [Reference][ref-use-decls]. /// /// The differences about paths and the `use` keyword between the 2015 and 2018 editions -/// can also be found in the [Reference]. +/// can also be found in the [Reference][ref-use-decls]. +/// +/// ## Precise capturing +/// +/// The `use<..>` syntax is used within certain `impl Trait` bounds to control which generic +/// parameters are captured. This is important for return-position `impl Trait` (RPIT) types, +/// as it affects borrow checking by controlling which generic parameters can be used in the +/// hidden type. +/// +/// For example, the following function demonstrates an error without precise capturing in +/// Rust 2021 and earlier editions: +/// +/// ```rust,compile_fail,edition2021 +/// fn f(x: &()) -> impl Sized { x } +/// ``` +/// +/// By using `use<'_>` for precise capturing, it can be resolved: +/// +/// ```rust +/// fn f(x: &()) -> impl Sized + use<'_> { x } +/// ``` +/// +/// This syntax specifies that the elided lifetime be captured and therefore available for +/// use in the hidden type. +/// +/// In Rust 2024, opaque types automatically capture all lifetime parameters in scope. +/// `use<..>` syntax serves as an important way of opting-out of that default. +/// +/// For more details about precise capturing, see the [Reference][ref-impl-trait]. /// /// [`crate`]: keyword.crate.html /// [`self`]: keyword.self.html /// [`super`]: keyword.super.html -/// [Reference]: ../reference/items/use-declarations.html +/// [ref-use-decls]: ../reference/items/use-declarations.html +/// [ref-impl-trait]: ../reference/types/impl-trait.html mod use_keyword {} #[doc(keyword = "where")] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 3ab652383689c..8c1a3c7682930 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -279,6 +279,8 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(strict_provenance))] +#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] @@ -336,7 +338,6 @@ #![feature(error_iter)] #![feature(exact_size_is_empty)] #![feature(exclusive_wrapper)] -#![feature(exposed_provenance)] #![feature(extend_one)] #![feature(float_gamma)] #![feature(float_minimum_maximum)] @@ -362,7 +363,6 @@ #![feature(slice_range)] #![feature(std_internals)] #![feature(str_internals)] -#![feature(strict_provenance)] #![feature(strict_provenance_atomic_ptr)] #![feature(ub_checks)] // tidy-alphabetical-end diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 9aadd9491169f..ef5adaf229088 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -154,6 +154,7 @@ pub trait CommandExt: Sealed { /// required to gracefully handle errors it is recommended to use the /// cross-platform `spawn` instead. #[stable(feature = "process_exec2", since = "1.9.0")] + #[must_use] fn exec(&mut self) -> io::Error; /// Set executable argument diff --git a/library/std/src/os/xous/ffi.rs b/library/std/src/os/xous/ffi.rs index 1a4a940bf358a..1db314e9ddad7 100644 --- a/library/std/src/os/xous/ffi.rs +++ b/library/std/src/os/xous/ffi.rs @@ -615,7 +615,7 @@ pub(crate) fn thread_id() -> Result { /// An error is generated if the `knob` is not a valid limit, or if the call /// would not succeed. pub(crate) fn adjust_limit(knob: Limits, current: usize, new: usize) -> Result { - let mut a0 = Syscall::JoinThread as usize; + let mut a0 = Syscall::AdjustProcessLimit as usize; let mut a1 = knob as usize; let a2 = current; let a3 = new; diff --git a/library/std/src/random.rs b/library/std/src/random.rs index 604fa4df11066..cdb88c795bfd4 100644 --- a/library/std/src/random.rs +++ b/library/std/src/random.rs @@ -40,6 +40,7 @@ use crate::sys::random as sys; /// Horizon | `getrandom` shim /// Hurd, L4Re, QNX | `/dev/urandom` /// Redox | `/scheme/rand` +/// RTEMS | [`arc4random_buf`](https://docs.rtems.org/branches/master/bsp-howto/getentropy.html) /// SGX | [`rdrand`](https://en.wikipedia.org/wiki/RDRAND) /// SOLID | `SOLID_RNG_SampleRandomBytes` /// TEEOS | `TEE_GenerateRandom` diff --git a/library/std/src/sys/alloc/xous.rs b/library/std/src/sys/alloc/xous.rs index 9ea43445d0206..321d30e0b11b2 100644 --- a/library/std/src/sys/alloc/xous.rs +++ b/library/std/src/sys/alloc/xous.rs @@ -1,3 +1,6 @@ +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use crate::alloc::{GlobalAlloc, Layout, System}; #[cfg(not(test))] diff --git a/library/std/src/sys/pal/hermit/futex.rs b/library/std/src/sys/pal/hermit/futex.rs index 21c5facd52fbd..670383b45aca9 100644 --- a/library/std/src/sys/pal/hermit/futex.rs +++ b/library/std/src/sys/pal/hermit/futex.rs @@ -3,9 +3,14 @@ use crate::ptr::null; use crate::sync::atomic::AtomicU32; use crate::time::Duration; +/// An atomic for use as a futex that is at least 32-bits but may be larger +pub type Futex = AtomicU32; +/// Must be the underlying type of Futex +pub type Primitive = u32; + /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallAtomic = AtomicU32; -/// Must be the underlying type of SmallAtomic +pub type SmallFutex = AtomicU32; +/// Must be the underlying type of SmallFutex pub type SmallPrimitive = u32; pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool { diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 4ced7065c826d..abc8e69a285f3 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -10,7 +10,7 @@ //! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) use r_efi::efi::{self, Guid}; -use r_efi::protocols::{device_path, device_path_to_text}; +use r_efi::protocols::{device_path, device_path_to_text, shell}; use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_io_error}; @@ -424,3 +424,24 @@ pub(crate) fn os_string_to_raw(s: &OsStr) -> Option> { let temp = s.encode_wide().chain(Some(0)).collect::>(); if temp[..temp.len() - 1].contains(&0) { None } else { Some(temp) } } + +pub(crate) fn open_shell() -> Option> { + static LAST_VALID_HANDLE: AtomicPtr = + AtomicPtr::new(crate::ptr::null_mut()); + + if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { + if let Ok(protocol) = open_protocol::(handle, shell::PROTOCOL_GUID) { + return Some(protocol); + } + } + + let handles = locate_handles(shell::PROTOCOL_GUID).ok()?; + for handle in handles { + if let Ok(protocol) = open_protocol::(handle, shell::PROTOCOL_GUID) { + LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); + return Some(protocol); + } + } + + None +} diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 4eb7698b43aa9..27395f7c3c0b3 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -125,7 +125,7 @@ pub fn error_string(errno: RawOsError) -> String { } pub fn getcwd() -> io::Result { - match uefi_shell::open_shell() { + match helpers::open_shell() { Some(shell) => { // SAFETY: path_ptr is managed by UEFI shell and should not be deallocated let path_ptr = unsafe { ((*shell.as_ptr()).get_cur_dir)(crate::ptr::null_mut()) }; @@ -144,7 +144,7 @@ pub fn getcwd() -> io::Result { } pub fn chdir(p: &path::Path) -> io::Result<()> { - let shell = uefi_shell::open_shell().ok_or(unsupported_err())?; + let shell = helpers::open_shell().ok_or(unsupported_err())?; let mut p = helpers::os_string_to_raw(p.as_os_str()) .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))?; @@ -192,44 +192,58 @@ pub fn current_exe() -> io::Result { helpers::device_path_to_text(protocol).map(PathBuf::from) } -pub struct Env(!); +pub struct EnvStrDebug<'a> { + iter: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut list = f.debug_list(); + for (a, b) in self.iter { + list.entry(&(a.to_str().unwrap(), b.to_str().unwrap())); + } + list.finish() + } +} + +pub struct Env(crate::vec::IntoIter<(OsString, OsString)>); impl Env { // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} + EnvStrDebug { iter: self.0.as_slice() } } } impl Iterator for Env { type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { - self.0 + self.0.next() } } impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) } } pub fn env() -> Env { - panic!("not supported on this platform") + let env = uefi_env::get_all().expect("not supported on this platform"); + Env(env.into_iter()) } -pub fn getenv(_: &OsStr) -> Option { - None +pub fn getenv(key: &OsStr) -> Option { + uefi_env::get(key) } -pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) +pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> { + uefi_env::set(key, val) } -pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) +pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> { + uefi_env::unset(key) } pub fn temp_dir() -> PathBuf { @@ -261,36 +275,84 @@ pub fn getpid() -> u32 { panic!("no pids on this platform") } -mod uefi_shell { - use r_efi::protocols::shell; - - use super::super::helpers; +mod uefi_env { + use crate::ffi::{OsStr, OsString}; + use crate::io; + use crate::os::uefi::ffi::OsStringExt; use crate::ptr::NonNull; - use crate::sync::atomic::{AtomicPtr, Ordering}; - - pub fn open_shell() -> Option> { - static LAST_VALID_HANDLE: AtomicPtr = - AtomicPtr::new(crate::ptr::null_mut()); - - if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { - if let Ok(protocol) = helpers::open_protocol::( - handle, - r_efi::protocols::shell::PROTOCOL_GUID, - ) { - return Some(protocol); - } + use crate::sys::{helpers, unsupported_err}; + + pub(crate) fn get(key: &OsStr) -> Option { + let shell = helpers::open_shell()?; + let mut key_ptr = helpers::os_string_to_raw(key)?; + unsafe { get_raw(shell, key_ptr.as_mut_ptr()) } + } + + pub(crate) fn set(key: &OsStr, val: &OsStr) -> io::Result<()> { + let mut key_ptr = helpers::os_string_to_raw(key) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Key"))?; + let mut val_ptr = helpers::os_string_to_raw(val) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Value"))?; + unsafe { set_raw(key_ptr.as_mut_ptr(), val_ptr.as_mut_ptr()) } + } + + pub(crate) fn unset(key: &OsStr) -> io::Result<()> { + let mut key_ptr = helpers::os_string_to_raw(key) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Key"))?; + unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) } + } + + pub(crate) fn get_all() -> io::Result> { + let shell = helpers::open_shell().ok_or(unsupported_err())?; + + let mut vars = Vec::new(); + let val = unsafe { ((*shell.as_ptr()).get_env)(crate::ptr::null_mut()) }; + + if val.is_null() { + return Ok(vars); } - let handles = helpers::locate_handles(shell::PROTOCOL_GUID).ok()?; - for handle in handles { - if let Ok(protocol) = - helpers::open_protocol::(handle, shell::PROTOCOL_GUID) - { - LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); - return Some(protocol); + let mut start = 0; + + // UEFI Shell returns all keys seperated by NULL. + // End of string is denoted by two NULLs + for i in 0.. { + if unsafe { *val.add(i) } == 0 { + // Two NULL signal end of string + if i == start { + break; + } + + let key = OsString::from_wide(unsafe { + crate::slice::from_raw_parts(val.add(start), i - start) + }); + // SAFETY: val.add(start) is always NULL terminated + let val = unsafe { get_raw(shell, val.add(start)) } + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Value"))?; + + vars.push((key, val)); + start = i + 1; } } - None + Ok(vars) + } + + unsafe fn get_raw( + shell: NonNull, + key_ptr: *mut r_efi::efi::Char16, + ) -> Option { + let val = unsafe { ((*shell.as_ptr()).get_env)(key_ptr) }; + helpers::os_string_from_raw(val) + } + + unsafe fn set_raw( + key_ptr: *mut r_efi::efi::Char16, + val_ptr: *mut r_efi::efi::Char16, + ) -> io::Result<()> { + let shell = helpers::open_shell().ok_or(unsupported_err())?; + let r = + unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, r_efi::efi::Boolean::FALSE) }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } } diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index 567577b2b4d23..f1f843a5f7ae7 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -189,7 +189,7 @@ cfg_has_statx! {{ // See: https://github.com/rust-lang/rust/issues/65662 // // FIXME what about transient conditions like `ENOMEM`? - let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut())) + let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_BASIC_STATS | libc::STATX_BTIME, ptr::null_mut())) .err() .and_then(|e| e.raw_os_error()); if err2 == Some(libc::EFAULT) { @@ -910,7 +910,7 @@ impl DirEntry { fd, name, libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, ) } { return ret; } @@ -1194,7 +1194,7 @@ impl File { fd, c"".as_ptr() as *const c_char, libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, ) } { return ret; } @@ -1767,7 +1767,7 @@ pub fn stat(p: &Path) -> io::Result { libc::AT_FDCWD, p.as_ptr(), libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, ) } { return ret; } @@ -1786,7 +1786,7 @@ pub fn lstat(p: &Path) -> io::Result { libc::AT_FDCWD, p.as_ptr(), libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, ) } { return ret; } diff --git a/library/std/src/sys/pal/unix/futex.rs b/library/std/src/sys/pal/unix/futex.rs index cc725045c4810..0fc765dc87a5d 100644 --- a/library/std/src/sys/pal/unix/futex.rs +++ b/library/std/src/sys/pal/unix/futex.rs @@ -11,9 +11,14 @@ use crate::sync::atomic::AtomicU32; use crate::time::Duration; +/// An atomic for use as a futex that is at least 32-bits but may be larger +pub type Futex = AtomicU32; +/// Must be the underlying type of Futex +pub type Primitive = u32; + /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallAtomic = AtomicU32; -/// Must be the underlying type of SmallAtomic +pub type SmallFutex = AtomicU32; +/// Must be the underlying type of SmallFutex pub type SmallPrimitive = u32; /// Waits for a `futex_wake` operation to wake us. diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index ac0858e1de876..69b31da427fcb 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -265,9 +265,7 @@ mod imp { /// Modern kernels on modern hardware can have dynamic signal stack sizes. #[cfg(any(target_os = "linux", target_os = "android"))] fn sigstack_size() -> usize { - // FIXME: reuse const from libc when available? - const AT_MINSIGSTKSZ: crate::ffi::c_ulong = 51; - let dynamic_sigstksz = unsafe { libc::getauxval(AT_MINSIGSTKSZ) }; + let dynamic_sigstksz = unsafe { libc::getauxval(libc::AT_MINSIGSTKSZ) }; // If getauxval couldn't find the entry, it returns 0, // so take the higher of the "constant" and auxval. // This transparently supports older kernels which don't provide AT_MINSIGSTKSZ diff --git a/library/std/src/sys/pal/wasm/atomics/futex.rs b/library/std/src/sys/pal/wasm/atomics/futex.rs index 42913a99ee9d6..bdad0da73f0a5 100644 --- a/library/std/src/sys/pal/wasm/atomics/futex.rs +++ b/library/std/src/sys/pal/wasm/atomics/futex.rs @@ -6,9 +6,14 @@ use core::arch::wasm64 as wasm; use crate::sync::atomic::AtomicU32; use crate::time::Duration; +/// An atomic for use as a futex that is at least 32-bits but may be larger +pub type Futex = AtomicU32; +/// Must be the underlying type of Futex +pub type Primitive = u32; + /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallAtomic = AtomicU32; -/// Must be the underlying type of SmallAtomic +pub type SmallFutex = AtomicU32; +/// Must be the underlying type of SmallFutex pub type SmallPrimitive = u32; /// Wait for a futex_wake operation to wake us. diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index 9c2e4500da068..192c95fd203c6 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2337,7 +2337,9 @@ Windows.Win32.Storage.FileSystem.FileStandardInfo Windows.Win32.Storage.FileSystem.FileStorageInfo Windows.Win32.Storage.FileSystem.FileStreamInfo Windows.Win32.Storage.FileSystem.FindClose -Windows.Win32.Storage.FileSystem.FindFirstFileW +Windows.Win32.Storage.FileSystem.FindExInfoBasic +Windows.Win32.Storage.FileSystem.FindExSearchNameMatch +Windows.Win32.Storage.FileSystem.FindFirstFileExW Windows.Win32.Storage.FileSystem.FindNextFileW Windows.Win32.Storage.FileSystem.FlushFileBuffers Windows.Win32.Storage.FileSystem.GetFileAttributesW diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index ab5f8919d7af6..52444c2c009ed 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -26,7 +26,7 @@ windows_targets::link!("kernel32.dll" "system" fn DeviceIoControl(hdevice : HAND windows_targets::link!("kernel32.dll" "system" fn DuplicateHandle(hsourceprocesshandle : HANDLE, hsourcehandle : HANDLE, htargetprocesshandle : HANDLE, lptargethandle : *mut HANDLE, dwdesiredaccess : u32, binherithandle : BOOL, dwoptions : DUPLICATE_HANDLE_OPTIONS) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn ExitProcess(uexitcode : u32) -> !); windows_targets::link!("kernel32.dll" "system" fn FindClose(hfindfile : HANDLE) -> BOOL); -windows_targets::link!("kernel32.dll" "system" fn FindFirstFileW(lpfilename : PCWSTR, lpfindfiledata : *mut WIN32_FIND_DATAW) -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn FindFirstFileExW(lpfilename : PCWSTR, finfolevelid : FINDEX_INFO_LEVELS, lpfindfiledata : *mut core::ffi::c_void, fsearchop : FINDEX_SEARCH_OPS, lpsearchfilter : *const core::ffi::c_void, dwadditionalflags : FIND_FIRST_EX_FLAGS) -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn FindNextFileW(hfindfile : HANDLE, lpfindfiledata : *mut WIN32_FIND_DATAW) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn FlushFileBuffers(hfile : HANDLE) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn FormatMessageW(dwflags : FORMAT_MESSAGE_OPTIONS, lpsource : *const core::ffi::c_void, dwmessageid : u32, dwlanguageid : u32, lpbuffer : PWSTR, nsize : u32, arguments : *const *const i8) -> u32); @@ -2501,6 +2501,9 @@ pub const FILE_WRITE_ATTRIBUTES: FILE_ACCESS_RIGHTS = 256u32; pub const FILE_WRITE_DATA: FILE_ACCESS_RIGHTS = 2u32; pub const FILE_WRITE_EA: FILE_ACCESS_RIGHTS = 16u32; pub const FILE_WRITE_THROUGH: NTCREATEFILE_CREATE_OPTIONS = 2u32; +pub type FINDEX_INFO_LEVELS = i32; +pub type FINDEX_SEARCH_OPS = i32; +pub type FIND_FIRST_EX_FLAGS = u32; pub const FIONBIO: i32 = -2147195266i32; #[repr(C)] #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] @@ -2565,6 +2568,8 @@ pub const FileRenameInfoEx: FILE_INFO_BY_HANDLE_CLASS = 22i32; pub const FileStandardInfo: FILE_INFO_BY_HANDLE_CLASS = 1i32; pub const FileStorageInfo: FILE_INFO_BY_HANDLE_CLASS = 16i32; pub const FileStreamInfo: FILE_INFO_BY_HANDLE_CLASS = 7i32; +pub const FindExInfoBasic: FINDEX_INFO_LEVELS = 1i32; +pub const FindExSearchNameMatch: FINDEX_SEARCH_OPS = 0i32; pub type GENERIC_ACCESS_RIGHTS = u32; pub const GENERIC_ALL: GENERIC_ACCESS_RIGHTS = 268435456u32; pub const GENERIC_EXECUTE: GENERIC_ACCESS_RIGHTS = 536870912u32; @@ -3307,7 +3312,6 @@ pub struct XSAVE_FORMAT { pub XmmRegisters: [M128A; 8], pub Reserved4: [u8; 224], } - #[cfg(target_arch = "arm")] #[repr(C)] pub struct WSADATA { diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index aab471e28eaeb..b237fa481e287 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -114,7 +114,7 @@ impl Iterator for ReadDir { fn next(&mut self) -> Option> { if self.handle.0 == c::INVALID_HANDLE_VALUE { // This iterator was initialized with an `INVALID_HANDLE_VALUE` as its handle. - // Simply return `None` because this is only the case when `FindFirstFileW` in + // Simply return `None` because this is only the case when `FindFirstFileExW` in // the construction of this iterator returns `ERROR_FILE_NOT_FOUND` which means // no matchhing files can be found. return None; @@ -1047,8 +1047,22 @@ pub fn readdir(p: &Path) -> io::Result { let path = maybe_verbatim(&star)?; unsafe { - let mut wfd = mem::zeroed(); - let find_handle = c::FindFirstFileW(path.as_ptr(), &mut wfd); + let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed(); + // this is like FindFirstFileW (see https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfileexw), + // but with FindExInfoBasic it should skip filling WIN32_FIND_DATAW.cAlternateFileName + // (see https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-win32_find_dataw) + // (which will be always null string value and currently unused) and should be faster. + // + // We can pass FIND_FIRST_EX_LARGE_FETCH to dwAdditionalFlags to speed up things more, + // but as we don't know user's use profile of this function, lets be conservative. + let find_handle = c::FindFirstFileExW( + path.as_ptr(), + c::FindExInfoBasic, + &mut wfd as *mut _ as _, + c::FindExSearchNameMatch, + ptr::null(), + 0, + ); if find_handle != c::INVALID_HANDLE_VALUE { Ok(ReadDir { @@ -1057,7 +1071,7 @@ pub fn readdir(p: &Path) -> io::Result { first: Some(wfd), }) } else { - // The status `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileW` function + // The status `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileExW` function // if no matching files can be found, but not necessarily that the path to find the // files in does not exist. // @@ -1079,7 +1093,7 @@ pub fn readdir(p: &Path) -> io::Result { // Just return the error constructed from the raw OS error if the above is not the case. // - // Note: `ERROR_PATH_NOT_FOUND` would have been returned by the `FindFirstFileW` function + // Note: `ERROR_PATH_NOT_FOUND` would have been returned by the `FindFirstFileExW` function // when the path to search in does not exist in the first place. Err(Error::from_raw_os_error(last_error.code as i32)) } @@ -1220,7 +1234,7 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result { opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | reparse.as_flag()); // Attempt to open the file normally. - // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileW`. + // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileExW`. // If the fallback fails for any reason we return the original error. match File::open(path, &opts) { Ok(file) => file.file_attr(), @@ -1237,13 +1251,20 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result { unsafe { let path = maybe_verbatim(path)?; - // `FindFirstFileW` accepts wildcard file names. + // `FindFirstFileExW` accepts wildcard file names. // Fortunately wildcards are not valid file names and // `ERROR_SHARING_VIOLATION` means the file exists (but is locked) // therefore it's safe to assume the file name given does not // include wildcards. - let mut wfd = mem::zeroed(); - let handle = c::FindFirstFileW(path.as_ptr(), &mut wfd); + let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed(); + let handle = c::FindFirstFileExW( + path.as_ptr(), + c::FindExInfoBasic, + &mut wfd as *mut _ as _, + c::FindExSearchNameMatch, + ptr::null(), + 0, + ); if handle == c::INVALID_HANDLE_VALUE { // This can fail if the user does not have read access to the @@ -1253,7 +1274,7 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result { // We no longer need the find handle. c::FindClose(handle); - // `FindFirstFileW` reads the cached file information from the + // `FindFirstFileExW` reads the cached file information from the // directory. The downside is that this metadata may be outdated. let attrs = FileAttr::from(wfd); if reparse == ReparsePoint::Follow && attrs.file_type().is_symlink() { diff --git a/library/std/src/sys/pal/windows/futex.rs b/library/std/src/sys/pal/windows/futex.rs index 4d6c4df9a5a90..38afb8c043b3b 100644 --- a/library/std/src/sys/pal/windows/futex.rs +++ b/library/std/src/sys/pal/windows/futex.rs @@ -9,22 +9,27 @@ use core::{mem, ptr}; use super::api::{self, WinError}; use crate::sys::{c, dur2timeout}; +/// An atomic for use as a futex that is at least 32-bits but may be larger +pub type Futex = AtomicU32; +/// Must be the underlying type of Futex +pub type Primitive = u32; + /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallAtomic = AtomicU8; -/// Must be the underlying type of SmallAtomic +pub type SmallFutex = AtomicU8; +/// Must be the underlying type of SmallFutex pub type SmallPrimitive = u8; -pub unsafe trait Futex {} +pub unsafe trait Futexable {} pub unsafe trait Waitable { - type Atomic; + type Futex; } macro_rules! unsafe_waitable_int { ($(($int:ty, $atomic:ty)),*$(,)?) => { $( unsafe impl Waitable for $int { - type Atomic = $atomic; + type Futex = $atomic; } - unsafe impl Futex for $atomic {} + unsafe impl Futexable for $atomic {} )* }; } @@ -42,15 +47,15 @@ unsafe_waitable_int! { (usize, AtomicUsize), } unsafe impl Waitable for *const T { - type Atomic = AtomicPtr; + type Futex = AtomicPtr; } unsafe impl Waitable for *mut T { - type Atomic = AtomicPtr; + type Futex = AtomicPtr; } -unsafe impl Futex for AtomicPtr {} +unsafe impl Futexable for AtomicPtr {} pub fn wait_on_address( - address: &W::Atomic, + address: &W::Futex, compare: W, timeout: Option, ) -> bool { @@ -63,30 +68,30 @@ pub fn wait_on_address( } } -pub fn wake_by_address_single(address: &T) { +pub fn wake_by_address_single(address: &T) { unsafe { let addr = ptr::from_ref(address).cast::(); c::WakeByAddressSingle(addr); } } -pub fn wake_by_address_all(address: &T) { +pub fn wake_by_address_all(address: &T) { unsafe { let addr = ptr::from_ref(address).cast::(); c::WakeByAddressAll(addr); } } -pub fn futex_wait(futex: &W::Atomic, expected: W, timeout: Option) -> bool { +pub fn futex_wait(futex: &W::Futex, expected: W, timeout: Option) -> bool { // return false only on timeout wait_on_address(futex, expected, timeout) || api::get_last_error() != WinError::TIMEOUT } -pub fn futex_wake(futex: &T) -> bool { +pub fn futex_wake(futex: &T) -> bool { wake_by_address_single(futex); false } -pub fn futex_wake_all(futex: &T) { +pub fn futex_wake_all(futex: &T) { wake_by_address_all(futex) } diff --git a/library/std/src/sys/pal/xous/args.rs b/library/std/src/sys/pal/xous/args.rs new file mode 100644 index 0000000000000..00c44ca220a9e --- /dev/null +++ b/library/std/src/sys/pal/xous/args.rs @@ -0,0 +1,53 @@ +use crate::ffi::OsString; +use crate::sys::pal::xous::os::get_application_parameters; +use crate::sys::pal::xous::os::params::ArgumentList; +use crate::{fmt, vec}; + +pub struct Args { + parsed_args_list: vec::IntoIter, +} + +pub fn args() -> Args { + let Some(params) = get_application_parameters() else { + return Args { parsed_args_list: vec![].into_iter() }; + }; + + for param in params { + if let Ok(args) = ArgumentList::try_from(¶m) { + let mut parsed_args = vec![]; + for arg in args { + parsed_args.push(arg.into()); + } + return Args { parsed_args_list: parsed_args.into_iter() }; + } + } + Args { parsed_args_list: vec![].into_iter() } +} + +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.parsed_args_list.as_slice().fmt(f) + } +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option { + self.parsed_args_list.next() + } + fn size_hint(&self) -> (usize, Option) { + self.parsed_args_list.size_hint() + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { + self.parsed_args_list.next_back() + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.parsed_args_list.len() + } +} diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index b211e94db65d6..a64cd06856006 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -1,6 +1,5 @@ #![forbid(unsafe_op_in_unsafe_fn)] -#[path = "../unsupported/args.rs"] pub mod args; #[path = "../unsupported/env.rs"] pub mod env; diff --git a/library/std/src/sys/pal/xous/net/dns.rs b/library/std/src/sys/pal/xous/net/dns.rs index d0083c6183793..1a2b56b4da5d3 100644 --- a/library/std/src/sys/pal/xous/net/dns.rs +++ b/library/std/src/sys/pal/xous/net/dns.rs @@ -6,6 +6,7 @@ use crate::os::xous::ffi::lend_mut; use crate::os::xous::services::{DnsLendMut, dns_server}; pub struct DnsError { + #[allow(dead_code)] pub code: u8, } diff --git a/library/std/src/sys/pal/xous/net/mod.rs b/library/std/src/sys/pal/xous/net/mod.rs index dd8b765aa74ae..3e18ed24208d3 100644 --- a/library/std/src/sys/pal/xous/net/mod.rs +++ b/library/std/src/sys/pal/xous/net/mod.rs @@ -60,6 +60,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in { + #[allow(dead_code)] pub sin_family: sa_family_t, pub sin_port: u16, pub sin_addr: in_addr, @@ -72,6 +73,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in6 { + #[allow(dead_code)] pub sin6_family: sa_family_t, pub sin6_port: u16, pub sin6_addr: in6_addr, diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index 8f8f35428c487..b0ab01a6383d2 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -1,29 +1,35 @@ use super::unsupported; +use crate::collections::HashMap; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::os::xous::ffi::Error as XousError; use crate::path::{self, PathBuf}; -use crate::{fmt, io}; +use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; +use crate::sync::{Mutex, Once}; +use crate::{fmt, io, vec}; + +pub(crate) mod params; + +static PARAMS_ADDRESS: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); #[cfg(not(test))] #[cfg(feature = "panic_unwind")] mod eh_unwinding { - pub(crate) struct EhFrameFinder(usize /* eh_frame */); - pub(crate) static mut EH_FRAME_SETTINGS: EhFrameFinder = EhFrameFinder(0); - impl EhFrameFinder { - pub(crate) unsafe fn init(&mut self, eh_frame: usize) { - unsafe { - EH_FRAME_SETTINGS.0 = eh_frame; - } - } - } + pub(crate) struct EhFrameFinder; + pub(crate) static mut EH_FRAME_ADDRESS: usize = 0; + pub(crate) static EH_FRAME_SETTINGS: EhFrameFinder = EhFrameFinder; + unsafe impl unwind::EhFrameFinder for EhFrameFinder { fn find(&self, _pc: usize) -> Option { - Some(unwind::FrameInfo { - text_base: None, - kind: unwind::FrameInfoKind::EhFrame(self.0), - }) + if unsafe { EH_FRAME_ADDRESS == 0 } { + None + } else { + Some(unwind::FrameInfo { + text_base: None, + kind: unwind::FrameInfoKind::EhFrame(unsafe { EH_FRAME_ADDRESS }), + }) + } } } } @@ -41,12 +47,21 @@ mod c_compat { } #[no_mangle] - pub extern "C" fn _start(eh_frame: usize) { + pub extern "C" fn _start(eh_frame: usize, params_address: usize) { #[cfg(feature = "panic_unwind")] - unsafe { - super::eh_unwinding::EH_FRAME_SETTINGS.init(eh_frame); + { + unsafe { super::eh_unwinding::EH_FRAME_ADDRESS = eh_frame }; unwind::set_custom_eh_frame_finder(&super::eh_unwinding::EH_FRAME_SETTINGS).ok(); } + + if params_address != 0 { + let params_address = crate::ptr::with_exposed_provenance_mut::(params_address); + if unsafe { + super::params::ApplicationParameters::new_from_ptr(params_address).is_some() + } { + super::PARAMS_ADDRESS.store(params_address, core::sync::atomic::Ordering::Relaxed); + } + } exit(unsafe { main() }); } @@ -116,44 +131,103 @@ pub fn current_exe() -> io::Result { unsupported() } -pub struct Env(!); +pub(crate) fn get_application_parameters() -> Option { + let params_address = PARAMS_ADDRESS.load(Ordering::Relaxed); + unsafe { params::ApplicationParameters::new_from_ptr(params_address) } +} + +// ---------- Environment handling ---------- // +static ENV: AtomicUsize = AtomicUsize::new(0); +static ENV_INIT: Once = Once::new(); +type EnvStore = Mutex>; + +fn get_env_store() -> &'static EnvStore { + ENV_INIT.call_once(|| { + let env_store = EnvStore::default(); + if let Some(params) = get_application_parameters() { + for param in params { + if let Ok(envs) = params::EnvironmentBlock::try_from(¶m) { + let mut env_store = env_store.lock().unwrap(); + for env in envs { + env_store.insert(env.key.into(), env.value.into()); + } + break; + } + } + } + ENV.store(Box::into_raw(Box::new(env_store)) as _, Ordering::Relaxed) + }); + unsafe { &*core::ptr::with_exposed_provenance::(ENV.load(Ordering::Relaxed)) } +} + +pub struct Env { + iter: vec::IntoIter<(OsString, OsString)>, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. +pub struct EnvStrDebug<'a> { + slice: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { slice } = self; + f.debug_list() + .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) + .finish() + } +} impl Env { // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} + let Self { iter } = self; + EnvStrDebug { slice: iter.as_slice() } } } impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + f.debug_list().entries(iter.as_slice()).finish() } } +impl !Send for Env {} +impl !Sync for Env {} + impl Iterator for Env { type Item = (OsString, OsString); fn next(&mut self) -> Option<(OsString, OsString)> { - self.0 + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() } } pub fn env() -> Env { - panic!("not supported on this platform") + let clone_to_vec = |map: &HashMap| -> Vec<_> { + map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() + }; + + let iter = clone_to_vec(&*get_env_store().lock().unwrap()).into_iter(); + Env { iter } } -pub fn getenv(_: &OsStr) -> Option { - None +pub fn getenv(k: &OsStr) -> Option { + get_env_store().lock().unwrap().get(k).cloned() } -pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let (k, v) = (k.to_owned(), v.to_owned()); + get_env_store().lock().unwrap().insert(k, v); + Ok(()) } -pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { + get_env_store().lock().unwrap().remove(k); + Ok(()) } pub fn temp_dir() -> PathBuf { diff --git a/library/std/src/sys/pal/xous/os/params.rs b/library/std/src/sys/pal/xous/os/params.rs new file mode 100644 index 0000000000000..0d02cf35477f9 --- /dev/null +++ b/library/std/src/sys/pal/xous/os/params.rs @@ -0,0 +1,271 @@ +/// Xous passes a pointer to the parameter block as the second argument. +/// This is used for passing flags such as environment variables. The +/// format of the argument block is: +/// +/// #[repr(C)] +/// struct BlockHeader { +/// /// Magic number that identifies this block. Must be printable ASCII. +/// magic: [u8; 4], +/// +/// /// The size of the data block. Does not include this header. May be 0. +/// size: u32, +/// +/// /// The contents of this block. Varies depending on the block type. +/// data: [u8; 0], +/// } +/// +/// There is a BlockHeader at the start that has magic `AppP`, and the data +/// that follows is the number of blocks present: +/// +/// #[repr(C)] +/// struct ApplicationParameters { +/// magic: b"AppP", +/// size: 4u32, +/// +/// /// The size of the entire application slice, in bytes, including all headers +/// length: u32, +/// +/// /// Number of application parameters present. Must be at least 1 (this block) +/// entries: (parameter_count as u32).to_bytes_le(), +/// } +/// +/// #[repr(C)] +/// struct EnvironmentBlock { +/// magic: b"EnvB", +/// +/// /// Total number of bytes, excluding this header +/// size: 2+data.len(), +/// +/// /// The number of environment variables +/// count: u16, +/// +/// /// Environment variable iteration +/// data: [u8; 0], +/// } +/// +/// Environment variables are present in an `EnvB` block. The `data` section is +/// a sequence of bytes of the form: +/// +/// (u16 /* key_len */; [0u8; key_len as usize] /* key */, +/// u16 /* val_len */ [0u8; val_len as usize]) +/// +/// #[repr(C)] +/// struct ArgumentList { +/// magic: b"ArgL", +/// +/// /// Total number of bytes, excluding this header +/// size: 2+data.len(), +/// +/// /// The number of arguments variables +/// count: u16, +/// +/// /// Argument variable iteration +/// data: [u8; 0], +/// } +/// +/// Args are just an array of strings that represent command line arguments. +/// They are a sequence of the form: +/// +/// (u16 /* val_len */ [0u8; val_len as usize]) +use core::slice; + +use crate::ffi::OsString; + +/// Magic number indicating we have an environment block +const ENV_MAGIC: [u8; 4] = *b"EnvB"; + +/// Command line arguments list +const ARGS_MAGIC: [u8; 4] = *b"ArgL"; + +/// Magic number indicating the loader has passed application parameters +const PARAMS_MAGIC: [u8; 4] = *b"AppP"; + +#[cfg(test)] +mod tests; + +pub(crate) struct ApplicationParameters { + data: &'static [u8], + offset: usize, + _entries: usize, +} + +impl ApplicationParameters { + pub(crate) unsafe fn new_from_ptr(data: *const u8) -> Option { + if data.is_null() { + return None; + } + + let magic = unsafe { core::slice::from_raw_parts(data, 4) }; + let block_length = unsafe { + u32::from_le_bytes(slice::from_raw_parts(data.add(4), 4).try_into().ok()?) as usize + }; + let data_length = unsafe { + u32::from_le_bytes(slice::from_raw_parts(data.add(8), 4).try_into().ok()?) as usize + }; + let entries = unsafe { + u32::from_le_bytes(slice::from_raw_parts(data.add(12), 4).try_into().ok()?) as usize + }; + + // Check for the main header + if data_length < 16 || magic != PARAMS_MAGIC || block_length != 8 { + return None; + } + + let data = unsafe { slice::from_raw_parts(data, data_length) }; + + Some(ApplicationParameters { data, offset: 0, _entries: entries }) + } +} + +impl Iterator for ApplicationParameters { + type Item = ApplicationParameter; + + fn next(&mut self) -> Option { + // Fetch magic, ensuring we don't run off the end + if self.offset + 4 > self.data.len() { + return None; + } + let magic = &self.data[self.offset..self.offset + 4]; + self.offset += 4; + + // Fetch header size + if self.offset + 4 > self.data.len() { + return None; + } + let size = u32::from_le_bytes(self.data[self.offset..self.offset + 4].try_into().unwrap()) + as usize; + self.offset += 4; + + // Fetch data contents + if self.offset + size > self.data.len() { + return None; + } + let data = &self.data[self.offset..self.offset + size]; + self.offset += size; + + Some(ApplicationParameter { data, magic: magic.try_into().unwrap() }) + } +} + +pub(crate) struct ApplicationParameter { + data: &'static [u8], + magic: [u8; 4], +} + +pub(crate) struct ApplicationParameterError; + +pub(crate) struct EnvironmentBlock { + _count: usize, + data: &'static [u8], + offset: usize, +} + +impl TryFrom<&ApplicationParameter> for EnvironmentBlock { + type Error = ApplicationParameterError; + + fn try_from(value: &ApplicationParameter) -> Result { + if value.data.len() < 2 || value.magic != ENV_MAGIC { + return Err(ApplicationParameterError); + } + + let count = u16::from_le_bytes(value.data[0..2].try_into().unwrap()) as usize; + + Ok(EnvironmentBlock { data: &value.data[2..], offset: 0, _count: count }) + } +} + +pub(crate) struct EnvironmentEntry { + pub key: &'static str, + pub value: &'static str, +} + +impl Iterator for EnvironmentBlock { + type Item = EnvironmentEntry; + + fn next(&mut self) -> Option { + if self.offset + 2 > self.data.len() { + return None; + } + let key_len = + u16::from_le_bytes(self.data[self.offset..self.offset + 2].try_into().ok()?) as usize; + self.offset += 2; + + if self.offset + key_len > self.data.len() { + return None; + } + let key = core::str::from_utf8(&self.data[self.offset..self.offset + key_len]).ok()?; + self.offset += key_len; + + if self.offset + 2 > self.data.len() { + return None; + } + let value_len = + u16::from_le_bytes(self.data[self.offset..self.offset + 2].try_into().ok()?) as usize; + self.offset += 2; + + if self.offset + value_len > self.data.len() { + return None; + } + let value = core::str::from_utf8(&self.data[self.offset..self.offset + value_len]).ok()?; + self.offset += value_len; + + Some(EnvironmentEntry { key, value }) + } +} + +pub(crate) struct ArgumentList { + data: &'static [u8], + _count: usize, + offset: usize, +} + +impl TryFrom<&ApplicationParameter> for ArgumentList { + type Error = ApplicationParameterError; + + fn try_from(value: &ApplicationParameter) -> Result { + if value.data.len() < 2 || value.magic != ARGS_MAGIC { + return Err(ApplicationParameterError); + } + let count = + u16::from_le_bytes(value.data[0..2].try_into().or(Err(ApplicationParameterError))?) + as usize; + Ok(ArgumentList { data: &value.data[2..], _count: count, offset: 0 }) + } +} + +pub(crate) struct ArgumentEntry { + value: &'static str, +} + +impl Into<&str> for ArgumentEntry { + fn into(self) -> &'static str { + self.value + } +} + +impl Into for ArgumentEntry { + fn into(self) -> OsString { + self.value.into() + } +} + +impl Iterator for ArgumentList { + type Item = ArgumentEntry; + + fn next(&mut self) -> Option { + if self.offset + 2 > self.data.len() { + return None; + } + let value_len = + u16::from_le_bytes(self.data[self.offset..self.offset + 2].try_into().ok()?) as usize; + self.offset += 2; + + if self.offset + value_len > self.data.len() { + return None; + } + let value = core::str::from_utf8(&self.data[self.offset..self.offset + value_len]).ok()?; + self.offset += value_len; + + Some(ArgumentEntry { value }) + } +} diff --git a/library/std/src/sys/pal/xous/os/params/tests.rs b/library/std/src/sys/pal/xous/os/params/tests.rs new file mode 100644 index 0000000000000..0ef04ee30919f --- /dev/null +++ b/library/std/src/sys/pal/xous/os/params/tests.rs @@ -0,0 +1,75 @@ +use super::*; +use crate::collections::HashMap; +use crate::io::Write; + +fn create_args_test() -> std::io::Result> { + let mut sample_data = vec![]; + let mut h = HashMap::new(); + + h.insert("foo", "bar"); + h.insert("baz", "qux"); + h.insert("some", "val"); + + // Magic number + sample_data.write_all(&PARAMS_MAGIC)?; + // Size of the AppP block + sample_data.write_all(&4u32.to_le_bytes())?; + // Number of blocks + sample_data.write_all(&2u32.to_le_bytes())?; + + // Magic number + sample_data.write_all(&ENV_MAGIC)?; + let mut data = vec![]; + for (key, value) in h.iter() { + data.extend_from_slice(&(key.len() as u16).to_le_bytes()); + data.extend_from_slice(key.as_bytes()); + data.extend_from_slice(&(value.len() as u16).to_le_bytes()); + data.extend_from_slice(value.as_bytes()); + } + // Size of the EnvB block + sample_data.write_all(&(data.len() as u32 + 2).to_le_bytes())?; + + // Number of environment variables + sample_data.write_all(&(h.len() as u16).to_le_bytes())?; + + // Environment variables + sample_data.write_all(&data)?; + + // Write command line arguments + let args = vec!["some", "command", "line variable", "entries"]; + sample_data.write_all(&ARGS_MAGIC)?; + let mut args_size = 0; + for entry in args.iter() { + args_size += entry.len() + 2; + } + sample_data.write_all(&(args_size as u32 + 2).to_le_bytes())?; + sample_data.write_all(&(args.len() as u16).to_le_bytes())?; + for entry in args { + sample_data.write_all(&(entry.len() as u16).to_le_bytes())?; + sample_data.write_all(entry.as_bytes())?; + } + + Ok(sample_data) +} + +#[test] +fn basic_arg_parsing() { + let arg_data = create_args_test().expect("couldn't create test data"); + for byte in &arg_data { + print!("{:02x} ", byte); + } + println!(); + + let args = ApplicationParameters::new(&arg_data).expect("Unable to parse arguments"); + for arg in args { + if let Ok(env) = EnvironmentBlock::try_from(&arg) { + for env in env { + println!("{}={}", env.key, env.value); + } + } else if let Ok(args) = ArgumentList::try_from(&arg) { + for arg in args { + println!("Arg: {}", arg.value); + } + } + } +} diff --git a/library/std/src/sys/random/arc4random.rs b/library/std/src/sys/random/arc4random.rs index 32467e9ebaa64..ffabaafbee803 100644 --- a/library/std/src/sys/random/arc4random.rs +++ b/library/std/src/sys/random/arc4random.rs @@ -12,6 +12,7 @@ #[cfg(not(any( target_os = "haiku", target_os = "illumos", + target_os = "rtems", target_os = "solaris", target_os = "vita", )))] @@ -21,6 +22,7 @@ use libc::arc4random_buf; #[cfg(any( target_os = "haiku", // See https://git.haiku-os.org/haiku/tree/headers/compatibility/bsd/stdlib.h target_os = "illumos", // See https://www.illumos.org/man/3C/arc4random + target_os = "rtems", // See https://docs.rtems.org/branches/master/bsp-howto/getentropy.html target_os = "solaris", // See https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html target_os = "vita", // See https://github.com/vitasdk/newlib/blob/b89e5bc183b516945f9ee07eef483ecb916e45ff/newlib/libc/include/stdlib.h#L74 ))] diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index d625814d15b39..edc2cacdfd874 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -17,6 +17,7 @@ cfg_if::cfg_if! { target_os = "illumos", target_os = "netbsd", target_os = "openbsd", + target_os = "rtems", target_os = "solaris", target_os = "vita", ))] { diff --git a/library/std/src/sys/sync/condvar/futex.rs b/library/std/src/sys/sync/condvar/futex.rs index 39cd97c01ea32..0d0c5f0dbe701 100644 --- a/library/std/src/sys/sync/condvar/futex.rs +++ b/library/std/src/sys/sync/condvar/futex.rs @@ -1,6 +1,5 @@ -use crate::sync::atomic::AtomicU32; use crate::sync::atomic::Ordering::Relaxed; -use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all}; +use crate::sys::futex::{Futex, futex_wait, futex_wake, futex_wake_all}; use crate::sys::sync::Mutex; use crate::time::Duration; @@ -8,13 +7,13 @@ pub struct Condvar { // The value of this atomic is simply incremented on every notification. // This is used by `.wait()` to not miss any notifications after // unlocking the mutex and before waiting for notifications. - futex: AtomicU32, + futex: Futex, } impl Condvar { #[inline] pub const fn new() -> Self { - Self { futex: AtomicU32::new(0) } + Self { futex: Futex::new(0) } } // All the memory orderings here are `Relaxed`, diff --git a/library/std/src/sys/sync/mutex/futex.rs b/library/std/src/sys/sync/mutex/futex.rs index 81afa94b14787..ce9b2daa5f808 100644 --- a/library/std/src/sys/sync/mutex/futex.rs +++ b/library/std/src/sys/sync/mutex/futex.rs @@ -1,11 +1,11 @@ use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::sys::futex::{self, futex_wait, futex_wake}; -type Atomic = futex::SmallAtomic; +type Futex = futex::SmallFutex; type State = futex::SmallPrimitive; pub struct Mutex { - futex: Atomic, + futex: Futex, } const UNLOCKED: State = 0; @@ -15,7 +15,7 @@ const CONTENDED: State = 2; // locked, and other threads waiting (contended) impl Mutex { #[inline] pub const fn new() -> Self { - Self { futex: Atomic::new(UNLOCKED) } + Self { futex: Futex::new(UNLOCKED) } } #[inline] diff --git a/library/std/src/sys/sync/once/futex.rs b/library/std/src/sys/sync/once/futex.rs index 25588a4217b62..10bfa81a6d72a 100644 --- a/library/std/src/sys/sync/once/futex.rs +++ b/library/std/src/sys/sync/once/futex.rs @@ -1,39 +1,38 @@ use crate::cell::Cell; use crate::sync as public; -use crate::sync::atomic::AtomicU32; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::sync::once::ExclusiveState; -use crate::sys::futex::{futex_wait, futex_wake_all}; +use crate::sys::futex::{Futex, Primitive, futex_wait, futex_wake_all}; // On some platforms, the OS is very nice and handles the waiter queue for us. // This means we only need one atomic value with 4 states: /// No initialization has run yet, and no thread is currently using the Once. -const INCOMPLETE: u32 = 0; +const INCOMPLETE: Primitive = 0; /// Some thread has previously attempted to initialize the Once, but it panicked, /// so the Once is now poisoned. There are no other threads currently accessing /// this Once. -const POISONED: u32 = 1; +const POISONED: Primitive = 1; /// Some thread is currently attempting to run initialization. It may succeed, /// so all future threads need to wait for it to finish. -const RUNNING: u32 = 2; +const RUNNING: Primitive = 2; /// Initialization has completed and all future calls should finish immediately. -const COMPLETE: u32 = 3; +const COMPLETE: Primitive = 3; // An additional bit indicates whether there are waiting threads: /// May only be set if the state is not COMPLETE. -const QUEUED: u32 = 4; +const QUEUED: Primitive = 4; // Threads wait by setting the QUEUED bit and calling `futex_wait` on the state // variable. When the running thread finishes, it will wake all waiting threads using // `futex_wake_all`. -const STATE_MASK: u32 = 0b11; +const STATE_MASK: Primitive = 0b11; pub struct OnceState { poisoned: bool, - set_state_to: Cell, + set_state_to: Cell, } impl OnceState { @@ -49,8 +48,8 @@ impl OnceState { } struct CompletionGuard<'a> { - state_and_queued: &'a AtomicU32, - set_state_on_drop_to: u32, + state_and_queued: &'a Futex, + set_state_on_drop_to: Primitive, } impl<'a> Drop for CompletionGuard<'a> { @@ -65,13 +64,13 @@ impl<'a> Drop for CompletionGuard<'a> { } pub struct Once { - state_and_queued: AtomicU32, + state_and_queued: Futex, } impl Once { #[inline] pub const fn new() -> Once { - Once { state_and_queued: AtomicU32::new(INCOMPLETE) } + Once { state_and_queued: Futex::new(INCOMPLETE) } } #[inline] diff --git a/library/std/src/sys/sync/once/queue.rs b/library/std/src/sys/sync/once/queue.rs index 17abaf0bf267b..3e83a4a088fd4 100644 --- a/library/std/src/sys/sync/once/queue.rs +++ b/library/std/src/sys/sync/once/queue.rs @@ -23,7 +23,7 @@ // You'll find a few more details in the implementation, but that's the gist of // it! // -// Atomic orderings: +// Futex orderings: // When running `Once` we deal with multiple atomics: // `Once.state_and_queue` and an unknown number of `Waiter.signaled`. // * `state_and_queue` is used (1) as a state flag, (2) for synchronizing the diff --git a/library/std/src/sys/sync/rwlock/futex.rs b/library/std/src/sys/sync/rwlock/futex.rs index 75ecc2ab5c52f..df22c36dd5a21 100644 --- a/library/std/src/sys/sync/rwlock/futex.rs +++ b/library/std/src/sys/sync/rwlock/futex.rs @@ -1,6 +1,5 @@ -use crate::sync::atomic::AtomicU32; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all}; +use crate::sys::futex::{Futex, Primitive, futex_wait, futex_wake, futex_wake_all}; pub struct RwLock { // The state consists of a 30-bit reader counter, a 'readers waiting' flag, and a 'writers waiting' flag. @@ -10,41 +9,41 @@ pub struct RwLock { // 0x3FFF_FFFF: Write locked // Bit 30: Readers are waiting on this futex. // Bit 31: Writers are waiting on the writer_notify futex. - state: AtomicU32, + state: Futex, // The 'condition variable' to notify writers through. // Incremented on every signal. - writer_notify: AtomicU32, + writer_notify: Futex, } -const READ_LOCKED: u32 = 1; -const MASK: u32 = (1 << 30) - 1; -const WRITE_LOCKED: u32 = MASK; -const MAX_READERS: u32 = MASK - 1; -const READERS_WAITING: u32 = 1 << 30; -const WRITERS_WAITING: u32 = 1 << 31; +const READ_LOCKED: Primitive = 1; +const MASK: Primitive = (1 << 30) - 1; +const WRITE_LOCKED: Primitive = MASK; +const MAX_READERS: Primitive = MASK - 1; +const READERS_WAITING: Primitive = 1 << 30; +const WRITERS_WAITING: Primitive = 1 << 31; #[inline] -fn is_unlocked(state: u32) -> bool { +fn is_unlocked(state: Primitive) -> bool { state & MASK == 0 } #[inline] -fn is_write_locked(state: u32) -> bool { +fn is_write_locked(state: Primitive) -> bool { state & MASK == WRITE_LOCKED } #[inline] -fn has_readers_waiting(state: u32) -> bool { +fn has_readers_waiting(state: Primitive) -> bool { state & READERS_WAITING != 0 } #[inline] -fn has_writers_waiting(state: u32) -> bool { +fn has_writers_waiting(state: Primitive) -> bool { state & WRITERS_WAITING != 0 } #[inline] -fn is_read_lockable(state: u32) -> bool { +fn is_read_lockable(state: Primitive) -> bool { // This also returns false if the counter could overflow if we tried to read lock it. // // We don't allow read-locking if there's readers waiting, even if the lock is unlocked @@ -55,14 +54,14 @@ fn is_read_lockable(state: u32) -> bool { } #[inline] -fn has_reached_max_readers(state: u32) -> bool { +fn has_reached_max_readers(state: Primitive) -> bool { state & MASK == MAX_READERS } impl RwLock { #[inline] pub const fn new() -> Self { - Self { state: AtomicU32::new(0), writer_notify: AtomicU32::new(0) } + Self { state: Futex::new(0), writer_notify: Futex::new(0) } } #[inline] @@ -225,7 +224,7 @@ impl RwLock { /// If both are waiting, this will wake up only one writer, but will fall /// back to waking up readers if there was no writer to wake up. #[cold] - fn wake_writer_or_readers(&self, mut state: u32) { + fn wake_writer_or_readers(&self, mut state: Primitive) { assert!(is_unlocked(state)); // The readers waiting bit might be turned on at any point now, @@ -290,7 +289,7 @@ impl RwLock { /// Spin for a while, but stop directly at the given condition. #[inline] - fn spin_until(&self, f: impl Fn(u32) -> bool) -> u32 { + fn spin_until(&self, f: impl Fn(Primitive) -> bool) -> Primitive { let mut spin = 100; // Chosen by fair dice roll. loop { let state = self.state.load(Relaxed); @@ -303,13 +302,13 @@ impl RwLock { } #[inline] - fn spin_write(&self) -> u32 { + fn spin_write(&self) -> Primitive { // Stop spinning when it's unlocked or when there's waiting writers, to keep things somewhat fair. self.spin_until(|state| is_unlocked(state) || has_writers_waiting(state)) } #[inline] - fn spin_read(&self) -> u32 { + fn spin_read(&self) -> Primitive { // Stop spinning when it's unlocked or read locked, or when there's waiting threads. self.spin_until(|state| { !is_write_locked(state) || has_readers_waiting(state) || has_writers_waiting(state) diff --git a/library/std/src/sys/sync/thread_parking/futex.rs b/library/std/src/sys/sync/thread_parking/futex.rs index ce852eaadc4d9..c8f7f26386a01 100644 --- a/library/std/src/sys/sync/thread_parking/futex.rs +++ b/library/std/src/sys/sync/thread_parking/futex.rs @@ -4,7 +4,7 @@ use crate::sync::atomic::Ordering::{Acquire, Release}; use crate::sys::futex::{self, futex_wait, futex_wake}; use crate::time::Duration; -type Atomic = futex::SmallAtomic; +type Futex = futex::SmallFutex; type State = futex::SmallPrimitive; const PARKED: State = State::MAX; @@ -12,7 +12,7 @@ const EMPTY: State = 0; const NOTIFIED: State = 1; pub struct Parker { - state: Atomic, + state: Futex, } // Notes about memory ordering: @@ -39,7 +39,7 @@ impl Parker { /// Constructs the futex parker. The UNIX parker implementation /// requires this to happen in-place. pub unsafe fn new_in_place(parker: *mut Parker) { - unsafe { parker.write(Self { state: Atomic::new(EMPTY) }) }; + unsafe { parker.write(Self { state: Futex::new(EMPTY) }) }; } // Assumes this is only called by the thread that owns the Parker, diff --git a/library/std/src/sys/thread_local/native/mod.rs b/library/std/src/sys/thread_local/native/mod.rs index f498dee0899f9..a5dffe3c45883 100644 --- a/library/std/src/sys/thread_local/native/mod.rs +++ b/library/std/src/sys/thread_local/native/mod.rs @@ -49,20 +49,21 @@ pub use lazy::Storage as LazyStorage; #[unstable(feature = "thread_local_internals", issue = "none")] #[rustc_macro_transparency = "semitransparent"] pub macro thread_local_inner { - // used to generate the `LocalKey` value for const-initialized thread locals + // NOTE: we cannot import `LocalKey`, `LazyStorage` or `EagerStorage` with a `use` because that + // can shadow user provided type or type alias with a matching name. Please update the shadowing + // test in `tests/thread.rs` if these types are renamed. + + // Used to generate the `LocalKey` value for const-initialized thread locals. (@key $t:ty, const $init:expr) => {{ const __INIT: $t = $init; unsafe { - use $crate::mem::needs_drop; - use $crate::thread::LocalKey; - use $crate::thread::local_impl::EagerStorage; - - LocalKey::new(const { - if needs_drop::<$t>() { + $crate::thread::LocalKey::new(const { + if $crate::mem::needs_drop::<$t>() { |_| { #[thread_local] - static VAL: EagerStorage<$t> = EagerStorage::new(__INIT); + static VAL: $crate::thread::local_impl::EagerStorage<$t> + = $crate::thread::local_impl::EagerStorage::new(__INIT); VAL.get() } } else { @@ -84,21 +85,19 @@ pub macro thread_local_inner { } unsafe { - use $crate::mem::needs_drop; - use $crate::thread::LocalKey; - use $crate::thread::local_impl::LazyStorage; - - LocalKey::new(const { - if needs_drop::<$t>() { + $crate::thread::LocalKey::new(const { + if $crate::mem::needs_drop::<$t>() { |init| { #[thread_local] - static VAL: LazyStorage<$t, ()> = LazyStorage::new(); + static VAL: $crate::thread::local_impl::LazyStorage<$t, ()> + = $crate::thread::local_impl::LazyStorage::new(); VAL.get_or_init(init, __init) } } else { |init| { #[thread_local] - static VAL: LazyStorage<$t, !> = LazyStorage::new(); + static VAL: $crate::thread::local_impl::LazyStorage<$t, !> + = $crate::thread::local_impl::LazyStorage::new(); VAL.get_or_init(init, __init) } } diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index 26ce3322a16e3..f5a2aaa6c6a3f 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -15,19 +15,24 @@ pub macro thread_local_inner { $crate::thread::local_impl::thread_local_inner!(@key $t, { const INIT_EXPR: $t = $init; INIT_EXPR }) }, - // used to generate the `LocalKey` value for `thread_local!` + // NOTE: we cannot import `Storage` or `LocalKey` with a `use` because that can shadow user + // provided type or type alias with a matching name. Please update the shadowing test in + // `tests/thread.rs` if these types are renamed. + + // used to generate the `LocalKey` value for `thread_local!`. (@key $t:ty, $init:expr) => {{ #[inline] fn __init() -> $t { $init } + // NOTE: this cannot import `LocalKey` or `Storage` with a `use` because that can shadow + // user provided type or type alias with a matching name. Please update the shadowing test + // in `tests/thread.rs` if these types are renamed. unsafe { - use $crate::thread::LocalKey; - use $crate::thread::local_impl::Storage; - // Inlining does not work on windows-gnu due to linking errors around // dllimports. See https://github.com/rust-lang/rust/issues/109797. - LocalKey::new(#[cfg_attr(windows, inline(never))] |init| { - static VAL: Storage<$t> = Storage::new(); + $crate::thread::LocalKey::new(#[cfg_attr(windows, inline(never))] |init| { + static VAL: $crate::thread::local_impl::Storage<$t> + = $crate::thread::local_impl::Storage::new(); VAL.get(init, __init) }) } diff --git a/library/std/tests/thread.rs b/library/std/tests/thread.rs index 83574176186a4..1bb17d149fa10 100644 --- a/library/std/tests/thread.rs +++ b/library/std/tests/thread.rs @@ -38,6 +38,29 @@ fn thread_local_containing_const_statements() { assert_eq!(REFCELL.take(), 1); } +#[test] +fn thread_local_hygeiene() { + // Previously `thread_local_inner!` had use imports for `LocalKey`, `Storage`, `EagerStorage` + // and `LazyStorage`. The use imports will shadow a user-provided type or type alias if the + // user-provided type or type alias has the same name. Make sure that this does not happen. See + // . + // + // NOTE: if the internal implementation details change (i.e. get renamed), this test should be + // updated. + + #![allow(dead_code)] + type LocalKey = (); + type Storage = (); + type LazyStorage = (); + type EagerStorage = (); + thread_local! { + static A: LocalKey = const { () }; + static B: Storage = const { () }; + static C: LazyStorage = const { () }; + static D: EagerStorage = const { () }; + } +} + #[test] // Include an ignore list on purpose, so that new platforms don't miss it #[cfg_attr( diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index 7165c3e48af42..aa6c3dc32e2ba 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # this is a dummy crate to ensure that all required crates appear in the sysroot [dependencies] proc_macro = { path = "../proc_macro" } +profiler_builtins = { path = "../profiler_builtins", optional = true } std = { path = "../std" } test = { path = "../test" } @@ -23,7 +24,7 @@ system-llvm-libunwind = ["std/system-llvm-libunwind"] panic-unwind = ["std/panic_unwind"] panic_immediate_abort = ["std/panic_immediate_abort"] optimize_for_size = ["std/optimize_for_size"] -profiler = ["std/profiler"] +profiler = ["dep:profiler_builtins"] std_detect_file_io = ["std/std_detect_file_io"] std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"] std_detect_env_override = ["std/std_detect_env_override"] diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml index 590de31a678ca..569a1b3299e5f 100644 --- a/library/unwind/Cargo.toml +++ b/library/unwind/Cargo.toml @@ -22,7 +22,7 @@ cfg-if = "1.0" libc = { version = "0.2.140", features = ['rustc-dep-of-std'], default-features = false } [target.'cfg(target_os = "xous")'.dependencies] -unwinding = { version = "0.2.1", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false } +unwinding = { version = "0.2.3", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false } [features] diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 5a476d5843b2f..79baa5b0b83ec 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -2,7 +2,6 @@ #![unstable(feature = "panic_unwind", issue = "32837")] #![feature(link_cfg)] #![feature(staged_api)] -#![feature(strict_provenance)] #![cfg_attr(not(target_env = "msvc"), feature(libc))] #![cfg_attr( all(target_family = "wasm", not(target_os = "emscripten")), diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index a555a26367d51..15137fbb2b5c2 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -193,7 +193,8 @@ def is_value_list(key): if option.value: print('\t{:30} {}'.format('--{}=VAL'.format(option.name), option.desc)) else: - print('\t{:30} {}'.format('--enable-{} OR --disable-{}'.format(option.name, option.name), option.desc)) + print('\t--enable-{:25} OR --disable-{}'.format(option.name, option.name)) + print('\t\t' + option.desc) print('') print('This configure script is a thin configuration shim over the true') print('configuration system, `config.toml`. You can explore the comments') diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 90e6a10d9d685..80ba9f4444863 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1591,9 +1591,15 @@ impl Step for Extended { prepare("cargo"); prepare("rust-std"); prepare("rust-analysis"); - prepare("clippy"); - prepare("rust-analyzer"); - for tool in &["rust-docs", "miri", "rustc-codegen-cranelift"] { + + for tool in &[ + "clippy", + "rustfmt", + "rust-analyzer", + "rust-docs", + "miri", + "rustc-codegen-cranelift", + ] { if built_tools.contains(tool) { prepare(tool); } @@ -1633,6 +1639,8 @@ impl Step for Extended { "rust-analyzer-preview".to_string() } else if name == "clippy" { "clippy-preview".to_string() + } else if name == "rustfmt" { + "rustfmt-preview".to_string() } else if name == "miri" { "miri-preview".to_string() } else if name == "rustc-codegen-cranelift" { @@ -1652,7 +1660,7 @@ impl Step for Extended { prepare("cargo"); prepare("rust-analysis"); prepare("rust-std"); - for tool in &["clippy", "rust-analyzer", "rust-docs", "miri"] { + for tool in &["clippy", "rustfmt", "rust-analyzer", "rust-docs", "miri"] { if built_tools.contains(tool) { prepare(tool); } @@ -1770,6 +1778,24 @@ impl Step for Extended { .arg(etc.join("msi/remove-duplicates.xsl")) .run(builder); } + if built_tools.contains("rustfmt") { + command(&heat) + .current_dir(&exe) + .arg("dir") + .arg("rustfmt") + .args(heat_flags) + .arg("-cg") + .arg("RustFmtGroup") + .arg("-dr") + .arg("RustFmt") + .arg("-var") + .arg("var.RustFmtDir") + .arg("-out") + .arg(exe.join("RustFmtGroup.wxs")) + .arg("-t") + .arg(etc.join("msi/remove-duplicates.xsl")) + .run(builder); + } if built_tools.contains("miri") { command(&heat) .current_dir(&exe) @@ -1841,6 +1867,9 @@ impl Step for Extended { if built_tools.contains("clippy") { cmd.arg("-dClippyDir=clippy"); } + if built_tools.contains("rustfmt") { + cmd.arg("-dRustFmtDir=rustfmt"); + } if built_tools.contains("rust-docs") { cmd.arg("-dDocsDir=rust-docs"); } @@ -1867,6 +1896,9 @@ impl Step for Extended { if built_tools.contains("clippy") { candle("ClippyGroup.wxs".as_ref()); } + if built_tools.contains("rustfmt") { + candle("RustFmtGroup.wxs".as_ref()); + } if built_tools.contains("miri") { candle("MiriGroup.wxs".as_ref()); } @@ -1905,6 +1937,9 @@ impl Step for Extended { if built_tools.contains("clippy") { cmd.arg("ClippyGroup.wixobj"); } + if built_tools.contains("rustfmt") { + cmd.arg("RustFmtGroup.wixobj"); + } if built_tools.contains("miri") { cmd.arg("MiriGroup.wixobj"); } diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index c7bcd76caddb9..a6dff7fde8054 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -283,3 +283,25 @@ impl Step for GenerateCompletions { run.builder.ensure(GenerateCompletions); } } + +#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] +pub struct UnicodeTableGenerator; + +impl Step for UnicodeTableGenerator { + type Output = (); + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/unicode-table-generator") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(UnicodeTableGenerator); + } + + fn run(self, builder: &Builder<'_>) { + let mut cmd = builder.tool_cmd(Tool::UnicodeTableGenerator); + cmd.arg(builder.src.join("library/core/src/unicode/unicode_data.rs")); + cmd.run(builder); + } +} diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index f1a10c3296e91..f5afa6c4c6c46 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -360,6 +360,7 @@ bootstrap_tool!( CoverageDump, "src/tools/coverage-dump", "coverage-dump"; RustcPerfWrapper, "src/tools/rustc-perf-wrapper", "rustc-perf-wrapper"; WasmComponentLd, "src/tools/wasm-component-ld", "wasm-component-ld", is_unstable_tool = true, allow_features = "min_specialization"; + UnicodeTableGenerator, "src/tools/unicode-table-generator", "unicode-table-generator"; ); /// These are the submodules that are required for rustbook to work due to diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs new file mode 100644 index 0000000000000..c3d0994f16410 --- /dev/null +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -0,0 +1,1220 @@ +use std::env; +use std::ffi::{OsStr, OsString}; +use std::path::{Path, PathBuf}; + +use super::{Builder, Kind}; +use crate::core::build_steps::tool::SourceType; +use crate::core::build_steps::{compile, test}; +use crate::core::config::SplitDebuginfo; +use crate::core::config::flags::Color; +use crate::utils::helpers::{ + self, LldThreads, add_link_lib_path, check_cfg_arg, linker_args, linker_flags, +}; +use crate::{ + BootstrapCommand, CLang, Compiler, DocTests, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode, + TargetSelection, command, prepare_behaviour_dump_dir, t, +}; + +/// Represents flag values in `String` form with whitespace delimiter to pass it to the compiler +/// later. +/// +/// `-Z crate-attr` flags will be applied recursively on the target code using the +/// `rustc_parse::parser::Parser`. See `rustc_builtin_macros::cmdline_attrs::inject` for more +/// information. +#[derive(Debug, Clone)] +struct Rustflags(String, TargetSelection); + +impl Rustflags { + fn new(target: TargetSelection) -> Rustflags { + let mut ret = Rustflags(String::new(), target); + ret.propagate_cargo_env("RUSTFLAGS"); + ret + } + + /// By default, cargo will pick up on various variables in the environment. However, bootstrap + /// reuses those variables to pass additional flags to rustdoc, so by default they get + /// overridden. Explicitly add back any previous value in the environment. + /// + /// `prefix` is usually `RUSTFLAGS` or `RUSTDOCFLAGS`. + fn propagate_cargo_env(&mut self, prefix: &str) { + // Inherit `RUSTFLAGS` by default ... + self.env(prefix); + + // ... and also handle target-specific env RUSTFLAGS if they're configured. + let target_specific = format!("CARGO_TARGET_{}_{}", crate::envify(&self.1.triple), prefix); + self.env(&target_specific); + } + + fn env(&mut self, env: &str) { + if let Ok(s) = env::var(env) { + for part in s.split(' ') { + self.arg(part); + } + } + } + + fn arg(&mut self, arg: &str) -> &mut Self { + assert_eq!(arg.split(' ').count(), 1); + if !self.0.is_empty() { + self.0.push(' '); + } + self.0.push_str(arg); + self + } +} + +/// Flags that are passed to the `rustc` shim binary. These flags will only be applied when +/// compiling host code, i.e. when `--target` is unset. +#[derive(Debug, Default)] +struct HostFlags { + rustc: Vec, +} + +impl HostFlags { + const SEPARATOR: &'static str = " "; + + /// Adds a host rustc flag. + fn arg>(&mut self, flag: S) { + let value = flag.into().trim().to_string(); + assert!(!value.contains(Self::SEPARATOR)); + self.rustc.push(value); + } + + /// Encodes all the flags into a single string. + fn encode(self) -> String { + self.rustc.join(Self::SEPARATOR) + } +} + +#[derive(Debug)] +pub struct Cargo { + command: BootstrapCommand, + compiler: Compiler, + target: TargetSelection, + rustflags: Rustflags, + rustdocflags: Rustflags, + hostflags: HostFlags, + allow_features: String, +} + +impl Cargo { + /// Calls [`Builder::cargo`] and [`Cargo::configure_linker`] to prepare an invocation of `cargo` + /// to be run. + pub fn new( + builder: &Builder<'_>, + compiler: Compiler, + mode: Mode, + source_type: SourceType, + target: TargetSelection, + cmd_kind: Kind, + ) -> Cargo { + let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd_kind); + + match cmd_kind { + // No need to configure the target linker for these command types, + // as they don't invoke rustc at all. + Kind::Clean | Kind::Suggest | Kind::Format | Kind::Setup => {} + _ => { + cargo.configure_linker(builder); + } + } + + cargo + } + + pub fn into_cmd(self) -> BootstrapCommand { + self.into() + } + + /// Same as [`Cargo::new`] except this one doesn't configure the linker with + /// [`Cargo::configure_linker`]. + pub fn new_for_mir_opt_tests( + builder: &Builder<'_>, + compiler: Compiler, + mode: Mode, + source_type: SourceType, + target: TargetSelection, + cmd_kind: Kind, + ) -> Cargo { + builder.cargo(compiler, mode, source_type, target, cmd_kind) + } + + pub fn rustdocflag(&mut self, arg: &str) -> &mut Cargo { + self.rustdocflags.arg(arg); + self + } + + pub fn rustflag(&mut self, arg: &str) -> &mut Cargo { + self.rustflags.arg(arg); + self + } + + pub fn arg(&mut self, arg: impl AsRef) -> &mut Cargo { + self.command.arg(arg.as_ref()); + self + } + + pub fn args(&mut self, args: I) -> &mut Cargo + where + I: IntoIterator, + S: AsRef, + { + for arg in args { + self.arg(arg.as_ref()); + } + self + } + + /// Add an env var to the cargo command instance. Note that `RUSTFLAGS`/`RUSTDOCFLAGS` must go + /// through [`Cargo::rustdocflags`] and [`Cargo::rustflags`] because inconsistent `RUSTFLAGS` + /// and `RUSTDOCFLAGS` usages will trigger spurious rebuilds. + pub fn env(&mut self, key: impl AsRef, value: impl AsRef) -> &mut Cargo { + assert_ne!(key.as_ref(), "RUSTFLAGS"); + assert_ne!(key.as_ref(), "RUSTDOCFLAGS"); + self.command.env(key.as_ref(), value.as_ref()); + self + } + + pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>) { + builder.add_rustc_lib_path(self.compiler, &mut self.command); + } + + pub fn current_dir(&mut self, dir: &Path) -> &mut Cargo { + self.command.current_dir(dir); + self + } + + /// Adds nightly-only features that this invocation is allowed to use. + /// + /// By default, all nightly features are allowed. Once this is called, it will be restricted to + /// the given set. + pub fn allow_features(&mut self, features: &str) -> &mut Cargo { + if !self.allow_features.is_empty() { + self.allow_features.push(','); + } + self.allow_features.push_str(features); + self + } + + fn configure_linker(&mut self, builder: &Builder<'_>) -> &mut Cargo { + let target = self.target; + let compiler = self.compiler; + + // Dealing with rpath here is a little special, so let's go into some + // detail. First off, `-rpath` is a linker option on Unix platforms + // which adds to the runtime dynamic loader path when looking for + // dynamic libraries. We use this by default on Unix platforms to ensure + // that our nightlies behave the same on Windows, that is they work out + // of the box. This can be disabled by setting `rpath = false` in `[rust]` + // table of `config.toml` + // + // Ok, so the astute might be wondering "why isn't `-C rpath` used + // here?" and that is indeed a good question to ask. This codegen + // option is the compiler's current interface to generating an rpath. + // Unfortunately it doesn't quite suffice for us. The flag currently + // takes no value as an argument, so the compiler calculates what it + // should pass to the linker as `-rpath`. This unfortunately is based on + // the **compile time** directory structure which when building with + // Cargo will be very different than the runtime directory structure. + // + // All that's a really long winded way of saying that if we use + // `-Crpath` then the executables generated have the wrong rpath of + // something like `$ORIGIN/deps` when in fact the way we distribute + // rustc requires the rpath to be `$ORIGIN/../lib`. + // + // So, all in all, to set up the correct rpath we pass the linker + // argument manually via `-C link-args=-Wl,-rpath,...`. Plus isn't it + // fun to pass a flag to a tool to pass a flag to pass a flag to a tool + // to change a flag in a binary? + if builder.config.rpath_enabled(target) && helpers::use_host_linker(target) { + let libdir = builder.sysroot_libdir_relative(compiler).to_str().unwrap(); + let rpath = if target.contains("apple") { + // Note that we need to take one extra step on macOS to also pass + // `-Wl,-instal_name,@rpath/...` to get things to work right. To + // do that we pass a weird flag to the compiler to get it to do + // so. Note that this is definitely a hack, and we should likely + // flesh out rpath support more fully in the future. + self.rustflags.arg("-Zosx-rpath-install-name"); + Some(format!("-Wl,-rpath,@loader_path/../{libdir}")) + } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") { + self.rustflags.arg("-Clink-args=-Wl,-z,origin"); + Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}")) + } else { + None + }; + if let Some(rpath) = rpath { + self.rustflags.arg(&format!("-Clink-args={rpath}")); + } + } + + for arg in linker_args(builder, compiler.host, LldThreads::Yes) { + self.hostflags.arg(&arg); + } + + if let Some(target_linker) = builder.linker(target) { + let target = crate::envify(&target.triple); + self.command.env(format!("CARGO_TARGET_{target}_LINKER"), target_linker); + } + // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not + // `linker_args` here. + for flag in linker_flags(builder, target, LldThreads::Yes) { + self.rustflags.arg(&flag); + } + for arg in linker_args(builder, target, LldThreads::Yes) { + self.rustdocflags.arg(&arg); + } + + if !builder.config.dry_run() + && builder.cc.borrow()[&target].args().iter().any(|arg| arg == "-gz") + { + self.rustflags.arg("-Clink-arg=-gz"); + } + + // Throughout the build Cargo can execute a number of build scripts + // compiling C/C++ code and we need to pass compilers, archivers, flags, etc + // obtained previously to those build scripts. + // Build scripts use either the `cc` crate or `configure/make` so we pass + // the options through environment variables that are fetched and understood by both. + // + // FIXME: the guard against msvc shouldn't need to be here + if target.is_msvc() { + if let Some(ref cl) = builder.config.llvm_clang_cl { + // FIXME: There is a bug in Clang 18 when building for ARM64: + // https://github.com/llvm/llvm-project/pull/81849. This is + // fixed in LLVM 19, but can't be backported. + if !target.starts_with("aarch64") && !target.starts_with("arm64ec") { + self.command.env("CC", cl).env("CXX", cl); + } + } + } else { + let ccache = builder.config.ccache.as_ref(); + let ccacheify = |s: &Path| { + let ccache = match ccache { + Some(ref s) => s, + None => return s.display().to_string(), + }; + // FIXME: the cc-rs crate only recognizes the literal strings + // `ccache` and `sccache` when doing caching compilations, so we + // mirror that here. It should probably be fixed upstream to + // accept a new env var or otherwise work with custom ccache + // vars. + match &ccache[..] { + "ccache" | "sccache" => format!("{} {}", ccache, s.display()), + _ => s.display().to_string(), + } + }; + let triple_underscored = target.triple.replace('-', "_"); + let cc = ccacheify(&builder.cc(target)); + self.command.env(format!("CC_{triple_underscored}"), &cc); + + let cflags = builder.cflags(target, GitRepo::Rustc, CLang::C).join(" "); + self.command.env(format!("CFLAGS_{triple_underscored}"), &cflags); + + if let Some(ar) = builder.ar(target) { + let ranlib = format!("{} s", ar.display()); + self.command + .env(format!("AR_{triple_underscored}"), ar) + .env(format!("RANLIB_{triple_underscored}"), ranlib); + } + + if let Ok(cxx) = builder.cxx(target) { + let cxx = ccacheify(&cxx); + let cxxflags = builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" "); + self.command + .env(format!("CXX_{triple_underscored}"), &cxx) + .env(format!("CXXFLAGS_{triple_underscored}"), cxxflags); + } + } + + self + } +} + +impl From for BootstrapCommand { + fn from(mut cargo: Cargo) -> BootstrapCommand { + let rustflags = &cargo.rustflags.0; + if !rustflags.is_empty() { + cargo.command.env("RUSTFLAGS", rustflags); + } + + let rustdocflags = &cargo.rustdocflags.0; + if !rustdocflags.is_empty() { + cargo.command.env("RUSTDOCFLAGS", rustdocflags); + } + + let encoded_hostflags = cargo.hostflags.encode(); + if !encoded_hostflags.is_empty() { + cargo.command.env("RUSTC_HOST_FLAGS", encoded_hostflags); + } + + if !cargo.allow_features.is_empty() { + cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features); + } + cargo.command + } +} + +impl Builder<'_> { + /// Like [`Builder::cargo`], but only passes flags that are valid for all commands. + pub fn bare_cargo( + &self, + compiler: Compiler, + mode: Mode, + target: TargetSelection, + cmd_kind: Kind, + ) -> BootstrapCommand { + let mut cargo = match cmd_kind { + Kind::Clippy => { + let mut cargo = self.cargo_clippy_cmd(compiler); + cargo.arg(cmd_kind.as_str()); + cargo + } + Kind::MiriSetup => { + let mut cargo = self.cargo_miri_cmd(compiler); + cargo.arg("miri").arg("setup"); + cargo + } + Kind::MiriTest => { + let mut cargo = self.cargo_miri_cmd(compiler); + cargo.arg("miri").arg("test"); + cargo + } + _ => { + let mut cargo = command(&self.initial_cargo); + cargo.arg(cmd_kind.as_str()); + cargo + } + }; + + // Run cargo from the source root so it can find .cargo/config. + // This matters when using vendoring and the working directory is outside the repository. + cargo.current_dir(&self.src); + + let out_dir = self.stage_out(compiler, mode); + cargo.env("CARGO_TARGET_DIR", &out_dir); + + // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger` + // from out of tree it shouldn't matter, since x.py is only used for + // building in-tree. + let color_logs = ["RUSTDOC_LOG_COLOR", "RUSTC_LOG_COLOR", "RUST_LOG_COLOR"]; + match self.build.config.color { + Color::Always => { + cargo.arg("--color=always"); + for log in &color_logs { + cargo.env(log, "always"); + } + } + Color::Never => { + cargo.arg("--color=never"); + for log in &color_logs { + cargo.env(log, "never"); + } + } + Color::Auto => {} // nothing to do + } + + if cmd_kind != Kind::Install { + cargo.arg("--target").arg(target.rustc_target_arg()); + } else { + assert_eq!(target, compiler.host); + } + + if self.config.rust_optimize.is_release() && + // cargo bench/install do not accept `--release` and miri doesn't want it + !matches!(cmd_kind, Kind::Bench | Kind::Install | Kind::Miri | Kind::MiriSetup | Kind::MiriTest) + { + cargo.arg("--release"); + } + + // Remove make-related flags to ensure Cargo can correctly set things up + cargo.env_remove("MAKEFLAGS"); + cargo.env_remove("MFLAGS"); + + cargo + } + + /// This will create a [`BootstrapCommand`] that represents a pending execution of cargo. This + /// cargo will be configured to use `compiler` as the actual rustc compiler, its output will be + /// scoped by `mode`'s output directory, it will pass the `--target` flag for the specified + /// `target`, and will be executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for + /// commands to be run with Miri. + fn cargo( + &self, + compiler: Compiler, + mode: Mode, + source_type: SourceType, + target: TargetSelection, + cmd_kind: Kind, + ) -> Cargo { + let mut cargo = self.bare_cargo(compiler, mode, target, cmd_kind); + let out_dir = self.stage_out(compiler, mode); + + let mut hostflags = HostFlags::default(); + + // Codegen backends are not yet tracked by -Zbinary-dep-depinfo, + // so we need to explicitly clear out if they've been updated. + for backend in self.codegen_backends(compiler) { + self.clear_if_dirty(&out_dir, &backend); + } + + if cmd_kind == Kind::Doc { + let my_out = match mode { + // This is the intended out directory for compiler documentation. + Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target), + Mode::Std => { + if self.config.cmd.json() { + out_dir.join(target).join("json-doc") + } else { + out_dir.join(target).join("doc") + } + } + _ => panic!("doc mode {mode:?} not expected"), + }; + let rustdoc = self.rustdoc(compiler); + self.clear_if_dirty(&my_out, &rustdoc); + } + + let profile_var = |name: &str| { + let profile = if self.config.rust_optimize.is_release() { "RELEASE" } else { "DEV" }; + format!("CARGO_PROFILE_{}_{}", profile, name) + }; + + // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config + // needs to not accidentally link to libLLVM in stage0/lib. + cargo.env("REAL_LIBRARY_PATH_VAR", helpers::dylib_path_var()); + if let Some(e) = env::var_os(helpers::dylib_path_var()) { + cargo.env("REAL_LIBRARY_PATH", e); + } + + // Set a flag for `check`/`clippy`/`fix`, so that certain build + // scripts can do less work (i.e. not building/requiring LLVM). + if matches!(cmd_kind, Kind::Check | Kind::Clippy | Kind::Fix) { + // If we've not yet built LLVM, or it's stale, then bust + // the rustc_llvm cache. That will always work, even though it + // may mean that on the next non-check build we'll need to rebuild + // rustc_llvm. But if LLVM is stale, that'll be a tiny amount + // of work comparatively, and we'd likely need to rebuild it anyway, + // so that's okay. + if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target, false) + .should_build() + { + cargo.env("RUST_CHECK", "1"); + } + } + + let stage = if compiler.stage == 0 && self.local_rebuild { + // Assume the local-rebuild rustc already has stage1 features. + 1 + } else { + compiler.stage + }; + + // We synthetically interpret a stage0 compiler used to build tools as a + // "raw" compiler in that it's the exact snapshot we download. Normally + // the stage0 build means it uses libraries build by the stage0 + // compiler, but for tools we just use the precompiled libraries that + // we've downloaded + let use_snapshot = mode == Mode::ToolBootstrap; + assert!(!use_snapshot || stage == 0 || self.local_rebuild); + + let maybe_sysroot = self.sysroot(compiler); + let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot }; + let libdir = self.rustc_libdir(compiler); + + let sysroot_str = sysroot.as_os_str().to_str().expect("sysroot should be UTF-8"); + if self.is_verbose() && !matches!(self.config.dry_run, DryRun::SelfCheck) { + println!("using sysroot {sysroot_str}"); + } + + let mut rustflags = Rustflags::new(target); + if stage != 0 { + if let Ok(s) = env::var("CARGOFLAGS_NOT_BOOTSTRAP") { + cargo.args(s.split_whitespace()); + } + rustflags.env("RUSTFLAGS_NOT_BOOTSTRAP"); + } else { + if let Ok(s) = env::var("CARGOFLAGS_BOOTSTRAP") { + cargo.args(s.split_whitespace()); + } + rustflags.env("RUSTFLAGS_BOOTSTRAP"); + rustflags.arg("--cfg=bootstrap"); + } + + if cmd_kind == Kind::Clippy { + // clippy overwrites sysroot if we pass it to cargo. + // Pass it directly to clippy instead. + // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`, + // so it has no way of knowing the sysroot. + rustflags.arg("--sysroot"); + rustflags.arg(sysroot_str); + } + + let use_new_symbol_mangling = match self.config.rust_new_symbol_mangling { + Some(setting) => { + // If an explicit setting is given, use that + setting + } + None => { + if mode == Mode::Std { + // The standard library defaults to the legacy scheme + false + } else { + // The compiler and tools default to the new scheme + true + } + } + }; + + // By default, windows-rs depends on a native library that doesn't get copied into the + // sysroot. Passing this cfg enables raw-dylib support instead, which makes the native + // library unnecessary. This can be removed when windows-rs enables raw-dylib + // unconditionally. + if let Mode::Rustc | Mode::ToolRustc = mode { + rustflags.arg("--cfg=windows_raw_dylib"); + } + + if use_new_symbol_mangling { + rustflags.arg("-Csymbol-mangling-version=v0"); + } else { + rustflags.arg("-Csymbol-mangling-version=legacy"); + } + + // FIXME: the following components don't build with `-Zrandomize-layout` yet: + // - wasm-component-ld, due to the `wast`crate + // - rust-analyzer, due to the rowan crate + // so we exclude entire categories of steps here due to lack of fine-grained control over + // rustflags. + if self.config.rust_randomize_layout && mode != Mode::ToolStd && mode != Mode::ToolRustc { + rustflags.arg("-Zrandomize-layout"); + } + + // Enable compile-time checking of `cfg` names, values and Cargo `features`. + // + // Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like + // backtrace, core_simd, std_float, ...), those dependencies have their own + // features but cargo isn't involved in the #[path] process and so cannot pass the + // complete list of features, so for that reason we don't enable checking of + // features for std crates. + if mode == Mode::Std { + rustflags.arg("--check-cfg=cfg(feature,values(any()))"); + } + + // Add extra cfg not defined in/by rustc + // + // Note: Although it would seems that "-Zunstable-options" to `rustflags` is useless as + // cargo would implicitly add it, it was discover that sometimes bootstrap only use + // `rustflags` without `cargo` making it required. + rustflags.arg("-Zunstable-options"); + for (restricted_mode, name, values) in EXTRA_CHECK_CFGS { + if restricted_mode.is_none() || *restricted_mode == Some(mode) { + rustflags.arg(&check_cfg_arg(name, *values)); + } + } + + // FIXME(rust-lang/cargo#5754) we shouldn't be using special command arguments + // to the host invocation here, but rather Cargo should know what flags to pass rustc + // itself. + if stage == 0 { + hostflags.arg("--cfg=bootstrap"); + } + // Cargo doesn't pass RUSTFLAGS to proc_macros: + // https://github.com/rust-lang/cargo/issues/4423 + // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`. + // We also declare that the flag is expected, which we need to do to not + // get warnings about it being unexpected. + hostflags.arg("-Zunstable-options"); + hostflags.arg("--check-cfg=cfg(bootstrap)"); + + // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`, + // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See + // #71458. + let mut rustdocflags = rustflags.clone(); + rustdocflags.propagate_cargo_env("RUSTDOCFLAGS"); + if stage == 0 { + rustdocflags.env("RUSTDOCFLAGS_BOOTSTRAP"); + } else { + rustdocflags.env("RUSTDOCFLAGS_NOT_BOOTSTRAP"); + } + + if let Ok(s) = env::var("CARGOFLAGS") { + cargo.args(s.split_whitespace()); + } + + match mode { + Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {} + Mode::Rustc | Mode::Codegen | Mode::ToolRustc => { + // Build proc macros both for the host and the target unless proc-macros are not + // supported by the target. + if target != compiler.host && cmd_kind != Kind::Check { + let error = command(self.rustc(compiler)) + .arg("--target") + .arg(target.rustc_target_arg()) + .arg("--print=file-names") + .arg("--crate-type=proc-macro") + .arg("-") + .run_capture(self) + .stderr(); + let not_supported = error + .lines() + .any(|line| line.contains("unsupported crate type `proc-macro`")); + if !not_supported { + cargo.arg("-Zdual-proc-macros"); + rustflags.arg("-Zdual-proc-macros"); + } + } + } + } + + // This tells Cargo (and in turn, rustc) to output more complete + // dependency information. Most importantly for bootstrap, this + // includes sysroot artifacts, like libstd, which means that we don't + // need to track those in bootstrap (an error prone process!). This + // feature is currently unstable as there may be some bugs and such, but + // it represents a big improvement in bootstrap's reliability on + // rebuilds, so we're using it here. + // + // For some additional context, see #63470 (the PR originally adding + // this), as well as #63012 which is the tracking issue for this + // feature on the rustc side. + cargo.arg("-Zbinary-dep-depinfo"); + let allow_features = match mode { + Mode::ToolBootstrap | Mode::ToolStd => { + // Restrict the allowed features so we don't depend on nightly + // accidentally. + // + // binary-dep-depinfo is used by bootstrap itself for all + // compilations. + // + // Lots of tools depend on proc_macro2 and proc-macro-error. + // Those have build scripts which assume nightly features are + // available if the `rustc` version is "nighty" or "dev". See + // bin/rustc.rs for why that is a problem. Instead of labeling + // those features for each individual tool that needs them, + // just blanket allow them here. + // + // If this is ever removed, be sure to add something else in + // its place to keep the restrictions in place (or make a way + // to unset RUSTC_BOOTSTRAP). + "binary-dep-depinfo,proc_macro_span,proc_macro_span_shrink,proc_macro_diagnostic" + .to_string() + } + Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => String::new(), + }; + + cargo.arg("-j").arg(self.jobs().to_string()); + + // FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005 + // Force cargo to output binaries with disambiguating hashes in the name + let mut metadata = if compiler.stage == 0 { + // Treat stage0 like a special channel, whether it's a normal prior- + // release rustc or a local rebuild with the same version, so we + // never mix these libraries by accident. + "bootstrap".to_string() + } else { + self.config.channel.to_string() + }; + // We want to make sure that none of the dependencies between + // std/test/rustc unify with one another. This is done for weird linkage + // reasons but the gist of the problem is that if librustc, libtest, and + // libstd all depend on libc from crates.io (which they actually do) we + // want to make sure they all get distinct versions. Things get really + // weird if we try to unify all these dependencies right now, namely + // around how many times the library is linked in dynamic libraries and + // such. If rustc were a static executable or if we didn't ship dylibs + // this wouldn't be a problem, but we do, so it is. This is in general + // just here to make sure things build right. If you can remove this and + // things still build right, please do! + match mode { + Mode::Std => metadata.push_str("std"), + // When we're building rustc tools, they're built with a search path + // that contains things built during the rustc build. For example, + // bitflags is built during the rustc build, and is a dependency of + // rustdoc as well. We're building rustdoc in a different target + // directory, though, which means that Cargo will rebuild the + // dependency. When we go on to build rustdoc, we'll look for + // bitflags, and find two different copies: one built during the + // rustc step and one that we just built. This isn't always a + // problem, somehow -- not really clear why -- but we know that this + // fixes things. + Mode::ToolRustc => metadata.push_str("tool-rustc"), + // Same for codegen backends. + Mode::Codegen => metadata.push_str("codegen"), + _ => {} + } + cargo.env("__CARGO_DEFAULT_LIB_METADATA", &metadata); + + if cmd_kind == Kind::Clippy { + rustflags.arg("-Zforce-unstable-if-unmarked"); + } + + rustflags.arg("-Zmacro-backtrace"); + + let want_rustdoc = self.doc_tests != DocTests::No; + + // Clear the output directory if the real rustc we're using has changed; + // Cargo cannot detect this as it thinks rustc is bootstrap/debug/rustc. + // + // Avoid doing this during dry run as that usually means the relevant + // compiler is not yet linked/copied properly. + // + // Only clear out the directory if we're compiling std; otherwise, we + // should let Cargo take care of things for us (via depdep info) + if !self.config.dry_run() && mode == Mode::Std && cmd_kind == Kind::Build { + self.clear_if_dirty(&out_dir, &self.rustc(compiler)); + } + + let rustdoc_path = match cmd_kind { + Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc(compiler), + _ => PathBuf::from("/path/to/nowhere/rustdoc/not/required"), + }; + + // Customize the compiler we're running. Specify the compiler to cargo + // as our shim and then pass it some various options used to configure + // how the actual compiler itself is called. + // + // These variables are primarily all read by + // src/bootstrap/bin/{rustc.rs,rustdoc.rs} + cargo + .env("RUSTBUILD_NATIVE_DIR", self.native_dir(target)) + .env("RUSTC_REAL", self.rustc(compiler)) + .env("RUSTC_STAGE", stage.to_string()) + .env("RUSTC_SYSROOT", sysroot) + .env("RUSTC_LIBDIR", libdir) + .env("RUSTDOC", self.bootstrap_out.join("rustdoc")) + .env("RUSTDOC_REAL", rustdoc_path) + .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir()) + .env("RUSTC_BREAK_ON_ICE", "1"); + + // Set RUSTC_WRAPPER to the bootstrap shim, which switches between beta and in-tree + // sysroot depending on whether we're building build scripts. + // NOTE: we intentionally use RUSTC_WRAPPER so that we can support clippy - RUSTC is not + // respected by clippy-driver; RUSTC_WRAPPER happens earlier, before clippy runs. + cargo.env("RUSTC_WRAPPER", self.bootstrap_out.join("rustc")); + // NOTE: we also need to set RUSTC so cargo can run `rustc -vV`; apparently that ignores RUSTC_WRAPPER >:( + cargo.env("RUSTC", self.bootstrap_out.join("rustc")); + + // Someone might have set some previous rustc wrapper (e.g. + // sccache) before bootstrap overrode it. Respect that variable. + if let Some(existing_wrapper) = env::var_os("RUSTC_WRAPPER") { + cargo.env("RUSTC_WRAPPER_REAL", existing_wrapper); + } + + // If this is for `miri-test`, prepare the sysroots. + if cmd_kind == Kind::MiriTest { + self.ensure(compile::Std::new(compiler, compiler.host)); + let host_sysroot = self.sysroot(compiler); + let miri_sysroot = test::Miri::build_miri_sysroot(self, compiler, target); + cargo.env("MIRI_SYSROOT", &miri_sysroot); + cargo.env("MIRI_HOST_SYSROOT", &host_sysroot); + } + + cargo.env(profile_var("STRIP"), self.config.rust_strip.to_string()); + + if let Some(stack_protector) = &self.config.rust_stack_protector { + rustflags.arg(&format!("-Zstack-protector={stack_protector}")); + } + + if !matches!(cmd_kind, Kind::Build | Kind::Check | Kind::Clippy | Kind::Fix) && want_rustdoc + { + cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler)); + } + + let debuginfo_level = match mode { + Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc, + Mode::Std => self.config.rust_debuginfo_level_std, + Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc => { + self.config.rust_debuginfo_level_tools + } + }; + cargo.env(profile_var("DEBUG"), debuginfo_level.to_string()); + if let Some(opt_level) = &self.config.rust_optimize.get_opt_level() { + cargo.env(profile_var("OPT_LEVEL"), opt_level); + } + cargo.env( + profile_var("DEBUG_ASSERTIONS"), + if mode == Mode::Std { + self.config.rust_debug_assertions_std.to_string() + } else { + self.config.rust_debug_assertions.to_string() + }, + ); + cargo.env( + profile_var("OVERFLOW_CHECKS"), + if mode == Mode::Std { + self.config.rust_overflow_checks_std.to_string() + } else { + self.config.rust_overflow_checks.to_string() + }, + ); + + match self.config.split_debuginfo(target) { + SplitDebuginfo::Packed => rustflags.arg("-Csplit-debuginfo=packed"), + SplitDebuginfo::Unpacked => rustflags.arg("-Csplit-debuginfo=unpacked"), + SplitDebuginfo::Off => rustflags.arg("-Csplit-debuginfo=off"), + }; + + if self.config.cmd.bless() { + // Bless `expect!` tests. + cargo.env("UPDATE_EXPECT", "1"); + } + + if !mode.is_tool() { + cargo.env("RUSTC_FORCE_UNSTABLE", "1"); + } + + if let Some(x) = self.crt_static(target) { + if x { + rustflags.arg("-Ctarget-feature=+crt-static"); + } else { + rustflags.arg("-Ctarget-feature=-crt-static"); + } + } + + if let Some(x) = self.crt_static(compiler.host) { + let sign = if x { "+" } else { "-" }; + hostflags.arg(format!("-Ctarget-feature={sign}crt-static")); + } + + if let Some(map_to) = self.build.debuginfo_map_to(GitRepo::Rustc) { + let map = format!("{}={}", self.build.src.display(), map_to); + cargo.env("RUSTC_DEBUGINFO_MAP", map); + + // `rustc` needs to know the virtual `/rustc/$hash` we're mapping to, + // in order to opportunistically reverse it later. + cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to); + } + + if self.config.rust_remap_debuginfo { + let mut env_var = OsString::new(); + if self.config.vendor { + let vendor = self.build.src.join("vendor"); + env_var.push(vendor); + env_var.push("=/rust/deps"); + } else { + let registry_src = t!(home::cargo_home()).join("registry").join("src"); + for entry in t!(std::fs::read_dir(registry_src)) { + if !env_var.is_empty() { + env_var.push("\t"); + } + env_var.push(t!(entry).path()); + env_var.push("=/rust/deps"); + } + } + cargo.env("RUSTC_CARGO_REGISTRY_SRC_TO_REMAP", env_var); + } + + // Enable usage of unstable features + cargo.env("RUSTC_BOOTSTRAP", "1"); + + if self.config.dump_bootstrap_shims { + prepare_behaviour_dump_dir(self.build); + + cargo + .env("DUMP_BOOTSTRAP_SHIMS", self.build.out.join("bootstrap-shims-dump")) + .env("BUILD_OUT", &self.build.out) + .env("CARGO_HOME", t!(home::cargo_home())); + }; + + self.add_rust_test_threads(&mut cargo); + + // Almost all of the crates that we compile as part of the bootstrap may + // have a build script, including the standard library. To compile a + // build script, however, it itself needs a standard library! This + // introduces a bit of a pickle when we're compiling the standard + // library itself. + // + // To work around this we actually end up using the snapshot compiler + // (stage0) for compiling build scripts of the standard library itself. + // The stage0 compiler is guaranteed to have a libstd available for use. + // + // For other crates, however, we know that we've already got a standard + // library up and running, so we can use the normal compiler to compile + // build scripts in that situation. + if mode == Mode::Std { + cargo + .env("RUSTC_SNAPSHOT", &self.initial_rustc) + .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir()); + } else { + cargo + .env("RUSTC_SNAPSHOT", self.rustc(compiler)) + .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_libdir(compiler)); + } + + // Tools that use compiler libraries may inherit the `-lLLVM` link + // requirement, but the `-L` library path is not propagated across + // separate Cargo projects. We can add LLVM's library path to the + // platform-specific environment variable as a workaround. + if mode == Mode::ToolRustc || mode == Mode::Codegen { + if let Some(llvm_config) = self.llvm_config(target) { + let llvm_libdir = + command(llvm_config).arg("--libdir").run_capture_stdout(self).stdout(); + add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cargo); + } + } + + // Compile everything except libraries and proc macros with the more + // efficient initial-exec TLS model. This doesn't work with `dlopen`, + // so we can't use it by default in general, but we can use it for tools + // and our own internal libraries. + if !mode.must_support_dlopen() && !target.triple.starts_with("powerpc-") { + cargo.env("RUSTC_TLS_MODEL_INITIAL_EXEC", "1"); + } + + // Ignore incremental modes except for stage0, since we're + // not guaranteeing correctness across builds if the compiler + // is changing under your feet. + if self.config.incremental && compiler.stage == 0 { + cargo.env("CARGO_INCREMENTAL", "1"); + } else { + // Don't rely on any default setting for incr. comp. in Cargo + cargo.env("CARGO_INCREMENTAL", "0"); + } + + if let Some(ref on_fail) = self.config.on_fail { + cargo.env("RUSTC_ON_FAIL", on_fail); + } + + if self.config.print_step_timings { + cargo.env("RUSTC_PRINT_STEP_TIMINGS", "1"); + } + + if self.config.print_step_rusage { + cargo.env("RUSTC_PRINT_STEP_RUSAGE", "1"); + } + + if self.config.backtrace_on_ice { + cargo.env("RUSTC_BACKTRACE_ON_ICE", "1"); + } + + if self.is_verbose() { + // This provides very useful logs especially when debugging build cache-related stuff. + cargo.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); + } + + cargo.env("RUSTC_VERBOSE", self.verbosity.to_string()); + + // Downstream forks of the Rust compiler might want to use a custom libc to add support for + // targets that are not yet available upstream. Adding a patch to replace libc with a + // custom one would cause compilation errors though, because Cargo would interpret the + // custom libc as part of the workspace, and apply the check-cfg lints on it. + // + // The libc build script emits check-cfg flags only when this environment variable is set, + // so this line allows the use of custom libcs. + cargo.env("LIBC_CHECK_CFG", "1"); + + if source_type == SourceType::InTree { + let mut lint_flags = Vec::new(); + // When extending this list, add the new lints to the RUSTFLAGS of the + // build_bootstrap function of src/bootstrap/bootstrap.py as well as + // some code doesn't go through this `rustc` wrapper. + lint_flags.push("-Wrust_2018_idioms"); + lint_flags.push("-Wunused_lifetimes"); + + if self.config.deny_warnings { + lint_flags.push("-Dwarnings"); + rustdocflags.arg("-Dwarnings"); + } + + // This does not use RUSTFLAGS due to caching issues with Cargo. + // Clippy is treated as an "in tree" tool, but shares the same + // cache as other "submodule" tools. With these options set in + // RUSTFLAGS, that causes *every* shared dependency to be rebuilt. + // By injecting this into the rustc wrapper, this circumvents + // Cargo's fingerprint detection. This is fine because lint flags + // are always ignored in dependencies. Eventually this should be + // fixed via better support from Cargo. + cargo.env("RUSTC_LINT_FLAGS", lint_flags.join(" ")); + + rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes"); + } + + if mode == Mode::Rustc { + rustflags.arg("-Wrustc::internal"); + // FIXME(edition_2024): Change this to `-Wrust_2024_idioms` when all + // of the individual lints are satisfied. + rustflags.arg("-Wkeyword_idents_2024"); + rustflags.arg("-Wunsafe_op_in_unsafe_fn"); + } + + if self.config.rust_frame_pointers { + rustflags.arg("-Cforce-frame-pointers=true"); + } + + // If Control Flow Guard is enabled, pass the `control-flow-guard` flag to rustc + // when compiling the standard library, since this might be linked into the final outputs + // produced by rustc. Since this mitigation is only available on Windows, only enable it + // for the standard library in case the compiler is run on a non-Windows platform. + // This is not needed for stage 0 artifacts because these will only be used for building + // the stage 1 compiler. + if cfg!(windows) + && mode == Mode::Std + && self.config.control_flow_guard + && compiler.stage >= 1 + { + rustflags.arg("-Ccontrol-flow-guard"); + } + + // If EHCont Guard is enabled, pass the `-Zehcont-guard` flag to rustc when compiling the + // standard library, since this might be linked into the final outputs produced by rustc. + // Since this mitigation is only available on Windows, only enable it for the standard + // library in case the compiler is run on a non-Windows platform. + // This is not needed for stage 0 artifacts because these will only be used for building + // the stage 1 compiler. + if cfg!(windows) && mode == Mode::Std && self.config.ehcont_guard && compiler.stage >= 1 { + rustflags.arg("-Zehcont-guard"); + } + + // For `cargo doc` invocations, make rustdoc print the Rust version into the docs + // This replaces spaces with tabs because RUSTDOCFLAGS does not + // support arguments with regular spaces. Hopefully someday Cargo will + // have space support. + let rust_version = self.rust_version().replace(' ', "\t"); + rustdocflags.arg("--crate-version").arg(&rust_version); + + // Environment variables *required* throughout the build + // + // FIXME: should update code to not require this env var + + // The host this new compiler will *run* on. + cargo.env("CFG_COMPILER_HOST_TRIPLE", target.triple); + // The host this new compiler is being *built* on. + cargo.env("CFG_COMPILER_BUILD_TRIPLE", compiler.host.triple); + + // Set this for all builds to make sure doc builds also get it. + cargo.env("CFG_RELEASE_CHANNEL", &self.config.channel); + + // This one's a bit tricky. As of the time of this writing the compiler + // links to the `winapi` crate on crates.io. This crate provides raw + // bindings to Windows system functions, sort of like libc does for + // Unix. This crate also, however, provides "import libraries" for the + // MinGW targets. There's an import library per dll in the windows + // distribution which is what's linked to. These custom import libraries + // are used because the winapi crate can reference Windows functions not + // present in the MinGW import libraries. + // + // For example MinGW may ship libdbghelp.a, but it may not have + // references to all the functions in the dbghelp dll. Instead the + // custom import library for dbghelp in the winapi crates has all this + // information. + // + // Unfortunately for us though the import libraries are linked by + // default via `-ldylib=winapi_foo`. That is, they're linked with the + // `dylib` type with a `winapi_` prefix (so the winapi ones don't + // conflict with the system MinGW ones). This consequently means that + // the binaries we ship of things like rustc_codegen_llvm (aka the rustc_codegen_llvm + // DLL) when linked against *again*, for example with procedural macros + // or plugins, will trigger the propagation logic of `-ldylib`, passing + // `-lwinapi_foo` to the linker again. This isn't actually available in + // our distribution, however, so the link fails. + // + // To solve this problem we tell winapi to not use its bundled import + // libraries. This means that it will link to the system MinGW import + // libraries by default, and the `-ldylib=foo` directives will still get + // passed to the final linker, but they'll look like `-lfoo` which can + // be resolved because MinGW has the import library. The downside is we + // don't get newer functions from Windows, but we don't use any of them + // anyway. + if !mode.is_tool() { + cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1"); + } + + for _ in 0..self.verbosity { + cargo.arg("-v"); + } + + match (mode, self.config.rust_codegen_units_std, self.config.rust_codegen_units) { + (Mode::Std, Some(n), _) | (_, _, Some(n)) => { + cargo.env(profile_var("CODEGEN_UNITS"), n.to_string()); + } + _ => { + // Don't set anything + } + } + + if self.config.locked_deps { + cargo.arg("--locked"); + } + if self.config.vendor || self.is_sudo { + cargo.arg("--frozen"); + } + + // Try to use a sysroot-relative bindir, in case it was configured absolutely. + cargo.env("RUSTC_INSTALL_BINDIR", self.config.bindir_relative()); + + cargo.force_coloring_in_ci(); + + // When we build Rust dylibs they're all intended for intermediate + // usage, so make sure we pass the -Cprefer-dynamic flag instead of + // linking all deps statically into the dylib. + if matches!(mode, Mode::Std) { + rustflags.arg("-Cprefer-dynamic"); + } + if matches!(mode, Mode::Rustc) && !self.link_std_into_rustc_driver(target) { + rustflags.arg("-Cprefer-dynamic"); + } + + cargo.env( + "RUSTC_LINK_STD_INTO_RUSTC_DRIVER", + if self.link_std_into_rustc_driver(target) { "1" } else { "0" }, + ); + + // When building incrementally we default to a lower ThinLTO import limit + // (unless explicitly specified otherwise). This will produce a somewhat + // slower code but give way better compile times. + { + let limit = match self.config.rust_thin_lto_import_instr_limit { + Some(limit) => Some(limit), + None if self.config.incremental => Some(10), + _ => None, + }; + + if let Some(limit) = limit { + if stage == 0 + || self.config.default_codegen_backend(target).unwrap_or_default() == "llvm" + { + rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}")); + } + } + } + + if matches!(mode, Mode::Std) { + if let Some(mir_opt_level) = self.config.rust_validate_mir_opts { + rustflags.arg("-Zvalidate-mir"); + rustflags.arg(&format!("-Zmir-opt-level={mir_opt_level}")); + } + if self.config.rust_randomize_layout { + rustflags.arg("--cfg=randomized_layouts"); + } + // Always enable inlining MIR when building the standard library. + // Without this flag, MIR inlining is disabled when incremental compilation is enabled. + // That causes some mir-opt tests which inline functions from the standard library to + // break when incremental compilation is enabled. So this overrides the "no inlining + // during incremental builds" heuristic for the standard library. + rustflags.arg("-Zinline-mir"); + + // Similarly, we need to keep debug info for functions inlined into other std functions, + // even if we're not going to output debuginfo for the crate we're currently building, + // so that it'll be available when downstream consumers of std try to use it. + rustflags.arg("-Zinline-mir-preserve-debug"); + } + + if self.config.rustc_parallel + && matches!(mode, Mode::ToolRustc | Mode::Rustc | Mode::Codegen) + { + // keep in sync with `bootstrap/lib.rs:Build::rustc_features` + // `cfg` option for rustc, `features` option for cargo, for conditional compilation + rustflags.arg("--cfg=parallel_compiler"); + rustdocflags.arg("--cfg=parallel_compiler"); + } + + Cargo { + command: cargo, + compiler, + target, + rustflags, + rustdocflags, + hostflags, + allow_features, + } + } +} diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder/mod.rs similarity index 51% rename from src/bootstrap/src/core/builder.rs rename to src/bootstrap/src/core/builder/mod.rs index 15c6f303f94a6..8f8ab1128574c 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1,7 +1,8 @@ +mod cargo; + use std::any::{Any, type_name}; use std::cell::{Cell, RefCell}; use std::collections::BTreeSet; -use std::ffi::{OsStr, OsString}; use std::fmt::{Debug, Write}; use std::hash::Hash; use std::ops::Deref; @@ -12,22 +13,17 @@ use std::{env, fs}; use clap::ValueEnum; +pub use self::cargo::Cargo; pub use crate::Compiler; -use crate::core::build_steps::tool::{self, SourceType}; use crate::core::build_steps::{ - check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, vendor, + check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, tool, vendor, }; -use crate::core::config::flags::{Color, Subcommand}; -use crate::core::config::{DryRun, SplitDebuginfo, TargetSelection}; +use crate::core::config::flags::Subcommand; +use crate::core::config::{DryRun, TargetSelection}; use crate::utils::cache::Cache; use crate::utils::exec::{BootstrapCommand, command}; -use crate::utils::helpers::{ - self, LldThreads, add_dylib_path, add_link_lib_path, check_cfg_arg, exe, libdir, linker_args, - linker_flags, t, -}; -use crate::{ - Build, CLang, Crate, DocTests, EXTRA_CHECK_CFGS, GitRepo, Mode, prepare_behaviour_dump_dir, -}; +use crate::utils::helpers::{self, LldThreads, add_dylib_path, exe, libdir, linker_args, t}; +use crate::{Build, Crate}; #[cfg(test)] mod tests; @@ -1010,6 +1006,7 @@ impl<'a> Builder<'a> { run::GenerateCopyright, run::GenerateWindowsSys, run::GenerateCompletions, + run::UnicodeTableGenerator, ), Kind::Setup => { describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor) @@ -1409,870 +1406,6 @@ impl<'a> Builder<'a> { None } - /// Like `cargo`, but only passes flags that are valid for all commands. - pub fn bare_cargo( - &self, - compiler: Compiler, - mode: Mode, - target: TargetSelection, - cmd_kind: Kind, - ) -> BootstrapCommand { - let mut cargo = match cmd_kind { - Kind::Clippy => { - let mut cargo = self.cargo_clippy_cmd(compiler); - cargo.arg(cmd_kind.as_str()); - cargo - } - Kind::MiriSetup => { - let mut cargo = self.cargo_miri_cmd(compiler); - cargo.arg("miri").arg("setup"); - cargo - } - Kind::MiriTest => { - let mut cargo = self.cargo_miri_cmd(compiler); - cargo.arg("miri").arg("test"); - cargo - } - _ => { - let mut cargo = command(&self.initial_cargo); - cargo.arg(cmd_kind.as_str()); - cargo - } - }; - - // Run cargo from the source root so it can find .cargo/config. - // This matters when using vendoring and the working directory is outside the repository. - cargo.current_dir(&self.src); - - let out_dir = self.stage_out(compiler, mode); - cargo.env("CARGO_TARGET_DIR", &out_dir); - - // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger` - // from out of tree it shouldn't matter, since x.py is only used for - // building in-tree. - let color_logs = ["RUSTDOC_LOG_COLOR", "RUSTC_LOG_COLOR", "RUST_LOG_COLOR"]; - match self.build.config.color { - Color::Always => { - cargo.arg("--color=always"); - for log in &color_logs { - cargo.env(log, "always"); - } - } - Color::Never => { - cargo.arg("--color=never"); - for log in &color_logs { - cargo.env(log, "never"); - } - } - Color::Auto => {} // nothing to do - } - - if cmd_kind != Kind::Install { - cargo.arg("--target").arg(target.rustc_target_arg()); - } else { - assert_eq!(target, compiler.host); - } - - if self.config.rust_optimize.is_release() && - // cargo bench/install do not accept `--release` and miri doesn't want it - !matches!(cmd_kind, Kind::Bench | Kind::Install | Kind::Miri | Kind::MiriSetup | Kind::MiriTest) - { - cargo.arg("--release"); - } - - // Remove make-related flags to ensure Cargo can correctly set things up - cargo.env_remove("MAKEFLAGS"); - cargo.env_remove("MFLAGS"); - - cargo - } - - /// This will create a `Command` that represents a pending execution of - /// Cargo. This cargo will be configured to use `compiler` as the actual - /// rustc compiler, its output will be scoped by `mode`'s output directory, - /// it will pass the `--target` flag for the specified `target`, and will be - /// executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for commands - /// to be run with Miri. - fn cargo( - &self, - compiler: Compiler, - mode: Mode, - source_type: SourceType, - target: TargetSelection, - cmd_kind: Kind, - ) -> Cargo { - let mut cargo = self.bare_cargo(compiler, mode, target, cmd_kind); - let out_dir = self.stage_out(compiler, mode); - - let mut hostflags = HostFlags::default(); - - // Codegen backends are not yet tracked by -Zbinary-dep-depinfo, - // so we need to explicitly clear out if they've been updated. - for backend in self.codegen_backends(compiler) { - self.clear_if_dirty(&out_dir, &backend); - } - - if cmd_kind == Kind::Doc { - let my_out = match mode { - // This is the intended out directory for compiler documentation. - Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target), - Mode::Std => { - if self.config.cmd.json() { - out_dir.join(target).join("json-doc") - } else { - out_dir.join(target).join("doc") - } - } - _ => panic!("doc mode {mode:?} not expected"), - }; - let rustdoc = self.rustdoc(compiler); - self.clear_if_dirty(&my_out, &rustdoc); - } - - let profile_var = |name: &str| { - let profile = if self.config.rust_optimize.is_release() { "RELEASE" } else { "DEV" }; - format!("CARGO_PROFILE_{}_{}", profile, name) - }; - - // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config - // needs to not accidentally link to libLLVM in stage0/lib. - cargo.env("REAL_LIBRARY_PATH_VAR", helpers::dylib_path_var()); - if let Some(e) = env::var_os(helpers::dylib_path_var()) { - cargo.env("REAL_LIBRARY_PATH", e); - } - - // Set a flag for `check`/`clippy`/`fix`, so that certain build - // scripts can do less work (i.e. not building/requiring LLVM). - if matches!(cmd_kind, Kind::Check | Kind::Clippy | Kind::Fix) { - // If we've not yet built LLVM, or it's stale, then bust - // the rustc_llvm cache. That will always work, even though it - // may mean that on the next non-check build we'll need to rebuild - // rustc_llvm. But if LLVM is stale, that'll be a tiny amount - // of work comparatively, and we'd likely need to rebuild it anyway, - // so that's okay. - if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target, false) - .should_build() - { - cargo.env("RUST_CHECK", "1"); - } - } - - let stage = if compiler.stage == 0 && self.local_rebuild { - // Assume the local-rebuild rustc already has stage1 features. - 1 - } else { - compiler.stage - }; - - // We synthetically interpret a stage0 compiler used to build tools as a - // "raw" compiler in that it's the exact snapshot we download. Normally - // the stage0 build means it uses libraries build by the stage0 - // compiler, but for tools we just use the precompiled libraries that - // we've downloaded - let use_snapshot = mode == Mode::ToolBootstrap; - assert!(!use_snapshot || stage == 0 || self.local_rebuild); - - let maybe_sysroot = self.sysroot(compiler); - let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot }; - let libdir = self.rustc_libdir(compiler); - - let sysroot_str = sysroot.as_os_str().to_str().expect("sysroot should be UTF-8"); - if self.is_verbose() && !matches!(self.config.dry_run, DryRun::SelfCheck) { - println!("using sysroot {sysroot_str}"); - } - - let mut rustflags = Rustflags::new(target); - if stage != 0 { - if let Ok(s) = env::var("CARGOFLAGS_NOT_BOOTSTRAP") { - cargo.args(s.split_whitespace()); - } - rustflags.env("RUSTFLAGS_NOT_BOOTSTRAP"); - } else { - if let Ok(s) = env::var("CARGOFLAGS_BOOTSTRAP") { - cargo.args(s.split_whitespace()); - } - rustflags.env("RUSTFLAGS_BOOTSTRAP"); - rustflags.arg("--cfg=bootstrap"); - } - - if cmd_kind == Kind::Clippy { - // clippy overwrites sysroot if we pass it to cargo. - // Pass it directly to clippy instead. - // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`, - // so it has no way of knowing the sysroot. - rustflags.arg("--sysroot"); - rustflags.arg(sysroot_str); - } - - let use_new_symbol_mangling = match self.config.rust_new_symbol_mangling { - Some(setting) => { - // If an explicit setting is given, use that - setting - } - None => { - if mode == Mode::Std { - // The standard library defaults to the legacy scheme - false - } else { - // The compiler and tools default to the new scheme - true - } - } - }; - - // By default, windows-rs depends on a native library that doesn't get copied into the - // sysroot. Passing this cfg enables raw-dylib support instead, which makes the native - // library unnecessary. This can be removed when windows-rs enables raw-dylib - // unconditionally. - if let Mode::Rustc | Mode::ToolRustc = mode { - rustflags.arg("--cfg=windows_raw_dylib"); - } - - if use_new_symbol_mangling { - rustflags.arg("-Csymbol-mangling-version=v0"); - } else { - rustflags.arg("-Csymbol-mangling-version=legacy"); - } - - // FIXME: the following components don't build with `-Zrandomize-layout` yet: - // - wasm-component-ld, due to the `wast`crate - // - rust-analyzer, due to the rowan crate - // so we exclude entire categories of steps here due to lack of fine-grained control over - // rustflags. - if self.config.rust_randomize_layout && mode != Mode::ToolStd && mode != Mode::ToolRustc { - rustflags.arg("-Zrandomize-layout"); - } - - // Enable compile-time checking of `cfg` names, values and Cargo `features`. - // - // Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like - // backtrace, core_simd, std_float, ...), those dependencies have their own - // features but cargo isn't involved in the #[path] process and so cannot pass the - // complete list of features, so for that reason we don't enable checking of - // features for std crates. - if mode == Mode::Std { - rustflags.arg("--check-cfg=cfg(feature,values(any()))"); - } - - // Add extra cfg not defined in/by rustc - // - // Note: Although it would seems that "-Zunstable-options" to `rustflags` is useless as - // cargo would implicitly add it, it was discover that sometimes bootstrap only use - // `rustflags` without `cargo` making it required. - rustflags.arg("-Zunstable-options"); - for (restricted_mode, name, values) in EXTRA_CHECK_CFGS { - if restricted_mode.is_none() || *restricted_mode == Some(mode) { - rustflags.arg(&check_cfg_arg(name, *values)); - } - } - - // FIXME(rust-lang/cargo#5754) we shouldn't be using special command arguments - // to the host invocation here, but rather Cargo should know what flags to pass rustc - // itself. - if stage == 0 { - hostflags.arg("--cfg=bootstrap"); - } - // Cargo doesn't pass RUSTFLAGS to proc_macros: - // https://github.com/rust-lang/cargo/issues/4423 - // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`. - // We also declare that the flag is expected, which we need to do to not - // get warnings about it being unexpected. - hostflags.arg("-Zunstable-options"); - hostflags.arg("--check-cfg=cfg(bootstrap)"); - - // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`, - // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See - // #71458. - let mut rustdocflags = rustflags.clone(); - rustdocflags.propagate_cargo_env("RUSTDOCFLAGS"); - if stage == 0 { - rustdocflags.env("RUSTDOCFLAGS_BOOTSTRAP"); - } else { - rustdocflags.env("RUSTDOCFLAGS_NOT_BOOTSTRAP"); - } - - if let Ok(s) = env::var("CARGOFLAGS") { - cargo.args(s.split_whitespace()); - } - - match mode { - Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {} - Mode::Rustc | Mode::Codegen | Mode::ToolRustc => { - // Build proc macros both for the host and the target unless proc-macros are not - // supported by the target. - if target != compiler.host && cmd_kind != Kind::Check { - let error = command(self.rustc(compiler)) - .arg("--target") - .arg(target.rustc_target_arg()) - .arg("--print=file-names") - .arg("--crate-type=proc-macro") - .arg("-") - .run_capture(self) - .stderr(); - let not_supported = error - .lines() - .any(|line| line.contains("unsupported crate type `proc-macro`")); - if !not_supported { - cargo.arg("-Zdual-proc-macros"); - rustflags.arg("-Zdual-proc-macros"); - } - } - } - } - - // This tells Cargo (and in turn, rustc) to output more complete - // dependency information. Most importantly for bootstrap, this - // includes sysroot artifacts, like libstd, which means that we don't - // need to track those in bootstrap (an error prone process!). This - // feature is currently unstable as there may be some bugs and such, but - // it represents a big improvement in bootstrap's reliability on - // rebuilds, so we're using it here. - // - // For some additional context, see #63470 (the PR originally adding - // this), as well as #63012 which is the tracking issue for this - // feature on the rustc side. - cargo.arg("-Zbinary-dep-depinfo"); - let allow_features = match mode { - Mode::ToolBootstrap | Mode::ToolStd => { - // Restrict the allowed features so we don't depend on nightly - // accidentally. - // - // binary-dep-depinfo is used by bootstrap itself for all - // compilations. - // - // Lots of tools depend on proc_macro2 and proc-macro-error. - // Those have build scripts which assume nightly features are - // available if the `rustc` version is "nighty" or "dev". See - // bin/rustc.rs for why that is a problem. Instead of labeling - // those features for each individual tool that needs them, - // just blanket allow them here. - // - // If this is ever removed, be sure to add something else in - // its place to keep the restrictions in place (or make a way - // to unset RUSTC_BOOTSTRAP). - "binary-dep-depinfo,proc_macro_span,proc_macro_span_shrink,proc_macro_diagnostic" - .to_string() - } - Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => String::new(), - }; - - cargo.arg("-j").arg(self.jobs().to_string()); - - // FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005 - // Force cargo to output binaries with disambiguating hashes in the name - let mut metadata = if compiler.stage == 0 { - // Treat stage0 like a special channel, whether it's a normal prior- - // release rustc or a local rebuild with the same version, so we - // never mix these libraries by accident. - "bootstrap".to_string() - } else { - self.config.channel.to_string() - }; - // We want to make sure that none of the dependencies between - // std/test/rustc unify with one another. This is done for weird linkage - // reasons but the gist of the problem is that if librustc, libtest, and - // libstd all depend on libc from crates.io (which they actually do) we - // want to make sure they all get distinct versions. Things get really - // weird if we try to unify all these dependencies right now, namely - // around how many times the library is linked in dynamic libraries and - // such. If rustc were a static executable or if we didn't ship dylibs - // this wouldn't be a problem, but we do, so it is. This is in general - // just here to make sure things build right. If you can remove this and - // things still build right, please do! - match mode { - Mode::Std => metadata.push_str("std"), - // When we're building rustc tools, they're built with a search path - // that contains things built during the rustc build. For example, - // bitflags is built during the rustc build, and is a dependency of - // rustdoc as well. We're building rustdoc in a different target - // directory, though, which means that Cargo will rebuild the - // dependency. When we go on to build rustdoc, we'll look for - // bitflags, and find two different copies: one built during the - // rustc step and one that we just built. This isn't always a - // problem, somehow -- not really clear why -- but we know that this - // fixes things. - Mode::ToolRustc => metadata.push_str("tool-rustc"), - // Same for codegen backends. - Mode::Codegen => metadata.push_str("codegen"), - _ => {} - } - cargo.env("__CARGO_DEFAULT_LIB_METADATA", &metadata); - - if cmd_kind == Kind::Clippy { - rustflags.arg("-Zforce-unstable-if-unmarked"); - } - - rustflags.arg("-Zmacro-backtrace"); - - let want_rustdoc = self.doc_tests != DocTests::No; - - // Clear the output directory if the real rustc we're using has changed; - // Cargo cannot detect this as it thinks rustc is bootstrap/debug/rustc. - // - // Avoid doing this during dry run as that usually means the relevant - // compiler is not yet linked/copied properly. - // - // Only clear out the directory if we're compiling std; otherwise, we - // should let Cargo take care of things for us (via depdep info) - if !self.config.dry_run() && mode == Mode::Std && cmd_kind == Kind::Build { - self.clear_if_dirty(&out_dir, &self.rustc(compiler)); - } - - let rustdoc_path = match cmd_kind { - Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc(compiler), - _ => PathBuf::from("/path/to/nowhere/rustdoc/not/required"), - }; - - // Customize the compiler we're running. Specify the compiler to cargo - // as our shim and then pass it some various options used to configure - // how the actual compiler itself is called. - // - // These variables are primarily all read by - // src/bootstrap/bin/{rustc.rs,rustdoc.rs} - cargo - .env("RUSTBUILD_NATIVE_DIR", self.native_dir(target)) - .env("RUSTC_REAL", self.rustc(compiler)) - .env("RUSTC_STAGE", stage.to_string()) - .env("RUSTC_SYSROOT", sysroot) - .env("RUSTC_LIBDIR", libdir) - .env("RUSTDOC", self.bootstrap_out.join("rustdoc")) - .env("RUSTDOC_REAL", rustdoc_path) - .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir()) - .env("RUSTC_BREAK_ON_ICE", "1"); - - // Set RUSTC_WRAPPER to the bootstrap shim, which switches between beta and in-tree - // sysroot depending on whether we're building build scripts. - // NOTE: we intentionally use RUSTC_WRAPPER so that we can support clippy - RUSTC is not - // respected by clippy-driver; RUSTC_WRAPPER happens earlier, before clippy runs. - cargo.env("RUSTC_WRAPPER", self.bootstrap_out.join("rustc")); - // NOTE: we also need to set RUSTC so cargo can run `rustc -vV`; apparently that ignores RUSTC_WRAPPER >:( - cargo.env("RUSTC", self.bootstrap_out.join("rustc")); - - // Someone might have set some previous rustc wrapper (e.g. - // sccache) before bootstrap overrode it. Respect that variable. - if let Some(existing_wrapper) = env::var_os("RUSTC_WRAPPER") { - cargo.env("RUSTC_WRAPPER_REAL", existing_wrapper); - } - - // If this is for `miri-test`, prepare the sysroots. - if cmd_kind == Kind::MiriTest { - self.ensure(compile::Std::new(compiler, compiler.host)); - let host_sysroot = self.sysroot(compiler); - let miri_sysroot = test::Miri::build_miri_sysroot(self, compiler, target); - cargo.env("MIRI_SYSROOT", &miri_sysroot); - cargo.env("MIRI_HOST_SYSROOT", &host_sysroot); - } - - cargo.env(profile_var("STRIP"), self.config.rust_strip.to_string()); - - if let Some(stack_protector) = &self.config.rust_stack_protector { - rustflags.arg(&format!("-Zstack-protector={stack_protector}")); - } - - if !matches!(cmd_kind, Kind::Build | Kind::Check | Kind::Clippy | Kind::Fix) && want_rustdoc - { - cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler)); - } - - let debuginfo_level = match mode { - Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc, - Mode::Std => self.config.rust_debuginfo_level_std, - Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc => { - self.config.rust_debuginfo_level_tools - } - }; - cargo.env(profile_var("DEBUG"), debuginfo_level.to_string()); - if let Some(opt_level) = &self.config.rust_optimize.get_opt_level() { - cargo.env(profile_var("OPT_LEVEL"), opt_level); - } - cargo.env( - profile_var("DEBUG_ASSERTIONS"), - if mode == Mode::Std { - self.config.rust_debug_assertions_std.to_string() - } else { - self.config.rust_debug_assertions.to_string() - }, - ); - cargo.env( - profile_var("OVERFLOW_CHECKS"), - if mode == Mode::Std { - self.config.rust_overflow_checks_std.to_string() - } else { - self.config.rust_overflow_checks.to_string() - }, - ); - - match self.config.split_debuginfo(target) { - SplitDebuginfo::Packed => rustflags.arg("-Csplit-debuginfo=packed"), - SplitDebuginfo::Unpacked => rustflags.arg("-Csplit-debuginfo=unpacked"), - SplitDebuginfo::Off => rustflags.arg("-Csplit-debuginfo=off"), - }; - - if self.config.cmd.bless() { - // Bless `expect!` tests. - cargo.env("UPDATE_EXPECT", "1"); - } - - if !mode.is_tool() { - cargo.env("RUSTC_FORCE_UNSTABLE", "1"); - } - - if let Some(x) = self.crt_static(target) { - if x { - rustflags.arg("-Ctarget-feature=+crt-static"); - } else { - rustflags.arg("-Ctarget-feature=-crt-static"); - } - } - - if let Some(x) = self.crt_static(compiler.host) { - let sign = if x { "+" } else { "-" }; - hostflags.arg(format!("-Ctarget-feature={sign}crt-static")); - } - - if let Some(map_to) = self.build.debuginfo_map_to(GitRepo::Rustc) { - let map = format!("{}={}", self.build.src.display(), map_to); - cargo.env("RUSTC_DEBUGINFO_MAP", map); - - // `rustc` needs to know the virtual `/rustc/$hash` we're mapping to, - // in order to opportunistically reverse it later. - cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to); - } - - if self.config.rust_remap_debuginfo { - let mut env_var = OsString::new(); - if self.config.vendor { - let vendor = self.build.src.join("vendor"); - env_var.push(vendor); - env_var.push("=/rust/deps"); - } else { - let registry_src = t!(home::cargo_home()).join("registry").join("src"); - for entry in t!(std::fs::read_dir(registry_src)) { - if !env_var.is_empty() { - env_var.push("\t"); - } - env_var.push(t!(entry).path()); - env_var.push("=/rust/deps"); - } - } - cargo.env("RUSTC_CARGO_REGISTRY_SRC_TO_REMAP", env_var); - } - - // Enable usage of unstable features - cargo.env("RUSTC_BOOTSTRAP", "1"); - - if self.config.dump_bootstrap_shims { - prepare_behaviour_dump_dir(self.build); - - cargo - .env("DUMP_BOOTSTRAP_SHIMS", self.build.out.join("bootstrap-shims-dump")) - .env("BUILD_OUT", &self.build.out) - .env("CARGO_HOME", t!(home::cargo_home())); - }; - - self.add_rust_test_threads(&mut cargo); - - // Almost all of the crates that we compile as part of the bootstrap may - // have a build script, including the standard library. To compile a - // build script, however, it itself needs a standard library! This - // introduces a bit of a pickle when we're compiling the standard - // library itself. - // - // To work around this we actually end up using the snapshot compiler - // (stage0) for compiling build scripts of the standard library itself. - // The stage0 compiler is guaranteed to have a libstd available for use. - // - // For other crates, however, we know that we've already got a standard - // library up and running, so we can use the normal compiler to compile - // build scripts in that situation. - if mode == Mode::Std { - cargo - .env("RUSTC_SNAPSHOT", &self.initial_rustc) - .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir()); - } else { - cargo - .env("RUSTC_SNAPSHOT", self.rustc(compiler)) - .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_libdir(compiler)); - } - - // Tools that use compiler libraries may inherit the `-lLLVM` link - // requirement, but the `-L` library path is not propagated across - // separate Cargo projects. We can add LLVM's library path to the - // platform-specific environment variable as a workaround. - if mode == Mode::ToolRustc || mode == Mode::Codegen { - if let Some(llvm_config) = self.llvm_config(target) { - let llvm_libdir = - command(llvm_config).arg("--libdir").run_capture_stdout(self).stdout(); - add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cargo); - } - } - - // Compile everything except libraries and proc macros with the more - // efficient initial-exec TLS model. This doesn't work with `dlopen`, - // so we can't use it by default in general, but we can use it for tools - // and our own internal libraries. - if !mode.must_support_dlopen() && !target.triple.starts_with("powerpc-") { - cargo.env("RUSTC_TLS_MODEL_INITIAL_EXEC", "1"); - } - - // Ignore incremental modes except for stage0, since we're - // not guaranteeing correctness across builds if the compiler - // is changing under your feet. - if self.config.incremental && compiler.stage == 0 { - cargo.env("CARGO_INCREMENTAL", "1"); - } else { - // Don't rely on any default setting for incr. comp. in Cargo - cargo.env("CARGO_INCREMENTAL", "0"); - } - - if let Some(ref on_fail) = self.config.on_fail { - cargo.env("RUSTC_ON_FAIL", on_fail); - } - - if self.config.print_step_timings { - cargo.env("RUSTC_PRINT_STEP_TIMINGS", "1"); - } - - if self.config.print_step_rusage { - cargo.env("RUSTC_PRINT_STEP_RUSAGE", "1"); - } - - if self.config.backtrace_on_ice { - cargo.env("RUSTC_BACKTRACE_ON_ICE", "1"); - } - - if self.is_verbose() { - // This provides very useful logs especially when debugging build cache-related stuff. - cargo.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); - } - - cargo.env("RUSTC_VERBOSE", self.verbosity.to_string()); - - // Downstream forks of the Rust compiler might want to use a custom libc to add support for - // targets that are not yet available upstream. Adding a patch to replace libc with a - // custom one would cause compilation errors though, because Cargo would interpret the - // custom libc as part of the workspace, and apply the check-cfg lints on it. - // - // The libc build script emits check-cfg flags only when this environment variable is set, - // so this line allows the use of custom libcs. - cargo.env("LIBC_CHECK_CFG", "1"); - - if source_type == SourceType::InTree { - let mut lint_flags = Vec::new(); - // When extending this list, add the new lints to the RUSTFLAGS of the - // build_bootstrap function of src/bootstrap/bootstrap.py as well as - // some code doesn't go through this `rustc` wrapper. - lint_flags.push("-Wrust_2018_idioms"); - lint_flags.push("-Wunused_lifetimes"); - - if self.config.deny_warnings { - lint_flags.push("-Dwarnings"); - rustdocflags.arg("-Dwarnings"); - } - - // This does not use RUSTFLAGS due to caching issues with Cargo. - // Clippy is treated as an "in tree" tool, but shares the same - // cache as other "submodule" tools. With these options set in - // RUSTFLAGS, that causes *every* shared dependency to be rebuilt. - // By injecting this into the rustc wrapper, this circumvents - // Cargo's fingerprint detection. This is fine because lint flags - // are always ignored in dependencies. Eventually this should be - // fixed via better support from Cargo. - cargo.env("RUSTC_LINT_FLAGS", lint_flags.join(" ")); - - rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes"); - } - - if mode == Mode::Rustc { - rustflags.arg("-Wrustc::internal"); - // FIXME(edition_2024): Change this to `-Wrust_2024_idioms` when all - // of the individual lints are satisfied. - rustflags.arg("-Wkeyword_idents_2024"); - rustflags.arg("-Wunsafe_op_in_unsafe_fn"); - } - - if self.config.rust_frame_pointers { - rustflags.arg("-Cforce-frame-pointers=true"); - } - - // If Control Flow Guard is enabled, pass the `control-flow-guard` flag to rustc - // when compiling the standard library, since this might be linked into the final outputs - // produced by rustc. Since this mitigation is only available on Windows, only enable it - // for the standard library in case the compiler is run on a non-Windows platform. - // This is not needed for stage 0 artifacts because these will only be used for building - // the stage 1 compiler. - if cfg!(windows) - && mode == Mode::Std - && self.config.control_flow_guard - && compiler.stage >= 1 - { - rustflags.arg("-Ccontrol-flow-guard"); - } - - // If EHCont Guard is enabled, pass the `-Zehcont-guard` flag to rustc when compiling the - // standard library, since this might be linked into the final outputs produced by rustc. - // Since this mitigation is only available on Windows, only enable it for the standard - // library in case the compiler is run on a non-Windows platform. - // This is not needed for stage 0 artifacts because these will only be used for building - // the stage 1 compiler. - if cfg!(windows) && mode == Mode::Std && self.config.ehcont_guard && compiler.stage >= 1 { - rustflags.arg("-Zehcont-guard"); - } - - // For `cargo doc` invocations, make rustdoc print the Rust version into the docs - // This replaces spaces with tabs because RUSTDOCFLAGS does not - // support arguments with regular spaces. Hopefully someday Cargo will - // have space support. - let rust_version = self.rust_version().replace(' ', "\t"); - rustdocflags.arg("--crate-version").arg(&rust_version); - - // Environment variables *required* throughout the build - // - // FIXME: should update code to not require this env var - - // The host this new compiler will *run* on. - cargo.env("CFG_COMPILER_HOST_TRIPLE", target.triple); - // The host this new compiler is being *built* on. - cargo.env("CFG_COMPILER_BUILD_TRIPLE", compiler.host.triple); - - // Set this for all builds to make sure doc builds also get it. - cargo.env("CFG_RELEASE_CHANNEL", &self.config.channel); - - // This one's a bit tricky. As of the time of this writing the compiler - // links to the `winapi` crate on crates.io. This crate provides raw - // bindings to Windows system functions, sort of like libc does for - // Unix. This crate also, however, provides "import libraries" for the - // MinGW targets. There's an import library per dll in the windows - // distribution which is what's linked to. These custom import libraries - // are used because the winapi crate can reference Windows functions not - // present in the MinGW import libraries. - // - // For example MinGW may ship libdbghelp.a, but it may not have - // references to all the functions in the dbghelp dll. Instead the - // custom import library for dbghelp in the winapi crates has all this - // information. - // - // Unfortunately for us though the import libraries are linked by - // default via `-ldylib=winapi_foo`. That is, they're linked with the - // `dylib` type with a `winapi_` prefix (so the winapi ones don't - // conflict with the system MinGW ones). This consequently means that - // the binaries we ship of things like rustc_codegen_llvm (aka the rustc_codegen_llvm - // DLL) when linked against *again*, for example with procedural macros - // or plugins, will trigger the propagation logic of `-ldylib`, passing - // `-lwinapi_foo` to the linker again. This isn't actually available in - // our distribution, however, so the link fails. - // - // To solve this problem we tell winapi to not use its bundled import - // libraries. This means that it will link to the system MinGW import - // libraries by default, and the `-ldylib=foo` directives will still get - // passed to the final linker, but they'll look like `-lfoo` which can - // be resolved because MinGW has the import library. The downside is we - // don't get newer functions from Windows, but we don't use any of them - // anyway. - if !mode.is_tool() { - cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1"); - } - - for _ in 0..self.verbosity { - cargo.arg("-v"); - } - - match (mode, self.config.rust_codegen_units_std, self.config.rust_codegen_units) { - (Mode::Std, Some(n), _) | (_, _, Some(n)) => { - cargo.env(profile_var("CODEGEN_UNITS"), n.to_string()); - } - _ => { - // Don't set anything - } - } - - if self.config.locked_deps { - cargo.arg("--locked"); - } - if self.config.vendor || self.is_sudo { - cargo.arg("--frozen"); - } - - // Try to use a sysroot-relative bindir, in case it was configured absolutely. - cargo.env("RUSTC_INSTALL_BINDIR", self.config.bindir_relative()); - - cargo.force_coloring_in_ci(); - - // When we build Rust dylibs they're all intended for intermediate - // usage, so make sure we pass the -Cprefer-dynamic flag instead of - // linking all deps statically into the dylib. - if matches!(mode, Mode::Std) { - rustflags.arg("-Cprefer-dynamic"); - } - if matches!(mode, Mode::Rustc) && !self.link_std_into_rustc_driver(target) { - rustflags.arg("-Cprefer-dynamic"); - } - - cargo.env( - "RUSTC_LINK_STD_INTO_RUSTC_DRIVER", - if self.link_std_into_rustc_driver(target) { "1" } else { "0" }, - ); - - // When building incrementally we default to a lower ThinLTO import limit - // (unless explicitly specified otherwise). This will produce a somewhat - // slower code but give way better compile times. - { - let limit = match self.config.rust_thin_lto_import_instr_limit { - Some(limit) => Some(limit), - None if self.config.incremental => Some(10), - _ => None, - }; - - if let Some(limit) = limit { - if stage == 0 - || self.config.default_codegen_backend(target).unwrap_or_default() == "llvm" - { - rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}")); - } - } - } - - if matches!(mode, Mode::Std) { - if let Some(mir_opt_level) = self.config.rust_validate_mir_opts { - rustflags.arg("-Zvalidate-mir"); - rustflags.arg(&format!("-Zmir-opt-level={mir_opt_level}")); - } - if self.config.rust_randomize_layout { - rustflags.arg("--cfg=randomized_layouts"); - } - // Always enable inlining MIR when building the standard library. - // Without this flag, MIR inlining is disabled when incremental compilation is enabled. - // That causes some mir-opt tests which inline functions from the standard library to - // break when incremental compilation is enabled. So this overrides the "no inlining - // during incremental builds" heuristic for the standard library. - rustflags.arg("-Zinline-mir"); - - // Similarly, we need to keep debug info for functions inlined into other std functions, - // even if we're not going to output debuginfo for the crate we're currently building, - // so that it'll be available when downstream consumers of std try to use it. - rustflags.arg("-Zinline-mir-preserve-debug"); - } - - if self.config.rustc_parallel - && matches!(mode, Mode::ToolRustc | Mode::Rustc | Mode::Codegen) - { - // keep in sync with `bootstrap/lib.rs:Build::rustc_features` - // `cfg` option for rustc, `features` option for cargo, for conditional compilation - rustflags.arg("--cfg=parallel_compiler"); - rustdocflags.arg("--cfg=parallel_compiler"); - } - - Cargo { - command: cargo, - compiler, - target, - rustflags, - rustdocflags, - hostflags, - allow_features, - } - } - /// Ensure that a given step is built, returning its output. This will /// cache the step, so it is safe (and good!) to call this as often as /// needed to ensure that all dependencies are built. @@ -2398,337 +1531,3 @@ impl<'a> Builder<'a> { } } } - -/// Represents flag values in `String` form with whitespace delimiter to pass it to the compiler later. -/// -/// `-Z crate-attr` flags will be applied recursively on the target code using the `rustc_parse::parser::Parser`. -/// See `rustc_builtin_macros::cmdline_attrs::inject` for more information. -#[derive(Debug, Clone)] -struct Rustflags(String, TargetSelection); - -impl Rustflags { - fn new(target: TargetSelection) -> Rustflags { - let mut ret = Rustflags(String::new(), target); - ret.propagate_cargo_env("RUSTFLAGS"); - ret - } - - /// By default, cargo will pick up on various variables in the environment. However, bootstrap - /// reuses those variables to pass additional flags to rustdoc, so by default they get overridden. - /// Explicitly add back any previous value in the environment. - /// - /// `prefix` is usually `RUSTFLAGS` or `RUSTDOCFLAGS`. - fn propagate_cargo_env(&mut self, prefix: &str) { - // Inherit `RUSTFLAGS` by default ... - self.env(prefix); - - // ... and also handle target-specific env RUSTFLAGS if they're configured. - let target_specific = format!("CARGO_TARGET_{}_{}", crate::envify(&self.1.triple), prefix); - self.env(&target_specific); - } - - fn env(&mut self, env: &str) { - if let Ok(s) = env::var(env) { - for part in s.split(' ') { - self.arg(part); - } - } - } - - fn arg(&mut self, arg: &str) -> &mut Self { - assert_eq!(arg.split(' ').count(), 1); - if !self.0.is_empty() { - self.0.push(' '); - } - self.0.push_str(arg); - self - } -} - -/// Flags that are passed to the `rustc` shim binary. -/// These flags will only be applied when compiling host code, i.e. when -/// `--target` is unset. -#[derive(Debug, Default)] -pub struct HostFlags { - rustc: Vec, -} - -impl HostFlags { - const SEPARATOR: &'static str = " "; - - /// Adds a host rustc flag. - fn arg>(&mut self, flag: S) { - let value = flag.into().trim().to_string(); - assert!(!value.contains(Self::SEPARATOR)); - self.rustc.push(value); - } - - /// Encodes all the flags into a single string. - fn encode(self) -> String { - self.rustc.join(Self::SEPARATOR) - } -} - -#[derive(Debug)] -pub struct Cargo { - command: BootstrapCommand, - compiler: Compiler, - target: TargetSelection, - rustflags: Rustflags, - rustdocflags: Rustflags, - hostflags: HostFlags, - allow_features: String, -} - -impl Cargo { - /// Calls `Builder::cargo` and `Cargo::configure_linker` to prepare an invocation of `cargo` to be run. - pub fn new( - builder: &Builder<'_>, - compiler: Compiler, - mode: Mode, - source_type: SourceType, - target: TargetSelection, - cmd_kind: Kind, - ) -> Cargo { - let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd_kind); - - match cmd_kind { - // No need to configure the target linker for these command types, - // as they don't invoke rustc at all. - Kind::Clean | Kind::Suggest | Kind::Format | Kind::Setup => {} - _ => { - cargo.configure_linker(builder); - } - } - - cargo - } - - pub fn into_cmd(self) -> BootstrapCommand { - self.into() - } - - /// Same as `Cargo::new` except this one doesn't configure the linker with `Cargo::configure_linker` - pub fn new_for_mir_opt_tests( - builder: &Builder<'_>, - compiler: Compiler, - mode: Mode, - source_type: SourceType, - target: TargetSelection, - cmd_kind: Kind, - ) -> Cargo { - builder.cargo(compiler, mode, source_type, target, cmd_kind) - } - - pub fn rustdocflag(&mut self, arg: &str) -> &mut Cargo { - self.rustdocflags.arg(arg); - self - } - - pub fn rustflag(&mut self, arg: &str) -> &mut Cargo { - self.rustflags.arg(arg); - self - } - - pub fn arg(&mut self, arg: impl AsRef) -> &mut Cargo { - self.command.arg(arg.as_ref()); - self - } - - pub fn args(&mut self, args: I) -> &mut Cargo - where - I: IntoIterator, - S: AsRef, - { - for arg in args { - self.arg(arg.as_ref()); - } - self - } - - pub fn env(&mut self, key: impl AsRef, value: impl AsRef) -> &mut Cargo { - // These are managed through rustflag/rustdocflag interfaces. - assert_ne!(key.as_ref(), "RUSTFLAGS"); - assert_ne!(key.as_ref(), "RUSTDOCFLAGS"); - self.command.env(key.as_ref(), value.as_ref()); - self - } - - pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>) { - builder.add_rustc_lib_path(self.compiler, &mut self.command); - } - - pub fn current_dir(&mut self, dir: &Path) -> &mut Cargo { - self.command.current_dir(dir); - self - } - - /// Adds nightly-only features that this invocation is allowed to use. - /// - /// By default, all nightly features are allowed. Once this is called, it - /// will be restricted to the given set. - pub fn allow_features(&mut self, features: &str) -> &mut Cargo { - if !self.allow_features.is_empty() { - self.allow_features.push(','); - } - self.allow_features.push_str(features); - self - } - - fn configure_linker(&mut self, builder: &Builder<'_>) -> &mut Cargo { - let target = self.target; - let compiler = self.compiler; - - // Dealing with rpath here is a little special, so let's go into some - // detail. First off, `-rpath` is a linker option on Unix platforms - // which adds to the runtime dynamic loader path when looking for - // dynamic libraries. We use this by default on Unix platforms to ensure - // that our nightlies behave the same on Windows, that is they work out - // of the box. This can be disabled by setting `rpath = false` in `[rust]` - // table of `config.toml` - // - // Ok, so the astute might be wondering "why isn't `-C rpath` used - // here?" and that is indeed a good question to ask. This codegen - // option is the compiler's current interface to generating an rpath. - // Unfortunately it doesn't quite suffice for us. The flag currently - // takes no value as an argument, so the compiler calculates what it - // should pass to the linker as `-rpath`. This unfortunately is based on - // the **compile time** directory structure which when building with - // Cargo will be very different than the runtime directory structure. - // - // All that's a really long winded way of saying that if we use - // `-Crpath` then the executables generated have the wrong rpath of - // something like `$ORIGIN/deps` when in fact the way we distribute - // rustc requires the rpath to be `$ORIGIN/../lib`. - // - // So, all in all, to set up the correct rpath we pass the linker - // argument manually via `-C link-args=-Wl,-rpath,...`. Plus isn't it - // fun to pass a flag to a tool to pass a flag to pass a flag to a tool - // to change a flag in a binary? - if builder.config.rpath_enabled(target) && helpers::use_host_linker(target) { - let libdir = builder.sysroot_libdir_relative(compiler).to_str().unwrap(); - let rpath = if target.contains("apple") { - // Note that we need to take one extra step on macOS to also pass - // `-Wl,-instal_name,@rpath/...` to get things to work right. To - // do that we pass a weird flag to the compiler to get it to do - // so. Note that this is definitely a hack, and we should likely - // flesh out rpath support more fully in the future. - self.rustflags.arg("-Zosx-rpath-install-name"); - Some(format!("-Wl,-rpath,@loader_path/../{libdir}")) - } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") { - self.rustflags.arg("-Clink-args=-Wl,-z,origin"); - Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}")) - } else { - None - }; - if let Some(rpath) = rpath { - self.rustflags.arg(&format!("-Clink-args={rpath}")); - } - } - - for arg in linker_args(builder, compiler.host, LldThreads::Yes) { - self.hostflags.arg(&arg); - } - - if let Some(target_linker) = builder.linker(target) { - let target = crate::envify(&target.triple); - self.command.env(format!("CARGO_TARGET_{target}_LINKER"), target_linker); - } - // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not - // `linker_args` here. - for flag in linker_flags(builder, target, LldThreads::Yes) { - self.rustflags.arg(&flag); - } - for arg in linker_args(builder, target, LldThreads::Yes) { - self.rustdocflags.arg(&arg); - } - - if !builder.config.dry_run() - && builder.cc.borrow()[&target].args().iter().any(|arg| arg == "-gz") - { - self.rustflags.arg("-Clink-arg=-gz"); - } - - // Throughout the build Cargo can execute a number of build scripts - // compiling C/C++ code and we need to pass compilers, archivers, flags, etc - // obtained previously to those build scripts. - // Build scripts use either the `cc` crate or `configure/make` so we pass - // the options through environment variables that are fetched and understood by both. - // - // FIXME: the guard against msvc shouldn't need to be here - if target.is_msvc() { - if let Some(ref cl) = builder.config.llvm_clang_cl { - // FIXME: There is a bug in Clang 18 when building for ARM64: - // https://github.com/llvm/llvm-project/pull/81849. This is - // fixed in LLVM 19, but can't be backported. - if !target.starts_with("aarch64") && !target.starts_with("arm64ec") { - self.command.env("CC", cl).env("CXX", cl); - } - } - } else { - let ccache = builder.config.ccache.as_ref(); - let ccacheify = |s: &Path| { - let ccache = match ccache { - Some(ref s) => s, - None => return s.display().to_string(), - }; - // FIXME: the cc-rs crate only recognizes the literal strings - // `ccache` and `sccache` when doing caching compilations, so we - // mirror that here. It should probably be fixed upstream to - // accept a new env var or otherwise work with custom ccache - // vars. - match &ccache[..] { - "ccache" | "sccache" => format!("{} {}", ccache, s.display()), - _ => s.display().to_string(), - } - }; - let triple_underscored = target.triple.replace('-', "_"); - let cc = ccacheify(&builder.cc(target)); - self.command.env(format!("CC_{triple_underscored}"), &cc); - - let cflags = builder.cflags(target, GitRepo::Rustc, CLang::C).join(" "); - self.command.env(format!("CFLAGS_{triple_underscored}"), &cflags); - - if let Some(ar) = builder.ar(target) { - let ranlib = format!("{} s", ar.display()); - self.command - .env(format!("AR_{triple_underscored}"), ar) - .env(format!("RANLIB_{triple_underscored}"), ranlib); - } - - if let Ok(cxx) = builder.cxx(target) { - let cxx = ccacheify(&cxx); - let cxxflags = builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" "); - self.command - .env(format!("CXX_{triple_underscored}"), &cxx) - .env(format!("CXXFLAGS_{triple_underscored}"), cxxflags); - } - } - - self - } -} - -impl From for BootstrapCommand { - fn from(mut cargo: Cargo) -> BootstrapCommand { - let rustflags = &cargo.rustflags.0; - if !rustflags.is_empty() { - cargo.command.env("RUSTFLAGS", rustflags); - } - - let rustdocflags = &cargo.rustdocflags.0; - if !rustdocflags.is_empty() { - cargo.command.env("RUSTDOCFLAGS", rustdocflags); - } - - let encoded_hostflags = cargo.hostflags.encode(); - if !encoded_hostflags.is_empty() { - cargo.command.env("RUSTC_HOST_FLAGS", encoded_hostflags); - } - - if !cargo.allow_features.is_empty() { - cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features); - } - cargo.command - } -} diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 695a66834d45d..21c5f7232a15c 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -637,7 +637,7 @@ mod dist { assert_eq!(first(builder.cache.all::()), &[test::Crate { compiler: Compiler { host, stage: 0 }, target: host, - mode: Mode::Std, + mode: crate::Mode::Std, crates: vec!["std".to_owned()], },]); } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index c2ab439891eaa..aeb81b146382e 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -891,6 +891,7 @@ define_config! { metrics: Option = "metrics", android_ndk: Option = "android-ndk", optimized_compiler_builtins: Option = "optimized-compiler-builtins", + jobs: Option = "jobs", } } @@ -1289,7 +1290,6 @@ impl Config { config.rustc_error_format = flags.rustc_error_format; config.json_output = flags.json_output; config.on_fail = flags.on_fail; - config.jobs = Some(threads_from_config(flags.jobs as u32)); config.cmd = flags.cmd; config.incremental = flags.incremental; config.dry_run = if flags.dry_run { DryRun::UserSelected } else { DryRun::Disabled }; @@ -1511,8 +1511,11 @@ impl Config { metrics: _, android_ndk, optimized_compiler_builtins, + jobs, } = toml.build.unwrap_or_default(); + config.jobs = Some(threads_from_config(flags.jobs.unwrap_or(jobs.unwrap_or(0)))); + if let Some(file_build) = build { config.build = TargetSelection::from_user(&file_build); }; diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 3aefe517a5be6..bfeb811508c04 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -110,11 +110,10 @@ pub struct Flags { short, long, value_hint = clap::ValueHint::Other, - default_value_t = std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get), value_name = "JOBS" )] /// number of jobs to run in parallel - pub jobs: usize, + pub jobs: Option, // This overrides the deny-warnings configuration option, // which passes -Dwarnings to the compiler invocations. #[arg(global = true, long)] diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 2611b6cf51bbe..1f02757682c22 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -352,3 +352,61 @@ fn parse_rust_std_features_empty() { fn parse_rust_std_features_invalid() { parse("rust.std-features = \"backtrace\""); } + +#[test] +fn parse_jobs() { + assert_eq!(parse("build.jobs = 1").jobs, Some(1)); +} + +#[test] +fn jobs_precedence() { + // `--jobs` should take precedence over using `--set build.jobs`. + + let config = Config::parse_inner( + Flags::parse(&[ + "check".to_owned(), + "--config=/does/not/exist".to_owned(), + "--jobs=67890".to_owned(), + "--set=build.jobs=12345".to_owned(), + ]), + |&_| toml::from_str(""), + ); + assert_eq!(config.jobs, Some(67890)); + + // `--set build.jobs` should take precedence over `config.toml`. + let config = Config::parse_inner( + Flags::parse(&[ + "check".to_owned(), + "--config=/does/not/exist".to_owned(), + "--set=build.jobs=12345".to_owned(), + ]), + |&_| { + toml::from_str( + r#" + [build] + jobs = 67890 + "#, + ) + }, + ); + assert_eq!(config.jobs, Some(12345)); + + // `--jobs` > `--set build.jobs` > `config.toml` + let config = Config::parse_inner( + Flags::parse(&[ + "check".to_owned(), + "--jobs=123".to_owned(), + "--config=/does/not/exist".to_owned(), + "--set=build.jobs=456".to_owned(), + ]), + |&_| { + toml::from_str( + r#" + [build] + jobs = 789 + "#, + ) + }, + ); + assert_eq!(config.jobs, Some(123)); +} diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index b37786496cb56..9169bc90a45dc 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -275,4 +275,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "New option `./x setup editor` added, replacing `./x setup vscode` and adding support for vim, emacs and helix.", }, + ChangeInfo { + change_id: 131838, + severity: ChangeSeverity::Info, + summary: "Allow setting `--jobs` in config.toml with `build.jobs`.", + }, ]; diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index 1b98d54169338..410f0f92e60b6 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -56,9 +56,9 @@ ENV \ CFLAGS_x86_64_fortanix_unknown_sgx="-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \ CXX_x86_64_fortanix_unknown_sgx=clang++-11 \ CXXFLAGS_x86_64_fortanix_unknown_sgx="-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \ - AR_i686_unknown_freebsd=i686-unknown-freebsd12-ar \ - CC_i686_unknown_freebsd=i686-unknown-freebsd12-clang \ - CXX_i686_unknown_freebsd=i686-unknown-freebsd12-clang++ \ + AR_i686_unknown_freebsd=i686-unknown-freebsd13-ar \ + CC_i686_unknown_freebsd=i686-unknown-freebsd13-clang \ + CXX_i686_unknown_freebsd=i686-unknown-freebsd13-clang++ \ CC_aarch64_unknown_uefi=clang-11 \ CXX_aarch64_unknown_uefi=clang++-11 \ CC_i686_unknown_uefi=clang-11 \ diff --git a/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile index b3c5f41bdd7d0..fd0f5da8c495a 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:18.04 +FROM ubuntu:22.04 RUN apt-get update && apt-get install -y --no-install-recommends \ clang \ @@ -29,9 +29,9 @@ COPY scripts/cmake.sh /scripts/ RUN /scripts/cmake.sh ENV \ - AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-ar \ - CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-clang \ - CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-clang++ + AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-ar \ + CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-clang \ + CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-clang++ ENV HOSTS=x86_64-unknown-freebsd diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh index 303a2f26c0f82..8324d1ec58624 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh @@ -59,6 +59,8 @@ case $HOST_TARGET in # "error: cannot produce cdylib for ... as the target ... does not support these crate types". # Only run "pass" tests, which is quite a bit faster. #FIXME: Re-enable this once CI issues are fixed + # See + # For now, these tests are moved to `x86_64-msvc-ext2` in `src/ci/github-actions/jobs.yml`. #python3 "$X_PY" test --stage 2 src/tools/miri --target aarch64-apple-darwin --test-args pass #python3 "$X_PY" test --stage 2 src/tools/miri --target i686-pc-windows-gnu --test-args pass ;; diff --git a/src/ci/docker/scripts/freebsd-toolchain.sh b/src/ci/docker/scripts/freebsd-toolchain.sh index 0d02636db9196..4826b81d56ce3 100755 --- a/src/ci/docker/scripts/freebsd-toolchain.sh +++ b/src/ci/docker/scripts/freebsd-toolchain.sh @@ -5,8 +5,8 @@ set -eux arch=$1 binutils_version=2.40 -freebsd_version=12.3 -triple=$arch-unknown-freebsd12 +freebsd_version=13.2 +triple=$arch-unknown-freebsd13 sysroot=/usr/local/$triple hide_output() { @@ -59,7 +59,7 @@ done # Originally downloaded from: # URL=https://download.freebsd.org/ftp/releases/${freebsd_arch}/${freebsd_version}-RELEASE/base.txz -URL=https://ci-mirrors.rust-lang.org/rustc/2022-05-06-freebsd-${freebsd_version}-${freebsd_arch}-base.txz +URL=https://ci-mirrors.rust-lang.org/rustc/2024-02-18-freebsd-${freebsd_version}-${freebsd_arch}-base.txz curl "$URL" | tar xJf - -C "$sysroot" --wildcards "${files_to_extract[@]}" # Clang can do cross-builds out of the box, if we give it the right @@ -68,7 +68,7 @@ curl "$URL" | tar xJf - -C "$sysroot" --wildcards "${files_to_extract[@]}" # there might be other problems.) # # The --target option is last because the cross-build of LLVM uses -# --target without an OS version ("-freebsd" vs. "-freebsd12"). This +# --target without an OS version ("-freebsd" vs. "-freebsd13"). This # makes Clang default to libstdc++ (which no longer exists), and also # controls other features, like GNU-style symbol table hashing and # anything predicated on the version number in the __FreeBSD__ diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 8f49f623afaa4..2b6366040490e 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -5,6 +5,11 @@ runners: env: { } - &job-linux-4c + os: ubuntu-20.04 + <<: *base-job + + # Large runner used mainly for its bigger disk capacity + - &job-linux-4c-largedisk os: ubuntu-20.04-4core-16gb <<: *base-job @@ -46,7 +51,7 @@ envs: RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_14.3.1.app + SELECT_XCODE: /Applications/Xcode_15.2.app NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 @@ -127,7 +132,7 @@ auto: - image: dist-aarch64-linux env: CODEGEN_BACKENDS: llvm,cranelift - <<: *job-linux-4c + <<: *job-linux-4c-largedisk - image: dist-android <<: *job-linux-4c @@ -148,28 +153,28 @@ auto: <<: *job-linux-4c - image: dist-loongarch64-linux - <<: *job-linux-4c + <<: *job-linux-4c-largedisk - image: dist-loongarch64-musl - <<: *job-linux-4c + <<: *job-linux-4c-largedisk - image: dist-ohos <<: *job-linux-4c - image: dist-powerpc-linux - <<: *job-linux-4c + <<: *job-linux-4c-largedisk - image: dist-powerpc64-linux - <<: *job-linux-4c + <<: *job-linux-4c-largedisk - image: dist-powerpc64le-linux - <<: *job-linux-4c + <<: *job-linux-4c-largedisk - image: dist-riscv64-linux <<: *job-linux-4c - image: dist-s390x-linux - <<: *job-linux-4c + <<: *job-linux-4c-largedisk - image: dist-various-1 <<: *job-linux-4c @@ -282,7 +287,7 @@ auto: RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set rust.lto=thin --set rust.codegen-units=1 RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_14.3.1.app + SELECT_XCODE: /Applications/Xcode_15.2.app NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 @@ -298,7 +303,7 @@ auto: RUST_CONFIGURE_ARGS: --enable-sanitizers --enable-profiler --set rust.jemalloc --set target.aarch64-apple-ios-macabi.sanitizers=false --set target.x86_64-apple-ios-macabi.sanitizers=false RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_14.3.1.app + SELECT_XCODE: /Applications/Xcode_15.2.app NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 @@ -327,7 +332,7 @@ auto: --set llvm.ninja=false --set rust.lto=thin RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - SELECT_XCODE: /Applications/Xcode_14.3.1.app + SELECT_XCODE: /Applications/Xcode_15.4.app USE_XCODE_CLANG: 1 MACOSX_DEPLOYMENT_TARGET: 11.0 MACOSX_STD_DEPLOYMENT_TARGET: 11.0 @@ -347,7 +352,7 @@ auto: --enable-profiler --set rust.jemalloc RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - SELECT_XCODE: /Applications/Xcode_14.3.1.app + SELECT_XCODE: /Applications/Xcode_15.4.app USE_XCODE_CLANG: 1 MACOSX_DEPLOYMENT_TARGET: 11.0 MACOSX_STD_DEPLOYMENT_TARGET: 11.0 @@ -381,6 +386,8 @@ auto: <<: *job-windows-8c # Temporary builder to workaround CI issues + # See + #FIXME: Remove this, and re-enable the same tests in `checktools.sh`, once CI issues are fixed. - image: x86_64-msvc-ext2 env: SCRIPT: > diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 022fc9da7e03a..e9c73ef1c2d83 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -100,7 +100,7 @@ target | notes [`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20, glibc 2.29) [`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20, musl 1.2.3) `s390x-unknown-linux-gnu` | S390x Linux (kernel 3.2, glibc 2.17) -`x86_64-unknown-freebsd` | 64-bit FreeBSD +`x86_64-unknown-freebsd` | 64-bit FreeBSD (version 13.2) `x86_64-unknown-illumos` | illumos `x86_64-unknown-linux-musl` | 64-bit Linux with musl 1.2.3 [`x86_64-unknown-netbsd`](platform-support/netbsd.md) | NetBSD/amd64 @@ -166,7 +166,7 @@ target | std | notes `i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, musl 1.2.3 [^x86_32-floats-x87] [`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android [^x86_32-floats-return-ABI] [`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+), LLVM ABI [^x86_32-floats-return-ABI] -`i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI] +`i686-unknown-freebsd` | ✓ | 32-bit FreeBSD (version 13.2) [^x86_32-floats-return-ABI] `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI] [`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI) @@ -257,7 +257,7 @@ target | std | host | notes [`aarch64-unknown-teeos`](platform-support/aarch64-unknown-teeos.md) | ? | | ARM64 TEEOS | [`aarch64-unknown-nto-qnx700`](platform-support/nto-qnx.md) | ? | | ARM64 QNX Neutrino 7.0 RTOS | [`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 7.1 RTOS | -`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD +`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD (version 13.2) [`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ | | ARM64 Hermit `aarch64-unknown-illumos` | ✓ | ✓ | ARM64 illumos `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI) @@ -276,14 +276,14 @@ target | std | host | notes `armv4t-unknown-linux-gnueabi` | ? | | Armv4T Linux [`armv5te-none-eabi`](platform-support/armv5te-none-eabi.md) | * | | Bare Armv5TE `armv5te-unknown-linux-uclibceabi` | ? | | Armv5TE Linux with uClibc -`armv6-unknown-freebsd` | ✓ | ✓ | Armv6 FreeBSD +`armv6-unknown-freebsd` | ✓ | ✓ | Armv6 FreeBSD (version 13.2) [`armv6-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv6 NetBSD w/hard-float [`armv6k-nintendo-3ds`](platform-support/armv6k-nintendo-3ds.md) | ? | | Armv6k Nintendo 3DS, Horizon (Requires devkitARM toolchain) [`armv7-rtems-eabihf`](platform-support/armv7-rtems-eabihf.md) | ? | | RTEMS OS for ARM BSPs [`armv7-sony-vita-newlibeabihf`](platform-support/armv7-sony-vita-newlibeabihf.md) | ✓ | | Armv7-A Cortex-A9 Sony PlayStation Vita (requires VITASDK toolchain) [`armv7-unknown-linux-uclibceabi`](platform-support/armv7-unknown-linux-uclibceabi.md) | ✓ | ✓ | Armv7-A Linux with uClibc, softfloat [`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | Armv7-A Linux with uClibc, hardfloat -`armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD +`armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD (version 13.2) [`armv7-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv7-A NetBSD w/hard-float [`armv7-unknown-trusty`](platform-support/trusty.md) | ? | | [`armv7-wrs-vxworks-eabihf`](platform-support/vxworks.md) | ✓ | | Armv7-A for VxWorks @@ -342,9 +342,9 @@ target | std | host | notes [`powerpc-unknown-openbsd`](platform-support/powerpc-unknown-openbsd.md) | * | | [`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ | | [`powerpc-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | -`powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2) -`powerpc64le-unknown-freebsd` | | | PPC64LE FreeBSD -`powerpc-unknown-freebsd` | | | PowerPC FreeBSD +`powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2, version 13.2) +`powerpc64le-unknown-freebsd` | | | PPC64LE FreeBSD (version 13.2) +`powerpc-unknown-freebsd` | | | PowerPC FreeBSD (version 13.2) `powerpc64-unknown-linux-musl` | ? | | 64-bit PowerPC Linux with musl 1.2.3 [`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | `powerpc64le-unknown-linux-musl` | ? | | 64-bit PowerPC Linux with musl 1.2.3, Little Endian @@ -360,7 +360,7 @@ target | std | host | notes [`riscv32imafc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF [`riscv32-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`riscv64gc-unknown-hermit`](platform-support/hermit.md) | ✓ | | RISC-V Hermit -`riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD +`riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD (version 13.2) `riscv64gc-unknown-fuchsia` | | | RISC-V Fuchsia [`riscv64gc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | RISC-V NetBSD [`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64 diff --git a/src/doc/rustc/src/platform-support/arm-none-eabi.md b/src/doc/rustc/src/platform-support/arm-none-eabi.md index de0ef322fa673..9732df4be7f72 100644 --- a/src/doc/rustc/src/platform-support/arm-none-eabi.md +++ b/src/doc/rustc/src/platform-support/arm-none-eabi.md @@ -42,16 +42,15 @@ their own document. There are two 32-bit instruction set architectures (ISAs) defined by Arm: - The [*A32 ISA*][a32-isa], with fixed-width 32-bit instructions. Previously - known as the *Arm* ISA, this originated with the original Arm1 of 1985 and has + known as the *Arm* ISA, this originated with the original ARM1 of 1985 and has been updated by various revisions to the architecture specifications ever since. - The [*T32 ISA*][t32-isa], with a mix of 16-bit and 32-bit width instructions. Note that this term includes both the original 16-bit width *Thumb* ISA introduced with the Armv4T architecture in 1994, and the later 16/32-bit sized - *Thumb-2* ISA introduced with the Armv6T2 architecture in 2003. - -Again, these ISAs have been revised by subsequent revisions to the relevant Arm -architecture specifications. + *Thumb-2* ISA introduced with the Armv6T2 architecture in 2003. Again, these + ISAs have been revised by subsequent revisions to the relevant Arm + architecture specifications. There is also a 64-bit ISA with fixed-width 32-bit instructions called the *A64 ISA*, but targets which implement that instruction set generally start with @@ -106,10 +105,14 @@ features you do not have available, leaving you with the optimized instruction scheduling and support for the features you do have. More details are available in the detailed target-specific documentation. -**Note:** Many target-features are currently unstable and subject to change, and +
+ +Many target-features are currently unstable and subject to change, and if you use them you should disassemble the compiler output and manually inspect it to ensure only appropriate instructions for your CPU have been generated. +
+ If you wish to use the *target-cpu* and *target-feature* options, you can add them to your `.cargo/config.toml` file alongside any other flags your project uses (likely linker related ones): diff --git a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md index f25ef0383b185..11c9486cb76ea 100644 --- a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md @@ -35,9 +35,9 @@ to use these flags. | CPU | FPU | DSP | Target CPU | Target Features | | ---------- | --- | --- | ----------- | --------------- | | Any | No | Yes | None | None | -| Cortex-M4 | No | Yes | `cortex-m4` | `+soft-float` | +| Cortex-M4 | No | Yes | `cortex-m4` | `-fpregs` | | Cortex-M4F | SP | Yes | `cortex-m4` | None | -| Cortex-M7 | No | Yes | `cortex-m7` | `+soft-float` | +| Cortex-M7 | No | Yes | `cortex-m7` | `-fpregs` | | Cortex-M7F | SP | Yes | `cortex-m7` | `-fp64` | | Cortex-M7F | DP | Yes | `cortex-m7` | None | @@ -50,6 +50,13 @@ to use these flags. | Cortex-M7F | SP | Yes | `cortex-m7` | `-fp64` | | Cortex-M7F | DP | Yes | `cortex-m7` | None | +
+ +Never use the `-fpregs` *target-feature* with the `thumbv7em-none-eabihf` target +as it will cause compilation units to have different ABIs, which is unsound. + +
+ ### Arm Cortex-M4 and Arm Cortex-M4F The target CPU is `cortex-m4`. @@ -59,7 +66,7 @@ The target CPU is `cortex-m4`. * enabled by default with this *target* * Cortex-M4F has a single precision FPU * support is enabled by default with this *target-cpu* - * disable support using the `+soft-float` feature (`eabi` only) + * disable support using the `-fpregs` *target-feature* (`eabi` only) ### Arm Cortex-M7 and Arm Cortex-M7F @@ -71,4 +78,4 @@ The target CPU is `cortex-m7`. * Cortex-M7F have either a single-precision or double-precision FPU * double-precision support is enabled by default with this *target-cpu* * opt-out by using the `-f64` *target-feature* - * disable support entirely using the `+soft-float` feature (`eabi` only) + * disable support entirely using the `-fpregs` *target-feature* (`eabi` only) diff --git a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md index 4e696f9c30461..82fdc5b21cf2d 100644 --- a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md @@ -39,22 +39,22 @@ to use these flags. | CPU | FPU | DSP | MVE | Target CPU | Target Features | | ----------- | --- | --- | --------- | ------------- | --------------------- | | Unspecified | No | No | No | None | None | -| Cortex-M33 | No | No | No | `cortex-m33` | `+soft-float,-dsp` | -| Cortex-M33 | No | Yes | No | `cortex-m33` | `+soft-float` | +| Cortex-M33 | No | No | No | `cortex-m33` | `-fpregs,-dsp` | +| Cortex-M33 | No | Yes | No | `cortex-m33` | `-fpregs` | | Cortex-M33 | SP | No | No | `cortex-m33` | `-dsp` | | Cortex-M33 | SP | Yes | No | `cortex-m33` | None | -| Cortex-M35P | No | No | No | `cortex-m35p` | `+soft-float,-dsp` | -| Cortex-M35P | No | Yes | No | `cortex-m35p` | `+soft-float` | +| Cortex-M35P | No | No | No | `cortex-m35p` | `-fpregs,-dsp` | +| Cortex-M35P | No | Yes | No | `cortex-m35p` | `-fpregs` | | Cortex-M35P | SP | No | No | `cortex-m35p` | `-dsp` | | Cortex-M35P | SP | Yes | No | `cortex-m35p` | None | -| Cortex-M55 | No | Yes | No | `cortex-m55` | `+soft-float,-mve` | +| Cortex-M55 | No | Yes | No | `cortex-m55` | `-fpregs,-mve` | | Cortex-M55 | DP | Yes | No | `cortex-m55` | `-mve` | -| Cortex-M55 | No | Yes | Int | `cortex-m55` | `+soft-float,-mve.fp` | +| Cortex-M55 | No | Yes | Int | `cortex-m55` | `-fpregs,-mve.fp,+mve`| | Cortex-M55 | DP | Yes | Int | `cortex-m55` | `-mve.fp` | | Cortex-M55 | DP | Yes | Int+Float | `cortex-m55` | None | -| Cortex-M85 | No | Yes | No | `cortex-m85` | `+soft-float,-mve` | +| Cortex-M85 | No | Yes | No | `cortex-m85` | `-fpregs,-mve` | | Cortex-M85 | DP | Yes | No | `cortex-m85` | `-mve` | -| Cortex-M85 | No | Yes | Int | `cortex-m85` | `+soft-float,-mve.fp` | +| Cortex-M85 | No | Yes | Int | `cortex-m85` | `-fpregs,-mve.fp,+mve`| | Cortex-M85 | DP | Yes | Int | `cortex-m85` | `-mve.fp` | | Cortex-M85 | DP | Yes | Int+Float | `cortex-m85` | None | @@ -74,6 +74,19 @@ to use these flags. | Cortex-M85 | DP | Yes | Int | `cortex-m85` | `-mve.fp` | | Cortex-M85 | DP | Yes | Int+Float | `cortex-m85` | None | +*Technically* you can use this hard-float ABI on a CPU which has no FPU but does +have Integer MVE, because MVE provides the same set of registers as the FPU +(including `s0` and `d0`). The particular set of flags that might enable this +unusual scenario are currently not recorded here. + +
+ +Never use the `-fpregs` *target-feature* with the `thumbv8m.main-none-eabihf` +target as it will cause compilation units to have different ABIs, which is +unsound. + +
+ ### Arm Cortex-M33 The target CPU is `cortex-m33`. @@ -83,7 +96,7 @@ The target CPU is `cortex-m33`. * enabled by default with this *target-cpu* * Has an optional single precision FPU * support is enabled by default with this *target-cpu* - * disable support using the `+soft-float` feature (`eabi` only) + * disable support using the `-fpregs` *target-feature* (`eabi` only) ### Arm Cortex-M35P @@ -94,7 +107,7 @@ The target CPU is `cortex-m35p`. * enabled by default with this *target-cpu* * Has an optional single precision FPU * support is enabled by default with this *target-cpu* - * disable support using the `+soft-float` feature (`eabi` only) + * disable support using the `-fpregs` *target-feature* (`eabi` only) ### Arm Cortex-M55 @@ -106,7 +119,7 @@ The target CPU is `cortex-m55`. * Has an optional double-precision FPU that also supports half-precision FP16 values * support is enabled by default with this *target-cpu* - * disable support using the `+soft-float` feature (`eabi` only) + * disable support using the `-fpregs` *target-feature* (`eabi` only) * Has optional support for M-Profile Vector Extensions * Also known as *Helium Technology* * Available with only integer support, or both integer/float support @@ -125,7 +138,7 @@ The target CPU is `cortex-m85`. * Has an optional double-precision FPU that also supports half-precision FP16 values * support is enabled by default with this *target-cpu* - * disable support using the `+soft-float` feature (`eabi` only) + * disable support using the `-fpregs` *target-feature* (`eabi` only) * Has optional support for M-Profile Vector Extensions * Also known as *Helium Technology* * Available with only integer support, or both integer/float support diff --git a/src/doc/unstable-book/src/language-features/result-ffi-guarantees.md b/src/doc/unstable-book/src/language-features/result-ffi-guarantees.md deleted file mode 100644 index dc9c196524ed4..0000000000000 --- a/src/doc/unstable-book/src/language-features/result-ffi-guarantees.md +++ /dev/null @@ -1,14 +0,0 @@ -# `result_ffi_guarantees` - -The tracking issue for this feature is: [#110503] - -[#110503]: https://github.com/rust-lang/rust/issues/110503 - ------------------------- - -This feature adds the possibility of using `Result` in FFI if T's niche -value can be used to describe E or vise-versa. - -See [RFC 3391] for more information. - -[RFC 3391]: https://github.com/rust-lang/rfcs/blob/master/text/3391-result_ffi_guarantees.md diff --git a/src/doc/unstable-book/src/language-features/strict-provenance.md b/src/doc/unstable-book/src/language-features/strict-provenance-lints.md similarity index 62% rename from src/doc/unstable-book/src/language-features/strict-provenance.md rename to src/doc/unstable-book/src/language-features/strict-provenance-lints.md index dc60f3f375d3c..81bdf07a86af9 100644 --- a/src/doc/unstable-book/src/language-features/strict-provenance.md +++ b/src/doc/unstable-book/src/language-features/strict-provenance-lints.md @@ -1,18 +1,17 @@ -# `strict_provenance` +# `strict_provenance_lints` The tracking issue for this feature is: [#95228] [#95228]: https://github.com/rust-lang/rust/issues/95228 ----- -The `strict_provenance` feature allows to enable the `fuzzy_provenance_casts` and `lossy_provenance_casts` lints. +The `strict_provenance_lints` feature allows to enable the `fuzzy_provenance_casts` and `lossy_provenance_casts` lints. These lint on casts between integers and pointers, that are recommended against or invalid in the strict provenance model. -The same feature gate is also used for the experimental strict provenance API in `std` (actually `core`). ## Example ```rust -#![feature(strict_provenance)] +#![feature(strict_provenance_lints)] #![warn(fuzzy_provenance_casts)] fn main() { diff --git a/src/etc/installer/msi/rust.wxs b/src/etc/installer/msi/rust.wxs index f29e1e4d27a27..2d155bf0b1019 100644 --- a/src/etc/installer/msi/rust.wxs +++ b/src/etc/installer/msi/rust.wxs @@ -172,6 +172,11 @@ + + + + + @@ -279,7 +284,41 @@ - + + + + + + + + + + + + + + + diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 34332de80b365..42df0b283817b 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -24,6 +24,7 @@ tracing = "0.1" tracing-tree = "0.3.0" threadpool = "1.8.1" unicode-segmentation = "1.9" +sha2 = "0.10.8" [dependencies.tracing-subscriber] version = "0.3.3" diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1ddad917b783c..7d4d8d8941dcb 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1817,7 +1817,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T // Only anon consts can implicitly capture params. // FIXME: is this correct behavior? let param_env = cx.tcx.param_env(*def_id); - ct.normalize(cx.tcx, param_env) + cx.tcx.normalize_erasing_regions(param_env, ct) } else { ct }; @@ -2033,8 +2033,8 @@ pub(crate) fn clean_middle_ty<'tcx>( Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), format!("{pat:?}").into_boxed_str(), ), - ty::Array(ty, mut n) => { - n = n.normalize(cx.tcx, ty::ParamEnv::reveal_all()); + ty::Array(ty, n) => { + let n = cx.tcx.normalize_erasing_regions(cx.param_env, n); let n = print_const(cx, n); Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), n.into()) } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 315b7742a4cf8..7826a5d8394a7 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -37,7 +37,7 @@ use std::sync::OnceLock; use pulldown_cmark::{ BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, TagEnd, html, }; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Diag, DiagMessage}; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::TyCtxt; @@ -57,6 +57,7 @@ use crate::html::length_limit::HtmlWithLimit; use crate::html::render::small_url_encode; use crate::html::toc::{Toc, TocBuilder}; +mod footnotes; #[cfg(test)] mod tests; @@ -646,81 +647,6 @@ impl<'a, I: Iterator>> Iterator for SummaryLine<'a, I> { } } -/// Moves all footnote definitions to the end and add back links to the -/// references. -struct Footnotes<'a, I> { - inner: I, - footnotes: FxIndexMap>, u16)>, -} - -impl<'a, I> Footnotes<'a, I> { - fn new(iter: I) -> Self { - Footnotes { inner: iter, footnotes: FxIndexMap::default() } - } - - fn get_entry(&mut self, key: &str) -> &mut (Vec>, u16) { - let new_id = self.footnotes.len() + 1; - let key = key.to_owned(); - self.footnotes.entry(key).or_insert((Vec::new(), new_id as u16)) - } -} - -impl<'a, I: Iterator>> Iterator for Footnotes<'a, I> { - type Item = SpannedEvent<'a>; - - fn next(&mut self) -> Option { - loop { - match self.inner.next() { - Some((Event::FootnoteReference(ref reference), range)) => { - let entry = self.get_entry(reference); - let reference = format!( - "{0}", - (*entry).1 - ); - return Some((Event::Html(reference.into()), range)); - } - Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => { - let mut content = Vec::new(); - for (event, _) in &mut self.inner { - if let Event::End(TagEnd::FootnoteDefinition) = event { - break; - } - content.push(event); - } - let entry = self.get_entry(&def); - (*entry).0 = content; - } - Some(e) => return Some(e), - None => { - if !self.footnotes.is_empty() { - let mut v: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect(); - v.sort_by(|a, b| a.1.cmp(&b.1)); - let mut ret = String::from("

    "); - for (mut content, id) in v { - write!(ret, "
  1. ").unwrap(); - let mut is_paragraph = false; - if let Some(&Event::End(TagEnd::Paragraph)) = content.last() { - content.pop(); - is_paragraph = true; - } - html::push_html(&mut ret, content.into_iter()); - write!(ret, " ").unwrap(); - if is_paragraph { - ret.push_str("

    "); - } - ret.push_str("
  2. "); - } - ret.push_str("
"); - return Some((Event::Html(ret.into()), 0..0)); - } else { - return None; - } - } - } - } - } -} - /// A newtype that represents a relative line number in Markdown. /// /// In other words, this represents an offset from the first line of Markdown @@ -1408,7 +1334,7 @@ impl Markdown<'_> { let mut s = String::with_capacity(md.len() * 3 / 2); let p = HeadingLinks::new(p, None, ids, heading_offset); - let p = Footnotes::new(p); + let p = footnotes::Footnotes::new(p); let p = LinkReplacer::new(p.map(|(ev, _)| ev), links); let p = TableWrapper::new(p); let p = CodeBlocks::new(p, codes, edition, playground); @@ -1443,7 +1369,7 @@ impl MarkdownWithToc<'_> { { let p = HeadingLinks::new(p, Some(&mut toc), ids, HeadingOffset::H1); - let p = Footnotes::new(p); + let p = footnotes::Footnotes::new(p); let p = TableWrapper::new(p.map(|(ev, _)| ev)); let p = CodeBlocks::new(p, codes, edition, playground); html::push_html(&mut s, p); @@ -1476,7 +1402,7 @@ impl MarkdownItemInfo<'_> { let mut s = String::with_capacity(md.len() * 3 / 2); let p = HeadingLinks::new(p, None, ids, HeadingOffset::H1); - let p = Footnotes::new(p); + let p = footnotes::Footnotes::new(p); let p = TableWrapper::new(p.map(|(ev, _)| ev)); let p = p.filter(|event| { !matches!(event, Event::Start(Tag::Paragraph) | Event::End(TagEnd::Paragraph)) diff --git a/src/librustdoc/html/markdown/footnotes.rs b/src/librustdoc/html/markdown/footnotes.rs new file mode 100644 index 0000000000000..3f0e586b8e372 --- /dev/null +++ b/src/librustdoc/html/markdown/footnotes.rs @@ -0,0 +1,113 @@ +//! Markdown footnote handling. +use std::fmt::Write as _; + +use pulldown_cmark::{Event, Tag, TagEnd, html}; +use rustc_data_structures::fx::FxIndexMap; + +use super::SpannedEvent; + +/// Moves all footnote definitions to the end and add back links to the +/// references. +pub(super) struct Footnotes<'a, I> { + inner: I, + footnotes: FxIndexMap>, +} + +/// The definition of a single footnote. +struct FootnoteDef<'a> { + content: Vec>, + /// The number that appears in the footnote reference and list. + id: u16, +} + +impl<'a, I> Footnotes<'a, I> { + pub(super) fn new(iter: I) -> Self { + Footnotes { inner: iter, footnotes: FxIndexMap::default() } + } + + fn get_entry(&mut self, key: &str) -> (&mut Vec>, u16) { + let new_id = self.footnotes.len() + 1; + let key = key.to_owned(); + let FootnoteDef { content, id } = self + .footnotes + .entry(key) + .or_insert(FootnoteDef { content: Vec::new(), id: new_id as u16 }); + // Don't allow changing the ID of existing entrys, but allow changing the contents. + (content, *id) + } +} + +impl<'a, I: Iterator>> Iterator for Footnotes<'a, I> { + type Item = SpannedEvent<'a>; + + fn next(&mut self) -> Option { + loop { + match self.inner.next() { + Some((Event::FootnoteReference(ref reference), range)) => { + // When we see a reference (to a footnote we may not know) the definition of, + // reserve a number for it, and emit a link to that number. + let (_, id) = self.get_entry(reference); + let reference = + format!("{0}", id); + return Some((Event::Html(reference.into()), range)); + } + Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => { + // When we see a footnote definition, collect the assocated content, and store + // that for rendering later. + let content = collect_footnote_def(&mut self.inner); + let (entry_content, _) = self.get_entry(&def); + *entry_content = content; + } + Some(e) => return Some(e), + None => { + if !self.footnotes.is_empty() { + // After all the markdown is emmited, emit an
then all the footnotes + // in a list. + let defs: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect(); + let defs_html = render_footnotes_defs(defs); + return Some((Event::Html(defs_html.into()), 0..0)); + } else { + return None; + } + } + } + } + } +} + +fn collect_footnote_def<'a>(events: impl Iterator>) -> Vec> { + let mut content = Vec::new(); + for (event, _) in events { + if let Event::End(TagEnd::FootnoteDefinition) = event { + break; + } + content.push(event); + } + content +} + +fn render_footnotes_defs(mut footnotes: Vec>) -> String { + let mut ret = String::from("

    "); + + // Footnotes must listed in order of id, so the numbers the + // browser generated for
  1. are right. + footnotes.sort_by_key(|x| x.id); + + for FootnoteDef { mut content, id } in footnotes { + write!(ret, "
  2. ").unwrap(); + let mut is_paragraph = false; + if let Some(&Event::End(TagEnd::Paragraph)) = content.last() { + content.pop(); + is_paragraph = true; + } + html::push_html(&mut ret, content.into_iter()); + write!(ret, " ").unwrap(); + if is_paragraph { + ret.push_str("

    "); + } + ret.push_str("
  3. "); + } + ret.push_str("
"); + + ret +} diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index df9776ff5f88c..2c17fd54006f5 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -44,7 +44,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-style: normal; font-weight: 400; src: local('Fira Sans'), - url("FiraSans-Regular-018c141bf0843ffd.woff2") format("woff2"); + url("FiraSans-Regular-0fe48ade.woff2") format("woff2"); font-display: swap; } @font-face { @@ -52,7 +52,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-style: normal; font-weight: 500; src: local('Fira Sans Medium'), - url("FiraSans-Medium-8f9a781e4970d388.woff2") format("woff2"); + url("FiraSans-Medium-e1aa3f0a.woff2") format("woff2"); font-display: swap; } @@ -62,7 +62,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-style: normal; font-weight: 400; src: local('Source Serif 4'), - url("SourceSerif4-Regular-46f98efaafac5295.ttf.woff2") format("woff2"); + url("SourceSerif4-Regular-6b053e98.ttf.woff2") format("woff2"); font-display: swap; } @font-face { @@ -70,7 +70,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-style: italic; font-weight: 400; src: local('Source Serif 4 Italic'), - url("SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2") format("woff2"); + url("SourceSerif4-It-ca3b17ed.ttf.woff2") format("woff2"); font-display: swap; } @font-face { @@ -78,7 +78,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-style: normal; font-weight: 700; src: local('Source Serif 4 Bold'), - url("SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2") format("woff2"); + url("SourceSerif4-Bold-6d4fd4c0.ttf.woff2") format("woff2"); font-display: swap; } @@ -89,28 +89,28 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-weight: 400; /* Avoid using locally installed font because bad versions are in circulation: * see https://github.com/rust-lang/rust/issues/24355 */ - src: url("SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2") format("woff2"); + src: url("SourceCodePro-Regular-8badfe75.ttf.woff2") format("woff2"); font-display: swap; } @font-face { font-family: 'Source Code Pro'; font-style: italic; font-weight: 400; - src: url("SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2") format("woff2"); + src: url("SourceCodePro-It-fc8b9304.ttf.woff2") format("woff2"); font-display: swap; } @font-face { font-family: 'Source Code Pro'; font-style: normal; font-weight: 600; - src: url("SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2") format("woff2"); + src: url("SourceCodePro-Semibold-aa29a496.ttf.woff2") format("woff2"); font-display: swap; } /* Avoid using legacy CJK serif fonts in Windows like Batang. */ @font-face { font-family: 'NanumBarunGothic'; - src: url("NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2") format("woff2"); + src: url("NanumBarunGothic-13b3dcba.ttf.woff2") format("woff2"); font-display: swap; unicode-range: U+AC00-D7AF, U+1100-11FF, U+3130-318F, U+A960-A97F, U+D7B0-D7FF; } diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index 6157598ba3888..9e0803f5d3fd1 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -3,12 +3,9 @@ //! All the static files are included here for centralized access in case anything other than the //! HTML rendering code (say, the theme checker) needs to access one of these files. -use std::hash::Hasher; use std::path::{Path, PathBuf}; use std::{fmt, str}; -use rustc_data_structures::fx::FxHasher; - pub(crate) struct StaticFile { pub(crate) filename: PathBuf, pub(crate) bytes: &'static [u8], @@ -64,9 +61,11 @@ pub(crate) fn static_filename(filename: &str, contents: &[u8]) -> PathBuf { } fn static_suffix(bytes: &[u8]) -> String { - let mut hasher = FxHasher::default(); - hasher.write(bytes); - format!("-{:016x}", hasher.finish()) + use sha2::Digest; + let bytes = sha2::Sha256::digest(bytes); + let mut digest = format!("-{bytes:x}"); + digest.truncate(9); + digest } macro_rules! static_files { diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 77e7d83090b94..0130f2ce517bf 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -672,12 +672,12 @@ impl FromClean for Trait { let tcx = renderer.tcx; let is_auto = trait_.is_auto(tcx); let is_unsafe = trait_.safety(tcx) == rustc_hir::Safety::Unsafe; - let is_object_safe = trait_.is_dyn_compatible(tcx); + let is_dyn_compatible = trait_.is_dyn_compatible(tcx); let clean::Trait { items, generics, bounds, .. } = trait_; Trait { is_auto, is_unsafe, - is_object_safe, + is_dyn_compatible, items: renderer.ids(items), generics: generics.into_json(renderer), bounds: bounds.into_json(renderer), diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index cbc6e351fac53..2f0ea8d618c12 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1091,7 +1091,6 @@ impl LinkCollector<'_, '_> { // resolutions are cached, for other links we want to report an error every // time so they are not cached. matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut), - false, )?; if resolved.len() > 1 { @@ -1404,9 +1403,6 @@ impl LinkCollector<'_, '_> { // If errors are cached then they are only reported on first occurrence // which we want in some cases but not in others. cache_errors: bool, - // If this call is intended to be recoverable, then pass true to silence. - // This is only recoverable when path is failed to resolved. - recoverable: bool, ) -> Option)>> { if let Some(res) = self.visited_links.get(&key) { if res.is_some() || cache_errors { @@ -1414,7 +1410,7 @@ impl LinkCollector<'_, '_> { } } - let mut candidates = self.resolve_with_disambiguator(&key, diag.clone(), recoverable); + let mut candidates = self.resolve_with_disambiguator(&key, diag.clone()); // FIXME: it would be nice to check that the feature gate was enabled in the original crate, not just ignore it altogether. // However I'm not sure how to check that across crates. @@ -1463,14 +1459,10 @@ impl LinkCollector<'_, '_> { } /// After parsing the disambiguator, resolve the main part of the link. - // FIXME(jynelson): wow this is just so much fn resolve_with_disambiguator( &mut self, key: &ResolutionInfo, diag: DiagnosticInfo<'_>, - // If this call is intended to be recoverable, then pass true to silence. - // This is only recoverable when path is failed to resolved. - recoverable: bool, ) -> Vec<(Res, Option)> { let disambiguator = key.dis; let path_str = &key.path_str; @@ -1500,9 +1492,7 @@ impl LinkCollector<'_, '_> { } } } - if !recoverable { - resolution_failure(self, diag, path_str, disambiguator, smallvec![err]); - } + resolution_failure(self, diag, path_str, disambiguator, smallvec![err]); return vec![]; } } @@ -1539,15 +1529,13 @@ impl LinkCollector<'_, '_> { .fold(0, |acc, res| if let Ok(res) = res { acc + res.len() } else { acc }); if len == 0 { - if !recoverable { - resolution_failure( - self, - diag, - path_str, - disambiguator, - candidates.into_iter().filter_map(|res| res.err()).collect(), - ); - } + resolution_failure( + self, + diag, + path_str, + disambiguator, + candidates.into_iter().filter_map(|res| res.err()).collect(), + ); return vec![]; } else if len == 1 { candidates.into_iter().filter_map(|res| res.ok()).flatten().collect::>() diff --git a/src/rustdoc-json-types/Cargo.toml b/src/rustdoc-json-types/Cargo.toml index d3548036d4c72..14ff1d0881639 100644 --- a/src/rustdoc-json-types/Cargo.toml +++ b/src/rustdoc-json-types/Cargo.toml @@ -6,9 +6,12 @@ edition = "2021" [lib] path = "lib.rs" +[features] +default = ["rustc-hash"] + [dependencies] serde = { version = "1.0", features = ["derive"] } -rustc-hash = "1.1.0" +rustc-hash = { version = "2.0", optional = true } [dev-dependencies] serde_json = "1.0" diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index fc64bc98bb981..f553a78d766e5 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -2,18 +2,35 @@ //! //! These types are the public API exposed through the `--output-format json` flag. The [`Crate`] //! struct is the root of the JSON blob and all other items are contained within. +//! +//! We expose a `rustc-hash` feature that is disabled by default. This feature switches the +//! [`std::collections::HashMap`] for [`rustc_hash::FxHashMap`] to improve the performance of said +//! `HashMap` in specific situations. +//! +//! `cargo-semver-checks` for example, saw a [-3% improvement][1] when benchmarking using the +//! `aws_sdk_ec2` JSON output (~500MB of JSON). As always, we recommend measuring the impact before +//! turning this feature on, as [`FxHashMap`][2] only concerns itself with hash speed, and may +//! increase the number of collisions. +//! +//! [1]: https://rust-lang.zulipchat.com/#narrow/channel/266220-t-rustdoc/topic/rustc-hash.20and.20performance.20of.20rustdoc-types/near/474855731 +//! [2]: https://crates.io/crates/rustc-hash +#[cfg(not(feature = "rustc-hash"))] +use std::collections::HashMap; use std::path::PathBuf; -pub use rustc_hash::FxHashMap; +#[cfg(feature = "rustc-hash")] +use rustc_hash::FxHashMap as HashMap; use serde::{Deserialize, Serialize}; +pub type FxHashMap = HashMap; // re-export for use in src/librustdoc + /// The version of JSON output that this crate represents. /// /// This integer is incremented with every breaking change to the API, /// and is returned along with the JSON blob as [`Crate::format_version`]. /// Consuming code should assert that this value matches the format version(s) that it supports. -pub const FORMAT_VERSION: u32 = 35; +pub const FORMAT_VERSION: u32 = 36; /// The root of the emitted JSON blob. /// @@ -30,11 +47,11 @@ pub struct Crate { pub includes_private: bool, /// A collection of all items in the local crate as well as some external traits and their /// items that are referenced locally. - pub index: FxHashMap, + pub index: HashMap, /// Maps IDs to fully qualified paths and other info helpful for generating links. - pub paths: FxHashMap, + pub paths: HashMap, /// Maps `crate_id` of items to a crate name and html_root_url if it exists. - pub external_crates: FxHashMap, + pub external_crates: HashMap, /// A single version number to be used in the future when making backwards incompatible changes /// to the JSON output. pub format_version: u32, @@ -95,7 +112,7 @@ pub struct Item { /// Some("") if there is some documentation but it is empty (EG `#[doc = ""]`). pub docs: Option, /// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs - pub links: FxHashMap, + pub links: HashMap, /// Stringified versions of the attributes on this item (e.g. `"#[inline]"`) pub attrs: Vec, /// Information about the item’s deprecation, if present. @@ -300,10 +317,10 @@ pub enum AssocItemConstraintKind { // FIXME(aDotInTheVoid): Consider making this non-public in rustdoc-types. pub struct Id(pub u32); -/// The fundamental kind of an item. Unlike [`ItemEnum`], this does not carry any aditional info. +/// The fundamental kind of an item. Unlike [`ItemEnum`], this does not carry any additional info. /// /// Part of [`ItemSummary`]. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum ItemKind { /// A module declaration, e.g. `mod foo;` or `mod foo {}` @@ -693,7 +710,7 @@ pub enum Abi { Aapcs { unwind: bool }, /// Can be specified as `extern "win64"`. Win64 { unwind: bool }, - /// Can be specifed as `extern "sysv64"`. + /// Can be specified as `extern "sysv64"`. SysV64 { unwind: bool }, /// Can be specified as `extern "system"`. System { unwind: bool }, @@ -887,7 +904,7 @@ pub enum GenericBound { } /// A set of modifiers applied to a trait. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum TraitBoundModifier { /// Marks the absence of a modifier. @@ -991,7 +1008,7 @@ pub enum Type { QualifiedPath { /// The name of the associated type in the parent type. /// - /// ```ignore (incomplete expresssion) + /// ```ignore (incomplete expression) /// as Iterator>::Item /// // ^^^^ /// ``` @@ -1078,12 +1095,15 @@ pub struct FunctionSignature { #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Trait { /// Whether the trait is marked `auto` and is thus implemented automatically - /// for all aplicable types. + /// for all applicable types. pub is_auto: bool, /// Whether the trait is marked as `unsafe`. pub is_unsafe: bool, - /// Whether the trait is [object safe](https://doc.rust-lang.org/reference/items/traits.html#object-safety). - pub is_object_safe: bool, + // FIXME(dyn_compat_renaming): Update the URL once the Reference is updated and hits stable. + /// Whether the trait is [dyn compatible](https://doc.rust-lang.org/reference/items/traits.html#object-safety)[^1]. + /// + /// [^1]: Formerly known as "object safe". + pub is_dyn_compatible: bool, /// Associated [`Item`]s that can/must be implemented by the `impl` blocks. pub items: Vec, /// Information about the type parameters and `where` clauses of the trait. @@ -1185,7 +1205,7 @@ pub struct ProcMacro { } /// The way a [`ProcMacro`] is declared to be used. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum MacroKind { /// A bang macro `foo!()`. diff --git a/src/tools/cargo b/src/tools/cargo index 8c30ce53688e2..cf53cc54bb593 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 8c30ce53688e25f7e9d860b33cc914fb2957ca9a +Subproject commit cf53cc54bb593b5ec3dc2be4b1702f50c36d24d5 diff --git a/src/tools/clippy/.github/deploy.sh b/src/tools/clippy/.github/deploy.sh index 5b4b4be4e36b5..ea118a3b6fce5 100644 --- a/src/tools/clippy/.github/deploy.sh +++ b/src/tools/clippy/.github/deploy.sh @@ -8,8 +8,8 @@ rm -rf out/master/ || exit 0 echo "Making the docs for master" mkdir out/master/ cp util/gh-pages/index.html out/master +cp util/gh-pages/theme.js out/master cp util/gh-pages/script.js out/master -cp util/gh-pages/lints.json out/master cp util/gh-pages/style.css out/master if [[ -n $TAG_NAME ]]; then diff --git a/src/tools/clippy/.gitignore b/src/tools/clippy/.gitignore index 181b71a658b9a..a7c25b29021fb 100644 --- a/src/tools/clippy/.gitignore +++ b/src/tools/clippy/.gitignore @@ -34,6 +34,7 @@ out # gh pages docs util/gh-pages/lints.json +util/gh-pages/index.html # rustfmt backups *.rs.bk diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 5d253d52531e6..4bdbc91db9397 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,46 @@ document. ## Unreleased / Beta / In Rust Nightly -[b794b8e0...master](https://github.com/rust-lang/rust-clippy/compare/b794b8e0...master) +[0f8eabd6...master](https://github.com/rust-lang/rust-clippy/compare/0f8eabd6...master) + +## Rust 1.82 + +Current stable, released 2024-10-17 + +[View all 108 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-07-11T20%3A12%3A07Z..2024-08-24T20%3A55%3A35Z+base%3Amaster) + +### New Lints + +* Added [`too_long_first_doc_paragraph`] to `nursery` + [#12993](https://github.com/rust-lang/rust-clippy/pull/12993) +* Added [`unused_result_ok`] to `restriction` + [#12150](https://github.com/rust-lang/rust-clippy/pull/12150) +* Added [`pathbuf_init_then_push`] to `restriction` + [#11700](https://github.com/rust-lang/rust-clippy/pull/11700) + +### Enhancements + +* [`explicit_iter_loop`]: Now respects the `msrv` configuration + [#13288](https://github.com/rust-lang/rust-clippy/pull/13288) +* [`assigning_clones`]: No longer lints in test code + [#13273](https://github.com/rust-lang/rust-clippy/pull/13273) +* [`inconsistent_struct_constructor`]: Lint attributes now work on the struct definition + [#13211](https://github.com/rust-lang/rust-clippy/pull/13211) +* [`set_contains_or_insert`]: Now also checks for `BTreeSet` + [#13053](https://github.com/rust-lang/rust-clippy/pull/13053) +* [`doc_markdown`]: Added the following identifiers to [`doc-valid-idents`]: AccessKit, + CoreFoundation, CoreGraphics, CoreText, Direct2D, Direct3D, DirectWrite, PostScript, + OpenAL, OpenType, WebRTC, WebSocket, WebTransport, NetBSD, and OpenBSD + [#13093](https://github.com/rust-lang/rust-clippy/pull/13093) + +### ICE Fixes + +* [`uninit_vec`] + [rust#128720](https://github.com/rust-lang/rust/pull/128720) ## Rust 1.81 -Current stable, released 2024-09-05 +Released 2024-09-05 ### New Lints @@ -5621,6 +5656,7 @@ Released 2018-09-13 [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten [`manual_hash_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one +[`manual_ignore_case_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ignore_case_cmp [`manual_inspect`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_inspect [`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed [`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check @@ -5874,6 +5910,7 @@ Released 2018-09-13 [`ref_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns +[`regex_creation_in_loops`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_creation_in_loops [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro [`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once @@ -6027,6 +6064,7 @@ Released 2018-09-13 [`unnecessary_get_then_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_get_then_check [`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations +[`unnecessary_literal_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_bound [`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap [`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor [`unnecessary_min_or_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_min_or_max diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index cf810798d8cc1..1f7784fc489f6 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.83" +version = "0.1.84" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -23,7 +23,7 @@ path = "src/driver.rs" [dependencies] clippy_config = { path = "clippy_config" } clippy_lints = { path = "clippy_lints" } -rustc_tools_util = "0.3.0" +rustc_tools_util = "0.4.0" tempfile = { version = "3.3", optional = true } termize = "0.1" color-print = "0.3.4" @@ -39,6 +39,8 @@ toml = "0.7.3" walkdir = "2.3" filetime = "0.2.9" itertools = "0.12" +pulldown-cmark = "0.11" +rinja = { version = "0.3", default-features = false, features = ["config"] } # UI test dependencies clippy_utils = { path = "clippy_utils" } @@ -50,7 +52,7 @@ parking_lot = "0.12" tokio = { version = "1", features = ["io-util"] } [build-dependencies] -rustc_tools_util = "0.3.0" +rustc_tools_util = "0.4.0" [features] integration = ["tempfile"] diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 07a56fb33df11..43b551ae21617 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -329,7 +329,7 @@ arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] ## `array-size-threshold` The maximum allowed size for arrays on the stack -**Default Value:** `512000` +**Default Value:** `16384` --- **Affected lints:** diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index 9da7112345de9..d21df202dcaba 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.83" +version = "0.1.84" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index e4e2c97fdc1d9..4757c0b1339a7 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -97,6 +97,30 @@ impl ConfError { } } +// Remove code tags and code behind '# 's, as they are not needed for the lint docs and --explain +pub fn sanitize_explanation(raw_docs: &str) -> String { + // Remove tags and hidden code: + let mut explanation = String::with_capacity(128); + let mut in_code = false; + for line in raw_docs.lines().map(str::trim) { + if let Some(lang) = line.strip_prefix("```") { + let tag = lang.split_once(',').map_or(lang, |(left, _)| left); + if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") { + explanation += "```rust\n"; + } else { + explanation += line; + explanation.push('\n'); + } + in_code = !in_code; + } else if !(in_code && line.starts_with("# ")) { + explanation += line; + explanation.push('\n'); + } + } + + explanation +} + macro_rules! wrap_option { () => { None @@ -366,7 +390,7 @@ define_Conf! { arithmetic_side_effects_allowed_unary: Vec = <_>::default(), /// The maximum allowed size for arrays on the stack #[lints(large_const_arrays, large_stack_arrays)] - array_size_threshold: u64 = 512_000, + array_size_threshold: u64 = 16 * 1024, /// Suppress lints whenever the suggested change would cause breakage for other crates. #[lints( box_collection, diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs index c63d98a0a13f9..42651521f8d7a 100644 --- a/src/tools/clippy/clippy_config/src/lib.rs +++ b/src/tools/clippy/clippy_config/src/lib.rs @@ -26,5 +26,5 @@ mod metadata; pub mod msrvs; pub mod types; -pub use conf::{Conf, get_configuration_metadata, lookup_conf_file}; +pub use conf::{Conf, get_configuration_metadata, lookup_conf_file, sanitize_explanation}; pub use metadata::ClippyConfiguration; diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs index 68a3b11d3848f..2f4da4cba3df3 100644 --- a/src/tools/clippy/clippy_config/src/msrvs.rs +++ b/src/tools/clippy/clippy_config/src/msrvs.rs @@ -17,8 +17,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { - 1,83,0 { CONST_EXTERN_FN } - 1,83,0 { CONST_FLOAT_BITS_CONV } + 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY } 1,82,0 { IS_NONE_OR } 1,81,0 { LINT_REASONS_STABILIZATION } 1,80,0 { BOX_INTO_ITER} diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index a5d72c3a559df..952a8711fb4e4 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -9,7 +9,7 @@ aho-corasick = "1.0" clap = { version = "4.4", features = ["derive"] } indoc = "1.0" itertools = "0.12" -opener = "0.6" +opener = "0.7" shell-escape = "0.1" walkdir = "2.3" diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 5fd65017cfb97..e15ba3397178a 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -207,13 +207,13 @@ pub(crate) fn get_stabilization_version() -> String { fn get_test_file_contents(lint_name: &str, msrv: bool) -> String { let mut test = formatdoc!( - r#" + r" #![warn(clippy::{lint_name})] fn main() {{ // test code goes here }} - "# + " ); if msrv { @@ -272,23 +272,23 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { result.push_str(&if enable_msrv { formatdoc!( - r#" + r" use clippy_config::msrvs::{{self, Msrv}}; use clippy_config::Conf; {pass_import} use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; use rustc_session::impl_lint_pass; - "# + " ) } else { formatdoc!( - r#" + r" {pass_import} use rustc_lint::{{{context_import}, {pass_type}}}; use rustc_session::declare_lint_pass; - "# + " ) }); @@ -296,7 +296,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { result.push_str(&if enable_msrv { formatdoc!( - r#" + r" pub struct {name_camel} {{ msrv: Msrv, }} @@ -315,15 +315,15 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { // TODO: Add MSRV level to `clippy_config/src/msrvs.rs` if needed. // TODO: Update msrv config comment in `clippy_config/src/conf.rs` - "# + " ) } else { formatdoc!( - r#" + r" declare_lint_pass!({name_camel} => [{name_upper}]); impl {pass_type}{pass_lifetimes} for {name_camel} {{}} - "# + " ) }); @@ -416,7 +416,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R } else { let _: fmt::Result = writedoc!( lint_file_contents, - r#" + r" use rustc_lint::{{{context_import}, LintContext}}; use super::{name_upper}; @@ -425,7 +425,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R pub(super) fn check(cx: &{context_import}{pass_lifetimes}) {{ todo!(); }} - "# + " ); } diff --git a/src/tools/clippy/clippy_dev/src/serve.rs b/src/tools/clippy/clippy_dev/src/serve.rs index cc14cd8dae69e..d367fefec619c 100644 --- a/src/tools/clippy/clippy_dev/src/serve.rs +++ b/src/tools/clippy/clippy_dev/src/serve.rs @@ -19,7 +19,9 @@ pub fn run(port: u16, lint: Option) -> ! { }); loop { - if mtime("util/gh-pages/lints.json") < mtime("clippy_lints/src") { + let index_time = mtime("util/gh-pages/index.html"); + + if index_time < mtime("clippy_lints/src") || index_time < mtime("util/gh-pages/index_template.html") { Command::new(env::var("CARGO").unwrap_or("cargo".into())) .arg("collect-metadata") .spawn() diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index d1188940b46a8..63ea6faf60dbd 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.83" +version = "0.1.84" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -13,7 +13,6 @@ arrayvec = { version = "0.7", default-features = false } cargo_metadata = "0.18" clippy_config = { path = "../clippy_config" } clippy_utils = { path = "../clippy_utils" } -declare_clippy_lint = { path = "../declare_clippy_lint" } itertools = "0.12" quine-mc_cluskey = "0.2" regex-syntax = "0.8" diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs index 40d154c0bdfe3..bf1d077fec28f 100644 --- a/src/tools/clippy/clippy_lints/src/box_default.rs +++ b/src/tools/clippy/clippy_lints/src/box_default.rs @@ -47,7 +47,7 @@ impl LateLintPass<'_> for BoxDefault { // And the call is that of a `Box` method && path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box()) // And the single argument to the call is another function call - // This is the `T::default()` of `Box::new(T::default())` + // This is the `T::default()` (or default equivalent) of `Box::new(T::default())` && let ExprKind::Call(arg_path, _) = arg.kind // And we are not in a foreign crate's macro && !in_external_macro(cx.sess(), expr.span) diff --git a/src/tools/clippy/clippy_lints/src/byte_char_slices.rs b/src/tools/clippy/clippy_lints/src/byte_char_slices.rs index dd2620b0b9df9..d88c0711b3971 100644 --- a/src/tools/clippy/clippy_lints/src/byte_char_slices.rs +++ b/src/tools/clippy/clippy_lints/src/byte_char_slices.rs @@ -41,7 +41,7 @@ impl EarlyLintPass for ByteCharSlice { "can be more succinctly written as a byte str", "try", format!("b\"{slice}\""), - Applicability::MaybeIncorrect, + Applicability::MachineApplicable, ); } } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs index d4d5ee37bccbb..b7b63250864e6 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -19,7 +19,7 @@ pub(super) fn check( if msrv.meets(msrvs::UNSIGNED_ABS) && let ty::Int(from) = cast_from.kind() && let ty::Uint(to) = cast_to.kind() - && let ExprKind::MethodCall(method_path, receiver, ..) = cast_expr.kind + && let ExprKind::MethodCall(method_path, receiver, [], _) = cast_expr.kind && method_path.ident.name.as_str() == "abs" { let span = if from.bit_width() == to.bit_width() { diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs index 960c81045e36f..b11b967f8bc78 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -19,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx.typeck_results().expr_ty(expr), ); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } else if let ExprKind::MethodCall(method_path, self_arg, ..) = &expr.kind { + } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind { if method_path.ident.name == sym!(cast) && let Some(generic_args) = method_path.args && let [GenericArg::Type(cast_to)] = generic_args.args diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs index 24570d8f4401c..b43906903a0e0 100644 --- a/src/tools/clippy/clippy_lints/src/create_dir.rs +++ b/src/tools/clippy/clippy_lints/src/create_dir.rs @@ -34,7 +34,7 @@ declare_lint_pass!(CreateDir => [CREATE_DIR]); impl LateLintPass<'_> for CreateDir { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Call(func, [arg, ..]) = expr.kind + if let ExprKind::Call(func, [arg]) = expr.kind && let ExprKind::Path(ref path) = func.kind && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id) diff --git a/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs new file mode 100644 index 0000000000000..b1e39c70baa2b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs @@ -0,0 +1,162 @@ +#[macro_export] +#[allow(clippy::crate_in_macro_def)] +macro_rules! declare_clippy_lint { + (@ + $(#[doc = $lit:literal])* + pub $lint_name:ident, + $category:ident, + $lintcategory:expr, + $desc:literal, + $version_expr:expr, + $version_lit:literal + ) => { + rustc_session::declare_tool_lint! { + $(#[doc = $lit])* + #[clippy::version = $version_lit] + pub clippy::$lint_name, + $category, + $desc, + report_in_external_macro:true + } + + pub(crate) static ${concat($lint_name, _INFO)}: &'static crate::LintInfo = &crate::LintInfo { + lint: &$lint_name, + category: $lintcategory, + explanation: concat!($($lit,"\n",)*), + location: concat!(file!(), "#L", line!()), + version: $version_expr + }; + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + restriction, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Allow, crate::LintCategory::Restriction, $desc, + Some($version), $version + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + style, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Warn, crate::LintCategory::Style, $desc, + Some($version), $version + + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + correctness, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Deny, crate::LintCategory::Correctness, $desc, + Some($version), $version + + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + perf, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Warn, crate::LintCategory::Perf, $desc, + Some($version), $version + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + complexity, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Warn, crate::LintCategory::Complexity, $desc, + Some($version), $version + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + suspicious, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Warn, crate::LintCategory::Suspicious, $desc, + Some($version), $version + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + nursery, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Allow, crate::LintCategory::Nursery, $desc, + Some($version), $version + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + pedantic, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Allow, crate::LintCategory::Pedantic, $desc, + Some($version), $version + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + cargo, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Allow, crate::LintCategory::Cargo, $desc, + Some($version), $version + } + }; + + ( + $(#[doc = $lit:literal])* + pub $lint_name:ident, + internal, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Allow, crate::LintCategory::Internal, $desc, + None, "0.0.0" + } + }; +} diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 9cec672beb004..3c4e75df8abe4 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -306,6 +306,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::manual_float_methods::MANUAL_IS_FINITE_INFO, crate::manual_float_methods::MANUAL_IS_INFINITE_INFO, crate::manual_hash_one::MANUAL_HASH_ONE_INFO, + crate::manual_ignore_case_cmp::MANUAL_IGNORE_CASE_CMP_INFO, crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, crate::manual_is_power_of_two::MANUAL_IS_POWER_OF_TWO_INFO, crate::manual_let_else::MANUAL_LET_ELSE_INFO, @@ -639,6 +640,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::ref_patterns::REF_PATTERNS_INFO, crate::reference::DEREF_ADDROF_INFO, crate::regex::INVALID_REGEX_INFO, + crate::regex::REGEX_CREATION_IN_LOOPS_INFO, crate::regex::TRIVIAL_REGEX_INFO, crate::repeat_vec_with_capacity::REPEAT_VEC_WITH_CAPACITY_INFO, crate::reserve_after_initialization::RESERVE_AFTER_INITIALIZATION_INFO, @@ -736,6 +738,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::unit_types::UNIT_CMP_INFO, crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO, crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO, + crate::unnecessary_literal_bound::UNNECESSARY_LITERAL_BOUND_INFO, crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO, crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index dc10b64698b4f..de775b647952e 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { if !expr.span.from_expansion() // Avoid cases already linted by `field_reassign_with_default` && !self.reassigned_linted.contains(&expr.span) - && let ExprKind::Call(path, ..) = expr.kind + && let ExprKind::Call(path, []) = expr.kind && !in_automatically_derived(cx.tcx, expr.hir_id) && let ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() @@ -253,7 +253,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { /// Checks if the given expression is the `default` method belonging to the `Default` trait. fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool { - if let ExprKind::Call(fn_expr, _) = &expr.kind + if let ExprKind::Call(fn_expr, []) = &expr.kind && let ExprKind::Path(qpath) = &fn_expr.kind && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id) { diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index e090644ae4410..89c6a4e08dc44 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -452,7 +452,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.82.0"] pub TOO_LONG_FIRST_DOC_PARAGRAPH, - style, + nursery, "ensure that the first line of a documentation paragraph isn't too long" } diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs index f37d11f7eb9b7..3c235fab0098b 100644 --- a/src/tools/clippy/clippy_lints/src/exit.rs +++ b/src/tools/clippy/clippy_lints/src/exit.rs @@ -43,7 +43,7 @@ declare_lint_pass!(Exit => [EXIT]); impl<'tcx> LateLintPass<'tcx> for Exit { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Call(path_expr, _args) = e.kind + if let ExprKind::Call(path_expr, [_]) = e.kind && let ExprKind::Path(ref path) = path_expr.kind && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::process_exit, def_id) diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index 5b423a96918f6..4e4434ec7d19c 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { && unwrap_fun.ident.name == sym::unwrap // match call to write_fmt && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = *look_in_block(cx, &write_call.kind) - && let ExprKind::Call(write_recv_path, _) = write_recv.kind + && let ExprKind::Call(write_recv_path, []) = write_recv.kind && write_fun.ident.name == sym!(write_fmt) && let Some(def_id) = path_def_id(cx, write_recv_path) { diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs index 8da6623f34de9..daa199779e3cb 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -436,12 +436,12 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { lhs, rhs, ) = expr.kind + && let ExprKind::MethodCall(path, self_arg, [], _) = &lhs.kind + && path.ident.name.as_str() == "exp" && cx.typeck_results().expr_ty(lhs).is_floating_point() && let Some(value) = ConstEvalCtxt::new(cx).eval(rhs) && (F32(1.0) == value || F64(1.0) == value) - && let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind && cx.typeck_results().expr_ty(self_arg).is_floating_point() - && path.ident.name.as_str() == "exp" { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs index 7c0515b8c5608..5619cb0ab1b06 100644 --- a/src/tools/clippy/clippy_lints/src/format_impl.rs +++ b/src/tools/clippy/clippy_lints/src/format_impl.rs @@ -151,7 +151,7 @@ struct FormatImplExpr<'a, 'tcx> { impl FormatImplExpr<'_, '_> { fn check_to_string_in_display(&self) { if self.format_trait_impl.name == sym::Display - && let ExprKind::MethodCall(path, self_arg, ..) = self.expr.kind + && let ExprKind::MethodCall(path, self_arg, [], _) = self.expr.kind // Get the hir_id of the object we are calling the method on // Is the method to_string() ? && path.ident.name == sym::to_string diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs index 1c52514a330d4..ba80c099a0153 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs @@ -82,7 +82,7 @@ fn mutex_lock_call<'tcx>( expr: &'tcx Expr<'_>, op_mutex: Option<&'tcx Expr<'_>>, ) -> ControlFlow<&'tcx Expr<'tcx>> { - if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind + if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind && path.ident.as_str() == "lock" && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs() && is_type_diagnostic_item(cx, ty, sym::Mutex) diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs index f4a64f5c20b44..3b84b569c3edf 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -139,6 +139,13 @@ fn check_manual_check<'tcx>( if_block, else_block, msrv, + matches!( + clippy_utils::get_parent_expr(cx, expr), + Some(Expr { + kind: ExprKind::If(..), + .. + }) + ), ), BinOpKind::Lt | BinOpKind::Le => check_gt( cx, @@ -149,6 +156,13 @@ fn check_manual_check<'tcx>( if_block, else_block, msrv, + matches!( + clippy_utils::get_parent_expr(cx, expr), + Some(Expr { + kind: ExprKind::If(..), + .. + }) + ), ), _ => {}, } @@ -165,6 +179,7 @@ fn check_gt( if_block: &Expr<'_>, else_block: &Expr<'_>, msrv: &Msrv, + is_composited: bool, ) { if let Some(big_var) = Var::new(big_var) && let Some(little_var) = Var::new(little_var) @@ -178,6 +193,7 @@ fn check_gt( if_block, else_block, msrv, + is_composited, ); } } @@ -206,6 +222,7 @@ fn check_subtraction( if_block: &Expr<'_>, else_block: &Expr<'_>, msrv: &Msrv, + is_composited: bool, ) { let if_block = peel_blocks(if_block); let else_block = peel_blocks(else_block); @@ -226,6 +243,7 @@ fn check_subtraction( else_block, if_block, msrv, + is_composited, ); return; } @@ -242,13 +260,18 @@ fn check_subtraction( && let Some(little_var_snippet) = snippet_opt(cx, little_var.span) && (!is_in_const_context(cx) || msrv.meets(msrvs::SATURATING_SUB_CONST)) { + let sugg = format!( + "{}{big_var_snippet}.saturating_sub({little_var_snippet}){}", + if is_composited { "{ " } else { "" }, + if is_composited { " }" } else { "" } + ); span_lint_and_sugg( cx, IMPLICIT_SATURATING_SUB, expr_span, "manual arithmetic check found", "replace it with", - format!("{big_var_snippet}.saturating_sub({little_var_snippet})"), + sugg, Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index 590d9afd1b437..00f832372248e 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -3,8 +3,8 @@ use clippy_utils::source::snippet; use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::def_id::DefId; use rustc_hir::{ - AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifier, - TyKind, WherePredicate, + AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifier, TyKind, + WherePredicate, }; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -190,15 +190,6 @@ fn is_same_generics<'tcx>( .enumerate() .skip(1) // skip `Self` implicit arg .all(|(arg_index, arg)| { - if [ - implied_by_generics.host_effect_index, - implied_generics.host_effect_index, - ] - .contains(&Some(arg_index)) - { - // skip host effect params in determining whether generics are same - return true; - } if let Some(ty) = arg.as_type() { if let &ty::Param(ty::ParamTy { index, .. }) = ty.kind() // `index == 0` means that it's referring to `Self`, diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index 22e9674714fe5..ae2c3e0491f2d 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { if let Some(range) = higher::Range::hir(index) { // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] if let ty::Array(_, s) = ty.kind() { - let size: u128 = if let Some(size) = s.try_eval_target_usize(cx.tcx, cx.param_env) { + let size: u128 = if let Some(size) = s.try_to_target_usize(cx.tcx) { size.into() } else { return; @@ -183,7 +183,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { && let ty::Uint(utype) = cx.typeck_results().expr_ty(index).kind() && *utype == ty::UintTy::Usize && let ty::Array(_, s) = ty.kind() - && let Some(size) = s.try_eval_target_usize(cx.tcx, cx.param_env) + && let Some(size) = s.try_to_target_usize(cx.tcx) { // get constant offset and check whether it is in bounds let off = usize::try_from(off).unwrap(); diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs index 66a8a3167a4f2..dd90e2a6e94f4 100644 --- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -50,11 +50,28 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Detects type names that are prefixed or suffixed by the - /// containing module's name. + /// Detects public item names that are prefixed or suffixed by the + /// containing public module's name. /// /// ### Why is this bad? - /// It requires the user to type the module name twice. + /// It requires the user to type the module name twice in each usage, + /// especially if they choose to import the module rather than its contents. + /// + /// Lack of such repetition is also the style used in the Rust standard library; + /// e.g. `io::Error` and `fmt::Error` rather than `io::IoError` and `fmt::FmtError`; + /// and `array::from_ref` rather than `array::array_from_ref`. + /// + /// ### Known issues + /// Glob re-exports are ignored; e.g. this will not warn even though it should: + /// + /// ```no_run + /// pub mod foo { + /// mod iteration { + /// pub struct FooIter {} + /// } + /// pub use iteration::*; // creates the path `foo::FooIter` + /// } + /// ``` /// /// ### Example /// ```no_run @@ -71,7 +88,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.33.0"] pub MODULE_NAME_REPETITIONS, - pedantic, + restriction, "type names prefixed/postfixed with their containing module's name" } @@ -389,12 +406,12 @@ impl LateLintPass<'_> for ItemNameRepetitions { let item_name = item.ident.name.as_str(); let item_camel = to_camel_case(item_name); if !item.span.from_expansion() && is_present_in_source(cx, item.span) { - if let [.., (mod_name, mod_camel, owner_id)] = &*self.modules { + if let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules { // constants don't have surrounding modules if !mod_camel.is_empty() { if mod_name == &item.ident.name && let ItemKind::Mod(..) = item.kind - && (!self.allow_private_module_inception || cx.tcx.visibility(owner_id.def_id).is_public()) + && (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public()) { span_lint( cx, @@ -403,9 +420,13 @@ impl LateLintPass<'_> for ItemNameRepetitions { "module has the same name as its containing module", ); } + // The `module_name_repetitions` lint should only trigger if the item has the module in its // name. Having the same name is accepted. - if cx.tcx.visibility(item.owner_id).is_public() && item_camel.len() > mod_camel.len() { + if cx.tcx.visibility(item.owner_id).is_public() + && cx.tcx.visibility(mod_owner_id.def_id).is_public() + && item_camel.len() > mod_camel.len() + { let matching = count_match_start(mod_camel, &item_camel); let rmatching = count_match_end(mod_camel, &item_camel); let nchars = mod_camel.chars().count(); diff --git a/src/tools/clippy/clippy_lints/src/large_futures.rs b/src/tools/clippy/clippy_lints/src/large_futures.rs index 6f5065e4936cb..25f9be8b2d7a4 100644 --- a/src/tools/clippy/clippy_lints/src/large_futures.rs +++ b/src/tools/clippy/clippy_lints/src/large_futures.rs @@ -57,7 +57,7 @@ impl_lint_pass!(LargeFuture => [LARGE_FUTURES]); impl<'tcx> LateLintPass<'tcx> for LargeFuture { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Match(scrutinee, _, MatchSource::AwaitDesugar) = expr.kind - && let ExprKind::Call(func, [arg, ..]) = scrutinee.kind + && let ExprKind::Call(func, [arg]) = scrutinee.kind && let ExprKind::Path(QPath::LangItem(LangItem::IntoFutureIntoFuture, ..)) = func.kind && !expr.span.from_expansion() && let ty = cx.typeck_results().expr_ty(arg) diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs index 0f061d6de504f..4ef881f11d599 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs @@ -1,3 +1,5 @@ +use std::num::Saturating; + use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; @@ -30,6 +32,7 @@ declare_clippy_lint! { pub struct LargeStackArrays { maximum_allowed_size: u64, prev_vec_macro_callsite: Option, + const_item_counter: Saturating, } impl LargeStackArrays { @@ -37,6 +40,7 @@ impl LargeStackArrays { Self { maximum_allowed_size: conf.array_size_threshold, prev_vec_macro_callsite: None, + const_item_counter: Saturating(0), } } @@ -60,8 +64,21 @@ impl LargeStackArrays { impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]); impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { + fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if matches!(item.kind, ItemKind::Static(..) | ItemKind::Const(..)) { + self.const_item_counter += 1; + } + } + + fn check_item_post(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if matches!(item.kind, ItemKind::Static(..) | ItemKind::Const(..)) { + self.const_item_counter -= 1; + } + } + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind + if self.const_item_counter.0 == 0 + && let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind && !self.is_from_vec_macro(cx, expr.span) && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind() && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind() diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 035ee40348c77..47c65ee6d0ba9 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -513,7 +513,7 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_> return; } - if let (&ExprKind::MethodCall(method_path, receiver, args, _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { + if let (&ExprKind::MethodCall(method_path, receiver, [], _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { // check if we are in an is_empty() method if let Some(name) = get_item_name(cx, method) { if name.as_str() == "is_empty" { @@ -521,29 +521,17 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_> } } - check_len( - cx, - span, - method_path.ident.name, - receiver, - args, - &lit.node, - op, - compare_to, - ); + check_len(cx, span, method_path.ident.name, receiver, &lit.node, op, compare_to); } else { check_empty_expr(cx, span, method, lit, op); } } -// FIXME(flip1995): Figure out how to reduce the number of arguments -#[allow(clippy::too_many_arguments)] fn check_len( cx: &LateContext<'_>, span: Span, method_name: Symbol, receiver: &Expr<'_>, - args: &[Expr<'_>], lit: &LitKind, op: &str, compare_to: u32, @@ -554,7 +542,7 @@ fn check_len( return; } - if method_name == sym::len && args.is_empty() && has_is_empty(cx, receiver) { + if method_name == sym::len && has_is_empty(cx, receiver) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 6ee064a6124b9..6e29dde2211fb 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -1,6 +1,7 @@ #![feature(array_windows)] #![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] +#![feature(macro_metavar_expr_concat)] #![feature(f128)] #![feature(f16)] #![feature(if_let_guard)] @@ -56,9 +57,10 @@ extern crate rustc_trait_selection; extern crate thin_vec; #[macro_use] -extern crate clippy_utils; +mod declare_clippy_lint; + #[macro_use] -extern crate declare_clippy_lint; +extern crate clippy_utils; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; @@ -203,6 +205,7 @@ mod manual_clamp; mod manual_div_ceil; mod manual_float_methods; mod manual_hash_one; +mod manual_ignore_case_cmp; mod manual_is_ascii_check; mod manual_is_power_of_two; mod manual_let_else; @@ -360,6 +363,7 @@ mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; mod unnecessary_box_returns; +mod unnecessary_literal_bound; mod unnecessary_map_on_constructor; mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; @@ -391,7 +395,7 @@ mod zero_sized_map_values; mod zombie_processes; // end lints modules, do not remove this comment, it’s used in `update_lints` -use clippy_config::{Conf, get_configuration_metadata}; +use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation}; use clippy_utils::macros::FormatArgsStorage; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; @@ -519,8 +523,9 @@ impl LintInfo { pub fn explain(name: &str) -> i32 { let target = format!("clippy::{}", name.to_ascii_uppercase()); + if let Some(info) = declared_lints::LINTS.iter().find(|info| info.lint.name == target) { - println!("{}", info.explanation); + println!("{}", sanitize_explanation(info.explanation)); // Check if the lint has configuration let mut mdconf = get_configuration_metadata(); let name = name.to_ascii_lowercase(); @@ -896,7 +901,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns)); store.register_early_pass(|| Box::new(visibility::Visibility)); store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions::new(conf))); - store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods)); + store.register_late_pass(move |_| Box::new(manual_float_methods::ManualFloatMethods::new(conf))); store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes)); store.register_late_pass(|_| Box::new(error_impl_error::ErrorImplError)); store.register_late_pass(move |_| Box::new(absolute_paths::AbsolutePaths::new(conf))); @@ -941,5 +946,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo)); store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)); store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))); + store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)); + store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index a7c48eb216aa4..5a3930b8bb846 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -6,8 +6,8 @@ use rustc_errors::Applicability; use rustc_hir::FnRetTy::Return; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound, - walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_where_predicate, + Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound, walk_poly_trait_ref, + walk_trait_ref, walk_ty, walk_where_predicate, }; use rustc_hir::{ BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics, diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index 81f2a03fb55fb..e2dcb20f906d3 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -412,7 +412,6 @@ impl LiteralDigitGrouping { } } -#[expect(clippy::module_name_repetitions)] pub struct DecimalLiteralRepresentation { threshold: u64, } diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs index b134af500f540..022c6bcc70bb0 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs @@ -30,7 +30,7 @@ pub(super) fn check( return; } } else if count - .try_eval_target_usize(cx.tcx, cx.param_env) + .try_to_target_usize(cx.tcx) .map_or(true, |x| x > 32) && !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN) { diff --git a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs index 858e3be5093ef..e25c03db53442 100644 --- a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs @@ -42,6 +42,7 @@ pub(super) fn check<'tcx>( let mut loop_visitor = LoopVisitor { cx, label, + inner_labels: label.into_iter().collect(), is_finite: false, loop_depth: 0, }; @@ -93,6 +94,7 @@ fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option struct LoopVisitor<'hir, 'tcx> { cx: &'hir LateContext<'tcx>, label: Option