diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 20052ad9bfcbd..658372ac336a8 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -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 } diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index cac5abb1059a8..d8758e045443c 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -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. diff --git a/src/test/ui/consts/array-literal-index-oob.rs b/src/test/ui/consts/array-literal-index-oob.rs index f36ebf38db4fe..9b3f735b1f849 100644 --- a/src/test/ui/consts/array-literal-index-oob.rs +++ b/src/test/ui/consts/array-literal-index-oob.rs @@ -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] } diff --git a/src/test/ui/consts/array-literal-index-oob.stderr b/src/test/ui/consts/array-literal-index-oob.stderr index 5916ea6d323e6..f96b8d48b3e7c 100644 --- a/src/test/ui/consts/array-literal-index-oob.stderr +++ b/src/test/ui/consts/array-literal-index-oob.stderr @@ -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 diff --git a/src/test/ui/consts/const-eval/const-eval-query-stack.rs b/src/test/ui/consts/const-eval/const-eval-query-stack.rs index 8a6f7de1c9fbd..39803c8f257e0 100644 --- a/src/test/ui/consts/const-eval/const-eval-query-stack.rs +++ b/src/test/ui/consts/const-eval/const-eval-query-stack.rs @@ -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); } diff --git a/src/test/ui/consts/const-eval/const-eval-query-stack.stderr b/src/test/ui/consts/const-eval/const-eval-query-stack.stderr index 8c57fd37e88f6..0016d301e598c 100644 --- a/src/test/ui/consts/const-eval/const-eval-query-stack.stderr +++ b/src/test/ui/consts/const-eval/const-eval-query-stack.stderr @@ -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: the lint level is defined here + --> $DIR/const-eval-query-stack.rs:19:8 | - = note: `#[deny(const_err)]` on by default +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 diff --git a/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr b/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr index ce83d8e9bb0c9..1cd1be5309b90 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr +++ b/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr @@ -1,38 +1,21 @@ -warning: this arithmetic operation will overflow - --> $DIR/promoted_errors.rs:12:20 - | -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 +warning: any use of this value will cause an error + --> $DIR/promoted_errors.rs:13:5 + | +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 - | -LL | println!("{}", 1 / (1 - 1)); - | ^^^^^^^^^^^ referenced constant has errors - -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 +warning: any use of this value will cause an error + --> $DIR/promoted_errors.rs:31:28 | -LL | let _x = 1 / (false as u32); - | ^^^^^^^^^^^^^^^^^^ attempt to divide `1_u32` by zero +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: 10 warnings emitted +warning: 2 warnings emitted diff --git a/src/test/ui/consts/const-eval/promoted_errors.opt.stderr b/src/test/ui/consts/const-eval/promoted_errors.opt.stderr index 2c66b175cfc2b..ca62e21b61385 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.opt.stderr +++ b/src/test/ui/consts/const-eval/promoted_errors.opt.stderr @@ -1,32 +1,21 @@ -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 - | -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 +warning: any use of this value will cause an error + --> $DIR/promoted_errors.rs:17:5 + | +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 - | -LL | println!("{}", 1 / (1 - 1)); - | ^^^^^^^^^^^ referenced constant has errors - -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 +warning: any use of this value will cause an error + --> $DIR/promoted_errors.rs:33:28 | -LL | let _x = 1 / (false as u32); - | ^^^^^^^^^^^^^^^^^^ attempt to divide `1_u32` by zero +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: 9 warnings emitted +warning: 2 warnings emitted diff --git a/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr b/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr index ce83d8e9bb0c9..1cd1be5309b90 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr +++ b/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr @@ -1,38 +1,21 @@ -warning: this arithmetic operation will overflow - --> $DIR/promoted_errors.rs:12:20 - | -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 +warning: any use of this value will cause an error + --> $DIR/promoted_errors.rs:13:5 + | +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 - | -LL | println!("{}", 1 / (1 - 1)); - | ^^^^^^^^^^^ referenced constant has errors - -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 +warning: any use of this value will cause an error + --> $DIR/promoted_errors.rs:31:28 | -LL | let _x = 1 / (false as u32); - | ^^^^^^^^^^^^^^^^^^ attempt to divide `1_u32` by zero +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: 10 warnings emitted +warning: 2 warnings emitted diff --git a/src/test/ui/consts/const-eval/promoted_errors.rs b/src/test/ui/consts/const-eval/promoted_errors.rs index 142ce75eebc80..a2136c8d09be4 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.rs +++ b/src/test/ui/consts/const-eval/promoted_errors.rs @@ -8,21 +8,34 @@ #![warn(const_err, arithmetic_overflow, 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() { - 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] } diff --git a/src/test/ui/consts/promote-not.rs b/src/test/ui/consts/promote-not.rs index 1e4d8586b872c..0d0c78b0fc260 100644 --- a/src/test/ui/consts/promote-not.rs +++ b/src/test/ui/consts/promote-not.rs @@ -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 } diff --git a/src/test/ui/consts/promote-not.stderr b/src/test/ui/consts/promote-not.stderr index 6e76d9ee6c165..108d0da7a674a 100644 --- a/src/test/ui/consts/promote-not.stderr +++ b/src/test/ui/consts/promote-not.stderr @@ -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[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 8 previous errors +error: aborting due to 13 previous errors For more information about this error, try `rustc --explain E0716`. diff --git a/src/test/ui/consts/promoted_div_by_zero.rs b/src/test/ui/consts/promoted_div_by_zero.rs deleted file mode 100644 index b4503f691ffd9..0000000000000 --- a/src/test/ui/consts/promoted_div_by_zero.rs +++ /dev/null @@ -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)); -} diff --git a/src/test/ui/consts/promotion.rs b/src/test/ui/consts/promotion.rs index b6e7127a9b779..580c6d62f10af 100644 --- a/src/test/ui/consts/promotion.rs +++ b/src/test/ui/consts/promotion.rs @@ -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(_: &'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]); + } }