From 9872160836291d880852986841afe83a57f08045 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Mar 2018 21:16:37 +0200 Subject: [PATCH 1/6] 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::middle::lang_items; 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 @@ impl<'a, 'tcx> Qualifier<'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 @@ impl<'a, 'tcx> Qualifier<'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 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { 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 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { /// 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 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { 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 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { 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 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { 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 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { } } - 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 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { 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 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { 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 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { // 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 @@ This does not pose a problem by itself because they can't be accessed directly." // 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 @@ This does not pose a problem by itself because they can't be accessed directly." // 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 @@ This does not pose a problem by itself because they can't be accessed directly." // 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 @@ declare_features! ( // 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`. From d7bf358dbd778f2c0a32936b959e868918e082ca Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 7 May 2018 11:30:41 +0200 Subject: [PATCH 2/6] Be more explicit about what's skipped --- src/librustc_mir/transform/qualify_consts.rs | 67 ++++++++++---------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index e79f3ac9d11..97acfbbe8fd 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -366,42 +366,41 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { 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.local_qualif[temp].is_none() { - continue; + // Check for unused values. This usually means + // there are extra statements in the AST. + for temp in mir.temps_iter() { + if self.local_qualif[temp].is_none() { + continue; + } + + let state = self.temp_promotion_state[temp]; + if let TempState::Defined { location, uses: 0 } = state { + let data = &mir[location.block]; + let stmt_idx = location.statement_index; + + // Get the span for the initialization. + let source_info = if stmt_idx < data.statements.len() { + data.statements[stmt_idx].source_info + } else { + data.terminator().source_info + }; + self.span = source_info.span; + + // Treat this as a statement in the AST. + self.statement_like(); + } } - let state = self.temp_promotion_state[temp]; - if let TempState::Defined { location, uses: 0 } = state { - let data = &mir[location.block]; - let stmt_idx = location.statement_index; - - // Get the span for the initialization. - let source_info = if stmt_idx < data.statements.len() { - data.statements[stmt_idx].source_info - } else { - data.terminator().source_info - }; - self.span = source_info.span; - - // Treat this as a statement in the AST. - self.statement_like(); - } - } - - // Make sure there are no extra unassigned variables. - self.qualif = Qualif::NOT_CONST; - for index in mir.vars_iter() { - if !self.const_fn_arg_vars.contains(index.index()) { - debug!("unassigned variable {:?}", index); - self.assign(&Place::Local(index), Location { - block: bb, - statement_index: usize::MAX, - }); + // Make sure there are no extra unassigned variables. + self.qualif = Qualif::NOT_CONST; + for index in mir.vars_iter() { + if !self.const_fn_arg_vars.contains(index.index()) { + debug!("unassigned variable {:?}", index); + self.assign(&Place::Local(index), Location { + block: bb, + statement_index: usize::MAX, + }); + } } } From 8a5eb689583163eef39e6b595506341ebfe19633 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 8 May 2018 13:59:26 +0200 Subject: [PATCH 3/6] Report let bindings and statements as unstable --- src/librustc_mir/transform/qualify_consts.rs | 35 +++++++------------ .../const-block-non-item-statement-2.rs | 12 ++++--- .../const-block-non-item-statement-3.rs | 6 ++-- .../const-block-non-item-statement.rs | 6 ++-- .../const-fn-destructuring-arg.rs | 10 ++++-- .../const-fn-not-safe-for-const.rs | 10 ++++-- src/test/compile-fail/issue-18118.rs | 7 ++-- src/test/compile-fail/issue-37550.rs | 8 +++-- src/test/compile-fail/issue32829.rs | 25 +++++++------ .../ctfe/const-fn-destructuring-arg.rs | 2 +- src/test/ui/const-eval/const_let.rs | 30 ++++++++++++++++ src/test/ui/const-eval/const_let.stderr | 15 ++++++++ src/test/ui/const-fn-error.rs | 3 +- src/test/ui/const-fn-error.stderr | 24 +++++++++---- src/test/ui/feature-gate-const_let.rs | 4 ++- src/test/ui/feature-gate-const_let.stderr | 18 +++++++--- 16 files changed, 150 insertions(+), 65 deletions(-) create mode 100644 src/test/ui/const-eval/const_let.rs create mode 100644 src/test/ui/const-eval/const_let.stderr diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 97acfbbe8fd..bb554b5e806 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -32,7 +32,7 @@ use rustc::middle::lang_items; use rustc_target::spec::abi::Abi; use syntax::attr; use syntax::ast::LitKind; -use syntax::feature_gate::{UnstableFeatures, emit_feature_err, GateIssue}; +use syntax::feature_gate::{UnstableFeatures, feature_err, emit_feature_err, GateIssue}; use syntax_pos::{Span, DUMMY_SP}; use std::fmt; @@ -189,17 +189,12 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { 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, + let mut err = feature_err( + &self.tcx.sess.parse_sess, + "const_let", self.span, - E0016, - "blocks in {}s are limited to items and tail expressions", - self.mode + GateIssue::Language, + &format!("statements in {}s are unstable", self.mode), ); if self.tcx.sess.teach(&err.get_code().unwrap()) { err.note("Blocks in constants may only contain items (such as constant, function \ @@ -365,7 +360,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { TerminatorKind::FalseUnwind { .. } => None, TerminatorKind::Return => { - if self.tcx.sess.features_untracked().const_let { + if !self.tcx.sess.features_untracked().const_let { // Check for unused values. This usually means // there are extra statements in the AST. for temp in mir.temps_iter() { @@ -466,10 +461,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { self.not_const(); } LocalKind::Var if !self.tcx.sess.features_untracked().const_let => { - if self.mode != Mode::Fn && self.span.allows_unstable() { + if self.mode != Mode::Fn { emit_feature_err(&self.tcx.sess.parse_sess, "const_let", self.span, GateIssue::Language, - "let bindings in const fn are unstable"); + &format!("let bindings in {}s are unstable",self.mode)); } self.add(Qualif::NOT_CONST); } @@ -1105,15 +1100,11 @@ This does not pose a problem by itself because they can't be accessed directly." // 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, + let mut err = feature_err( + &self.tcx.sess.parse_sess, + "const_let", decl.source_info.span, - E0022, + GateIssue::Language, "arguments of constant functions can only be immutable by-value bindings" ); if self.tcx.sess.teach(&err.get_code().unwrap()) { diff --git a/src/test/compile-fail/const-block-non-item-statement-2.rs b/src/test/compile-fail/const-block-non-item-statement-2.rs index 83166c9bd4b..f80d55cb342 100644 --- a/src/test/compile-fail/const-block-non-item-statement-2.rs +++ b/src/test/compile-fail/const-block-non-item-statement-2.rs @@ -9,18 +9,20 @@ // except according to those terms. const A: usize = { 1; 2 }; -//~^ ERROR: blocks in constants are limited to items and tail expressions +//~^ ERROR statements in constants are unstable const B: usize = { { } 2 }; -//~^ ERROR: blocks in constants are limited to items and tail expressions +//~^ ERROR statements in constants are unstable macro_rules! foo { - () => (()) //~ ERROR: blocks in constants are limited to items and tail expressions + () => (()) //~ ERROR statements in constants are unstable } const C: usize = { foo!(); 2 }; const D: usize = { let x = 4; 2 }; -//~^ ERROR: blocks in constants are limited to items and tail expressions -//~^^ ERROR: blocks in constants are limited to items and tail expressions +//~^ ERROR let bindings in constants are unstable +//~| ERROR statements in constants are unstable +//~| ERROR let bindings in constants are unstable +//~| ERROR statements in constants are unstable pub fn main() {} diff --git a/src/test/compile-fail/const-block-non-item-statement-3.rs b/src/test/compile-fail/const-block-non-item-statement-3.rs index 70703791101..cfa4b778dde 100644 --- a/src/test/compile-fail/const-block-non-item-statement-3.rs +++ b/src/test/compile-fail/const-block-non-item-statement-3.rs @@ -9,7 +9,9 @@ // except according to those terms. type Array = [u32; { let x = 2; 5 }]; -//~^ ERROR: blocks in constants are limited to items and tail expressions -//~^^ ERROR: blocks in constants are limited to items and tail expressions +//~^ ERROR let bindings in constants are unstable +//~| ERROR statements in constants are unstable +//~| ERROR let bindings in constants are unstable +//~| ERROR statements in constants are unstable pub fn main() {} diff --git a/src/test/compile-fail/const-block-non-item-statement.rs b/src/test/compile-fail/const-block-non-item-statement.rs index 802e660b904..f974a24c26f 100644 --- a/src/test/compile-fail/const-block-non-item-statement.rs +++ b/src/test/compile-fail/const-block-non-item-statement.rs @@ -10,8 +10,10 @@ enum Foo { Bar = { let x = 1; 3 } - //~^ ERROR: blocks in constants are limited to items and tail expressions - //~^^ ERROR: blocks in constants are limited to items and tail expressions + //~^ ERROR let bindings in constants are unstable + //~| ERROR statements in constants are unstable + //~| ERROR let bindings in constants are unstable + //~| ERROR statements in constants are unstable } pub fn main() {} diff --git a/src/test/compile-fail/const-fn-destructuring-arg.rs b/src/test/compile-fail/const-fn-destructuring-arg.rs index c3d5975fe01..e239bd701c5 100644 --- a/src/test/compile-fail/const-fn-destructuring-arg.rs +++ b/src/test/compile-fail/const-fn-destructuring-arg.rs @@ -8,16 +8,20 @@ // 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 +// test that certain things are disallowed in constant functionssignatures #![feature(const_fn)] // no destructuring const fn i(( - a, //~ ERROR: E0022 - b //~ ERROR: E0022 + a, + //~^ ERROR arguments of constant functions can only be immutable by-value bindings + b + //~^ ERROR arguments of constant functions can only be immutable by-value bindings ): (u32, u32)) -> u32 { a + b + //~^ ERROR let bindings in constant functions are unstable + //~| ERROR let bindings in constant functions are unstable } fn main() {} diff --git a/src/test/compile-fail/const-fn-not-safe-for-const.rs b/src/test/compile-fail/const-fn-not-safe-for-const.rs index 48877a60d25..d985bae1f24 100644 --- a/src/test/compile-fail/const-fn-not-safe-for-const.rs +++ b/src/test/compile-fail/const-fn-not-safe-for-const.rs @@ -38,9 +38,15 @@ const fn get_Y_addr() -> &'static u32 { } const fn get() -> u32 { - let x = 22; //~ ERROR E0016 - let y = 44; //~ ERROR E0016 + 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 x + y + //~^ ERROR let bindings in constant functions are unstable + //~| ERROR let bindings in constant functions are unstable } fn main() { diff --git a/src/test/compile-fail/issue-18118.rs b/src/test/compile-fail/issue-18118.rs index 35e57dffb6c..7194c159c1e 100644 --- a/src/test/compile-fail/issue-18118.rs +++ b/src/test/compile-fail/issue-18118.rs @@ -10,9 +10,12 @@ pub fn main() { const z: &'static isize = { - //~^ ERROR blocks in constants are limited to items and tail expressions + //~^ ERROR let bindings in constants are unstable + //~| ERROR statements in constants are unstable let p = 3; - //~^ ERROR blocks in constants are limited to items and tail expressions + //~^ ERROR let bindings in constants are unstable + //~| ERROR statements in constants are unstable &p //~ ERROR `p` does not live long enough + //~^ ERROR let bindings in constants are unstable }; } diff --git a/src/test/compile-fail/issue-37550.rs b/src/test/compile-fail/issue-37550.rs index e1f7f64e01a..af1f6ef5ed4 100644 --- a/src/test/compile-fail/issue-37550.rs +++ b/src/test/compile-fail/issue-37550.rs @@ -11,8 +11,12 @@ #![feature(const_fn)] const fn x() { - let t = true; //~ ERROR blocks in constant functions are limited to items and tail expressions - let x = || t; //~ ERROR blocks in constant functions are limited to items and tail expressions + let t = true; + //~^ ERROR let bindings in constant functions are unstable + //~| ERROR statements in constant functions are unstable + let x = || t; + //~^ ERROR let bindings in constant functions are unstable + //~| ERROR statements in constant functions are unstable } fn main() {} diff --git a/src/test/compile-fail/issue32829.rs b/src/test/compile-fail/issue32829.rs index 9a84322ad06..2b223bac8e6 100644 --- a/src/test/compile-fail/issue32829.rs +++ b/src/test/compile-fail/issue32829.rs @@ -14,7 +14,8 @@ const bad : u32 = { { - 5; //~ ERROR: blocks in constants are limited to items and tail expressions + 5; + //~^ ERROR statements in constants are unstable 0 } }; @@ -22,7 +23,7 @@ const bad : u32 = { const bad_two : u32 = { { invalid(); - //~^ ERROR: blocks in constants are limited to items and tail expressions + //~^ ERROR statements in constants are unstable //~^^ ERROR: calls in constants are limited to constant functions, tuple structs and tuple variants 0 } @@ -31,14 +32,15 @@ const bad_two : u32 = { const bad_three : u32 = { { valid(); - //~^ ERROR: blocks in constants are limited to items and tail expressions + //~^ ERROR statements in constants are unstable 0 } }; static bad_four : u32 = { { - 5; //~ ERROR: blocks in statics are limited to items and tail expressions + 5; + //~^ ERROR statements in statics are unstable 0 } }; @@ -46,8 +48,8 @@ static bad_four : u32 = { static bad_five : u32 = { { invalid(); - //~^ ERROR: blocks in statics are limited to items and tail expressions - //~^^ ERROR: calls in statics are limited to constant functions, tuple structs and tuple variants + //~^ ERROR: calls in statics are limited to constant functions, tuple structs and tuple variants + //~| ERROR statements in statics are unstable 0 } }; @@ -55,14 +57,15 @@ static bad_five : u32 = { static bad_six : u32 = { { valid(); - //~^ ERROR: blocks in statics are limited to items and tail expressions + //~^ ERROR statements in statics are unstable 0 } }; static mut bad_seven : u32 = { { - 5; //~ ERROR: blocks in statics are limited to items and tail expressions + 5; + //~^ ERROR statements in statics are unstable 0 } }; @@ -70,8 +73,8 @@ static mut bad_seven : u32 = { static mut bad_eight : u32 = { { invalid(); - //~^ ERROR: blocks in statics are limited to items and tail expressions - //~^^ ERROR: calls in statics are limited to constant functions, tuple structs and tuple variants + //~^ ERROR statements in statics are unstable + //~| ERROR: calls in statics are limited to constant functions, tuple structs and tuple variants 0 } }; @@ -79,7 +82,7 @@ static mut bad_eight : u32 = { static mut bad_nine : u32 = { { valid(); - //~^ ERROR: blocks in statics are limited to items and tail expressions + //~^ ERROR statements in statics are unstable 0 } }; diff --git a/src/test/run-pass/ctfe/const-fn-destructuring-arg.rs b/src/test/run-pass/ctfe/const-fn-destructuring-arg.rs index a73a15b1762..d22c94074fb 100644 --- a/src/test/run-pass/ctfe/const-fn-destructuring-arg.rs +++ b/src/test/run-pass/ctfe/const-fn-destructuring-arg.rs @@ -8,7 +8,7 @@ // 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 +// test that certain things are disallowed in constant functionssignatures #![feature(const_fn, const_let)] diff --git a/src/test/ui/const-eval/const_let.rs b/src/test/ui/const-eval/const_let.rs new file mode 100644 index 00000000000..602d4da24f3 --- /dev/null +++ b/src/test/ui/const-eval/const_let.rs @@ -0,0 +1,30 @@ +// 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. + +#![feature(const_let)] + +fn main() {} + +struct FakeNeedsDrop; + +impl Drop for FakeNeedsDrop { + fn drop(&mut self) {} +} + +// ok +const X: FakeNeedsDrop = { let x = FakeNeedsDrop; x }; + +// error +const Y: FakeNeedsDrop = { let mut x = FakeNeedsDrop; x = FakeNeedsDrop; x }; +//~^ ERROR constant contains unimplemented expression type + +// error +const Z: () = { let mut x = None; x = Some(FakeNeedsDrop); }; +//~^ ERROR constant contains unimplemented expression type diff --git a/src/test/ui/const-eval/const_let.stderr b/src/test/ui/const-eval/const_let.stderr new file mode 100644 index 00000000000..86e3482fda6 --- /dev/null +++ b/src/test/ui/const-eval/const_let.stderr @@ -0,0 +1,15 @@ +error[E0019]: constant contains unimplemented expression type + --> $DIR/const_let.rs:25:55 + | +LL | const Y: FakeNeedsDrop = { let mut x = FakeNeedsDrop; x = FakeNeedsDrop; x }; + | ^ + +error[E0019]: constant contains unimplemented expression type + --> $DIR/const_let.rs:29:35 + | +LL | const Z: () = { let mut x = None; x = Some(FakeNeedsDrop); }; + | ^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0019`. diff --git a/src/test/ui/const-fn-error.rs b/src/test/ui/const-fn-error.rs index 9e09f66776c..17dc9f94fe1 100644 --- a/src/test/ui/const-fn-error.rs +++ b/src/test/ui/const-fn-error.rs @@ -14,7 +14,8 @@ const X : usize = 2; const fn f(x: usize) -> usize { let mut sum = 0; - //~^ ERROR E0016 + //~^ let bindings in constant functions are unstable + //~| statements in constant functions are unstable for i in 0..x { //~^ ERROR E0015 //~| ERROR E0019 diff --git a/src/test/ui/const-fn-error.stderr b/src/test/ui/const-fn-error.stderr index 767f28ff7b1..29edc2756af 100644 --- a/src/test/ui/const-fn-error.stderr +++ b/src/test/ui/const-fn-error.stderr @@ -1,23 +1,33 @@ -error[E0016]: blocks in constant functions are limited to items and tail expressions +error[E0658]: let bindings in constant functions are unstable (see issue #48821) --> $DIR/const-fn-error.rs:16:19 | LL | let mut sum = 0; | ^ + | + = 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-error.rs:16:19 + | +LL | let mut sum = 0; + | ^ + | + = help: add #![feature(const_let)] to the crate attributes to enable error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants - --> $DIR/const-fn-error.rs:18:14 + --> $DIR/const-fn-error.rs:19:14 | LL | for i in 0..x { | ^^^^ error[E0019]: constant function contains unimplemented expression type - --> $DIR/const-fn-error.rs:18:14 + --> $DIR/const-fn-error.rs:19:14 | LL | for i in 0..x { | ^^^^ error[E0080]: constant evaluation error - --> $DIR/const-fn-error.rs:18:14 + --> $DIR/const-fn-error.rs:19:14 | LL | for i in 0..x { | ^^^^ calling non-const fn `>::into_iter` @@ -26,12 +36,12 @@ LL | let a : [i32; f(X)]; | ---- inside call to `f` | note: for constant expression here - --> $DIR/const-fn-error.rs:29:13 + --> $DIR/const-fn-error.rs:30:13 | LL | let a : [i32; f(X)]; | ^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -Some errors occurred: E0015, E0016, E0019, E0080. +Some errors occurred: E0015, E0019, E0080, E0658. For more information about an error, try `rustc --explain E0015`. diff --git a/src/test/ui/feature-gate-const_let.rs b/src/test/ui/feature-gate-const_let.rs index 04d2fd5ccd1..05d02e62bc8 100644 --- a/src/test/ui/feature-gate-const_let.rs +++ b/src/test/ui/feature-gate-const_let.rs @@ -13,7 +13,9 @@ #![feature(const_fn)] const fn foo() -> usize { - let x = 42; //~ ERROR blocks in constant functions are limited to items and tail expressions + let x = 42; + //~^ ERROR statements in constant functions are unstable + //~| ERROR: let bindings in constant functions are unstable 42 } diff --git a/src/test/ui/feature-gate-const_let.stderr b/src/test/ui/feature-gate-const_let.stderr index a07281ded8d..6a7f6255678 100644 --- a/src/test/ui/feature-gate-const_let.stderr +++ b/src/test/ui/feature-gate-const_let.stderr @@ -1,9 +1,19 @@ -error[E0016]: blocks in constant functions are limited to items and tail expressions +error[E0658]: let bindings in constant functions are unstable (see issue #48821) --> $DIR/feature-gate-const_let.rs:16:13 | -LL | let x = 42; //~ ERROR blocks in constant functions are limited to items and tail expressions +LL | let x = 42; | ^^ + | + = help: add #![feature(const_let)] to the crate attributes to enable -error: aborting due to previous error +error[E0658]: statements in constant functions are unstable (see issue #48821) + --> $DIR/feature-gate-const_let.rs:16:13 + | +LL | let x = 42; + | ^^ + | + = help: add #![feature(const_let)] to the crate attributes to enable -For more information about this error, try `rustc --explain E0016`. +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. From cfb68164246a28299ecfcb26bb8e463e49e10fbb Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 8 May 2018 14:35:45 +0200 Subject: [PATCH 4/6] Remove now unused error code --- src/librustc_mir/diagnostics.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs index 4f36c3888b9..f195775bf86 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -597,21 +597,6 @@ See [RFC 911] for more details on the design of `const fn`s. [RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md "##, -E0016: r##" -Blocks in constants may only contain items (such as constant, function -definition, etc...) and a tail expression. Erroneous code example: - -```compile_fail,E0016 -const FOO: i32 = { let x = 0; x }; // 'x' isn't an item! -``` - -To avoid it, you have to replace the non-item object: - -``` -const FOO: i32 = { const X : i32 = 0; X }; -``` -"##, - E0017: r##" References in statics and constants may only refer to immutable values. Erroneous code example: From 61c58c2f6710913a315c4c13dfc3b3cb088af18a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 8 May 2018 17:29:09 +0200 Subject: [PATCH 5/6] Fix a typo in a comment --- src/test/compile-fail/const-fn-destructuring-arg.rs | 2 +- src/test/run-pass/ctfe/const-fn-destructuring-arg.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/compile-fail/const-fn-destructuring-arg.rs b/src/test/compile-fail/const-fn-destructuring-arg.rs index e239bd701c5..fce1688716d 100644 --- a/src/test/compile-fail/const-fn-destructuring-arg.rs +++ b/src/test/compile-fail/const-fn-destructuring-arg.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// test that certain things are disallowed in constant functionssignatures +// test that certain things are disallowed in constant functions #![feature(const_fn)] diff --git a/src/test/run-pass/ctfe/const-fn-destructuring-arg.rs b/src/test/run-pass/ctfe/const-fn-destructuring-arg.rs index d22c94074fb..8b832976aab 100644 --- a/src/test/run-pass/ctfe/const-fn-destructuring-arg.rs +++ b/src/test/run-pass/ctfe/const-fn-destructuring-arg.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// test that certain things are disallowed in constant functionssignatures +// test that certain things are disallowed in constant functions #![feature(const_fn, const_let)] From 2483c812176c8ecca54207517a2ba5500805b205 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 22 May 2018 13:56:37 +0200 Subject: [PATCH 6/6] Deduplicate match arms --- src/librustc_mir/transform/qualify_consts.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index bb554b5e806..999e3d89fc7 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -293,14 +293,11 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { 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); + Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp || + self.mir.local_kind(index) == LocalKind::ReturnPointer => { + debug!("store to {:?} (temp or return pointer)", 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.local_qualif[RETURN_PLACE]) - } Place::Projection(box Projection { base: Place::Local(index), @@ -772,7 +769,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } if let Place::Local(local) = *place { if self.mir.local_kind(local) == LocalKind::Temp { - if let Some(qualif) = self.temp_qualif[local] { + if let Some(qualif) = self.local_qualif[local] { // `forbidden_mut` is false, so we can safely ignore // `MUTABLE_INTERIOR` from the local's qualifications. // This allows borrowing fields which don't have