diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 2f19f591830f9..b48fc3a83b037 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -160,8 +160,9 @@ macro_rules! make_mir_visitor { fn visit_projection(&mut self, place: & $($mutability)? Projection<'tcx>, + context: PlaceContext, location: Location) { - self.super_projection(place, location); + self.super_projection(place, context, location); } fn visit_constant(&mut self, @@ -686,8 +687,7 @@ macro_rules! make_mir_visitor { PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) }; - self.visit_place(& $($mutability)? proj.base, context, location); - self.visit_projection(proj, location); + self.visit_projection(proj, context, location); } } } @@ -708,7 +708,12 @@ macro_rules! make_mir_visitor { fn super_projection(&mut self, proj: & $($mutability)? Projection<'tcx>, + context: PlaceContext, location: Location) { + // this is calling `super_place` in preparation for changing `Place` to be + // a struct with a base and a slice of projections. `visit_place` should only ever + // be called for the outermost place now. + self.super_place(& $($mutability)? proj.base, context, location); match & $($mutability)? proj.elem { ProjectionElem::Deref => { } diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index f36ede4e8d9cd..35640e9a45e59 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -103,11 +103,11 @@ impl<'tcx> MutVisitor<'tcx> for DerefArgVisitor { place: &mut Place<'tcx>, context: PlaceContext, location: Location) { - if *place == Place::Base(PlaceBase::Local(self_arg())) { - *place = Place::Projection(Box::new(Projection { - base: place.clone(), + if place.base_local() == Some(self_arg()) { + replace_base(place, Place::Projection(Box::new(Projection { + base: Place::Base(PlaceBase::Local(self_arg())), elem: ProjectionElem::Deref, - })); + }))); } else { self.super_place(place, context, location); } @@ -130,17 +130,25 @@ impl<'tcx> MutVisitor<'tcx> for PinArgVisitor<'tcx> { place: &mut Place<'tcx>, context: PlaceContext, location: Location) { - if *place == Place::Base(PlaceBase::Local(self_arg())) { - *place = Place::Projection(Box::new(Projection { - base: place.clone(), + if place.base_local() == Some(self_arg()) { + replace_base(place, Place::Projection(Box::new(Projection { + base: Place::Base(PlaceBase::Local(self_arg())), elem: ProjectionElem::Field(Field::new(0), self.ref_gen_ty), - })); + }))); } else { self.super_place(place, context, location); } } } +fn replace_base(place: &mut Place<'tcx>, new_base: Place<'tcx>) { + if let Place::Projection(proj) = place { + replace_base(&mut proj.base, new_base); + } else { + *place = new_base; + } +} + fn self_arg() -> Local { Local::new(1) } @@ -236,10 +244,10 @@ impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> { place: &mut Place<'tcx>, context: PlaceContext, location: Location) { - if let Place::Base(PlaceBase::Local(l)) = *place { + if let Some(l) = place.base_local() { // Replace an Local in the remap with a generator struct access if let Some(&(ty, variant_index, idx)) = self.remap.get(&l) { - *place = self.make_field(variant_index, idx, ty); + replace_base(place, self.make_field(variant_index, idx, ty)); } } else { self.super_place(place, context, location); diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 8696291e05875..64aecee633719 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -926,125 +926,135 @@ impl<'a, 'tcx> Checker<'a, 'tcx> { /// For functions (constant or not), it also records /// candidates for promotion in `promotion_candidates`. impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> { - fn visit_place(&mut self, - place: &Place<'tcx>, - context: PlaceContext, - location: Location) { - debug!("visit_place: place={:?} context={:?} location={:?}", place, context, location); - place.iterate(|place_base, place_projections| { - match place_base { - PlaceBase::Local(_) => {} - PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. }) => { - unreachable!() - } - PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. }) => { - if self.tcx - .get_attrs(*def_id) - .iter() - .any(|attr| attr.check_name(sym::thread_local)) { - if self.mode != Mode::Fn { - span_err!(self.tcx.sess, self.span, E0625, - "thread-local statics cannot be \ - accessed at compile-time"); - } - return; + fn visit_place_base( + &mut self, + place_base: &PlaceBase<'tcx>, + context: PlaceContext, + location: Location, + ) { + self.super_place_base(place_base, context, location); + match place_base { + PlaceBase::Local(_) => {} + PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. }) => { + unreachable!() + } + PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. }) => { + if self.tcx + .get_attrs(*def_id) + .iter() + .any(|attr| attr.check_name(sym::thread_local)) { + if self.mode != Mode::Fn { + span_err!(self.tcx.sess, self.span, E0625, + "thread-local statics cannot be \ + accessed at compile-time"); } + return; + } - // Only allow statics (not consts) to refer to other statics. - if self.mode == Mode::Static || self.mode == Mode::StaticMut { - if self.mode == Mode::Static && context.is_mutating_use() { - // this is not strictly necessary as miri will also bail out - // For interior mutability we can't really catch this statically as that - // goes through raw pointers and intermediate temporaries, so miri has - // to catch this anyway - self.tcx.sess.span_err( - self.span, - "cannot mutate statics in the initializer of another static", - ); - } - return; + // Only allow statics (not consts) to refer to other statics. + if self.mode == Mode::Static || self.mode == Mode::StaticMut { + if self.mode == Mode::Static && context.is_mutating_use() { + // this is not strictly necessary as miri will also bail out + // For interior mutability we can't really catch this statically as that + // goes through raw pointers and intermediate temporaries, so miri has + // to catch this anyway + self.tcx.sess.span_err( + self.span, + "cannot mutate statics in the initializer of another static", + ); } - unleash_miri!(self); + return; + } + unleash_miri!(self); - if self.mode != Mode::Fn { - let mut err = struct_span_err!(self.tcx.sess, self.span, E0013, - "{}s cannot refer to statics, use \ - a constant instead", self.mode); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "Static and const variables can refer to other const variables. \ - But a const variable cannot refer to a static variable." - ); - err.help( - "To fix this, the value can be extracted as a const and then used." - ); - } - err.emit() + if self.mode != Mode::Fn { + let mut err = struct_span_err!(self.tcx.sess, self.span, E0013, + "{}s cannot refer to statics, use \ + a constant instead", self.mode); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note( + "Static and const variables can refer to other const variables. \ + But a const variable cannot refer to a static variable." + ); + err.help( + "To fix this, the value can be extracted as a const and then used." + ); } + err.emit() } } + } + } - for proj in place_projections { - match proj.elem { - ProjectionElem::Deref => { - if context.is_mutating_use() { - // `not_const` errors out in const contexts - self.not_const() - } - let base_ty = proj.base.ty(self.mir, self.tcx).ty; - match self.mode { - Mode::Fn => {}, - _ => { - if let ty::RawPtr(_) = base_ty.sty { - if !self.tcx.features().const_raw_ptr_deref { - emit_feature_err( - &self.tcx.sess.parse_sess, sym::const_raw_ptr_deref, - self.span, GateIssue::Language, - &format!( - "dereferencing raw pointers in {}s is unstable", - self.mode, - ), - ); - } - } + fn visit_projection( + &mut self, + proj: &Projection<'tcx>, + context: PlaceContext, + location: Location, + ) { + debug!( + "visit_place_projection: proj={:?} context={:?} location={:?}", + proj, context, location, + ); + self.super_projection(proj, context, location); + match proj.elem { + ProjectionElem::Deref => { + if context.is_mutating_use() { + // `not_const` errors out in const contexts + self.not_const() + } + let base_ty = proj.base.ty(self.mir, self.tcx).ty; + match self.mode { + Mode::Fn => {}, + _ => { + if let ty::RawPtr(_) = base_ty.sty { + if !self.tcx.features().const_raw_ptr_deref { + emit_feature_err( + &self.tcx.sess.parse_sess, sym::const_raw_ptr_deref, + self.span, GateIssue::Language, + &format!( + "dereferencing raw pointers in {}s is unstable", + self.mode, + ), + ); } } } + } + } - ProjectionElem::ConstantIndex {..} | - ProjectionElem::Subslice {..} | - ProjectionElem::Field(..) | - ProjectionElem::Index(_) => { - let base_ty = proj.base.ty(self.mir, self.tcx).ty; - if let Some(def) = base_ty.ty_adt_def() { - if def.is_union() { - match self.mode { - Mode::ConstFn => { - if !self.tcx.features().const_fn_union { - emit_feature_err( - &self.tcx.sess.parse_sess, sym::const_fn_union, - self.span, GateIssue::Language, - "unions in const fn are unstable", - ); - } - }, - - | Mode::Fn - | Mode::Static - | Mode::StaticMut - | Mode::Const - => {}, + ProjectionElem::ConstantIndex {..} | + ProjectionElem::Subslice {..} | + ProjectionElem::Field(..) | + ProjectionElem::Index(_) => { + let base_ty = proj.base.ty(self.mir, self.tcx).ty; + if let Some(def) = base_ty.ty_adt_def() { + if def.is_union() { + match self.mode { + Mode::ConstFn => { + if !self.tcx.features().const_fn_union { + emit_feature_err( + &self.tcx.sess.parse_sess, sym::const_fn_union, + self.span, GateIssue::Language, + "unions in const fn are unstable", + ); } - } - } - } + }, - ProjectionElem::Downcast(..) => { - self.not_const() + | Mode::Fn + | Mode::Static + | Mode::StaticMut + | Mode::Const + => {}, + } } } } - }); + + ProjectionElem::Downcast(..) => { + self.not_const() + } + } } fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { @@ -1069,17 +1079,17 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> { // Check nested operands and places. if let Rvalue::Ref(_, kind, ref place) = *rvalue { // Special-case reborrows. - let mut is_reborrow = false; + let mut reborrow_place = None; if let Place::Projection(ref proj) = *place { if let ProjectionElem::Deref = proj.elem { let base_ty = proj.base.ty(self.mir, self.tcx).ty; if let ty::Ref(..) = base_ty.sty { - is_reborrow = true; + reborrow_place = Some(&proj.base); } } } - if is_reborrow { + if let Some(place) = reborrow_place { let ctx = match kind { BorrowKind::Shared => PlaceContext::NonMutatingUse( NonMutatingUseContext::SharedBorrow, @@ -1094,7 +1104,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> { MutatingUseContext::Borrow, ), }; - self.super_place(place, ctx, location); + self.visit_place(place, ctx, location); } else { self.super_rvalue(rvalue, location); }