Auto merge of #97382 - Dylan-DPC:rollup-2t4ov4z, r=Dylan-DPC
Rollup of 5 pull requests Successful merges: - #93604 (Make llvm-libunwind a per-target option) - #97026 (Change orderings of `Debug` for the Atomic types to `Relaxed`.) - #97105 (Add tests for lint on type dependent on consts) - #97323 (Introduce stricter checks for might_permit_raw_init under a debug flag ) - #97379 (Add aliases for `current_dir`) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
cbdce42320
18 changed files with 403 additions and 182 deletions
|
@ -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::print::with_no_trimmed_paths;
|
||||||
use rustc_middle::ty::subst::SubstsRef;
|
use rustc_middle::ty::subst::SubstsRef;
|
||||||
use rustc_span::symbol::{kw, sym, Symbol};
|
use rustc_span::symbol::{kw, sym, Symbol};
|
||||||
|
use rustc_target::abi::InitKind;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use cranelift_codegen::ir::AtomicRmwOp;
|
use cranelift_codegen::ir::AtomicRmwOp;
|
||||||
|
@ -671,7 +672,12 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
||||||
return;
|
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!({
|
with_no_trimmed_paths!({
|
||||||
crate::base::codegen_panic(
|
crate::base::codegen_panic(
|
||||||
fx,
|
fx,
|
||||||
|
@ -682,7 +688,12 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
||||||
return;
|
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!({
|
with_no_trimmed_paths!({
|
||||||
crate::base::codegen_panic(
|
crate::base::codegen_panic(
|
||||||
fx,
|
fx,
|
||||||
|
|
|
@ -22,7 +22,7 @@ use rustc_span::source_map::Span;
|
||||||
use rustc_span::{sym, Symbol};
|
use rustc_span::{sym, Symbol};
|
||||||
use rustc_symbol_mangling::typeid_for_fnabi;
|
use rustc_symbol_mangling::typeid_for_fnabi;
|
||||||
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
|
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;
|
use rustc_target::spec::abi::Abi;
|
||||||
|
|
||||||
/// Used by `FunctionCx::codegen_terminator` for emitting common patterns
|
/// 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,
|
source_info: mir::SourceInfo,
|
||||||
target: Option<mir::BasicBlock>,
|
target: Option<mir::BasicBlock>,
|
||||||
cleanup: Option<mir::BasicBlock>,
|
cleanup: Option<mir::BasicBlock>,
|
||||||
|
strict_validity: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Emit a panic or a no-op for `assert_*` intrinsics.
|
// Emit a panic or a no-op for `assert_*` intrinsics.
|
||||||
// These are intrinsics that compile to panics so that we can get a message
|
// 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 layout = bx.layout_of(ty);
|
||||||
let do_panic = match intrinsic {
|
let do_panic = match intrinsic {
|
||||||
Inhabited => layout.abi.is_uninhabited(),
|
Inhabited => layout.abi.is_uninhabited(),
|
||||||
ZeroValid => !layout.might_permit_raw_init(bx, /*zero:*/ true),
|
ZeroValid => !layout.might_permit_raw_init(bx, InitKind::Zero, strict_validity),
|
||||||
UninitValid => !layout.might_permit_raw_init(bx, /*zero:*/ false),
|
UninitValid => !layout.might_permit_raw_init(bx, InitKind::Uninit, strict_validity),
|
||||||
};
|
};
|
||||||
if do_panic {
|
if do_panic {
|
||||||
let msg_str = with_no_visible_paths!({
|
let msg_str = with_no_visible_paths!({
|
||||||
|
@ -678,6 +679,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
source_info,
|
source_info,
|
||||||
target,
|
target,
|
||||||
cleanup,
|
cleanup,
|
||||||
|
self.cx.tcx().sess.opts.debugging_opts.strict_init_checks,
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ use rustc_middle::ty::layout::LayoutOf as _;
|
||||||
use rustc_middle::ty::subst::SubstsRef;
|
use rustc_middle::ty::subst::SubstsRef;
|
||||||
use rustc_middle::ty::{Ty, TyCtxt};
|
use rustc_middle::ty::{Ty, TyCtxt};
|
||||||
use rustc_span::symbol::{sym, Symbol};
|
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::{
|
use super::{
|
||||||
util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy,
|
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
|
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(
|
M::abort(
|
||||||
self,
|
self,
|
||||||
|
@ -419,7 +423,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
if intrinsic_name == sym::assert_uninit_valid
|
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(
|
M::abort(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -1495,6 +1495,8 @@ options! {
|
||||||
"hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"),
|
"hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"),
|
||||||
stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED],
|
stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED],
|
||||||
"control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"),
|
"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],
|
strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
|
||||||
"tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"),
|
"tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"),
|
||||||
split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [UNTRACKED],
|
split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [UNTRACKED],
|
||||||
|
|
|
@ -894,6 +894,15 @@ impl Scalar {
|
||||||
Scalar::Union { .. } => true,
|
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.
|
/// Describes how the fields of a type are located in memory.
|
||||||
|
@ -1355,6 +1364,14 @@ pub struct PointeeInfo {
|
||||||
pub address_space: AddressSpace,
|
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
|
/// Trait that needs to be implemented by the higher-level type representation
|
||||||
/// (e.g. `rustc_middle::ty::Ty`), to provide `rustc_target::abi` functionality.
|
/// (e.g. `rustc_middle::ty::Ty`), to provide `rustc_target::abi` functionality.
|
||||||
pub trait TyAbiInterface<'a, C>: Sized {
|
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
|
/// Determines if this type permits "raw" initialization by just transmuting some
|
||||||
/// memory into an instance of `T`.
|
/// 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`.
|
/// This is conservative: in doubt, it will answer `true`.
|
||||||
///
|
///
|
||||||
/// FIXME: Once we removed all the conservatism, we could alternatively
|
/// 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
|
/// 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.
|
/// this is a valid value for the given type.
|
||||||
pub fn might_permit_raw_init<C>(self, cx: &C, zero: bool) -> bool
|
pub fn might_permit_raw_init<C>(self, cx: &C, init_kind: InitKind, strict: bool) -> bool
|
||||||
where
|
where
|
||||||
Self: Copy,
|
Self: Copy,
|
||||||
Ty: TyAbiInterface<'a, C>,
|
Ty: TyAbiInterface<'a, C>,
|
||||||
C: HasDataLayout,
|
C: HasDataLayout,
|
||||||
{
|
{
|
||||||
let scalar_allows_raw_init = move |s: Scalar| -> bool {
|
let scalar_allows_raw_init = move |s: Scalar| -> bool {
|
||||||
if zero {
|
match init_kind {
|
||||||
// The range must contain 0.
|
InitKind::Zero => {
|
||||||
s.valid_range(cx).contains(0)
|
// The range must contain 0.
|
||||||
} else {
|
s.valid_range(cx).contains(0)
|
||||||
// The range must include all values.
|
}
|
||||||
s.is_always_valid(cx)
|
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.
|
// If we have not found an error yet, we need to recursively descend into fields.
|
||||||
match &self.fields {
|
match &self.fields {
|
||||||
FieldsShape::Primitive | FieldsShape::Union { .. } => {}
|
FieldsShape::Primitive | FieldsShape::Union { .. } => {}
|
||||||
FieldsShape::Array { .. } => {
|
FieldsShape::Array { count, .. } => {
|
||||||
// FIXME(#66151): For now, we are conservative and do not check arrays.
|
// 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, .. } => {
|
FieldsShape::Arbitrary { offsets, .. } => {
|
||||||
for idx in 0..offsets.len() {
|
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.
|
// We found a field that is unhappy with this kind of initialization.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#![feature(label_break_value)]
|
#![feature(label_break_value)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
#![feature(let_else)]
|
#![feature(let_else)]
|
||||||
|
#![feature(if_let_guard)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
#![recursion_limit = "512"] // For rustdoc
|
#![recursion_limit = "512"] // For rustdoc
|
||||||
|
|
||||||
|
|
|
@ -39,150 +39,148 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
|
||||||
let tcx = infcx.tcx;
|
let tcx = infcx.tcx;
|
||||||
|
|
||||||
if tcx.features().generic_const_exprs {
|
if tcx.features().generic_const_exprs {
|
||||||
match AbstractConst::new(tcx, uv)? {
|
if let Some(ct) = AbstractConst::new(tcx, uv)? {
|
||||||
// We are looking at a generic abstract constant.
|
if satisfied_from_param_env(tcx, ct, param_env)? {
|
||||||
Some(ct) => {
|
return Ok(());
|
||||||
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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => {
|
|
||||||
// If we are dealing with a concrete constant, we can
|
// We were unable to unify the abstract constant with
|
||||||
// reuse the old code path and try to evaluate
|
// a constant found in the caller bounds, there are
|
||||||
// the constant.
|
// 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 concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span));
|
||||||
|
match concrete {
|
||||||
let future_compat_lint = || {
|
Err(ErrorHandled::TooGeneric) => Err(if !uv.has_infer_types_or_consts() {
|
||||||
if let Some(local_def_id) = uv.def.did.as_local() {
|
infcx
|
||||||
infcx.tcx.struct_span_lint_hir(
|
.tcx
|
||||||
lint::builtin::CONST_EVALUATABLE_UNCHECKED,
|
.sess
|
||||||
infcx.tcx.hir().local_def_id_to_hir_id(local_def_id),
|
.delay_span_bug(span, &format!("unexpected `TooGeneric` for {:?}", uv));
|
||||||
span,
|
NotConstEvaluatable::MentionsParam
|
||||||
|err| {
|
} else {
|
||||||
err.build("cannot use constants which depend on generic parameters in types")
|
NotConstEvaluatable::MentionsInfer
|
||||||
.emit();
|
}),
|
||||||
},
|
Err(ErrorHandled::Linted) => {
|
||||||
);
|
let reported = infcx
|
||||||
}
|
.tcx
|
||||||
};
|
.sess
|
||||||
|
.delay_span_bug(span, "constant in type had error reported as lint");
|
||||||
// FIXME: We should only try to evaluate a given constant here if it is fully concrete
|
Err(NotConstEvaluatable::Error(reported))
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => 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
|
match concrete {
|
||||||
// const exprs, AND it would've passed if that expression had been evaluated with
|
// If we're evaluating a foreign constant, under a nightly compiler without generic
|
||||||
// generic const exprs, then suggest using generic const exprs.
|
// const exprs, AND it would've passed if that expression had been evaluated with
|
||||||
if concrete.is_err()
|
// generic const exprs, then suggest using generic const exprs.
|
||||||
&& tcx.sess.is_nightly_build()
|
Err(_) if tcx.sess.is_nightly_build()
|
||||||
&& !uv.def.did.is_local()
|
&& let Ok(Some(ct)) = AbstractConst::new(tcx, uv)
|
||||||
&& !tcx.features().generic_const_exprs
|
&& satisfied_from_param_env(tcx, ct, param_env) == Ok(true) => {
|
||||||
&& let Ok(Some(ct)) = AbstractConst::new(tcx, uv)
|
tcx.sess
|
||||||
&& satisfied_from_param_env(tcx, ct, param_env) == Ok(true)
|
.struct_span_fatal(
|
||||||
{
|
// Slightly better span than just using `span` alone
|
||||||
tcx.sess
|
if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span },
|
||||||
.struct_span_fatal(
|
"failed to evaluate generic const expression",
|
||||||
// Slightly better span than just using `span` alone
|
)
|
||||||
if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span },
|
.note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
|
||||||
"failed to evaluate generic const expression",
|
.span_suggestion_verbose(
|
||||||
)
|
rustc_span::DUMMY_SP,
|
||||||
.note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
|
"consider enabling this feature",
|
||||||
.span_suggestion_verbose(
|
"#![feature(generic_const_exprs)]\n".to_string(),
|
||||||
rustc_span::DUMMY_SP,
|
rustc_errors::Applicability::MaybeIncorrect,
|
||||||
"consider enabling this feature",
|
)
|
||||||
"#![feature(generic_const_exprs)]\n".to_string(),
|
.emit()
|
||||||
rustc_errors::Applicability::MaybeIncorrect,
|
}
|
||||||
)
|
|
||||||
.emit()
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!(?concrete, "is_const_evaluatable");
|
Err(ErrorHandled::TooGeneric) => Err(if uv.has_infer_types_or_consts() {
|
||||||
match concrete {
|
NotConstEvaluatable::MentionsInfer
|
||||||
Err(ErrorHandled::TooGeneric) => Err(match uv.has_infer_types_or_consts() {
|
} else {
|
||||||
true => NotConstEvaluatable::MentionsInfer,
|
NotConstEvaluatable::MentionsParam
|
||||||
false => NotConstEvaluatable::MentionsParam,
|
}),
|
||||||
}),
|
Err(ErrorHandled::Linted) => {
|
||||||
Err(ErrorHandled::Linted) => {
|
let reported =
|
||||||
let reported =
|
infcx.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint");
|
||||||
infcx.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint");
|
Err(NotConstEvaluatable::Error(reported))
|
||||||
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(()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -605,16 +605,9 @@ changelog-seen = 2
|
||||||
# development of NLL
|
# development of NLL
|
||||||
#test-compare-mode = false
|
#test-compare-mode = false
|
||||||
|
|
||||||
# Use LLVM libunwind as the implementation for Rust's unwinder.
|
# Global default for llvm-libunwind for all targets. See the target-specific
|
||||||
# Accepted values are 'in-tree' (formerly true), 'system' or 'no' (formerly false).
|
# documentation for llvm-libunwind below. Note that the target-specific
|
||||||
# This option only applies for Linux and Fuchsia targets.
|
# option will override this if set.
|
||||||
# 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'
|
#llvm-libunwind = 'no'
|
||||||
|
|
||||||
# Enable Windows Control Flow Guard checks in the standard library.
|
# 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.
|
# not, you can specify an explicit file name for it.
|
||||||
#llvm-filecheck = "/path/to/llvm-version/bin/FileCheck"
|
#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
|
# 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
|
# the NDK for the target lives. This is used to find the C compiler to link and
|
||||||
# build native code.
|
# build native code.
|
||||||
|
|
|
@ -1517,7 +1517,7 @@ macro_rules! atomic_int {
|
||||||
#[$stable_debug]
|
#[$stable_debug]
|
||||||
impl fmt::Debug for $atomic_type {
|
impl fmt::Debug for $atomic_type {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
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")]
|
#[stable(feature = "atomic_debug", since = "1.3.0")]
|
||||||
impl fmt::Debug for AtomicBool {
|
impl fmt::Debug for AtomicBool {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
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")]
|
#[stable(feature = "atomic_debug", since = "1.3.0")]
|
||||||
impl<T> fmt::Debug for AtomicPtr<T> {
|
impl<T> fmt::Debug for AtomicPtr<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,9 @@ use crate::sys::os as os_imp;
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[doc(alias = "pwd")]
|
||||||
|
#[doc(alias = "getcwd")]
|
||||||
|
#[doc(alias = "GetCurrentDirectory")]
|
||||||
#[stable(feature = "env", since = "1.0.0")]
|
#[stable(feature = "env", since = "1.0.0")]
|
||||||
pub fn current_dir() -> io::Result<PathBuf> {
|
pub fn current_dir() -> io::Result<PathBuf> {
|
||||||
os_imp::getcwd()
|
os_imp::getcwd()
|
||||||
|
|
|
@ -176,7 +176,7 @@ fn copy_third_party_objects(
|
||||||
|
|
||||||
if target == "x86_64-fortanix-unknown-sgx"
|
if target == "x86_64-fortanix-unknown-sgx"
|
||||||
|| target.contains("pc-windows-gnullvm")
|
|| target.contains("pc-windows-gnullvm")
|
||||||
|| builder.config.llvm_libunwind == LlvmLibunwind::InTree
|
|| builder.config.llvm_libunwind(target) == LlvmLibunwind::InTree
|
||||||
&& (target.contains("linux") || target.contains("fuchsia"))
|
&& (target.contains("linux") || target.contains("fuchsia"))
|
||||||
{
|
{
|
||||||
let libunwind_path =
|
let libunwind_path =
|
||||||
|
|
|
@ -67,7 +67,6 @@ pub struct Config {
|
||||||
pub rustc_error_format: Option<String>,
|
pub rustc_error_format: Option<String>,
|
||||||
pub json_output: bool,
|
pub json_output: bool,
|
||||||
pub test_compare_mode: bool,
|
pub test_compare_mode: bool,
|
||||||
pub llvm_libunwind: LlvmLibunwind,
|
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
pub patch_binaries_for_nix: bool,
|
pub patch_binaries_for_nix: bool,
|
||||||
|
|
||||||
|
@ -151,6 +150,7 @@ pub struct Config {
|
||||||
pub rust_profile_generate: Option<String>,
|
pub rust_profile_generate: Option<String>,
|
||||||
pub llvm_profile_use: Option<String>,
|
pub llvm_profile_use: Option<String>,
|
||||||
pub llvm_profile_generate: bool,
|
pub llvm_profile_generate: bool,
|
||||||
|
pub llvm_libunwind_default: Option<LlvmLibunwind>,
|
||||||
|
|
||||||
pub build: TargetSelection,
|
pub build: TargetSelection,
|
||||||
pub hosts: Vec<TargetSelection>,
|
pub hosts: Vec<TargetSelection>,
|
||||||
|
@ -342,6 +342,7 @@ pub struct Target {
|
||||||
pub llvm_config: Option<PathBuf>,
|
pub llvm_config: Option<PathBuf>,
|
||||||
/// Some(path to FileCheck) if one was specified.
|
/// Some(path to FileCheck) if one was specified.
|
||||||
pub llvm_filecheck: Option<PathBuf>,
|
pub llvm_filecheck: Option<PathBuf>,
|
||||||
|
pub llvm_libunwind: Option<LlvmLibunwind>,
|
||||||
pub cc: Option<PathBuf>,
|
pub cc: Option<PathBuf>,
|
||||||
pub cxx: Option<PathBuf>,
|
pub cxx: Option<PathBuf>,
|
||||||
pub ar: Option<PathBuf>,
|
pub ar: Option<PathBuf>,
|
||||||
|
@ -680,6 +681,7 @@ define_config! {
|
||||||
linker: Option<String> = "linker",
|
linker: Option<String> = "linker",
|
||||||
llvm_config: Option<String> = "llvm-config",
|
llvm_config: Option<String> = "llvm-config",
|
||||||
llvm_filecheck: Option<String> = "llvm-filecheck",
|
llvm_filecheck: Option<String> = "llvm-filecheck",
|
||||||
|
llvm_libunwind: Option<String> = "llvm-libunwind",
|
||||||
android_ndk: Option<String> = "android-ndk",
|
android_ndk: Option<String> = "android-ndk",
|
||||||
sanitizers: Option<bool> = "sanitizers",
|
sanitizers: Option<bool> = "sanitizers",
|
||||||
profiler: Option<bool> = "profiler",
|
profiler: Option<bool> = "profiler",
|
||||||
|
@ -1043,10 +1045,6 @@ impl Config {
|
||||||
set(&mut config.rust_rpath, rust.rpath);
|
set(&mut config.rust_rpath, rust.rpath);
|
||||||
set(&mut config.jemalloc, rust.jemalloc);
|
set(&mut config.jemalloc, rust.jemalloc);
|
||||||
set(&mut config.test_compare_mode, rust.test_compare_mode);
|
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.backtrace, rust.backtrace);
|
||||||
set(&mut config.channel, rust.channel);
|
set(&mut config.channel, rust.channel);
|
||||||
config.description = rust.description;
|
config.description = rust.description;
|
||||||
|
@ -1069,6 +1067,9 @@ impl Config {
|
||||||
config.rust_thin_lto_import_instr_limit = rust.thin_lto_import_instr_limit;
|
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.rust_remap_debuginfo, rust.remap_debuginfo);
|
||||||
set(&mut config.control_flow_guard, rust.control_flow_guard);
|
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 {
|
if let Some(ref backends) = rust.codegen_backends {
|
||||||
config.rust_codegen_backends =
|
config.rust_codegen_backends =
|
||||||
|
@ -1095,6 +1096,10 @@ impl Config {
|
||||||
if let Some(ref s) = cfg.llvm_filecheck {
|
if let Some(ref s) = cfg.llvm_filecheck {
|
||||||
target.llvm_filecheck = Some(config.src.join(s));
|
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 {
|
if let Some(ref s) = cfg.android_ndk {
|
||||||
target.ndk = Some(config.src.join(s));
|
target.ndk = Some(config.src.join(s));
|
||||||
}
|
}
|
||||||
|
@ -1328,6 +1333,14 @@ impl Config {
|
||||||
self.rust_codegen_backends.contains(&INTERNER.intern_str("llvm"))
|
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 {
|
pub fn submodules(&self, rust_info: &GitInfo) -> bool {
|
||||||
self.submodules.unwrap_or(rust_info.is_git())
|
self.submodules.unwrap_or(rust_info.is_git())
|
||||||
}
|
}
|
||||||
|
|
|
@ -720,7 +720,7 @@ impl Build {
|
||||||
fn std_features(&self, target: TargetSelection) -> String {
|
fn std_features(&self, target: TargetSelection) -> String {
|
||||||
let mut features = "panic-unwind".to_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::InTree => features.push_str(" llvm-libunwind"),
|
||||||
LlvmLibunwind::System => features.push_str(" system-llvm-libunwind"),
|
LlvmLibunwind::System => features.push_str(" system-llvm-libunwind"),
|
||||||
LlvmLibunwind::No => {}
|
LlvmLibunwind::No => {}
|
||||||
|
|
|
@ -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::<T>() } 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 <https://github.com/rust-lang/rust/issues/76200>
|
||||||
|
|
||||||
|
warning: cannot use constants which depend on generic parameters in types
|
||||||
|
--> $DIR/dependence_lint.rs:16:9
|
||||||
|
|
|
||||||
|
LL | [0; if false { size_of::<T>() } 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 <https://github.com/rust-lang/rust/issues/76200>
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors; 2 warnings emitted
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
error: overly complex generic constant
|
||||||
|
--> $DIR/dependence_lint.rs:16:9
|
||||||
|
|
|
||||||
|
LL | [0; if false { size_of::<T>() } 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::<T>() } 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
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
// revisions: full gce
|
||||||
|
|
||||||
|
#![cfg_attr(gce, feature(generic_const_exprs))]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
fn foo<T>() {
|
||||||
|
[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::<T>() } 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::<T>() } else { 3 }]; // error on stable, error with gce
|
||||||
|
//[full]~^ ERROR generic parameters may not be used
|
||||||
|
//[gce]~^^ ERROR overly complex
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,13 @@
|
||||||
|
// check-pass
|
||||||
|
#![feature(generic_const_exprs)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
fn two_args<const N: usize, const M: usize>() -> [u8; M + 2] {
|
||||||
|
[0; M + 2]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn yay<const N: usize>() -> [u8; 4] {
|
||||||
|
two_args::<N, 2>() // no lint
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -1,8 +1,9 @@
|
||||||
// run-pass
|
// run-pass
|
||||||
// needs-unwind
|
// needs-unwind
|
||||||
// ignore-wasm32-bare compiled with panic=abort by default
|
// ignore-wasm32-bare compiled with panic=abort by default
|
||||||
// revisions: mir thir
|
// revisions: mir thir strict
|
||||||
// [thir]compile-flags: -Zthir-unsafeck
|
// [thir]compile-flags: -Zthir-unsafeck
|
||||||
|
// [strict]compile-flags: -Zstrict-init-checks
|
||||||
// ignore-tidy-linelength
|
// ignore-tidy-linelength
|
||||||
|
|
||||||
// This test checks panic emitted from `mem::{uninitialized,zeroed}`.
|
// This test checks panic emitted from `mem::{uninitialized,zeroed}`.
|
||||||
|
@ -54,6 +55,8 @@ enum LR_NonZero {
|
||||||
Right(num::NonZeroI64),
|
Right(num::NonZeroI64),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ZeroSized;
|
||||||
|
|
||||||
fn test_panic_msg<T>(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) {
|
fn test_panic_msg<T>(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) {
|
||||||
let err = panic::catch_unwind(op).err();
|
let err = panic::catch_unwind(op).err();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -228,11 +231,40 @@ fn main() {
|
||||||
let _val = mem::zeroed::<[!; 0]>();
|
let _val = mem::zeroed::<[!; 0]>();
|
||||||
let _val = mem::uninitialized::<MaybeUninit<bool>>();
|
let _val = mem::uninitialized::<MaybeUninit<bool>>();
|
||||||
let _val = mem::uninitialized::<[!; 0]>();
|
let _val = mem::uninitialized::<[!; 0]>();
|
||||||
|
let _val = mem::uninitialized::<()>();
|
||||||
|
let _val = mem::uninitialized::<ZeroSized>();
|
||||||
|
|
||||||
// These are UB because they have not been officially blessed, but we await the resolution
|
if cfg!(strict) {
|
||||||
// of <https://github.com/rust-lang/unsafe-code-guidelines/issues/71> before doing
|
test_panic_msg(
|
||||||
// anything about that.
|
|| mem::uninitialized::<i32>(),
|
||||||
let _val = mem::uninitialized::<i32>();
|
"attempted to leave type `i32` uninitialized, which is invalid"
|
||||||
let _val = mem::uninitialized::<*const ()>();
|
);
|
||||||
|
|
||||||
|
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::<NonNull<()>>(),
|
||||||
|
"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 <https://github.com/rust-lang/unsafe-code-guidelines/issues/71> before doing
|
||||||
|
// anything about that.
|
||||||
|
let _val = mem::uninitialized::<i32>();
|
||||||
|
let _val = mem::uninitialized::<*const ()>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue