From 9872160836291d880852986841afe83a57f08045 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Mar 2018 21:16:37 +0200 Subject: [PATCH] Allow let bindings in const fn and constants --- src/librustc_mir/transform/qualify_consts.rs | 61 +++++++++++++------ src/libsyntax/feature_gate.rs | 3 + .../ctfe/const-block-non-item-statement-3.rs | 15 +++++ .../ctfe/const-block-non-item-statement.rs | 17 ++++++ .../ctfe/const-fn-destructuring-arg.rs | 23 +++++++ src/test/run-pass/ctfe/issue-37550.rs | 18 ++++++ src/test/run-pass/ctfe/locals-in-const-fn.rs | 45 ++++++++++++++ src/test/ui/feature-gate-const_let.rs | 20 ++++++ src/test/ui/feature-gate-const_let.stderr | 9 +++ 9 files changed, 193 insertions(+), 18 deletions(-) create mode 100644 src/test/run-pass/ctfe/const-block-non-item-statement-3.rs create mode 100644 src/test/run-pass/ctfe/const-block-non-item-statement.rs create mode 100644 src/test/run-pass/ctfe/const-fn-destructuring-arg.rs create mode 100644 src/test/run-pass/ctfe/issue-37550.rs create mode 100644 src/test/run-pass/ctfe/locals-in-const-fn.rs create mode 100644 src/test/ui/feature-gate-const_let.rs create mode 100644 src/test/ui/feature-gate-const_let.stderr diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index fd4ba1d7562..e79f3ac9d11 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -32,7 +32,7 @@ use rustc_target::spec::abi::Abi; use syntax::attr; 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 std::fmt; @@ -120,8 +120,7 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { rpo: ReversePostorder<'a, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, - temp_qualif: IndexVec>, - return_qualif: Option, + local_qualif: IndexVec>, qualif: Qualif, const_fn_arg_vars: BitVector, temp_promotion_state: IndexVec, @@ -140,11 +139,11 @@ fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, 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() { let mut qualif = Qualif::NEEDS_DROP; qualif.restrict(mir.local_decls[arg].ty, tcx, param_env); - temp_qualif[arg] = Some(qualif); + local_qualif[arg] = Some(qualif); } Qualifier { @@ -155,8 +154,7 @@ fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, rpo, tcx, param_env, - temp_qualif, - return_qualif: None, + local_qualif, qualif: Qualif::empty(), const_fn_arg_vars: BitVector::new(mir.local_decls.len()), temp_promotion_state: temps, @@ -191,6 +189,11 @@ fn not_const(&mut self) { fn statement_like(&mut self) { self.add(Qualif::NOT_CONST); 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!( self.tcx.sess, self.span, @@ -266,6 +269,7 @@ fn try_consume(&mut self) -> bool { /// Assign the current qualification to the given destination. fn assign(&mut self, dest: &Place<'tcx>, location: Location) { + trace!("assign: {:?}", dest); let qualif = self.qualif; let span = self.span; let store = |slot: &mut Option| { @@ -281,20 +285,26 @@ fn assign(&mut self, dest: &Place<'tcx>, location: Location) { if self.mir.local_kind(index) == LocalKind::Temp && self.temp_promotion_state[index].is_promotable() { debug!("store to promotable temp {:?}", index); - store(&mut self.temp_qualif[index]); + store(&mut self.local_qualif[index]); } } return; } 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 => { 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 => { debug!("store to return place {:?}", index); - store(&mut self.return_qualif) + store(&mut self.local_qualif[RETURN_PLACE]) } Place::Projection(box Projection { @@ -302,7 +312,7 @@ fn assign(&mut self, dest: &Place<'tcx>, location: Location) { elem: ProjectionElem::Deref }) if self.mir.local_kind(index) == LocalKind::Temp && 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) }) => { // Part of `box expr`, we should've errored @@ -355,10 +365,13 @@ fn qualify_const(&mut self) -> (Qualif, Lrc>) { TerminatorKind::FalseUnwind { .. } => None, TerminatorKind::Return => { + if self.tcx.sess.features_untracked().const_let { + break; + } // Check for unused values. This usually means // there are extra statements in the AST. for temp in mir.temps_iter() { - if self.temp_qualif[temp].is_none() { + if self.local_qualif[temp].is_none() { continue; } @@ -408,7 +421,7 @@ fn qualify_const(&mut self) -> (Qualif, Lrc>) { } } - 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 // conservative type qualification instead. @@ -453,9 +466,15 @@ fn visit_local(&mut self, LocalKind::ReturnPointer => { 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); } + LocalKind::Var | LocalKind::Arg | LocalKind::Temp => { if let LocalKind::Arg = kind { @@ -466,7 +485,7 @@ fn visit_local(&mut self, self.add(Qualif::NOT_PROMOTABLE); } - if let Some(qualif) = self.temp_qualif[local] { + if let Some(qualif) = self.local_qualif[local] { self.add(qualif); } else { self.not_const(); @@ -588,7 +607,7 @@ fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { // Mark the consumed locals to indicate later drops are noops. 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 ); } @@ -1033,7 +1052,7 @@ fn visit_terminator_kind(&mut self, // HACK(eddyb) Emulate a bit of dataflow analysis, // conservatively, that drop elaboration will do. 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) } else { None @@ -1070,7 +1089,8 @@ fn visit_assign(&mut self, // Check the allowed const fn argument forms. if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) { 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. match *rvalue { @@ -1086,6 +1106,11 @@ fn visit_assign(&mut self, // Avoid a generic error for other uses of arguments. if self.qualif.intersects(Qualif::FN_ARGUMENT) { 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!( self.tcx.sess, decl.source_info.span, diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 709c3653b02..3a02646d0af 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -214,6 +214,9 @@ pub fn walk_feature_fields(&self, mut f: F) // Allows the definition of `const fn` functions. (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. // // rustc internal diff --git a/src/test/run-pass/ctfe/const-block-non-item-statement-3.rs b/src/test/run-pass/ctfe/const-block-non-item-statement-3.rs new file mode 100644 index 00000000000..e233107169c --- /dev/null +++ b/src/test/run-pass/ctfe/const-block-non-item-statement-3.rs @@ -0,0 +1,15 @@ +// 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 or the MIT license +// , 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() {} diff --git a/src/test/run-pass/ctfe/const-block-non-item-statement.rs b/src/test/run-pass/ctfe/const-block-non-item-statement.rs new file mode 100644 index 00000000000..b5a9bfb45a1 --- /dev/null +++ b/src/test/run-pass/ctfe/const-block-non-item-statement.rs @@ -0,0 +1,17 @@ +// 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 or the MIT license +// , 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() {} diff --git a/src/test/run-pass/ctfe/const-fn-destructuring-arg.rs b/src/test/run-pass/ctfe/const-fn-destructuring-arg.rs new file mode 100644 index 00000000000..a73a15b1762 --- /dev/null +++ b/src/test/run-pass/ctfe/const-fn-destructuring-arg.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , 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() {} diff --git a/src/test/run-pass/ctfe/issue-37550.rs b/src/test/run-pass/ctfe/issue-37550.rs new file mode 100644 index 00000000000..27796a5feea --- /dev/null +++ b/src/test/run-pass/ctfe/issue-37550.rs @@ -0,0 +1,18 @@ +// 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 or the MIT license +// , 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() {} diff --git a/src/test/run-pass/ctfe/locals-in-const-fn.rs b/src/test/run-pass/ctfe/locals-in-const-fn.rs new file mode 100644 index 00000000000..8c153315c25 --- /dev/null +++ b/src/test/run-pass/ctfe/locals-in-const-fn.rs @@ -0,0 +1,45 @@ +// 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 or the MIT license +// , 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); +} diff --git a/src/test/ui/feature-gate-const_let.rs b/src/test/ui/feature-gate-const_let.rs new file mode 100644 index 00000000000..04d2fd5ccd1 --- /dev/null +++ b/src/test/ui/feature-gate-const_let.rs @@ -0,0 +1,20 @@ +// 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 or the MIT license +// , 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() {} diff --git a/src/test/ui/feature-gate-const_let.stderr b/src/test/ui/feature-gate-const_let.stderr new file mode 100644 index 00000000000..a07281ded8d --- /dev/null +++ b/src/test/ui/feature-gate-const_let.stderr @@ -0,0 +1,9 @@ +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`. -- GitLab