Skip to content

Commit 4b4fc63

Browse files
committed
Stabilize let bindings and destructuring in constants and const fn
1 parent 167ceff commit 4b4fc63

File tree

86 files changed

+283
-865
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+283
-865
lines changed

src/librustc_mir/transform/qualify_consts.rs

Lines changed: 41 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUs
2121
use rustc::middle::lang_items;
2222
use rustc::session::config::nightly_options;
2323
use syntax::ast::LitKind;
24-
use syntax::feature_gate::{UnstableFeatures, feature_err, emit_feature_err, GateIssue};
24+
use syntax::feature_gate::{UnstableFeatures, emit_feature_err, GateIssue};
2525
use syntax_pos::{Span, DUMMY_SP};
2626

2727
use std::fmt;
@@ -104,7 +104,6 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
104104
param_env: ty::ParamEnv<'tcx>,
105105
local_qualif: IndexVec<Local, Option<Qualif>>,
106106
qualif: Qualif,
107-
const_fn_arg_vars: BitSet<Local>,
108107
temp_promotion_state: IndexVec<Local, TempState>,
109108
promotion_candidates: Vec<Candidate>
110109
}
@@ -139,7 +138,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
139138
param_env,
140139
local_qualif,
141140
qualif: Qualif::empty(),
142-
const_fn_arg_vars: BitSet::new_empty(mir.local_decls.len()),
143141
temp_promotion_state: temps,
144142
promotion_candidates: vec![]
145143
}
@@ -168,26 +166,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
168166
}
169167
}
170168

171-
/// Error about extra statements in a constant.
172-
fn statement_like(&mut self) {
173-
self.add(Qualif::NOT_CONST);
174-
if self.mode != Mode::Fn {
175-
let mut err = feature_err(
176-
&self.tcx.sess.parse_sess,
177-
"const_let",
178-
self.span,
179-
GateIssue::Language,
180-
&format!("statements in {}s are unstable", self.mode),
181-
);
182-
if self.tcx.sess.teach(&err.get_code().unwrap()) {
183-
err.note("Blocks in constants may only contain items (such as constant, function \
184-
definition, etc...) and a tail expression.");
185-
err.help("To avoid it, you have to replace the non-item object.");
186-
}
187-
err.emit();
188-
}
189-
}
190-
191169
/// Add the given qualification to self.qualif.
192170
fn add(&mut self, qualif: Qualif) {
193171
self.qualif = self.qualif | qualif;
@@ -233,80 +211,46 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
233211
return;
234212
}
235213

236-
if self.tcx.features().const_let {
237-
let mut dest = dest;
238-
let index = loop {
239-
match dest {
240-
// with `const_let` active, we treat all locals equal
241-
Place::Local(index) => break *index,
242-
// projections are transparent for assignments
243-
// we qualify the entire destination at once, even if just a field would have
244-
// stricter qualification
245-
Place::Projection(proj) => {
246-
// Catch more errors in the destination. `visit_place` also checks various
247-
// projection rules like union field access and raw pointer deref
248-
self.visit_place(
249-
dest,
250-
PlaceContext::MutatingUse(MutatingUseContext::Store),
251-
location
252-
);
253-
dest = &proj.base;
254-
},
255-
Place::Promoted(..) => bug!("promoteds don't exist yet during promotion"),
256-
Place::Static(..) => {
257-
// Catch more errors in the destination. `visit_place` also checks that we
258-
// do not try to access statics from constants or try to mutate statics
259-
self.visit_place(
260-
dest,
261-
PlaceContext::MutatingUse(MutatingUseContext::Store),
262-
location
263-
);
264-
return;
265-
}
214+
let mut dest = dest;
215+
let index = loop {
216+
match dest {
217+
// We treat all locals equal in constants
218+
Place::Local(index) => break *index,
219+
// projections are transparent for assignments
220+
// we qualify the entire destination at once, even if just a field would have
221+
// stricter qualification
222+
Place::Projection(proj) => {
223+
// Catch more errors in the destination. `visit_place` also checks various
224+
// projection rules like union field access and raw pointer deref
225+
self.visit_place(
226+
dest,
227+
PlaceContext::MutatingUse(MutatingUseContext::Store),
228+
location
229+
);
230+
dest = &proj.base;
231+
},
232+
Place::Promoted(..) => bug!("promoteds don't exist yet during promotion"),
233+
Place::Static(..) => {
234+
// Catch more errors in the destination. `visit_place` also checks that we
235+
// do not try to access statics from constants or try to mutate statics
236+
self.visit_place(
237+
dest,
238+
PlaceContext::MutatingUse(MutatingUseContext::Store),
239+
location
240+
);
241+
return;
266242
}
267-
};
268-
debug!("store to var {:?}", index);
269-
match &mut self.local_qualif[index] {
270-
// this is overly restrictive, because even full assignments do not clear the qualif
271-
// While we could special case full assignments, this would be inconsistent with
272-
// aggregates where we overwrite all fields via assignments, which would not get
273-
// that feature.
274-
Some(ref mut qualif) => *qualif = *qualif | self.qualif,
275-
// insert new qualification
276-
qualif @ None => *qualif = Some(self.qualif),
277-
}
278-
return;
279-
}
280-
281-
match *dest {
282-
Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp ||
283-
self.mir.local_kind(index) == LocalKind::ReturnPointer => {
284-
debug!("store to {:?} (temp or return pointer)", index);
285-
store(&mut self.local_qualif[index])
286-
}
287-
288-
Place::Projection(box Projection {
289-
base: Place::Local(index),
290-
elem: ProjectionElem::Deref
291-
}) if self.mir.local_kind(index) == LocalKind::Temp
292-
&& self.mir.local_decls[index].ty.is_box()
293-
&& self.local_qualif[index].map_or(false, |qualif| {
294-
qualif.contains(Qualif::NOT_CONST)
295-
}) => {
296-
// Part of `box expr`, we should've errored
297-
// already for the Box allocation Rvalue.
298-
}
299-
300-
// This must be an explicit assignment.
301-
_ => {
302-
// Catch more errors in the destination.
303-
self.visit_place(
304-
dest,
305-
PlaceContext::MutatingUse(MutatingUseContext::Store),
306-
location
307-
);
308-
self.statement_like();
309243
}
244+
};
245+
debug!("store to var {:?}", index);
246+
match &mut self.local_qualif[index] {
247+
// this is overly restrictive, because even full assignments do not clear the qualif
248+
// While we could special case full assignments, this would be inconsistent with
249+
// aggregates where we overwrite all fields via assignments, which would not get
250+
// that feature.
251+
Some(ref mut qualif) => *qualif = *qualif | self.qualif,
252+
// insert new qualification
253+
qualif @ None => *qualif = Some(self.qualif),
310254
}
311255
}
312256

@@ -347,45 +291,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
347291
TerminatorKind::FalseUnwind { .. } => None,
348292

349293
TerminatorKind::Return => {
350-
if !self.tcx.features().const_let {
351-
// Check for unused values. This usually means
352-
// there are extra statements in the AST.
353-
for temp in mir.temps_iter() {
354-
if self.local_qualif[temp].is_none() {
355-
continue;
356-
}
357-
358-
let state = self.temp_promotion_state[temp];
359-
if let TempState::Defined { location, uses: 0 } = state {
360-
let data = &mir[location.block];
361-
let stmt_idx = location.statement_index;
362-
363-
// Get the span for the initialization.
364-
let source_info = if stmt_idx < data.statements.len() {
365-
data.statements[stmt_idx].source_info
366-
} else {
367-
data.terminator().source_info
368-
};
369-
self.span = source_info.span;
370-
371-
// Treat this as a statement in the AST.
372-
self.statement_like();
373-
}
374-
}
375-
376-
// Make sure there are no extra unassigned variables.
377-
self.qualif = Qualif::NOT_CONST;
378-
for index in mir.vars_iter() {
379-
if !self.const_fn_arg_vars.contains(index) {
380-
debug!("unassigned variable {:?}", index);
381-
self.assign(&Place::Local(index), Location {
382-
block: bb,
383-
statement_index: usize::MAX,
384-
});
385-
}
386-
}
387-
}
388-
389294
break;
390295
}
391296
};
@@ -454,12 +359,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
454359
LocalKind::ReturnPointer => {
455360
self.not_const();
456361
}
457-
LocalKind::Var if !self.tcx.features().const_let => {
458-
if self.mode != Mode::Fn {
459-
emit_feature_err(&self.tcx.sess.parse_sess, "const_let",
460-
self.span, GateIssue::Language,
461-
&format!("let bindings in {}s are unstable",self.mode));
462-
}
362+
LocalKind::Arg |
363+
LocalKind::Var if self.mode == Mode::Fn => {
463364
self.add(Qualif::NOT_CONST);
464365
}
465366
LocalKind::Var |
@@ -1168,46 +1069,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
11681069
debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
11691070
self.visit_rvalue(rvalue, location);
11701071

1171-
// Check the allowed const fn argument forms.
1172-
if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) {
1173-
if self.mir.local_kind(index) == LocalKind::Var &&
1174-
self.const_fn_arg_vars.insert(index) &&
1175-
!self.tcx.features().const_let {
1176-
// Direct use of an argument is permitted.
1177-
match *rvalue {
1178-
Rvalue::Use(Operand::Copy(Place::Local(local))) |
1179-
Rvalue::Use(Operand::Move(Place::Local(local))) => {
1180-
if self.mir.local_kind(local) == LocalKind::Arg {
1181-
return;
1182-
}
1183-
}
1184-
_ => {}
1185-
}
1186-
// Avoid a generic error for other uses of arguments.
1187-
if self.qualif.contains(Qualif::FN_ARGUMENT) {
1188-
let decl = &self.mir.local_decls[index];
1189-
let mut err = feature_err(
1190-
&self.tcx.sess.parse_sess,
1191-
"const_let",
1192-
decl.source_info.span,
1193-
GateIssue::Language,
1194-
"arguments of constant functions can only be immutable by-value bindings"
1195-
);
1196-
if self.tcx.sess.teach(&err.get_code().unwrap()) {
1197-
err.note("Constant functions are not allowed to mutate anything. Thus, \
1198-
binding to an argument with a mutable pattern is not allowed.");
1199-
err.note("Remove any mutable bindings from the argument list to fix this \
1200-
error. In case you need to mutate the argument, try lazily \
1201-
initializing a global variable instead of using a const fn, or \
1202-
refactoring the code to a functional style to avoid mutation if \
1203-
possible.");
1204-
}
1205-
err.emit();
1206-
return;
1207-
}
1208-
}
1209-
}
1210-
12111072
self.assign(dest, location);
12121073
}
12131074

src/librustc_mir/transform/qualify_min_const_fn.rs

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,6 @@ pub fn is_min_const_fn(
6565
}
6666
}
6767

68-
for local in mir.vars_iter() {
69-
return Err((
70-
mir.local_decls[local].source_info.span,
71-
"local variables in const fn are unstable".into(),
72-
));
73-
}
7468
for local in &mir.local_decls {
7569
check_ty(tcx, local.ty, local.source_info.span)?;
7670
}
@@ -147,7 +141,7 @@ fn check_rvalue(
147141
check_operand(tcx, mir, operand, span)
148142
}
149143
Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) => {
150-
check_place(tcx, mir, place, span, PlaceMode::Read)
144+
check_place(tcx, mir, place, span)
151145
}
152146
Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
153147
use rustc::ty::cast::CastTy;
@@ -213,11 +207,6 @@ fn check_rvalue(
213207
}
214208
}
215209

216-
enum PlaceMode {
217-
Assign,
218-
Read,
219-
}
220-
221210
fn check_statement(
222211
tcx: TyCtxt<'a, 'tcx, 'tcx>,
223212
mir: &'a Mir<'tcx>,
@@ -226,11 +215,11 @@ fn check_statement(
226215
let span = statement.source_info.span;
227216
match &statement.kind {
228217
StatementKind::Assign(place, rval) => {
229-
check_place(tcx, mir, place, span, PlaceMode::Assign)?;
218+
check_place(tcx, mir, place, span)?;
230219
check_rvalue(tcx, mir, rval, span)
231220
}
232221

233-
StatementKind::FakeRead(_, place) => check_place(tcx, mir, place, span, PlaceMode::Read),
222+
StatementKind::FakeRead(_, place) => check_place(tcx, mir, place, span),
234223

235224
// just an assignment
236225
StatementKind::SetDiscriminant { .. } => Ok(()),
@@ -256,7 +245,7 @@ fn check_operand(
256245
) -> McfResult {
257246
match operand {
258247
Operand::Move(place) | Operand::Copy(place) => {
259-
check_place(tcx, mir, place, span, PlaceMode::Read)
248+
check_place(tcx, mir, place, span)
260249
}
261250
Operand::Constant(_) => Ok(()),
262251
}
@@ -267,25 +256,16 @@ fn check_place(
267256
mir: &'a Mir<'tcx>,
268257
place: &Place<'tcx>,
269258
span: Span,
270-
mode: PlaceMode,
271259
) -> McfResult {
272260
match place {
273-
Place::Local(l) => match mode {
274-
PlaceMode::Assign => match mir.local_kind(*l) {
275-
LocalKind::Temp | LocalKind::ReturnPointer => Ok(()),
276-
LocalKind::Arg | LocalKind::Var => {
277-
Err((span, "assignments in const fn are unstable".into()))
278-
}
279-
},
280-
PlaceMode::Read => Ok(()),
281-
},
261+
Place::Local(_) => Ok(()),
282262
// promoteds are always fine, they are essentially constants
283263
Place::Promoted(_) => Ok(()),
284264
Place::Static(_) => Err((span, "cannot access `static` items in const fn".into())),
285265
Place::Projection(proj) => {
286266
match proj.elem {
287267
| ProjectionElem::Deref | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {
288-
check_place(tcx, mir, &proj.base, span, mode)
268+
check_place(tcx, mir, &proj.base, span)
289269
}
290270
// slice patterns are unstable
291271
| ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
@@ -311,10 +291,10 @@ fn check_terminator(
311291
| TerminatorKind::Resume => Ok(()),
312292

313293
TerminatorKind::Drop { location, .. } => {
314-
check_place(tcx, mir, location, span, PlaceMode::Read)
294+
check_place(tcx, mir, location, span)
315295
}
316296
TerminatorKind::DropAndReplace { location, value, .. } => {
317-
check_place(tcx, mir, location, span, PlaceMode::Read)?;
297+
check_place(tcx, mir, location, span)?;
318298
check_operand(tcx, mir, value, span)
319299
},
320300

src/libsyntax/feature_gate.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,6 @@ declare_features! (
193193
// Allows the definition of `const` functions with some advanced features.
194194
(active, const_fn, "1.2.0", Some(24111), None),
195195

196-
// Allows let bindings and destructuring in `const` functions and constants.
197-
(active, const_let, "1.22.1", Some(48821), None),
198-
199196
// Allows accessing fields of unions inside `const` functions.
200197
(active, const_fn_union, "1.27.0", Some(51909), None),
201198

@@ -688,6 +685,8 @@ declare_features! (
688685
(accepted, min_const_unsafe_fn, "1.33.0", Some(55607), None),
689686
// `#[cfg_attr(predicate, multiple, attributes, here)]`
690687
(accepted, cfg_attr_multi, "1.33.0", Some(54881), None),
688+
// Allows let bindings and destructuring in `const` functions and constants.
689+
(accepted, const_let, "1.33.0", Some(48821), None),
691690
);
692691

693692
// If you change this, please modify `src/doc/unstable-book` as well. You must

0 commit comments

Comments
 (0)