about summary refs log tree commit diff
diff options
context:
space:
mode:
authorOliver Schneider <git-no-reply-9879165716479413131@oli-obk.de>2018-03-27 21:16:37 +0200
committerOliver Schneider <git-no-reply-9879165716479413131@oli-obk.de>2018-05-22 10:54:05 +0200
commit9872160836291d880852986841afe83a57f08045 (patch)
tree8dcda1ef8b6ca6c7a00c0fe1dd49d9baed8ac26d
parent6835748725b9bd892d5b64d20ded4ced973ed3b7 (diff)
downloadrust-9872160836291d880852986841afe83a57f08045.tar.gz
rust-9872160836291d880852986841afe83a57f08045.zip
Allow let bindings in const fn and constants
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs61
-rw-r--r--src/libsyntax/feature_gate.rs3
-rw-r--r--src/test/run-pass/ctfe/const-block-non-item-statement-3.rs15
-rw-r--r--src/test/run-pass/ctfe/const-block-non-item-statement.rs17
-rw-r--r--src/test/run-pass/ctfe/const-fn-destructuring-arg.rs23
-rw-r--r--src/test/run-pass/ctfe/issue-37550.rs18
-rw-r--r--src/test/run-pass/ctfe/locals-in-const-fn.rs45
-rw-r--r--src/test/ui/feature-gate-const_let.rs20
-rw-r--r--src/test/ui/feature-gate-const_let.stderr9
9 files changed, 193 insertions, 18 deletions
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index fd4ba1d7562..e79f3ac9d11 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -32,7 +32,7 @@ use rustc::middle::lang_items;
 use rustc_target::spec::abi::Abi;
 use syntax::attr;
 use syntax::ast::LitKind;
-use syntax::feature_gate::UnstableFeatures;
+use syntax::feature_gate::{UnstableFeatures, emit_feature_err, GateIssue};
 use syntax_pos::{Span, DUMMY_SP};
 
 use std::fmt;
@@ -120,8 +120,7 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     rpo: ReversePostorder<'a, 'tcx>,
     tcx: TyCtxt<'a, 'gcx, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
-    temp_qualif: IndexVec<Local, Option<Qualif>>,
-    return_qualif: Option<Qualif>,
+    local_qualif: IndexVec<Local, Option<Qualif>>,
     qualif: Qualif,
     const_fn_arg_vars: BitVector,
     temp_promotion_state: IndexVec<Local, TempState>,
@@ -140,11 +139,11 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
 
         let param_env = tcx.param_env(def_id);
 
-        let mut temp_qualif = IndexVec::from_elem(None, &mir.local_decls);
+        let mut local_qualif = IndexVec::from_elem(None, &mir.local_decls);
         for arg in mir.args_iter() {
             let mut qualif = Qualif::NEEDS_DROP;
             qualif.restrict(mir.local_decls[arg].ty, tcx, param_env);
-            temp_qualif[arg] = Some(qualif);
+            local_qualif[arg] = Some(qualif);
         }
 
         Qualifier {
@@ -155,8 +154,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
             rpo,
             tcx,
             param_env,
-            temp_qualif,
-            return_qualif: None,
+            local_qualif,
             qualif: Qualif::empty(),
             const_fn_arg_vars: BitVector::new(mir.local_decls.len()),
             temp_promotion_state: temps,
@@ -191,6 +189,11 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
     fn statement_like(&mut self) {
         self.add(Qualif::NOT_CONST);
         if self.mode != Mode::Fn {
+            if self.span.allows_unstable() {
+                emit_feature_err(&self.tcx.sess.parse_sess, "const_let",
+                                self.span, GateIssue::Language,
+                                "statements in const fn are unstable");
+            }
             let mut err = struct_span_err!(
                 self.tcx.sess,
                 self.span,
@@ -266,6 +269,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
 
     /// Assign the current qualification to the given destination.
     fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
+        trace!("assign: {:?}", dest);
         let qualif = self.qualif;
         let span = self.span;
         let store = |slot: &mut Option<Qualif>| {
@@ -281,20 +285,26 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                 if self.mir.local_kind(index) == LocalKind::Temp
                 && self.temp_promotion_state[index].is_promotable() {
                     debug!("store to promotable temp {:?}", index);
-                    store(&mut self.temp_qualif[index]);
+                    store(&mut self.local_qualif[index]);
                 }
             }
             return;
         }
 
         match *dest {
+            Place::Local(index) if (self.mir.local_kind(index) == LocalKind::Var ||
+                                   self.mir.local_kind(index) == LocalKind::Arg) &&
+                                   self.tcx.sess.features_untracked().const_let => {
+                debug!("store to var {:?}", index);
+                self.local_qualif[index] = Some(self.qualif);
+            }
             Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp => {
                 debug!("store to temp {:?}", index);
-                store(&mut self.temp_qualif[index])
+                store(&mut self.local_qualif[index])
             }
             Place::Local(index) if self.mir.local_kind(index) == LocalKind::ReturnPointer => {
                 debug!("store to return place {:?}", index);
-                store(&mut self.return_qualif)
+                store(&mut self.local_qualif[RETURN_PLACE])
             }
 
             Place::Projection(box Projection {
@@ -302,7 +312,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                 elem: ProjectionElem::Deref
             }) if self.mir.local_kind(index) == LocalKind::Temp
                && self.mir.local_decls[index].ty.is_box()
-               && self.temp_qualif[index].map_or(false, |qualif| {
+               && self.local_qualif[index].map_or(false, |qualif| {
                     qualif.intersects(Qualif::NOT_CONST)
                }) => {
                 // Part of `box expr`, we should've errored
@@ -355,10 +365,13 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                 TerminatorKind::FalseUnwind { .. } => None,
 
                 TerminatorKind::Return => {
+                    if self.tcx.sess.features_untracked().const_let {
+                        break;
+                    }
                     // Check for unused values. This usually means
                     // there are extra statements in the AST.
                     for temp in mir.temps_iter() {
-                        if self.temp_qualif[temp].is_none() {
+                        if self.local_qualif[temp].is_none() {
                             continue;
                         }
 
@@ -408,7 +421,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
             }
         }
 
-        self.qualif = self.return_qualif.unwrap_or(Qualif::NOT_CONST);
+        self.qualif = self.local_qualif[RETURN_PLACE].unwrap_or(Qualif::NOT_CONST);
 
         // Account for errors in consts by using the
         // conservative type qualification instead.
@@ -453,9 +466,15 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
             LocalKind::ReturnPointer => {
                 self.not_const();
             }
-            LocalKind::Var => {
+            LocalKind::Var if !self.tcx.sess.features_untracked().const_let => {
+                if self.mode != Mode::Fn && self.span.allows_unstable() {
+                    emit_feature_err(&self.tcx.sess.parse_sess, "const_let",
+                                    self.span, GateIssue::Language,
+                                    "let bindings in const fn are unstable");
+                }
                 self.add(Qualif::NOT_CONST);
             }
+            LocalKind::Var |
             LocalKind::Arg |
             LocalKind::Temp => {
                 if let LocalKind::Arg = kind {
@@ -466,7 +485,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                     self.add(Qualif::NOT_PROMOTABLE);
                 }
 
-                if let Some(qualif) = self.temp_qualif[local] {
+                if let Some(qualif) = self.local_qualif[local] {
                     self.add(qualif);
                 } else {
                     self.not_const();
@@ -588,7 +607,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
 
                 // Mark the consumed locals to indicate later drops are noops.
                 if let Operand::Move(Place::Local(local)) = *operand {
-                    self.temp_qualif[local] = self.temp_qualif[local].map(|q|
+                    self.local_qualif[local] = self.local_qualif[local].map(|q|
                         q - Qualif::NEEDS_DROP
                     );
                 }
@@ -1033,7 +1052,7 @@ This does not pose a problem by itself because they can't be accessed directly."
                 // HACK(eddyb) Emulate a bit of dataflow analysis,
                 // conservatively, that drop elaboration will do.
                 let needs_drop = if let Place::Local(local) = *place {
-                    if self.temp_qualif[local].map_or(true, |q| q.intersects(Qualif::NEEDS_DROP)) {
+                    if self.local_qualif[local].map_or(true, |q| q.intersects(Qualif::NEEDS_DROP)) {
                         Some(self.mir.local_decls[local].source_info.span)
                     } else {
                         None
@@ -1070,7 +1089,8 @@ This does not pose a problem by itself because they can't be accessed directly."
         // Check the allowed const fn argument forms.
         if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) {
             if self.mir.local_kind(index) == LocalKind::Var &&
-               self.const_fn_arg_vars.insert(index.index()) {
+               self.const_fn_arg_vars.insert(index.index()) &&
+               !self.tcx.sess.features_untracked().const_let {
 
                 // Direct use of an argument is permitted.
                 match *rvalue {
@@ -1086,6 +1106,11 @@ This does not pose a problem by itself because they can't be accessed directly."
                 // Avoid a generic error for other uses of arguments.
                 if self.qualif.intersects(Qualif::FN_ARGUMENT) {
                     let decl = &self.mir.local_decls[index];
+                    if decl.source_info.span.allows_unstable() {
+                        emit_feature_err(&self.tcx.sess.parse_sess, "const_let",
+                                        decl.source_info.span, GateIssue::Language,
+                                        "locals and patterns in const fn are unstable");
+                    }
                     let mut err = struct_span_err!(
                         self.tcx.sess,
                         decl.source_info.span,
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 709c3653b02..3a02646d0af 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -214,6 +214,9 @@ declare_features! (
     // Allows the definition of `const fn` functions.
     (active, const_fn, "1.2.0", Some(24111), None),
 
+    // Allows let bindings and destructuring in `const fn` functions and constants.
+    (active, const_let, "1.22.1", Some(48821), None),
+
     // Allows using #[prelude_import] on glob `use` items.
     //
     // rustc internal
diff --git a/src/test/run-pass/ctfe/const-block-non-item-statement-3.rs b/src/test/run-pass/ctfe/const-block-non-item-statement-3.rs
new file mode 100644
index 00000000000..e233107169c
--- /dev/null
+++ b/src/test/run-pass/ctfe/const-block-non-item-statement-3.rs
@@ -0,0 +1,15 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(const_let)]
+
+type Array = [u32; {  let x = 2; 5 }];
+
+pub fn main() {}
diff --git a/src/test/run-pass/ctfe/const-block-non-item-statement.rs b/src/test/run-pass/ctfe/const-block-non-item-statement.rs
new file mode 100644
index 00000000000..b5a9bfb45a1
--- /dev/null
+++ b/src/test/run-pass/ctfe/const-block-non-item-statement.rs
@@ -0,0 +1,17 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(const_let)]
+
+enum Foo {
+    Bar = { let x = 1; 3 }
+}
+
+pub fn main() {}
diff --git a/src/test/run-pass/ctfe/const-fn-destructuring-arg.rs b/src/test/run-pass/ctfe/const-fn-destructuring-arg.rs
new file mode 100644
index 00000000000..a73a15b1762
--- /dev/null
+++ b/src/test/run-pass/ctfe/const-fn-destructuring-arg.rs
@@ -0,0 +1,23 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// test that certain things are disallowed in const fn signatures
+
+#![feature(const_fn, const_let)]
+
+// no destructuring
+const fn i((
+            a,
+            b
+           ): (u32, u32)) -> u32 {
+    a + b
+}
+
+fn main() {}
diff --git a/src/test/run-pass/ctfe/issue-37550.rs b/src/test/run-pass/ctfe/issue-37550.rs
new file mode 100644
index 00000000000..27796a5feea
--- /dev/null
+++ b/src/test/run-pass/ctfe/issue-37550.rs
@@ -0,0 +1,18 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(const_fn, const_let)]
+
+const fn x() {
+    let t = true;
+    let x = || t;
+}
+
+fn main() {}
diff --git a/src/test/run-pass/ctfe/locals-in-const-fn.rs b/src/test/run-pass/ctfe/locals-in-const-fn.rs
new file mode 100644
index 00000000000..8c153315c25
--- /dev/null
+++ b/src/test/run-pass/ctfe/locals-in-const-fn.rs
@@ -0,0 +1,45 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// https://github.com/rust-lang/rust/issues/48821
+
+#![feature(const_fn, const_let)]
+
+const fn foo(i: usize) -> usize {
+    let x = i;
+    x
+}
+
+static FOO: usize = foo(42);
+
+const fn bar(mut i: usize) -> usize {
+    i += 8;
+    let x = &i;
+    *x
+}
+
+static BAR: usize = bar(42);
+
+const fn boo(mut i: usize) -> usize {
+    {
+        let mut x = i;
+        x += 10;
+        i = x;
+    }
+    i
+}
+
+static BOO: usize = boo(42);
+
+fn main() {
+    assert!(FOO == 42);
+    assert!(BAR == 50);
+    assert!(BOO == 52);
+}
diff --git a/src/test/ui/feature-gate-const_let.rs b/src/test/ui/feature-gate-const_let.rs
new file mode 100644
index 00000000000..04d2fd5ccd1
--- /dev/null
+++ b/src/test/ui/feature-gate-const_let.rs
@@ -0,0 +1,20 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test use of const let without feature gate.
+
+#![feature(const_fn)]
+
+const fn foo() -> usize {
+    let x = 42; //~ ERROR blocks in constant functions are limited to items and tail expressions
+    42
+}
+
+fn main() {}
diff --git a/src/test/ui/feature-gate-const_let.stderr b/src/test/ui/feature-gate-const_let.stderr
new file mode 100644
index 00000000000..a07281ded8d
--- /dev/null
+++ b/src/test/ui/feature-gate-const_let.stderr
@@ -0,0 +1,9 @@
+error[E0016]: blocks in constant functions are limited to items and tail expressions
+  --> $DIR/feature-gate-const_let.rs:16:13
+   |
+LL |     let x = 42; //~ ERROR blocks in constant functions are limited to items and tail expressions
+   |             ^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0016`.