diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index f5b8b9d6a1e6f..40bf435e6d110 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -2184,28 +2184,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - // See . - // - // In the new solver, check the well-formedness of the return type. - // This emulates, in a way, the predicates that fall out of - // normalizing the return type in the old solver. - // - // FIXME(-Znext-solver): We alternatively could check the predicates of - // the method itself hold, but we intentionally do not do this in the old - // solver b/c of cycles, and doing it in the new solver would be stronger. - // This should be fixed in the future, since it likely leads to much better - // method winnowing. - if let Some(xform_ret_ty) = xform_ret_ty - && self.infcx.next_trait_solver() - { - ocx.register_obligation(traits::Obligation::new( - self.tcx, - cause.clone(), - self.param_env, - ty::ClauseKind::WellFormed(xform_ret_ty.into()), - )); - } - // Evaluate those obligations to see if they might possibly hold. for error in ocx.try_evaluate_obligations() { result = ProbeResult::NoMatch; diff --git a/compiler/rustc_interface/src/diagnostics.rs b/compiler/rustc_interface/src/diagnostics.rs index 2a2757f814715..191f36ee2f9f1 100644 --- a/compiler/rustc_interface/src/diagnostics.rs +++ b/compiler/rustc_interface/src/diagnostics.rs @@ -122,13 +122,16 @@ pub(crate) struct MultipleOutputTypesToStdout; #[diag( "target feature `{$feature}` must be {$enabled} to ensure that the ABI of the current target can be implemented correctly" )] -#[note( - "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 #116344 ")] pub(crate) struct AbiRequiredTargetFeature<'a> { pub feature: &'a str, pub enabled: &'a str, + #[note( + "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 #116344 " + )] + pub fcw: bool, } #[derive(Diagnostic)] diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 307d858bf2428..5173f12d6fb66 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -29,7 +29,7 @@ use rustc_span::edition::Edition; use rustc_span::source_map::SourceMapInputs; use rustc_span::{SessionGlobals, Symbol, sym}; use rustc_structures::CrateType; -use rustc_target::spec::Target; +use rustc_target::spec::{Arch, Target}; use tracing::info; use crate::diagnostics; @@ -102,16 +102,36 @@ pub(crate) fn check_abi_required_features(sess: &Session) { ); } + // Make this a hard error on ARM since starting with LLVM24, the backend will otherwise + // emit a (less friendly) hard error. + let hard_error = matches!(sess.target.arch, Arch::Arm); + for feature in abi_feature_constraints.required { if !sess.internal_target_features.contains(&Symbol::intern(feature)) { - sess.dcx() - .emit_warn(diagnostics::AbiRequiredTargetFeature { feature, enabled: "enabled" }); + let diag = diagnostics::AbiRequiredTargetFeature { + feature, + enabled: "enabled", + fcw: !hard_error, + }; + if hard_error { + sess.dcx().emit_err(diag); + } else { + sess.dcx().emit_warn(diag); + } } } for feature in abi_feature_constraints.incompatible { if sess.internal_target_features.contains(&Symbol::intern(feature)) { - sess.dcx() - .emit_warn(diagnostics::AbiRequiredTargetFeature { feature, enabled: "disabled" }); + let diag = diagnostics::AbiRequiredTargetFeature { + feature, + enabled: "disabled", + fcw: !hard_error, + }; + if hard_error { + sess.dcx().emit_err(diag); + } else { + sess.dcx().emit_warn(diag); + } } } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 86c5aaf4a46ed..7bb9b4ff8c375 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1256,13 +1256,11 @@ impl<'tcx> Debug for Rvalue<'tcx> { }; let mut struct_fmt = fmt.debug_struct(&name); - // FIXME(project-rfc-2229#48): This should be a list of capture names/places - if let Some(def_id) = def_id.as_local() - && let Some(upvars) = tcx.upvars_mentioned(def_id) - { - for (&var_id, place) in iter::zip(upvars.keys(), places) { - let var_name = tcx.hir_name(var_id); - struct_fmt.field(var_name.as_str(), place); + if let Some(def_id) = def_id.as_local() { + let captures = tcx.closure_captures(def_id); + assert_eq!(captures.len(), places.len()); + for (&capture, place) in iter::zip(captures, places) { + struct_fmt.field(capture.to_symbol().as_str(), place); } } else { for (index, place) in places.iter().enumerate() { @@ -1277,13 +1275,11 @@ impl<'tcx> Debug for Rvalue<'tcx> { let name = format!("{{coroutine@{:?}}}", tcx.def_span(def_id)); let mut struct_fmt = fmt.debug_struct(&name); - // FIXME(project-rfc-2229#48): This should be a list of capture names/places - if let Some(def_id) = def_id.as_local() - && let Some(upvars) = tcx.upvars_mentioned(def_id) - { - for (&var_id, place) in iter::zip(upvars.keys(), places) { - let var_name = tcx.hir_name(var_id); - struct_fmt.field(var_name.as_str(), place); + if let Some(def_id) = def_id.as_local() { + let captures = tcx.closure_captures(def_id); + assert_eq!(captures.len(), places.len()); + for (&capture, place) in iter::zip(captures, places) { + struct_fmt.field(capture.to_symbol().as_str(), place); } } else { for (index, place) in places.iter().enumerate() { diff --git a/compiler/rustc_mir_transform/src/coverage/hir_info.rs b/compiler/rustc_mir_transform/src/coverage/hir_info.rs index ab66bf1a733ef..246104ee97843 100644 --- a/compiler/rustc_mir_transform/src/coverage/hir_info.rs +++ b/compiler/rustc_mir_transform/src/coverage/hir_info.rs @@ -1,6 +1,7 @@ use rustc_hir as hir; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_middle::hir::nested_filter; +use rustc_middle::mir; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::Span; use rustc_span::def_id::LocalDefId; @@ -20,21 +21,24 @@ pub(crate) struct ExtractedHirInfo { pub(crate) hole_spans: Vec, } -pub(crate) fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHirInfo { - // FIXME(#79625): Consider improving MIR to provide the information needed, to avoid going back - // to HIR for it. - - // Synthetic by-move coroutine bodies don't have useful HIR of their own. - // Use the original coroutine body instead. These synthetic bodies are - // created with a coroutine type, so we can inspect that type as-is. - if tcx.is_synthetic_mir(def_id) { - let effective_def_id = +pub(crate) fn extract_hir_info<'tcx>( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, +) -> ExtractedHirInfo { + let def_id: LocalDefId = { + let mut def_id = mir_body.source.def_id().expect_local(); + + // Synthetic by-move coroutine bodies don't have useful HIR of their own. + // Use the original coroutine body instead. These synthetic bodies are + // created with a coroutine type, so we can inspect that type as-is. + if tcx.is_synthetic_mir(def_id) { match *tcx.type_of(def_id).instantiate_identity().skip_normalization().kind() { - ty::Coroutine(coroutine_def_id, _) => coroutine_def_id.expect_local(), - _ => tcx.local_parent(def_id), - }; - return extract_hir_info(tcx, effective_def_id); - } + ty::Coroutine(coroutine_def_id, _) => def_id = coroutine_def_id.expect_local(), + _ => def_id = tcx.local_parent(def_id), + } + } + 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"); @@ -45,14 +49,15 @@ pub(crate) fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> E let mut body_span = hir_body.value.span; - use hir::{Closure, Expr, ExprKind, Node}; // Unexpand a closure's body span back to the context of its declaration. // This helps with closure bodies that consist of just a single bang-macro, // and also with closure bodies produced by async desugaring. - if let Node::Expr(&Expr { kind: ExprKind::Closure(&Closure { fn_decl_span, .. }), .. }) = - hir_node + if let hir::Node::Expr(expr) = hir_node + && let hir::ExprKind::Closure(closure) = expr.kind + && let Some(effective_body_span) = + body_span.find_ancestor_in_same_ctxt(closure.fn_decl_span) { - body_span = body_span.find_ancestor_in_same_ctxt(fn_decl_span).unwrap_or(body_span); + body_span = effective_body_span; } // The actual signature span is only used if it has the same context and diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index fdca5e9bfdc9b..d8c4ba4b40b04 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -58,10 +58,10 @@ impl<'tcx> crate::MirPass<'tcx> for InstrumentCoverage { } fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { - let def_id = mir_body.source.def_id(); - let _span = debug_span!("instrument_function_for_coverage", ?def_id).entered(); + let _span = debug_span!("instrument_function_for_coverage", def_id = ?mir_body.source.def_id()) + .entered(); - let hir_info = hir_info::extract_hir_info(tcx, def_id.expect_local()); + let hir_info = hir_info::extract_hir_info(tcx, mir_body); // Build the coverage graph, which is a simplified view of the MIR control-flow // graph that ignores some details not relevant to coverage instrumentation. diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index c4ad541e5b30c..3e7b3d2ae2daa 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -79,7 +79,6 @@ use crate::error_reporting::traits::ambiguity::{ use crate::infer; use crate::infer::relate::{self, RelateResult, TypeRelation}; use crate::infer::{InferCtxt, InferCtxtExt as _, TypeTrace, ValuePairs}; -use crate::solve::deeply_normalize_for_diagnostics; use crate::traits::{ MatchExpressionArmCause, Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt, specialization_graph, @@ -1577,10 +1576,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let (expected_found, exp_found, is_simple_error, values, param_env) = match values { None => (None, Mismatch::Fixed("type"), false, None, None), Some(ty::ParamEnvAnd { param_env, value: values }) => { - let mut values = self.resolve_vars_if_possible(values); - if self.next_trait_solver() { - values = deeply_normalize_for_diagnostics(self, param_env, values); - } + let values = self.resolve_vars_if_possible(values); let (is_simple_error, exp_found) = match values { ValuePairs::Terms(ExpectedFound { expected, found }) => { match (expected.kind(), found.kind()) { 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 11b051b530198..29ae564d9eb9a 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 @@ -1669,8 +1669,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { bound_predicate.rebind(data), ); let unnormalized_term = data.projection_term.to_term(self.tcx, ty::IsRigid::No); - // FIXME(-Znext-solver): For diagnostic purposes, it would be nice - // to deeply normalize this type. let normalized_term = ocx.normalize( &obligation.cause, obligation.param_env, diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index dc29b6311cc7e..fc16b6d44c310 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -504,7 +504,16 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // (*) The predicates of an inherent associated type include the // predicates of the impl that it's contained in. - if !data.self_ty().has_escaping_bound_vars() { + // In an ideal world, there are no escaping bound vars here. However, WF is jank, and + // sometimes there are. We can only `compute_inherent_assoc_term_args` if the Self ty in the + // args has no escaping bound vars. If we already have impl format args, though, + // `compute_inherent_assoc_term_args` is a no-op (and we have no Self type), so no need to + // check for escaping bound vars. + let can_compute_impl_args = + matches!(data.kind, ty::AliasTermKind::InherentConstImpl { .. }) + || !data.self_ty().has_escaping_bound_vars(); + + if can_compute_impl_args { // FIXME(inherent_associated_types): Should this happen inside of a snapshot? // FIXME(inherent_associated_types): This is incompatible with the new solver and lazy norm! let args = traits::project::compute_inherent_assoc_term_args( @@ -1099,10 +1108,12 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { self.add_wf_preds_for_inherent_projection(alias_const.into()); return; // Subtree is handled by above function } - // please ping khyperia and/or BoxyUwU if this `bug!` fires - ty::AliasConstKind::InherentImpl { .. } => bug!( - "This ought to be unreachable, the entrypoints of WF should still have InherentSelf-form alias consts." - ), + // FIXME: This should be unreachable but isn't because we normalize in item + // wfck before computing wf requirements + ty::AliasConstKind::InherentImpl { .. } => { + self.add_wf_preds_for_inherent_projection(alias_const.into()); + return; + } ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => { diff --git a/compiler/rustc_type_ir/src/term_kind.rs b/compiler/rustc_type_ir/src/term_kind.rs index bb4ebf054d263..ed23b196fe269 100644 --- a/compiler/rustc_type_ir/src/term_kind.rs +++ b/compiler/rustc_type_ir/src/term_kind.rs @@ -1,3 +1,5 @@ +use std::debug_assert_matches; + use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash_NoContext}; @@ -265,11 +267,25 @@ impl AliasTerm { /// The following methods work only with (trait) associated term projections. // FIXME: Replace by an impl on Alias impl AliasTerm { + fn debug_assert_has_self(self) { + // InherentConstImpl is deliberately omitted here, it is not self-format args + debug_assert_matches!( + self.kind, + AliasTermKind::ProjectionTy { .. } + | AliasTermKind::ProjectionConst { .. } + | AliasTermKind::InherentTy { .. } + | AliasTermKind::InherentConstSelf { .. }, + "AliasTerm::self_ty is only valid on projection and inherent aliases" + ); + } + pub fn self_ty(self) -> I::Ty { + self.debug_assert_has_self(); self.args.type_at(0) } pub fn with_replaced_self_ty(self, interner: I, self_ty: I::Ty) -> Self { + self.debug_assert_has_self(); AliasTerm::new( interner, self.kind, diff --git a/library/core/src/cmp/clamp.rs b/library/core/src/cmp/clamp.rs index a737b59bbbe51..e42cd7fb39f29 100644 --- a/library/core/src/cmp/clamp.rs +++ b/library/core/src/cmp/clamp.rs @@ -66,6 +66,7 @@ macro impl_for_float($t:ty) { #[unstable(feature = "clamp_bounds", issue = "147781")] #[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")] const impl ClampBounds<$t> for RangeFrom<$t> { + #[inline] fn clamp(self, value: $t) -> $t { assert!(!self.start.is_nan(), "start was NaN"); value.max(self.start) @@ -75,6 +76,7 @@ macro impl_for_float($t:ty) { #[unstable(feature = "clamp_bounds", issue = "147781")] #[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")] const impl ClampBounds<$t> for RangeToInclusive<$t> { + #[inline] fn clamp(self, value: $t) -> $t { assert!(!self.end.is_nan(), "end was NaN"); value.min(self.end) @@ -88,6 +90,7 @@ macro impl_for_float($t:ty) { clippy::neg_cmp_op_on_partial_ord, reason = "NaN check is intentionally included in comparison" )] + #[inline] fn clamp(self, value: $t) -> $t { let (start, end) = self.into_inner(); assert!(start <= end, "start > end, or either was NaN"); diff --git a/library/core/src/num/complex.rs b/library/core/src/num/complex.rs index 9321718899cdd..73b904d81fc53 100644 --- a/library/core/src/num/complex.rs +++ b/library/core/src/num/complex.rs @@ -1,5 +1,7 @@ +use crate::ops::{Add, Sub}; + /// A complex number. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[unstable(feature = "complex_numbers", issue = "154023")] #[repr(C)] #[lang = "complex"] @@ -18,3 +20,39 @@ impl Complex { Complex { re, im } } } + +#[unstable(feature = "complex_numbers", issue = "154023")] +impl Add for Complex { + type Output = Complex; + + fn add(self, rhs: Self) -> Self::Output { + Complex::new(self.re + rhs.re, self.im + rhs.im) + } +} + +#[unstable(feature = "complex_numbers", issue = "154023")] +impl> Add for Complex { + type Output = Complex; + + fn add(self, rhs: T) -> Self::Output { + Complex::new(self.re + rhs, self.im) + } +} + +#[unstable(feature = "complex_numbers", issue = "154023")] +impl Sub for Complex { + type Output = Complex; + + fn sub(self, rhs: Self) -> Self::Output { + Complex::new(self.re - rhs.re, self.im - rhs.im) + } +} + +#[unstable(feature = "complex_numbers", issue = "154023")] +impl> Sub for Complex { + type Output = Complex; + + fn sub(self, rhs: T) -> Self::Output { + Complex::new(self.re - rhs, self.im) + } +} diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 816da7a2fb7f2..f9e2dc6b7f849 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -345,6 +345,8 @@ impl Duration { /// Creates a new `Duration` from the specified number of weeks. /// + /// For this method, one week is defined as 7 days, or 604,800 seconds. + /// /// # Panics /// /// Panics if the given number of weeks overflows the `Duration` size. @@ -373,6 +375,8 @@ impl Duration { /// Creates a new `Duration` from the specified number of days. /// + /// For this method, one day is defined as 24 hours, or 86,400 seconds. + /// /// # Panics /// /// Panics if the given number of days overflows the `Duration` size. @@ -401,6 +405,8 @@ impl Duration { /// Creates a new `Duration` from the specified number of hours. /// + /// For this method, one hour is defined as 60 minutes, or 3,600 seconds. + /// /// # Panics /// /// Panics if the given number of hours overflows the `Duration` size. @@ -429,6 +435,8 @@ impl Duration { /// Creates a new `Duration` from the specified number of minutes. /// + /// For this method, one minute is defined as 60 seconds. + /// /// # Panics /// /// Panics if the given number of minutes overflows the `Duration` size. diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 142df37c2b7fe..4cee09495de8c 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -19,6 +19,7 @@ #![feature(clone_to_uninit)] #![feature(cmp_minmax)] #![feature(cmp_splat)] +#![feature(complex_numbers)] #![feature(const_array)] #![feature(const_bool)] #![feature(const_cell_traits)] diff --git a/library/coretests/tests/num/complex.rs b/library/coretests/tests/num/complex.rs new file mode 100644 index 0000000000000..4260c0a9a27a9 --- /dev/null +++ b/library/coretests/tests/num/complex.rs @@ -0,0 +1,44 @@ +use core::num::{Complex, Wrapping}; + +#[test] +fn complex_addition() { + let a = Complex::new(1, 2); + let b = Complex::new(3, 4); + assert_eq!(a + b, Complex::new(a.re + b.re, a.im + b.im)); + assert_eq!(a + b, b + a); + assert_eq!(a + 8, Complex::new(a.re + 8, a.im)); + + let a = Complex::new(Wrapping(1u8), Wrapping(2)); + let b = Complex::new(Wrapping(3u8), Wrapping(4)); + assert_eq!(a + b, Complex::new(a.re + b.re, a.im + b.im)); + assert_eq!(a + b, b + a); + let c = a + Wrapping(u8::MAX); + assert_eq!(c, Complex::new(a.re + Wrapping(u8::MAX), a.im)); + assert_eq!(c.re.0, 1u8.wrapping_add(u8::MAX)); + + let a = Complex::new(1.0, 2.0); + let b = Complex::new(3.0, 4.0); + assert_eq!(a + b, Complex::new(a.re + b.re, a.im + b.im)); + assert_eq!(a + b, b + a); + assert_eq!(a + 8.0, Complex::new(a.re + 8.0, a.im)); +} + +#[test] +fn complex_subtraction() { + let a = Complex::new(1, 2); + let b = Complex::new(3, 4); + assert_eq!(a - b, Complex::new(a.re - b.re, a.im - b.im)); + assert_eq!(a - 8, Complex::new(a.re - 8, a.im)); + + let a = Complex::new(Wrapping(1u8), Wrapping(2)); + let b = Complex::new(Wrapping(3u8), Wrapping(4)); + assert_eq!(a - b, Complex::new(a.re - b.re, a.im - b.im)); + let c = a - Wrapping(u8::MAX); + assert_eq!(c, Complex::new(a.re - Wrapping(u8::MAX), a.im)); + assert_eq!(c.re.0, 1u8.wrapping_sub(u8::MAX)); + + let a = Complex::new(1.0, 2.0); + let b = Complex::new(3.0, 4.0); + assert_eq!(a - b, Complex::new(a.re - b.re, a.im - b.im)); + assert_eq!(a - 8.0, Complex::new(a.re - 8.0, a.im)); +} diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index 0e003e5a9ec27..b1c3001790f07 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -24,6 +24,7 @@ mod u8; mod bignum; mod carryless_mul; mod cast; +mod complex; mod const_from; mod dec2flt; mod float_conversions; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index c2007433ff5ea..817d208a8a620 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -390,6 +390,7 @@ #![feature(str_internals)] #![feature(sync_unsafe_cell)] #![feature(temporary_niche_types)] +#![feature(trim_prefix_suffix)] #![feature(ub_checks)] #![feature(uint_carryless_mul)] #![feature(unsafe_pinned)] diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index c99524375113a..288331d4db69a 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -6,6 +6,7 @@ use crate::ffi::{OsStr, OsString, c_void}; use crate::fs::TryLockError; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem::{self, MaybeUninit, offset_of}; +use crate::os::windows::ffi::{OsStrExt, OsStringExt}; use crate::os::windows::io::{AsHandle, BorrowedHandle}; use crate::os::windows::prelude::*; use crate::path::{Path, PathBuf}; @@ -18,6 +19,9 @@ use crate::sys::time::SystemTime; use crate::sys::{Align8, AsInner, FromInner, IntoInner, c, cvt}; use crate::{fmt, ptr, slice}; +#[cfg(test)] +mod tests; + mod dir; pub use dir::Dir; mod remove_dir_all; @@ -1591,14 +1595,77 @@ pub fn set_times_nofollow(p: &WCStr, times: FileTimes) -> io::Result<()> { } fn get_path(f: impl AsRawHandle) -> io::Result { + let h = f.as_raw_handle(); + // If getting the canonical path fails with ERROR_INVALID_FUNCTION + // then it's likely it failed to resolve the path's drive. + // In that case, use the fallback method to resolve it. + let invalid_function = Some(c::ERROR_INVALID_FUNCTION as i32); + match get_path_canonical(h) { + Err(e) if e.raw_os_error() == invalid_function => get_path_fallback(h).ok_or(e), + result => result, + } +} + +fn get_path_canonical(handle: c::HANDLE) -> io::Result { fill_utf16_buf( - |buf, sz| unsafe { - c::GetFinalPathNameByHandleW(f.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS) - }, + |buf, sz| unsafe { c::GetFinalPathNameByHandleW(handle, buf, sz, c::VOLUME_NAME_DOS) }, |buf| PathBuf::from(OsString::from_wide(buf)), ) } +/// Fallback in case `get_path_canonical` fails. +/// +/// `get_path_canonical` can fail if the Win32 drive name cannot be resolved. +/// This can happen with certain third party drivers that don't integrate +/// with the mount manager. +/// +/// Instead we manually do the same job by getting the NT path +/// and then finding the first drive letter that points to a prefix of +/// that path. From there we can construct a Win32 path. +/// +/// It's implemented by first getting the NT path, which should always succeed. +/// Then we use [`GetLogicalDrives`] to get a bit array of win32 drive letters +/// from 'A' to 'Z'. If the corresponding bit is set then it means that drive exists. +/// E.g. bit 2 being set means there's a `C:` drive. +/// +/// Then for each drive we use [`QueryDosDeviceW`] to see the NT path that drive resolves to. +/// If that path is a prefix to the path we got initially then we treat that as the canonical drive letter. +/// So in the unlikely even two drives point to the same device, the lowest one is considered canonical. +/// +/// [`GetLogicalDrives`]: https://learn.microsoft.com/windows/win32/api/fileapi/nf-fileapi-getlogicaldrives +/// [`QueryDosDeviceW`]: https://learn.microsoft.com/windows/win32/api/fileapi/nf-fileapi-querydosdevicew +fn get_path_fallback(handle: c::HANDLE) -> Option { + fill_utf16_buf( + |buf, sz| unsafe { c::GetFinalPathNameByHandleW(handle, buf, sz, c::VOLUME_NAME_NT) }, + |nt_path| { + let mut buf = [0_u16; c::MAX_PATH as usize]; + for letter in api::get_logical_drives() { + let device_name = [letter as u16, b':' as u16, 0]; + // SAFETY: `device_name` is a null terminated u16 string + if let Some(drive_path) = unsafe { api::query_dos_device(&device_name, &mut buf) } { + if let Some(nt_path) = nt_path.strip_prefix(drive_path) { + // Reserve approximately enough space for the drive + path. + let mut path = Vec::with_capacity(r"\\?\C:".len() + nt_path.len()); + // Create a verbatim drive root (e.g. \\?\D:) + let mut verbatim_root = *br#"\\?\C:"#; + verbatim_root[4] = letter; + path.extend_from_slice(&verbatim_root); + path.extend(OsString::from_wide(nt_path).into_encoded_bytes()); + // SAFETY: All characters are either in the ASCII range (the prefix) + // or else came from an OsString. + unsafe { + return Some(OsString::from_encoded_bytes_unchecked(path).into()); + } + } + } + } + None + }, + ) + .ok() + .flatten() +} + pub fn canonicalize(p: &WCStr) -> io::Result { let mut opts = OpenOptions::new(); // No read or write permissions are necessary diff --git a/library/std/src/sys/fs/windows/tests.rs b/library/std/src/sys/fs/windows/tests.rs new file mode 100644 index 0000000000000..b53dfddecf627 --- /dev/null +++ b/library/std/src/sys/fs/windows/tests.rs @@ -0,0 +1,21 @@ +use super::{get_path_canonical, get_path_fallback}; +use crate::env; +use crate::fs::{File, canonicalize}; +use crate::os::windows::io::AsRawHandle; +use crate::test_helpers::tmpdir; + +#[test] +/// Test that `get_path_canonical` and `get_path_fallback` return the exact same path. +fn canonicalize_fallback() { + let t = tmpdir(); + let fname = t.join("hello.txt"); + // This test may break if run in an environment that requires the fallback. + // So skip it if not in CI. + if env::var_os("CI").is_none() && canonicalize(&fname).is_err() { + return; + } + let f = File::create(fname).unwrap(); + let canonical = get_path_canonical(f.as_raw_handle()).unwrap(); + let fallback = get_path_fallback(f.as_raw_handle()).unwrap(); + assert_eq!(canonical, fallback); +} diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs index 25a6c2d7d8eda..c3494bf9aa4e6 100644 --- a/library/std/src/sys/pal/windows/api.rs +++ b/library/std/src/sys/pal/windows/api.rs @@ -364,3 +364,42 @@ pub macro unicode_str { ) } } + +/// Returns a list of enabled drive letters. +/// +/// This is a wrapper around [`GetLogicalDrives`]. +/// Each letter is returned as an ascii byte. +/// +/// [`GetLogicalDrives`]: (https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getlogicaldrives) +pub fn get_logical_drives() -> impl Iterator { + // SAFETY: `GetLogicalDrives` only returns information. + let drives = unsafe { c::GetLogicalDrives() }; + (b'A'..=b'Z').filter(move |letter| drives >> (letter - b'A') & 1 == 1) +} + +/// Get the NT path a device name points to. +/// +/// # Safety +/// +/// `device_name` must be null-terminated. +// FIXME: Use a null-terminated wide string type to assert validity, similar to CStr. +// Then this function can be safe. +pub unsafe fn query_dos_device<'a>( + device_name: &[u16], + buffer: &'a mut [u16], +) -> Option<&'a [u16]> { + let device_ptr = device_name.as_ptr(); + let buffer_ptr = buffer.as_mut_ptr(); + let buffer_len = buffer.len().try_into().ok()?; + // SAFETY: `device_ptr` points to a null-terminated u16 string. + // `buffer_ptr` is writeable up to buffer_len u16s. + let result = unsafe { c::QueryDosDeviceW(device_ptr, buffer_ptr, buffer_len) } as usize; + if result > 0 { + // QueryDosDeviceW returns a list of null-terminated strings where the list itself is also null-terminated + // In the case where you pass a device name (which we always) it only returns one string. + // Therefore to get the string we trim off both the list null termination and the string null termination. + Some(buffer[..result].trim_suffix(&[0, 0])) + } else { + None + } +} diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index a0b2126af9a58..c4c4ac8c7d06e 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2181,6 +2181,7 @@ GETFINALPATHNAMEBYHANDLE_FLAGS GetFinalPathNameByHandleW GetFullPathNameW GetLastError +GetLogicalDrives GetModuleFileNameW GetModuleHandleA GetModuleHandleExW @@ -2354,6 +2355,7 @@ PROFILE_KERNEL PROFILE_SERVER PROFILE_USER PROGRESS_CONTINUE +QueryDosDeviceW QueryPerformanceCounter QueryPerformanceFrequency READ_CONTROL @@ -2508,6 +2510,7 @@ UpdateProcThreadAttribute VOLUME_NAME_DOS VOLUME_NAME_GUID VOLUME_NAME_NONE +VOLUME_NAME_NT WAIT_ABANDONED WAIT_ABANDONED_0 WAIT_FAILED 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 9c6f593e1e108..c3c5e193e41f1 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -54,6 +54,7 @@ windows_link::link!("kernel32.dll" "system" fn GetFileType(hfile : HANDLE) -> FI windows_link::link!("kernel32.dll" "system" fn GetFinalPathNameByHandleW(hfile : HANDLE, lpszfilepath : PWSTR, cchfilepath : u32, dwflags : GETFINALPATHNAMEBYHANDLE_FLAGS) -> u32); windows_link::link!("kernel32.dll" "system" fn GetFullPathNameW(lpfilename : PCWSTR, nbufferlength : u32, lpbuffer : PWSTR, lpfilepart : *mut PWSTR) -> u32); windows_link::link!("kernel32.dll" "system" fn GetLastError() -> WIN32_ERROR); +windows_link::link!("kernel32.dll" "system" fn GetLogicalDrives() -> u32); windows_link::link!("kernel32.dll" "system" fn GetModuleFileNameW(hmodule : HMODULE, lpfilename : PWSTR, nsize : u32) -> u32); windows_link::link!("kernel32.dll" "system" fn GetModuleHandleA(lpmodulename : PCSTR) -> HMODULE); windows_link::link!("kernel32.dll" "system" fn GetModuleHandleExW(dwflags : u32, lpmodulename : PCWSTR, phmodule : *mut HMODULE) -> BOOL); @@ -83,6 +84,7 @@ windows_link::link!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, even windows_link::link!("ntdll.dll" "system" fn NtSetInformationFile(filehandle : HANDLE, iostatusblock : *mut IO_STATUS_BLOCK, fileinformation : *const core::ffi::c_void, length : u32, fileinformationclass : FILE_INFORMATION_CLASS) -> NTSTATUS); windows_link::link!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); windows_link::link!("advapi32.dll" "system" fn OpenProcessToken(processhandle : HANDLE, desiredaccess : TOKEN_ACCESS_MASK, tokenhandle : *mut HANDLE) -> BOOL); +windows_link::link!("kernel32.dll" "system" fn QueryDosDeviceW(lpdevicename : PCWSTR, lptargetpath : PWSTR, ucchmax : u32) -> u32); windows_link::link!("kernel32.dll" "system" fn QueryPerformanceCounter(lpperformancecount : *mut i64) -> BOOL); windows_link::link!("kernel32.dll" "system" fn QueryPerformanceFrequency(lpfrequency : *mut i64) -> BOOL); windows_link::link!("kernel32.dll" "system" fn ReadConsoleW(hconsoleinput : HANDLE, lpbuffer : *mut core::ffi::c_void, nnumberofcharstoread : u32, lpnumberofcharsread : *mut u32, pinputcontrol : *const CONSOLE_READCONSOLE_CONTROL) -> BOOL); @@ -3411,6 +3413,7 @@ impl Default for UNICODE_STRING { pub const VOLUME_NAME_DOS: GETFINALPATHNAMEBYHANDLE_FLAGS = 0u32; pub const VOLUME_NAME_GUID: GETFINALPATHNAMEBYHANDLE_FLAGS = 1u32; pub const VOLUME_NAME_NONE: GETFINALPATHNAMEBYHANDLE_FLAGS = 4u32; +pub const VOLUME_NAME_NT: GETFINALPATHNAMEBYHANDLE_FLAGS = 2u32; pub const WAIT_ABANDONED: WAIT_EVENT = 128u32; pub const WAIT_ABANDONED_0: WAIT_EVENT = 128u32; pub type WAIT_EVENT = u32; diff --git a/src/bootstrap/src/core/build_steps/synthetic_targets.rs b/src/bootstrap/src/core/build_steps/synthetic_targets.rs index 2b5039214f62c..2c35b39287d70 100644 --- a/src/bootstrap/src/core/build_steps/synthetic_targets.rs +++ b/src/bootstrap/src/core/build_steps/synthetic_targets.rs @@ -11,18 +11,40 @@ use crate::core::builder::{Builder, Step}; use crate::core::compiler::Compiler; use crate::core::config::TargetSelection; +/// Note that this currently only contains panic strategies that we somehow use in bootstrap, not +/// all possible strategires supported by rustc. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) enum PanicStrategy { + Unwind, + Abort, +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) struct MirOptPanicAbortSyntheticTarget { +pub(crate) struct SyntheticTargetWithPanicStrategy { pub(crate) compiler: Compiler, pub(crate) base: TargetSelection, + pub(crate) strategy: PanicStrategy, +} + +impl SyntheticTargetWithPanicStrategy { + pub(crate) fn panic_abort(compiler: Compiler, base: TargetSelection) -> Self { + Self { compiler, base, strategy: PanicStrategy::Abort } + } + pub(crate) fn panic_unwind(compiler: Compiler, base: TargetSelection) -> Self { + Self { compiler, base, strategy: PanicStrategy::Unwind } + } } -impl Step for MirOptPanicAbortSyntheticTarget { +impl Step for SyntheticTargetWithPanicStrategy { type Output = TargetSelection; fn run(self, builder: &Builder<'_>) -> Self::Output { + let strategy = match self.strategy { + PanicStrategy::Unwind => "unwind", + PanicStrategy::Abort => "abort", + }; create_synthetic_target(builder, self.compiler, "miropt-abort", self.base, |spec| { - spec.insert("panic-strategy".into(), "abort".into()); + spec.insert("panic-strategy".into(), strategy.into()); }) } } @@ -49,16 +71,7 @@ fn create_synthetic_target( return TargetSelection::create_synthetic(&name, path.to_str().unwrap()); } - let mut cmd = builder.rustc_cmd(compiler); - cmd.arg("--target").arg(base.rustc_target_arg()); - cmd.args(["-Zunstable-options", "--print", "target-spec-json"]); - - // If `rust.channel` is set to either beta or stable, rustc will complain that - // we cannot use nightly features. So `RUSTC_BOOTSTRAP` is needed here. - cmd.env("RUSTC_BOOTSTRAP", "1"); - - let output = cmd.run_capture(builder).stdout(); - let mut spec: serde_json::Value = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut spec = get_target_specs(builder, compiler, base); let spec_map = spec.as_object_mut().unwrap(); // The `is-builtin` attribute of a spec needs to be removed, otherwise rustc will complain. @@ -69,3 +82,23 @@ fn create_synthetic_target( std::fs::write(&path, serde_json::to_vec_pretty(&spec).unwrap()).unwrap(); TargetSelection::create_synthetic(&name, path.to_str().unwrap()) } + +/// Get the JSON target specs from the given compiler. +/// Note that the set of targets will differ between the stage0 and stage1+ (in-tree) compiler! +pub fn get_target_specs( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, +) -> serde_json::Value { + let mut cmd = builder.rustc_cmd(compiler); + cmd.arg("--target").arg(target.rustc_target_arg()); + cmd.args(["-Zunstable-options", "--print", "target-spec-json"]); + + // If `rust.channel` is set to either beta or stable, rustc will complain that + // we cannot use nightly features. So `RUSTC_BOOTSTRAP` is needed here. + cmd.env("RUSTC_BOOTSTRAP", "1"); + + let output = cmd.cached().run_capture(builder).stdout(); + let spec: serde_json::Value = serde_json::from_slice(output.as_bytes()).unwrap(); + spec +} diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 47318d3c086f5..aba8d52959c88 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -22,7 +22,9 @@ use crate::core::build_steps::format::InternalRustfmt; use crate::core::build_steps::gcc::{Gcc, GccTargetPair, add_cg_gcc_cargo_flags}; use crate::core::build_steps::llvm::get_llvm_version; use crate::core::build_steps::run::{get_completion_paths, get_help_path}; -use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget; +use crate::core::build_steps::synthetic_targets::{ + PanicStrategy, SyntheticTargetWithPanicStrategy, get_target_specs, +}; use crate::core::build_steps::test::compiletest::CompiletestMode; use crate::core::build_steps::test::failed_tests::{RecordFailedTests, SetupFailedTestsFile}; use crate::core::build_steps::tool::{ @@ -2168,8 +2170,8 @@ test!(CoverageRunRustdoc { // For the mir-opt suite we do not use macros, as we need custom behavior when blessing. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct MirOpt { - pub compiler: Compiler, - pub target: TargetSelection, + compiler: Compiler, + target: TargetSelection, } impl CommandLineStep for MirOpt { @@ -2185,45 +2187,110 @@ impl CommandLineStep for MirOpt { fn make_run(run: RunConfig<'_>) { let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - run.builder.ensure(MirOpt { compiler, target: run.target }); - } - fn run(self, builder: &Builder<'_>) { - let run = |target| { - builder.ensure(Compiletest { - test_compiler: self.compiler, - target, - mode: CompiletestMode::MirOpt, - suite: "mir-opt", - path: "tests/mir-opt", - compare_mode: None, - }) + // The mir-opt tests check four distinct configurations, the cross-product of the + // following two axes: + // - Bit-width: 32-bit and 64-bit + // - Panic strategy: unwind and abort + + // Return the bitwidth and panic strategy of the default (usually host) target + let get_bitwidth_and_panic_strategy = || -> (u64, PanicStrategy) { + if run.builder.config.dry_run() { + return (64, PanicStrategy::Unwind); + } + + let specs = get_target_specs(run.builder, compiler, run.target); + let specs = specs.as_object(); + let bitwidth = specs + .and_then(|obj| obj.get("target-pointer-width")) + .and_then(|v| v.as_i64()) + .map(|v| v as u64) + .unwrap_or(64); + let panic_strategy = specs + .and_then(|obj| obj.get("panic-strategy")) + .and_then(|v| v.as_str()) + .map(|v| match v { + "unwind" => PanicStrategy::Unwind, + _ => PanicStrategy::Abort, + }) + // The default panic strategy is unwind + .unwrap_or(PanicStrategy::Unwind); + (bitwidth, panic_strategy) }; - run(self.target); + // Here we generate several configurations of this step to evaluate multiple targets. + let targets = if run.builder.config.cmd.bless() { + // When blessing, we generate a fixed set of 4 targets that cover all the + // possible combinations. This selection covers all our tier 1 operating systems and + // architectures using only tier 1 targets. - // Run more targets with `--bless`. But we always run the host target first, since some - // tests use very specific `only` clauses that are not covered by the target set below. - if builder.config.cmd.bless() { - // All that we really need to do is cover all combinations of 32/64-bit and unwind/abort, - // but while we're at it we might as well flex our cross-compilation support. This - // selection covers all our tier 1 operating systems and architectures using only tier - // 1 targets. + // We also include the host target, since some tests use very specific `only` clauses + // that are not covered by the target set below. + + let (bitwidth, strategy) = get_bitwidth_and_panic_strategy(); + let mut targets = vec![(bitwidth, strategy, run.target)]; - for target in ["aarch64-unknown-linux-gnu", "i686-pc-windows-msvc"] { - run(TargetSelection::from_user(target)); + // 64-bit and 32-bit panic=unwind + for (bitwidth, target) in + [(64, "aarch64-unknown-linux-gnu"), (32, "i686-pc-windows-msvc")] + { + targets.push((bitwidth, PanicStrategy::Unwind, TargetSelection::from_user(target))); } - for target in ["x86_64-apple-darwin", "i686-unknown-linux-musl"] { + // 64-bit and 32-bit panic=abort + for (bitwidth, target) in [(64, "x86_64-apple-darwin"), (32, "i686-unknown-linux-musl")] + { let target = TargetSelection::from_user(target); - let panic_abort_target = builder.ensure(MirOptPanicAbortSyntheticTarget { - compiler: self.compiler, - base: target, - }); - run(panic_abort_target); + let panic_abort_target = run + .builder + .ensure(SyntheticTargetWithPanicStrategy::panic_abort(compiler, target)); + targets.push((bitwidth, PanicStrategy::Abort, panic_abort_target)); } + // This is a small optimization for local blessing. + // If we figure out that the host target already has a given bitwidth/panic strategy + // combination, we do not add the fixed targets to the list. + let mut unique = HashSet::new(); + targets.retain(|(bitwidth, strategy, _)| unique.insert((*bitwidth, *strategy))); + + targets.into_iter().map(|(_, _, target)| target).collect() + } else { + // When not blessing, we could also test all four configurations. But that would make + // local tests quite slow. So instead, we check the current target, and then the + // current target with switched panic strategy. + // On CI, we should be running this test for both 32-bit and 64-bit targets, so together + // this should check all possible configurations on CI. + + // The complicated thing here is how to figure out the panic strategy of the current + // target. In theory, we could just assume that in most situations, the target is + // panic=unwind, and force generation of panic=abort. But to ensure that we do this + // properly, we actually query the compiler to figure out the panic strategy, and then + // generate a synthetic target with the opposite strategy. + let panic_strategy = get_bitwidth_and_panic_strategy().1; + let synthetic_target = if panic_strategy == PanicStrategy::Unwind { + run.builder + .ensure(SyntheticTargetWithPanicStrategy::panic_abort(compiler, run.target)) + } else { + run.builder + .ensure(SyntheticTargetWithPanicStrategy::panic_unwind(compiler, run.target)) + }; + vec![run.target, synthetic_target] + }; + + for target in targets { + run.builder.ensure(MirOpt { compiler, target }); } } + + fn run(self, builder: &Builder<'_>) { + builder.ensure(Compiletest { + test_compiler: self.compiler, + target: self.target, + mode: CompiletestMode::MirOpt, + suite: "mir-opt", + path: "tests/mir-opt", + compare_mode: None, + }); + } } /// Executes the `compiletest` tool to run a suite of tests. diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index b04f61eafee32..5b63dc4a0f7ae 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -178,7 +178,11 @@ impl Cargo { // No need to configure the target linker for these command types. Kind::Clean | Kind::Check | Kind::Format | Kind::Setup => {} _ => { - cargo.configure_linker(builder); + // Do not configure the linker for synthetic targets, as we won't have cc set up + // for them. + if !target.is_synthetic() { + cargo.configure_linker(builder); + } } } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index f5ab543e87d2b..79791819b0d28 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1941,6 +1941,8 @@ mod snapshot { [test] compiletest-coverage 1 [build] rustc 1 -> std 1 [test] compiletest-mir-opt 1 + [build] rustc 1 -> std 1 + [test] compiletest-mir-opt 1 [test] compiletest-codegen-llvm 1 [test] compiletest-codegen-units 1 [test] compiletest-assembly-llvm 1 @@ -2122,6 +2124,9 @@ mod snapshot { [test] compiletest-coverage 2 [build] rustc 2 -> std 2 [test] compiletest-mir-opt 2 + [build] rustc 1 -> std 1 + [build] rustc 2 -> std 2 + [test] compiletest-mir-opt 2 [test] compiletest-codegen-llvm 2 [test] compiletest-codegen-units 2 [test] compiletest-assembly-llvm 2 @@ -2389,6 +2394,52 @@ mod snapshot { "); } + #[test] + fn test_mir_opt() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + prepare_test_config(&ctx) + .path("tests/mir-opt") + .render_steps(), @" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 0 -> Compiletest 1 + [test] compiletest-mir-opt 1 + [build] rustc 1 -> std 1 + [test] compiletest-mir-opt 1 + "); + } + + #[test] + fn test_mir_opt_bless() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + prepare_test_config(&ctx) + .arg("--bless") + .hosts(&[TEST_TRIPLE_1]) + .arg("--build") + .arg(TEST_TRIPLE_1) + .targets(&[TEST_TRIPLE_1]) + .path("tests/mir-opt") + .get_steps() + .render_with(RenderConfig { + normalize_host: false + }), @" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 0 -> Compiletest 1 + [test] compiletest-mir-opt 1 + [build] rustc 1 -> std 1 + [test] compiletest-mir-opt 1 + [build] rustc 1 -> std 1 + [test] compiletest-mir-opt 1 + [build] rustc 1 -> std 1 + [test] compiletest-mir-opt 1 + "); + } + #[test] fn doc_all() { let ctx = TestCtx::new(); @@ -3184,7 +3235,7 @@ fn render_metadata(metadata: &StepMetadata, config: &RenderConfig) -> String { } fn normalize_target(target: TargetSelection, config: &RenderConfig) -> String { - let mut target = target.to_string(); + let mut target = target.triple.to_string(); if config.normalize_host { target = target.replace(&host_target(), "host"); } diff --git a/src/tools/generate-windows-sys/src/main.rs b/src/tools/generate-windows-sys/src/main.rs index 9b1d62f14bb7b..e51340af9a95c 100644 --- a/src/tools/generate-windows-sys/src/main.rs +++ b/src/tools/generate-windows-sys/src/main.rs @@ -33,7 +33,7 @@ fn main() -> Result<(), Box> { let mut f = std::fs::File::options().append(true).open("windows_sys.rs")?; f.write_all(ARM32_SHIM.as_bytes())?; - writeln!(&mut f, "// ignore-tidy-filelength")?; + writeln!(&mut f, "// ignore-tidy-file-filelength")?; Ok(()) } diff --git a/tests/codegen-llvm/intrinsics/unchecked_math.rs b/tests/codegen-llvm/intrinsics/unchecked_math.rs index 419c120ede9ec..7f63ef99c4e12 100644 --- a/tests/codegen-llvm/intrinsics/unchecked_math.rs +++ b/tests/codegen-llvm/intrinsics/unchecked_math.rs @@ -1,3 +1,4 @@ +//@ compile-flags: -Z merge-functions=disabled #![crate_type = "lib"] #![feature(core_intrinsics)] diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.rs b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.rs index 5534a45f19d64..8593a322ad363 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.rs +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.rs @@ -1,4 +1,6 @@ //@ test-mir-pass: GVN +// layout randomization affects the alloc output +//@ needs-deterministic-layouts //@ compile-flags: -Zinline-mir --crate-type lib // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/issues/issue_154166.rs b/tests/mir-opt/issues/issue_154166.rs new file mode 100644 index 0000000000000..e65c59ea8fca4 --- /dev/null +++ b/tests/mir-opt/issues/issue_154166.rs @@ -0,0 +1,20 @@ +// Check that closure debug implementation correctly displays all captures precisely. + +//@ revisions: e2018 e2021 +//@[e2018] edition: 2018 +//@[e2021] edition: 2021 + +#![crate_type = "lib"] + +pub fn foo(x: (String, String)) { + // CHECK-LABEL: foo( + // e2018: {closure{{.*}}issue_154166{{.*}}} { x: {{.*}} }; + // e2021: {closure{{.*}}issue_154166{{.*}}} { x__0: {{.*}}, x__1: {{.*}} }; + let _closure = || { + if std::hint::black_box(true) { + let _a = &x.1; + } else { + let _b = x.0; + } + }; +} diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-abort.mir index 549af7af4d888..b42087b5c822d 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-abort.mir @@ -21,7 +21,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug x => _34; } scope 18 (inlined > as Iterator>::next) { - let mut _22: std::option::Option; + let mut _22: std::option::Option; let mut _27: std::option::Option<&T>; let mut _30: (usize, bool); let mut _31: (usize, &T); @@ -32,7 +32,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } scope 20 { scope 21 { - scope 27 (inlined as FromResidual>>::from_residual) { + scope 27 (inlined as FromResidual>>::from_residual) { let mut _21: isize; let mut _23: bool; } diff --git a/tests/ui/const-generics/gca/wf-inherentimpl.old.stderr b/tests/ui/const-generics/gca/wf-inherentimpl.old.stderr new file mode 100644 index 0000000000000..0766847a93b18 --- /dev/null +++ b/tests/ui/const-generics/gca/wf-inherentimpl.old.stderr @@ -0,0 +1,10 @@ +error: `generic_const_args` requires -Znext-solver=globally to be enabled + --> $DIR/wf-inherentimpl.rs:7:12 + | +LL | #![feature(generic_const_args, min_generic_const_args)] + | ^^^^^^^^^^^^^^^^^^ + | + = help: enable all of these features + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/gca/wf-inherentimpl.rs b/tests/ui/const-generics/gca/wf-inherentimpl.rs new file mode 100644 index 0000000000000..cb3df20daa2dc --- /dev/null +++ b/tests/ui/const-generics/gca/wf-inherentimpl.rs @@ -0,0 +1,16 @@ +//@[next] check-pass +//@ revisions: next old +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +#![feature(inherent_associated_types)] +#![feature(macroless_generic_const_args)] +#![feature(generic_const_args, min_generic_const_args)] +//[old]~^ ERROR `generic_const_args` requires -Znext-solver=globally to be enabled +struct Foo; +impl Foo { + const SIZE: usize = { todo!() }; + fn to_bytes() -> [u8; Self::SIZE] { + todo!() + } +} +fn main() {} diff --git a/tests/ui/target-feature/abi-required-target-feature-missing-in-target-cpu.arm.stderr b/tests/ui/target-feature/abi-required-target-feature-missing-in-target-cpu.arm.stderr index 52862ec5151b3..73b3e5bacd906 100644 --- a/tests/ui/target-feature/abi-required-target-feature-missing-in-target-cpu.arm.stderr +++ b/tests/ui/target-feature/abi-required-target-feature-missing-in-target-cpu.arm.stderr @@ -1,7 +1,4 @@ -warning: target feature `fpregs` must be enabled to ensure that the ABI of the current target can be implemented correctly - | - = note: 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 #116344 +error: target feature `fpregs` must be enabled to ensure that the ABI of the current target can be implemented correctly -warning: 1 warning emitted +error: aborting due to 1 previous error diff --git a/tests/ui/target-feature/abi-required-target-feature-missing-in-target-cpu.rs b/tests/ui/target-feature/abi-required-target-feature-missing-in-target-cpu.rs index e652f321ad33b..59d0ba1a0ba4f 100644 --- a/tests/ui/target-feature/abi-required-target-feature-missing-in-target-cpu.rs +++ b/tests/ui/target-feature/abi-required-target-feature-missing-in-target-cpu.rs @@ -9,14 +9,14 @@ //@[arm] compile-flags: --target=armv8r-none-eabihf -Ctarget-cpu=cortex-r4 //@[arm] needs-llvm-components: arm -// LLVM 24 refuses to compile ARM minicore due to mismatched target features. -// FIXME(#161276): With LLVM rejecting this, we should make Rust's own warning an error. -//@[arm] max-llvm-major-version: 23 +// On x86 this is just a warning. +//@[x86] check-pass +//@[arm] check-fail -// For now this is just a warning. -//@ build-pass //@ ignore-backends: gcc //@ add-minicore +// Don't inherit the target-cpu above for minicore, to avoid errors when building that. +//@ minicore-compile-flags: -Ctarget-cpu=generic #![feature(no_core)] #![no_core] @@ -24,4 +24,5 @@ extern crate minicore; use minicore::*; -//~? WARN must be enabled to ensure that the ABI of the current target can be implemented correctly +//[x86]~? WARN must be enabled to ensure that the ABI of the current target can be implemented correctly +//[arm]~? ERROR must be enabled to ensure that the ABI of the current target can be implemented correctly