about summary refs log tree commit diff
diff options
context:
space:
mode:
authorkennytm <kennytm@gmail.com>2018-11-24 01:31:52 +0800
committerGitHub <noreply@github.com>2018-11-24 01:31:52 +0800
commit419a101d9c8a323ccc984141b3bd5c4cbe5a5673 (patch)
tree4bb2b62b49e7a438fd269d576042e6d43f680a13
parent12f6a42f610f0d4cd72886848d91f00e98d40590 (diff)
parentbcf82efe081dd12e4b6708241f5a1d165d103cae (diff)
downloadrust-419a101d9c8a323ccc984141b3bd5c4cbe5a5673.tar.gz
rust-419a101d9c8a323ccc984141b3bd5c4cbe5a5673.zip
Rollup merge of #56022 - RalfJung:validate-before-jump, r=oli-obk
When popping in CTFE, perform validation before jumping to next statement to have a better span for the error

Currently, when validating the return value fails, the span points at the next statement after the call. That does not make much sense.

r? @oli-obk
-rw-r--r--src/librustc_mir/interpret/eval_context.rs21
1 files changed, 14 insertions, 7 deletions
diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs
index 936b476df39..2eb5f7c853f 100644
--- a/src/librustc_mir/interpret/eval_context.rs
+++ b/src/librustc_mir/interpret/eval_context.rs
@@ -504,15 +504,14 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
         let frame = self.stack.pop().expect(
             "tried to pop a stack frame, but there were none",
         );
+        // Abort early if we do not want to clean up: We also avoid validation in that case,
+        // because this is CTFE and the final value will be thoroughly validated anyway.
         match frame.return_to_block {
-            StackPopCleanup::Goto(block) => {
-                self.goto_block(block)?;
-            }
+            StackPopCleanup::Goto(_) => {},
             StackPopCleanup::None { cleanup } => {
                 if !cleanup {
-                    // Leak the locals. Also skip validation, this is only used by
-                    // static/const computation which does its own (stronger) final
-                    // validation.
+                    assert!(self.stack.is_empty(), "only the topmost frame should ever be leaked");
+                    // Leak the locals, skip validation.
                     return Ok(());
                 }
             }
@@ -521,7 +520,8 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
         for local in frame.locals {
             self.deallocate_local(local)?;
         }
-        // Validate the return value.
+        // Validate the return value. Do this after deallocating so that we catch dangling
+        // references.
         if let Some(return_place) = frame.return_place {
             if M::enforce_validity(self) {
                 // Data got changed, better make sure it matches the type!
@@ -542,6 +542,13 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
             // Uh, that shouldn't happen... the function did not intend to return
             return err!(Unreachable);
         }
+        // Jump to new block -- *after* validation so that the spans make more sense.
+        match frame.return_to_block {
+            StackPopCleanup::Goto(block) => {
+                self.goto_block(block)?;
+            }
+            StackPopCleanup::None { .. } => {}
+        }
 
         if self.stack.len() > 1 { // FIXME should be "> 0", printing topmost frame crashes rustc...
             debug!("CONTINUING({}) {}", self.cur_frame(), self.frame().instance);