about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-03-05 17:46:58 +0000
committerbors <bors@rust-lang.org>2020-03-05 17:46:58 +0000
commit96bb8b31c81dc2394317f2f083c3acf8087efea1 (patch)
tree88176218be713c7786c332a5ab3d7980322c764c
parentc79f5f064725535f7520e693e69c65c3d0f2730f (diff)
parent527456e219d1d898eee53b2d0e29cdb296c7bab8 (diff)
downloadrust-96bb8b31c81dc2394317f2f083c3acf8087efea1.tar.gz
rust-96bb8b31c81dc2394317f2f083c3acf8087efea1.zip
Auto merge of #67260 - TheSamsa:const-limit, r=oli-obk
const limit for CTFE

I tried to tackle the first steps for this issue.
The active feature flag does link to the issue below, I think this has to change, because there should be a tracking issue?
https://github.com/TheSamsa/rust/blob/1679a7647da0de672bac26b716db82d16f3896a8/src/librustc_feature/active.rs#L530

Also, I only put up the storage of the limit like "recursion_limit" but created a seperate file in the same place. Since I guess the invocation happens seperately.
https://github.com/TheSamsa/rust/blob/const-limit/src/librustc/middle/const_limit.rs

If this does not hold up for the issue and since there is a time pressure, just reject it.

hopefully this does not put more load on you than I expected...
-rw-r--r--src/doc/unstable-book/src/language-features/const-eval-limit.md7
-rw-r--r--src/librustc/middle/limits.rs (renamed from src/librustc/middle/recursion_limit.rs)27
-rw-r--r--src/librustc/middle/mod.rs2
-rw-r--r--src/librustc_feature/active.rs3
-rw-r--r--src/librustc_feature/builtin_attrs.rs4
-rw-r--r--src/librustc_interface/passes.rs2
-rw-r--r--src/librustc_mir/const_eval/eval_queries.rs4
-rw-r--r--src/librustc_mir/const_eval/machine.rs18
-rw-r--r--src/librustc_session/session.rs4
-rw-r--r--src/librustc_span/symbol.rs1
-rw-r--r--src/test/ui/consts/const_limit/const_eval_limit_not_reached.rs15
-rw-r--r--src/test/ui/consts/const_limit/const_eval_limit_overflow.rs15
-rw-r--r--src/test/ui/consts/const_limit/const_eval_limit_overflow.stderr10
-rw-r--r--src/test/ui/consts/const_limit/const_eval_limit_reached.rs21
-rw-r--r--src/test/ui/consts/const_limit/const_eval_limit_reached.stderr16
-rw-r--r--src/test/ui/consts/const_limit/feature-gate-const_eval_limit.rs14
-rw-r--r--src/test/ui/consts/const_limit/feature-gate-const_eval_limit.stderr12
-rw-r--r--src/test/ui/recursion_limit/empty.rs4
-rw-r--r--src/test/ui/recursion_limit/empty.stderr4
-rw-r--r--src/test/ui/recursion_limit/invalid_digit.rs2
-rw-r--r--src/test/ui/recursion_limit/invalid_digit.stderr2
-rw-r--r--src/test/ui/recursion_limit/overflow.rs4
-rw-r--r--src/test/ui/recursion_limit/overflow.stderr4
-rw-r--r--src/test/ui/recursion_limit/zero.rs2
24 files changed, 163 insertions, 34 deletions
diff --git a/src/doc/unstable-book/src/language-features/const-eval-limit.md b/src/doc/unstable-book/src/language-features/const-eval-limit.md
new file mode 100644
index 00000000000..df68e83bcac
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/const-eval-limit.md
@@ -0,0 +1,7 @@
+# `const_eval_limit`
+
+The tracking issue for this feature is: [#67217]
+
+[#67217]: https://github.com/rust-lang/rust/issues/67217
+
+The `const_eval_limit` allows someone to limit the evaluation steps the CTFE undertakes to evaluate a `const fn`.
diff --git a/src/librustc/middle/recursion_limit.rs b/src/librustc/middle/limits.rs
index ae31a2cc63d..22e4f5ea222 100644
--- a/src/librustc/middle/recursion_limit.rs
+++ b/src/librustc/middle/limits.rs
@@ -1,9 +1,9 @@
-// Recursion limit.
-//
-// There are various parts of the compiler that must impose arbitrary limits
-// on how deeply they recurse to prevent stack overflow. Users can override
-// this via an attribute on the crate like `#![recursion_limit="22"]`. This pass
-// just peeks and looks for that attribute.
+//! Registering limits, recursion_limit, type_length_limit and const_eval_limit
+//!
+//! There are various parts of the compiler that must impose arbitrary limits
+//! on how deeply they recurse to prevent stack overflow. Users can override
+//! this via an attribute on the crate like `#![recursion_limit="22"]`. This pass
+//! just peeks and looks for that attribute.
 
 use crate::session::Session;
 use core::num::IntErrorKind;
@@ -16,6 +16,7 @@ use rustc_data_structures::sync::Once;
 pub fn update_limits(sess: &Session, krate: &ast::Crate) {
     update_limit(sess, krate, &sess.recursion_limit, sym::recursion_limit, 128);
     update_limit(sess, krate, &sess.type_length_limit, sym::type_length_limit, 1048576);
+    update_limit(sess, krate, &sess.const_eval_limit, sym::const_eval_limit, 1_000_000);
 }
 
 fn update_limit(
@@ -37,10 +38,8 @@ fn update_limit(
                     return;
                 }
                 Err(e) => {
-                    let mut err = sess.struct_span_err(
-                        attr.span,
-                        "`recursion_limit` must be a non-negative integer",
-                    );
+                    let mut err =
+                        sess.struct_span_err(attr.span, "`limit` must be a non-negative integer");
 
                     let value_span = attr
                         .meta()
@@ -49,11 +48,11 @@ fn update_limit(
                         .unwrap_or(attr.span);
 
                     let error_str = match e.kind() {
-                        IntErrorKind::Overflow => "`recursion_limit` is too large",
-                        IntErrorKind::Empty => "`recursion_limit` must be a non-negative integer",
+                        IntErrorKind::Overflow => "`limit` is too large",
+                        IntErrorKind::Empty => "`limit` must be a non-negative integer",
                         IntErrorKind::InvalidDigit => "not a valid integer",
-                        IntErrorKind::Underflow => bug!("`recursion_limit` should never underflow"),
-                        IntErrorKind::Zero => bug!("zero is a valid `recursion_limit`"),
+                        IntErrorKind::Underflow => bug!("`limit` should never underflow"),
+                        IntErrorKind::Zero => bug!("zero is a valid `limit`"),
                         kind => bug!("unimplemented IntErrorKind variant: {:?}", kind),
                     };
 
diff --git a/src/librustc/middle/mod.rs b/src/librustc/middle/mod.rs
index b20f2cf3a85..464488964af 100644
--- a/src/librustc/middle/mod.rs
+++ b/src/librustc/middle/mod.rs
@@ -28,8 +28,8 @@ pub mod lib_features {
         }
     }
 }
+pub mod limits;
 pub mod privacy;
-pub mod recursion_limit;
 pub mod region;
 pub mod resolve_lifetime;
 pub mod stability;
diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs
index 0082f4f1a6e..6517a22701b 100644
--- a/src/librustc_feature/active.rs
+++ b/src/librustc_feature/active.rs
@@ -552,6 +552,9 @@ declare_features! (
     /// Allows the use of `no_sanitize` attribute.
     (active, no_sanitize, "1.42.0", Some(39699), None),
 
+    // Allows limiting the evaluation steps of const expressions
+    (active, const_eval_limit, "1.43.0", Some(67217), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
diff --git a/src/librustc_feature/builtin_attrs.rs b/src/librustc_feature/builtin_attrs.rs
index e2e061c185c..c140adf64d5 100644
--- a/src/librustc_feature/builtin_attrs.rs
+++ b/src/librustc_feature/builtin_attrs.rs
@@ -239,6 +239,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // Limits:
     ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N")),
     ungated!(type_length_limit, CrateLevel, template!(NameValueStr: "N")),
+    gated!(
+        const_eval_limit, CrateLevel, template!(NameValueStr: "N"), const_eval_limit,
+        experimental!(const_eval_limit)
+    ),
 
     // Entry point:
     ungated!(main, Normal, template!(Word)),
diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs
index 1e9b76c3e14..29e9ea1833f 100644
--- a/src/librustc_interface/passes.rs
+++ b/src/librustc_interface/passes.rs
@@ -189,7 +189,7 @@ pub fn register_plugins<'a>(
     }
 
     sess.time("recursion_limit", || {
-        middle::recursion_limit::update_limits(sess, &krate);
+        middle::limits::update_limits(sess, &krate);
     });
 
     let mut lint_store = rustc_lint::new_lint_store(
diff --git a/src/librustc_mir/const_eval/eval_queries.rs b/src/librustc_mir/const_eval/eval_queries.rs
index 4d5464f774f..1bf748e66e2 100644
--- a/src/librustc_mir/const_eval/eval_queries.rs
+++ b/src/librustc_mir/const_eval/eval_queries.rs
@@ -89,7 +89,7 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
     InterpCx::new(
         tcx.at(span),
         param_env,
-        CompileTimeInterpreter::new(),
+        CompileTimeInterpreter::new(*tcx.sess.const_eval_limit.get()),
         MemoryExtra { can_access_statics },
     )
 }
@@ -297,7 +297,7 @@ pub fn const_eval_raw_provider<'tcx>(
     let mut ecx = InterpCx::new(
         tcx.at(span),
         key.param_env,
-        CompileTimeInterpreter::new(),
+        CompileTimeInterpreter::new(*tcx.sess.const_eval_limit.get()),
         MemoryExtra { can_access_statics: is_static },
     );
 
diff --git a/src/librustc_mir/const_eval/machine.rs b/src/librustc_mir/const_eval/machine.rs
index 25727b75faf..ed802983468 100644
--- a/src/librustc_mir/const_eval/machine.rs
+++ b/src/librustc_mir/const_eval/machine.rs
@@ -3,6 +3,7 @@ use rustc::ty::layout::HasTyCtxt;
 use rustc::ty::{self, Ty};
 use std::borrow::{Borrow, Cow};
 use std::collections::hash_map::Entry;
+use std::convert::TryFrom;
 use std::hash::Hash;
 
 use rustc_data_structures::fx::FxHashMap;
@@ -85,9 +86,6 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
     }
 }
 
-/// Number of steps until the detector even starts doing anything.
-/// Also, a warning is shown to the user when this number is reached.
-const STEPS_UNTIL_DETECTOR_ENABLED: isize = 1_000_000;
 /// The number of steps between loop detector snapshots.
 /// Should be a power of two for performance reasons.
 const DETECTOR_SNAPSHOT_PERIOD: isize = 256;
@@ -100,6 +98,8 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
     /// detector period.
     pub(super) steps_since_detector_enabled: isize,
 
+    pub(super) is_detector_enabled: bool,
+
     /// Extra state to detect loops.
     pub(super) loop_detector: snapshot::InfiniteLoopDetector<'mir, 'tcx>,
 }
@@ -111,10 +111,14 @@ pub struct MemoryExtra {
 }
 
 impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
-    pub(super) fn new() -> Self {
+    pub(super) fn new(const_eval_limit: usize) -> Self {
+        let steps_until_detector_enabled =
+            isize::try_from(const_eval_limit).unwrap_or(std::isize::MAX);
+
         CompileTimeInterpreter {
             loop_detector: Default::default(),
-            steps_since_detector_enabled: -STEPS_UNTIL_DETECTOR_ENABLED,
+            steps_since_detector_enabled: -steps_until_detector_enabled,
+            is_detector_enabled: const_eval_limit != 0,
         }
     }
 }
@@ -343,6 +347,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
     }
 
     fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
+        if !ecx.machine.is_detector_enabled {
+            return Ok(());
+        }
+
         {
             let steps = &mut ecx.machine.steps_since_detector_enabled;
 
diff --git a/src/librustc_session/session.rs b/src/librustc_session/session.rs
index 2fb7977dce9..173b120e1f6 100644
--- a/src/librustc_session/session.rs
+++ b/src/librustc_session/session.rs
@@ -88,6 +88,9 @@ pub struct Session {
     /// The maximum length of types during monomorphization.
     pub type_length_limit: Once<usize>,
 
+    /// The maximum blocks a const expression can evaluate.
+    pub const_eval_limit: Once<usize>,
+
     /// Map from imported macro spans (which consist of
     /// the localized span for the macro body) to the
     /// macro name and definition span in the source crate.
@@ -1053,6 +1056,7 @@ fn build_session_(
         features: Once::new(),
         recursion_limit: Once::new(),
         type_length_limit: Once::new(),
+        const_eval_limit: Once::new(),
         imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())),
         incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
         cgu_reuse_tracker,
diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs
index d6232f32f4c..c39f9f360c0 100644
--- a/src/librustc_span/symbol.rs
+++ b/src/librustc_span/symbol.rs
@@ -208,6 +208,7 @@ symbols! {
         console,
         const_compare_raw_pointers,
         const_constructor,
+        const_eval_limit,
         const_extern_fn,
         const_fn,
         const_fn_union,
diff --git a/src/test/ui/consts/const_limit/const_eval_limit_not_reached.rs b/src/test/ui/consts/const_limit/const_eval_limit_not_reached.rs
new file mode 100644
index 00000000000..4ed908312fb
--- /dev/null
+++ b/src/test/ui/consts/const_limit/const_eval_limit_not_reached.rs
@@ -0,0 +1,15 @@
+// check-pass
+#![feature(const_eval_limit)]
+#![const_eval_limit="1000"]
+
+const CONSTANT: usize = limit();
+
+fn main() {
+    assert_eq!(CONSTANT, 1764);
+}
+
+const fn limit() -> usize {
+    let x = 42;
+
+    x * 42
+}
diff --git a/src/test/ui/consts/const_limit/const_eval_limit_overflow.rs b/src/test/ui/consts/const_limit/const_eval_limit_overflow.rs
new file mode 100644
index 00000000000..1c49593cd53
--- /dev/null
+++ b/src/test/ui/consts/const_limit/const_eval_limit_overflow.rs
@@ -0,0 +1,15 @@
+#![feature(const_eval_limit)]
+#![const_eval_limit="18_446_744_073_709_551_615"]
+//~^ ERROR `limit` must be a non-negative integer
+
+const CONSTANT: usize = limit();
+
+fn main() {
+    assert_eq!(CONSTANT, 1764);
+}
+
+const fn limit() -> usize {
+    let x = 42;
+
+    x * 42
+}
diff --git a/src/test/ui/consts/const_limit/const_eval_limit_overflow.stderr b/src/test/ui/consts/const_limit/const_eval_limit_overflow.stderr
new file mode 100644
index 00000000000..7f5d5e6cd4c
--- /dev/null
+++ b/src/test/ui/consts/const_limit/const_eval_limit_overflow.stderr
@@ -0,0 +1,10 @@
+error: `limit` must be a non-negative integer
+  --> $DIR/const_eval_limit_overflow.rs:2:1
+   |
+LL | #![const_eval_limit="18_446_744_073_709_551_615"]
+   | ^^^^^^^^^^^^^^^^^^^^----------------------------^
+   |                     |
+   |                     not a valid integer
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/consts/const_limit/const_eval_limit_reached.rs b/src/test/ui/consts/const_limit/const_eval_limit_reached.rs
new file mode 100644
index 00000000000..d962398d413
--- /dev/null
+++ b/src/test/ui/consts/const_limit/const_eval_limit_reached.rs
@@ -0,0 +1,21 @@
+// ignore-tidy-linelength
+// only-x86_64
+// check-pass
+// NOTE: We always compile this test with -Copt-level=0 because higher opt-levels
+//       optimize away the const function
+// compile-flags:-Copt-level=0
+#![feature(const_eval_limit)]
+#![const_eval_limit="2"]
+
+const CONSTANT: usize = limit();
+//~^ WARNING Constant evaluating a complex constant, this might take some time
+
+fn main() {
+    assert_eq!(CONSTANT, 1764);
+}
+
+const fn limit() -> usize { //~ WARNING Constant evaluating a complex constant, this might take some time
+    let x = 42;
+
+    x * 42
+}
diff --git a/src/test/ui/consts/const_limit/const_eval_limit_reached.stderr b/src/test/ui/consts/const_limit/const_eval_limit_reached.stderr
new file mode 100644
index 00000000000..e0871ff7185
--- /dev/null
+++ b/src/test/ui/consts/const_limit/const_eval_limit_reached.stderr
@@ -0,0 +1,16 @@
+warning: Constant evaluating a complex constant, this might take some time
+  --> $DIR/const_eval_limit_reached.rs:17:1
+   |
+LL | / const fn limit() -> usize {
+LL | |     let x = 42;
+LL | |
+LL | |     x * 42
+LL | | }
+   | |_^
+
+warning: Constant evaluating a complex constant, this might take some time
+  --> $DIR/const_eval_limit_reached.rs:10:1
+   |
+LL | const CONSTANT: usize = limit();
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
diff --git a/src/test/ui/consts/const_limit/feature-gate-const_eval_limit.rs b/src/test/ui/consts/const_limit/feature-gate-const_eval_limit.rs
new file mode 100644
index 00000000000..61119d7511d
--- /dev/null
+++ b/src/test/ui/consts/const_limit/feature-gate-const_eval_limit.rs
@@ -0,0 +1,14 @@
+#![const_eval_limit="42"]
+//~^ ERROR the `#[const_eval_limit]` attribute is an experimental feature [E0658]
+
+const CONSTANT: usize = limit();
+
+fn main() {
+    assert_eq!(CONSTANT, 1764);
+}
+
+const fn limit() -> usize {
+    let x = 42;
+
+    x * 42
+}
diff --git a/src/test/ui/consts/const_limit/feature-gate-const_eval_limit.stderr b/src/test/ui/consts/const_limit/feature-gate-const_eval_limit.stderr
new file mode 100644
index 00000000000..5bd29c7dfd2
--- /dev/null
+++ b/src/test/ui/consts/const_limit/feature-gate-const_eval_limit.stderr
@@ -0,0 +1,12 @@
+error[E0658]: the `#[const_eval_limit]` attribute is an experimental feature
+  --> $DIR/feature-gate-const_eval_limit.rs:1:1
+   |
+LL | #![const_eval_limit="42"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #67217 <https://github.com/rust-lang/rust/issues/67217> for more information
+   = help: add `#![feature(const_eval_limit)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/recursion_limit/empty.rs b/src/test/ui/recursion_limit/empty.rs
index 2a064f3e115..31ff9c1e3a7 100644
--- a/src/test/ui/recursion_limit/empty.rs
+++ b/src/test/ui/recursion_limit/empty.rs
@@ -1,6 +1,6 @@
 // Test the parse error for an empty recursion_limit
 
-#![recursion_limit = ""] //~ ERROR `recursion_limit` must be a non-negative integer
-                         //~| `recursion_limit` must be a non-negative integer
+#![recursion_limit = ""] //~ ERROR `limit` must be a non-negative integer
+                         //~| `limit` must be a non-negative integer
 
 fn main() {}
diff --git a/src/test/ui/recursion_limit/empty.stderr b/src/test/ui/recursion_limit/empty.stderr
index 690c33a7463..bcd1d27e59b 100644
--- a/src/test/ui/recursion_limit/empty.stderr
+++ b/src/test/ui/recursion_limit/empty.stderr
@@ -1,10 +1,10 @@
-error: `recursion_limit` must be a non-negative integer
+error: `limit` must be a non-negative integer
   --> $DIR/empty.rs:3:1
    |
 LL | #![recursion_limit = ""]
    | ^^^^^^^^^^^^^^^^^^^^^--^
    |                      |
-   |                      `recursion_limit` must be a non-negative integer
+   |                      `limit` must be a non-negative integer
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/recursion_limit/invalid_digit.rs b/src/test/ui/recursion_limit/invalid_digit.rs
index 903d8040476..759d69d0af2 100644
--- a/src/test/ui/recursion_limit/invalid_digit.rs
+++ b/src/test/ui/recursion_limit/invalid_digit.rs
@@ -1,6 +1,6 @@
 // Test the parse error for an invalid digit in recursion_limit
 
-#![recursion_limit = "-100"] //~ ERROR `recursion_limit` must be a non-negative integer
+#![recursion_limit = "-100"] //~ ERROR `limit` must be a non-negative integer
                              //~| not a valid integer
 
 fn main() {}
diff --git a/src/test/ui/recursion_limit/invalid_digit.stderr b/src/test/ui/recursion_limit/invalid_digit.stderr
index 1dcfea547c0..e6fd6b72a09 100644
--- a/src/test/ui/recursion_limit/invalid_digit.stderr
+++ b/src/test/ui/recursion_limit/invalid_digit.stderr
@@ -1,4 +1,4 @@
-error: `recursion_limit` must be a non-negative integer
+error: `limit` must be a non-negative integer
   --> $DIR/invalid_digit.rs:3:1
    |
 LL | #![recursion_limit = "-100"]
diff --git a/src/test/ui/recursion_limit/overflow.rs b/src/test/ui/recursion_limit/overflow.rs
index 6487b1350aa..8eee2792b23 100644
--- a/src/test/ui/recursion_limit/overflow.rs
+++ b/src/test/ui/recursion_limit/overflow.rs
@@ -1,7 +1,7 @@
 // Test the parse error for an overflowing recursion_limit
 
 #![recursion_limit = "999999999999999999999999"]
-//~^ ERROR `recursion_limit` must be a non-negative integer
-//~| `recursion_limit` is too large
+//~^ ERROR `limit` must be a non-negative integer
+//~| `limit` is too large
 
 fn main() {}
diff --git a/src/test/ui/recursion_limit/overflow.stderr b/src/test/ui/recursion_limit/overflow.stderr
index c3fc11989dc..f6ed76c1ebc 100644
--- a/src/test/ui/recursion_limit/overflow.stderr
+++ b/src/test/ui/recursion_limit/overflow.stderr
@@ -1,10 +1,10 @@
-error: `recursion_limit` must be a non-negative integer
+error: `limit` must be a non-negative integer
   --> $DIR/overflow.rs:3:1
    |
 LL | #![recursion_limit = "999999999999999999999999"]
    | ^^^^^^^^^^^^^^^^^^^^^--------------------------^
    |                      |
-   |                      `recursion_limit` is too large
+   |                      `limit` is too large
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/recursion_limit/zero.rs b/src/test/ui/recursion_limit/zero.rs
index f7199944e00..eb95d7babc6 100644
--- a/src/test/ui/recursion_limit/zero.rs
+++ b/src/test/ui/recursion_limit/zero.rs
@@ -1,4 +1,4 @@
-// Test that a `recursion_limit` of 0 is valid
+// Test that a `limit` of 0 is valid
 
 #![recursion_limit = "0"]