Skip to content

Commit 917945d

Browse files
committed
Auto merge of #52011 - oli-obk:dont_you_hate_it_too_when_everything_panics_constantly, r=eddyb
Allow panicking with string literal messages inside constants r? @eddyb cc #51999 we can't implement things like `panic!("foo: {}", x)` right now because we can't call trait methods (most notably `Display::fmt`) inside constants. Also most of these impls probably have loops and conditions, so it's messy anyway. But hey `panic!("foo")` works at least. cc @japaric got any test ideas for `#![no_std]`?
2 parents f1b506a + bd6ae6a commit 917945d

20 files changed

+368
-25
lines changed

src/librustc/ich/impls_ty.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,6 @@ for ::mir::interpret::EvalErrorKind<'gcx, O> {
536536
DeallocateNonBasePtr |
537537
HeapAllocZeroBytes |
538538
Unreachable |
539-
Panic |
540539
ReadFromReturnPointer |
541540
UnimplementedTraitSelection |
542541
TypeckError |
@@ -550,6 +549,12 @@ for ::mir::interpret::EvalErrorKind<'gcx, O> {
550549
GeneratorResumedAfterReturn |
551550
GeneratorResumedAfterPanic |
552551
InfiniteLoop => {}
552+
Panic { ref msg, ref file, line, col } => {
553+
msg.hash_stable(hcx, hasher);
554+
file.hash_stable(hcx, hasher);
555+
line.hash_stable(hcx, hasher);
556+
col.hash_stable(hcx, hasher);
557+
},
553558
ReferencedConstant(ref err) => err.hash_stable(hcx, hasher),
554559
MachineError(ref err) => err.hash_stable(hcx, hasher),
555560
FunctionPointerTyMismatch(a, b) => {

src/librustc/middle/lang_items.rs

+2
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,8 @@ language_item_table! {
305305
PanicBoundsCheckFnLangItem, "panic_bounds_check", panic_bounds_check_fn;
306306
PanicInfoLangItem, "panic_info", panic_info;
307307
PanicImplLangItem, "panic_impl", panic_impl;
308+
// Libstd panic entry point. Necessary for const eval to be able to catch it
309+
BeginPanicFnLangItem, "begin_panic", begin_panic_fn;
308310

309311
ExchangeMallocFnLangItem, "exchange_malloc", exchange_malloc_fn;
310312
BoxFreeFnLangItem, "box_free", box_free_fn;

src/librustc/mir/interpret/error.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use errors::DiagnosticBuilder;
1717

1818
use syntax_pos::Span;
1919
use syntax::ast;
20+
use syntax::symbol::Symbol;
2021

2122
pub type ConstEvalResult<'tcx> = Result<&'tcx ty::Const<'tcx>, Lrc<ConstEvalErr<'tcx>>>;
2223

@@ -250,7 +251,12 @@ pub enum EvalErrorKind<'tcx, O> {
250251
HeapAllocZeroBytes,
251252
HeapAllocNonPowerOfTwoAlignment(u64),
252253
Unreachable,
253-
Panic,
254+
Panic {
255+
msg: Symbol,
256+
line: u32,
257+
col: u32,
258+
file: Symbol,
259+
},
254260
ReadFromReturnPointer,
255261
PathNotFound(Vec<String>),
256262
UnimplementedTraitSelection,
@@ -370,7 +376,7 @@ impl<'tcx, O> EvalErrorKind<'tcx, O> {
370376
"tried to re-, de-, or allocate heap memory with alignment that is not a power of two",
371377
Unreachable =>
372378
"entered unreachable code",
373-
Panic =>
379+
Panic { .. } =>
374380
"the evaluated program panicked",
375381
ReadFromReturnPointer =>
376382
"tried to read from the return pointer",
@@ -465,6 +471,8 @@ impl<'tcx, O: fmt::Debug> fmt::Debug for EvalErrorKind<'tcx, O> {
465471
write!(f, "{}", inner),
466472
IncorrectAllocationInformation(size, size2, align, align2) =>
467473
write!(f, "incorrect alloc info: expected size {} and align {}, got size {} and align {}", size.bytes(), align.abi(), size2.bytes(), align2.abi()),
474+
Panic { ref msg, line, col, ref file } =>
475+
write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col),
468476
_ => write!(f, "{}", self.description()),
469477
}
470478
}

src/librustc/ty/structural_impls.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,11 @@ impl<'a, 'tcx, O: Lift<'tcx>> Lift<'tcx> for interpret::EvalErrorKind<'a, O> {
574574
HeapAllocZeroBytes => HeapAllocZeroBytes,
575575
HeapAllocNonPowerOfTwoAlignment(n) => HeapAllocNonPowerOfTwoAlignment(n),
576576
Unreachable => Unreachable,
577-
Panic => Panic,
577+
Panic { ref msg, ref file, line, col } => Panic {
578+
msg: msg.clone(),
579+
file: file.clone(),
580+
line, col,
581+
},
578582
ReadFromReturnPointer => ReadFromReturnPointer,
579583
PathNotFound(ref v) => PathNotFound(v.clone()),
580584
UnimplementedTraitSelection => UnimplementedTraitSelection,

src/librustc_mir/interpret/const_eval.rs

+81-14
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,22 @@ use rustc::hir;
55
use rustc::mir::interpret::ConstEvalErr;
66
use rustc::mir;
77
use rustc::ty::{self, TyCtxt, Instance};
8-
use rustc::ty::layout::{LayoutOf, Primitive, TyLayout};
8+
use rustc::ty::layout::{LayoutOf, Primitive, TyLayout, Size};
99
use rustc::ty::subst::Subst;
1010
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
1111

1212
use syntax::ast::Mutability;
1313
use syntax::source_map::Span;
1414
use syntax::source_map::DUMMY_SP;
15+
use syntax::symbol::Symbol;
1516

1617
use rustc::mir::interpret::{
1718
EvalResult, EvalError, EvalErrorKind, GlobalId,
1819
Scalar, AllocId, Allocation, ConstValue,
1920
};
2021
use super::{
2122
Place, PlaceExtra, PlaceTy, MemPlace, OpTy, Operand, Value,
22-
EvalContext, StackPopCleanup, Memory, MemoryKind
23+
EvalContext, StackPopCleanup, Memory, MemoryKind, MPlaceTy,
2324
};
2425

2526
pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
@@ -237,23 +238,56 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
237238
if !ecx.tcx.is_const_fn(instance.def_id()) {
238239
let def_id = instance.def_id();
239240
// Some fn calls are actually BinOp intrinsics
240-
let (op, oflo) = if let Some(op) = ecx.tcx.is_binop_lang_item(def_id) {
241-
op
241+
let _: ! = if let Some((op, oflo)) = ecx.tcx.is_binop_lang_item(def_id) {
242+
let (dest, bb) = destination.expect("128 lowerings can't diverge");
243+
let l = ecx.read_value(args[0])?;
244+
let r = ecx.read_value(args[1])?;
245+
if oflo {
246+
ecx.binop_with_overflow(op, l, r, dest)?;
247+
} else {
248+
ecx.binop_ignore_overflow(op, l, r, dest)?;
249+
}
250+
ecx.goto_block(bb);
251+
return Ok(true);
252+
} else if Some(def_id) == ecx.tcx.lang_items().panic_fn() {
253+
assert!(args.len() == 1);
254+
// &(&'static str, &'static str, u32, u32)
255+
let ptr = ecx.read_value(args[0])?;
256+
let place = ecx.ref_to_mplace(ptr)?;
257+
let (msg, file, line, col) = (
258+
place_field(ecx, 0, place)?,
259+
place_field(ecx, 1, place)?,
260+
place_field(ecx, 2, place)?,
261+
place_field(ecx, 3, place)?,
262+
);
263+
264+
let msg = to_str(ecx, msg)?;
265+
let file = to_str(ecx, file)?;
266+
let line = to_u32(line)?;
267+
let col = to_u32(col)?;
268+
return Err(EvalErrorKind::Panic { msg, file, line, col }.into());
269+
} else if Some(def_id) == ecx.tcx.lang_items().begin_panic_fn() {
270+
assert!(args.len() == 2);
271+
// &'static str, &(&'static str, u32, u32)
272+
let msg = ecx.read_value(args[0])?;
273+
let ptr = ecx.read_value(args[1])?;
274+
let place = ecx.ref_to_mplace(ptr)?;
275+
let (file, line, col) = (
276+
place_field(ecx, 0, place)?,
277+
place_field(ecx, 1, place)?,
278+
place_field(ecx, 2, place)?,
279+
);
280+
281+
let msg = to_str(ecx, msg.value)?;
282+
let file = to_str(ecx, file)?;
283+
let line = to_u32(line)?;
284+
let col = to_u32(col)?;
285+
return Err(EvalErrorKind::Panic { msg, file, line, col }.into());
242286
} else {
243287
return Err(
244288
ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
245289
);
246290
};
247-
let (dest, bb) = destination.expect("128 lowerings can't diverge");
248-
let l = ecx.read_value(args[0])?;
249-
let r = ecx.read_value(args[1])?;
250-
if oflo {
251-
ecx.binop_with_overflow(op, l, r, dest)?;
252-
} else {
253-
ecx.binop_ignore_overflow(op, l, r, dest)?;
254-
}
255-
ecx.goto_block(bb);
256-
return Ok(true);
257291
}
258292
let mir = match ecx.load_mir(instance.def) {
259293
Ok(mir) => mir,
@@ -412,6 +446,39 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
412446
}
413447
}
414448

449+
fn place_field<'a, 'tcx, 'mir>(
450+
ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
451+
i: u64,
452+
place: MPlaceTy<'tcx>,
453+
) -> EvalResult<'tcx, Value> {
454+
let place = ecx.mplace_field(place, i)?;
455+
Ok(ecx.try_read_value_from_mplace(place)?.expect("bad panic arg layout"))
456+
}
457+
458+
fn to_str<'a, 'tcx, 'mir>(
459+
ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
460+
val: Value,
461+
) -> EvalResult<'tcx, Symbol> {
462+
if let Value::ScalarPair(ptr, len) = val {
463+
let len = len.not_undef()?.to_bits(ecx.memory.pointer_size())?;
464+
let bytes = ecx.memory.read_bytes(ptr.not_undef()?, Size::from_bytes(len as u64))?;
465+
let str = ::std::str::from_utf8(bytes).map_err(|err| EvalErrorKind::ValidationFailure(err.to_string()))?;
466+
Ok(Symbol::intern(str))
467+
} else {
468+
bug!("panic arg is not a str")
469+
}
470+
}
471+
472+
fn to_u32<'a, 'tcx, 'mir>(
473+
val: Value,
474+
) -> EvalResult<'tcx, u32> {
475+
if let Value::Scalar(n) = val {
476+
Ok(n.not_undef()?.to_bits(Size::from_bits(32))? as u32)
477+
} else {
478+
bug!("panic arg is not a str")
479+
}
480+
}
481+
415482
/// Project to a field of a (variant of a) const
416483
pub fn const_field<'a, 'tcx>(
417484
tcx: TyCtxt<'a, 'tcx, 'tcx>,

src/librustc_mir/interpret/operand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ fn from_known_layout<'tcx>(
213213
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
214214
/// Try reading a value in memory; this is interesting particularily for ScalarPair.
215215
/// Return None if the layout does not permit loading this as a value.
216-
fn try_read_value_from_mplace(
216+
pub(super) fn try_read_value_from_mplace(
217217
&self,
218218
mplace: MPlaceTy<'tcx>,
219219
) -> EvalResult<'tcx, Option<Value>> {

src/librustc_mir/transform/const_prop.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
230230
// FIXME: implement
231231
=> {},
232232

233-
| Panic
233+
| Panic { .. }
234234
| BoundsCheck{..}
235235
| Overflow(_)
236236
| OverflowNeg

src/librustc_mir/transform/qualify_consts.rs

+24-2
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,11 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
400400

401401
(self.qualif, Lrc::new(promoted_temps))
402402
}
403+
404+
fn is_const_panic_fn(&self, def_id: DefId) -> bool {
405+
Some(def_id) == self.tcx.lang_items().panic_fn() ||
406+
Some(def_id) == self.tcx.lang_items().begin_panic_fn()
407+
}
403408
}
404409

405410
/// Accumulates an Rvalue or Call's effects in self.qualif.
@@ -834,7 +839,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
834839
}
835840
}
836841
_ => {
837-
if self.tcx.is_const_fn(def_id) {
842+
if self.tcx.is_const_fn(def_id) || self.is_const_panic_fn(def_id) {
838843
is_const_fn = Some(def_id);
839844
}
840845
}
@@ -880,8 +885,25 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
880885

881886
// Const fn calls.
882887
if let Some(def_id) = is_const_fn {
888+
// check the const_panic feature gate or
883889
// find corresponding rustc_const_unstable feature
884-
if let Some(&attr::Stability {
890+
// FIXME: cannot allow this inside `allow_internal_unstable` because that would make
891+
// `panic!` insta stable in constants, since the macro is marked with the attr
892+
if self.is_const_panic_fn(def_id) {
893+
if self.mode == Mode::Fn {
894+
// never promote panics
895+
self.qualif = Qualif::NOT_CONST;
896+
} else if !self.tcx.sess.features_untracked().const_panic {
897+
// don't allow panics in constants without the feature gate
898+
emit_feature_err(
899+
&self.tcx.sess.parse_sess,
900+
"const_panic",
901+
self.span,
902+
GateIssue::Language,
903+
&format!("panicking in {}s is unstable", self.mode),
904+
);
905+
}
906+
} else if let Some(&attr::Stability {
885907
rustc_const_unstable: Some(attr::RustcConstUnstable {
886908
feature: ref feature_name
887909
}),

src/libstd/panicking.rs

+1
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ fn continue_panic_fmt(info: &PanicInfo) -> ! {
397397
#[unstable(feature = "libstd_sys_internals",
398398
reason = "used by the panic! macro",
399399
issue = "0")]
400+
#[cfg_attr(not(any(stage0, test)), lang = "begin_panic")]
400401
#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
401402
pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! {
402403
// Note that this should be the only allocation performed in this code path.

src/libsyntax/feature_gate.rs

+3
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,9 @@ declare_features! (
224224
// Allows comparing raw pointers during const eval
225225
(active, const_compare_raw_pointers, "1.27.0", Some(53020), None),
226226

227+
// Allows panicking during const eval (produces compile-time errors)
228+
(active, const_panic, "1.30.0", Some(51999), None),
229+
227230
// Allows using #[prelude_import] on glob `use` items.
228231
//
229232
// rustc internal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(const_panic)]
12+
13+
fn main() {}
14+
15+
const Z: () = panic!("cheese");
16+
//~^ ERROR this constant cannot be used
17+
18+
const Y: () = unreachable!();
19+
//~^ ERROR this constant cannot be used
20+
21+
const X: () = unimplemented!();
22+
//~^ ERROR this constant cannot be used
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error: this constant cannot be used
2+
--> $DIR/const_panic.rs:15:1
3+
|
4+
LL | const Z: () = panic!("cheese");
5+
| ^^^^^^^^^^^^^^----------------^
6+
| |
7+
| the evaluated program panicked at 'cheese', $DIR/const_panic.rs:15:15
8+
|
9+
= note: #[deny(const_err)] on by default
10+
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
11+
12+
error: this constant cannot be used
13+
--> $DIR/const_panic.rs:18:1
14+
|
15+
LL | const Y: () = unreachable!();
16+
| ^^^^^^^^^^^^^^--------------^
17+
| |
18+
| the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic.rs:18:15
19+
|
20+
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
21+
22+
error: this constant cannot be used
23+
--> $DIR/const_panic.rs:21:1
24+
|
25+
LL | const X: () = unimplemented!();
26+
| ^^^^^^^^^^^^^^----------------^
27+
| |
28+
| the evaluated program panicked at 'not yet implemented', $DIR/const_panic.rs:21:15
29+
|
30+
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
31+
32+
error: aborting due to 3 previous errors
33+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![no_std]
12+
#![crate_type = "lib"]
13+
#![feature(const_panic)]
14+
15+
const Z: () = panic!("cheese");
16+
//~^ ERROR this constant cannot be used
17+
18+
const Y: () = unreachable!();
19+
//~^ ERROR this constant cannot be used
20+
21+
const X: () = unimplemented!();
22+
//~^ ERROR this constant cannot be used

0 commit comments

Comments
 (0)