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
This commit is contained in:
bors 2018-12-18 14:21:07 +00:00
commit cb84844e83
37 changed files with 1086 additions and 133 deletions

View file

@ -121,7 +121,6 @@
#![feature(const_slice_len)]
#![feature(const_str_as_bytes)]
#![feature(const_str_len)]
#![feature(const_let)]
#![feature(const_int_rotate)]
#![feature(const_int_wrapping)]
#![feature(const_int_sign)]

View file

@ -149,6 +149,14 @@ pub struct Mir<'tcx> {
/// This is used for the "rust-call" ABI.
pub spread_arg: Option<Local>,
/// Mark this MIR of a const context other than const functions as having converted a `&&` or
/// `||` expression into `&` or `|` respectively. This is problematic because if we ever stop
/// this conversion from happening and use short circuiting, we will cause the following code
/// to change the value of `x`: `let mut x = 42; false && { x = 55; true };`
///
/// List of places where control flow was destroyed. Used for error reporting.
pub control_flow_destroyed: Vec<(Span, String)>,
/// A span representing this MIR, for error reporting
pub span: Span,
@ -167,6 +175,7 @@ pub fn new(
arg_count: usize,
upvar_decls: Vec<UpvarDecl>,
span: Span,
control_flow_destroyed: Vec<(Span, String)>,
) -> Self {
// We need `arg_count` locals, and one for the return place
assert!(
@ -191,6 +200,7 @@ pub fn new(
spread_arg: None,
span,
cache: cache::Cache::new(),
control_flow_destroyed,
}
}
@ -421,6 +431,7 @@ pub enum Safety {
arg_count,
upvar_decls,
spread_arg,
control_flow_destroyed,
span,
cache
});
@ -1748,6 +1759,9 @@ pub enum StatementKind<'tcx> {
/// (e.g., inspecting constants and discriminant values), and the
/// kind of pattern it comes from. This is in order to adapt potential
/// error messages to these specific patterns.
///
/// Note that this also is emitted for regular `let` bindings to ensure that locals that are
/// never accessed still get some sanity checks for e.g. `let x: ! = ..;`
FakeRead(FakeReadCause, Place<'tcx>),
/// Write the discriminant for a variant to the enum Place.
@ -2984,6 +2998,7 @@ impl<'tcx> TypeFoldable<'tcx> for Mir<'tcx> {
arg_count,
upvar_decls,
spread_arg,
control_flow_destroyed,
span,
cache,
}

View file

@ -35,6 +35,7 @@
usize,
::ty::layout::VariantIdx,
u64,
String,
::middle::region::Scope,
::syntax::ast::FloatTy,
::syntax::ast::NodeId,

View file

@ -849,15 +849,17 @@ fn finish(self,
}
}
Mir::new(self.cfg.basic_blocks,
self.source_scopes,
ClearCrossCrate::Set(self.source_scope_local_data),
IndexVec::new(),
yield_ty,
self.local_decls,
self.arg_count,
self.upvar_decls,
self.fn_span
Mir::new(
self.cfg.basic_blocks,
self.source_scopes,
ClearCrossCrate::Set(self.source_scope_local_data),
IndexVec::new(),
yield_ty,
self.local_decls,
self.arg_count,
self.upvar_decls,
self.fn_span,
self.hir.control_flow_destroyed(),
)
}

View file

@ -372,6 +372,10 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
// FIXME(eddyb) use logical ops in constants when
// they can handle that kind of control-flow.
(hir::BinOpKind::And, hir::Constness::Const) => {
cx.control_flow_destroyed.push((
op.span,
"`&&` operator".into(),
));
ExprKind::Binary {
op: BinOp::BitAnd,
lhs: lhs.to_ref(),
@ -379,6 +383,10 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
}
}
(hir::BinOpKind::Or, hir::Constness::Const) => {
cx.control_flow_destroyed.push((
op.span,
"`||` operator".into(),
));
ExprKind::Binary {
op: BinOp::BitOr,
lhs: lhs.to_ref(),

View file

@ -56,6 +56,9 @@ pub struct Cx<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
/// True if this constant/function needs overflow checks.
check_overflow: bool,
/// See field with the same name on `Mir`
control_flow_destroyed: Vec<(Span, String)>,
}
impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
@ -96,9 +99,13 @@ pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
constness,
body_owner_kind,
check_overflow,
control_flow_destroyed: Vec::new(),
}
}
pub fn control_flow_destroyed(self) -> Vec<(Span, String)> {
self.control_flow_destroyed
}
}
impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {

View file

@ -219,7 +219,8 @@ fn build_drop_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
local_decls_for_sig(&sig, span),
sig.inputs().len(),
vec![],
span
span,
vec![],
);
if let Some(..) = ty {
@ -396,7 +397,8 @@ fn into_mir(self) -> Mir<'tcx> {
self.local_decls,
self.sig.inputs().len(),
vec![],
self.span
self.span,
vec![],
)
}
@ -844,7 +846,8 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
local_decls,
sig.inputs().len(),
vec![],
span
span,
vec![],
);
if let Abi::RustCall = sig.abi {
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>,
local_decls,
sig.inputs().len(),
vec![],
span
span,
vec![],
)
}

View file

@ -412,7 +412,8 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
initial_locals,
0,
vec![],
mir.span
mir.span,
vec![],
),
tcx,
source: mir,

View file

@ -553,7 +553,13 @@ fn visit_place(&mut self,
this.super_place(place, context, location);
match proj.elem {
ProjectionElem::Deref => {
this.add(Qualif::NOT_CONST);
if context.is_mutating_use() {
// `not_const` errors out in const contexts
this.not_const()
} else {
// just make sure this doesn't get promoted
this.add(Qualif::NOT_CONST);
}
let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
match this.mode {
Mode::Fn => {},
@ -1178,7 +1184,6 @@ fn visit_assign(&mut self,
if self.mir.local_kind(index) == LocalKind::Var &&
self.const_fn_arg_vars.insert(index) &&
!self.tcx.features().const_let {
// Direct use of an argument is permitted.
match *rvalue {
Rvalue::Use(Operand::Copy(Place::Local(local))) |
@ -1189,7 +1194,6 @@ fn visit_assign(&mut self,
}
_ => {}
}
// Avoid a generic error for other uses of arguments.
if self.qualif.contains(Qualif::FN_ARGUMENT) {
let decl = &self.mir.local_decls[index];
@ -1348,6 +1352,37 @@ fn run_pass<'a, 'tcx>(&self,
// Do the actual promotion, now that we know what's viable.
promote_consts::promote_candidates(mir, tcx, temps, candidates);
} else {
if !mir.control_flow_destroyed.is_empty() {
let mut locals = mir.vars_iter();
if let Some(local) = locals.next() {
let span = mir.local_decls[local].source_info.span;
let mut error = tcx.sess.struct_span_err(
span,
&format!(
"new features like let bindings are not permitted in {}s \
which also use short circuiting operators",
mode,
),
);
for (span, kind) in mir.control_flow_destroyed.iter() {
error.span_note(
*span,
&format!("use of {} here does not actually short circuit due to \
the const evaluator presently not being able to do control flow. \
See https://github.com/rust-lang/rust/issues/49146 for more \
information.", kind),
);
}
for local in locals {
let span = mir.local_decls[local].source_info.span;
error.span_note(
span,
"more locals defined here",
);
}
error.emit();
}
}
let promoted_temps = if mode == Mode::Const {
// Already computed by `mir_const_qualif`.
const_promoted_temps.unwrap()

View file

@ -230,7 +230,7 @@ fn check_statement(
check_rvalue(tcx, mir, rval, span)
}
StatementKind::FakeRead(..) => Err((span, "match in const fn is unstable".into())),
StatementKind::FakeRead(_, place) => check_place(tcx, mir, place, span, PlaceMode::Read),
// just an assignment
StatementKind::SetDiscriminant { .. } => Ok(()),

View file

@ -8,14 +8,12 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(const_fn)]
#![feature(const_fn, const_let)]
const X : usize = 2;
const fn f(x: usize) -> usize {
let mut sum = 0;
//~^ let bindings in constant functions are unstable
//~| statements in constant functions are unstable
for i in 0..x {
//~^ ERROR E0015
//~| ERROR E0019

View file

@ -1,26 +0,0 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// run-pass
#![allow(dead_code)]
// test that certain things are disallowed in constant functions
#![feature(const_fn, const_let)]
// no destructuring
const fn i((
a,
b
): (u32, u32)) -> u32 {
a + b
}
fn main() {}

View file

@ -24,7 +24,7 @@ unsafe impl Sync for Foo {}
static FOO: Foo = Foo(UnsafeCell::new(42));
static BAR: () = unsafe {
*FOO.0.get() = 5; //~ ERROR could not evaluate static initializer
*FOO.0.get() = 5; //~ ERROR contains unimplemented expression type
};
fn main() {}

View file

@ -1,9 +1,9 @@
error[E0080]: could not evaluate static initializer
error[E0019]: static contains unimplemented expression type
--> $DIR/assign-to-static-within-other-static-2.rs:27:5
|
LL | *FOO.0.get() = 5; //~ ERROR could not evaluate static initializer
| ^^^^^^^^^^^^^^^^ tried to modify a static's initial value from another static's initializer
LL | *FOO.0.get() = 5; //~ ERROR contains unimplemented expression type
| ^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.
For more information about this error, try `rustc --explain E0019`.

View file

@ -27,9 +27,7 @@ fn foo() {}
static BAR: () = unsafe {
*FOO.0.get() = 5;
// we do not error on the above access, because that is not detectable statically. Instead,
// const evaluation will error when trying to evaluate it. Due to the error below, we never even
// attempt to const evaluate `BAR`, so we don't see the error
//~^ contains unimplemented expression
foo();
//~^ ERROR calls in statics are limited to constant functions, tuple structs and tuple variants

View file

@ -1,9 +1,16 @@
error[E0019]: static contains unimplemented expression type
--> $DIR/mod-static-with-const-fn.rs:29:5
|
LL | *FOO.0.get() = 5;
| ^^^^^^^^^^^^^^^^
error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
--> $DIR/mod-static-with-const-fn.rs:34:5
--> $DIR/mod-static-with-const-fn.rs:32:5
|
LL | foo();
| ^^^^^
error: aborting due to previous error
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0015`.
Some errors occurred: E0015, E0019.
For more information about an error, try `rustc --explain E0015`.

View file

@ -37,15 +37,13 @@ const fn get_Y_addr() -> &'static u32 {
}
const fn get() -> u32 {
let x = 22;
//~^ ERROR let bindings in constant functions are unstable
//~| ERROR statements in constant functions are unstable
let y = 44;
//~^ ERROR let bindings in constant functions are unstable
//~| ERROR statements in constant functions are unstable
let x = 22; //~ ERROR let bindings in constant functions are unstable
//~^ ERROR statements in constant functions
let y = 44; //~ ERROR let bindings in constant functions are unstable
//~^ ERROR statements in constant functions
x + y
//~^ ERROR let bindings in constant functions are unstable
//~| ERROR let bindings in constant functions are unstable
//~^ ERROR let bindings in constant functions are unstable
//~| ERROR let bindings in constant functions are unstable
}
fn main() {}

View file

@ -19,7 +19,7 @@ LL | &Y
error[E0658]: let bindings in constant functions are unstable (see issue #48821)
--> $DIR/const-fn-not-safe-for-const.rs:40:13
|
LL | let x = 22;
LL | let x = 22; //~ ERROR let bindings in constant functions are unstable
| ^^
|
= help: add #![feature(const_let)] to the crate attributes to enable
@ -27,29 +27,29 @@ LL | let x = 22;
error[E0658]: statements in constant functions are unstable (see issue #48821)
--> $DIR/const-fn-not-safe-for-const.rs:40:13
|
LL | let x = 22;
LL | let x = 22; //~ ERROR let bindings in constant functions are unstable
| ^^
|
= help: add #![feature(const_let)] to the crate attributes to enable
error[E0658]: let bindings in constant functions are unstable (see issue #48821)
--> $DIR/const-fn-not-safe-for-const.rs:43:13
--> $DIR/const-fn-not-safe-for-const.rs:42:13
|
LL | let y = 44;
LL | let y = 44; //~ ERROR let bindings in constant functions are unstable
| ^^
|
= help: add #![feature(const_let)] to the crate attributes to enable
error[E0658]: statements in constant functions are unstable (see issue #48821)
--> $DIR/const-fn-not-safe-for-const.rs:43:13
--> $DIR/const-fn-not-safe-for-const.rs:42:13
|
LL | let y = 44;
LL | let y = 44; //~ ERROR let bindings in constant functions are unstable
| ^^
|
= help: add #![feature(const_let)] to the crate attributes to enable
error[E0658]: let bindings in constant functions are unstable (see issue #48821)
--> $DIR/const-fn-not-safe-for-const.rs:46:5
--> $DIR/const-fn-not-safe-for-const.rs:44:5
|
LL | x + y
| ^
@ -57,7 +57,7 @@ LL | x + y
= help: add #![feature(const_let)] to the crate attributes to enable
error[E0658]: let bindings in constant functions are unstable (see issue #48821)
--> $DIR/const-fn-not-safe-for-const.rs:46:9
--> $DIR/const-fn-not-safe-for-const.rs:44:9
|
LL | x + y
| ^

View file

@ -8,6 +8,7 @@ struct S {
impl S {
const fn foo(&mut self, x: u32) {
self.state = x;
//~^ contains unimplemented expression
}
}

View file

@ -1,9 +1,16 @@
error[E0019]: constant function contains unimplemented expression type
--> $DIR/const_let_assign3.rs:10:9
|
LL | self.state = x;
| ^^^^^^^^^^^^^^
error[E0017]: references in constants may only refer to immutable values
--> $DIR/const_let_assign3.rs:16:5
--> $DIR/const_let_assign3.rs:17:5
|
LL | s.foo(3); //~ ERROR references in constants may only refer to immutable values
| ^ constants require immutable values
error: aborting due to previous error
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0017`.
Some errors occurred: E0017, E0019.
For more information about an error, try `rustc --explain E0017`.

View file

@ -0,0 +1,470 @@
#![feature(const_let, const_fn)]
// run-pass
struct Foo<T>(T);
struct Bar<T> { x: T }
struct W(u32);
struct A { a: u32 }
const fn basics((a,): (u32,)) -> u32 {
// Deferred assignment:
let b: u32;
b = a + 1;
// Immediate assignment:
let c: u32 = b + 1;
// Mutables:
let mut d: u32 = c + 1;
d = d + 1;
// +4 so far.
// No effect statements work:
; ;
1;
// Array projection
let mut arr: [u32; 1] = [0];
arr[0] = 1;
d = d + arr[0];
// +5
// Field projection:
let mut foo: Foo<u32> = Foo(0);
let mut bar: Bar<u32> = Bar { x: 0 };
foo.0 = 1;
bar.x = 1;
d = d + foo.0 + bar.x;
// +7
// Array + Field projection:
let mut arr: [Foo<u32>; 1] = [Foo(0)];
arr[0].0 = 1;
d = d + arr[0].0;
let mut arr: [Bar<u32>; 1] = [Bar { x: 0 }];
arr[0].x = 1;
d = d + arr[0].x;
// +9
// Field + Array projection:
let mut arr: Foo<[u32; 1]> = Foo([0]);
(arr.0)[0] = 1;
d = d + (arr.0)[0];
let mut arr: Bar<[u32; 1]> = Bar { x: [0] };
arr.x[0] = 1;
d = d + arr.x[0];
// +11
d
}
const fn add_assign(W(a): W) -> u32 {
// Mutables:
let mut d: u32 = a + 1;
d += 1;
// +2 so far.
// Array projection
let mut arr: [u32; 1] = [0];
arr[0] += 1;
d += arr[0];
// +3
// Field projection:
let mut foo: Foo<u32> = Foo(0);
let mut bar: Bar<u32> = Bar { x: 0 };
foo.0 += 1;
bar.x += 1;
d += foo.0 + bar.x;
// +5
// Array + Field projection:
let mut arr: [Foo<u32>; 1] = [Foo(0)];
arr[0].0 += 1;
d += arr[0].0;
let mut arr: [Bar<u32>; 1] = [Bar { x: 0 }];
arr[0].x += 1;
d += arr[0].x;
// +7
// Field + Array projection:
let mut arr: Foo<[u32; 1]> = Foo([0]);
(arr.0)[0] += 1;
d += (arr.0)[0];
let mut arr: Bar<[u32; 1]> = Bar { x: [0] };
arr.x[0] += 1;
d += arr.x[0];
// +9
d
}
const fn mul_assign(A { a }: A) -> u32 {
// Mutables:
let mut d: u32 = a + 1;
d *= 2;
// 2^1 * (a + 1)
// Array projection
let mut arr: [u32; 1] = [1];
arr[0] *= 2;
d *= arr[0];
// 2^2 * (a + 1)
// Field projection:
let mut foo: Foo<u32> = Foo(1);
let mut bar: Bar<u32> = Bar { x: 1 };
foo.0 *= 2;
bar.x *= 2;
d *= foo.0 + bar.x;
// 2^4 * (a + 1)
// Array + Field projection:
let mut arr: [Foo<u32>; 1] = [Foo(1)];
arr[0].0 *= 2;
d *= arr[0].0;
let mut arr: [Bar<u32>; 1] = [Bar { x: 1 }];
arr[0].x *= 2;
d *= arr[0].x;
// 2^6 * (a + 1)
// Field + Array projection:
let mut arr: Foo<[u32; 1]> = Foo([1]);
(arr.0)[0] *= 2;
d *= (arr.0)[0];
let mut arr: Bar<[u32; 1]> = Bar { x: [1] };
arr.x[0] *= 2;
d *= arr.x[0];
// 2^8 * (a + 1)
d
}
const fn div_assign(a: [u32; 1]) -> u32 {
let a = a[0];
// Mutables:
let mut d: u32 = 1024 * a;
d /= 2;
// 512
// Array projection
let mut arr: [u32; 1] = [4];
arr[0] /= 2;
d /= arr[0];
// 256
// Field projection:
let mut foo: Foo<u32> = Foo(4);
let mut bar: Bar<u32> = Bar { x: 4 };
foo.0 /= 2;
bar.x /= 2;
d /= foo.0;
d /= bar.x;
// 64
// Array + Field projection:
let mut arr: [Foo<u32>; 1] = [Foo(4)];
arr[0].0 /= 2;
d /= arr[0].0;
let mut arr: [Bar<u32>; 1] = [Bar { x: 4 }];
arr[0].x /= 2;
d /= arr[0].x;
// 16
// Field + Array projection:
let mut arr: Foo<[u32; 1]> = Foo([4]);
(arr.0)[0] /= 2;
d /= (arr.0)[0];
let mut arr: Bar<[u32; 1]> = Bar { x: [4] };
arr.x[0] /= 2;
d /= arr.x[0];
// 4
d
}
const fn rem_assign(W(a): W) -> u32 {
// Mutables:
let mut d: u32 = a;
d %= 10;
d += 10;
// Array projection
let mut arr: [u32; 1] = [3];
arr[0] %= 2;
d %= 9 + arr[0];
d += 10;
// Field projection:
let mut foo: Foo<u32> = Foo(5);
let mut bar: Bar<u32> = Bar { x: 7 };
foo.0 %= 2;
bar.x %= 2;
d %= 8 + foo.0 + bar.x;
d += 10;
// Array + Field projection:
let mut arr: [Foo<u32>; 1] = [Foo(4)];
arr[0].0 %= 3;
d %= 9 + arr[0].0;
d += 10;
let mut arr: [Bar<u32>; 1] = [Bar { x: 7 }];
arr[0].x %= 3;
d %= 9 + arr[0].x;
d += 10;
// Field + Array projection:
let mut arr: Foo<[u32; 1]> = Foo([6]);
(arr.0)[0] %= 5;
d %= 9 + (arr.0)[0];
let mut arr: Bar<[u32; 1]> = Bar { x: [11] };
arr.x[0] %= 5;
d %= 9 + arr.x[0];
d
}
const fn sub_assign(W(a): W) -> u32 {
// Mutables:
let mut d: u32 = a;
d -= 1;
// Array projection
let mut arr: [u32; 1] = [2];
arr[0] -= 1;
d -= arr[0];
// Field projection:
let mut foo: Foo<u32> = Foo(2);
let mut bar: Bar<u32> = Bar { x: 2 };
foo.0 -= 1;
bar.x -= 1;
d -= foo.0 + bar.x;
// Array + Field projection:
let mut arr: [Foo<u32>; 1] = [Foo(2)];
arr[0].0 -= 1;
d -= arr[0].0;
let mut arr: [Bar<u32>; 1] = [Bar { x: 2 }];
arr[0].x -= 1;
d -= arr[0].x;
// Field + Array projection:
let mut arr: Foo<[u32; 1]> = Foo([2]);
(arr.0)[0] -= 1;
d -= (arr.0)[0];
let mut arr: Bar<[u32; 1]> = Bar { x: [2] };
arr.x[0] -= 1;
d -= arr.x[0];
d
}
const fn shl_assign(W(a): W) -> u32 {
// Mutables:
let mut d: u32 = a;
d <<= 1; // 10
// Array projection
let mut arr: [u32; 1] = [1];
arr[0] <<= 1;
d <<= arr[0]; // 10 << 2
// Field projection:
let mut foo: Foo<u32> = Foo(1);
let mut bar: Bar<u32> = Bar { x: 1 };
foo.0 <<= 1;
bar.x <<= 1;
d <<= foo.0 + bar.x; // 1000 << 4
// Array + Field projection:
let mut arr: [Foo<u32>; 1] = [Foo(1)];
arr[0].0 <<= 1;
d <<= arr[0].0; // 1000_0000 << 2
let mut arr: [Bar<u32>; 1] = [Bar { x: 1 }];
arr[0].x <<= 1;
d <<= arr[0].x; // 1000_0000_00 << 2
// Field + Array projection:
let mut arr: Foo<[u32; 1]> = Foo([1]);
(arr.0)[0] <<= 1;
d <<= (arr.0)[0]; // 1000_0000_0000 << 2
let mut arr: Bar<[u32; 1]> = Bar { x: [1] };
arr.x[0] <<= 1;
d <<= arr.x[0]; // 1000_0000_0000_00 << 2
d
}
const fn shr_assign(W(a): W) -> u32 {
// Mutables:
let mut d: u32 = a;
d >>= 1; // /= 2
// Array projection
let mut arr: [u32; 1] = [2];
arr[0] >>= 1;
d >>= arr[0]; // /= 4
// Field projection:
let mut foo: Foo<u32> = Foo(2);
let mut bar: Bar<u32> = Bar { x: 2 };
foo.0 >>= 1;
bar.x >>= 1;
d >>= foo.0 + bar.x; // /= 16
// Array + Field projection:
let mut arr: [Foo<u32>; 1] = [Foo(2)];
arr[0].0 >>= 1;
d >>= arr[0].0; // /= 32
let mut arr: [Bar<u32>; 1] = [Bar { x: 2 }];
arr[0].x >>= 1;
d >>= arr[0].x; // /= 64
// Field + Array projection:
let mut arr: Foo<[u32; 1]> = Foo([2]);
(arr.0)[0] >>= 1;
d >>= (arr.0)[0]; // /= 128
let mut arr: Bar<[u32; 1]> = Bar { x: [2] };
arr.x[0] >>= 1;
d >>= arr.x[0]; // /= 256
d
}
const fn bit_and_assign(W(a): W) -> u32 {
let f = 0b1111_1111_1111_1111;
// Mutables:
let mut d: u32 = a;
d &= 0b1111_1111_1111_1110;
// Array projection
let mut arr: [u32; 1] = [f];
arr[0] &= 0b1111_1111_1111_1101;
d &= arr[0];
// Field projection:
let mut foo: Foo<u32> = Foo(f);
let mut bar: Bar<u32> = Bar { x: f };
foo.0 &= 0b1111_1111_1111_0111;
bar.x &= 0b1111_1111_1101_1111;
d &= foo.0 & bar.x;
// Array + Field projection:
let mut arr: [Foo<u32>; 1] = [Foo(f)];
arr[0].0 &= 0b1111_1110_1111_1111;
d &= arr[0].0;
let mut arr: [Bar<u32>; 1] = [Bar { x: f }];
arr[0].x &= 0b1111_1101_1111_1111;
d &= arr[0].x;
// Field + Array projection:
let mut arr: Foo<[u32; 1]> = Foo([f]);
(arr.0)[0] &= 0b1011_1111_1111_1111;
d &= (arr.0)[0];
let mut arr: Bar<[u32; 1]> = Bar { x: [f] };
arr.x[0] &= 0b0111_1111_1111_1111;
d &= arr.x[0];
d
}
const fn bit_or_assign(W(a): W) -> u32 {
let f = 0b0000_0000_0000_0000;
// Mutables:
let mut d: u32 = a;
d |= 0b0000_0000_0000_0001;
// Array projection
let mut arr: [u32; 1] = [f];
arr[0] |= 0b0000_0000_0000_1001;
d |= arr[0];
// Field projection:
let mut foo: Foo<u32> = Foo(f);
let mut bar: Bar<u32> = Bar { x: f };
foo.0 |= 0b0000_0000_0001_0000;
bar.x |= 0b0000_0000_0100_0000;
d |= foo.0 | bar.x;
// Array + Field projection:
let mut arr: [Foo<u32>; 1] = [Foo(f)];
arr[0].0 |= 0b0000_0001_0000_0000;
d |= arr[0].0;
let mut arr: [Bar<u32>; 1] = [Bar { x: f }];
arr[0].x |= 0b0000_0010_0000_0000;
d |= arr[0].x;
// Field + Array projection:
let mut arr: Foo<[u32; 1]> = Foo([f]);
(arr.0)[0] |= 0b1000_0000_0000_0000;
d |= (arr.0)[0]; // /= 128
let mut arr: Bar<[u32; 1]> = Bar { x: [f] };
arr.x[0] |= 0b1100_0000_0000_0000;
d |= arr.x[0]; // /= 256
d
}
const fn bit_xor_assign(W(a): W) -> u32 {
let f = 0b0000_0000_0000_0000;
// Mutables:
let mut d: u32 = a;
d ^= 0b0000_0000_0000_0001;
// Array projection
let mut arr: [u32; 1] = [f];
arr[0] ^= 0b0000_0000_0000_0010;
d ^= arr[0];
// Field projection:
let mut foo: Foo<u32> = Foo(f);
let mut bar: Bar<u32> = Bar { x: f };
foo.0 ^= 0b0000_0000_0001_0000;
bar.x ^= 0b0000_0000_1000_0000;
d ^= foo.0 ^ bar.x;
// Array + Field projection:
let mut arr: [Foo<u32>; 1] = [Foo(f)];
arr[0].0 ^= 0b0000_0001_0000_0000;
d ^= arr[0].0;
let mut arr: [Bar<u32>; 1] = [Bar { x: f }];
arr[0].x ^= 0b0000_0010_0000_0000;
d ^= arr[0].x;
// Field + Array projection:
let mut arr: Foo<[u32; 1]> = Foo([f]);
(arr.0)[0] ^= 0b0100_0000_0000_0000;
d ^= (arr.0)[0];
let mut arr: Bar<[u32; 1]> = Bar { x: [f] };
arr.x[0] ^= 0b1000_0000_0000_0000;
d ^= arr.x[0];
d
}
macro_rules! test {
($c:ident, $e:expr, $r:expr) => {
const $c: u32 = $e;
assert_eq!($c, $r);
assert_eq!($e, $r);
}
}
fn main() {
test!(BASICS, basics((2,)), 13);
test!(ADD, add_assign(W(1)), 10);
test!(MUL, mul_assign(A { a: 0 }), 256);
test!(DIV, div_assign([1]), 4);
test!(REM, rem_assign(W(5)), 5);
test!(SUB, sub_assign(W(8)), 0);
test!(SHL, shl_assign(W(1)), 0b1000_0000_0000_0000);
test!(SHR, shr_assign(W(256)), 1);
test!(AND, bit_and_assign(W(0b1011_1111_1111_1111_1111)), 0b0011_1100_1101_0100);
test!(OR, bit_or_assign(W(0b1011_0000_0000_0000)), 0b1111_0011_0101_1001);
test!(XOR, bit_xor_assign(W(0b0000_0000_0000_0000)), 0b1100_0011_1001_0011);
}

View file

@ -0,0 +1,279 @@
// compile-pass
#![feature(const_let, const_fn)]
struct Foo<T>(T);
struct Bar<T> { x: T }
struct W(f32);
struct A { a: f32 }
const fn basics((a,): (f32,)) -> f32 {
// Deferred assignment:
let b: f32;
b = a + 1.0;
// Immediate assignment:
let c: f32 = b + 1.0;
// Mutables:
let mut d: f32 = c + 1.0;
d = d + 1.0;
// +4 so far.
// No effect statements work:
; ;
1;
// Array projection
let mut arr: [f32; 1] = [0.0];
arr[0] = 1.0;
d = d + arr[0];
// +5
// Field projection:
let mut foo: Foo<f32> = Foo(0.0);
let mut bar: Bar<f32> = Bar { x: 0.0 };
foo.0 = 1.0;
bar.x = 1.0;
d = d + foo.0 + bar.x;
// +7
// Array + Field projection:
let mut arr: [Foo<f32>; 1] = [Foo(0.0)];
arr[0].0 = 1.0;
d = d + arr[0].0;
let mut arr: [Bar<f32>; 1] = [Bar { x: 0.0 }];
arr[0].x = 1.0;
d = d + arr[0].x;
// +9
// Field + Array projection:
let mut arr: Foo<[f32; 1]> = Foo([0.0]);
(arr.0)[0] = 1.0;
d = d + (arr.0)[0];
let mut arr: Bar<[f32; 1]> = Bar { x: [0.0] };
arr.x[0] = 1.0;
d = d + arr.x[0];
// +11
d
}
const fn add_assign(W(a): W) -> f32 {
// Mutables:
let mut d: f32 = a + 1.0;
d += 1.0;
// +2 so far.
// Array projection
let mut arr: [f32; 1] = [0.0];
arr[0] += 1.0;
d += arr[0];
// +3
// Field projection:
let mut foo: Foo<f32> = Foo(0.0);
let mut bar: Bar<f32> = Bar { x: 0.0 };
foo.0 += 1.0;
bar.x += 1.0;
d += foo.0 + bar.x;
// +5
// Array + Field projection:
let mut arr: [Foo<f32>; 1] = [Foo(0.0)];
arr[0].0 += 1.0;
d += arr[0].0;
let mut arr: [Bar<f32>; 1] = [Bar { x: 0.0 }];
arr[0].x += 1.0;
d += arr[0].x;
// +7
// Field + Array projection:
let mut arr: Foo<[f32; 1]> = Foo([0.0]);
(arr.0)[0] += 1.0;
d += (arr.0)[0];
let mut arr: Bar<[f32; 1]> = Bar { x: [0.0] };
arr.x[0] += 1.0;
d += arr.x[0];
// +9
d
}
const fn mul_assign(A { a }: A) -> f32 {
// Mutables:
let mut d: f32 = a + 1.0;
d *= 2.0;
// 2^1 * (a + 1)
// Array projection
let mut arr: [f32; 1] = [1.0];
arr[0] *= 2.0;
d *= arr[0];
// 2^2 * (a + 1)
// Field projection:
let mut foo: Foo<f32> = Foo(1.0);
let mut bar: Bar<f32> = Bar { x: 1.0 };
foo.0 *= 2.0;
bar.x *= 2.0;
d *= foo.0 + bar.x;
// 2^4 * (a + 1)
// Array + Field projection:
let mut arr: [Foo<f32>; 1] = [Foo(1.0)];
arr[0].0 *= 2.0;
d *= arr[0].0;
let mut arr: [Bar<f32>; 1] = [Bar { x: 1.0 }];
arr[0].x *= 2.0;
d *= arr[0].x;
// 2^6 * (a + 1)
// Field + Array projection:
let mut arr: Foo<[f32; 1]> = Foo([1.0]);
(arr.0)[0] *= 2.0;
d *= (arr.0)[0];
let mut arr: Bar<[f32; 1]> = Bar { x: [1.0] };
arr.x[0] *= 2.0;
d *= arr.x[0];
// 2^8 * (a + 1)
d
}
const fn div_assign(a: [f32; 1]) -> f32 {
let a = a[0];
// Mutables:
let mut d: f32 = 1024.0 * a;
d /= 2.0;
// 512
// Array projection
let mut arr: [f32; 1] = [4.0];
arr[0] /= 2.0;
d /= arr[0];
// 256
// Field projection:
let mut foo: Foo<f32> = Foo(4.0);
let mut bar: Bar<f32> = Bar { x: 4.0 };
foo.0 /= 2.0;
bar.x /= 2.0;
d /= foo.0;
d /= bar.x;
// 64
// Array + Field projection:
let mut arr: [Foo<f32>; 1] = [Foo(4.0)];
arr[0].0 /= 2.0;
d /= arr[0].0;
let mut arr: [Bar<f32>; 1] = [Bar { x: 4.0 }];
arr[0].x /= 2.0;
d /= arr[0].x;
// 16
// Field + Array projection:
let mut arr: Foo<[f32; 1]> = Foo([4.0]);
(arr.0)[0] /= 2.0;
d /= (arr.0)[0];
let mut arr: Bar<[f32; 1]> = Bar { x: [4.0] };
arr.x[0] /= 2.0;
d /= arr.x[0];
// 4
d
}
const fn rem_assign(W(a): W) -> f32 {
// Mutables:
let mut d: f32 = a;
d %= 10.0;
d += 10.0;
// Array projection
let mut arr: [f32; 1] = [3.0];
arr[0] %= 2.0;
d %= 9.0 + arr[0];
d += 10.0;
// Field projection:
let mut foo: Foo<f32> = Foo(5.0);
let mut bar: Bar<f32> = Bar { x: 7.0 };
foo.0 %= 2.0;
bar.x %= 2.0;
d %= 8.0 + foo.0 + bar.x;
d += 10.0;
// Array + Field projection:
let mut arr: [Foo<f32>; 1] = [Foo(4.0)];
arr[0].0 %= 3.0;
d %= 9.0 + arr[0].0;
d += 10.0;
let mut arr: [Bar<f32>; 1] = [Bar { x: 7.0 }];
arr[0].x %= 3.0;
d %= 9.0 + arr[0].x;
d += 10.0;
// Field + Array projection:
let mut arr: Foo<[f32; 1]> = Foo([6.0]);
(arr.0)[0] %= 5.0;
d %= 9.0 + (arr.0)[0];
let mut arr: Bar<[f32; 1]> = Bar { x: [11.0] };
arr.x[0] %= 5.0;
d %= 9.0 + arr.x[0];
d
}
const fn sub_assign(W(a): W) -> f32 {
// Mutables:
let mut d: f32 = a;
d -= 1.0;
// Array projection
let mut arr: [f32; 1] = [2.0];
arr[0] -= 1.0;
d -= arr[0];
// Field projection:
let mut foo: Foo<f32> = Foo(2.0);
let mut bar: Bar<f32> = Bar { x: 2.0 };
foo.0 -= 1.0;
bar.x -= 1.0;
d -= foo.0 + bar.x;
// Array + Field projection:
let mut arr: [Foo<f32>; 1] = [Foo(2.0)];
arr[0].0 -= 1.0;
d -= arr[0].0;
let mut arr: [Bar<f32>; 1] = [Bar { x: 2.0 }];
arr[0].x -= 1.0;
d -= arr[0].x;
// Field + Array projection:
let mut arr: Foo<[f32; 1]> = Foo([2.0]);
(arr.0)[0] -= 1.0;
d -= (arr.0)[0];
let mut arr: Bar<[f32; 1]> = Bar { x: [2.0] };
arr.x[0] -= 1.0;
d -= arr.x[0];
d
}
macro_rules! test {
($c:ident, $e:expr, $r:expr) => {
const $c: f32 = $e;
assert_eq!($c, $r);
assert_eq!($e, $r);
}
}
fn main() {
test!(BASICS, basics((2.0,)), 13.0);
test!(ADD, add_assign(W(1.0)), 10.0);
test!(MUL, mul_assign(A { a: 0.0 }), 256.0);
test!(DIV, div_assign([1.0]), 4.0);
test!(REM, rem_assign(W(5.0)), 5.0);
test!(SUB, sub_assign(W(8.0)), 0.0);
}

View file

@ -0,0 +1,16 @@
#![feature(underscore_const_names, const_let)]
const _: bool = false && false;
const _: bool = true && false;
const _: bool = {
let mut x = true && false;
//~^ ERROR new features like let bindings are not permitted
x
};
const _: bool = {
let x = true && false;
//~^ ERROR new features like let bindings are not permitted
x
};
fn main() {}

View file

@ -0,0 +1,26 @@
error: new features like let bindings are not permitted in constants which also use short circuiting operators
--> $DIR/const_short_circuit.rs:6:9
|
LL | let mut x = true && false;
| ^^^^^
|
note: use of `&&` operator here does not actually short circuit due to the const evaluator presently not being able to do control flow. See https://github.com/rust-lang/rust/issues/49146 for more information.
--> $DIR/const_short_circuit.rs:6:22
|
LL | let mut x = true && false;
| ^^
error: new features like let bindings are not permitted in constants which also use short circuiting operators
--> $DIR/const_short_circuit.rs:11:9
|
LL | let x = true && false;
| ^
|
note: use of `&&` operator here does not actually short circuit due to the const evaluator presently not being able to do control flow. See https://github.com/rust-lang/rust/issues/49146 for more information.
--> $DIR/const_short_circuit.rs:11:18
|
LL | let x = true && false;
| ^^
error: aborting due to 2 previous errors

View file

@ -115,7 +115,7 @@ LL | const fn foo30_5(b: bool) { while b { } } //~ ERROR not stable in const fn
error: local variables in const fn are unstable
--> $DIR/min_const_fn.rs:109:34
|
LL | const fn foo30_6() -> bool { let x = true; x } //~ ERROR local variables in const fn are unstable
LL | const fn foo30_6() -> bool { let x = true; x } //~ ERROR local variables in const fn
| ^
error: `if`, `match`, `&&` and `||` are not stable in const fn

View file

@ -106,7 +106,7 @@ const fn foo30_2(x: *mut u32) -> usize { x as usize }
const fn foo30_4(b: bool) -> usize { if b { 1 } else { 42 } }
//~^ ERROR `if`, `match`, `&&` and `||` are not stable in const fn
const fn foo30_5(b: bool) { while b { } } //~ ERROR not stable in const fn
const fn foo30_6() -> bool { let x = true; x } //~ ERROR local variables in const fn are unstable
const fn foo30_6() -> bool { let x = true; x } //~ ERROR local variables in const fn
const fn foo36(a: bool, b: bool) -> bool { a && b }
//~^ ERROR `if`, `match`, `&&` and `||` are not stable in const fn
const fn foo37(a: bool, b: bool) -> bool { a || b }

View file

@ -115,7 +115,7 @@ LL | const fn foo30_5(b: bool) { while b { } } //~ ERROR not stable in const fn
error: local variables in const fn are unstable
--> $DIR/min_const_fn.rs:109:34
|
LL | const fn foo30_6() -> bool { let x = true; x } //~ ERROR local variables in const fn are unstable
LL | const fn foo30_6() -> bool { let x = true; x } //~ ERROR local variables in const fn
| ^
error: `if`, `match`, `&&` and `||` are not stable in const fn

View file

@ -0,0 +1,17 @@
const fn mutable_ref_in_const() -> u8 {
let mut a = 0; //~ ERROR local variables in const fn
let b = &mut a;
*b
}
struct X;
impl X {
const fn inherent_mutable_ref_in_const() -> u8 {
let mut a = 0; //~ ERROR local variables in const fn
let b = &mut a;
*b
}
}
fn main() {}

View file

@ -0,0 +1,14 @@
error: local variables in const fn are unstable
--> $DIR/mutable_borrow.rs:2:9
|
LL | let mut a = 0; //~ ERROR local variables in const fn
| ^^^^^
error: local variables in const fn are unstable
--> $DIR/mutable_borrow.rs:11:13
|
LL | let mut a = 0; //~ ERROR local variables in const fn
| ^^^^^
error: aborting due to 2 previous errors

View file

@ -7,6 +7,7 @@
{
let b: *mut u32 = &mut a; //~ ERROR may only refer to immutable values
unsafe { *b = 5; } //~ ERROR dereferencing raw pointers in constants
//~^ contains unimplemented expression
}
&{a}
};

View file

@ -4,6 +4,12 @@ error[E0017]: references in constants may only refer to immutable values
LL | let b: *mut u32 = &mut a; //~ ERROR may only refer to immutable values
| ^^^^^^ constants require immutable values
error[E0019]: constant contains unimplemented expression type
--> $DIR/projection_qualif.rs:9:18
|
LL | unsafe { *b = 5; } //~ ERROR dereferencing raw pointers in constants
| ^^^^^^
error[E0658]: dereferencing raw pointers in constants is unstable (see issue #51911)
--> $DIR/projection_qualif.rs:9:18
|
@ -12,7 +18,7 @@ LL | unsafe { *b = 5; } //~ ERROR dereferencing raw pointers in constant
|
= help: add #![feature(const_raw_ptr_deref)] to the crate attributes to enable
error: aborting due to 2 previous errors
error: aborting due to 3 previous errors
Some errors occurred: E0017, E0658.
Some errors occurred: E0017, E0019, E0658.
For more information about an error, try `rustc --explain E0017`.

View file

@ -2,6 +2,14 @@ enum Foo {
Prob,
}
const FOO: u32 = match Foo::Prob {
Foo::Prob => 42, //~ ERROR unimplemented expression type
};
const BAR: u32 = match Foo::Prob {
x => 42, //~ ERROR unimplemented expression type
};
impl Foo {
pub const fn as_val(&self) -> u8 {
use self::Foo::*;

View file

@ -1,8 +1,21 @@
error[E0019]: constant contains unimplemented expression type
--> $DIR/single_variant_match_ice.rs:6:5
|
LL | Foo::Prob => 42, //~ ERROR unimplemented expression type
| ^^^^^^^^^
error[E0019]: constant contains unimplemented expression type
--> $DIR/single_variant_match_ice.rs:10:5
|
LL | x => 42, //~ ERROR unimplemented expression type
| ^
error: `if`, `match`, `&&` and `||` are not stable in const fn
--> $DIR/single_variant_match_ice.rs:10:13
--> $DIR/single_variant_match_ice.rs:18:13
|
LL | Prob => 0x1, //~ ERROR `if`, `match`, `&&` and `||` are not stable in const fn
| ^^^^
error: aborting due to previous error
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0019`.

View file

@ -10,13 +10,22 @@
// Test use of const let without feature gate.
#![feature(const_fn)]
const fn foo() -> usize {
const FOO: usize = {
//~^ ERROR statements in constants are unstable
//~| ERROR: let bindings in constants are unstable
let x = 42;
//~^ ERROR statements in constant functions are unstable
//~| ERROR: let bindings in constant functions are unstable
//~^ ERROR statements in constants are unstable
//~| ERROR: let bindings in constants are unstable
42
}
};
static BAR: usize = {
//~^ ERROR statements in statics are unstable
//~| ERROR: let bindings in statics are unstable
let x = 42;
//~^ ERROR statements in statics are unstable
//~| ERROR: let bindings in statics are unstable
42
};
fn main() {}

View file

@ -1,4 +1,4 @@
error[E0658]: let bindings in constant functions are unstable (see issue #48821)
error[E0658]: let bindings in constants are unstable (see issue #48821)
--> $DIR/feature-gate-const_let.rs:16:13
|
LL | let x = 42;
@ -6,7 +6,7 @@ LL | let x = 42;
|
= help: add #![feature(const_let)] to the crate attributes to enable
error[E0658]: statements in constant functions are unstable (see issue #48821)
error[E0658]: statements in constants are unstable (see issue #48821)
--> $DIR/feature-gate-const_let.rs:16:13
|
LL | let x = 42;
@ -14,6 +14,78 @@ LL | let x = 42;
|
= help: add #![feature(const_let)] to the crate attributes to enable
error: aborting due to 2 previous errors
error[E0658]: let bindings in constants are unstable (see issue #48821)
--> $DIR/feature-gate-const_let.rs:13:1
|
LL | / const FOO: usize = {
LL | | //~^ ERROR statements in constants are unstable
LL | | //~| ERROR: let bindings in constants are unstable
LL | | let x = 42;
... |
LL | | 42
LL | | };
| |__^
|
= help: add #![feature(const_let)] to the crate attributes to enable
error[E0658]: statements in constants are unstable (see issue #48821)
--> $DIR/feature-gate-const_let.rs:13:1
|
LL | / const FOO: usize = {
LL | | //~^ ERROR statements in constants are unstable
LL | | //~| ERROR: let bindings in constants are unstable
LL | | let x = 42;
... |
LL | | 42
LL | | };
| |__^
|
= help: add #![feature(const_let)] to the crate attributes to enable
error[E0658]: let bindings in statics are unstable (see issue #48821)
--> $DIR/feature-gate-const_let.rs:25:13
|
LL | let x = 42;
| ^^
|
= help: add #![feature(const_let)] to the crate attributes to enable
error[E0658]: statements in statics are unstable (see issue #48821)
--> $DIR/feature-gate-const_let.rs:25:13
|
LL | let x = 42;
| ^^
|
= help: add #![feature(const_let)] to the crate attributes to enable
error[E0658]: let bindings in statics are unstable (see issue #48821)
--> $DIR/feature-gate-const_let.rs:22:1
|
LL | / static BAR: usize = {
LL | | //~^ ERROR statements in statics are unstable
LL | | //~| ERROR: let bindings in statics are unstable
LL | | let x = 42;
... |
LL | | 42
LL | | };
| |__^
|
= help: add #![feature(const_let)] to the crate attributes to enable
error[E0658]: statements in statics are unstable (see issue #48821)
--> $DIR/feature-gate-const_let.rs:22:1
|
LL | / static BAR: usize = {
LL | | //~^ ERROR statements in statics are unstable
LL | | //~| ERROR: let bindings in statics are unstable
LL | | let x = 42;
... |
LL | | 42
LL | | };
| |__^
|
= help: add #![feature(const_let)] to the crate attributes to enable
error: aborting due to 8 previous errors
For more information about this error, try `rustc --explain E0658`.

View file

@ -8,15 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(const_fn)]
const fn x() {
let t = true;
//~^ ERROR let bindings in constant functions are unstable
//~| ERROR statements in constant functions are unstable
let t = true; //~ ERROR local variables in const fn
let x = || t;
//~^ ERROR let bindings in constant functions are unstable
//~| ERROR statements in constant functions are unstable
}
fn main() {}

View file

@ -1,35 +1,8 @@
error[E0658]: let bindings in constant functions are unstable (see issue #48821)
--> $DIR/issue-37550.rs:14:13
error: local variables in const fn are unstable
--> $DIR/issue-37550.rs:12:9
|
LL | let t = true;
| ^^^^
|
= help: add #![feature(const_let)] to the crate attributes to enable
LL | let t = true; //~ ERROR local variables in const fn
| ^
error[E0658]: statements in constant functions are unstable (see issue #48821)
--> $DIR/issue-37550.rs:14:13
|
LL | let t = true;
| ^^^^
|
= help: add #![feature(const_let)] to the crate attributes to enable
error: aborting due to previous error
error[E0658]: let bindings in constant functions are unstable (see issue #48821)
--> $DIR/issue-37550.rs:17:13
|
LL | let x = || t;
| ^^^^
|
= help: add #![feature(const_let)] to the crate attributes to enable
error[E0658]: statements in constant functions are unstable (see issue #48821)
--> $DIR/issue-37550.rs:17:13
|
LL | let x = || t;
| ^^^^
|
= help: add #![feature(const_let)] to the crate attributes to enable
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0658`.