Skip to content

Commit 9f80ea3

Browse files
committed
Auto merge of #49172 - oli-obk:const_let, r=eddyb
Allow let bindings and destructuring in constants and const fn r? @eddyb cc #48821
2 parents ff8fa5c + 2483c81 commit 9f80ea3

22 files changed

+354
-113
lines changed

src/librustc_mir/diagnostics.rs

-15
Original file line numberDiff line numberDiff line change
@@ -597,21 +597,6 @@ See [RFC 911] for more details on the design of `const fn`s.
597597
[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
598598
"##,
599599

600-
E0016: r##"
601-
Blocks in constants may only contain items (such as constant, function
602-
definition, etc...) and a tail expression. Erroneous code example:
603-
604-
```compile_fail,E0016
605-
const FOO: i32 = { let x = 0; x }; // 'x' isn't an item!
606-
```
607-
608-
To avoid it, you have to replace the non-item object:
609-
610-
```
611-
const FOO: i32 = { const X : i32 = 0; X };
612-
```
613-
"##,
614-
615600
E0017: r##"
616601
References in statics and constants may only refer to immutable values.
617602
Erroneous code example:

src/librustc_mir/transform/qualify_consts.rs

+73-61
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, feature_err, 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,12 +189,12 @@ 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 {
194-
let mut err = struct_span_err!(
195-
self.tcx.sess,
192+
let mut err = feature_err(
193+
&self.tcx.sess.parse_sess,
194+
"const_let",
196195
self.span,
197-
E0016,
198-
"blocks in {}s are limited to items and tail expressions",
199-
self.mode
196+
GateIssue::Language,
197+
&format!("statements in {}s are unstable", self.mode),
200198
);
201199
if self.tcx.sess.teach(&err.get_code().unwrap()) {
202200
err.note("Blocks in constants may only contain items (such as constant, function \
@@ -266,6 +264,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
266264

267265
/// Assign the current qualification to the given destination.
268266
fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
267+
trace!("assign: {:?}", dest);
269268
let qualif = self.qualif;
270269
let span = self.span;
271270
let store = |slot: &mut Option<Qualif>| {
@@ -281,28 +280,31 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
281280
if self.mir.local_kind(index) == LocalKind::Temp
282281
&& self.temp_promotion_state[index].is_promotable() {
283282
debug!("store to promotable temp {:?}", index);
284-
store(&mut self.temp_qualif[index]);
283+
store(&mut self.local_qualif[index]);
285284
}
286285
}
287286
return;
288287
}
289288

290289
match *dest {
291-
Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp => {
292-
debug!("store to temp {:?}", index);
293-
store(&mut self.temp_qualif[index])
290+
Place::Local(index) if (self.mir.local_kind(index) == LocalKind::Var ||
291+
self.mir.local_kind(index) == LocalKind::Arg) &&
292+
self.tcx.sess.features_untracked().const_let => {
293+
debug!("store to var {:?}", index);
294+
self.local_qualif[index] = Some(self.qualif);
294295
}
295-
Place::Local(index) if self.mir.local_kind(index) == LocalKind::ReturnPointer => {
296-
debug!("store to return place {:?}", index);
297-
store(&mut self.return_qualif)
296+
Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp ||
297+
self.mir.local_kind(index) == LocalKind::ReturnPointer => {
298+
debug!("store to {:?} (temp or return pointer)", index);
299+
store(&mut self.local_qualif[index])
298300
}
299301

300302
Place::Projection(box Projection {
301303
base: Place::Local(index),
302304
elem: ProjectionElem::Deref
303305
}) if self.mir.local_kind(index) == LocalKind::Temp
304306
&& self.mir.local_decls[index].ty.is_box()
305-
&& self.temp_qualif[index].map_or(false, |qualif| {
307+
&& self.local_qualif[index].map_or(false, |qualif| {
306308
qualif.intersects(Qualif::NOT_CONST)
307309
}) => {
308310
// Part of `box expr`, we should've errored
@@ -355,40 +357,42 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
355357
TerminatorKind::FalseUnwind { .. } => None,
356358

357359
TerminatorKind::Return => {
358-
// Check for unused values. This usually means
359-
// there are extra statements in the AST.
360-
for temp in mir.temps_iter() {
361-
if self.temp_qualif[temp].is_none() {
362-
continue;
363-
}
364-
365-
let state = self.temp_promotion_state[temp];
366-
if let TempState::Defined { location, uses: 0 } = state {
367-
let data = &mir[location.block];
368-
let stmt_idx = location.statement_index;
369-
370-
// Get the span for the initialization.
371-
let source_info = if stmt_idx < data.statements.len() {
372-
data.statements[stmt_idx].source_info
373-
} else {
374-
data.terminator().source_info
375-
};
376-
self.span = source_info.span;
360+
if !self.tcx.sess.features_untracked().const_let {
361+
// Check for unused values. This usually means
362+
// there are extra statements in the AST.
363+
for temp in mir.temps_iter() {
364+
if self.local_qualif[temp].is_none() {
365+
continue;
366+
}
377367

378-
// Treat this as a statement in the AST.
379-
self.statement_like();
368+
let state = self.temp_promotion_state[temp];
369+
if let TempState::Defined { location, uses: 0 } = state {
370+
let data = &mir[location.block];
371+
let stmt_idx = location.statement_index;
372+
373+
// Get the span for the initialization.
374+
let source_info = if stmt_idx < data.statements.len() {
375+
data.statements[stmt_idx].source_info
376+
} else {
377+
data.terminator().source_info
378+
};
379+
self.span = source_info.span;
380+
381+
// Treat this as a statement in the AST.
382+
self.statement_like();
383+
}
380384
}
381-
}
382385

383-
// Make sure there are no extra unassigned variables.
384-
self.qualif = Qualif::NOT_CONST;
385-
for index in mir.vars_iter() {
386-
if !self.const_fn_arg_vars.contains(index.index()) {
387-
debug!("unassigned variable {:?}", index);
388-
self.assign(&Place::Local(index), Location {
389-
block: bb,
390-
statement_index: usize::MAX,
391-
});
386+
// Make sure there are no extra unassigned variables.
387+
self.qualif = Qualif::NOT_CONST;
388+
for index in mir.vars_iter() {
389+
if !self.const_fn_arg_vars.contains(index.index()) {
390+
debug!("unassigned variable {:?}", index);
391+
self.assign(&Place::Local(index), Location {
392+
block: bb,
393+
statement_index: usize::MAX,
394+
});
395+
}
392396
}
393397
}
394398

@@ -408,7 +412,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
408412
}
409413
}
410414

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

413417
// Account for errors in consts by using the
414418
// conservative type qualification instead.
@@ -453,9 +457,15 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
453457
LocalKind::ReturnPointer => {
454458
self.not_const();
455459
}
456-
LocalKind::Var => {
460+
LocalKind::Var if !self.tcx.sess.features_untracked().const_let => {
461+
if self.mode != Mode::Fn {
462+
emit_feature_err(&self.tcx.sess.parse_sess, "const_let",
463+
self.span, GateIssue::Language,
464+
&format!("let bindings in {}s are unstable",self.mode));
465+
}
457466
self.add(Qualif::NOT_CONST);
458467
}
468+
LocalKind::Var |
459469
LocalKind::Arg |
460470
LocalKind::Temp => {
461471
if let LocalKind::Arg = kind {
@@ -466,7 +476,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
466476
self.add(Qualif::NOT_PROMOTABLE);
467477
}
468478

469-
if let Some(qualif) = self.temp_qualif[local] {
479+
if let Some(qualif) = self.local_qualif[local] {
470480
self.add(qualif);
471481
} else {
472482
self.not_const();
@@ -588,7 +598,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
588598

589599
// Mark the consumed locals to indicate later drops are noops.
590600
if let Operand::Move(Place::Local(local)) = *operand {
591-
self.temp_qualif[local] = self.temp_qualif[local].map(|q|
601+
self.local_qualif[local] = self.local_qualif[local].map(|q|
592602
q - Qualif::NEEDS_DROP
593603
);
594604
}
@@ -759,7 +769,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
759769
}
760770
if let Place::Local(local) = *place {
761771
if self.mir.local_kind(local) == LocalKind::Temp {
762-
if let Some(qualif) = self.temp_qualif[local] {
772+
if let Some(qualif) = self.local_qualif[local] {
763773
// `forbidden_mut` is false, so we can safely ignore
764774
// `MUTABLE_INTERIOR` from the local's qualifications.
765775
// This allows borrowing fields which don't have
@@ -1033,7 +1043,7 @@ This does not pose a problem by itself because they can't be accessed directly."
10331043
// HACK(eddyb) Emulate a bit of dataflow analysis,
10341044
// conservatively, that drop elaboration will do.
10351045
let needs_drop = if let Place::Local(local) = *place {
1036-
if self.temp_qualif[local].map_or(true, |q| q.intersects(Qualif::NEEDS_DROP)) {
1046+
if self.local_qualif[local].map_or(true, |q| q.intersects(Qualif::NEEDS_DROP)) {
10371047
Some(self.mir.local_decls[local].source_info.span)
10381048
} else {
10391049
None
@@ -1070,7 +1080,8 @@ This does not pose a problem by itself because they can't be accessed directly."
10701080
// Check the allowed const fn argument forms.
10711081
if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) {
10721082
if self.mir.local_kind(index) == LocalKind::Var &&
1073-
self.const_fn_arg_vars.insert(index.index()) {
1083+
self.const_fn_arg_vars.insert(index.index()) &&
1084+
!self.tcx.sess.features_untracked().const_let {
10741085

10751086
// Direct use of an argument is permitted.
10761087
match *rvalue {
@@ -1086,10 +1097,11 @@ This does not pose a problem by itself because they can't be accessed directly."
10861097
// Avoid a generic error for other uses of arguments.
10871098
if self.qualif.intersects(Qualif::FN_ARGUMENT) {
10881099
let decl = &self.mir.local_decls[index];
1089-
let mut err = struct_span_err!(
1090-
self.tcx.sess,
1100+
let mut err = feature_err(
1101+
&self.tcx.sess.parse_sess,
1102+
"const_let",
10911103
decl.source_info.span,
1092-
E0022,
1104+
GateIssue::Language,
10931105
"arguments of constant functions can only be immutable by-value bindings"
10941106
);
10951107
if self.tcx.sess.teach(&err.get_code().unwrap()) {

src/libsyntax/feature_gate.rs

+3
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

src/test/compile-fail/const-block-non-item-statement-2.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,20 @@
99
// except according to those terms.
1010

1111
const A: usize = { 1; 2 };
12-
//~^ ERROR: blocks in constants are limited to items and tail expressions
12+
//~^ ERROR statements in constants are unstable
1313

1414
const B: usize = { { } 2 };
15-
//~^ ERROR: blocks in constants are limited to items and tail expressions
15+
//~^ ERROR statements in constants are unstable
1616

1717
macro_rules! foo {
18-
() => (()) //~ ERROR: blocks in constants are limited to items and tail expressions
18+
() => (()) //~ ERROR statements in constants are unstable
1919
}
2020
const C: usize = { foo!(); 2 };
2121

2222
const D: usize = { let x = 4; 2 };
23-
//~^ ERROR: blocks in constants are limited to items and tail expressions
24-
//~^^ ERROR: blocks in constants are limited to items and tail expressions
23+
//~^ ERROR let bindings in constants are unstable
24+
//~| ERROR statements in constants are unstable
25+
//~| ERROR let bindings in constants are unstable
26+
//~| ERROR statements in constants are unstable
2527

2628
pub fn main() {}

src/test/compile-fail/const-block-non-item-statement-3.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
// except according to those terms.
1010

1111
type Array = [u32; { let x = 2; 5 }];
12-
//~^ ERROR: blocks in constants are limited to items and tail expressions
13-
//~^^ ERROR: blocks in constants are limited to items and tail expressions
12+
//~^ ERROR let bindings in constants are unstable
13+
//~| ERROR statements in constants are unstable
14+
//~| ERROR let bindings in constants are unstable
15+
//~| ERROR statements in constants are unstable
1416

1517
pub fn main() {}

src/test/compile-fail/const-block-non-item-statement.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010

1111
enum Foo {
1212
Bar = { let x = 1; 3 }
13-
//~^ ERROR: blocks in constants are limited to items and tail expressions
14-
//~^^ ERROR: blocks in constants are limited to items and tail expressions
13+
//~^ ERROR let bindings in constants are unstable
14+
//~| ERROR statements in constants are unstable
15+
//~| ERROR let bindings in constants are unstable
16+
//~| ERROR statements in constants are unstable
1517
}
1618

1719
pub fn main() {}

src/test/compile-fail/const-fn-destructuring-arg.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,20 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// test that certain things are disallowed in const fn signatures
11+
// test that certain things are disallowed in constant functions
1212

1313
#![feature(const_fn)]
1414

1515
// no destructuring
1616
const fn i((
17-
a, //~ ERROR: E0022
18-
b //~ ERROR: E0022
17+
a,
18+
//~^ ERROR arguments of constant functions can only be immutable by-value bindings
19+
b
20+
//~^ ERROR arguments of constant functions can only be immutable by-value bindings
1921
): (u32, u32)) -> u32 {
2022
a + b
23+
//~^ ERROR let bindings in constant functions are unstable
24+
//~| ERROR let bindings in constant functions are unstable
2125
}
2226

2327
fn main() {}

src/test/compile-fail/const-fn-not-safe-for-const.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,15 @@ const fn get_Y_addr() -> &'static u32 {
3838
}
3939

4040
const fn get() -> u32 {
41-
let x = 22; //~ ERROR E0016
42-
let y = 44; //~ ERROR E0016
41+
let x = 22;
42+
//~^ ERROR let bindings in constant functions are unstable
43+
//~| ERROR statements in constant functions are unstable
44+
let y = 44;
45+
//~^ ERROR let bindings in constant functions are unstable
46+
//~| ERROR statements in constant functions are unstable
4347
x + y
48+
//~^ ERROR let bindings in constant functions are unstable
49+
//~| ERROR let bindings in constant functions are unstable
4450
}
4551

4652
fn main() {

0 commit comments

Comments
 (0)