Skip to content

Commit bd8f464

Browse files
authored
Rollup merge of rust-lang#57175 - oli-obk:const_let_stabilization, r=nikomatsakis
Stabilize `let` bindings and destructuring in constants and const fn r? @Centril This PR stabilizes the following features in constants and `const` functions: * irrefutable destructuring patterns (e.g. `const fn foo((x, y): (u8, u8)) { ... }`) * `let` bindings (e.g. `let x = 1;`) * mutable `let` bindings (e.g. `let mut x = 1;`) * assignment (e.g. `x = y`) and assignment operator (e.g. `x += y`) expressions, even where the assignment target is a projection (e.g. a struct field or index operation like `x[3] = 42`) * expression statements (e.g. `3;`) This PR does explicitly *not* stabilize: * mutable references (i.e. `&mut T`) * dereferencing mutable references * refutable patterns (e.g. `Some(x)`) * operations on `UnsafeCell` types (as that would need raw pointers and mutable references and such, not because it is explicitly forbidden. We can't explicitly forbid it as such values are OK as long as they aren't mutated.) * We are not stabilizing `let` bindings in constants that use `&&` and `||` short circuiting operations. These are treated as `&` and `|` inside `const` and `static` items right now. If we stopped treating them as `&` and `|` after stabilizing `let` bindings, we'd break code like `let mut x = false; false && { x = true; false };`. So to use `let` bindings in constants you need to change `&&` and `||` to `&` and `|` respectively.
2 parents 017f046 + 6c62322 commit bd8f464

File tree

94 files changed

+408
-969
lines changed

Some content is hidden

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

94 files changed

+408
-969
lines changed

src/librustc_mir/transform/qualify_consts.rs

+42-182
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,7 @@ 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::Var if self.mode == Mode::Fn => {
463363
self.add(Qualif::NOT_CONST);
464364
}
465365
LocalKind::Var |
@@ -569,6 +469,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
569469
}
570470
}
571471

472+
ProjectionElem::ConstantIndex {..} |
473+
ProjectionElem::Subslice {..} |
572474
ProjectionElem::Field(..) |
573475
ProjectionElem::Index(_) => {
574476
let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
@@ -598,8 +500,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
598500
this.qualif.restrict(ty, this.tcx, this.param_env);
599501
}
600502

601-
ProjectionElem::ConstantIndex {..} |
602-
ProjectionElem::Subslice {..} |
603503
ProjectionElem::Downcast(..) => {
604504
this.not_const()
605505
}
@@ -1168,46 +1068,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
11681068
debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
11691069
self.visit_rvalue(rvalue, location);
11701070

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-
12111071
self.assign(dest, location);
12121072
}
12131073

src/librustc_mir/transform/qualify_min_const_fn.rs

+9-32
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,29 +256,17 @@ 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 {
267+
| ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. }
287268
| ProjectionElem::Deref | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {
288-
check_place(tcx, mir, &proj.base, span, mode)
289-
}
290-
// slice patterns are unstable
291-
| ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
292-
return Err((span, "slice patterns in const fn are unstable".into()))
269+
check_place(tcx, mir, &proj.base, span)
293270
}
294271
| ProjectionElem::Downcast(..) => {
295272
Err((span, "`match` or `if let` in `const fn` is unstable".into()))
@@ -311,10 +288,10 @@ fn check_terminator(
311288
| TerminatorKind::Resume => Ok(()),
312289

313290
TerminatorKind::Drop { location, .. } => {
314-
check_place(tcx, mir, location, span, PlaceMode::Read)
291+
check_place(tcx, mir, location, span)
315292
}
316293
TerminatorKind::DropAndReplace { location, value, .. } => {
317-
check_place(tcx, mir, location, span, PlaceMode::Read)?;
294+
check_place(tcx, mir, location, span)?;
318295
check_operand(tcx, mir, value, span)
319296
},
320297

0 commit comments

Comments
 (0)