Auto merge of #80579 - RalfJung:no-fallible-promotion, r=oli-obk

avoid promoting division, modulo and indexing operations that could fail

For division, `x / y` will still be promoted if `y` is a non-zero integer literal; however, `1/(1+1)` will not be promoted any more.

While at it, also see if we can reject promoting floating-point arithmetic (which are [complicated](https://github.com/rust-lang/unsafe-code-guidelines/issues/237) so maybe we should not promote them).

This will need a crater run to see if there's code out there that relies on these things being promoted.

If we can land this, promoteds in `fn`/`const fn` cannot fail to evaluate any more, which should let us do some simplifications in codegen/Miri!

Cc https://github.com/rust-lang/rfcs/pull/3027
Fixes https://github.com/rust-lang/rust/issues/61821
r? `@oli-obk`
This commit is contained in:
bors 2021-01-23 13:19:04 +00:00
commit 4d0dd02ee0
14 changed files with 329 additions and 331 deletions

View File

@ -238,41 +238,22 @@ declare_lint! {
///
/// ```rust,compile_fail
/// #![allow(unconditional_panic)]
/// let x: &'static i32 = &(1 / 0);
/// const C: i32 = 1/0;
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// This lint detects code that is very likely incorrect. If this lint is
/// allowed, then the code will not be evaluated at compile-time, and
/// instead continue to generate code to evaluate at runtime, which may
/// panic during runtime.
/// This lint detects constants that fail to evaluate. Allowing the lint will accept the
/// constant declaration, but any use of this constant will still lead to a hard error. This is
/// a future incompatibility lint; the plan is to eventually entirely forbid even declaring
/// constants that cannot be evaluated. See [issue #71800] for more details.
///
/// Note that this lint may trigger in either inside or outside of a
/// [const context]. Outside of a [const context], the compiler can
/// sometimes evaluate an expression at compile-time in order to generate
/// more efficient code. As the compiler becomes better at doing this, it
/// needs to decide what to do when it encounters code that it knows for
/// certain will panic or is otherwise incorrect. Making this a hard error
/// would prevent existing code that exhibited this behavior from
/// compiling, breaking backwards-compatibility. However, this is almost
/// certainly incorrect code, so this is a deny-by-default lint. For more
/// details, see [RFC 1229] and [issue #28238].
///
/// Note that there are several other more specific lints associated with
/// compile-time evaluation, such as [`arithmetic_overflow`],
/// [`unconditional_panic`].
///
/// [const context]: https://doc.rust-lang.org/reference/const_eval.html#const-context
/// [RFC 1229]: https://github.com/rust-lang/rfcs/blob/master/text/1229-compile-time-asserts.md
/// [issue #28238]: https://github.com/rust-lang/rust/issues/28238
/// [`arithmetic_overflow`]: deny-by-default.html#arithmetic-overflow
/// [`unconditional_panic`]: deny-by-default.html#unconditional-panic
/// [issue #71800]: https://github.com/rust-lang/rust/issues/71800
pub CONST_ERR,
Deny,
"constant evaluation detected erroneous expression",
"constant evaluation encountered erroneous expression",
report_in_external_macro
}

View File

@ -415,10 +415,11 @@ impl<'tcx> Validator<'_, 'tcx> {
// FIXME(eddyb) maybe cache this?
fn validate_local(&self, local: Local) -> Result<(), Unpromotable> {
if let TempState::Defined { location: loc, .. } = self.temps[local] {
let num_stmts = self.body[loc.block].statements.len();
let block = &self.body[loc.block];
let num_stmts = block.statements.len();
if loc.statement_index < num_stmts {
let statement = &self.body[loc.block].statements[loc.statement_index];
let statement = &block.statements[loc.statement_index];
match &statement.kind {
StatementKind::Assign(box (_, rhs)) => self.validate_rvalue(rhs),
_ => {
@ -430,7 +431,7 @@ impl<'tcx> Validator<'_, 'tcx> {
}
}
} else {
let terminator = self.body[loc.block].terminator();
let terminator = block.terminator();
match &terminator.kind {
TerminatorKind::Call { func, args, .. } => self.validate_call(func, args),
TerminatorKind::Yield { .. } => Err(Unpromotable),
@ -452,22 +453,15 @@ impl<'tcx> Validator<'_, 'tcx> {
match elem {
ProjectionElem::Deref => {
let mut promotable = false;
// The `is_empty` predicate is introduced to exclude the case
// where the projection operations are [ .field, * ].
// The reason is because promotion will be illegal if field
// accesses precede the dereferencing.
// We need to make sure this is a `Deref` of a local with no further projections.
// Discussion can be found at
// https://github.com/rust-lang/rust/pull/74945#discussion_r463063247
// There may be opportunity for generalization, but this needs to be
// accounted for.
if place_base.projection.is_empty() {
if let Some(local) = place_base.as_local() {
// This is a special treatment for cases like *&STATIC where STATIC is a
// global static variable.
// This pattern is generated only when global static variables are directly
// accessed and is qualified for promotion safely.
if let TempState::Defined { location, .. } =
self.temps[place_base.local]
{
if let TempState::Defined { location, .. } = self.temps[local] {
let def_stmt = self.body[location.block]
.statements
.get(location.statement_index);
@ -505,6 +499,50 @@ impl<'tcx> Validator<'_, 'tcx> {
ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {}
ProjectionElem::Index(local) => {
if !self.explicit {
let mut promotable = false;
// Only accept if we can predict the index and are indexing an array.
let val = if let TempState::Defined { location: loc, .. } =
self.temps[local]
{
let block = &self.body[loc.block];
if loc.statement_index < block.statements.len() {
let statement = &block.statements[loc.statement_index];
match &statement.kind {
StatementKind::Assign(box (
_,
Rvalue::Use(Operand::Constant(c)),
)) => c.literal.try_eval_usize(self.tcx, self.param_env),
_ => None,
}
} else {
None
}
} else {
None
};
if let Some(idx) = val {
// Determine the type of the thing we are indexing.
let ty = place_base.ty(self.body, self.tcx).ty;
match ty.kind() {
ty::Array(_, len) => {
// It's an array; determine its length.
if let Some(len) =
len.try_eval_usize(self.tcx, self.param_env)
{
// If the index is in-bounds, go ahead.
if idx < len {
promotable = true;
}
}
}
_ => {}
}
}
if !promotable {
return Err(Unpromotable);
}
}
self.validate_local(local)?;
}
@ -589,9 +627,7 @@ impl<'tcx> Validator<'_, 'tcx> {
fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> {
match rvalue {
Rvalue::Use(operand)
| Rvalue::Repeat(operand, _)
| Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, operand) => {
Rvalue::Use(operand) | Rvalue::Repeat(operand, _) => {
self.validate_operand(operand)?;
}
@ -616,10 +652,26 @@ impl<'tcx> Validator<'_, 'tcx> {
self.validate_operand(operand)?;
}
Rvalue::NullaryOp(op, _) => match op {
NullOp::Box => return Err(Unpromotable),
NullOp::SizeOf => {}
},
Rvalue::UnaryOp(op, operand) => {
match op {
// These operations can never fail.
UnOp::Neg | UnOp::Not => {}
}
self.validate_operand(operand)?;
}
Rvalue::BinaryOp(op, lhs, rhs) | Rvalue::CheckedBinaryOp(op, lhs, rhs) => {
let op = *op;
if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind() {
// raw pointer operations are not allowed inside consts and thus not promotable
let lhs_ty = lhs.ty(self.body, self.tcx);
if let ty::RawPtr(_) | ty::FnPtr(..) = lhs_ty.kind() {
// Raw and fn pointer operations are not allowed inside consts and thus not promotable.
assert!(matches!(
op,
BinOp::Eq
@ -634,7 +686,22 @@ impl<'tcx> Validator<'_, 'tcx> {
}
match op {
// FIXME: reject operations that can fail -- namely, division and modulo.
BinOp::Div | BinOp::Rem => {
if !self.explicit && lhs_ty.is_integral() {
// Integer division: the RHS must be a non-zero const.
let const_val = match rhs {
Operand::Constant(c) => {
c.literal.try_eval_bits(self.tcx, self.param_env, lhs_ty)
}
_ => None,
};
match const_val {
Some(x) if x != 0 => {} // okay
_ => return Err(Unpromotable), // value not known or 0 -- not okay
}
}
}
// The remaining operations can never fail.
BinOp::Eq
| BinOp::Ne
| BinOp::Le
@ -645,8 +712,6 @@ impl<'tcx> Validator<'_, 'tcx> {
| BinOp::Add
| BinOp::Sub
| BinOp::Mul
| BinOp::Div
| BinOp::Rem
| BinOp::BitXor
| BinOp::BitAnd
| BinOp::BitOr
@ -658,11 +723,6 @@ impl<'tcx> Validator<'_, 'tcx> {
self.validate_operand(rhs)?;
}
Rvalue::NullaryOp(op, _) => match op {
NullOp::Box => return Err(Unpromotable),
NullOp::SizeOf => {}
},
Rvalue::AddressOf(_, place) => {
// We accept `&raw *`, i.e., raw reborrows -- creating a raw pointer is
// no problem, only using it is.

View File

@ -6,6 +6,4 @@
fn main() {
&{ [1, 2, 3][4] };
//~^ WARN operation will panic
//~| WARN reaching this expression at runtime will panic or abort
//~| WARN erroneous constant used [const_err]
}

View File

@ -10,25 +10,5 @@ note: the lint level is defined here
LL | #![warn(const_err, unconditional_panic)]
| ^^^^^^^^^^^^^^^^^^^
warning: reaching this expression at runtime will panic or abort
--> $DIR/array-literal-index-oob.rs:7:8
|
LL | &{ [1, 2, 3][4] };
| ---^^^^^^^^^^^^--
| |
| indexing out of bounds: the len is 3 but the index is 4
|
note: the lint level is defined here
--> $DIR/array-literal-index-oob.rs:4:9
|
LL | #![warn(const_err, unconditional_panic)]
| ^^^^^^^^^
warning: erroneous constant used
--> $DIR/array-literal-index-oob.rs:7:5
|
LL | &{ [1, 2, 3][4] };
| ^^^^^^^^^^^^^^^^^ referenced constant has errors
warning: 3 warnings emitted
warning: 1 warning emitted

View File

@ -1,4 +1,5 @@
// compile-flags: -Ztreat-err-as-bug
//~ERROR constructed but no error reported
// compile-flags: -Ztreat-err-as-bug=2
// build-fail
// failure-status: 101
// rustc-env:RUST_BACKTRACE=1
@ -15,8 +16,11 @@
#![allow(unconditional_panic)]
#[warn(const_err)]
const X: i32 = 1 / 0; //~WARN any use of this value will cause an error
fn main() {
let x: &'static i32 = &(1 / 0);
//~^ ERROR reaching this expression at runtime will panic or abort [const_err]
let x: &'static i32 = &X;
//~^ ERROR evaluation of constant expression failed
println!("x={}", x);
}

View File

@ -1,18 +1,26 @@
error: reaching this expression at runtime will panic or abort
--> $DIR/const-eval-query-stack.rs:19:28
warning: any use of this value will cause an error
--> $DIR/const-eval-query-stack.rs:20:16
|
LL | let x: &'static i32 = &(1 / 0);
| -^^^^^^^
| |
| dividing by zero
LL | const X: i32 = 1 / 0;
| ---------------^^^^^-
| |
| attempt to divide `1_i32` by zero
|
= note: `#[deny(const_err)]` on by default
note: the lint level is defined here
--> $DIR/const-eval-query-stack.rs:19:8
|
LL | #[warn(const_err)]
| ^^^^^^^^^
error[E0080]: evaluation of constant expression failed
--> $DIR/const-eval-query-stack.rs:23:27
|
LL | let x: &'static i32 = &X;
| ^-
| |
| referenced constant has errors
query stack during panic:
#0 [eval_to_allocation_raw] const-evaluating + checking `main::promoted[1]`
#1 [eval_to_const_value_raw] simplifying constant for the type system `main::promoted[1]`
#2 [eval_to_const_value_raw] simplifying constant for the type system `main::promoted[1]`
#3 [normalize_generic_arg_after_erasing_regions] normalizing `main::promoted[1]`
#4 [optimized_mir] optimizing MIR for `main`
#5 [collect_and_partition_mono_items] collect_and_partition_mono_items
#0 [normalize_generic_arg_after_erasing_regions] normalizing `main::promoted[1]`
#1 [optimized_mir] optimizing MIR for `main`
#2 [collect_and_partition_mono_items] collect_and_partition_mono_items
end of query stack

View File

@ -1,38 +1,21 @@
warning: this arithmetic operation will overflow
--> $DIR/promoted_errors.rs:12:20
warning: any use of this value will cause an error
--> $DIR/promoted_errors.rs:13:5
|
LL | println!("{}", 0u32 - 1);
| ^^^^^^^^ attempt to compute `0_u32 - 1_u32`, which would overflow
|
note: the lint level is defined here
--> $DIR/promoted_errors.rs:9:20
|
LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
| ^^^^^^^^^^^^^^^^^^^
warning: this arithmetic operation will overflow
--> $DIR/promoted_errors.rs:14:14
|
LL | let _x = 0u32 - 1;
| ^^^^^^^^ attempt to compute `0_u32 - 1_u32`, which would overflow
warning: this operation will panic at runtime
--> $DIR/promoted_errors.rs:16:20
|
LL | println!("{}", 1 / (1 - 1));
| ^^^^^^^^^^^ attempt to divide `1_i32` by zero
|
note: the lint level is defined here
--> $DIR/promoted_errors.rs:9:41
|
LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
| ^^^^^^^^^^^^^^^^^^^
warning: reaching this expression at runtime will panic or abort
--> $DIR/promoted_errors.rs:16:20
|
LL | println!("{}", 1 / (1 - 1));
| ^^^^^^^^^^^ dividing by zero
LL | 0 - 1
| ^^^^^
| |
| attempt to compute `0_u32 - 1_u32`, which would overflow
| inside `overflow` at $DIR/promoted_errors.rs:13:5
| inside `X` at $DIR/promoted_errors.rs:31:29
...
LL | / const X: () = {
LL | | let _x: &'static u32 = &overflow();
LL | |
LL | | let _x: &'static i32 = &div_by_zero1();
... |
LL | | let _x: &'static i32 = &oob();
LL | | };
| |__-
|
note: the lint level is defined here
--> $DIR/promoted_errors.rs:9:9
@ -40,41 +23,18 @@ note: the lint level is defined here
LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
| ^^^^^^^^^
warning: erroneous constant used
--> $DIR/promoted_errors.rs:16:20
warning: any use of this value will cause an error
--> $DIR/promoted_errors.rs:31:28
|
LL | println!("{}", 1 / (1 - 1));
| ^^^^^^^^^^^ referenced constant has errors
LL | / const X: () = {
LL | | let _x: &'static u32 = &overflow();
| | ^^^^^^^^^^^ referenced constant has errors
LL | |
LL | | let _x: &'static i32 = &div_by_zero1();
... |
LL | | let _x: &'static i32 = &oob();
LL | | };
| |__-
warning: this operation will panic at runtime
--> $DIR/promoted_errors.rs:20:14
|
LL | let _x = 1 / (1 - 1);
| ^^^^^^^^^^^ attempt to divide `1_i32` by zero
warning: this operation will panic at runtime
--> $DIR/promoted_errors.rs:22:20
|
LL | println!("{}", 1 / (false as u32));
| ^^^^^^^^^^^^^^^^^^ attempt to divide `1_u32` by zero
warning: reaching this expression at runtime will panic or abort
--> $DIR/promoted_errors.rs:22:20
|
LL | println!("{}", 1 / (false as u32));
| ^^^^^^^^^^^^^^^^^^ dividing by zero
warning: erroneous constant used
--> $DIR/promoted_errors.rs:22:20
|
LL | println!("{}", 1 / (false as u32));
| ^^^^^^^^^^^^^^^^^^ referenced constant has errors
warning: this operation will panic at runtime
--> $DIR/promoted_errors.rs:26:14
|
LL | let _x = 1 / (false as u32);
| ^^^^^^^^^^^^^^^^^^ attempt to divide `1_u32` by zero
warning: 10 warnings emitted
warning: 2 warnings emitted

View File

@ -1,32 +1,21 @@
warning: this arithmetic operation will overflow
--> $DIR/promoted_errors.rs:14:14
warning: any use of this value will cause an error
--> $DIR/promoted_errors.rs:17:5
|
LL | let _x = 0u32 - 1;
| ^^^^^^^^ attempt to compute `0_u32 - 1_u32`, which would overflow
|
note: the lint level is defined here
--> $DIR/promoted_errors.rs:9:20
|
LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
| ^^^^^^^^^^^^^^^^^^^
warning: this operation will panic at runtime
--> $DIR/promoted_errors.rs:16:20
|
LL | println!("{}", 1 / (1 - 1));
| ^^^^^^^^^^^ attempt to divide `1_i32` by zero
|
note: the lint level is defined here
--> $DIR/promoted_errors.rs:9:41
|
LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
| ^^^^^^^^^^^^^^^^^^^
warning: reaching this expression at runtime will panic or abort
--> $DIR/promoted_errors.rs:16:20
|
LL | println!("{}", 1 / (1 - 1));
| ^^^^^^^^^^^ dividing by zero
LL | 1 / 0
| ^^^^^
| |
| attempt to divide `1_i32` by zero
| inside `div_by_zero1` at $DIR/promoted_errors.rs:17:5
| inside `X` at $DIR/promoted_errors.rs:33:29
...
LL | / const X: () = {
LL | | let _x: &'static u32 = &overflow();
LL | |
LL | | let _x: &'static i32 = &div_by_zero1();
... |
LL | | let _x: &'static i32 = &oob();
LL | | };
| |__-
|
note: the lint level is defined here
--> $DIR/promoted_errors.rs:9:9
@ -34,41 +23,18 @@ note: the lint level is defined here
LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
| ^^^^^^^^^
warning: erroneous constant used
--> $DIR/promoted_errors.rs:16:20
warning: any use of this value will cause an error
--> $DIR/promoted_errors.rs:33:28
|
LL | println!("{}", 1 / (1 - 1));
| ^^^^^^^^^^^ referenced constant has errors
LL | / const X: () = {
LL | | let _x: &'static u32 = &overflow();
LL | |
LL | | let _x: &'static i32 = &div_by_zero1();
| | ^^^^^^^^^^^^^^^ referenced constant has errors
... |
LL | | let _x: &'static i32 = &oob();
LL | | };
| |__-
warning: this operation will panic at runtime
--> $DIR/promoted_errors.rs:20:14
|
LL | let _x = 1 / (1 - 1);
| ^^^^^^^^^^^ attempt to divide `1_i32` by zero
warning: this operation will panic at runtime
--> $DIR/promoted_errors.rs:22:20
|
LL | println!("{}", 1 / (false as u32));
| ^^^^^^^^^^^^^^^^^^ attempt to divide `1_u32` by zero
warning: reaching this expression at runtime will panic or abort
--> $DIR/promoted_errors.rs:22:20
|
LL | println!("{}", 1 / (false as u32));
| ^^^^^^^^^^^^^^^^^^ dividing by zero
warning: erroneous constant used
--> $DIR/promoted_errors.rs:22:20
|
LL | println!("{}", 1 / (false as u32));
| ^^^^^^^^^^^^^^^^^^ referenced constant has errors
warning: this operation will panic at runtime
--> $DIR/promoted_errors.rs:26:14
|
LL | let _x = 1 / (false as u32);
| ^^^^^^^^^^^^^^^^^^ attempt to divide `1_u32` by zero
warning: 9 warnings emitted
warning: 2 warnings emitted

View File

@ -1,38 +1,21 @@
warning: this arithmetic operation will overflow
--> $DIR/promoted_errors.rs:12:20
warning: any use of this value will cause an error
--> $DIR/promoted_errors.rs:13:5
|
LL | println!("{}", 0u32 - 1);
| ^^^^^^^^ attempt to compute `0_u32 - 1_u32`, which would overflow
|
note: the lint level is defined here
--> $DIR/promoted_errors.rs:9:20
|
LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
| ^^^^^^^^^^^^^^^^^^^
warning: this arithmetic operation will overflow
--> $DIR/promoted_errors.rs:14:14
|
LL | let _x = 0u32 - 1;
| ^^^^^^^^ attempt to compute `0_u32 - 1_u32`, which would overflow
warning: this operation will panic at runtime
--> $DIR/promoted_errors.rs:16:20
|
LL | println!("{}", 1 / (1 - 1));
| ^^^^^^^^^^^ attempt to divide `1_i32` by zero
|
note: the lint level is defined here
--> $DIR/promoted_errors.rs:9:41
|
LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
| ^^^^^^^^^^^^^^^^^^^
warning: reaching this expression at runtime will panic or abort
--> $DIR/promoted_errors.rs:16:20
|
LL | println!("{}", 1 / (1 - 1));
| ^^^^^^^^^^^ dividing by zero
LL | 0 - 1
| ^^^^^
| |
| attempt to compute `0_u32 - 1_u32`, which would overflow
| inside `overflow` at $DIR/promoted_errors.rs:13:5
| inside `X` at $DIR/promoted_errors.rs:31:29
...
LL | / const X: () = {
LL | | let _x: &'static u32 = &overflow();
LL | |
LL | | let _x: &'static i32 = &div_by_zero1();
... |
LL | | let _x: &'static i32 = &oob();
LL | | };
| |__-
|
note: the lint level is defined here
--> $DIR/promoted_errors.rs:9:9
@ -40,41 +23,18 @@ note: the lint level is defined here
LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)]
| ^^^^^^^^^
warning: erroneous constant used
--> $DIR/promoted_errors.rs:16:20
warning: any use of this value will cause an error
--> $DIR/promoted_errors.rs:31:28
|
LL | println!("{}", 1 / (1 - 1));
| ^^^^^^^^^^^ referenced constant has errors
LL | / const X: () = {
LL | | let _x: &'static u32 = &overflow();
| | ^^^^^^^^^^^ referenced constant has errors
LL | |
LL | | let _x: &'static i32 = &div_by_zero1();
... |
LL | | let _x: &'static i32 = &oob();
LL | | };
| |__-
warning: this operation will panic at runtime
--> $DIR/promoted_errors.rs:20:14
|
LL | let _x = 1 / (1 - 1);
| ^^^^^^^^^^^ attempt to divide `1_i32` by zero
warning: this operation will panic at runtime
--> $DIR/promoted_errors.rs:22:20
|
LL | println!("{}", 1 / (false as u32));
| ^^^^^^^^^^^^^^^^^^ attempt to divide `1_u32` by zero
warning: reaching this expression at runtime will panic or abort
--> $DIR/promoted_errors.rs:22:20
|
LL | println!("{}", 1 / (false as u32));
| ^^^^^^^^^^^^^^^^^^ dividing by zero
warning: erroneous constant used
--> $DIR/promoted_errors.rs:22:20
|
LL | println!("{}", 1 / (false as u32));
| ^^^^^^^^^^^^^^^^^^ referenced constant has errors
warning: this operation will panic at runtime
--> $DIR/promoted_errors.rs:26:14
|
LL | let _x = 1 / (false as u32);
| ^^^^^^^^^^^^^^^^^^ attempt to divide `1_u32` by zero
warning: 10 warnings emitted
warning: 2 warnings emitted

View File

@ -8,21 +8,34 @@
#![warn(const_err, arithmetic_overflow, unconditional_panic)]
fn main() {
println!("{}", 0u32 - 1);
//[opt_with_overflow_checks,noopt]~^ WARN [arithmetic_overflow]
let _x = 0u32 - 1;
//~^ WARN [arithmetic_overflow]
println!("{}", 1 / (1 - 1));
//~^ WARN [unconditional_panic]
//~| WARN panic or abort [const_err]
//~| WARN erroneous constant used [const_err]
let _x = 1 / (1 - 1);
//~^ WARN [unconditional_panic]
println!("{}", 1 / (false as u32));
//~^ WARN [unconditional_panic]
//~| WARN panic or abort [const_err]
//~| WARN erroneous constant used [const_err]
let _x = 1 / (false as u32);
//~^ WARN [unconditional_panic]
// The only way to have promoteds that fail is in `const fn` called from `const`/`static`.
const fn overflow() -> u32 {
0 - 1
//[opt_with_overflow_checks,noopt]~^ WARN any use of this value will cause an error
}
const fn div_by_zero1() -> i32 {
1 / 0
//[opt]~^ WARN any use of this value will cause an error
}
const fn div_by_zero2() -> i32 {
1 / (1-1)
}
const fn div_by_zero3() -> i32 {
1 / (false as i32)
}
const fn oob() -> i32 {
[1,2,3][4]
}
const X: () = {
let _x: &'static u32 = &overflow();
//[opt_with_overflow_checks,noopt]~^ WARN any use of this value will cause an error
let _x: &'static i32 = &div_by_zero1();
//[opt]~^ WARN any use of this value will cause an error
let _x: &'static i32 = &div_by_zero2();
let _x: &'static i32 = &div_by_zero3();
let _x: &'static i32 = &oob();
};
fn main() {
}

View File

@ -44,4 +44,11 @@ fn main() {
// We must not promote things with interior mutability. Not even if we "project it away".
let _val: &'static _ = &(Cell::new(1), 2).0; //~ ERROR temporary value dropped while borrowed
let _val: &'static _ = &(Cell::new(1), 2).1; //~ ERROR temporary value dropped while borrowed
// No promotion of fallible operations.
let _val: &'static _ = &(1/0); //~ ERROR temporary value dropped while borrowed
let _val: &'static _ = &(1/(1-1)); //~ ERROR temporary value dropped while borrowed
let _val: &'static _ = &(1%0); //~ ERROR temporary value dropped while borrowed
let _val: &'static _ = &(1%(1-1)); //~ ERROR temporary value dropped while borrowed
let _val: &'static _ = &([1,2,3][4]+1); //~ ERROR temporary value dropped while borrowed
}

View File

@ -65,7 +65,7 @@ LL | let _val: &'static _ = &(Cell::new(1), 2).0;
| ---------- ^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
LL | let _val: &'static _ = &(Cell::new(1), 2).1;
...
LL | }
| - temporary value is freed at the end of this statement
@ -76,9 +76,64 @@ LL | let _val: &'static _ = &(Cell::new(1), 2).1;
| ---------- ^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
...
LL | }
| - temporary value is freed at the end of this statement
error: aborting due to 8 previous errors
error[E0716]: temporary value dropped while borrowed
--> $DIR/promote-not.rs:49:29
|
LL | let _val: &'static _ = &(1/0);
| ---------- ^^^^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
...
LL | }
| - temporary value is freed at the end of this statement
error[E0716]: temporary value dropped while borrowed
--> $DIR/promote-not.rs:50:29
|
LL | let _val: &'static _ = &(1/(1-1));
| ---------- ^^^^^^^^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
...
LL | }
| - temporary value is freed at the end of this statement
error[E0716]: temporary value dropped while borrowed
--> $DIR/promote-not.rs:51:29
|
LL | let _val: &'static _ = &(1%0);
| ---------- ^^^^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
...
LL | }
| - temporary value is freed at the end of this statement
error[E0716]: temporary value dropped while borrowed
--> $DIR/promote-not.rs:52:29
|
LL | let _val: &'static _ = &(1%(1-1));
| ---------- ^^^^^^^^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
LL | let _val: &'static _ = &([1,2,3][4]+1);
LL | }
| - temporary value is freed at the end of this statement
error[E0716]: temporary value dropped while borrowed
--> $DIR/promote-not.rs:53:29
|
LL | let _val: &'static _ = &([1,2,3][4]+1);
| ---------- ^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
LL | }
| - temporary value is freed at the end of this statement
error: aborting due to 13 previous errors
For more information about this error, try `rustc --explain E0716`.

View File

@ -1,9 +0,0 @@
#![allow(unconditional_panic, const_err)]
// run-fail
// error-pattern: attempt to divide by zero
// ignore-emscripten no processes
fn main() {
let x = &(1 / (1 - 1));
}

View File

@ -1,28 +1,43 @@
// check-pass
// revisions: noopt opt opt_with_overflow_checks
//[noopt]compile-flags: -C opt-level=0
//[opt]compile-flags: -O
//[opt_with_overflow_checks]compile-flags: -C overflow-checks=on -O
// compile-flags: -O
// build-pass
#[allow(arithmetic_overflow)]
fn foo(_: &'static [&'static str]) {}
fn bar(_: &'static [&'static str; 3]) {}
const fn baz_i32(_: &'static i32) {}
const fn baz_u32(_: &'static u32) {}
const fn assert_static<T>(_: &'static T) {}
const fn fail() -> i32 { 1/0 }
const C: i32 = {
// Promoted that fails to evaluate in dead code -- this must work
// (for backwards compatibility reasons).
if false {
baz_i32(&fail());
assert_static(&fail());
}
42
};
fn main() {
foo(&["a", "b", "c"]);
bar(&["d", "e", "f"]);
assert_static(&["a", "b", "c"]);
assert_static(&["d", "e", "f"]);
assert_eq!(C, 42);
// make sure that these do not cause trouble despite overflowing
baz_u32(&(0-1));
baz_i32(&-i32::MIN);
assert_static(&(0-1));
assert_static(&-i32::MIN);
// div-by-non-0 is okay
assert_static(&(1/1));
assert_static(&(1%1));
// in-bounds array access is okay
assert_static(&([1,2,3][0] + 1));
assert_static(&[[1,2][1]]);
// Top-level projections are not part of the promoted, so no error here.
if false {
#[allow(unconditional_panic)]
assert_static(&[1,2,3][4]);
}
}