Skip to content

Commit 9872160

Browse files
committed
Allow let bindings in const fn and constants
1 parent 6835748 commit 9872160

9 files changed

+193
-18
lines changed

src/librustc_mir/transform/qualify_consts.rs

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use rustc::middle::lang_items;
3232
use rustc_target::spec::abi::Abi;
3333
use syntax::attr;
3434
use syntax::ast::LitKind;
35-
use syntax::feature_gate::UnstableFeatures;
35+
use syntax::feature_gate::{UnstableFeatures, emit_feature_err, GateIssue};
3636
use syntax_pos::{Span, DUMMY_SP};
3737

3838
use std::fmt;
@@ -120,8 +120,7 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
120120
rpo: ReversePostorder<'a, 'tcx>,
121121
tcx: TyCtxt<'a, 'gcx, 'tcx>,
122122
param_env: ty::ParamEnv<'tcx>,
123-
temp_qualif: IndexVec<Local, Option<Qualif>>,
124-
return_qualif: Option<Qualif>,
123+
local_qualif: IndexVec<Local, Option<Qualif>>,
125124
qualif: Qualif,
126125
const_fn_arg_vars: BitVector,
127126
temp_promotion_state: IndexVec<Local, TempState>,
@@ -140,11 +139,11 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
140139

141140
let param_env = tcx.param_env(def_id);
142141

143-
let mut temp_qualif = IndexVec::from_elem(None, &mir.local_decls);
142+
let mut local_qualif = IndexVec::from_elem(None, &mir.local_decls);
144143
for arg in mir.args_iter() {
145144
let mut qualif = Qualif::NEEDS_DROP;
146145
qualif.restrict(mir.local_decls[arg].ty, tcx, param_env);
147-
temp_qualif[arg] = Some(qualif);
146+
local_qualif[arg] = Some(qualif);
148147
}
149148

150149
Qualifier {
@@ -155,8 +154,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
155154
rpo,
156155
tcx,
157156
param_env,
158-
temp_qualif,
159-
return_qualif: None,
157+
local_qualif,
160158
qualif: Qualif::empty(),
161159
const_fn_arg_vars: BitVector::new(mir.local_decls.len()),
162160
temp_promotion_state: temps,
@@ -191,6 +189,11 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
191189
fn statement_like(&mut self) {
192190
self.add(Qualif::NOT_CONST);
193191
if self.mode != Mode::Fn {
192+
if self.span.allows_unstable() {
193+
emit_feature_err(&self.tcx.sess.parse_sess, "const_let",
194+
self.span, GateIssue::Language,
195+
"statements in const fn are unstable");
196+
}
194197
let mut err = struct_span_err!(
195198
self.tcx.sess,
196199
self.span,
@@ -266,6 +269,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
266269

267270
/// Assign the current qualification to the given destination.
268271
fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
272+
trace!("assign: {:?}", dest);
269273
let qualif = self.qualif;
270274
let span = self.span;
271275
let store = |slot: &mut Option<Qualif>| {
@@ -281,28 +285,34 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
281285
if self.mir.local_kind(index) == LocalKind::Temp
282286
&& self.temp_promotion_state[index].is_promotable() {
283287
debug!("store to promotable temp {:?}", index);
284-
store(&mut self.temp_qualif[index]);
288+
store(&mut self.local_qualif[index]);
285289
}
286290
}
287291
return;
288292
}
289293

290294
match *dest {
295+
Place::Local(index) if (self.mir.local_kind(index) == LocalKind::Var ||
296+
self.mir.local_kind(index) == LocalKind::Arg) &&
297+
self.tcx.sess.features_untracked().const_let => {
298+
debug!("store to var {:?}", index);
299+
self.local_qualif[index] = Some(self.qualif);
300+
}
291301
Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp => {
292302
debug!("store to temp {:?}", index);
293-
store(&mut self.temp_qualif[index])
303+
store(&mut self.local_qualif[index])
294304
}
295305
Place::Local(index) if self.mir.local_kind(index) == LocalKind::ReturnPointer => {
296306
debug!("store to return place {:?}", index);
297-
store(&mut self.return_qualif)
307+
store(&mut self.local_qualif[RETURN_PLACE])
298308
}
299309

300310
Place::Projection(box Projection {
301311
base: Place::Local(index),
302312
elem: ProjectionElem::Deref
303313
}) if self.mir.local_kind(index) == LocalKind::Temp
304314
&& self.mir.local_decls[index].ty.is_box()
305-
&& self.temp_qualif[index].map_or(false, |qualif| {
315+
&& self.local_qualif[index].map_or(false, |qualif| {
306316
qualif.intersects(Qualif::NOT_CONST)
307317
}) => {
308318
// Part of `box expr`, we should've errored
@@ -355,10 +365,13 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
355365
TerminatorKind::FalseUnwind { .. } => None,
356366

357367
TerminatorKind::Return => {
368+
if self.tcx.sess.features_untracked().const_let {
369+
break;
370+
}
358371
// Check for unused values. This usually means
359372
// there are extra statements in the AST.
360373
for temp in mir.temps_iter() {
361-
if self.temp_qualif[temp].is_none() {
374+
if self.local_qualif[temp].is_none() {
362375
continue;
363376
}
364377

@@ -408,7 +421,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
408421
}
409422
}
410423

411-
self.qualif = self.return_qualif.unwrap_or(Qualif::NOT_CONST);
424+
self.qualif = self.local_qualif[RETURN_PLACE].unwrap_or(Qualif::NOT_CONST);
412425

413426
// Account for errors in consts by using the
414427
// conservative type qualification instead.
@@ -453,9 +466,15 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
453466
LocalKind::ReturnPointer => {
454467
self.not_const();
455468
}
456-
LocalKind::Var => {
469+
LocalKind::Var if !self.tcx.sess.features_untracked().const_let => {
470+
if self.mode != Mode::Fn && self.span.allows_unstable() {
471+
emit_feature_err(&self.tcx.sess.parse_sess, "const_let",
472+
self.span, GateIssue::Language,
473+
"let bindings in const fn are unstable");
474+
}
457475
self.add(Qualif::NOT_CONST);
458476
}
477+
LocalKind::Var |
459478
LocalKind::Arg |
460479
LocalKind::Temp => {
461480
if let LocalKind::Arg = kind {
@@ -466,7 +485,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
466485
self.add(Qualif::NOT_PROMOTABLE);
467486
}
468487

469-
if let Some(qualif) = self.temp_qualif[local] {
488+
if let Some(qualif) = self.local_qualif[local] {
470489
self.add(qualif);
471490
} else {
472491
self.not_const();
@@ -588,7 +607,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
588607

589608
// Mark the consumed locals to indicate later drops are noops.
590609
if let Operand::Move(Place::Local(local)) = *operand {
591-
self.temp_qualif[local] = self.temp_qualif[local].map(|q|
610+
self.local_qualif[local] = self.local_qualif[local].map(|q|
592611
q - Qualif::NEEDS_DROP
593612
);
594613
}
@@ -1033,7 +1052,7 @@ This does not pose a problem by itself because they can't be accessed directly."
10331052
// HACK(eddyb) Emulate a bit of dataflow analysis,
10341053
// conservatively, that drop elaboration will do.
10351054
let needs_drop = if let Place::Local(local) = *place {
1036-
if self.temp_qualif[local].map_or(true, |q| q.intersects(Qualif::NEEDS_DROP)) {
1055+
if self.local_qualif[local].map_or(true, |q| q.intersects(Qualif::NEEDS_DROP)) {
10371056
Some(self.mir.local_decls[local].source_info.span)
10381057
} else {
10391058
None
@@ -1070,7 +1089,8 @@ This does not pose a problem by itself because they can't be accessed directly."
10701089
// Check the allowed const fn argument forms.
10711090
if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) {
10721091
if self.mir.local_kind(index) == LocalKind::Var &&
1073-
self.const_fn_arg_vars.insert(index.index()) {
1092+
self.const_fn_arg_vars.insert(index.index()) &&
1093+
!self.tcx.sess.features_untracked().const_let {
10741094

10751095
// Direct use of an argument is permitted.
10761096
match *rvalue {
@@ -1086,6 +1106,11 @@ This does not pose a problem by itself because they can't be accessed directly."
10861106
// Avoid a generic error for other uses of arguments.
10871107
if self.qualif.intersects(Qualif::FN_ARGUMENT) {
10881108
let decl = &self.mir.local_decls[index];
1109+
if decl.source_info.span.allows_unstable() {
1110+
emit_feature_err(&self.tcx.sess.parse_sess, "const_let",
1111+
decl.source_info.span, GateIssue::Language,
1112+
"locals and patterns in const fn are unstable");
1113+
}
10891114
let mut err = struct_span_err!(
10901115
self.tcx.sess,
10911116
decl.source_info.span,

src/libsyntax/feature_gate.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,9 @@ declare_features! (
214214
// Allows the definition of `const fn` functions.
215215
(active, const_fn, "1.2.0", Some(24111), None),
216216

217+
// Allows let bindings and destructuring in `const fn` functions and constants.
218+
(active, const_let, "1.22.1", Some(48821), None),
219+
217220
// Allows using #[prelude_import] on glob `use` items.
218221
//
219222
// rustc internal
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2014 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_let)]
12+
13+
type Array = [u32; { let x = 2; 5 }];
14+
15+
pub fn main() {}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2014 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_let)]
12+
13+
enum Foo {
14+
Bar = { let x = 1; 3 }
15+
}
16+
17+
pub fn main() {}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2015 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+
// test that certain things are disallowed in const fn signatures
12+
13+
#![feature(const_fn, const_let)]
14+
15+
// no destructuring
16+
const fn i((
17+
a,
18+
b
19+
): (u32, u32)) -> u32 {
20+
a + b
21+
}
22+
23+
fn main() {}

src/test/run-pass/ctfe/issue-37550.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2017 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_fn, const_let)]
12+
13+
const fn x() {
14+
let t = true;
15+
let x = || t;
16+
}
17+
18+
fn main() {}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
// https://github.com/rust-lang/rust/issues/48821
12+
13+
#![feature(const_fn, const_let)]
14+
15+
const fn foo(i: usize) -> usize {
16+
let x = i;
17+
x
18+
}
19+
20+
static FOO: usize = foo(42);
21+
22+
const fn bar(mut i: usize) -> usize {
23+
i += 8;
24+
let x = &i;
25+
*x
26+
}
27+
28+
static BAR: usize = bar(42);
29+
30+
const fn boo(mut i: usize) -> usize {
31+
{
32+
let mut x = i;
33+
x += 10;
34+
i = x;
35+
}
36+
i
37+
}
38+
39+
static BOO: usize = boo(42);
40+
41+
fn main() {
42+
assert!(FOO == 42);
43+
assert!(BAR == 50);
44+
assert!(BOO == 52);
45+
}

src/test/ui/feature-gate-const_let.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
// Test use of const let without feature gate.
12+
13+
#![feature(const_fn)]
14+
15+
const fn foo() -> usize {
16+
let x = 42; //~ ERROR blocks in constant functions are limited to items and tail expressions
17+
42
18+
}
19+
20+
fn main() {}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0016]: blocks in constant functions are limited to items and tail expressions
2+
--> $DIR/feature-gate-const_let.rs:16:13
3+
|
4+
LL | let x = 42; //~ ERROR blocks in constant functions are limited to items and tail expressions
5+
| ^^
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0016`.

0 commit comments

Comments
 (0)