Skip to content

Commit 8937faa

Browse files
committed
Reenable const_let feature gate
1 parent 866664c commit 8937faa

File tree

83 files changed

+871
-395
lines changed

Some content is hidden

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

83 files changed

+871
-395
lines changed

src/librustc_mir/transform/qualify_consts.rs

Lines changed: 191 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUs
3030
use rustc::middle::lang_items;
3131
use rustc_target::spec::abi::Abi;
3232
use syntax::ast::LitKind;
33-
use syntax::feature_gate::{UnstableFeatures, emit_feature_err, GateIssue};
33+
use syntax::feature_gate::{UnstableFeatures, feature_err, emit_feature_err, GateIssue};
3434
use syntax_pos::{Span, DUMMY_SP};
3535

3636
use std::fmt;
@@ -114,6 +114,7 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
114114
param_env: ty::ParamEnv<'tcx>,
115115
local_qualif: IndexVec<Local, Option<Qualif>>,
116116
qualif: Qualif,
117+
const_fn_arg_vars: BitSet<Local>,
117118
temp_promotion_state: IndexVec<Local, TempState>,
118119
promotion_candidates: Vec<Candidate>
119120
}
@@ -148,6 +149,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
148149
param_env,
149150
local_qualif,
150151
qualif: Qualif::empty(),
152+
const_fn_arg_vars: BitSet::new_empty(mir.local_decls.len()),
151153
temp_promotion_state: temps,
152154
promotion_candidates: vec![]
153155
}
@@ -176,6 +178,26 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
176178
}
177179
}
178180

181+
/// Error about extra statements in a constant.
182+
fn statement_like(&mut self) {
183+
self.add(Qualif::NOT_CONST);
184+
if self.mode != Mode::Fn {
185+
let mut err = feature_err(
186+
&self.tcx.sess.parse_sess,
187+
"const_let",
188+
self.span,
189+
GateIssue::Language,
190+
&format!("statements in {}s are unstable", self.mode),
191+
);
192+
if self.tcx.sess.teach(&err.get_code().unwrap()) {
193+
err.note("Blocks in constants may only contain items (such as constant, function \
194+
definition, etc...) and a tail expression.");
195+
err.help("To avoid it, you have to replace the non-item object.");
196+
}
197+
err.emit();
198+
}
199+
}
200+
179201
/// Add the given qualification to self.qualif.
180202
fn add(&mut self, qualif: Qualif) {
181203
self.qualif = self.qualif | qualif;
@@ -221,46 +243,85 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
221243
return;
222244
}
223245

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

266327
/// Qualify a whole const, static initializer or const fn.
@@ -299,7 +360,48 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
299360
TerminatorKind::FalseEdges { .. } |
300361
TerminatorKind::FalseUnwind { .. } => None,
301362

302-
TerminatorKind::Return => break,
363+
TerminatorKind::Return => {
364+
if !self.const_let_allowed() {
365+
// Check for unused values. This usually means
366+
// there are extra statements in the AST.
367+
for temp in mir.temps_iter() {
368+
if self.local_qualif[temp].is_none() {
369+
continue;
370+
}
371+
372+
let state = self.temp_promotion_state[temp];
373+
if let TempState::Defined { location, uses: 0 } = state {
374+
let data = &mir[location.block];
375+
let stmt_idx = location.statement_index;
376+
377+
// Get the span for the initialization.
378+
let source_info = if stmt_idx < data.statements.len() {
379+
data.statements[stmt_idx].source_info
380+
} else {
381+
data.terminator().source_info
382+
};
383+
self.span = source_info.span;
384+
385+
// Treat this as a statement in the AST.
386+
self.statement_like();
387+
}
388+
}
389+
390+
// Make sure there are no extra unassigned variables.
391+
self.qualif = Qualif::NOT_CONST;
392+
for index in mir.vars_iter() {
393+
if !self.const_fn_arg_vars.contains(index) {
394+
debug!("unassigned variable {:?}", index);
395+
self.assign(&Place::Local(index), Location {
396+
block: bb,
397+
statement_index: usize::MAX,
398+
});
399+
}
400+
}
401+
}
402+
403+
break;
404+
}
303405
};
304406

305407
match target {
@@ -366,6 +468,14 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
366468
LocalKind::ReturnPointer => {
367469
self.not_const();
368470
}
471+
LocalKind::Var if !self.const_let_allowed() => {
472+
if self.mode != Mode::Fn {
473+
emit_feature_err(&self.tcx.sess.parse_sess, "const_let",
474+
self.span, GateIssue::Language,
475+
&format!("let bindings in {}s are unstable",self.mode));
476+
}
477+
self.add(Qualif::NOT_CONST);
478+
}
369479
LocalKind::Var |
370480
LocalKind::Arg |
371481
LocalKind::Temp => {
@@ -448,6 +558,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
448558
ProjectionElem::Deref => {
449559
if context.is_mutating_use() {
450560
this.not_const()
561+
} else {
562+
this.qualif = Qualif::NOT_CONST;
451563
}
452564
let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
453565
match this.mode {
@@ -1050,6 +1162,46 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
10501162
debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
10511163
self.visit_rvalue(rvalue, location);
10521164

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

src/librustc_mir/transform/qualify_min_const_fn.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ pub fn is_min_const_fn(
6464
}
6565
}
6666

67+
for local in mir.vars_iter() {
68+
return Err((
69+
mir.local_decls[local].source_info.span,
70+
"local variables in const fn are unstable".into(),
71+
));
72+
}
6773
for local in &mir.local_decls {
6874
check_ty(tcx, local.ty, local.source_info.span)?;
6975
}
@@ -264,8 +270,15 @@ fn check_place(
264270
mode: PlaceMode,
265271
) -> McfResult {
266272
match place {
267-
// assignments to locals, arguments, temporaries or the return slot are fine
268-
Place::Local(_) => Ok(()),
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+
},
269282
// promoteds are always fine, they are essentially constants
270283
Place::Promoted(_) => Ok(()),
271284
Place::Static(_) => Err((span, "cannot access `static` items in const fn".into())),

src/libsyntax/feature_gate.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ declare_features! (
209209
// Allows the definition of `const fn` functions with some advanced features.
210210
(active, const_fn, "1.2.0", Some(24111), None),
211211

212+
// Allows let bindings and destructuring in `const fn` functions and constants.
213+
(active, const_let, "1.22.1", Some(48821), None),
214+
212215
// Allows accessing fields of unions inside const fn.
213216
(active, const_fn_union, "1.27.0", Some(51909), None),
214217

src/test/run-pass/ctfe/const-block-non-item-statement-3.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
// run-pass
1212
#![allow(dead_code)]
1313

14+
#![feature(const_let)]
15+
1416
type Array = [u32; { let x = 2; 5 }];
1517

1618
pub fn main() {}

src/test/run-pass/ctfe/const-block-non-item-statement.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
// run-pass
1212
#![allow(dead_code)]
1313

14+
#![feature(const_let)]
15+
1416
enum Foo {
1517
Bar = { let x = 1; 3 }
1618
}

src/test/run-pass/ctfe/locals-in-const-fn.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
// https://github.com/rust-lang/rust/issues/48821
1414

15-
#![feature(const_fn)]
15+
#![feature(const_fn, const_let)]
1616

1717
const fn foo(i: usize) -> usize {
1818
let x = i;

0 commit comments

Comments
 (0)