diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index f8c69d46d1f..6937e658ed5 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -58,6 +58,7 @@ pub(crate) use llvm::codegen_llvm_intrinsic_call; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::SubstsRef; use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_target::abi::InitKind; use crate::prelude::*; use cranelift_codegen::ir::AtomicRmwOp; @@ -671,7 +672,12 @@ fn codegen_regular_intrinsic_call<'tcx>( return; } - if intrinsic == sym::assert_zero_valid && !layout.might_permit_raw_init(fx, /*zero:*/ true) { + if intrinsic == sym::assert_zero_valid + && !layout.might_permit_raw_init( + fx, + InitKind::Zero, + fx.tcx.sess.opts.debugging_opts.strict_init_checks) { + with_no_trimmed_paths!({ crate::base::codegen_panic( fx, @@ -682,7 +688,12 @@ fn codegen_regular_intrinsic_call<'tcx>( return; } - if intrinsic == sym::assert_uninit_valid && !layout.might_permit_raw_init(fx, /*zero:*/ false) { + if intrinsic == sym::assert_uninit_valid + && !layout.might_permit_raw_init( + fx, + InitKind::Uninit, + fx.tcx.sess.opts.debugging_opts.strict_init_checks) { + with_no_trimmed_paths!({ crate::base::codegen_panic( fx, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index f1007ba1578..03ef6d50d44 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -22,7 +22,7 @@ use rustc_span::source_map::Span; use rustc_span::{sym, Symbol}; use rustc_symbol_mangling::typeid_for_fnabi; use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; -use rustc_target::abi::{self, HasDataLayout, WrappingRange}; +use rustc_target::abi::{self, HasDataLayout, InitKind, WrappingRange}; use rustc_target::spec::abi::Abi; /// Used by `FunctionCx::codegen_terminator` for emitting common patterns @@ -521,6 +521,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { source_info: mir::SourceInfo, target: Option, cleanup: Option, + strict_validity: bool, ) -> bool { // Emit a panic or a no-op for `assert_*` intrinsics. // These are intrinsics that compile to panics so that we can get a message @@ -543,8 +544,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let layout = bx.layout_of(ty); let do_panic = match intrinsic { Inhabited => layout.abi.is_uninhabited(), - ZeroValid => !layout.might_permit_raw_init(bx, /*zero:*/ true), - UninitValid => !layout.might_permit_raw_init(bx, /*zero:*/ false), + ZeroValid => !layout.might_permit_raw_init(bx, InitKind::Zero, strict_validity), + UninitValid => !layout.might_permit_raw_init(bx, InitKind::Uninit, strict_validity), }; if do_panic { let msg_str = with_no_visible_paths!({ @@ -678,6 +679,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { source_info, target, cleanup, + self.cx.tcx().sess.opts.debugging_opts.strict_init_checks, ) { return; } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index b747be3a636..bf1cf816ddd 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -15,7 +15,7 @@ use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; -use rustc_target::abi::{Abi, Align, Primitive, Size}; +use rustc_target::abi::{Abi, Align, InitKind, Primitive, Size}; use super::{ util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy, @@ -408,7 +408,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { )?; } if intrinsic_name == sym::assert_zero_valid - && !layout.might_permit_raw_init(self, /*zero:*/ true) + && !layout.might_permit_raw_init( + self, + InitKind::Zero, + self.tcx.sess.opts.debugging_opts.strict_init_checks, + ) { M::abort( self, @@ -419,7 +423,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { )?; } if intrinsic_name == sym::assert_uninit_valid - && !layout.might_permit_raw_init(self, /*zero:*/ false) + && !layout.might_permit_raw_init( + self, + InitKind::Uninit, + self.tcx.sess.opts.debugging_opts.strict_init_checks, + ) { M::abort( self, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 12e00ef5114..66198dff2ae 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1495,6 +1495,8 @@ options! { "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED], "control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"), + strict_init_checks: bool = (false, parse_bool, [TRACKED], + "control if mem::uninitialized and mem::zeroed panic on more UB"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"), split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [UNTRACKED], diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index a2cd3c4c468..a771369c807 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -894,6 +894,15 @@ impl Scalar { Scalar::Union { .. } => true, } } + + /// Returns `true` if this type can be left uninit. + #[inline] + pub fn is_uninit_valid(&self) -> bool { + match *self { + Scalar::Initialized { .. } => false, + Scalar::Union { .. } => true, + } + } } /// Describes how the fields of a type are located in memory. @@ -1355,6 +1364,14 @@ pub struct PointeeInfo { pub address_space: AddressSpace, } +/// Used in `might_permit_raw_init` to indicate the kind of initialisation +/// that is checked to be valid +#[derive(Copy, Clone, Debug)] +pub enum InitKind { + Zero, + Uninit, +} + /// Trait that needs to be implemented by the higher-level type representation /// (e.g. `rustc_middle::ty::Ty`), to provide `rustc_target::abi` functionality. pub trait TyAbiInterface<'a, C>: Sized { @@ -1461,26 +1478,37 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { /// Determines if this type permits "raw" initialization by just transmuting some /// memory into an instance of `T`. - /// `zero` indicates if the memory is zero-initialized, or alternatively - /// left entirely uninitialized. + /// + /// `init_kind` indicates if the memory is zero-initialized or left uninitialized. + /// + /// `strict` is an opt-in debugging flag added in #97323 that enables more checks. + /// /// This is conservative: in doubt, it will answer `true`. /// /// FIXME: Once we removed all the conservatism, we could alternatively /// create an all-0/all-undef constant and run the const value validator to see if /// this is a valid value for the given type. - pub fn might_permit_raw_init(self, cx: &C, zero: bool) -> bool + pub fn might_permit_raw_init(self, cx: &C, init_kind: InitKind, strict: bool) -> bool where Self: Copy, Ty: TyAbiInterface<'a, C>, C: HasDataLayout, { let scalar_allows_raw_init = move |s: Scalar| -> bool { - if zero { - // The range must contain 0. - s.valid_range(cx).contains(0) - } else { - // The range must include all values. - s.is_always_valid(cx) + match init_kind { + InitKind::Zero => { + // The range must contain 0. + s.valid_range(cx).contains(0) + } + InitKind::Uninit => { + if strict { + // The type must be allowed to be uninit (which means "is a union"). + s.is_uninit_valid() + } else { + // The range must include all values. + s.is_always_valid(cx) + } + } } }; @@ -1500,12 +1528,19 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { // If we have not found an error yet, we need to recursively descend into fields. match &self.fields { FieldsShape::Primitive | FieldsShape::Union { .. } => {} - FieldsShape::Array { .. } => { - // FIXME(#66151): For now, we are conservative and do not check arrays. + FieldsShape::Array { count, .. } => { + // FIXME(#66151): For now, we are conservative and do not check arrays by default. + if strict + && *count > 0 + && !self.field(cx, 0).might_permit_raw_init(cx, init_kind, strict) + { + // Found non empty array with a type that is unhappy about this kind of initialization + return false; + } } FieldsShape::Arbitrary { offsets, .. } => { for idx in 0..offsets.len() { - if !self.field(cx, idx).might_permit_raw_init(cx, zero) { + if !self.field(cx, idx).might_permit_raw_init(cx, init_kind, strict) { // We found a field that is unhappy with this kind of initialization. return false; } diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index f46e8ff0004..539e94993ae 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -20,6 +20,7 @@ #![feature(label_break_value)] #![feature(let_chains)] #![feature(let_else)] +#![feature(if_let_guard)] #![feature(never_type)] #![recursion_limit = "512"] // For rustdoc diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 27ce08ea045..0dea2c3d8bf 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -39,150 +39,148 @@ pub fn is_const_evaluatable<'cx, 'tcx>( let tcx = infcx.tcx; if tcx.features().generic_const_exprs { - match AbstractConst::new(tcx, uv)? { - // We are looking at a generic abstract constant. - Some(ct) => { - if satisfied_from_param_env(tcx, ct, param_env)? { - return Ok(()); - } - - // We were unable to unify the abstract constant with - // a constant found in the caller bounds, there are - // now three possible cases here. - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] - enum FailureKind { - /// The abstract const still references an inference - /// variable, in this case we return `TooGeneric`. - MentionsInfer, - /// The abstract const references a generic parameter, - /// this means that we emit an error here. - MentionsParam, - /// The substs are concrete enough that we can simply - /// try and evaluate the given constant. - Concrete, - } - let mut failure_kind = FailureKind::Concrete; - walk_abstract_const::(tcx, ct, |node| match node.root(tcx) { - Node::Leaf(leaf) => { - if leaf.has_infer_types_or_consts() { - failure_kind = FailureKind::MentionsInfer; - } else if leaf.has_param_types_or_consts() { - failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); - } - - ControlFlow::CONTINUE - } - Node::Cast(_, _, ty) => { - if ty.has_infer_types_or_consts() { - failure_kind = FailureKind::MentionsInfer; - } else if ty.has_param_types_or_consts() { - failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); - } - - ControlFlow::CONTINUE - } - Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => { - ControlFlow::CONTINUE - } - }); - - match failure_kind { - FailureKind::MentionsInfer => { - return Err(NotConstEvaluatable::MentionsInfer); - } - FailureKind::MentionsParam => { - return Err(NotConstEvaluatable::MentionsParam); - } - FailureKind::Concrete => { - // Dealt with below by the same code which handles this - // without the feature gate. - } - } + if let Some(ct) = AbstractConst::new(tcx, uv)? { + if satisfied_from_param_env(tcx, ct, param_env)? { + return Ok(()); } - None => { - // If we are dealing with a concrete constant, we can - // reuse the old code path and try to evaluate - // the constant. + + // We were unable to unify the abstract constant with + // a constant found in the caller bounds, there are + // now three possible cases here. + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] + enum FailureKind { + /// The abstract const still references an inference + /// variable, in this case we return `TooGeneric`. + MentionsInfer, + /// The abstract const references a generic parameter, + /// this means that we emit an error here. + MentionsParam, + /// The substs are concrete enough that we can simply + /// try and evaluate the given constant. + Concrete, + } + let mut failure_kind = FailureKind::Concrete; + walk_abstract_const::(tcx, ct, |node| match node.root(tcx) { + Node::Leaf(leaf) => { + if leaf.has_infer_types_or_consts() { + failure_kind = FailureKind::MentionsInfer; + } else if leaf.has_param_types_or_consts() { + failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); + } + + ControlFlow::CONTINUE + } + Node::Cast(_, _, ty) => { + if ty.has_infer_types_or_consts() { + failure_kind = FailureKind::MentionsInfer; + } else if ty.has_param_types_or_consts() { + failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); + } + + ControlFlow::CONTINUE + } + Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => { + ControlFlow::CONTINUE + } + }); + + match failure_kind { + FailureKind::MentionsInfer => { + return Err(NotConstEvaluatable::MentionsInfer); + } + FailureKind::MentionsParam => { + return Err(NotConstEvaluatable::MentionsParam); + } + // returned below + FailureKind::Concrete => {} } } - } - - let future_compat_lint = || { - if let Some(local_def_id) = uv.def.did.as_local() { - infcx.tcx.struct_span_lint_hir( - lint::builtin::CONST_EVALUATABLE_UNCHECKED, - infcx.tcx.hir().local_def_id_to_hir_id(local_def_id), - span, - |err| { - err.build("cannot use constants which depend on generic parameters in types") - .emit(); - }, - ); - } - }; - - // FIXME: We should only try to evaluate a given constant here if it is fully concrete - // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`. - // - // We previously did not check this, so we only emit a future compat warning if - // const evaluation succeeds and the given constant is still polymorphic for now - // and hopefully soon change this to an error. - // - // See #74595 for more details about this. - let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span)); - - if concrete.is_ok() && uv.substs.has_param_types_or_consts() { - match infcx.tcx.def_kind(uv.def.did) { - DefKind::AnonConst | DefKind::InlineConst => { - let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def); - - if mir_body.is_polymorphic { - future_compat_lint(); - } + let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span)); + match concrete { + Err(ErrorHandled::TooGeneric) => Err(if !uv.has_infer_types_or_consts() { + infcx + .tcx + .sess + .delay_span_bug(span, &format!("unexpected `TooGeneric` for {:?}", uv)); + NotConstEvaluatable::MentionsParam + } else { + NotConstEvaluatable::MentionsInfer + }), + Err(ErrorHandled::Linted) => { + let reported = infcx + .tcx + .sess + .delay_span_bug(span, "constant in type had error reported as lint"); + Err(NotConstEvaluatable::Error(reported)) } - _ => future_compat_lint(), + Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)), + Ok(_) => Ok(()), } - } + } else { + // FIXME: We should only try to evaluate a given constant here if it is fully concrete + // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`. + // + // We previously did not check this, so we only emit a future compat warning if + // const evaluation succeeds and the given constant is still polymorphic for now + // and hopefully soon change this to an error. + // + // See #74595 for more details about this. + let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span)); - // If we're evaluating a foreign constant, under a nightly compiler without generic - // const exprs, AND it would've passed if that expression had been evaluated with - // generic const exprs, then suggest using generic const exprs. - if concrete.is_err() - && tcx.sess.is_nightly_build() - && !uv.def.did.is_local() - && !tcx.features().generic_const_exprs - && let Ok(Some(ct)) = AbstractConst::new(tcx, uv) - && satisfied_from_param_env(tcx, ct, param_env) == Ok(true) - { - tcx.sess - .struct_span_fatal( - // Slightly better span than just using `span` alone - if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span }, - "failed to evaluate generic const expression", - ) - .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") - .span_suggestion_verbose( - rustc_span::DUMMY_SP, - "consider enabling this feature", - "#![feature(generic_const_exprs)]\n".to_string(), - rustc_errors::Applicability::MaybeIncorrect, - ) - .emit() - } + match concrete { + // If we're evaluating a foreign constant, under a nightly compiler without generic + // const exprs, AND it would've passed if that expression had been evaluated with + // generic const exprs, then suggest using generic const exprs. + Err(_) if tcx.sess.is_nightly_build() + && let Ok(Some(ct)) = AbstractConst::new(tcx, uv) + && satisfied_from_param_env(tcx, ct, param_env) == Ok(true) => { + tcx.sess + .struct_span_fatal( + // Slightly better span than just using `span` alone + if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span }, + "failed to evaluate generic const expression", + ) + .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") + .span_suggestion_verbose( + rustc_span::DUMMY_SP, + "consider enabling this feature", + "#![feature(generic_const_exprs)]\n".to_string(), + rustc_errors::Applicability::MaybeIncorrect, + ) + .emit() + } - debug!(?concrete, "is_const_evaluatable"); - match concrete { - Err(ErrorHandled::TooGeneric) => Err(match uv.has_infer_types_or_consts() { - true => NotConstEvaluatable::MentionsInfer, - false => NotConstEvaluatable::MentionsParam, - }), - Err(ErrorHandled::Linted) => { - let reported = - infcx.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint"); - Err(NotConstEvaluatable::Error(reported)) + Err(ErrorHandled::TooGeneric) => Err(if uv.has_infer_types_or_consts() { + NotConstEvaluatable::MentionsInfer + } else { + NotConstEvaluatable::MentionsParam + }), + Err(ErrorHandled::Linted) => { + let reported = + infcx.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint"); + Err(NotConstEvaluatable::Error(reported)) + } + Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)), + Ok(_) => { + if uv.substs.has_param_types_or_consts() { + assert!(matches!(infcx.tcx.def_kind(uv.def.did), DefKind::AnonConst)); + let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def); + + if mir_body.is_polymorphic { + let Some(local_def_id) = uv.def.did.as_local() else { return Ok(()) }; + tcx.struct_span_lint_hir( + lint::builtin::CONST_EVALUATABLE_UNCHECKED, + tcx.hir().local_def_id_to_hir_id(local_def_id), + span, + |err| { + err.build("cannot use constants which depend on generic parameters in types").emit(); + }) + } + } + + Ok(()) + }, } - Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)), - Ok(_) => Ok(()), } } diff --git a/config.toml.example b/config.toml.example index a7968bca7be..a810e8c0e12 100644 --- a/config.toml.example +++ b/config.toml.example @@ -605,16 +605,9 @@ changelog-seen = 2 # development of NLL #test-compare-mode = false -# Use LLVM libunwind as the implementation for Rust's unwinder. -# Accepted values are 'in-tree' (formerly true), 'system' or 'no' (formerly false). -# This option only applies for Linux and Fuchsia targets. -# On Linux target, if crt-static is not enabled, 'no' means dynamic link to -# `libgcc_s.so`, 'in-tree' means static link to the in-tree build of llvm libunwind -# and 'system' means dynamic link to `libunwind.so`. If crt-static is enabled, -# the behavior is depend on the libc. On musl target, 'no' and 'in-tree' both -# means static link to the in-tree build of llvm libunwind, and 'system' means -# static link to `libunwind.a` provided by system. Due to the limitation of glibc, -# it must link to `libgcc_eh.a` to get a working output, and this option have no effect. +# Global default for llvm-libunwind for all targets. See the target-specific +# documentation for llvm-libunwind below. Note that the target-specific +# option will override this if set. #llvm-libunwind = 'no' # Enable Windows Control Flow Guard checks in the standard library. @@ -671,6 +664,18 @@ changelog-seen = 2 # not, you can specify an explicit file name for it. #llvm-filecheck = "/path/to/llvm-version/bin/FileCheck" +# Use LLVM libunwind as the implementation for Rust's unwinder. +# Accepted values are 'in-tree' (formerly true), 'system' or 'no' (formerly false). +# This option only applies for Linux and Fuchsia targets. +# On Linux target, if crt-static is not enabled, 'no' means dynamic link to +# `libgcc_s.so`, 'in-tree' means static link to the in-tree build of llvm libunwind +# and 'system' means dynamic link to `libunwind.so`. If crt-static is enabled, +# the behavior is depend on the libc. On musl target, 'no' and 'in-tree' both +# means static link to the in-tree build of llvm libunwind, and 'system' means +# static link to `libunwind.a` provided by system. Due to the limitation of glibc, +# it must link to `libgcc_eh.a` to get a working output, and this option have no effect. +#llvm-libunwind = 'no' if Linux, 'in-tree' if Fuchsia + # If this target is for Android, this option will be required to specify where # the NDK for the target lives. This is used to find the C compiler to link and # build native code. diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 36df5c3beb3..43cfb1bb640 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1517,7 +1517,7 @@ macro_rules! atomic_int { #[$stable_debug] impl fmt::Debug for $atomic_type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.load(Ordering::SeqCst), f) + fmt::Debug::fmt(&self.load(Ordering::Relaxed), f) } } @@ -2996,7 +2996,7 @@ pub fn compiler_fence(order: Ordering) { #[stable(feature = "atomic_debug", since = "1.3.0")] impl fmt::Debug for AtomicBool { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.load(Ordering::SeqCst), f) + fmt::Debug::fmt(&self.load(Ordering::Relaxed), f) } } @@ -3004,7 +3004,7 @@ impl fmt::Debug for AtomicBool { #[stable(feature = "atomic_debug", since = "1.3.0")] impl fmt::Debug for AtomicPtr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.load(Ordering::SeqCst), f) + fmt::Debug::fmt(&self.load(Ordering::Relaxed), f) } } diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 4027a71a06c..463f714064c 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -49,6 +49,9 @@ use crate::sys::os as os_imp; /// Ok(()) /// } /// ``` +#[doc(alias = "pwd")] +#[doc(alias = "getcwd")] +#[doc(alias = "GetCurrentDirectory")] #[stable(feature = "env", since = "1.0.0")] pub fn current_dir() -> io::Result { os_imp::getcwd() diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 7a8c7fee5f5..0b430f64e1e 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -176,7 +176,7 @@ fn copy_third_party_objects( if target == "x86_64-fortanix-unknown-sgx" || target.contains("pc-windows-gnullvm") - || builder.config.llvm_libunwind == LlvmLibunwind::InTree + || builder.config.llvm_libunwind(target) == LlvmLibunwind::InTree && (target.contains("linux") || target.contains("fuchsia")) { let libunwind_path = diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index f9acd52274f..843d276cd7a 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -67,7 +67,6 @@ pub struct Config { pub rustc_error_format: Option, pub json_output: bool, pub test_compare_mode: bool, - pub llvm_libunwind: LlvmLibunwind, pub color: Color, pub patch_binaries_for_nix: bool, @@ -151,6 +150,7 @@ pub struct Config { pub rust_profile_generate: Option, pub llvm_profile_use: Option, pub llvm_profile_generate: bool, + pub llvm_libunwind_default: Option, pub build: TargetSelection, pub hosts: Vec, @@ -342,6 +342,7 @@ pub struct Target { pub llvm_config: Option, /// Some(path to FileCheck) if one was specified. pub llvm_filecheck: Option, + pub llvm_libunwind: Option, pub cc: Option, pub cxx: Option, pub ar: Option, @@ -680,6 +681,7 @@ define_config! { linker: Option = "linker", llvm_config: Option = "llvm-config", llvm_filecheck: Option = "llvm-filecheck", + llvm_libunwind: Option = "llvm-libunwind", android_ndk: Option = "android-ndk", sanitizers: Option = "sanitizers", profiler: Option = "profiler", @@ -1043,10 +1045,6 @@ impl Config { set(&mut config.rust_rpath, rust.rpath); set(&mut config.jemalloc, rust.jemalloc); set(&mut config.test_compare_mode, rust.test_compare_mode); - config.llvm_libunwind = rust - .llvm_libunwind - .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind")) - .unwrap_or_default(); set(&mut config.backtrace, rust.backtrace); set(&mut config.channel, rust.channel); config.description = rust.description; @@ -1069,6 +1067,9 @@ impl Config { config.rust_thin_lto_import_instr_limit = rust.thin_lto_import_instr_limit; set(&mut config.rust_remap_debuginfo, rust.remap_debuginfo); set(&mut config.control_flow_guard, rust.control_flow_guard); + config.llvm_libunwind_default = rust + .llvm_libunwind + .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind")); if let Some(ref backends) = rust.codegen_backends { config.rust_codegen_backends = @@ -1095,6 +1096,10 @@ impl Config { if let Some(ref s) = cfg.llvm_filecheck { target.llvm_filecheck = Some(config.src.join(s)); } + target.llvm_libunwind = cfg + .llvm_libunwind + .as_ref() + .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind")); if let Some(ref s) = cfg.android_ndk { target.ndk = Some(config.src.join(s)); } @@ -1328,6 +1333,14 @@ impl Config { self.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) } + pub fn llvm_libunwind(&self, target: TargetSelection) -> LlvmLibunwind { + self.target_config + .get(&target) + .and_then(|t| t.llvm_libunwind) + .or(self.llvm_libunwind_default) + .unwrap_or(LlvmLibunwind::No) + } + pub fn submodules(&self, rust_info: &GitInfo) -> bool { self.submodules.unwrap_or(rust_info.is_git()) } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 769382525fb..591f9a1ca50 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -720,7 +720,7 @@ impl Build { fn std_features(&self, target: TargetSelection) -> String { let mut features = "panic-unwind".to_string(); - match self.config.llvm_libunwind { + match self.config.llvm_libunwind(target) { LlvmLibunwind::InTree => features.push_str(" llvm-libunwind"), LlvmLibunwind::System => features.push_str(" system-llvm-libunwind"), LlvmLibunwind::No => {} diff --git a/src/test/ui/const-generics/generic_const_exprs/dependence_lint.full.stderr b/src/test/ui/const-generics/generic_const_exprs/dependence_lint.full.stderr new file mode 100644 index 00000000000..4cd86fecd7e --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/dependence_lint.full.stderr @@ -0,0 +1,39 @@ +error: generic parameters may not be used in const operations + --> $DIR/dependence_lint.rs:13:32 + | +LL | let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce + | ^ cannot perform const operation using `T` + | + = note: type parameters may not be used in const expressions + = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions + +error: generic parameters may not be used in const operations + --> $DIR/dependence_lint.rs:20:37 + | +LL | let _: [u8; if true { size_of::() } else { 3 }]; // error on stable, error with gce + | ^ cannot perform const operation using `T` + | + = note: type parameters may not be used in const expressions + = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions + +warning: cannot use constants which depend on generic parameters in types + --> $DIR/dependence_lint.rs:9:9 + | +LL | [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs` + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(const_evaluatable_unchecked)]` 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 #76200 + +warning: cannot use constants which depend on generic parameters in types + --> $DIR/dependence_lint.rs:16:9 + | +LL | [0; if false { size_of::() } else { 3 }]; // lint on stable, error with gce + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #76200 + +error: aborting due to 2 previous errors; 2 warnings emitted + diff --git a/src/test/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr b/src/test/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr new file mode 100644 index 00000000000..b13bcdb2c47 --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr @@ -0,0 +1,34 @@ +error: overly complex generic constant + --> $DIR/dependence_lint.rs:16:9 + | +LL | [0; if false { size_of::() } else { 3 }]; // lint on stable, error with gce + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants + | + = help: consider moving this anonymous constant into a `const` function + +error: overly complex generic constant + --> $DIR/dependence_lint.rs:20:17 + | +LL | let _: [u8; if true { size_of::() } else { 3 }]; // error on stable, error with gce + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants + | + = help: consider moving this anonymous constant into a `const` function + +error: unconstrained generic constant + --> $DIR/dependence_lint.rs:13:12 + | +LL | let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); size_of::<*mut T>()]:` + +error: unconstrained generic constant + --> $DIR/dependence_lint.rs:9:9 + | +LL | [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs` + | ^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); size_of::<*mut T>()]:` + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/const-generics/generic_const_exprs/dependence_lint.rs b/src/test/ui/const-generics/generic_const_exprs/dependence_lint.rs new file mode 100644 index 00000000000..dcdfd75def9 --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/dependence_lint.rs @@ -0,0 +1,25 @@ +// revisions: full gce + +#![cfg_attr(gce, feature(generic_const_exprs))] +#![allow(incomplete_features)] + +use std::mem::size_of; + +fn foo() { + [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs` + //[gce]~^ ERROR unconstrained + //[full]~^^ WARNING cannot use constants + //[full]~| WARNING this was previously accepted + let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce + //[full]~^ ERROR generic parameters may not be used + //[gce]~^^ ERROR unconstrained generic + [0; if false { size_of::() } else { 3 }]; // lint on stable, error with gce + //[gce]~^ ERROR overly complex + //[full]~^^ WARNING cannot use constants + //[full]~| WARNING this was previously accepted + let _: [u8; if true { size_of::() } else { 3 }]; // error on stable, error with gce + //[full]~^ ERROR generic parameters may not be used + //[gce]~^^ ERROR overly complex +} + +fn main() {} diff --git a/src/test/ui/const-generics/generic_const_exprs/no_dependence.rs b/src/test/ui/const-generics/generic_const_exprs/no_dependence.rs new file mode 100644 index 00000000000..db8dc6ed443 --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/no_dependence.rs @@ -0,0 +1,13 @@ +// check-pass +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +fn two_args() -> [u8; M + 2] { + [0; M + 2] +} + +fn yay() -> [u8; 4] { + two_args::() // no lint +} + +fn main() {} diff --git a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs index 98fd13553c0..3ffd35ecdb8 100644 --- a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -1,8 +1,9 @@ // run-pass // needs-unwind // ignore-wasm32-bare compiled with panic=abort by default -// revisions: mir thir +// revisions: mir thir strict // [thir]compile-flags: -Zthir-unsafeck +// [strict]compile-flags: -Zstrict-init-checks // ignore-tidy-linelength // This test checks panic emitted from `mem::{uninitialized,zeroed}`. @@ -54,6 +55,8 @@ enum LR_NonZero { Right(num::NonZeroI64), } +struct ZeroSized; + fn test_panic_msg(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) { let err = panic::catch_unwind(op).err(); assert_eq!( @@ -228,11 +231,40 @@ fn main() { let _val = mem::zeroed::<[!; 0]>(); let _val = mem::uninitialized::>(); let _val = mem::uninitialized::<[!; 0]>(); + let _val = mem::uninitialized::<()>(); + let _val = mem::uninitialized::(); - // These are UB because they have not been officially blessed, but we await the resolution - // of before doing - // anything about that. - let _val = mem::uninitialized::(); - let _val = mem::uninitialized::<*const ()>(); + if cfg!(strict) { + test_panic_msg( + || mem::uninitialized::(), + "attempted to leave type `i32` uninitialized, which is invalid" + ); + + test_panic_msg( + || mem::uninitialized::<*const ()>(), + "attempted to leave type `*const ()` uninitialized, which is invalid" + ); + + test_panic_msg( + || mem::uninitialized::<[i32; 1]>(), + "attempted to leave type `[i32; 1]` uninitialized, which is invalid" + ); + + test_panic_msg( + || mem::zeroed::>(), + "attempted to zero-initialize type `core::ptr::non_null::NonNull<()>`, which is invalid" + ); + + test_panic_msg( + || mem::zeroed::<[NonNull<()>; 1]>(), + "attempted to zero-initialize type `[core::ptr::non_null::NonNull<()>; 1]`, which is invalid" + ); + } else { + // These are UB because they have not been officially blessed, but we await the resolution + // of before doing + // anything about that. + let _val = mem::uninitialized::(); + let _val = mem::uninitialized::<*const ()>(); + } } }