From d11667f5ccf148270362fb9a5cf39ed06eff7374 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Fri, 13 May 2022 20:59:21 +0200 Subject: [PATCH 1/6] Change orderings of `Debug` for the Atomic types to `Relaxed`. This reduces synchronization between threads when debugging the atomic types. Reducing the synchronization means that executions with and without the debug calls will be more consistent, making it easier to debug. --- library/core/src/sync/atomic.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index a01f3fbad565c..c0aa5dd4cc327 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) } } From edae6edd32f1c68dd42841888c963353f89473b4 Mon Sep 17 00:00:00 2001 From: kadmin Date: Tue, 17 May 2022 05:50:16 +0000 Subject: [PATCH 2/6] Add tests for lint on type dependent on consts --- .../src/traits/const_evaluatable.rs | 3 ++ .../dependence_lint.full.stderr | 39 +++++++++++++++++++ .../dependence_lint.gce.stderr | 34 ++++++++++++++++ .../generic_const_exprs/dependence_lint.rs | 25 ++++++++++++ .../generic_const_exprs/no_dependence.rs | 13 +++++++ 5 files changed, 114 insertions(+) create mode 100644 src/test/ui/const-generics/generic_const_exprs/dependence_lint.full.stderr create mode 100644 src/test/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr create mode 100644 src/test/ui/const-generics/generic_const_exprs/dependence_lint.rs create mode 100644 src/test/ui/const-generics/generic_const_exprs/no_dependence.rs diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 27ce08ea0453f..c1b77c67c1fe2 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -108,6 +108,9 @@ pub fn is_const_evaluatable<'cx, 'tcx>( } let future_compat_lint = || { + if tcx.features().generic_const_exprs { + return; + } if let Some(local_def_id) = uv.def.did.as_local() { infcx.tcx.struct_span_lint_hir( lint::builtin::CONST_EVALUATABLE_UNCHECKED, 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 0000000000000..4cd86fecd7e9b --- /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 0000000000000..b13bcdb2c4786 --- /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 0000000000000..dcdfd75def906 --- /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 0000000000000..db8dc6ed4434e --- /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() {} From ee8efc5c4a634a26be59e2a90a8a686b1242ce03 Mon Sep 17 00:00:00 2001 From: kadmin Date: Thu, 19 May 2022 18:53:01 +0000 Subject: [PATCH 3/6] Coalesce branches Move a bunch of branches together into one if block, for easier reading. Resolve comments Attempt to make some branches unreachable [tmp] Revert unreachable branches --- compiler/rustc_trait_selection/src/lib.rs | 1 + .../src/traits/const_evaluatable.rs | 269 +++++++++--------- 2 files changed, 133 insertions(+), 137 deletions(-) diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 0dd497448ca47..5e549319abf6a 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -21,6 +21,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 c1b77c67c1fe2..0dea2c3d8bfe8 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -39,153 +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 + if let Some(ct) = AbstractConst::new(tcx, uv)? { + 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); } - 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 + 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); } - }); - 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. - } + ControlFlow::CONTINUE } - } - None => { - // If we are dealing with a concrete constant, we can - // reuse the old code path and try to evaluate - // the constant. - } - } - } + Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => { + ControlFlow::CONTINUE + } + }); - let future_compat_lint = || { - if tcx.features().generic_const_exprs { - return; - } - 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(); + match failure_kind { + FailureKind::MentionsInfer => { + return Err(NotConstEvaluatable::MentionsInfer); + } + FailureKind::MentionsParam => { + return Err(NotConstEvaluatable::MentionsParam); } + // returned below + FailureKind::Concrete => {} } - _ => future_compat_lint(), } - } - - // 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() - } - - 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)) + 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)) + } + 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)); + + 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() + } + + 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(()), } } From dd9f31d000a33c52383f9af9e1dbf44f754590c3 Mon Sep 17 00:00:00 2001 From: 5225225 <5225225@mailbox.org> Date: Mon, 23 May 2022 16:44:05 +0100 Subject: [PATCH 4/6] Add flag for stricter checks on uninit/zeroed --- .../src/intrinsics/mod.rs | 15 ++++- compiler/rustc_codegen_ssa/src/mir/block.rs | 8 ++- .../src/interpret/intrinsics.rs | 14 ++++- compiler/rustc_session/src/options.rs | 2 + compiler/rustc_target/src/abi/mod.rs | 59 +++++++++++++++---- .../intrinsics/panic-uninitialized-zeroed.rs | 44 ++++++++++++-- 6 files changed, 116 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index f8c69d46d1f67..6937e658ed5ee 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 f1007ba157829..03ef6d50d44cd 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 b747be3a63626..bf1cf816ddd0b 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 12e00ef51140b..66198dff2ae3d 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 a2cd3c4c46816..a771369c80789 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/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs index 98fd13553c009..3ffd35ecdb8da 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::(); + + 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" + ); - // 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 ()>(); + 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 ()>(); + } } } From f1e3d40456ad9d9b508439d828dac14c0670455b Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Wed, 2 Feb 2022 22:48:09 +0000 Subject: [PATCH 5/6] Make llvm-libunwind a per-target option --- config.toml.example | 25 +++++++++++++++---------- src/bootstrap/compile.rs | 2 +- src/bootstrap/config.rs | 23 ++++++++++++++++++----- src/bootstrap/lib.rs | 2 +- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/config.toml.example b/config.toml.example index a7968bca7be89..a810e8c0e12d6 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/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 7a8c7fee5f549..0b430f64e1edc 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 f9acd52274f88..843d276cd7a88 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 769382525fbb8..591f9a1ca50d3 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 => {} From 84c80e73487ddc88c2cac8023bd7bb9bd8694756 Mon Sep 17 00:00:00 2001 From: julio Date: Tue, 24 May 2022 19:41:40 -0700 Subject: [PATCH 6/6] add aliases for current_dir --- library/std/src/env.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 4027a71a06c5f..463f714064c61 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()