Allow let bindings in const fn and constants

上级 68357487
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
use syntax::attr; use syntax::attr;
use syntax::ast::LitKind; use syntax::ast::LitKind;
use syntax::feature_gate::UnstableFeatures; use syntax::feature_gate::{UnstableFeatures, emit_feature_err, GateIssue};
use syntax_pos::{Span, DUMMY_SP}; use syntax_pos::{Span, DUMMY_SP};
use std::fmt; use std::fmt;
...@@ -120,8 +120,7 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { ...@@ -120,8 +120,7 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
rpo: ReversePostorder<'a, 'tcx>, rpo: ReversePostorder<'a, 'tcx>,
tcx: TyCtxt<'a, 'gcx, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
temp_qualif: IndexVec<Local, Option<Qualif>>, local_qualif: IndexVec<Local, Option<Qualif>>,
return_qualif: Option<Qualif>,
qualif: Qualif, qualif: Qualif,
const_fn_arg_vars: BitVector, const_fn_arg_vars: BitVector,
temp_promotion_state: IndexVec<Local, TempState>, temp_promotion_state: IndexVec<Local, TempState>,
...@@ -140,11 +139,11 @@ fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, ...@@ -140,11 +139,11 @@ fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let param_env = tcx.param_env(def_id); let param_env = tcx.param_env(def_id);
let mut temp_qualif = IndexVec::from_elem(None, &mir.local_decls); let mut local_qualif = IndexVec::from_elem(None, &mir.local_decls);
for arg in mir.args_iter() { for arg in mir.args_iter() {
let mut qualif = Qualif::NEEDS_DROP; let mut qualif = Qualif::NEEDS_DROP;
qualif.restrict(mir.local_decls[arg].ty, tcx, param_env); qualif.restrict(mir.local_decls[arg].ty, tcx, param_env);
temp_qualif[arg] = Some(qualif); local_qualif[arg] = Some(qualif);
} }
Qualifier { Qualifier {
...@@ -155,8 +154,7 @@ fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, ...@@ -155,8 +154,7 @@ fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
rpo, rpo,
tcx, tcx,
param_env, param_env,
temp_qualif, local_qualif,
return_qualif: None,
qualif: Qualif::empty(), qualif: Qualif::empty(),
const_fn_arg_vars: BitVector::new(mir.local_decls.len()), const_fn_arg_vars: BitVector::new(mir.local_decls.len()),
temp_promotion_state: temps, temp_promotion_state: temps,
...@@ -191,6 +189,11 @@ fn not_const(&mut self) { ...@@ -191,6 +189,11 @@ fn not_const(&mut self) {
fn statement_like(&mut self) { fn statement_like(&mut self) {
self.add(Qualif::NOT_CONST); self.add(Qualif::NOT_CONST);
if self.mode != Mode::Fn { if self.mode != Mode::Fn {
if self.span.allows_unstable() {
emit_feature_err(&self.tcx.sess.parse_sess, "const_let",
self.span, GateIssue::Language,
"statements in const fn are unstable");
}
let mut err = struct_span_err!( let mut err = struct_span_err!(
self.tcx.sess, self.tcx.sess,
self.span, self.span,
...@@ -266,6 +269,7 @@ fn try_consume(&mut self) -> bool { ...@@ -266,6 +269,7 @@ fn try_consume(&mut self) -> bool {
/// Assign the current qualification to the given destination. /// Assign the current qualification to the given destination.
fn assign(&mut self, dest: &Place<'tcx>, location: Location) { fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
trace!("assign: {:?}", dest);
let qualif = self.qualif; let qualif = self.qualif;
let span = self.span; let span = self.span;
let store = |slot: &mut Option<Qualif>| { let store = |slot: &mut Option<Qualif>| {
...@@ -281,20 +285,26 @@ fn assign(&mut self, dest: &Place<'tcx>, location: Location) { ...@@ -281,20 +285,26 @@ fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
if self.mir.local_kind(index) == LocalKind::Temp if self.mir.local_kind(index) == LocalKind::Temp
&& self.temp_promotion_state[index].is_promotable() { && self.temp_promotion_state[index].is_promotable() {
debug!("store to promotable temp {:?}", index); debug!("store to promotable temp {:?}", index);
store(&mut self.temp_qualif[index]); store(&mut self.local_qualif[index]);
} }
} }
return; return;
} }
match *dest { match *dest {
Place::Local(index) if (self.mir.local_kind(index) == LocalKind::Var ||
self.mir.local_kind(index) == LocalKind::Arg) &&
self.tcx.sess.features_untracked().const_let => {
debug!("store to var {:?}", index);
self.local_qualif[index] = Some(self.qualif);
}
Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp => { Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp => {
debug!("store to temp {:?}", index); debug!("store to temp {:?}", index);
store(&mut self.temp_qualif[index]) store(&mut self.local_qualif[index])
} }
Place::Local(index) if self.mir.local_kind(index) == LocalKind::ReturnPointer => { Place::Local(index) if self.mir.local_kind(index) == LocalKind::ReturnPointer => {
debug!("store to return place {:?}", index); debug!("store to return place {:?}", index);
store(&mut self.return_qualif) store(&mut self.local_qualif[RETURN_PLACE])
} }
Place::Projection(box Projection { Place::Projection(box Projection {
...@@ -302,7 +312,7 @@ fn assign(&mut self, dest: &Place<'tcx>, location: Location) { ...@@ -302,7 +312,7 @@ fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
elem: ProjectionElem::Deref elem: ProjectionElem::Deref
}) if self.mir.local_kind(index) == LocalKind::Temp }) if self.mir.local_kind(index) == LocalKind::Temp
&& self.mir.local_decls[index].ty.is_box() && self.mir.local_decls[index].ty.is_box()
&& self.temp_qualif[index].map_or(false, |qualif| { && self.local_qualif[index].map_or(false, |qualif| {
qualif.intersects(Qualif::NOT_CONST) qualif.intersects(Qualif::NOT_CONST)
}) => { }) => {
// Part of `box expr`, we should've errored // Part of `box expr`, we should've errored
...@@ -355,10 +365,13 @@ fn qualify_const(&mut self) -> (Qualif, Lrc<IdxSetBuf<Local>>) { ...@@ -355,10 +365,13 @@ fn qualify_const(&mut self) -> (Qualif, Lrc<IdxSetBuf<Local>>) {
TerminatorKind::FalseUnwind { .. } => None, TerminatorKind::FalseUnwind { .. } => None,
TerminatorKind::Return => { TerminatorKind::Return => {
if self.tcx.sess.features_untracked().const_let {
break;
}
// Check for unused values. This usually means // Check for unused values. This usually means
// there are extra statements in the AST. // there are extra statements in the AST.
for temp in mir.temps_iter() { for temp in mir.temps_iter() {
if self.temp_qualif[temp].is_none() { if self.local_qualif[temp].is_none() {
continue; continue;
} }
...@@ -408,7 +421,7 @@ fn qualify_const(&mut self) -> (Qualif, Lrc<IdxSetBuf<Local>>) { ...@@ -408,7 +421,7 @@ fn qualify_const(&mut self) -> (Qualif, Lrc<IdxSetBuf<Local>>) {
} }
} }
self.qualif = self.return_qualif.unwrap_or(Qualif::NOT_CONST); self.qualif = self.local_qualif[RETURN_PLACE].unwrap_or(Qualif::NOT_CONST);
// Account for errors in consts by using the // Account for errors in consts by using the
// conservative type qualification instead. // conservative type qualification instead.
...@@ -453,9 +466,15 @@ fn visit_local(&mut self, ...@@ -453,9 +466,15 @@ fn visit_local(&mut self,
LocalKind::ReturnPointer => { LocalKind::ReturnPointer => {
self.not_const(); self.not_const();
} }
LocalKind::Var => { LocalKind::Var if !self.tcx.sess.features_untracked().const_let => {
if self.mode != Mode::Fn && self.span.allows_unstable() {
emit_feature_err(&self.tcx.sess.parse_sess, "const_let",
self.span, GateIssue::Language,
"let bindings in const fn are unstable");
}
self.add(Qualif::NOT_CONST); self.add(Qualif::NOT_CONST);
} }
LocalKind::Var |
LocalKind::Arg | LocalKind::Arg |
LocalKind::Temp => { LocalKind::Temp => {
if let LocalKind::Arg = kind { if let LocalKind::Arg = kind {
...@@ -466,7 +485,7 @@ fn visit_local(&mut self, ...@@ -466,7 +485,7 @@ fn visit_local(&mut self,
self.add(Qualif::NOT_PROMOTABLE); self.add(Qualif::NOT_PROMOTABLE);
} }
if let Some(qualif) = self.temp_qualif[local] { if let Some(qualif) = self.local_qualif[local] {
self.add(qualif); self.add(qualif);
} else { } else {
self.not_const(); self.not_const();
...@@ -588,7 +607,7 @@ fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { ...@@ -588,7 +607,7 @@ fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
// Mark the consumed locals to indicate later drops are noops. // Mark the consumed locals to indicate later drops are noops.
if let Operand::Move(Place::Local(local)) = *operand { if let Operand::Move(Place::Local(local)) = *operand {
self.temp_qualif[local] = self.temp_qualif[local].map(|q| self.local_qualif[local] = self.local_qualif[local].map(|q|
q - Qualif::NEEDS_DROP q - Qualif::NEEDS_DROP
); );
} }
...@@ -1033,7 +1052,7 @@ fn visit_terminator_kind(&mut self, ...@@ -1033,7 +1052,7 @@ fn visit_terminator_kind(&mut self,
// HACK(eddyb) Emulate a bit of dataflow analysis, // HACK(eddyb) Emulate a bit of dataflow analysis,
// conservatively, that drop elaboration will do. // conservatively, that drop elaboration will do.
let needs_drop = if let Place::Local(local) = *place { let needs_drop = if let Place::Local(local) = *place {
if self.temp_qualif[local].map_or(true, |q| q.intersects(Qualif::NEEDS_DROP)) { if self.local_qualif[local].map_or(true, |q| q.intersects(Qualif::NEEDS_DROP)) {
Some(self.mir.local_decls[local].source_info.span) Some(self.mir.local_decls[local].source_info.span)
} else { } else {
None None
...@@ -1070,7 +1089,8 @@ fn visit_assign(&mut self, ...@@ -1070,7 +1089,8 @@ fn visit_assign(&mut self,
// Check the allowed const fn argument forms. // Check the allowed const fn argument forms.
if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) { if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) {
if self.mir.local_kind(index) == LocalKind::Var && if self.mir.local_kind(index) == LocalKind::Var &&
self.const_fn_arg_vars.insert(index.index()) { self.const_fn_arg_vars.insert(index.index()) &&
!self.tcx.sess.features_untracked().const_let {
// Direct use of an argument is permitted. // Direct use of an argument is permitted.
match *rvalue { match *rvalue {
...@@ -1086,6 +1106,11 @@ fn visit_assign(&mut self, ...@@ -1086,6 +1106,11 @@ fn visit_assign(&mut self,
// Avoid a generic error for other uses of arguments. // Avoid a generic error for other uses of arguments.
if self.qualif.intersects(Qualif::FN_ARGUMENT) { if self.qualif.intersects(Qualif::FN_ARGUMENT) {
let decl = &self.mir.local_decls[index]; let decl = &self.mir.local_decls[index];
if decl.source_info.span.allows_unstable() {
emit_feature_err(&self.tcx.sess.parse_sess, "const_let",
decl.source_info.span, GateIssue::Language,
"locals and patterns in const fn are unstable");
}
let mut err = struct_span_err!( let mut err = struct_span_err!(
self.tcx.sess, self.tcx.sess,
decl.source_info.span, decl.source_info.span,
......
...@@ -214,6 +214,9 @@ pub fn walk_feature_fields<F>(&self, mut f: F) ...@@ -214,6 +214,9 @@ pub fn walk_feature_fields<F>(&self, mut f: F)
// Allows the definition of `const fn` functions. // Allows the definition of `const fn` functions.
(active, const_fn, "1.2.0", Some(24111), None), (active, const_fn, "1.2.0", Some(24111), None),
// Allows let bindings and destructuring in `const fn` functions and constants.
(active, const_let, "1.22.1", Some(48821), None),
// Allows using #[prelude_import] on glob `use` items. // Allows using #[prelude_import] on glob `use` items.
// //
// rustc internal // rustc internal
......
// Copyright 2014 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.
#![feature(const_let)]
type Array = [u32; { let x = 2; 5 }];
pub fn main() {}
// Copyright 2014 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.
#![feature(const_let)]
enum Foo {
Bar = { let x = 1; 3 }
}
pub fn main() {}
// 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.
// test that certain things are disallowed in const fn signatures
#![feature(const_fn, const_let)]
// no destructuring
const fn i((
a,
b
): (u32, u32)) -> u32 {
a + b
}
fn main() {}
// Copyright 2017 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.
#![feature(const_fn, const_let)]
const fn x() {
let t = true;
let x = || t;
}
fn main() {}
// Copyright 2018 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.
// https://github.com/rust-lang/rust/issues/48821
#![feature(const_fn, const_let)]
const fn foo(i: usize) -> usize {
let x = i;
x
}
static FOO: usize = foo(42);
const fn bar(mut i: usize) -> usize {
i += 8;
let x = &i;
*x
}
static BAR: usize = bar(42);
const fn boo(mut i: usize) -> usize {
{
let mut x = i;
x += 10;
i = x;
}
i
}
static BOO: usize = boo(42);
fn main() {
assert!(FOO == 42);
assert!(BAR == 50);
assert!(BOO == 52);
}
// Copyright 2018 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.
// Test use of const let without feature gate.
#![feature(const_fn)]
const fn foo() -> usize {
let x = 42; //~ ERROR blocks in constant functions are limited to items and tail expressions
42
}
fn main() {}
error[E0016]: blocks in constant functions are limited to items and tail expressions
--> $DIR/feature-gate-const_let.rs:16:13
|
LL | let x = 42; //~ ERROR blocks in constant functions are limited to items and tail expressions
| ^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0016`.
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册