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/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/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 986957d5a8267..b50f81686c564 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,54 +36,22 @@ use syntax::ast::LitKind; use syntax::feature_gate::{UnstableFeatures, feature_err, emit_feature_err, GateIssue}; use syntax_pos::{Span, DUMMY_SP}; -use std::fmt; -use rustc_data_structures::sync::Lrc; use std::usize; +use std::fmt; 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, with - // the exception of `STATIC_REF` (in statics only). - struct Qualif: u8 { - // Constant containing interior mutability (UnsafeCell). - const MUTABLE_INTERIOR = 1 << 0; - - // 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; - - // Refers to temporaries which cannot be promoted as - // promote_consts decided they weren't simple enough. - const NOT_PROMOTABLE = 1 << 4; - - // 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; - } +pub enum QualifBits { + MutInterior, + NeedsDrop, + NotConst, } -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>) { - if ty.is_freeze(tcx, param_env, DUMMY_SP) { - *self = *self - Qualif::MUTABLE_INTERIOR; - } - if !ty.needs_drop(tcx, param_env) { - *self = *self - Qualif::NEEDS_DROP; - } - } +fn qualif_bits((mut_interior, needs_drop, not_const): (bool, bool, bool)) -> u8 { + (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. @@ -106,7 +75,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, @@ -114,33 +101,125 @@ 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>, - qualif: Qualif, + local_not_const: IdxSetBuf, + 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_qualif = IndexVec::from_elem(None, &mir.local_decls); + let mut local_not_const = IdxSetBuf::new_filled(mir.local_decls.len()); 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_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,19 +227,28 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { rpo, tcx, param_env, - local_qualif, - qualif: Qualif::empty(), + local_not_const, + 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.add(Qualif::NOT_CONST); + self.not_const = true; if self.mode != Mode::Fn { let mut err = struct_span_err!( self.tcx.sess, @@ -181,7 +269,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.not_const = true; if self.mode != Mode::Fn { let mut err = feature_err( &self.tcx.sess.parse_sess, @@ -199,46 +287,33 @@ 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; - } - - /// 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); - self.qualif.restrict(ty, 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::empty(); + 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.add(original); + self.mut_interior_checker.mut_interior |= original_mut_interior; + self.drop_checker.needs_drop |= original_needs_drop; + 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 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); - }; // Only handle promotable temps in non-const functions. if self.mode == Mode::Fn { 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); + self.local_not_const.set_member(&index, self.not_const); } } return; @@ -249,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); - self.local_qualif[index] = Some(self.qualif); + 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(&mut self.local_qualif[index]) + self.local_not_const.set_member(&index, self.not_const); } Place::Projection(box Projection { @@ -262,9 +337,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.contains(&index) => { // Part of `box expr`, we should've errored // already for the Box allocation Rvalue. } @@ -279,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; @@ -319,7 +392,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.mut_interior_checker.local_mut_interior.contains(&temp) && + !self.drop_checker.local_needs_drop.contains(&temp) && + !self.local_not_const.contains(&temp) { continue; } @@ -342,7 +417,9 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { } // Make sure there are no extra unassigned variables. - self.qualif = Qualif::NOT_CONST; + 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); @@ -370,16 +447,12 @@ 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); - } - + 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()); @@ -398,55 +471,408 @@ 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 | + 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 >> QualifBits::MutInterior as u32) & 1 != 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 | + 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 >> QualifBits::NeedsDrop as u32) & 1 != 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.add(Qualif::NOT_CONST); + self.not_const = true; } 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); - } - - if let Some(qualif) = self.local_qualif[local] { - self.add(qualif); - } else { - self.not_const(); - } + self.not_const |= self.local_not_const.contains(&local) || + !self.temp_promotion_state[local].is_promotable(); + } + LocalKind::ReturnPointer => { + self.not_const(); } } } fn visit_place(&mut self, - place: &Place<'tcx>, - context: PlaceContext<'tcx>, - location: Location) { + 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) => { @@ -459,7 +885,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.not_const = true; return; } @@ -467,7 +893,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.not_const = true; if self.mode != Mode::Fn { let mut err = struct_span_err!(self.tcx.sess, self.span, E0013, @@ -490,7 +916,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.not_const = true; let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx); if let ty::TyRawPtr(_) = base_ty.sty { @@ -528,9 +954,6 @@ 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); } ProjectionElem::ConstantIndex {..} | @@ -547,33 +970,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_qualif[local] = self.local_qualif[local].map(|q| - q - Qualif::NEEDS_DROP - ); - } - } + 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).expect("invalid mir_const_qualif"); - 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.not_const |= (bits >> QualifBits::NotConst as u32) & 1 != 0; } } } @@ -581,6 +991,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; @@ -646,7 +1059,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } if forbidden_mut { - self.add(Qualif::NOT_CONST); + 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 \ @@ -670,11 +1083,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.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 = self.qualif - Qualif::MUTABLE_INTERIOR; + 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 \ @@ -687,7 +1100,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } if forbidden_mut { - self.add(Qualif::NOT_CONST); + self.not_const = true; } else { // We might have a candidate for promotion. let candidate = Candidate::Ref(location); @@ -701,15 +1114,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.drop_checker.local_needs_drop.contains(&local) && + !self.local_not_const.contains(&local) { + self.promotion_candidates.push(candidate); } } } @@ -723,7 +1135,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.not_const = true; if self.mode != Mode::Fn { let mut err = struct_span_err!( self.tcx.sess, @@ -774,7 +1186,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.not_const = true; if self.mode != Mode::Fn { struct_span_err!( self.tcx.sess, self.span, E0395, @@ -789,7 +1201,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.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); @@ -806,19 +1218,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.add(Qualif::NEEDS_DROP); - } - - 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)); - } - } - } + Rvalue::Aggregate(..) => {} } } @@ -826,6 +1226,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); @@ -875,7 +1278,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.is_qualif_empty() { this.promotion_candidates.push(candidate); } else { span_err!(this.tcx.sess, this.span, E0526, @@ -891,7 +1294,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.is_qualif_empty() { this.promotion_candidates.push(candidate); } else { this.tcx.sess.span_err(this.span, @@ -922,7 +1325,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.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 @@ -938,7 +1343,7 @@ This does not pose a problem by itself because they can't be accessed directly." } } } else { - self.qualif = Qualif::NOT_CONST; + 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 = @@ -965,16 +1370,6 @@ 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) { - 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); - } self.assign(dest, location); } } else if let TerminatorKind::Drop { location: ref place, .. } = *kind { @@ -985,7 +1380,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.drop_checker.local_needs_drop.contains(&local) { Some(self.mir.local_decls[local].source_info.span) } else { None @@ -1013,10 +1408,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. @@ -1035,29 +1433,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; - } } } @@ -1065,10 +1440,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 { @@ -1092,6 +1473,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)); } } @@ -1114,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::NOT_CONST.bits(), Lrc::new(IdxSetBuf::new_empty(0))); + return (qualif_bits((false, false, true)) as u8, 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) as u8, promoted_temps) } pub struct QualifyAndPromoteConstants; @@ -1159,20 +1543,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. @@ -1182,7 +1566,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` 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", 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..0fa5840a8d006 100644 --- a/src/test/compile-fail/const-block-non-item-statement.rs +++ b/src/test/compile-fail/const-block-non-item-statement.rs @@ -12,8 +12,6 @@ enum Foo { Bar = { let x = 1; 3 } //~^ ERROR let bindings in constants are unstable //~| ERROR statements in constants are unstable - //~| ERROR let bindings in constants are unstable - //~| ERROR statements in constants are unstable } pub fn main() {} diff --git a/src/test/compile-fail/const-fn-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..fddb92ce75ca6 --- /dev/null +++ b/src/test/run-pass/static-by-reference.rs @@ -0,0 +1,18 @@ +// 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..81155dd1cf9cc 100644 --- a/src/test/ui/issue-17718-const-bad-values.rs +++ b/src/test/ui/issue-17718-const-bad-values.rs @@ -13,8 +13,8 @@ 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 +//~| ERROR: constants cannot refer to statics, use a constant instead fn main() {}