From e76172c51593ee5e8dbbc171e899064dd7cd5b7e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 15 Jun 2018 19:20:09 +0200 Subject: [PATCH 1/5] Do not promote constants that contain unpromotable code --- src/librustc_mir/transform/qualify_consts.rs | 16 ++++---- src/librustc_passes/rvalue_promotion.rs | 17 ++++---- src/test/ui/const-eval/issue-44578.nll.stderr | 39 ------------------- src/test/ui/const-eval/issue-44578.rs | 5 +-- src/test/ui/const-eval/issue-44578.stderr | 22 +++++------ src/test/ui/const-eval/issue-50814-2.rs | 3 +- src/test/ui/const-eval/issue-50814-2.stderr | 25 +++++------- src/test/ui/const-eval/issue-50814.rs | 3 +- src/test/ui/const-eval/issue-50814.stderr | 25 +++++------- .../ui/const-eval/unpromotable_constant.rs | 26 +++++++++++++ .../const-eval/unpromotable_constant.stderr | 24 ++++++++++++ 11 files changed, 99 insertions(+), 106 deletions(-) delete mode 100644 src/test/ui/const-eval/issue-44578.nll.stderr create mode 100644 src/test/ui/const-eval/unpromotable_constant.rs create mode 100644 src/test/ui/const-eval/unpromotable_constant.stderr diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index f3fba5b47be14..8e2a551f4e976 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -566,14 +566,15 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { ProjectionElem::Field(..) | ProjectionElem::Index(_) => { - if this.mode == Mode::Fn { - let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx); - if let Some(def) = base_ty.ty_adt_def() { - if def.is_union() { - this.not_const(); - } + let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx); + // accessing union fields is never allowed inside promoteds + if let Some(def) = base_ty.ty_adt_def() { + if def.is_union() { + this.add(Qualif::NOT_PROMOTABLE); } - } else if this.qualif.intersects(Qualif::STATIC) { + } + // Only functions may refer to statics directly + if this.mode != Mode::Fn && this.qualif.intersects(Qualif::STATIC) { span_err!(this.tcx.sess, this.span, E0494, "cannot refer to the interior of another \ static, use a constant instead"); @@ -615,6 +616,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } = constant.literal { // Don't peek inside trait associated constants. if self.tcx.trait_of_item(def_id).is_some() { + self.add(Qualif::NOT_PROMOTABLE); self.add_type(ty); } else { let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id); diff --git a/src/librustc_passes/rvalue_promotion.rs b/src/librustc_passes/rvalue_promotion.rs index 82ac112b534e9..b3835b9e08457 100644 --- a/src/librustc_passes/rvalue_promotion.rs +++ b/src/librustc_passes/rvalue_promotion.rs @@ -267,7 +267,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> { } if self.promotable { - self.result.insert(ex.hir_id.local_id); + assert!(self.result.insert(ex.hir_id.local_id)); } self.promotable &= outer; } @@ -361,17 +361,14 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node Def::Const(did) | Def::AssociatedConst(did) => { - let promotable = if v.tcx.trait_of_item(did).is_some() { + if v.tcx.trait_of_item(did).is_some() { // Don't peek inside trait associated constants. - false + v.promotable = false; } else { - v.tcx.at(e.span).const_is_rvalue_promotable_to_static(did) - }; - - // Just in case the type is more specific than the definition, - // e.g. impl associated const with type parameters, check it. - // Also, trait associated consts are relaxed by this. - v.promotable &= promotable || v.type_has_only_promotable_values(node_ty); + // Just in case the type is more specific than the definition, + // e.g. impl associated const with type parameters, check it. + v.promotable &= v.tcx.at(e.span).const_is_rvalue_promotable_to_static(did); + } } _ => { diff --git a/src/test/ui/const-eval/issue-44578.nll.stderr b/src/test/ui/const-eval/issue-44578.nll.stderr deleted file mode 100644 index ad4f08966c012..0000000000000 --- a/src/test/ui/const-eval/issue-44578.nll.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error[E0080]: referenced constant - --> $DIR/issue-44578.rs:35:5 - | -LL | const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; - | ------------------------------------ index out of bounds: the len is 1 but the index is 1 -... -LL | println!("{}", as Foo>::AMT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) - -error[E0080]: erroneous constant used - --> $DIR/issue-44578.rs:35:5 - | -LL | println!("{}", as Foo>::AMT); - | ^^^^^^^^^^^^^^^--------------------------^^ - | | - | referenced constant has errors - | - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) - -error[E0080]: referenced constant - --> $DIR/issue-44578.rs:35:20 - | -LL | const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; - | ------------------------------------ index out of bounds: the len is 1 but the index is 1 -... -LL | println!("{}", as Foo>::AMT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0080]: erroneous constant used - --> $DIR/issue-44578.rs:35:20 - | -LL | println!("{}", as Foo>::AMT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-eval/issue-44578.rs b/src/test/ui/const-eval/issue-44578.rs index 59ac4ab311c6c..03bfb117120b5 100644 --- a/src/test/ui/const-eval/issue-44578.rs +++ b/src/test/ui/const-eval/issue-44578.rs @@ -12,6 +12,7 @@ trait Foo { const AMT: usize; + //~^ ERROR E0080 } enum Bar { @@ -20,7 +21,7 @@ enum Bar { } impl Foo for Bar { - const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; + const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; //~ ERROR E0080 } impl Foo for u8 { @@ -33,6 +34,4 @@ impl Foo for u16 { fn main() { println!("{}", as Foo>::AMT); - //~^ ERROR erroneous constant used - //~| ERROR E0080 } diff --git a/src/test/ui/const-eval/issue-44578.stderr b/src/test/ui/const-eval/issue-44578.stderr index 28a723a069edf..5d418efffbec5 100644 --- a/src/test/ui/const-eval/issue-44578.stderr +++ b/src/test/ui/const-eval/issue-44578.stderr @@ -1,17 +1,17 @@ -error[E0080]: referenced constant - --> $DIR/issue-44578.rs:35:20 +error[E0080]: constant evaluation error + --> $DIR/issue-44578.rs:24:24 | -LL | const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; - | ------------------------------------ index out of bounds: the len is 1 but the index is 1 -... -LL | println!("{}", as Foo>::AMT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; //~ ERROR E0080 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ index out of bounds: the len is 1 but the index is 1 -error[E0080]: erroneous constant used - --> $DIR/issue-44578.rs:35:20 +error[E0080]: constant evaluation error + --> $DIR/issue-44578.rs:14:5 | -LL | println!("{}", as Foo>::AMT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors +LL | const AMT: usize; + | ^^^^^^^^^^^^^^^^^ +... +LL | const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; //~ ERROR E0080 + | ------------------------------------ index out of bounds: the len is 1 but the index is 1 error: aborting due to 2 previous errors diff --git a/src/test/ui/const-eval/issue-50814-2.rs b/src/test/ui/const-eval/issue-50814-2.rs index af627ee5fbe70..e88837e00e0c2 100644 --- a/src/test/ui/const-eval/issue-50814-2.rs +++ b/src/test/ui/const-eval/issue-50814-2.rs @@ -23,8 +23,7 @@ impl Foo for A { } fn foo() -> &'static usize { - & as Foo>::BAR //~ ERROR erroneous constant used -//~| ERROR E0080 + & as Foo>::BAR //~ ERROR borrowed value does not live long enough } impl C for () { diff --git a/src/test/ui/const-eval/issue-50814-2.stderr b/src/test/ui/const-eval/issue-50814-2.stderr index 3c59cb0e2bc72..b43b7235ab3f0 100644 --- a/src/test/ui/const-eval/issue-50814-2.stderr +++ b/src/test/ui/const-eval/issue-50814-2.stderr @@ -1,20 +1,13 @@ -error[E0080]: referenced constant - --> $DIR/issue-50814-2.rs:26:5 +error[E0597]: borrowed value does not live long enough + --> $DIR/issue-50814-2.rs:26:6 | -LL | const BAR: usize = [5, 6, 7][T::BOO]; - | ----------------- index out of bounds: the len is 3 but the index is 42 -... -LL | & as Foo>::BAR //~ ERROR erroneous constant used - | ^^^^^^^^^^^^^^^^^^^^^^ - -error[E0080]: erroneous constant used - --> $DIR/issue-50814-2.rs:26:5 +LL | & as Foo>::BAR //~ ERROR borrowed value does not live long enough + | ^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough +LL | } + | - temporary value only lives until here | -LL | & as Foo>::BAR //~ ERROR erroneous constant used - | ^--------------------- - | | - | referenced constant has errors + = note: borrowed value must be valid for the static lifetime... -error: aborting due to 2 previous errors +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 E0597`. diff --git a/src/test/ui/const-eval/issue-50814.rs b/src/test/ui/const-eval/issue-50814.rs index 8f2752453b52e..db11bf11d9dc0 100644 --- a/src/test/ui/const-eval/issue-50814.rs +++ b/src/test/ui/const-eval/issue-50814.rs @@ -24,8 +24,7 @@ impl Unsigned for Sum { } fn foo(_: T) -> &'static u8 { - &Sum::::MAX //~ ERROR erroneous constant used -//~| ERROR E0080 + &Sum::::MAX //~ ERROR borrowed value does not live long enough } fn main() { diff --git a/src/test/ui/const-eval/issue-50814.stderr b/src/test/ui/const-eval/issue-50814.stderr index 145279ccc033d..d97b1fe481350 100644 --- a/src/test/ui/const-eval/issue-50814.stderr +++ b/src/test/ui/const-eval/issue-50814.stderr @@ -1,20 +1,13 @@ -error[E0080]: referenced constant - --> $DIR/issue-50814.rs:27:5 +error[E0597]: borrowed value does not live long enough + --> $DIR/issue-50814.rs:27:6 | -LL | const MAX: u8 = A::MAX + B::MAX; - | --------------- attempt to add with overflow -... -LL | &Sum::::MAX //~ ERROR erroneous constant used - | ^^^^^^^^^^^^^^^^^^ - -error[E0080]: erroneous constant used - --> $DIR/issue-50814.rs:27:5 +LL | &Sum::::MAX //~ ERROR borrowed value does not live long enough + | ^^^^^^^^^^^^^^^^^ temporary value does not live long enough +LL | } + | - temporary value only lives until here | -LL | &Sum::::MAX //~ ERROR erroneous constant used - | ^----------------- - | | - | referenced constant has errors + = note: borrowed value must be valid for the static lifetime... -error: aborting due to 2 previous errors +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 E0597`. diff --git a/src/test/ui/const-eval/unpromotable_constant.rs b/src/test/ui/const-eval/unpromotable_constant.rs new file mode 100644 index 0000000000000..897bf86f3c381 --- /dev/null +++ b/src/test/ui/const-eval/unpromotable_constant.rs @@ -0,0 +1,26 @@ +// 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. + +#![allow(const_err)] + +union Bad { + usize: usize, + ptr: &'static u32, +} + +const FOO: usize = unsafe { + Bad { ptr: &1 }.usize +}; + + +fn main() { + let x: &'static usize = &FOO; //~ ERROR does not live long enough + let y: &'static usize = &(FOO % 42); //~ ERROR does not live long enough +} diff --git a/src/test/ui/const-eval/unpromotable_constant.stderr b/src/test/ui/const-eval/unpromotable_constant.stderr new file mode 100644 index 0000000000000..05e4624897360 --- /dev/null +++ b/src/test/ui/const-eval/unpromotable_constant.stderr @@ -0,0 +1,24 @@ +error[E0597]: borrowed value does not live long enough + --> $DIR/unpromotable_constant.rs:24:30 + | +LL | let x: &'static usize = &FOO; //~ ERROR does not live long enough + | ^^^ temporary value does not live long enough +LL | let y: &'static usize = &(FOO % 42); //~ ERROR does not live long enough +LL | } + | - temporary value only lives until here + | + = note: borrowed value must be valid for the static lifetime... + +error[E0597]: borrowed value does not live long enough + --> $DIR/unpromotable_constant.rs:25:30 + | +LL | let y: &'static usize = &(FOO % 42); //~ ERROR does not live long enough + | ^^^^^^^^^^ temporary value does not live long enough +LL | } + | - temporary value only lives until here + | + = note: borrowed value must be valid for the static lifetime... + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. From 912bc724709fbfe08039de6fef509f317e062fad Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sun, 17 Jun 2018 19:40:24 +0200 Subject: [PATCH 2/5] Don't promote calls to const fns containing non-promotable content --- src/librustc/hir/map/blocks.rs | 8 +- src/librustc/ty/steal.rs | 5 + src/librustc_metadata/decoder.rs | 14 +- src/librustc_metadata/encoder.rs | 22 +- src/librustc_metadata/schema.rs | 3 +- src/librustc_mir/build/mod.rs | 2 +- src/librustc_mir/transform/mod.rs | 4 + src/librustc_mir/transform/qualify_consts.rs | 236 +++++++++++++++++- src/librustc_passes/rvalue_promotion.rs | 45 +++- src/libstd/io/stdio.rs | 2 +- src/libstd/thread/local.rs | 4 +- .../dont_promote_unstable_const_fn.nll.stderr | 1 + .../dont_promote_unstable_const_fn.rs | 1 + .../dont_promote_unstable_const_fn.stderr | 13 +- .../promoted_const_fn_fail.nll.stderr | 14 ++ .../ui/const-eval/promoted_const_fn_fail.rs | 6 +- .../const-eval/promoted_const_fn_fail.stderr | 26 +- 17 files changed, 356 insertions(+), 50 deletions(-) create mode 100644 src/test/ui/const-eval/promoted_const_fn_fail.nll.stderr diff --git a/src/librustc/hir/map/blocks.rs b/src/librustc/hir/map/blocks.rs index 362c0bf07f73b..170efdf8e330f 100644 --- a/src/librustc/hir/map/blocks.rs +++ b/src/librustc/hir/map/blocks.rs @@ -52,6 +52,12 @@ impl MaybeFnLike for ast::Item { } } +impl MaybeFnLike for ast::ImplItem { + fn is_fn_like(&self) -> bool { + match self.node { ast::ImplItemKind::Method(..) => true, _ => false, } + } +} + impl MaybeFnLike for ast::TraitItem { fn is_fn_like(&self) -> bool { match self.node { @@ -144,7 +150,7 @@ impl<'a> FnLikeNode<'a> { let fn_like = match node { map::NodeItem(item) => item.is_fn_like(), map::NodeTraitItem(tm) => tm.is_fn_like(), - map::NodeImplItem(_) => true, + map::NodeImplItem(it) => it.is_fn_like(), map::NodeExpr(e) => e.is_fn_like(), _ => false }; diff --git a/src/librustc/ty/steal.rs b/src/librustc/ty/steal.rs index 842c0d6573432..32a9c4c84e922 100644 --- a/src/librustc/ty/steal.rs +++ b/src/librustc/ty/steal.rs @@ -49,6 +49,11 @@ impl Steal { }) } + /// In case of cyclic queries, use this function to break the cycle + pub fn try_borrow(&self) -> ReadGuard> { + self.value.borrow() + } + pub fn steal(&self) -> T { let value_ref = &mut *self.value.try_write().expect("stealing value which is locked"); let value = mem::replace(value_ref, None); diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index fd00cde375b90..7be3cb30c83b4 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -415,9 +415,9 @@ impl<'tcx> EntryKind<'tcx> { EntryKind::ForeignMutStatic => Def::Static(did, true), EntryKind::Struct(_, _) => Def::Struct(did), EntryKind::Union(_, _) => Def::Union(did), - EntryKind::Fn(_) | + EntryKind::Fn(..) | EntryKind::ForeignFn(_) => Def::Fn(did), - EntryKind::Method(_) => Def::Method(did), + EntryKind::Method(..) => Def::Method(did), EntryKind::Type => Def::TyAlias(did), EntryKind::AssociatedType(_) => Def::AssociatedTy(did), EntryKind::Mod(_) => Def::Mod(did), @@ -765,7 +765,13 @@ impl<'a, 'tcx> CrateMetadata { match self.entry(id).kind { EntryKind::AssociatedConst(_, data, _) | EntryKind::Const(data, _) => data.ast_promotable, - _ => bug!(), + EntryKind::Fn(ref data) => { + data.decode(self).qualif.map_or(false, |q| q.ast_promotable) + }, + EntryKind::Method(ref data) => { + data.decode(self).fn_data.qualif.map_or(false, |q| q.ast_promotable) + }, + _ => bug!("{:?}", id), } } @@ -791,6 +797,8 @@ impl<'a, 'tcx> CrateMetadata { EntryKind::AssociatedConst(AssociatedContainer::ImplFinal, qualif, _) => { qualif.mir } + EntryKind::Method(data) => data.decode(self).fn_data.qualif.map_or(0, |q| q.mir), + EntryKind::Fn(data) => data.decode(self).qualif.map_or(0, |q| q.mir), _ => bug!(), } } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index ab30ff7f072b7..4c269ecd89644 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -827,6 +827,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { constness: hir::Constness::NotConst, arg_names, sig: self.lazy(&tcx.fn_sig(def_id)), + qualif: None, } } else { bug!() @@ -880,6 +881,15 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { !self.tcx.sess.opts.output_types.should_codegen() } + fn const_fn_qualif(&self, def_id: DefId, body_id: hir::BodyId, span: Span) -> Option { + if self.tcx.is_const_fn(def_id) { + let mir = self.tcx.at(span).mir_const_qualif(def_id).0; + Some(self.const_qualif(mir, body_id)) + } else { + None + } + } + fn const_qualif(&self, mir: u8, body_id: hir::BodyId) -> ConstQualif { let body_owner_def_id = self.tcx.hir.body_owner_def_id(body_id); let ast_promotable = self.tcx.const_is_rvalue_promotable_to_static(body_owner_def_id); @@ -915,11 +925,12 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { } } ty::AssociatedKind::Method => { - let fn_data = if let hir::ImplItemKind::Method(ref sig, body) = ast_item.node { + let fn_data = if let hir::ImplItemKind::Method(ref sig, body_id) = ast_item.node { FnData { constness: sig.constness, - arg_names: self.encode_fn_arg_names_for_body(body), + arg_names: self.encode_fn_arg_names_for_body(body_id), sig: self.lazy(&tcx.fn_sig(def_id)), + qualif: self.const_fn_qualif(def_id, body_id, ast_item.span), } } else { bug!() @@ -1045,13 +1056,13 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { self.encode_rendered_const_for_body(body_id) ) } - hir::ItemFn(_, _, constness, .., body) => { + hir::ItemFn(_, _, constness, .., body_id) => { let data = FnData { constness, - arg_names: self.encode_fn_arg_names_for_body(body), + arg_names: self.encode_fn_arg_names_for_body(body_id), sig: self.lazy(&tcx.fn_sig(def_id)), + qualif: self.const_fn_qualif(def_id, body_id, item.span), }; - EntryKind::Fn(self.lazy(&data)) } hir::ItemMod(ref m) => { @@ -1565,6 +1576,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { constness: hir::Constness::NotConst, arg_names: self.encode_fn_arg_names(names), sig: self.lazy(&tcx.fn_sig(def_id)), + qualif: None, }; EntryKind::ForeignFn(self.lazy(&data)) } diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index 2e89ea6d2c121..9f9d3cab4b84e 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -434,9 +434,10 @@ pub struct FnData<'tcx> { pub constness: hir::Constness, pub arg_names: LazySeq, pub sig: Lazy>, + pub qualif: Option, } -impl_stable_hash_for!(struct FnData<'tcx> { constness, arg_names, sig }); +impl_stable_hash_for!(struct FnData<'tcx> { constness, arg_names, sig, qualif }); #[derive(RustcEncodable, RustcDecodable)] pub struct VariantData<'tcx> { diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index ca7a2daf39d2e..5226ee6b631cf 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -35,7 +35,7 @@ use util as mir_util; /// Construct the MIR for a given def-id. pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'tcx> { - let id = tcx.hir.as_local_node_id(def_id).unwrap(); + let id = tcx.hir.as_local_node_id(def_id).expect("can't build non-local MIR"); let unsupported = || { span_bug!(tcx.hir.span(id), "can't build MIR for {:?}", def_id); }; diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index e2f2312dbd204..f2604ee018113 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -210,6 +210,10 @@ fn mir_validated<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx // Ensure that we compute the `mir_const_qualif` for constants at // this point, before we steal the mir-const result. let _ = tcx.mir_const_qualif(def_id); + } else if tcx.is_const_fn(def_id) { + // Ensure that we compute the `mir_const_qualif` for const fns at + // this point, before we steal the mir-const result. + let _ = tcx.mir_const_qualif(def_id); } let mut mir = tcx.mir_const(def_id).steal(); diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 8e2a551f4e976..f48f12068d258 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -915,15 +915,28 @@ This does not pose a problem by itself because they can't be accessed directly." Abi::PlatformIntrinsic => { assert!(!self.tcx.is_const_fn(def_id)); match &self.tcx.item_name(def_id).as_str()[..] { + // DANGER_ZONE + // Do not add anything to this list without consulting + // @eddyb, @nikomatsakis and @oli-obk + // + // Any intrinsic which has arguments can potentially cause + // a mismatch between what becomes promoted to `'static` and + // what can be evaluated at compile-time | "size_of" | "min_align_of" - | "type_id" + | "type_id" => is_const_fn = Some(def_id), + // END(DANGER_ZONE) + + // If you add anything to this list, make sure to also implement it + // in `rustc_mir::interpret::const_eval` | "bswap" | "ctpop" | "cttz" | "cttz_nonzero" | "ctlz" - | "ctlz_nonzero" => is_const_fn = Some(def_id), + | "ctlz_nonzero" => { + is_const_fn = Some(def_id) + }, name if name.starts_with("simd_shuffle") => { is_shuffle = true; @@ -935,6 +948,11 @@ This does not pose a problem by itself because they can't be accessed directly." _ => { if self.tcx.is_const_fn(def_id) { is_const_fn = Some(def_id); + if def_id != self.def_id { + if !PromotableConstFn::is_promotable(self.tcx, def_id) { + self.add(Qualif::NOT_PROMOTABLE); + } + } } } } @@ -1193,7 +1211,13 @@ fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, return (Qualif::NOT_CONST.bits(), Lrc::new(IdxSetBuf::new_empty(0))); } - let mut qualifier = Qualifier::new(tcx, def_id, mir, Mode::Const); + let mode = if tcx.is_const_fn(def_id) { + Mode::ConstFn + } else { + Mode::Const + }; + + let mut qualifier = Qualifier::new(tcx, def_id, mir, mode); let (qualif, promoted_temps) = qualifier.qualify_const(); (qualif.bits(), promoted_temps) } @@ -1241,7 +1265,7 @@ impl MirPass for QualifyAndPromoteConstants { let mut qualifier = Qualifier::new(tcx, def_id, mir, mode); if mode == Mode::ConstFn { // Enforce a constant-like CFG for `const fn`. - qualifier.qualify_const(); + tcx.mir_const_qualif(def_id); } else { while let Some((bb, data)) = qualifier.rpo.next() { qualifier.visit_basic_block_data(bb, data); @@ -1325,3 +1349,207 @@ fn args_required_const(tcx: TyCtxt, def_id: DefId) -> Option> { } Some(ret) } + +// `root` is a const fn, check whether it can be promoted +struct PromotableConstFn<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + remaining: Vec, + seen: FxHashSet, +} + +impl<'a, 'tcx> PromotableConstFn<'a, 'tcx> { + fn is_promotable(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool { + let mut checker = PromotableConstFn { + remaining: vec![def_id], + seen: FxHashSet(), + tcx, + }; + checker.seen.insert(def_id); + while let Some(def_id) = checker.remaining.pop() { + if !checker.check_mir(def_id) { + return false; + } + } + true + } + + fn check_later(&mut self, def_id: DefId) { + if self.seen.insert(def_id) { + self.remaining.push(def_id); + } + } + + fn check_qualifs(&self, def_id: DefId, span: Span) -> bool { + let (bits, _) = self.tcx.at(span).mir_const_qualif(def_id); + + !Qualif::from_bits(bits).expect("invalid mir_const_qualif").contains(Qualif::NOT_PROMOTABLE) + } + + fn check_mir(&mut self, def_id: DefId) -> bool { + if def_id.is_local() { + match &*self.tcx.mir_const(def_id).try_borrow() { + Some(mir) => mir.basic_blocks().iter().all(|bb| self.check_bb(&mir, &bb)), + // mir_const_qualif has already been executed for this MIR, + // so we don't have to recurse, we just fetch the cache value + // also we can't recurse, because the `mir_const` MIR has been stolen + None => self.check_qualifs(def_id, self.tcx.def_span(def_id)), + } + } else { + // dependencies are already cached + self.check_qualifs(def_id, self.tcx.def_span(def_id)) + } + } + fn check_bb(&mut self, mir: &Mir<'tcx>, bb: &BasicBlockData<'tcx>) -> bool { + bb.statements.iter().all(|stmt| self.check_statement(mir, stmt)) && + bb.terminator.as_ref().map_or(true, |t| self.check_terminator_kind(mir, &t.kind)) + } + fn check_terminator_kind( + &mut self, + mir: &Mir<'tcx>, + kind: &TerminatorKind<'tcx>, + ) -> bool { + match kind { + TerminatorKind::Call { ref func, .. } => { + let fn_ty = func.ty(mir, self.tcx); + if let ty::TyFnDef(def_id, _) = fn_ty.sty { + if self.tcx.is_const_fn(def_id) { + self.check_later(def_id); + true + } else { + false + } + } else { + false + } + } + // trivial constrol flow + TerminatorKind::Goto { .. } | + TerminatorKind::Return => true, + // currently disallowed + TerminatorKind::SwitchInt { .. } => false, + // assertions are fine, they are essentially "goto or abort" + TerminatorKind::Assert { cond, .. } => self.check_operand(mir, cond), + // everything else is not promotable + _ => false, + } + } + + fn check_operand( + &mut self, + mir: &Mir<'tcx>, + operand: &Operand<'tcx>, + ) -> bool { + match operand { + Operand::Copy(place) | Operand::Move(place) => self.check_place(mir, place), + Operand::Constant(c) => self.check_constant(c), + } + } + + fn check_constant( + &mut self, + constant: &Constant<'tcx>, + ) -> bool { + match constant.literal { + Literal::Value { value: &ty::Const { val, .. } } => { + match val { + ConstVal::Unevaluated(def_id, _) => { + // Don't peek inside trait associated constants. + self.tcx.trait_of_item(def_id).is_none() && + self.check_qualifs(def_id, constant.span) + } + // This seems not quite right. When are constants evaluated? + // We can't really check through evaluated constants + ConstVal::Value(_) => true, + } + }, + Literal::Promoted { .. } => true, + } + } + + fn check_statement( + &mut self, + mir: &Mir<'tcx>, + stmt: &Statement<'tcx>, + ) -> bool { + match &stmt.kind { + StatementKind::Assign(place, rvalue) => { + self.check_place(mir, place) && self.check_rvalue(mir, rvalue) + }, + StatementKind::ReadForMatch(place) | + StatementKind::SetDiscriminant { place, .. } => self.check_place(mir, place), + StatementKind::Validate(..) | StatementKind::EndRegion(_) | + StatementKind::UserAssertTy(..) | StatementKind::Nop | + StatementKind::StorageLive(_) | StatementKind::StorageDead(_) => true, + StatementKind::InlineAsm { .. } => false, + } + } + + fn check_rvalue( + &mut self, + mir: &Mir<'tcx>, + rvalue: &Rvalue<'tcx>, + ) -> bool { + match rvalue { + | Rvalue::Use(op) + | Rvalue::Repeat(op, _) + | Rvalue::UnaryOp(_, op) + => self.check_operand(mir, op), + + Rvalue::NullaryOp(..) => true, + + | Rvalue::Len(place) + | Rvalue::Discriminant(place) + | Rvalue::Ref(_, _, place) + => self.check_place(mir, place), + + | Rvalue::Aggregate(_, operands) + => operands.iter().any(|op| self.check_operand(mir, op)), + + | Rvalue::BinaryOp(_, a, b) + | Rvalue::CheckedBinaryOp(_, a, b) + => self.check_operand(mir, a) && self.check_operand(mir, b), + + | Rvalue::Cast(CastKind::ReifyFnPointer, op, _) + | Rvalue::Cast(CastKind::UnsafeFnPointer, op, _) + | Rvalue::Cast(CastKind::ClosureFnPointer, op, _) + | Rvalue::Cast(CastKind::Unsize, op, _) + => self.check_operand(mir, op), + + Rvalue::Cast(CastKind::Misc, operand, cast_ty) => { + let operand_ty = operand.ty(mir, self.tcx); + let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); + let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); + match (cast_in, cast_out) { + (CastTy::Ptr(_), CastTy::Int(_)) | + (CastTy::FnPtr, CastTy::Int(_)) => false, + _ => self.check_operand(mir, operand) + } + } + } + } + + fn check_place( + &mut self, + mir: &Mir<'tcx>, + place: &Place<'tcx>, + ) -> bool { + match place { + Place::Projection(proj) => self.check_place(mir, &proj.base) && match proj.elem { + ProjectionElem::Field(..) => { + // accessing union fields is never allowed inside promoteds + match proj.base.ty(mir, self.tcx).to_ty(self.tcx).ty_adt_def() { + Some(def) => !def.is_union(), + None => true, + } + }, + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Deref + | ProjectionElem::Index(_) + | ProjectionElem::Subslice { .. } + | ProjectionElem::Downcast(..) => true, + }, + Place::Local(_) => true, + Place::Static(_) => false, + } + } +} \ No newline at end of file diff --git a/src/librustc_passes/rvalue_promotion.rs b/src/librustc_passes/rvalue_promotion.rs index b3835b9e08457..213e7da270b09 100644 --- a/src/librustc_passes/rvalue_promotion.rs +++ b/src/librustc_passes/rvalue_promotion.rs @@ -27,14 +27,13 @@ use rustc::ty::cast::CastKind; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::DefId; -use rustc::hir::map::blocks::FnLikeNode; use rustc::middle::expr_use_visitor as euv; use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::maps::Providers; use rustc::ty::subst::Substs; -use rustc::util::nodemap::{ItemLocalSet, NodeSet}; +use rustc::util::nodemap::{ItemLocalSet, NodeSet, FxHashSet}; use rustc::hir; use rustc_data_structures::sync::Lrc; use syntax::ast; @@ -75,6 +74,14 @@ fn rvalue_promotable_map<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Lrc { + rvalue_promotable_map_inner(tcx, def_id, &mut FxHashSet()) +} + +fn rvalue_promotable_map_inner<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + seen: &mut FxHashSet, +) -> Lrc { let outer_def_id = tcx.closure_base_def_id(def_id); if outer_def_id != def_id { return tcx.rvalue_promotable_map(outer_def_id); @@ -90,8 +97,11 @@ fn rvalue_promotable_map<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv::empty(), identity_substs: Substs::empty(), result: ItemLocalSet(), + seen, }; + visitor.seen.insert(def_id); + // `def_id` should be a `Body` owner let node_id = tcx.hir.as_local_node_id(def_id) .expect("rvalue_promotable_map invoked with non-local def-id"); @@ -111,6 +121,7 @@ struct CheckCrateVisitor<'a, 'tcx: 'a> { identity_substs: &'tcx Substs<'tcx>, tables: &'a ty::TypeckTables<'tcx>, result: ItemLocalSet, + seen: &'a mut FxHashSet, } impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { @@ -123,14 +134,6 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { fn handle_const_fn_call(&mut self, def_id: DefId, ret_ty: Ty<'gcx>, span: Span) { self.promotable &= self.type_has_only_promotable_values(ret_ty); - self.promotable &= if let Some(fn_id) = self.tcx.hir.as_local_node_id(def_id) { - FnLikeNode::from_node(self.tcx.hir.get(fn_id)).map_or(false, |fn_like| { - fn_like.constness() == hir::Constness::Const - }) - } else { - self.tcx.is_const_fn(def_id) - }; - if let Some(&attr::Stability { rustc_const_unstable: Some(attr::RustcConstUnstable { feature: ref feature_name @@ -149,6 +152,28 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> { // this comes from a macro that has #[allow_internal_unstable] span.allows_unstable(); } + + + let is_const_fn = self.tcx.is_const_fn(def_id); + self.promotable &= is_const_fn; + if is_const_fn { + if def_id.is_local() { + // Prevent cycles in checking `const_is_rvalue_promotable_to_static` + if self.seen.insert(def_id) { + let sub_map = rvalue_promotable_map_inner(self.tcx, def_id, self.seen); + let node_id = self + .tcx + .hir + .as_local_node_id(def_id) + .expect("rvalue_promotable_map invoked with non-local def-id"); + let hir_id = self.tcx.hir.node_to_hir_id(node_id); + self.promotable &= sub_map.contains(&hir_id.local_id); + } + } else { + // dependencies are already cached + self.promotable &= self.tcx.at(span).const_is_rvalue_promotable_to_static(def_id); + } + } } /// While the `ExprUseVisitor` walks, we will identify which diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 2472bed5ba435..28639f700e0d1 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -670,7 +670,7 @@ pub fn set_print(sink: Option>) -> Option> { /// However, if the actual I/O causes an error, this function does panic. fn print_to( args: fmt::Arguments, - local_s: &'static LocalKey>>>, + local_s: &LocalKey>>>, global_s: fn() -> T, label: &str, ) diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 40d3280baa687..fef964d5dab70 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -243,7 +243,7 @@ impl LocalKey { /// destructor running, and it **may** panic if the destructor has /// previously been run for this thread. #[stable(feature = "rust1", since = "1.0.0")] - pub fn with(&'static self, f: F) -> R + pub fn with(&self, f: F) -> R where F: FnOnce(&T) -> R { self.try_with(f).expect("cannot access a TLS value during or \ after it is destroyed") @@ -283,7 +283,7 @@ impl LocalKey { /// This function will still `panic!()` if the key is uninitialized and the /// key's initializer panics. #[stable(feature = "thread_local_try_with", since = "1.26.0")] - pub fn try_with(&'static self, f: F) -> Result + pub fn try_with(&self, f: F) -> Result where F: FnOnce(&T) -> R, { diff --git a/src/test/ui/const-eval/dont_promote_unstable_const_fn.nll.stderr b/src/test/ui/const-eval/dont_promote_unstable_const_fn.nll.stderr index 684fa1c997bf1..c3ec7825fff05 100644 --- a/src/test/ui/const-eval/dont_promote_unstable_const_fn.nll.stderr +++ b/src/test/ui/const-eval/dont_promote_unstable_const_fn.nll.stderr @@ -11,6 +11,7 @@ error[E0597]: borrowed value does not live long enough | LL | let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough +LL | //~^ ERROR does not live long enough LL | } | - temporary value only lives until here | diff --git a/src/test/ui/const-eval/dont_promote_unstable_const_fn.rs b/src/test/ui/const-eval/dont_promote_unstable_const_fn.rs index a590e569947f8..a371d5485e146 100644 --- a/src/test/ui/const-eval/dont_promote_unstable_const_fn.rs +++ b/src/test/ui/const-eval/dont_promote_unstable_const_fn.rs @@ -31,4 +31,5 @@ fn a() { fn main() { let _: &'static u32 = &meh(); //~ ERROR does not live long enough let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis(); + //~^ ERROR does not live long enough } diff --git a/src/test/ui/const-eval/dont_promote_unstable_const_fn.stderr b/src/test/ui/const-eval/dont_promote_unstable_const_fn.stderr index 7963cbb4e4569..b3d7ba3e5c47a 100644 --- a/src/test/ui/const-eval/dont_promote_unstable_const_fn.stderr +++ b/src/test/ui/const-eval/dont_promote_unstable_const_fn.stderr @@ -21,12 +21,23 @@ error[E0597]: borrowed value does not live long enough | LL | let _: &'static u32 = &meh(); //~ ERROR does not live long enough | ^^^^^ temporary value does not live long enough +... +LL | } + | - temporary value only lives until here + | + = note: borrowed value must be valid for the static lifetime... + +error[E0597]: borrowed value does not live long enough + --> $DIR/dont_promote_unstable_const_fn.rs:33:26 + | LL | let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough +LL | //~^ ERROR does not live long enough LL | } | - temporary value only lives until here | = note: borrowed value must be valid for the static lifetime... -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/const-eval/promoted_const_fn_fail.nll.stderr b/src/test/ui/const-eval/promoted_const_fn_fail.nll.stderr new file mode 100644 index 0000000000000..a7e2b59d16d9b --- /dev/null +++ b/src/test/ui/const-eval/promoted_const_fn_fail.nll.stderr @@ -0,0 +1,14 @@ +error[E0597]: borrowed value does not live long enough + --> $DIR/promoted_const_fn_fail.rs:25:19 + | +LL | Bar { a: &42 }.b as u8 + | ^^ temporary value does not live long enough +LL | } +LL | } + | - temporary value only lives until here + | + = note: borrowed value must be valid for the static lifetime... + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/const-eval/promoted_const_fn_fail.rs b/src/test/ui/const-eval/promoted_const_fn_fail.rs index 4888ed6e8dc7b..bd3ea3e9f333f 100644 --- a/src/test/ui/const-eval/promoted_const_fn_fail.rs +++ b/src/test/ui/const-eval/promoted_const_fn_fail.rs @@ -23,16 +23,12 @@ const fn bar() -> u8 { // is run on a system whose pointers need more // than 8 bits Bar { a: &42 }.b as u8 - //~^ ERROR this expression will panic at runtime - //~| ERROR this expression will panic at runtime } } fn main() { - // FIXME(oli-obk): this should compile but panic at runtime - // if we change the `const_err` lint to allow this will actually compile, but then - // continue with undefined values. let x: &'static u8 = &(bar() + 1); + //~^ ERROR borrowed value does not live long enough let y = *x; unreachable!(); } diff --git a/src/test/ui/const-eval/promoted_const_fn_fail.stderr b/src/test/ui/const-eval/promoted_const_fn_fail.stderr index d805e1a27c993..a7b7062db42c8 100644 --- a/src/test/ui/const-eval/promoted_const_fn_fail.stderr +++ b/src/test/ui/const-eval/promoted_const_fn_fail.stderr @@ -1,20 +1,14 @@ -error: this expression will panic at runtime - --> $DIR/promoted_const_fn_fail.rs:25:9 +error[E0597]: borrowed value does not live long enough + --> $DIR/promoted_const_fn_fail.rs:30:27 | -LL | Bar { a: &42 }.b as u8 - | ^^^^^^^^^^^^^^^^^^^^^^ a raw memory access tried to access part of a pointer value as raw bytes +LL | let x: &'static u8 = &(bar() + 1); + | ^^^^^^^^^^^ temporary value does not live long enough +... +LL | } + | - temporary value only lives until here | -note: lint level defined here - --> $DIR/promoted_const_fn_fail.rs:13:9 - | -LL | #![deny(const_err)] - | ^^^^^^^^^ - -error: this expression will panic at runtime - --> $DIR/promoted_const_fn_fail.rs:25:9 - | -LL | Bar { a: &42 }.b as u8 - | ^^^^^^^^^^^^^^^^^^^^^^ a raw memory access tried to access part of a pointer value as raw bytes + = note: borrowed value must be valid for the static lifetime... -error: aborting due to 2 previous errors +error: aborting due to previous error +For more information about this error, try `rustc --explain E0597`. From a4f6438bec60b279a0fffbbaf9dcd2b9b20eae0c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sun, 17 Jun 2018 20:07:17 +0200 Subject: [PATCH 3/5] Fix tidy --- src/librustc_metadata/encoder.rs | 7 ++++++- src/librustc_mir/transform/qualify_consts.rs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 4c269ecd89644..8ae47332fdff4 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -881,7 +881,12 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { !self.tcx.sess.opts.output_types.should_codegen() } - fn const_fn_qualif(&self, def_id: DefId, body_id: hir::BodyId, span: Span) -> Option { + fn const_fn_qualif( + &self, + def_id: DefId, + body_id: hir::BodyId, + span: Span, + ) -> Option { if self.tcx.is_const_fn(def_id) { let mir = self.tcx.at(span).mir_const_qualif(def_id).0; Some(self.const_qualif(mir, body_id)) diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index f48f12068d258..b4464554b8981 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -1552,4 +1552,4 @@ impl<'a, 'tcx> PromotableConstFn<'a, 'tcx> { Place::Static(_) => false, } } -} \ No newline at end of file +} From d12f35f51ee1ff89c8cbc4cf2321098cd8812406 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 18 Jun 2018 13:03:59 +0200 Subject: [PATCH 4/5] make thread local statics actual statics --- src/librustc_mir/transform/qualify_consts.rs | 1 + src/libstd/io/stdio.rs | 2 +- src/libstd/thread/local.rs | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index b4464554b8981..fc5f622846738 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -935,6 +935,7 @@ This does not pose a problem by itself because they can't be accessed directly." | "cttz_nonzero" | "ctlz" | "ctlz_nonzero" => { + self.add(Qualif::NOT_PROMOTABLE); is_const_fn = Some(def_id) }, diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 28639f700e0d1..2472bed5ba435 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -670,7 +670,7 @@ pub fn set_print(sink: Option>) -> Option> { /// However, if the actual I/O causes an error, this function does panic. fn print_to( args: fmt::Arguments, - local_s: &LocalKey>>>, + local_s: &'static LocalKey>>>, global_s: fn() -> T, label: &str, ) diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index fef964d5dab70..b1d7c4ff048fd 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -194,7 +194,7 @@ macro_rules! __thread_local_inner { } }; ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => { - $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = + $(#[$attr])* $vis static $name: $crate::thread::LocalKey<$t> = __thread_local_inner!(@key $(#[$attr])* $vis $name, $t, $init); } } @@ -243,7 +243,7 @@ impl LocalKey { /// destructor running, and it **may** panic if the destructor has /// previously been run for this thread. #[stable(feature = "rust1", since = "1.0.0")] - pub fn with(&self, f: F) -> R + pub fn with(&'static self, f: F) -> R where F: FnOnce(&T) -> R { self.try_with(f).expect("cannot access a TLS value during or \ after it is destroyed") @@ -283,7 +283,7 @@ impl LocalKey { /// This function will still `panic!()` if the key is uninitialized and the /// key's initializer panics. #[stable(feature = "thread_local_try_with", since = "1.26.0")] - pub fn try_with(&self, f: F) -> Result + pub fn try_with(&'static self, f: F) -> Result where F: FnOnce(&T) -> R, { From 1030037a95a66a391a21cd4d7da71f64a888d3c3 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 18 Jun 2018 13:36:45 +0200 Subject: [PATCH 5/5] Only promote adt construction const fns --- src/librustc_mir/transform/qualify_consts.rs | 24 +++++++++----------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index fc5f622846738..ef01a5558d653 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -1410,12 +1410,13 @@ impl<'a, 'tcx> PromotableConstFn<'a, 'tcx> { kind: &TerminatorKind<'tcx>, ) -> bool { match kind { - TerminatorKind::Call { ref func, .. } => { + TerminatorKind::Call { ref func, ref args, destination: Some((dest, _)), .. } => { let fn_ty = func.ty(mir, self.tcx); if let ty::TyFnDef(def_id, _) = fn_ty.sty { if self.tcx.is_const_fn(def_id) { self.check_later(def_id); - true + args.iter().all(|op| self.check_operand(mir, op)) && + self.check_place(mir, dest) } else { false } @@ -1426,10 +1427,6 @@ impl<'a, 'tcx> PromotableConstFn<'a, 'tcx> { // trivial constrol flow TerminatorKind::Goto { .. } | TerminatorKind::Return => true, - // currently disallowed - TerminatorKind::SwitchInt { .. } => false, - // assertions are fine, they are essentially "goto or abort" - TerminatorKind::Assert { cond, .. } => self.check_operand(mir, cond), // everything else is not promotable _ => false, } @@ -1493,23 +1490,24 @@ impl<'a, 'tcx> PromotableConstFn<'a, 'tcx> { match rvalue { | Rvalue::Use(op) | Rvalue::Repeat(op, _) - | Rvalue::UnaryOp(_, op) => self.check_operand(mir, op), Rvalue::NullaryOp(..) => true, - | Rvalue::Len(place) - | Rvalue::Discriminant(place) + // disallow anything that processes the bits of a value + | Rvalue::UnaryOp(_, _) + | Rvalue::Len(_) + | Rvalue::Discriminant(_) + | Rvalue::BinaryOp(_, _, _) + | Rvalue::CheckedBinaryOp(_, _, _) + => false, + | Rvalue::Ref(_, _, place) => self.check_place(mir, place), | Rvalue::Aggregate(_, operands) => operands.iter().any(|op| self.check_operand(mir, op)), - | Rvalue::BinaryOp(_, a, b) - | Rvalue::CheckedBinaryOp(_, a, b) - => self.check_operand(mir, a) && self.check_operand(mir, b), - | Rvalue::Cast(CastKind::ReifyFnPointer, op, _) | Rvalue::Cast(CastKind::UnsafeFnPointer, op, _) | Rvalue::Cast(CastKind::ClosureFnPointer, op, _)