From f24d770a530ec6b6c820a6c9f5de1cad69a6c0d6 Mon Sep 17 00:00:00 2001 From: QuietMisdreavus Date: Fri, 13 Jul 2018 15:45:21 -0500 Subject: [PATCH 01/10] stabilize lint handling in rustdoc --- src/librustdoc/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 089ecebbc9c07..ae64c6f1bfd7f 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -310,19 +310,19 @@ pub fn opts() -> Vec { "disable-minification", "Disable minification applied on JS files") }), - unstable("warn", |o| { + stable("warn", |o| { o.optmulti("W", "warn", "Set lint warnings", "OPT") }), - unstable("allow", |o| { + stable("allow", |o| { o.optmulti("A", "allow", "Set lint allowed", "OPT") }), - unstable("deny", |o| { + stable("deny", |o| { o.optmulti("D", "deny", "Set lint denied", "OPT") }), - unstable("forbid", |o| { + stable("forbid", |o| { o.optmulti("F", "forbid", "Set lint forbidden", "OPT") }), - unstable("cap-lints", |o| { + stable("cap-lints", |o| { o.optmulti( "", "cap-lints", From cac461bb7aa5fae9beda6c2eb31ca8f9597eff82 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Wed, 28 Mar 2018 12:33:13 +0100 Subject: [PATCH 02/10] Removed FN_ARGUMENT from `Qualif` flags. --- src/librustc_mir/transform/qualify_consts.rs | 37 +------------------ .../const-block-non-item-statement-2.rs | 2 - .../const-block-non-item-statement-3.rs | 2 - .../const-block-non-item-statement.rs | 7 ++++ 4 files changed, 9 insertions(+), 39 deletions(-) diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 986957d5a8267..369a385c773ae 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -37,7 +37,6 @@ use syntax_pos::{Span, DUMMY_SP}; use std::fmt; use rustc_data_structures::sync::Lrc; -use std::usize; use transform::{MirPass, MirSource}; use super::promote_consts::{self, Candidate, TempState}; @@ -53,16 +52,13 @@ bitflags! { // Constant containing an ADT that implements Drop. const NEEDS_DROP = 1 << 1; - // Function argument. - const FN_ARGUMENT = 1 << 2; - // Not constant at all - non-`const fn` calls, asm!, // pointer comparisons, ptr-to-int casts, etc. - const NOT_CONST = 1 << 3; + const NOT_CONST = 1 << 2; // Refers to temporaries which cannot be promoted as // promote_consts decided they weren't simple enough. - const NOT_PROMOTABLE = 1 << 4; + const NOT_PROMOTABLE = 1 << 3; // Const items can only have MUTABLE_INTERIOR // and NOT_PROMOTABLE without producing an error. @@ -116,7 +112,6 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { param_env: ty::ParamEnv<'tcx>, local_qualif: IndexVec>, qualif: Qualif, - const_fn_arg_vars: BitVector, temp_promotion_state: IndexVec, promotion_candidates: Vec } @@ -150,7 +145,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { param_env, local_qualif, qualif: Qualif::empty(), - const_fn_arg_vars: BitVector::new(mir.local_decls.len()), temp_promotion_state: temps, promotion_candidates: vec![] } @@ -426,10 +420,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { LocalKind::Var | LocalKind::Arg | LocalKind::Temp => { - if let LocalKind::Arg = kind { - self.add(Qualif::FN_ARGUMENT); - } - if !self.temp_promotion_state[local].is_promotable() { self.add(Qualif::NOT_PROMOTABLE); } @@ -1035,29 +1025,6 @@ 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.contains(Qualif::FN_ARGUMENT) { - let decl = &self.mir.local_decls[index]; - let mut err = feature_err( - &self.tcx.sess.parse_sess, - "const_let", - decl.source_info.span, - GateIssue::Language, - "arguments of constant functions can only be immutable by-value bindings" - ); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note("Constant functions are not allowed to mutate anything. Thus, \ - binding to an argument with a mutable pattern is not allowed."); - err.note("Remove any mutable bindings from the argument list to fix this \ - error. In case you need to mutate the argument, try lazily \ - initializing a global variable instead of using a const fn, or \ - refactoring the code to a functional style to avoid mutation if \ - possible."); - } - err.emit(); - return; - } } } 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 f80d55cb34267..83b47a620f534 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 @@ -22,7 +22,5 @@ const C: usize = { foo!(); 2 }; const D: usize = { let x = 4; 2 }; //~^ 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 cfa4b778dde80..9812a4853f850 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 @@ -11,7 +11,5 @@ type Array = [u32; { let x = 2; 5 }]; //~^ 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 f974a24c26f72..63161410f7cee 100644 --- a/src/test/compile-fail/const-block-non-item-statement.rs +++ b/src/test/compile-fail/const-block-non-item-statement.rs @@ -10,10 +10,17 @@ enum Foo { Bar = { let x = 1; 3 } +<<<<<<< HEAD //~^ 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 +||||||| merged common ancestors + //~^ ERROR: blocks in constants are limited to items and tail expressions + //~^^ ERROR: blocks in constants are limited to items and tail expressions +======= + //~^ ERROR: blocks in constants are limited to items and tail expressions +>>>>>>> Removed FN_ARGUMENT from Qualif flags. } pub fn main() {} From 9d5fa12b00b72f7355f9c1a525673727d302986c Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Wed, 28 Mar 2018 14:18:45 +0100 Subject: [PATCH 03/10] Removed NOT_PROMOTABLE from `Qualif` flags. --- src/librustc_mir/transform/qualify_consts.rs | 24 +++----------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 369a385c773ae..922b7e254cbc4 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -52,18 +52,9 @@ bitflags! { // Constant containing an ADT that implements Drop. const NEEDS_DROP = 1 << 1; - // Not constant at all - non-`const fn` calls, asm!, + // Not constant or not promotable - non-`const fn` calls, asm!, // pointer comparisons, ptr-to-int casts, etc. const NOT_CONST = 1 << 2; - - // Refers to temporaries which cannot be promoted as - // promote_consts decided they weren't simple enough. - const NOT_PROMOTABLE = 1 << 3; - - // Const items can only have MUTABLE_INTERIOR - // and NOT_PROMOTABLE without producing an error. - const CONST_ERROR = !Qualif::MUTABLE_INTERIOR.bits & - !Qualif::NOT_PROMOTABLE.bits; } } @@ -366,15 +357,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { self.qualif = self.local_qualif[RETURN_PLACE].unwrap_or(Qualif::NOT_CONST); - // Account for errors in consts by using the - // conservative type qualification instead. - if self.qualif.intersects(Qualif::CONST_ERROR) { - self.qualif = Qualif::empty(); - let return_ty = mir.return_ty(); - self.add_type(return_ty); - } - - // Collect all the temps we need to promote. let mut promoted_temps = IdxSetBuf::new_empty(self.temp_promotion_state.len()); @@ -421,7 +403,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { LocalKind::Arg | LocalKind::Temp => { if !self.temp_promotion_state[local].is_promotable() { - self.add(Qualif::NOT_PROMOTABLE); + self.add(Qualif::NOT_CONST); } if let Some(qualif) = self.local_qualif[local] { @@ -956,7 +938,7 @@ This does not pose a problem by itself because they can't be accessed directly." if let Some((ref dest, _)) = *destination { // Avoid propagating irrelevant callee/argument qualifications. - if self.qualif.intersects(Qualif::CONST_ERROR) { + if self.qualif.intersects(Qualif::NOT_CONST) { self.qualif = Qualif::NOT_CONST; } else { // Be conservative about the returned value of a const fn. From 2ba3d498fa66e291750c40c9f9e53307a6b98c48 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Thu, 29 Mar 2018 03:15:59 +0100 Subject: [PATCH 04/10] Removed STATIC & STATIC_REF from `Qualif` flags. --- src/librustc_mir/transform/promote_consts.rs | 5 ++--- src/librustc_mir/transform/qualify_consts.rs | 3 +-- .../const-fn-not-safe-for-const.rs | 3 +-- src/test/run-pass/static-by-reference.rs | 19 +++++++++++++++++++ src/test/run-pass/static-by-value.rs | 16 ++++++++++++++++ src/test/ui/issue-17718-const-bad-values.rs | 3 +-- 6 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 src/test/run-pass/static-by-reference.rs create mode 100644 src/test/run-pass/static-by-value.rs diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 8dc6539b65d6a..2f368ea8cc840 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -384,9 +384,8 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> { local: &mut Local, _: PlaceContext<'tcx>, _: Location) { - if self.source.local_kind(*local) == LocalKind::Temp { - *local = self.promote_temp(*local); - } + assert_eq!(self.source.local_kind(*local), LocalKind::Temp); + *local = self.promote_temp(*local); } } diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 922b7e254cbc4..621a1c7501203 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -43,8 +43,7 @@ use super::promote_consts::{self, Candidate, TempState}; bitflags! { // Borrows of temporaries can be promoted only if - // they have none of these qualifications, with - // the exception of `STATIC_REF` (in statics only). + // they have none of these qualifications. struct Qualif: u8 { // Constant containing interior mutability (UnsafeCell). const MUTABLE_INTERIOR = 1 << 0; 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 341cc7bb49116..eb3ef98ee40cf 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 @@ -28,12 +28,11 @@ static Y: u32 = 0; const fn get_Y() -> u32 { Y - //~^ ERROR E0013 + //~^ ERROR E0013 } const fn get_Y_addr() -> &'static u32 { &Y - //~^ ERROR E0013 } const fn get() -> u32 { diff --git a/src/test/run-pass/static-by-reference.rs b/src/test/run-pass/static-by-reference.rs new file mode 100644 index 0000000000000..a83f29b8c70da --- /dev/null +++ b/src/test/run-pass/static-by-reference.rs @@ -0,0 +1,19 @@ +// Copyright 2016 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. + +struct Foo { + a: u32 +} + +static S : Foo = Foo { a : 0 }; +static A : &'static u32 = &S.a; + +fn main() { +} diff --git a/src/test/run-pass/static-by-value.rs b/src/test/run-pass/static-by-value.rs new file mode 100644 index 0000000000000..5bd2e33e7a17c --- /dev/null +++ b/src/test/run-pass/static-by-value.rs @@ -0,0 +1,16 @@ +// Copyright 2016 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(warnings)] + +static A: u32 = 0; +static B: u32 = A; + +fn main() {} diff --git a/src/test/ui/issue-17718-const-bad-values.rs b/src/test/ui/issue-17718-const-bad-values.rs index 17ec77d77eea2..b72076f0c93c6 100644 --- a/src/test/ui/issue-17718-const-bad-values.rs +++ b/src/test/ui/issue-17718-const-bad-values.rs @@ -13,8 +13,7 @@ const C1: &'static mut [usize] = &mut []; static mut S: usize = 3; const C2: &'static mut usize = unsafe { &mut S }; -//~^ ERROR: constants cannot refer to statics -//~| ERROR: references in constants may only refer to immutable values +//~^ ERROR: references in constants may only refer to immutable values //~| ERROR: references in constants may only refer to immutable values fn main() {} From 9aa706db3bde74246b264d2de5798616a92e46e7 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Thu, 29 Mar 2018 16:53:15 +0100 Subject: [PATCH 05/10] Simplify const_qualif propagation of const fn calls. --- src/librustc_mir/transform/qualify_consts.rs | 33 ++++++++------------ 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 621a1c7501203..70cf985ec46cb 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -45,7 +45,7 @@ bitflags! { // Borrows of temporaries can be promoted only if // they have none of these qualifications. struct Qualif: u8 { - // Constant containing interior mutability (UnsafeCell). + // Constant containing interior mutability (`UnsafeCell`). const MUTABLE_INTERIOR = 1 << 0; // Constant containing an ADT that implements Drop. @@ -62,11 +62,12 @@ impl<'a, 'tcx> Qualif { fn restrict(&mut self, ty: Ty<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>) { + self.insert(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP); if ty.is_freeze(tcx, param_env, DUMMY_SP) { - *self = *self - Qualif::MUTABLE_INTERIOR; + self.remove(Qualif::MUTABLE_INTERIOR); } if !ty.needs_drop(tcx, param_env) { - *self = *self - Qualif::NEEDS_DROP; + self.remove(Qualif::NEEDS_DROP); } } } @@ -183,18 +184,17 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { } } - /// Add the given qualification to self.qualif. + /// Add the given qualification to `self.qualif`. fn add(&mut self, qualif: Qualif) { self.qualif = self.qualif | qualif; } - /// Add the given type's qualification to self.qualif. - fn add_type(&mut self, ty: Ty<'tcx>) { - self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP); + /// Restrict `self.qualif` by the given type's qualification. + fn restrict_to_type(&mut self, ty: Ty<'tcx>) { self.qualif.restrict(ty, self.tcx, self.param_env); } - /// Within the provided closure, self.qualif will start + /// Within the provided closure, `self.qualif` will start /// out empty, and its value after the closure returns will /// be combined with the value before the call to nest. fn nest(&mut self, f: F) { @@ -501,7 +501,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx); - this.qualif.restrict(ty, this.tcx, this.param_env); + this.restrict_to_type(ty); } ProjectionElem::ConstantIndex {..} | @@ -534,7 +534,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_type(ty); + self.restrict_to_type(ty); } else { let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id); @@ -936,16 +936,9 @@ This does not pose a problem by itself because they can't be accessed directly." } if let Some((ref dest, _)) = *destination { - // Avoid propagating irrelevant callee/argument qualifications. - if self.qualif.intersects(Qualif::NOT_CONST) { - self.qualif = Qualif::NOT_CONST; - } else { - // Be conservative about the returned value of a const fn. - let tcx = self.tcx; - let ty = dest.ty(self.mir, tcx).to_ty(tcx); - self.qualif = Qualif::empty(); - self.add_type(ty); - } + // Be conservative about the returned value of a const fn. + let ty = dest.ty(self.mir, self.tcx).to_ty(self.tcx); + self.restrict_to_type(ty); self.assign(dest, location); } } else if let TerminatorKind::Drop { location: ref place, .. } = *kind { From 2966a1415ca4bbda9cb43d40f459c86fd36961ab Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Thu, 29 Mar 2018 19:05:50 +0100 Subject: [PATCH 06/10] Merged `temp_qualif` and `return_qualif` and split into three boolean vectors for qualif flags. --- src/librustc_mir/lib.rs | 6 +- src/librustc_mir/transform/qualify_consts.rs | 209 ++++++++++-------- .../const-block-non-item-statement.rs | 9 - src/test/ui/issue-17718-const-bad-values.rs | 1 + 4 files changed, 118 insertions(+), 107 deletions(-) diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index d58affbae75ed..781d8b342aaec 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -42,14 +42,14 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! extern crate arena; #[macro_use] -extern crate bitflags; -#[macro_use] extern crate log; +extern crate log; extern crate either; extern crate graphviz as dot; extern crate polonius_engine; #[macro_use] extern crate rustc; -#[macro_use] extern crate rustc_data_structures; +#[macro_use] +extern crate rustc_data_structures; extern crate serialize as rustc_serialize; extern crate rustc_errors; #[macro_use] diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 70cf985ec46cb..55839443ae4f0 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -18,6 +18,7 @@ use rustc_data_structures::bitvec::BitVector; use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sync::Lrc; use rustc::hir; use rustc::hir::def_id::DefId; use rustc::mir::interpret::ConstValue; @@ -35,40 +36,48 @@ use syntax::ast::LitKind; use syntax::feature_gate::{UnstableFeatures, feature_err, emit_feature_err, GateIssue}; use syntax_pos::{Span, DUMMY_SP}; +use std::usize; use std::fmt; -use rustc_data_structures::sync::Lrc; use transform::{MirPass, MirSource}; use super::promote_consts::{self, Candidate, TempState}; -bitflags! { - // Borrows of temporaries can be promoted only if - // they have none of these qualifications. - struct Qualif: u8 { - // Constant containing interior mutability (`UnsafeCell`). - const MUTABLE_INTERIOR = 1 << 0; +// Borrows of temporaries can be promoted only if +// they have none of these qualifications. +#[derive(Clone, Copy, Default, Eq, PartialEq)] +struct Qualif { + // Constant containing interior mutability (`UnsafeCell`). + mut_interior: bool, - // Constant containing an ADT that implements Drop. - const NEEDS_DROP = 1 << 1; + // Constant containing an ADT that implements Drop. + needs_drop: bool, - // Not constant or not promotable - non-`const fn` calls, asm!, - // pointer comparisons, ptr-to-int casts, etc. - const NOT_CONST = 1 << 2; - } + // Not constant or not promotable - non-`const fn` calls, asm!, + // pointer comparisons, ptr-to-int casts, etc. + not_const: bool, } -impl<'a, 'tcx> Qualif { - /// Remove flags which are impossible for the given type. - fn restrict(&mut self, ty: Ty<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>) { - self.insert(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP); - if ty.is_freeze(tcx, param_env, DUMMY_SP) { - self.remove(Qualif::MUTABLE_INTERIOR); +impl Qualif { + fn from_bits(bits: u8) -> Self { + Self { + mut_interior: bits & (1 << 0) != 0, + needs_drop: bits & (1 << 1) != 0, + not_const: bits & (1 << 2) != 0, + } + } + + fn bits(&self) -> u8 { + let mut bits = 0; + if self.mut_interior { + bits |= 1 << 0; } - if !ty.needs_drop(tcx, param_env) { - self.remove(Qualif::NEEDS_DROP); + if self.needs_drop { + bits |= 1 << 1; } + if self.not_const { + bits |= 1 << 2; + } + bits } } @@ -101,8 +110,11 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { rpo: ReversePostorder<'a, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, - local_qualif: IndexVec>, + local_mut_interior: IndexVec, + local_needs_drop: IndexVec, + local_not_const: IndexVec, qualif: Qualif, + const_fn_arg_vars: BitVector, temp_promotion_state: IndexVec, promotion_candidates: Vec } @@ -119,11 +131,11 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { let param_env = tcx.param_env(def_id); - let mut local_qualif = IndexVec::from_elem(None, &mir.local_decls); + let mut local_needs_drop = IndexVec::from_elem(false, &mir.local_decls); + let mut local_not_const = IndexVec::from_elem(true, &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); - local_qualif[arg] = Some(qualif); + local_needs_drop[arg] = mir.local_decls[arg].ty.needs_drop(tcx, param_env); + local_not_const[arg] = false; } Qualifier { @@ -134,8 +146,11 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { rpo, tcx, param_env, - local_qualif, - qualif: Qualif::empty(), + local_mut_interior: IndexVec::from_elem(false, &mir.local_decls), + local_needs_drop, + local_not_const, + qualif: Qualif::default(), + const_fn_arg_vars: BitVector::new(mir.local_decls.len()), temp_promotion_state: temps, promotion_candidates: vec![] } @@ -145,7 +160,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { // categories, but enabling full miri would make that // slightly pointless (even with feature-gating). fn not_const(&mut self) { - self.add(Qualif::NOT_CONST); + self.qualif.not_const = true; if self.mode != Mode::Fn { let mut err = struct_span_err!( self.tcx.sess, @@ -166,7 +181,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { /// Error about extra statements in a constant. fn statement_like(&mut self) { - self.add(Qualif::NOT_CONST); + self.qualif.not_const = true; if self.mode != Mode::Fn { let mut err = feature_err( &self.tcx.sess.parse_sess, @@ -186,12 +201,15 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { /// Add the given qualification to `self.qualif`. fn add(&mut self, qualif: Qualif) { - self.qualif = self.qualif | qualif; + self.qualif.mut_interior |= qualif.mut_interior; + self.qualif.needs_drop |= qualif.needs_drop; + self.qualif.not_const |= qualif.not_const; } /// Restrict `self.qualif` by the given type's qualification. fn restrict_to_type(&mut self, ty: Ty<'tcx>) { - self.qualif.restrict(ty, self.tcx, self.param_env); + self.qualif.mut_interior = !ty.is_freeze(self.tcx, self.param_env, DUMMY_SP); + self.qualif.needs_drop = ty.needs_drop(self.tcx, self.param_env); } /// Within the provided closure, `self.qualif` will start @@ -199,7 +217,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { /// be combined with the value before the call to nest. fn nest(&mut self, f: F) { let original = self.qualif; - self.qualif = Qualif::empty(); + self.qualif = Qualif::default(); f(self); self.add(original); } @@ -207,13 +225,10 @@ 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| { - if slot.is_some() { - span_bug!(span, "multiple assignments to {:?}", dest); - } - *slot = Some(qualif); + let store = |this: &mut Self, index| { + this.local_mut_interior[index] = this.qualif.mut_interior; + this.local_needs_drop[index] = this.qualif.needs_drop; + this.local_not_const[index] = this.qualif.not_const; }; // Only handle promotable temps in non-const functions. @@ -221,8 +236,8 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { if let Place::Local(index) = *dest { if self.mir.local_kind(index) == LocalKind::Temp && self.temp_promotion_state[index].is_promotable() { - debug!("store to promotable temp {:?} ({:?})", index, qualif); - store(&mut self.local_qualif[index]); + debug!("store to promotable temp {:?}", index); + store(self, index) } } return; @@ -233,12 +248,12 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { 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); + store(self, 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]) + store(self, index) } Place::Projection(box Projection { @@ -246,9 +261,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.local_qualif[index].map_or(false, |qualif| { - qualif.contains(Qualif::NOT_CONST) - }) => { + && self.local_not_const[index] => { // Part of `box expr`, we should've errored // already for the Box allocation Rvalue. } @@ -303,7 +316,9 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { // 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() { + if !self.local_mut_interior[temp] && + !self.local_needs_drop[temp] && + !self.local_not_const[temp] { continue; } @@ -326,7 +341,9 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { } // Make sure there are no extra unassigned variables. - self.qualif = Qualif::NOT_CONST; + self.qualif.mut_interior = false; + self.qualif.needs_drop = false; + self.qualif.not_const = true; for index in mir.vars_iter() { if !self.const_fn_arg_vars.contains(index.index()) { debug!("unassigned variable {:?}", index); @@ -354,7 +371,9 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { } } - self.qualif = self.local_qualif[RETURN_PLACE].unwrap_or(Qualif::NOT_CONST); + self.qualif.mut_interior = self.local_mut_interior[RETURN_PLACE]; + self.qualif.needs_drop = self.local_needs_drop[RETURN_PLACE]; + self.qualif.not_const = self.local_not_const[RETURN_PLACE]; // Collect all the temps we need to promote. let mut promoted_temps = IdxSetBuf::new_empty(self.temp_promotion_state.len()); @@ -396,28 +415,28 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { self.span, GateIssue::Language, &format!("let bindings in {}s are unstable",self.mode)); } - self.add(Qualif::NOT_CONST); + self.qualif.not_const = true; } LocalKind::Var | LocalKind::Arg | LocalKind::Temp => { if !self.temp_promotion_state[local].is_promotable() { - self.add(Qualif::NOT_CONST); + self.qualif.not_const = true; } - if let Some(qualif) = self.local_qualif[local] { - self.add(qualif); - } else { - self.not_const(); - } + let mut qualif = Qualif::default(); + qualif.mut_interior = self.local_mut_interior[local]; + qualif.needs_drop = self.local_needs_drop[local]; + qualif.not_const = self.local_not_const[local]; + self.add(qualif); } } } fn visit_place(&mut self, - place: &Place<'tcx>, - context: PlaceContext<'tcx>, - location: Location) { + place: &Place<'tcx>, + context: PlaceContext<'tcx>, + location: Location) { match *place { Place::Local(ref local) => self.visit_local(local, context, location), Place::Static(ref global) => { @@ -430,7 +449,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { "thread-local statics cannot be \ accessed at compile-time"); } - self.add(Qualif::NOT_CONST); + self.qualif.not_const = true; return; } @@ -438,7 +457,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { if self.mode == Mode::Static || self.mode == Mode::StaticMut { return; } - self.add(Qualif::NOT_CONST); + self.qualif.not_const = true; if self.mode != Mode::Fn { let mut err = struct_span_err!(self.tcx.sess, self.span, E0013, @@ -461,7 +480,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { this.super_place(place, context, location); match proj.elem { ProjectionElem::Deref => { - this.add(Qualif::NOT_CONST); + this.qualif.not_const = true; let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx); if let ty::TyRawPtr(_) = base_ty.sty { @@ -523,9 +542,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { Operand::Move(_) => { // Mark the consumed locals to indicate later drops are noops. if let Operand::Move(Place::Local(local)) = *operand { - self.local_qualif[local] = self.local_qualif[local].map(|q| - q - Qualif::NEEDS_DROP - ); + self.local_needs_drop[local] = false; } } Operand::Constant(ref constant) => { @@ -538,13 +555,13 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } else { let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id); - let qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif"); + let qualif = Qualif::from_bits(bits); self.add(qualif); // Just in case the type is more specific than // the definition, e.g. impl associated const // with type parameters, take it into account. - self.qualif.restrict(ty, self.tcx, self.param_env); + self.restrict_to_type(ty); } } } @@ -617,7 +634,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } if forbidden_mut { - self.add(Qualif::NOT_CONST); + self.qualif.not_const = true; if self.mode != Mode::Fn { let mut err = struct_span_err!(self.tcx.sess, self.span, E0017, "references in {}s may only refer \ @@ -641,11 +658,11 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { // Constants cannot be borrowed if they contain interior mutability as // it means that our "silent insertion of statics" could change // initializer values (very bad). - if self.qualif.contains(Qualif::MUTABLE_INTERIOR) { + if self.qualif.mut_interior { // A reference of a MUTABLE_INTERIOR place is instead // NOT_CONST (see `if forbidden_mut` below), to avoid // duplicate errors (from reborrowing, for example). - self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR; + self.qualif.mut_interior = false; if self.mode != Mode::Fn { span_err!(self.tcx.sess, self.span, E0492, "cannot borrow a constant which may contain \ @@ -658,7 +675,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } if forbidden_mut { - self.add(Qualif::NOT_CONST); + self.qualif.not_const = true; } else { // We might have a candidate for promotion. let candidate = Candidate::Ref(location); @@ -672,15 +689,14 @@ 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.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 - // `MUTABLE_INTERIOR`, from a type that does, e.g.: - // `let _: &'static _ = &(Cell::new(1), 2).1;` - if (qualif - Qualif::MUTABLE_INTERIOR).is_empty() { - self.promotion_candidates.push(candidate); - } + // `forbidden_mut` is false, so we can safely ignore + // `MUTABLE_INTERIOR` from the local's qualifications. + // This allows borrowing fields which don't have + // `MUTABLE_INTERIOR`, from a type that does, e.g.: + // `let _: &'static _ = &(Cell::new(1), 2).1;` + if !self.local_not_const[local] && + !self.local_needs_drop[local] { + self.promotion_candidates.push(candidate); } } } @@ -694,7 +710,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { match (cast_in, cast_out) { (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => { - self.add(Qualif::NOT_CONST); + self.qualif.not_const = true; if self.mode != Mode::Fn { let mut err = struct_span_err!( self.tcx.sess, @@ -745,7 +761,7 @@ This does not pose a problem by itself because they can't be accessed directly." op == BinOp::Ge || op == BinOp::Gt || op == BinOp::Offset); - self.add(Qualif::NOT_CONST); + self.qualif.not_const = true; if self.mode != Mode::Fn { struct_span_err!( self.tcx.sess, self.span, E0395, @@ -760,7 +776,7 @@ This does not pose a problem by itself because they can't be accessed directly." } Rvalue::NullaryOp(NullOp::Box, _) => { - self.add(Qualif::NOT_CONST); + self.qualif.not_const = true; if self.mode != Mode::Fn { let mut err = struct_span_err!(self.tcx.sess, self.span, E0010, "allocations are not allowed in {}s", self.mode); @@ -780,13 +796,13 @@ This does not pose a problem by itself because they can't be accessed directly." Rvalue::Aggregate(ref kind, _) => { if let AggregateKind::Adt(def, ..) = **kind { if def.has_dtor(self.tcx) { - self.add(Qualif::NEEDS_DROP); + self.qualif.needs_drop = true; } if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() { let ty = rvalue.ty(self.mir, self.tcx); - self.add_type(ty); - assert!(self.qualif.contains(Qualif::MUTABLE_INTERIOR)); + self.restrict_to_type(ty); + assert!(self.qualif.mut_interior); } } } @@ -846,7 +862,7 @@ This does not pose a problem by itself because they can't be accessed directly." } let candidate = Candidate::Argument { bb, index: i }; if is_shuffle && i == 2 { - if this.qualif.is_empty() { + if this.qualif == Default::default() { this.promotion_candidates.push(candidate); } else { span_err!(this.tcx.sess, this.span, E0526, @@ -862,7 +878,7 @@ This does not pose a problem by itself because they can't be accessed directly." if !constant_arguments.contains(&i) { return } - if this.qualif.is_empty() { + if this.qualif == Default::default() { this.promotion_candidates.push(candidate); } else { this.tcx.sess.span_err(this.span, @@ -893,7 +909,9 @@ This does not pose a problem by itself because they can't be accessed directly." // this doesn't come from a macro that has #[allow_internal_unstable] !self.span.allows_unstable() { - self.qualif = Qualif::NOT_CONST; + self.qualif.mut_interior = false; + self.qualif.needs_drop = false; + self.qualif.not_const = true; if self.mode != Mode::Fn { // inside a constant environment, not having the feature gate is // an error @@ -909,7 +927,7 @@ This does not pose a problem by itself because they can't be accessed directly." } } } else { - self.qualif = Qualif::NOT_CONST; + self.qualif.not_const = true; if self.mode != Mode::Fn { // FIXME(#24111) Remove this check when const fn stabilizes let (msg, note) = if let UnstableFeatures::Disallow = @@ -949,7 +967,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.local_qualif[local].map_or(true, |q| q.contains(Qualif::NEEDS_DROP)) { + if self.local_needs_drop[local] { Some(self.mir.local_decls[local].source_info.span) } else { None @@ -1055,7 +1073,8 @@ fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if mir.return_ty().references_error() { tcx.sess.delay_span_bug(mir.span, "mir_const_qualif: Mir had errors"); - return (Qualif::NOT_CONST.bits(), Lrc::new(IdxSetBuf::new_empty(0))); + return (Qualif {not_const: true, ..Default::default()}.bits(), + Lrc::new(IdxSetBuf::new_empty(0))); } let mut qualifier = Qualifier::new(tcx, def_id, mir, Mode::Const); 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 63161410f7cee..0fa5840a8d006 100644 --- a/src/test/compile-fail/const-block-non-item-statement.rs +++ b/src/test/compile-fail/const-block-non-item-statement.rs @@ -10,17 +10,8 @@ enum Foo { Bar = { let x = 1; 3 } -<<<<<<< HEAD //~^ 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 -||||||| merged common ancestors - //~^ ERROR: blocks in constants are limited to items and tail expressions - //~^^ ERROR: blocks in constants are limited to items and tail expressions -======= - //~^ ERROR: blocks in constants are limited to items and tail expressions ->>>>>>> Removed FN_ARGUMENT from Qualif flags. } pub fn main() {} diff --git a/src/test/ui/issue-17718-const-bad-values.rs b/src/test/ui/issue-17718-const-bad-values.rs index b72076f0c93c6..81155dd1cf9cc 100644 --- a/src/test/ui/issue-17718-const-bad-values.rs +++ b/src/test/ui/issue-17718-const-bad-values.rs @@ -15,5 +15,6 @@ static mut S: usize = 3; const C2: &'static mut usize = unsafe { &mut S }; //~^ ERROR: references in constants may only refer to immutable values //~| ERROR: references in constants may only refer to immutable values +//~| ERROR: constants cannot refer to statics, use a constant instead fn main() {} From 49c19c7a2915a4816690c76285a553954e7fc98c Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Fri, 30 Mar 2018 02:38:35 +0100 Subject: [PATCH 07/10] Changed use of `IndexVec` to `IdxSetBuf` in `Qualifier`. --- src/librustc_data_structures/indexed_set.rs | 10 +++ src/librustc_mir/transform/qualify_consts.rs | 68 ++++++++++---------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/librustc_data_structures/indexed_set.rs b/src/librustc_data_structures/indexed_set.rs index 2e95a45479c4f..47d099e6b87e8 100644 --- a/src/librustc_data_structures/indexed_set.rs +++ b/src/librustc_data_structures/indexed_set.rs @@ -210,6 +210,16 @@ impl IdxSet { self.bits.set_bit(elem.index()) } + /// Adds `elem` to the set `self` if `member` is `true`, otherwise removes `elem` from the set; + /// returns true iff this changed `self`. + pub fn set_member(&mut self, elem: &T, member: bool) -> bool { + if member { + self.add(elem) + } else { + self.remove(elem) + } + } + pub fn range(&self, elems: &Range) -> &Self { let elems = elems.start.index()..elems.end.index(); unsafe { Self::from_slice(&self.bits[elems]) } diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 55839443ae4f0..80b311c67b823 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -110,9 +110,9 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { rpo: ReversePostorder<'a, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, - local_mut_interior: IndexVec, - local_needs_drop: IndexVec, - local_not_const: IndexVec, + local_mut_interior: IdxSetBuf, + local_needs_drop: IdxSetBuf, + local_not_const: IdxSetBuf, qualif: Qualif, const_fn_arg_vars: BitVector, temp_promotion_state: IndexVec, @@ -131,11 +131,13 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { let param_env = tcx.param_env(def_id); - let mut local_needs_drop = IndexVec::from_elem(false, &mir.local_decls); - let mut local_not_const = IndexVec::from_elem(true, &mir.local_decls); + let mut local_needs_drop = IdxSetBuf::new_empty(mir.local_decls.len()); + let mut local_not_const = IdxSetBuf::new_filled(mir.local_decls.len()); for arg in mir.args_iter() { - local_needs_drop[arg] = mir.local_decls[arg].ty.needs_drop(tcx, param_env); - local_not_const[arg] = false; + if mir.local_decls[arg].ty.needs_drop(tcx, param_env) { + local_needs_drop.add(&arg); + } + local_not_const.remove(&arg); } Qualifier { @@ -146,7 +148,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { rpo, tcx, param_env, - local_mut_interior: IndexVec::from_elem(false, &mir.local_decls), + local_mut_interior: IdxSetBuf::new_empty(mir.local_decls.len()), local_needs_drop, local_not_const, qualif: Qualif::default(), @@ -206,8 +208,8 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { self.qualif.not_const |= qualif.not_const; } - /// Restrict `self.qualif` by the given type's qualification. - fn restrict_to_type(&mut self, ty: Ty<'tcx>) { + /// Add the given type's qualification to `self.qualif`. + fn add_type(&mut self, ty: Ty<'tcx>) { self.qualif.mut_interior = !ty.is_freeze(self.tcx, self.param_env, DUMMY_SP); self.qualif.needs_drop = ty.needs_drop(self.tcx, self.param_env); } @@ -226,9 +228,9 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { fn assign(&mut self, dest: &Place<'tcx>, location: Location) { trace!("assign: {:?}", dest); let store = |this: &mut Self, index| { - this.local_mut_interior[index] = this.qualif.mut_interior; - this.local_needs_drop[index] = this.qualif.needs_drop; - this.local_not_const[index] = this.qualif.not_const; + this.local_mut_interior.set_member(&index, this.qualif.mut_interior); + this.local_needs_drop.set_member(&index, this.qualif.needs_drop); + this.local_not_const.set_member(&index, this.qualif.not_const); }; // Only handle promotable temps in non-const functions. @@ -261,7 +263,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.local_not_const[index] => { + && self.local_not_const.contains(&index) => { // Part of `box expr`, we should've errored // already for the Box allocation Rvalue. } @@ -316,9 +318,9 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { // Check for unused values. This usually means // there are extra statements in the AST. for temp in mir.temps_iter() { - if !self.local_mut_interior[temp] && - !self.local_needs_drop[temp] && - !self.local_not_const[temp] { + if !self.local_mut_interior.contains(&temp) && + !self.local_needs_drop.contains(&temp) && + !self.local_not_const.contains(&temp) { continue; } @@ -371,9 +373,9 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { } } - self.qualif.mut_interior = self.local_mut_interior[RETURN_PLACE]; - self.qualif.needs_drop = self.local_needs_drop[RETURN_PLACE]; - self.qualif.not_const = self.local_not_const[RETURN_PLACE]; + self.qualif.mut_interior = self.local_mut_interior.contains(&RETURN_PLACE); + self.qualif.needs_drop = self.local_needs_drop.contains(&RETURN_PLACE); + self.qualif.not_const = self.local_not_const.contains(&RETURN_PLACE); // Collect all the temps we need to promote. let mut promoted_temps = IdxSetBuf::new_empty(self.temp_promotion_state.len()); @@ -396,7 +398,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { } } -/// Accumulates an Rvalue or Call's effects in self.qualif. +/// Accumulates an Rvalue or Call's effects in `self.qualif`. /// For functions (constant or not), it also records /// candidates for promotion in promotion_candidates. impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { @@ -425,9 +427,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } let mut qualif = Qualif::default(); - qualif.mut_interior = self.local_mut_interior[local]; - qualif.needs_drop = self.local_needs_drop[local]; - qualif.not_const = self.local_not_const[local]; + qualif.mut_interior = self.local_mut_interior.contains(&local); + qualif.needs_drop = self.local_needs_drop.contains(&local); + qualif.not_const = self.local_not_const.contains(&local); self.add(qualif); } } @@ -520,7 +522,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx); - this.restrict_to_type(ty); + this.add_type(ty); } ProjectionElem::ConstantIndex {..} | @@ -542,7 +544,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { Operand::Move(_) => { // Mark the consumed locals to indicate later drops are noops. if let Operand::Move(Place::Local(local)) = *operand { - self.local_needs_drop[local] = false; + self.local_needs_drop.remove(&local); } } Operand::Constant(ref constant) => { @@ -551,7 +553,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.restrict_to_type(ty); + self.add_type(ty); } else { let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id); @@ -561,7 +563,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { // Just in case the type is more specific than // the definition, e.g. impl associated const // with type parameters, take it into account. - self.restrict_to_type(ty); + self.add_type(ty); } } } @@ -694,8 +696,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { // This allows borrowing fields which don't have // `MUTABLE_INTERIOR`, from a type that does, e.g.: // `let _: &'static _ = &(Cell::new(1), 2).1;` - if !self.local_not_const[local] && - !self.local_needs_drop[local] { + if !self.local_not_const.contains(&local) && + !self.local_needs_drop.contains(&local) { self.promotion_candidates.push(candidate); } } @@ -801,7 +803,7 @@ This does not pose a problem by itself because they can't be accessed directly." if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() { let ty = rvalue.ty(self.mir, self.tcx); - self.restrict_to_type(ty); + self.add_type(ty); assert!(self.qualif.mut_interior); } } @@ -956,7 +958,7 @@ This does not pose a problem by itself because they can't be accessed directly." if let Some((ref dest, _)) = *destination { // Be conservative about the returned value of a const fn. let ty = dest.ty(self.mir, self.tcx).to_ty(self.tcx); - self.restrict_to_type(ty); + self.add_type(ty); self.assign(dest, location); } } else if let TerminatorKind::Drop { location: ref place, .. } = *kind { @@ -967,7 +969,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.local_needs_drop[local] { + if self.local_needs_drop.contains(&local) { Some(self.mir.local_decls[local].source_info.span) } else { None From 2b2b8add8146d36c5518b09c72a0f64d363c9bde Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Fri, 30 Mar 2018 05:17:02 +0100 Subject: [PATCH 08/10] Moved static-array-across-crate test from compile-fail to run-pass. --- src/test/run-pass/static-by-reference.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/run-pass/static-by-reference.rs b/src/test/run-pass/static-by-reference.rs index a83f29b8c70da..fddb92ce75ca6 100644 --- a/src/test/run-pass/static-by-reference.rs +++ b/src/test/run-pass/static-by-reference.rs @@ -12,8 +12,7 @@ struct Foo { a: u32 } -static S : Foo = Foo { a : 0 }; -static A : &'static u32 = &S.a; +static S: Foo = Foo { a : 0 }; +static A: &'static u32 = &S.a; -fn main() { -} +fn main() {} From 94c5e92d1964104be591d168686aa89fba912809 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Sun, 22 Jul 2018 14:20:30 +0100 Subject: [PATCH 09/10] Split const qualification into three passes (mutable interior, needs drop, const checker). --- src/librustc_mir/transform/qualify_consts.rs | 768 ++++++++++++++----- 1 file changed, 596 insertions(+), 172 deletions(-) diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 80b311c67b823..15034cde2f1c4 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -42,43 +42,22 @@ use std::fmt; use transform::{MirPass, MirSource}; use super::promote_consts::{self, Candidate, TempState}; -// Borrows of temporaries can be promoted only if -// they have none of these qualifications. -#[derive(Clone, Copy, Default, Eq, PartialEq)] -struct Qualif { - // Constant containing interior mutability (`UnsafeCell`). - mut_interior: bool, - - // Constant containing an ADT that implements Drop. - needs_drop: bool, - - // Not constant or not promotable - non-`const fn` calls, asm!, - // pointer comparisons, ptr-to-int casts, etc. - not_const: bool, -} - -impl Qualif { - fn from_bits(bits: u8) -> Self { - Self { - mut_interior: bits & (1 << 0) != 0, - needs_drop: bits & (1 << 1) != 0, - not_const: bits & (1 << 2) != 0, - } +pub const MUT_INTERIOR_BITS: u8 = 1 << 0; +pub const NEEDS_DROP_BITS: u8 = 1 << 1; +pub const NOT_CONST_BITS: u8 = 1 << 2; + +fn qualif_bits((mut_interior, needs_drop, not_const): (bool, bool, bool)) -> u8 { + let mut bits = 0; + if mut_interior { + bits |= MUT_INTERIOR_BITS; } - - fn bits(&self) -> u8 { - let mut bits = 0; - if self.mut_interior { - bits |= 1 << 0; - } - if self.needs_drop { - bits |= 1 << 1; - } - if self.not_const { - bits |= 1 << 2; - } - bits + if needs_drop { + bits |= NEEDS_DROP_BITS; } + if not_const { + bits |= NOT_CONST_BITS; + } + bits } /// What kind of item we are in. @@ -102,7 +81,25 @@ impl fmt::Display for Mode { } } -struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { +struct MutInteriorChecker<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { + span: Span, + mir: &'a Mir<'tcx>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + local_mut_interior: IdxSetBuf, + mut_interior: bool +} + +struct DropChecker<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { + span: Span, + mir: &'a Mir<'tcx>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + local_needs_drop: IdxSetBuf, + needs_drop: bool +} + +struct ConstChecker<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { mode: Mode, span: Span, def_id: DefId, @@ -110,37 +107,125 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { rpo: ReversePostorder<'a, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, - local_mut_interior: IdxSetBuf, - local_needs_drop: IdxSetBuf, local_not_const: IdxSetBuf, - qualif: Qualif, + not_const: bool, + mut_interior_checker: MutInteriorChecker<'a, 'gcx, 'tcx>, + drop_checker: DropChecker<'a, 'gcx, 'tcx>, const_fn_arg_vars: BitVector, temp_promotion_state: IndexVec, promotion_candidates: Vec } -impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { +impl<'a, 'tcx> MutInteriorChecker<'a, 'tcx, 'tcx> { + fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + mir: &'a Mir<'tcx>) + -> MutInteriorChecker<'a, 'tcx, 'tcx> { + let param_env = tcx.param_env(def_id); + + MutInteriorChecker { + span: mir.span, + mir, + tcx, + param_env, + local_mut_interior: IdxSetBuf::new_empty(mir.local_decls.len()), + mut_interior: false + } + } + + /// Add the given type's qualification to `self.mut_interior`. + fn add_type(&mut self, ty: Ty<'tcx>) { + self.mut_interior = !ty.is_freeze(self.tcx, self.param_env, DUMMY_SP); + } + + /// Within the provided closure, `self.mut_interior` will start + /// out false, and its value after the closure returns will + /// be combined with the value before the call to nest. + fn nest(&mut self, f: F) { + let original = self.mut_interior; + self.mut_interior = false; + f(self); + self.mut_interior |= original; + } + + /// Assign the current qualification to the given destination. + fn assign(&mut self, dest: &Place<'tcx>, _: Location) { + if let Place::Local(index) = *dest { + debug!("store to {:?} {:?}", self.mir.local_kind(index), index); + self.local_mut_interior.set_member(&index, self.mut_interior); + } + } +} + +impl<'a, 'tcx> DropChecker<'a, 'tcx, 'tcx> { + fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + mir: &'a Mir<'tcx>) + -> DropChecker<'a, 'tcx, 'tcx> { + let param_env = tcx.param_env(def_id); + + let mut local_needs_drop = IdxSetBuf::new_empty(mir.local_decls.len()); + for arg in mir.args_iter() { + if mir.local_decls[arg].ty.needs_drop(tcx, param_env) { + local_needs_drop.add(&arg); + } + } + + DropChecker { + span: mir.span, + mir, + tcx, + param_env, + local_needs_drop, + needs_drop: false + } + } + + /// Add the given type's qualification to `self.needs_drop`. + fn add_type(&mut self, ty: Ty<'tcx>) { + self.needs_drop = ty.needs_drop(self.tcx, self.param_env); + } + + /// Within the provided closure, `self.needs_drop` will start + /// out false, and its value after the closure returns will + /// be combined with the value before the call to nest. + fn nest(&mut self, f: F) { + let original = self.needs_drop; + self.needs_drop = false; + f(self); + self.needs_drop |= original; + } + + /// Assign the current qualification to the given destination. + fn assign(&mut self, dest: &Place<'tcx>, _: Location) { + if let Place::Local(index) = *dest { + debug!("store to {:?} {:?}", self.mir.local_kind(index), index); + self.local_needs_drop.set_member(&index, self.needs_drop); + } + } +} + +impl<'a, 'tcx> ConstChecker<'a, 'tcx, 'tcx> { fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, mir: &'a Mir<'tcx>, mode: Mode) - -> Qualifier<'a, 'tcx, 'tcx> { + -> ConstChecker<'a, 'tcx, 'tcx> { let mut rpo = traversal::reverse_postorder(mir); let temps = promote_consts::collect_temps(mir, &mut rpo); rpo.reset(); let param_env = tcx.param_env(def_id); - let mut local_needs_drop = IdxSetBuf::new_empty(mir.local_decls.len()); let mut local_not_const = IdxSetBuf::new_filled(mir.local_decls.len()); for arg in mir.args_iter() { - if mir.local_decls[arg].ty.needs_drop(tcx, param_env) { - local_needs_drop.add(&arg); - } local_not_const.remove(&arg); } - Qualifier { + let mut_interior_checker = MutInteriorChecker::new(tcx, def_id, mir); + let drop_checker = DropChecker::new(tcx, def_id, mir); + + ConstChecker { mode, span: mir.span, def_id, @@ -148,21 +233,28 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { rpo, tcx, param_env, - local_mut_interior: IdxSetBuf::new_empty(mir.local_decls.len()), - local_needs_drop, local_not_const, - qualif: Qualif::default(), + not_const: false, + mut_interior_checker, + drop_checker, const_fn_arg_vars: BitVector::new(mir.local_decls.len()), temp_promotion_state: temps, - promotion_candidates: vec![] + promotion_candidates: Vec::new() } } + /// Returns whether all qualification flags are unset. + fn is_qualif_empty(&self) -> bool { + !(self.not_const || + self.mut_interior_checker.mut_interior || + self.drop_checker.needs_drop) + } + // FIXME(eddyb) we could split the errors into meaningful // categories, but enabling full miri would make that // slightly pointless (even with feature-gating). fn not_const(&mut self) { - self.qualif.not_const = true; + self.not_const = true; if self.mode != Mode::Fn { let mut err = struct_span_err!( self.tcx.sess, @@ -183,7 +275,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { /// Error about extra statements in a constant. fn statement_like(&mut self) { - self.qualif.not_const = true; + self.not_const = true; if self.mode != Mode::Fn { let mut err = feature_err( &self.tcx.sess.parse_sess, @@ -201,37 +293,19 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { } } - /// Add the given qualification to `self.qualif`. - fn add(&mut self, qualif: Qualif) { - self.qualif.mut_interior |= qualif.mut_interior; - self.qualif.needs_drop |= qualif.needs_drop; - self.qualif.not_const |= qualif.not_const; - } - - /// Add the given type's qualification to `self.qualif`. - fn add_type(&mut self, ty: Ty<'tcx>) { - self.qualif.mut_interior = !ty.is_freeze(self.tcx, self.param_env, DUMMY_SP); - self.qualif.needs_drop = ty.needs_drop(self.tcx, self.param_env); - } - - /// Within the provided closure, `self.qualif` will start + /// Within the provided closure, `self.not_const` will start /// out empty, and its value after the closure returns will - /// be combined with the value before the call to nest. + /// be combined with the value before the call to `nest`. fn nest(&mut self, f: F) { - let original = self.qualif; - self.qualif = Qualif::default(); + let original = self.not_const; + self.not_const = false; f(self); - self.add(original); + self.not_const |= original; } /// Assign the current qualification to the given destination. fn assign(&mut self, dest: &Place<'tcx>, location: Location) { trace!("assign: {:?}", dest); - let store = |this: &mut Self, index| { - this.local_mut_interior.set_member(&index, this.qualif.mut_interior); - this.local_needs_drop.set_member(&index, this.qualif.needs_drop); - this.local_not_const.set_member(&index, this.qualif.not_const); - }; // Only handle promotable temps in non-const functions. if self.mode == Mode::Fn { @@ -239,7 +313,7 @@ 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(self, index) + self.local_not_const.set_member(&index, self.not_const); } } return; @@ -250,12 +324,12 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { self.mir.local_kind(index) == LocalKind::Arg) && self.tcx.sess.features_untracked().const_let => { debug!("store to var {:?}", index); - store(self, index) + self.local_not_const.set_member(&index, self.not_const); } 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(self, index) + self.local_not_const.set_member(&index, self.not_const); } Place::Projection(box Projection { @@ -278,7 +352,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { } /// Qualify a whole const, static initializer or const fn. - fn qualify_const(&mut self) -> (Qualif, Lrc>) { + fn qualify_const(&mut self) -> ((bool, bool, bool), Lrc>) { debug!("qualifying {} {:?}", self.mode, self.def_id); let mir = self.mir; @@ -318,8 +392,8 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { // Check for unused values. This usually means // there are extra statements in the AST. for temp in mir.temps_iter() { - if !self.local_mut_interior.contains(&temp) && - !self.local_needs_drop.contains(&temp) && + if !self.mut_interior_checker.local_mut_interior.contains(&temp) && + !self.drop_checker.local_needs_drop.contains(&temp) && !self.local_not_const.contains(&temp) { continue; } @@ -343,9 +417,9 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { } // Make sure there are no extra unassigned variables. - self.qualif.mut_interior = false; - self.qualif.needs_drop = false; - self.qualif.not_const = true; + self.mut_interior_checker.mut_interior = false; + self.drop_checker.needs_drop = false; + self.not_const = true; for index in mir.vars_iter() { if !self.const_fn_arg_vars.contains(index.index()) { debug!("unassigned variable {:?}", index); @@ -373,9 +447,12 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { } } - self.qualif.mut_interior = self.local_mut_interior.contains(&RETURN_PLACE); - self.qualif.needs_drop = self.local_needs_drop.contains(&RETURN_PLACE); - self.qualif.not_const = self.local_not_const.contains(&RETURN_PLACE); + self.mut_interior_checker.mut_interior = + self.mut_interior_checker.local_mut_interior.contains(&RETURN_PLACE); + self.drop_checker.needs_drop = + self.drop_checker.local_needs_drop.contains(&RETURN_PLACE); + self.not_const = + self.local_not_const.contains(&RETURN_PLACE); // Collect all the temps we need to promote. let mut promoted_temps = IdxSetBuf::new_empty(self.temp_promotion_state.len()); @@ -394,43 +471,399 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { } } - (self.qualif, Lrc::new(promoted_temps)) + let qualif = (self.mut_interior_checker.mut_interior, + self.drop_checker.needs_drop, + self.not_const); + (qualif, Lrc::new(promoted_temps)) + } +} + +/// Accumulates an Rvalue or a Call's effects into `self.mut_interior`. +impl<'a, 'tcx> Visitor<'tcx> for MutInteriorChecker<'a, 'tcx, 'tcx> { + fn visit_local(&mut self, + &local: &Local, + _: PlaceContext<'tcx>, + _: Location) { + let kind = self.mir.local_kind(local); + match kind { + LocalKind::Var if !self.tcx.sess.features_untracked().const_let => {} + LocalKind::Var | + LocalKind::Arg | + LocalKind::Temp => { + self.mut_interior |= self.local_mut_interior.contains(&local); + } + LocalKind::ReturnPointer => {} + } + } + + fn visit_place(&mut self, + place: &Place<'tcx>, + context: PlaceContext<'tcx>, + location: Location) { + match *place { + Place::Local(ref local) => self.visit_local(local, context, location), + Place::Static(..) => {}, + Place::Projection(ref proj) => { + self.nest(|this| { + this.super_place(place, context, location); + match proj.elem { + ProjectionElem::Deref => {} + + ProjectionElem::Field(..) | + ProjectionElem::Index(_) => { + let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx); + this.add_type(ty); + } + + ProjectionElem::ConstantIndex {..} | + ProjectionElem::Subslice {..} | + ProjectionElem::Downcast(..) => {} + } + }); + } + } + } + + fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { + self.super_operand(operand, location); + + match *operand { + Operand::Copy(_) | + Operand::Move(_) => {} + Operand::Constant(ref constant) => { + if let Literal::Value { + value: &ty::Const { val: ConstValue::Unevaluated(def_id, _), ty, .. } + } = constant.literal { + // Don't peek inside trait associated constants. + if self.tcx.trait_of_item(def_id).is_some() { + self.add_type(ty); + } else { + let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id); + self.mut_interior |= (bits & MUT_INTERIOR_BITS) != 0; + + // Just in case the type is more specific than + // the definition, e.g. impl associated const + // with type parameters, take it into account. + self.add_type(ty); + } + } + } + } + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + // Recurse through operands and places. + if let Rvalue::Ref(region, kind, ref place) = *rvalue { + let mut is_reborrow = false; + if let Place::Projection(ref proj) = *place { + if let ProjectionElem::Deref = proj.elem { + let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); + if let ty::TyRef(..) = base_ty.sty { + is_reborrow = true; + } + } + } + + if is_reborrow { + self.super_place(place, PlaceContext::Borrow { + region, + kind + }, location); + } else { + self.super_rvalue(rvalue, location); + } + } else { + self.super_rvalue(rvalue, location); + } + } + + fn visit_terminator_kind(&mut self, + bb: BasicBlock, + kind: &TerminatorKind<'tcx>, + location: Location) { + if let TerminatorKind::Call { ref func, ref destination, .. } = *kind { + self.visit_operand(func, location); + + if let Some((ref dest, _)) = *destination { + // Be conservative about the returned value of a const fn. + let ty = dest.ty(self.mir, self.tcx).to_ty(self.tcx); + self.add_type(ty); + self.assign(dest, location); + } + } else if let TerminatorKind::Drop { .. } = *kind { + self.super_terminator_kind(bb, kind, location); + } else { + // Qualify any operands inside other terminators. + self.super_terminator_kind(bb, kind, location); + } + } + + fn visit_assign(&mut self, + _: BasicBlock, + dest: &Place<'tcx>, + rvalue: &Rvalue<'tcx>, + location: Location) { + self.visit_rvalue(rvalue, location); + self.assign(dest, location); + } + + fn visit_source_info(&mut self, source_info: &SourceInfo) { + self.span = source_info.span; + } + + fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) { + self.nest(|this| { + this.visit_source_info(&statement.source_info); + match statement.kind { + StatementKind::Assign(ref place, ref rvalue) => { + this.visit_assign(bb, place, rvalue, location); + } + StatementKind::ReadForMatch(..) | + StatementKind::SetDiscriminant { .. } | + StatementKind::StorageLive(_) | + StatementKind::StorageDead(_) | + StatementKind::InlineAsm {..} | + StatementKind::EndRegion(_) | + StatementKind::Validate(..) | + StatementKind::UserAssertTy(..) | + StatementKind::Nop => {} + } + }); + } + + fn visit_terminator(&mut self, + bb: BasicBlock, + terminator: &Terminator<'tcx>, + location: Location) { + self.nest(|this| this.super_terminator(bb, terminator, location)); + } +} + +/// Accumulates an Rvalue or a Call's effects into `self.needs_drop`. +impl<'a, 'tcx> Visitor<'tcx> for DropChecker<'a, 'tcx, 'tcx> { + fn visit_local(&mut self, + &local: &Local, + _: PlaceContext<'tcx>, + _: Location) { + let kind = self.mir.local_kind(local); + match kind { + LocalKind::Var if !self.tcx.sess.features_untracked().const_let => {} + LocalKind::Var | + LocalKind::Arg | + LocalKind::Temp => { + self.needs_drop |= self.local_needs_drop.contains(&local); + } + LocalKind::ReturnPointer => {} + } + } + + fn visit_place(&mut self, + place: &Place<'tcx>, + context: PlaceContext<'tcx>, + location: Location) { + match *place { + Place::Local(ref local) => self.visit_local(local, context, location), + Place::Static(..) => {} + + Place::Projection(ref proj) => { + self.nest(|this| { + this.super_place(place, context, location); + match proj.elem { + ProjectionElem::Deref => {} + + ProjectionElem::Field(..) | + ProjectionElem::Index(_) => { + let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx); + this.add_type(ty); + } + + ProjectionElem::ConstantIndex {..} | + ProjectionElem::Subslice {..} | + ProjectionElem::Downcast(..) => {} + } + }); + } + } + } + + fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { + self.super_operand(operand, location); + + match *operand { + Operand::Copy(_) | + Operand::Move(_) => { + // Mark the consumed locals to indicate later drops are noops. + if let Operand::Move(Place::Local(local)) = *operand { + self.local_needs_drop.remove(&local); + } + } + Operand::Constant(ref constant) => { + if let Literal::Value { + value: &ty::Const { val: ConstValue::Unevaluated(def_id, _), ty } + } = constant.literal { + // Don't peek inside trait-associated constants. + if self.tcx.trait_of_item(def_id).is_some() { + self.add_type(ty); + } else { + let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id); + self.needs_drop |= (bits & NEEDS_DROP_BITS) != 0; + + // Just in case the type is more specific than + // the definition, e.g. impl associated const + // with type parameters, take it into account. + self.add_type(ty); + } + } + } + } + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + // Recurse through operands and places. + if let Rvalue::Ref(region, kind, ref place) = *rvalue { + let mut is_reborrow = false; + if let Place::Projection(ref proj) = *place { + if let ProjectionElem::Deref = proj.elem { + let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); + if let ty::TyRef(..) = base_ty.sty { + is_reborrow = true; + } + } + } + + if is_reborrow { + self.super_place(place, PlaceContext::Borrow { + region, + kind + }, location); + } else { + self.super_rvalue(rvalue, location); + } + } else { + self.super_rvalue(rvalue, location); + } + + match *rvalue { + Rvalue::Use(_) | + Rvalue::Repeat(..) | + Rvalue::UnaryOp(UnOp::Neg, _) | + Rvalue::UnaryOp(UnOp::Not, _) | + Rvalue::NullaryOp(NullOp::SizeOf, _) | + Rvalue::CheckedBinaryOp(..) | + Rvalue::Cast(CastKind::ReifyFnPointer, ..) | + Rvalue::Cast(CastKind::UnsafeFnPointer, ..) | + Rvalue::Cast(CastKind::ClosureFnPointer, ..) | + Rvalue::Cast(CastKind::Unsize, ..) | + Rvalue::Discriminant(..) | + Rvalue::Len(_) | + Rvalue::Ref(..) | + Rvalue::Cast(CastKind::Misc, ..) | + Rvalue::BinaryOp(..) | + Rvalue::NullaryOp(NullOp::Box, _) => {} + + Rvalue::Aggregate(ref kind, _) => { + if let AggregateKind::Adt(def, ..) = **kind { + if def.has_dtor(self.tcx) { + self.needs_drop = true; + } + + if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() { + let ty = rvalue.ty(self.mir, self.tcx); + self.add_type(ty); + } + } + } + } + } + + fn visit_terminator_kind(&mut self, + bb: BasicBlock, + kind: &TerminatorKind<'tcx>, + location: Location) { + if let TerminatorKind::Call { ref func, ref destination, .. } = *kind { + self.visit_operand(func, location); + + if let Some((ref dest, _)) = *destination { + // Be conservative about the returned value of a const fn. + let ty = dest.ty(self.mir, self.tcx).to_ty(self.tcx); + self.add_type(ty); + self.assign(dest, location); + } + } else if let TerminatorKind::Drop { .. } = *kind { + self.super_terminator_kind(bb, kind, location); + } else { + // Qualify any operands inside other terminators. + self.super_terminator_kind(bb, kind, location); + } + } + + fn visit_assign(&mut self, + _: BasicBlock, + dest: &Place<'tcx>, + rvalue: &Rvalue<'tcx>, + location: Location) { + self.visit_rvalue(rvalue, location); + self.assign(dest, location); + } + + fn visit_source_info(&mut self, source_info: &SourceInfo) { + self.span = source_info.span; + } + + fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) { + self.nest(|this| { + this.visit_source_info(&statement.source_info); + match statement.kind { + StatementKind::Assign(ref place, ref rvalue) => { + this.visit_assign(bb, place, rvalue, location); + } + StatementKind::ReadForMatch(..) | + StatementKind::SetDiscriminant { .. } | + StatementKind::StorageLive(_) | + StatementKind::StorageDead(_) | + StatementKind::InlineAsm {..} | + StatementKind::EndRegion(_) | + StatementKind::Validate(..) | + StatementKind::UserAssertTy(..) | + StatementKind::Nop => {} + } + }); + } + + fn visit_terminator(&mut self, + bb: BasicBlock, + terminator: &Terminator<'tcx>, + location: Location) { + self.nest(|this| this.super_terminator(bb, terminator, location)); } } -/// Accumulates an Rvalue or Call's effects in `self.qualif`. +/// Accumulates an Rvalue or Call's effects in `self.not_const`. /// For functions (constant or not), it also records /// candidates for promotion in promotion_candidates. -impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { +impl<'a, 'tcx> Visitor<'tcx> for ConstChecker<'a, 'tcx, 'tcx> { fn visit_local(&mut self, &local: &Local, _: PlaceContext<'tcx>, _: Location) { let kind = self.mir.local_kind(local); match kind { - LocalKind::ReturnPointer => { - self.not_const(); - } LocalKind::Var if !self.tcx.sess.features_untracked().const_let => { if self.mode != Mode::Fn { emit_feature_err(&self.tcx.sess.parse_sess, "const_let", self.span, GateIssue::Language, &format!("let bindings in {}s are unstable",self.mode)); } - self.qualif.not_const = true; + self.not_const = true; } LocalKind::Var | LocalKind::Arg | LocalKind::Temp => { - if !self.temp_promotion_state[local].is_promotable() { - self.qualif.not_const = true; - } - - let mut qualif = Qualif::default(); - qualif.mut_interior = self.local_mut_interior.contains(&local); - qualif.needs_drop = self.local_needs_drop.contains(&local); - qualif.not_const = self.local_not_const.contains(&local); - self.add(qualif); + self.not_const |= self.local_not_const.contains(&local) || + !self.temp_promotion_state[local].is_promotable(); + } + LocalKind::ReturnPointer => { + self.not_const(); } } } @@ -439,6 +872,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { place: &Place<'tcx>, context: PlaceContext<'tcx>, location: Location) { + self.mut_interior_checker.visit_place(place, context, location); + self.drop_checker.visit_place(place, context, location); + match *place { Place::Local(ref local) => self.visit_local(local, context, location), Place::Static(ref global) => { @@ -451,7 +887,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { "thread-local statics cannot be \ accessed at compile-time"); } - self.qualif.not_const = true; + self.not_const = true; return; } @@ -459,7 +895,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { if self.mode == Mode::Static || self.mode == Mode::StaticMut { return; } - self.qualif.not_const = true; + self.not_const = true; if self.mode != Mode::Fn { let mut err = struct_span_err!(self.tcx.sess, self.span, E0013, @@ -482,7 +918,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { this.super_place(place, context, location); match proj.elem { ProjectionElem::Deref => { - this.qualif.not_const = true; + this.not_const = true; let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx); if let ty::TyRawPtr(_) = base_ty.sty { @@ -520,9 +956,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } } } - - let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx); - this.add_type(ty); } ProjectionElem::ConstantIndex {..} | @@ -539,31 +972,20 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { self.super_operand(operand, location); + self.mut_interior_checker.visit_operand(operand, location); + self.drop_checker.visit_operand(operand, location); + match *operand { Operand::Copy(_) | - Operand::Move(_) => { - // Mark the consumed locals to indicate later drops are noops. - if let Operand::Move(Place::Local(local)) = *operand { - self.local_needs_drop.remove(&local); - } - } + Operand::Move(_) => {} Operand::Constant(ref constant) => { if let Literal::Value { - value: &ty::Const { val: ConstValue::Unevaluated(def_id, _), ty, .. } + value: &ty::Const { val: ConstValue::Unevaluated(def_id, _), .. } } = constant.literal { // Don't peek inside trait associated constants. - if self.tcx.trait_of_item(def_id).is_some() { - self.add_type(ty); - } else { + if self.tcx.trait_of_item(def_id).is_none() { let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id); - - let qualif = Qualif::from_bits(bits); - self.add(qualif); - - // Just in case the type is more specific than - // the definition, e.g. impl associated const - // with type parameters, take it into account. - self.add_type(ty); + self.not_const |= (bits & NOT_CONST_BITS) != 0; } } } @@ -571,6 +993,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + self.mut_interior_checker.visit_rvalue(rvalue, location); + self.drop_checker.visit_rvalue(rvalue, location); + // Recurse through operands and places. if let Rvalue::Ref(region, kind, ref place) = *rvalue { let mut is_reborrow = false; @@ -636,7 +1061,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } if forbidden_mut { - self.qualif.not_const = true; + self.not_const = true; if self.mode != Mode::Fn { let mut err = struct_span_err!(self.tcx.sess, self.span, E0017, "references in {}s may only refer \ @@ -660,11 +1085,11 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { // Constants cannot be borrowed if they contain interior mutability as // it means that our "silent insertion of statics" could change // initializer values (very bad). - if self.qualif.mut_interior { + if self.mut_interior_checker.mut_interior { // A reference of a MUTABLE_INTERIOR place is instead // NOT_CONST (see `if forbidden_mut` below), to avoid // duplicate errors (from reborrowing, for example). - self.qualif.mut_interior = false; + self.mut_interior_checker.mut_interior = false; if self.mode != Mode::Fn { span_err!(self.tcx.sess, self.span, E0492, "cannot borrow a constant which may contain \ @@ -677,7 +1102,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } if forbidden_mut { - self.qualif.not_const = true; + self.not_const = true; } else { // We might have a candidate for promotion. let candidate = Candidate::Ref(location); @@ -696,8 +1121,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { // This allows borrowing fields which don't have // `MUTABLE_INTERIOR`, from a type that does, e.g.: // `let _: &'static _ = &(Cell::new(1), 2).1;` - if !self.local_not_const.contains(&local) && - !self.local_needs_drop.contains(&local) { + if !self.drop_checker.local_needs_drop.contains(&local) && + !self.local_not_const.contains(&local) { self.promotion_candidates.push(candidate); } } @@ -712,7 +1137,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { match (cast_in, cast_out) { (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => { - self.qualif.not_const = true; + self.not_const = true; if self.mode != Mode::Fn { let mut err = struct_span_err!( self.tcx.sess, @@ -763,7 +1188,7 @@ This does not pose a problem by itself because they can't be accessed directly." op == BinOp::Ge || op == BinOp::Gt || op == BinOp::Offset); - self.qualif.not_const = true; + self.not_const = true; if self.mode != Mode::Fn { struct_span_err!( self.tcx.sess, self.span, E0395, @@ -778,7 +1203,7 @@ This does not pose a problem by itself because they can't be accessed directly." } Rvalue::NullaryOp(NullOp::Box, _) => { - self.qualif.not_const = true; + self.not_const = true; if self.mode != Mode::Fn { let mut err = struct_span_err!(self.tcx.sess, self.span, E0010, "allocations are not allowed in {}s", self.mode); @@ -795,19 +1220,7 @@ This does not pose a problem by itself because they can't be accessed directly." } } - Rvalue::Aggregate(ref kind, _) => { - if let AggregateKind::Adt(def, ..) = **kind { - if def.has_dtor(self.tcx) { - self.qualif.needs_drop = true; - } - - if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() { - let ty = rvalue.ty(self.mir, self.tcx); - self.add_type(ty); - assert!(self.qualif.mut_interior); - } - } - } + Rvalue::Aggregate(..) => {} } } @@ -815,6 +1228,9 @@ This does not pose a problem by itself because they can't be accessed directly." bb: BasicBlock, kind: &TerminatorKind<'tcx>, location: Location) { + self.mut_interior_checker.visit_terminator_kind(bb, kind, location); + self.drop_checker.visit_terminator_kind(bb, kind, location); + if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind { self.visit_operand(func, location); @@ -864,7 +1280,7 @@ This does not pose a problem by itself because they can't be accessed directly." } let candidate = Candidate::Argument { bb, index: i }; if is_shuffle && i == 2 { - if this.qualif == Default::default() { + if this.is_qualif_empty() { this.promotion_candidates.push(candidate); } else { span_err!(this.tcx.sess, this.span, E0526, @@ -880,7 +1296,7 @@ This does not pose a problem by itself because they can't be accessed directly." if !constant_arguments.contains(&i) { return } - if this.qualif == Default::default() { + if this.is_qualif_empty() { this.promotion_candidates.push(candidate); } else { this.tcx.sess.span_err(this.span, @@ -911,9 +1327,9 @@ This does not pose a problem by itself because they can't be accessed directly." // this doesn't come from a macro that has #[allow_internal_unstable] !self.span.allows_unstable() { - self.qualif.mut_interior = false; - self.qualif.needs_drop = false; - self.qualif.not_const = true; + self.mut_interior_checker.mut_interior = false; + self.drop_checker.needs_drop = false; + self.not_const = true; if self.mode != Mode::Fn { // inside a constant environment, not having the feature gate is // an error @@ -929,7 +1345,7 @@ This does not pose a problem by itself because they can't be accessed directly." } } } else { - self.qualif.not_const = true; + self.not_const = true; if self.mode != Mode::Fn { // FIXME(#24111) Remove this check when const fn stabilizes let (msg, note) = if let UnstableFeatures::Disallow = @@ -956,9 +1372,6 @@ This does not pose a problem by itself because they can't be accessed directly." } if let Some((ref dest, _)) = *destination { - // Be conservative about the returned value of a const fn. - let ty = dest.ty(self.mir, self.tcx).to_ty(self.tcx); - self.add_type(ty); self.assign(dest, location); } } else if let TerminatorKind::Drop { location: ref place, .. } = *kind { @@ -969,7 +1382,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.local_needs_drop.contains(&local) { + if self.drop_checker.local_needs_drop.contains(&local) { Some(self.mir.local_decls[local].source_info.span) } else { None @@ -997,10 +1410,13 @@ This does not pose a problem by itself because they can't be accessed directly." } fn visit_assign(&mut self, - _: BasicBlock, + bb: BasicBlock, dest: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { + self.mut_interior_checker.visit_assign(bb, dest, rvalue, location); + self.drop_checker.visit_assign(bb, dest, rvalue, location); + self.visit_rvalue(rvalue, location); // Check the allowed const fn argument forms. @@ -1026,10 +1442,16 @@ This does not pose a problem by itself because they can't be accessed directly." } fn visit_source_info(&mut self, source_info: &SourceInfo) { + self.mut_interior_checker.visit_source_info(source_info); + self.drop_checker.visit_source_info(source_info); + self.span = source_info.span; } fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) { + self.mut_interior_checker.visit_statement(bb, statement, location); + self.drop_checker.visit_statement(bb, statement, location); + self.nest(|this| { this.visit_source_info(&statement.source_info); match statement.kind { @@ -1053,6 +1475,9 @@ This does not pose a problem by itself because they can't be accessed directly." bb: BasicBlock, terminator: &Terminator<'tcx>, location: Location) { + self.mut_interior_checker.visit_terminator(bb, terminator, location); + self.drop_checker.visit_terminator(bb, terminator, location); + self.nest(|this| this.super_terminator(bb, terminator, location)); } } @@ -1075,13 +1500,12 @@ fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if mir.return_ty().references_error() { tcx.sess.delay_span_bug(mir.span, "mir_const_qualif: Mir had errors"); - return (Qualif {not_const: true, ..Default::default()}.bits(), - Lrc::new(IdxSetBuf::new_empty(0))); + return (qualif_bits((false, false, true)), Lrc::new(IdxSetBuf::new_empty(0))); } - let mut qualifier = Qualifier::new(tcx, def_id, mir, Mode::Const); - let (qualif, promoted_temps) = qualifier.qualify_const(); - (qualif.bits(), promoted_temps) + let mut checker = ConstChecker::new(tcx, def_id, mir, Mode::Const); + let (qualif, promoted_temps) = checker.qualify_const(); + (qualif_bits(qualif), promoted_temps) } pub struct QualifyAndPromoteConstants; @@ -1121,20 +1545,20 @@ impl MirPass for QualifyAndPromoteConstants { }; if mode == Mode::Fn || mode == Mode::ConstFn { - // This is ugly because Qualifier holds onto mir, + // This is ugly because `ConstChecker` holds onto mir, // which can't be mutated until its scope ends. let (temps, candidates) = { - let mut qualifier = Qualifier::new(tcx, def_id, mir, mode); + let mut checker = ConstChecker::new(tcx, def_id, mir, mode); if mode == Mode::ConstFn { // Enforce a constant-like CFG for `const fn`. - qualifier.qualify_const(); + checker.qualify_const(); } else { - while let Some((bb, data)) = qualifier.rpo.next() { - qualifier.visit_basic_block_data(bb, data); + while let Some((bb, data)) = checker.rpo.next() { + checker.visit_basic_block_data(bb, data); } } - (qualifier.temp_promotion_state, qualifier.promotion_candidates) + (checker.temp_promotion_state, checker.promotion_candidates) }; // Do the actual promotion, now that we know what's viable. @@ -1144,7 +1568,7 @@ impl MirPass for QualifyAndPromoteConstants { // Already computed by `mir_const_qualif`. const_promoted_temps.unwrap() } else { - Qualifier::new(tcx, def_id, mir, mode).qualify_const().1 + ConstChecker::new(tcx, def_id, mir, mode).qualify_const().1 }; // In `const` and `static` everything without `StorageDead` From c1e78037d073a58cda3f145cda3bdb32967bcc7f Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Wed, 8 Aug 2018 03:51:57 +0100 Subject: [PATCH 10/10] Addressed concerns raised in review. --- src/librustc_mir/transform/qualify_consts.rs | 40 ++++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 15034cde2f1c4..b50f81686c564 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -42,22 +42,16 @@ use std::fmt; use transform::{MirPass, MirSource}; use super::promote_consts::{self, Candidate, TempState}; -pub const MUT_INTERIOR_BITS: u8 = 1 << 0; -pub const NEEDS_DROP_BITS: u8 = 1 << 1; -pub const NOT_CONST_BITS: u8 = 1 << 2; +pub enum QualifBits { + MutInterior, + NeedsDrop, + NotConst, +} fn qualif_bits((mut_interior, needs_drop, not_const): (bool, bool, bool)) -> u8 { - let mut bits = 0; - if mut_interior { - bits |= MUT_INTERIOR_BITS; - } - if needs_drop { - bits |= NEEDS_DROP_BITS; - } - if not_const { - bits |= NOT_CONST_BITS; - } - bits + (mut_interior as u8) << (QualifBits::MutInterior as u32) | + (needs_drop as u8) << (QualifBits::NeedsDrop as u32) | + (not_const as u8) << (QualifBits::NotConst as u32) } /// What kind of item we are in. @@ -297,9 +291,15 @@ impl<'a, 'tcx> ConstChecker<'a, 'tcx, 'tcx> { /// out empty, and its value after the closure returns will /// be combined with the value before the call to `nest`. fn nest(&mut self, f: F) { + let original_mut_interior = self.mut_interior_checker.mut_interior; + let original_needs_drop = self.drop_checker.needs_drop; let original = self.not_const; + self.mut_interior_checker.mut_interior = false; + self.drop_checker.needs_drop = false; self.not_const = false; f(self); + self.mut_interior_checker.mut_interior |= original_mut_interior; + self.drop_checker.needs_drop |= original_needs_drop; self.not_const |= original; } @@ -486,7 +486,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutInteriorChecker<'a, 'tcx, 'tcx> { _: Location) { let kind = self.mir.local_kind(local); match kind { - LocalKind::Var if !self.tcx.sess.features_untracked().const_let => {} LocalKind::Var | LocalKind::Arg | LocalKind::Temp => { @@ -539,7 +538,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MutInteriorChecker<'a, 'tcx, 'tcx> { self.add_type(ty); } else { let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id); - self.mut_interior |= (bits & MUT_INTERIOR_BITS) != 0; + self.mut_interior |= (bits >> QualifBits::MutInterior as u32) & 1 != 0; // Just in case the type is more specific than // the definition, e.g. impl associated const @@ -647,7 +646,6 @@ impl<'a, 'tcx> Visitor<'tcx> for DropChecker<'a, 'tcx, 'tcx> { _: Location) { let kind = self.mir.local_kind(local); match kind { - LocalKind::Var if !self.tcx.sess.features_untracked().const_let => {} LocalKind::Var | LocalKind::Arg | LocalKind::Temp => { @@ -706,7 +704,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DropChecker<'a, 'tcx, 'tcx> { self.add_type(ty); } else { let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id); - self.needs_drop |= (bits & NEEDS_DROP_BITS) != 0; + self.needs_drop |= (bits >> QualifBits::NeedsDrop as u32) & 1 != 0; // Just in case the type is more specific than // the definition, e.g. impl associated const @@ -985,7 +983,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ConstChecker<'a, 'tcx, 'tcx> { // Don't peek inside trait associated constants. if self.tcx.trait_of_item(def_id).is_none() { let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id); - self.not_const |= (bits & NOT_CONST_BITS) != 0; + self.not_const |= (bits >> QualifBits::NotConst as u32) & 1 != 0; } } } @@ -1500,12 +1498,12 @@ fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if mir.return_ty().references_error() { tcx.sess.delay_span_bug(mir.span, "mir_const_qualif: Mir had errors"); - return (qualif_bits((false, false, true)), Lrc::new(IdxSetBuf::new_empty(0))); + return (qualif_bits((false, false, true)) as u8, Lrc::new(IdxSetBuf::new_empty(0))); } let mut checker = ConstChecker::new(tcx, def_id, mir, Mode::Const); let (qualif, promoted_temps) = checker.qualify_const(); - (qualif_bits(qualif), promoted_temps) + (qualif_bits(qualif) as u8, promoted_temps) } pub struct QualifyAndPromoteConstants;