]> git.lizzy.rs Git - rust.git/commitdiff
thread location info through mir typeck (but do not use)
authorNiko Matsakis <niko@alum.mit.edu>
Sat, 4 Nov 2017 13:08:11 +0000 (09:08 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Wed, 15 Nov 2017 21:49:23 +0000 (16:49 -0500)
src/librustc/mir/mod.rs
src/librustc_mir/transform/type_check.rs

index 18c26500dbe18647e186aea2efbf59ece2de4917..bea273c84a92e20ab28b328119203a715880614b 100644 (file)
@@ -555,6 +555,15 @@ pub struct UpvarDecl {
 
 newtype_index!(BasicBlock { DEBUG_FORMAT = "bb{}" });
 
+impl BasicBlock {
+    pub fn start_location(self) -> Location {
+        Location {
+            block: self,
+            statement_index: 0,
+        }
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // BasicBlockData and Terminator
 
@@ -638,7 +647,32 @@ pub enum TerminatorKind<'tcx> {
         unwind: Option<BasicBlock>
     },
 
-    /// Drop the Lvalue and assign the new value over it
+    /// Drop the Lvalue and assign the new value over it. This ensures
+    /// that the assignment to LV occurs *even if* the destructor for
+    /// lvalue unwinds. Its semantics are best explained by by the
+    /// elaboration:
+    ///
+    /// ```
+    /// BB0 {
+    ///   DropAndReplace(LV <- RV, goto BB1, unwind BB2)
+    /// }
+    /// ```
+    ///
+    /// becomes
+    ///
+    /// ```
+    /// BB0 {
+    ///   Drop(LV, goto BB1, unwind BB2)
+    /// }
+    /// BB1 {
+    ///   // LV is now unitialized
+    ///   LV <- RV
+    /// }
+    /// BB2 {
+    ///   // LV is now unitialized -- its dtor panicked
+    ///   LV <- RV
+    /// }
+    /// ```
     DropAndReplace {
         location: Lvalue<'tcx>,
         value: Operand<'tcx>,
index dc462cd9c74fc775ce1408148525273b2424918b..0a9f673abe0608eb24208248a2ffa1b31f572925 100644 (file)
@@ -139,8 +139,8 @@ fn sanitize_lvalue(&mut self, lvalue: &Lvalue<'tcx>, location: Location) -> Lval
             Lvalue::Static(box Static { def_id, ty: sty }) => {
                 let sty = self.sanitize_type(lvalue, sty);
                 let ty = self.tcx().type_of(def_id);
-                let ty = self.cx.normalize(&ty);
-                if let Err(terr) = self.cx.eq_types(self.last_span, ty, sty) {
+                let ty = self.cx.normalize(&ty, location);
+                if let Err(terr) = self.cx.eq_types(self.last_span, ty, sty, location) {
                     span_mirbug!(
                         self, lvalue, "bad static type ({:?}: {:?}): {:?}",
                         ty, sty, terr);
@@ -165,7 +165,7 @@ fn sanitize_projection(&mut self,
                            base: LvalueTy<'tcx>,
                            pi: &LvalueElem<'tcx>,
                            lvalue: &Lvalue<'tcx>,
-                           _: Location)
+                           location: Location)
                            -> LvalueTy<'tcx> {
         debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, lvalue);
         let tcx = self.tcx();
@@ -254,9 +254,9 @@ fn sanitize_projection(&mut self,
                 },
             ProjectionElem::Field(field, fty) => {
                 let fty = self.sanitize_type(lvalue, fty);
-                match self.field_ty(lvalue, base, field) {
+                match self.field_ty(lvalue, base, field, location) {
                     Ok(ty) => {
-                        if let Err(terr) = self.cx.eq_types(span, ty, fty) {
+                        if let Err(terr) = self.cx.eq_types(span, ty, fty, location) {
                             span_mirbug!(
                                 self, lvalue, "bad field access ({:?}: {:?}): {:?}",
                                 ty, fty, terr);
@@ -281,7 +281,8 @@ fn error(&mut self) -> Ty<'tcx> {
     fn field_ty(&mut self,
                 parent: &fmt::Debug,
                 base_ty: LvalueTy<'tcx>,
-                field: Field)
+                field: Field,
+                location: Location)
                 -> Result<Ty<'tcx>, FieldAccessError>
     {
         let tcx = self.tcx();
@@ -329,7 +330,7 @@ fn field_ty(&mut self,
         };
 
         if let Some(field) = variant.fields.get(field.index()) {
-            Ok(self.cx.normalize(&field.ty(tcx, substs)))
+            Ok(self.cx.normalize(&field.ty(tcx, substs), location))
         } else {
             Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
         }
@@ -371,7 +372,7 @@ pub fn register_infer_ok_obligations<T>(&mut self, infer_ok: InferOk<'tcx, T>) -
         infer_ok.value
     }
 
-    fn sub_types(&mut self, sub: Ty<'tcx>, sup: Ty<'tcx>)
+    fn sub_types(&mut self, sub: Ty<'tcx>, sup: Ty<'tcx>, _at_location: Location)
                  -> infer::UnitResult<'tcx>
     {
         self.infcx.at(&self.misc(self.last_span), self.param_env)
@@ -379,7 +380,7 @@ fn sub_types(&mut self, sub: Ty<'tcx>, sup: Ty<'tcx>)
                   .map(|ok| self.register_infer_ok_obligations(ok))
     }
 
-    fn eq_types(&mut self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>)
+    fn eq_types(&mut self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>, _at_location: Location)
                 -> infer::UnitResult<'tcx>
     {
         self.infcx.at(&self.misc(span), self.param_env)
@@ -391,14 +392,17 @@ fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
         self.infcx.tcx
     }
 
-    fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>) {
+    fn check_stmt(&mut self,
+                  mir: &Mir<'tcx>,
+                  stmt: &Statement<'tcx>,
+                  location: Location) {
         debug!("check_stmt: {:?}", stmt);
         let tcx = self.tcx();
         match stmt.kind {
             StatementKind::Assign(ref lv, ref rv) => {
                 let lv_ty = lv.ty(mir, tcx).to_ty(tcx);
                 let rv_ty = rv.ty(mir, tcx);
-                if let Err(terr) = self.sub_types(rv_ty, lv_ty) {
+                if let Err(terr) = self.sub_types(rv_ty, lv_ty, location.successor_within_block()) {
                     span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}",
                                  lv_ty, rv_ty, terr);
                 }
@@ -432,7 +436,8 @@ fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>) {
 
     fn check_terminator(&mut self,
                         mir: &Mir<'tcx>,
-                        term: &Terminator<'tcx>) {
+                        term: &Terminator<'tcx>,
+                        location: Location) {
         debug!("check_terminator: {:?}", term);
         let tcx = self.tcx();
         match term.kind {
@@ -450,18 +455,30 @@ fn check_terminator(&mut self,
             TerminatorKind::DropAndReplace {
                 ref location,
                 ref value,
-                ..
+                target,
+                unwind,
             } => {
                 let lv_ty = location.ty(mir, tcx).to_ty(tcx);
                 let rv_ty = value.ty(mir, tcx);
-                if let Err(terr) = self.sub_types(rv_ty, lv_ty) {
+
+                if let Err(terr) = self.sub_types(rv_ty, lv_ty, target.start_location()) {
                     span_mirbug!(self, term, "bad DropAndReplace ({:?} = {:?}): {:?}",
                                  lv_ty, rv_ty, terr);
                 }
+
+                // Subtle: this assignment occurs at the start of
+                // *both* blocks, so we need to ensure that it holds
+                // at both locations.
+                if let Some(unwind) = unwind {
+                    if let Err(terr) = self.sub_types(rv_ty, lv_ty, unwind.start_location()) {
+                        span_mirbug!(self, term, "bad DropAndReplace ({:?} = {:?}): {:?}",
+                                     lv_ty, rv_ty, terr);
+                    }
+                }
             }
             TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => {
                 let discr_ty = discr.ty(mir, tcx);
-                if let Err(terr) = self.sub_types(discr_ty, switch_ty) {
+                if let Err(terr) = self.sub_types(discr_ty, switch_ty, location) {
                     span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}",
                                  switch_ty, discr_ty, terr);
                 }
@@ -483,13 +500,13 @@ fn check_terminator(&mut self,
                     }
                 };
                 let sig = tcx.erase_late_bound_regions(&sig);
-                let sig = self.normalize(&sig);
+                let sig = self.normalize(&sig, location);
                 self.check_call_dest(mir, term, &sig, destination);
 
                 if self.is_box_free(func) {
-                    self.check_box_free_inputs(mir, term, &sig, args);
+                    self.check_box_free_inputs(mir, term, &sig, args, location);
                 } else {
-                    self.check_call_inputs(mir, term, &sig, args);
+                    self.check_call_inputs(mir, term, &sig, args, location);
                 }
             }
             TerminatorKind::Assert { ref cond, ref msg, .. } => {
@@ -512,7 +529,7 @@ fn check_terminator(&mut self,
                 match mir.yield_ty {
                     None => span_mirbug!(self, term, "yield in non-generator"),
                     Some(ty) => {
-                        if let Err(terr) = self.sub_types(value_ty, ty) {
+                        if let Err(terr) = self.sub_types(value_ty, ty, location) {
                             span_mirbug!(self,
                                 term,
                                 "type of yield value is {:?}, but the yield type is {:?}: {:?}",
@@ -533,9 +550,11 @@ fn check_call_dest(&mut self,
                        destination: &Option<(Lvalue<'tcx>, BasicBlock)>) {
         let tcx = self.tcx();
         match *destination {
-            Some((ref dest, _)) => {
+            Some((ref dest, target_block)) => {
                 let dest_ty = dest.ty(mir, tcx).to_ty(tcx);
-                if let Err(terr) = self.sub_types(sig.output(), dest_ty) {
+                if let Err(terr) = self.sub_types(sig.output(),
+                                                  dest_ty,
+                                                  target_block.start_location()) {
                     span_mirbug!(self, term,
                                  "call dest mismatch ({:?} <- {:?}): {:?}",
                                  dest_ty, sig.output(), terr);
@@ -554,7 +573,8 @@ fn check_call_inputs(&mut self,
                          mir: &Mir<'tcx>,
                          term: &Terminator<'tcx>,
                          sig: &ty::FnSig<'tcx>,
-                         args: &[Operand<'tcx>])
+                         args: &[Operand<'tcx>],
+                         location: Location)
     {
         debug!("check_call_inputs({:?}, {:?})", sig, args);
         if args.len() < sig.inputs().len() ||
@@ -563,7 +583,7 @@ fn check_call_inputs(&mut self,
         }
         for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() {
             let op_arg_ty = op_arg.ty(mir, self.tcx());
-            if let Err(terr) = self.sub_types(op_arg_ty, fn_arg) {
+            if let Err(terr) = self.sub_types(op_arg_ty, fn_arg, location) {
                 span_mirbug!(self, term, "bad arg #{:?} ({:?} <- {:?}): {:?}",
                              n, fn_arg, op_arg_ty, terr);
             }
@@ -587,7 +607,8 @@ fn check_box_free_inputs(&mut self,
                              mir: &Mir<'tcx>,
                              term: &Terminator<'tcx>,
                              sig: &ty::FnSig<'tcx>,
-                             args: &[Operand<'tcx>])
+                             args: &[Operand<'tcx>],
+                             location: Location)
     {
         debug!("check_box_free_inputs");
 
@@ -621,69 +642,69 @@ fn check_box_free_inputs(&mut self,
             }
         };
 
-        if let Err(terr) = self.sub_types(arg_ty, pointee_ty) {
+        if let Err(terr) = self.sub_types(arg_ty, pointee_ty, location) {
             span_mirbug!(self, term, "bad box_free arg ({:?} <- {:?}): {:?}",
                          pointee_ty, arg_ty, terr);
         }
     }
 
-    fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block: &BasicBlockData<'tcx>)
+    fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block_data: &BasicBlockData<'tcx>)
     {
-        let is_cleanup = block.is_cleanup;
-        self.last_span = block.terminator().source_info.span;
-        match block.terminator().kind {
+        let is_cleanup = block_data.is_cleanup;
+        self.last_span = block_data.terminator().source_info.span;
+        match block_data.terminator().kind {
             TerminatorKind::Goto { target } =>
-                self.assert_iscleanup(mir, block, target, is_cleanup),
+                self.assert_iscleanup(mir, block_data, target, is_cleanup),
             TerminatorKind::SwitchInt { ref targets, .. } => {
                 for target in targets {
-                    self.assert_iscleanup(mir, block, *target, is_cleanup);
+                    self.assert_iscleanup(mir, block_data, *target, is_cleanup);
                 }
             }
             TerminatorKind::Resume => {
                 if !is_cleanup {
-                    span_mirbug!(self, block, "resume on non-cleanup block!")
+                    span_mirbug!(self, block_data, "resume on non-cleanup block!")
                 }
             }
             TerminatorKind::Return => {
                 if is_cleanup {
-                    span_mirbug!(self, block, "return on cleanup block")
+                    span_mirbug!(self, block_data, "return on cleanup block")
                 }
             }
             TerminatorKind::GeneratorDrop { .. } => {
                 if is_cleanup {
-                    span_mirbug!(self, block, "generator_drop in cleanup block")
+                    span_mirbug!(self, block_data, "generator_drop in cleanup block")
                 }
             }
             TerminatorKind::Yield { resume, drop, .. } => {
                 if is_cleanup {
-                    span_mirbug!(self, block, "yield in cleanup block")
+                    span_mirbug!(self, block_data, "yield in cleanup block")
                 }
-                self.assert_iscleanup(mir, block, resume, is_cleanup);
+                self.assert_iscleanup(mir, block_data, resume, is_cleanup);
                 if let Some(drop) = drop {
-                    self.assert_iscleanup(mir, block, drop, is_cleanup);
+                    self.assert_iscleanup(mir, block_data, drop, is_cleanup);
                 }
             }
             TerminatorKind::Unreachable => {}
             TerminatorKind::Drop { target, unwind, .. } |
             TerminatorKind::DropAndReplace { target, unwind, .. } |
             TerminatorKind::Assert { target, cleanup: unwind, .. } => {
-                self.assert_iscleanup(mir, block, target, is_cleanup);
+                self.assert_iscleanup(mir, block_data, target, is_cleanup);
                 if let Some(unwind) = unwind {
                     if is_cleanup {
-                        span_mirbug!(self, block, "unwind on cleanup block")
+                        span_mirbug!(self, block_data, "unwind on cleanup block")
                     }
-                    self.assert_iscleanup(mir, block, unwind, true);
+                    self.assert_iscleanup(mir, block_data, unwind, true);
                 }
             }
             TerminatorKind::Call { ref destination, cleanup, .. } => {
                 if let &Some((_, target)) = destination {
-                    self.assert_iscleanup(mir, block, target, is_cleanup);
+                    self.assert_iscleanup(mir, block_data, target, is_cleanup);
                 }
                 if let Some(cleanup) = cleanup {
                     if is_cleanup {
-                        span_mirbug!(self, block, "cleanup on cleanup block")
+                        span_mirbug!(self, block_data, "cleanup on cleanup block")
                     }
-                    self.assert_iscleanup(mir, block, cleanup, true);
+                    self.assert_iscleanup(mir, block_data, cleanup, true);
                 }
             }
             TerminatorKind::FalseEdges { real_target, ref imaginary_targets } => {
@@ -744,21 +765,23 @@ fn typeck_mir(&mut self, mir: &Mir<'gcx>) {
             self.check_local(mir, local, local_decl);
         }
 
-        for block in mir.basic_blocks() {
-            for stmt in &block.statements {
+        for (block, block_data) in mir.basic_blocks().iter_enumerated() {
+            let mut location = Location { block, statement_index: 0 };
+            for stmt in &block_data.statements {
                 if stmt.source_info.span != DUMMY_SP {
                     self.last_span = stmt.source_info.span;
                 }
-                self.check_stmt(mir, stmt);
+                self.check_stmt(mir, stmt, location);
+                location.statement_index += 1;
             }
 
-            self.check_terminator(mir, block.terminator());
-            self.check_iscleanup(mir, block);
+            self.check_terminator(mir, block_data.terminator(), location);
+            self.check_iscleanup(mir, block_data);
         }
     }
 
 
-    fn normalize<T>(&mut self, value: &T) -> T
+    fn normalize<T>(&mut self, value: &T, _location: Location) -> T
         where T: fmt::Debug + TypeFoldable<'tcx>
     {
         let mut selcx = traits::SelectionContext::new(self.infcx);