Skip to content

Commit cb84844

Browse files
committed
Auto merge of #56160 - oli-obk:const_fn_let, r=nikomatsakis
Fix various aspects around `let` bindings inside const functions * forbid `let` bindings in const contexts that use short circuiting operators * harden analysis code against derefs of mutable references Initially this PR was about stabilizing `let` bindings, but too many flaws were exposed that need some more testing on nightly
2 parents 041254b + d815e2b commit cb84844

37 files changed

+1085
-132
lines changed

src/libcore/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@
121121
#![feature(const_slice_len)]
122122
#![feature(const_str_as_bytes)]
123123
#![feature(const_str_len)]
124-
#![feature(const_let)]
125124
#![feature(const_int_rotate)]
126125
#![feature(const_int_wrapping)]
127126
#![feature(const_int_sign)]

src/librustc/mir/mod.rs

+15
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,14 @@ pub struct Mir<'tcx> {
149149
/// This is used for the "rust-call" ABI.
150150
pub spread_arg: Option<Local>,
151151

152+
/// Mark this MIR of a const context other than const functions as having converted a `&&` or
153+
/// `||` expression into `&` or `|` respectively. This is problematic because if we ever stop
154+
/// this conversion from happening and use short circuiting, we will cause the following code
155+
/// to change the value of `x`: `let mut x = 42; false && { x = 55; true };`
156+
///
157+
/// List of places where control flow was destroyed. Used for error reporting.
158+
pub control_flow_destroyed: Vec<(Span, String)>,
159+
152160
/// A span representing this MIR, for error reporting
153161
pub span: Span,
154162

@@ -167,6 +175,7 @@ impl<'tcx> Mir<'tcx> {
167175
arg_count: usize,
168176
upvar_decls: Vec<UpvarDecl>,
169177
span: Span,
178+
control_flow_destroyed: Vec<(Span, String)>,
170179
) -> Self {
171180
// We need `arg_count` locals, and one for the return place
172181
assert!(
@@ -191,6 +200,7 @@ impl<'tcx> Mir<'tcx> {
191200
spread_arg: None,
192201
span,
193202
cache: cache::Cache::new(),
203+
control_flow_destroyed,
194204
}
195205
}
196206

@@ -421,6 +431,7 @@ impl_stable_hash_for!(struct Mir<'tcx> {
421431
arg_count,
422432
upvar_decls,
423433
spread_arg,
434+
control_flow_destroyed,
424435
span,
425436
cache
426437
});
@@ -1748,6 +1759,9 @@ pub enum StatementKind<'tcx> {
17481759
/// (e.g., inspecting constants and discriminant values), and the
17491760
/// kind of pattern it comes from. This is in order to adapt potential
17501761
/// error messages to these specific patterns.
1762+
///
1763+
/// Note that this also is emitted for regular `let` bindings to ensure that locals that are
1764+
/// never accessed still get some sanity checks for e.g. `let x: ! = ..;`
17511765
FakeRead(FakeReadCause, Place<'tcx>),
17521766

17531767
/// Write the discriminant for a variant to the enum Place.
@@ -2984,6 +2998,7 @@ BraceStructTypeFoldableImpl! {
29842998
arg_count,
29852999
upvar_decls,
29863000
spread_arg,
3001+
control_flow_destroyed,
29873002
span,
29883003
cache,
29893004
}

src/librustc/ty/structural_impls.rs

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ CloneTypeFoldableAndLiftImpls! {
3535
usize,
3636
::ty::layout::VariantIdx,
3737
u64,
38+
String,
3839
::middle::region::Scope,
3940
::syntax::ast::FloatTy,
4041
::syntax::ast::NodeId,

src/librustc_mir/build/mod.rs

+11-9
Original file line numberDiff line numberDiff line change
@@ -849,15 +849,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
849849
}
850850
}
851851

852-
Mir::new(self.cfg.basic_blocks,
853-
self.source_scopes,
854-
ClearCrossCrate::Set(self.source_scope_local_data),
855-
IndexVec::new(),
856-
yield_ty,
857-
self.local_decls,
858-
self.arg_count,
859-
self.upvar_decls,
860-
self.fn_span
852+
Mir::new(
853+
self.cfg.basic_blocks,
854+
self.source_scopes,
855+
ClearCrossCrate::Set(self.source_scope_local_data),
856+
IndexVec::new(),
857+
yield_ty,
858+
self.local_decls,
859+
self.arg_count,
860+
self.upvar_decls,
861+
self.fn_span,
862+
self.hir.control_flow_destroyed(),
861863
)
862864
}
863865

src/librustc_mir/hair/cx/expr.rs

+8
Original file line numberDiff line numberDiff line change
@@ -372,13 +372,21 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
372372
// FIXME(eddyb) use logical ops in constants when
373373
// they can handle that kind of control-flow.
374374
(hir::BinOpKind::And, hir::Constness::Const) => {
375+
cx.control_flow_destroyed.push((
376+
op.span,
377+
"`&&` operator".into(),
378+
));
375379
ExprKind::Binary {
376380
op: BinOp::BitAnd,
377381
lhs: lhs.to_ref(),
378382
rhs: rhs.to_ref(),
379383
}
380384
}
381385
(hir::BinOpKind::Or, hir::Constness::Const) => {
386+
cx.control_flow_destroyed.push((
387+
op.span,
388+
"`||` operator".into(),
389+
));
382390
ExprKind::Binary {
383391
op: BinOp::BitOr,
384392
lhs: lhs.to_ref(),

src/librustc_mir/hair/cx/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ pub struct Cx<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
5656

5757
/// True if this constant/function needs overflow checks.
5858
check_overflow: bool,
59+
60+
/// See field with the same name on `Mir`
61+
control_flow_destroyed: Vec<(Span, String)>,
5962
}
6063

6164
impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
@@ -96,9 +99,13 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
9699
constness,
97100
body_owner_kind,
98101
check_overflow,
102+
control_flow_destroyed: Vec::new(),
99103
}
100104
}
101105

106+
pub fn control_flow_destroyed(self) -> Vec<(Span, String)> {
107+
self.control_flow_destroyed
108+
}
102109
}
103110

104111
impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {

src/librustc_mir/shim.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,8 @@ fn build_drop_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
219219
local_decls_for_sig(&sig, span),
220220
sig.inputs().len(),
221221
vec![],
222-
span
222+
span,
223+
vec![],
223224
);
224225

225226
if let Some(..) = ty {
@@ -396,7 +397,8 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
396397
self.local_decls,
397398
self.sig.inputs().len(),
398399
vec![],
399-
self.span
400+
self.span,
401+
vec![],
400402
)
401403
}
402404

@@ -844,7 +846,8 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
844846
local_decls,
845847
sig.inputs().len(),
846848
vec![],
847-
span
849+
span,
850+
vec![],
848851
);
849852
if let Abi::RustCall = sig.abi {
850853
mir.spread_arg = Some(Local::new(sig.inputs().len()));
@@ -921,6 +924,7 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>,
921924
local_decls,
922925
sig.inputs().len(),
923926
vec![],
924-
span
927+
span,
928+
vec![],
925929
)
926930
}

src/librustc_mir/transform/promote_consts.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,8 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
412412
initial_locals,
413413
0,
414414
vec![],
415-
mir.span
415+
mir.span,
416+
vec![],
416417
),
417418
tcx,
418419
source: mir,

src/librustc_mir/transform/qualify_consts.rs

+38-3
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,13 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
553553
this.super_place(place, context, location);
554554
match proj.elem {
555555
ProjectionElem::Deref => {
556-
this.add(Qualif::NOT_CONST);
556+
if context.is_mutating_use() {
557+
// `not_const` errors out in const contexts
558+
this.not_const()
559+
} else {
560+
// just make sure this doesn't get promoted
561+
this.add(Qualif::NOT_CONST);
562+
}
557563
let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
558564
match this.mode {
559565
Mode::Fn => {},
@@ -1178,7 +1184,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
11781184
if self.mir.local_kind(index) == LocalKind::Var &&
11791185
self.const_fn_arg_vars.insert(index) &&
11801186
!self.tcx.features().const_let {
1181-
11821187
// Direct use of an argument is permitted.
11831188
match *rvalue {
11841189
Rvalue::Use(Operand::Copy(Place::Local(local))) |
@@ -1189,7 +1194,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
11891194
}
11901195
_ => {}
11911196
}
1192-
11931197
// Avoid a generic error for other uses of arguments.
11941198
if self.qualif.contains(Qualif::FN_ARGUMENT) {
11951199
let decl = &self.mir.local_decls[index];
@@ -1348,6 +1352,37 @@ impl MirPass for QualifyAndPromoteConstants {
13481352
// Do the actual promotion, now that we know what's viable.
13491353
promote_consts::promote_candidates(mir, tcx, temps, candidates);
13501354
} else {
1355+
if !mir.control_flow_destroyed.is_empty() {
1356+
let mut locals = mir.vars_iter();
1357+
if let Some(local) = locals.next() {
1358+
let span = mir.local_decls[local].source_info.span;
1359+
let mut error = tcx.sess.struct_span_err(
1360+
span,
1361+
&format!(
1362+
"new features like let bindings are not permitted in {}s \
1363+
which also use short circuiting operators",
1364+
mode,
1365+
),
1366+
);
1367+
for (span, kind) in mir.control_flow_destroyed.iter() {
1368+
error.span_note(
1369+
*span,
1370+
&format!("use of {} here does not actually short circuit due to \
1371+
the const evaluator presently not being able to do control flow. \
1372+
See https://github.com/rust-lang/rust/issues/49146 for more \
1373+
information.", kind),
1374+
);
1375+
}
1376+
for local in locals {
1377+
let span = mir.local_decls[local].source_info.span;
1378+
error.span_note(
1379+
span,
1380+
"more locals defined here",
1381+
);
1382+
}
1383+
error.emit();
1384+
}
1385+
}
13511386
let promoted_temps = if mode == Mode::Const {
13521387
// Already computed by `mir_const_qualif`.
13531388
const_promoted_temps.unwrap()

src/librustc_mir/transform/qualify_min_const_fn.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ fn check_statement(
230230
check_rvalue(tcx, mir, rval, span)
231231
}
232232

233-
StatementKind::FakeRead(..) => Err((span, "match in const fn is unstable".into())),
233+
StatementKind::FakeRead(_, place) => check_place(tcx, mir, place, span, PlaceMode::Read),
234234

235235
// just an assignment
236236
StatementKind::SetDiscriminant { .. } => Ok(()),

src/test/compile-fail/const-fn-error.rs

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

11-
#![feature(const_fn)]
11+
#![feature(const_fn, const_let)]
1212

1313
const X : usize = 2;
1414

1515
const fn f(x: usize) -> usize {
1616
let mut sum = 0;
17-
//~^ let bindings in constant functions are unstable
18-
//~| statements in constant functions are unstable
1917
for i in 0..x {
2018
//~^ ERROR E0015
2119
//~| ERROR E0019

src/test/run-pass/ctfe/const-fn-destructuring-arg.rs

-26
This file was deleted.

src/test/ui/consts/const-eval/assign-to-static-within-other-static-2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ unsafe impl Sync for Foo {}
2424
static FOO: Foo = Foo(UnsafeCell::new(42));
2525

2626
static BAR: () = unsafe {
27-
*FOO.0.get() = 5; //~ ERROR could not evaluate static initializer
27+
*FOO.0.get() = 5; //~ ERROR contains unimplemented expression type
2828
};
2929

3030
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
error[E0080]: could not evaluate static initializer
1+
error[E0019]: static contains unimplemented expression type
22
--> $DIR/assign-to-static-within-other-static-2.rs:27:5
33
|
4-
LL | *FOO.0.get() = 5; //~ ERROR could not evaluate static initializer
5-
| ^^^^^^^^^^^^^^^^ tried to modify a static's initial value from another static's initializer
4+
LL | *FOO.0.get() = 5; //~ ERROR contains unimplemented expression type
5+
| ^^^^^^^^^^^^^^^^
66

77
error: aborting due to previous error
88

9-
For more information about this error, try `rustc --explain E0080`.
9+
For more information about this error, try `rustc --explain E0019`.

src/test/ui/consts/const-eval/mod-static-with-const-fn.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ fn foo() {}
2727

2828
static BAR: () = unsafe {
2929
*FOO.0.get() = 5;
30-
// we do not error on the above access, because that is not detectable statically. Instead,
31-
// const evaluation will error when trying to evaluate it. Due to the error below, we never even
32-
// attempt to const evaluate `BAR`, so we don't see the error
30+
//~^ contains unimplemented expression
3331

3432
foo();
3533
//~^ ERROR calls in statics are limited to constant functions, tuple structs and tuple variants
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1+
error[E0019]: static contains unimplemented expression type
2+
--> $DIR/mod-static-with-const-fn.rs:29:5
3+
|
4+
LL | *FOO.0.get() = 5;
5+
| ^^^^^^^^^^^^^^^^
6+
17
error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
2-
--> $DIR/mod-static-with-const-fn.rs:34:5
8+
--> $DIR/mod-static-with-const-fn.rs:32:5
39
|
410
LL | foo();
511
| ^^^^^
612

7-
error: aborting due to previous error
13+
error: aborting due to 2 previous errors
814

9-
For more information about this error, try `rustc --explain E0015`.
15+
Some errors occurred: E0015, E0019.
16+
For more information about an error, try `rustc --explain E0015`.

src/test/ui/consts/const-fn-not-safe-for-const.rs

+6-8
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,13 @@ const fn get_Y_addr() -> &'static u32 {
3737
}
3838

3939
const fn get() -> u32 {
40-
let x = 22;
41-
//~^ ERROR let bindings in constant functions are unstable
42-
//~| ERROR statements in constant functions are unstable
43-
let y = 44;
44-
//~^ ERROR let bindings in constant functions are unstable
45-
//~| ERROR statements in constant functions are unstable
40+
let x = 22; //~ ERROR let bindings in constant functions are unstable
41+
//~^ ERROR statements in constant functions
42+
let y = 44; //~ ERROR let bindings in constant functions are unstable
43+
//~^ ERROR statements in constant functions
4644
x + y
47-
//~^ ERROR let bindings in constant functions are unstable
48-
//~| ERROR let bindings in constant functions are unstable
45+
//~^ ERROR let bindings in constant functions are unstable
46+
//~| ERROR let bindings in constant functions are unstable
4947
}
5048

5149
fn main() {}

0 commit comments

Comments
 (0)