]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_mir/interpret/validity.rs
Don't duplicate body of try_validation.
[rust.git] / src / librustc_mir / interpret / validity.rs
index 2dffd78d776bd4310edfd38df75aee038fe068a4..42cdb1dc2a651a3318d40464312833b90aca06f7 100644 (file)
 };
 
 macro_rules! throw_validation_failure {
-    ($what:expr, $where:expr, $details:expr) => {{
-        let mut msg = format!("encountered {}", $what);
-        let where_ = &$where;
-        if !where_.is_empty() {
-            msg.push_str(" at ");
-            write_path(&mut msg, where_);
-        }
-        write!(&mut msg, ", but expected {}", $details).unwrap();
-        throw_ub!(ValidationFailure(msg))
-    }};
-    ($what:expr, $where:expr) => {{
+    ($what:expr, $where:expr $(, $details:expr )?) => {{
         let mut msg = format!("encountered {}", $what);
         let where_ = &$where;
         if !where_.is_empty() {
             msg.push_str(" at ");
             write_path(&mut msg, where_);
         }
+        $( write!(&mut msg, ", but expected {}", $details).unwrap(); )?
         throw_ub!(ValidationFailure(msg))
     }};
 }
 
+/// Returns a validation failure for any Err value of $e.
 macro_rules! try_validation {
-    ($e:expr, $what:expr, $where:expr, $details:expr) => {{
-        match $e {
-            Ok(x) => x,
-            // We re-throw the error, so we are okay with allocation:
-            // this can only slow down builds that fail anyway.
-            Err(_) => throw_validation_failure!($what, $where, $details),
-        }
+    ($e:expr, $what:expr, $where:expr $(, $details:expr )?) => {{
+        try_validation_pat!($e, _, $what, $where $(, $details )?)
     }};
-
-    ($e:expr, $what:expr, $where:expr) => {{
+}
+/// Like try_validation, but will throw a validation error if any of the patterns in $p are
+/// matched. Other errors are passed back to the caller, unchanged. This lets you use the patterns
+/// as a kind of validation blacklist:
+///
+/// ```rust
+/// let v = try_validation_pat(some_fn(), Foo | Bar | Baz, "some failure", "some place");
+/// // Failures that match $p are thrown up as validation errors, but other errors are passed back
+/// // unchanged.
+/// ```
+macro_rules! try_validation_pat {
+    ($e:expr, $( $p:pat )|*, $what:expr, $where:expr $(, $details:expr )?) => {{
         match $e {
             Ok(x) => x,
-            // We re-throw the error, so we are okay with allocation:
-            // this can only slow down builds that fail anyway.
-            Err(_) => throw_validation_failure!($what, $where),
+            // We catch the error and turn it into a validation failure. We are okay with
+            // allocation here as this can only slow down builds that fail anyway.
+            $( Err($p) )|* if true => throw_validation_failure!($what, $where $(, $details)?),
+            Err(e) =>  Err::<!, _>(e)?,
         }
     }};
 }
@@ -178,7 +176,7 @@ struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
     ecx: &'rt InterpCx<'mir, 'tcx, M>,
 }
 
-impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M> {
+impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M> {
     fn aggregate_field_path_elem(&mut self, layout: TyAndLayout<'tcx>, field: usize) -> PathElem {
         // First, check if we are projecting to a variant.
         match layout.variants {
@@ -199,9 +197,9 @@ fn aggregate_field_path_elem(&mut self, layout: TyAndLayout<'tcx>, field: usize)
             // generators and closures.
             ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
                 let mut name = None;
-                if def_id.is_local() {
+                if let Some(def_id) = def_id.as_local() {
                     let tables = self.ecx.tcx.typeck_tables_of(def_id);
-                    if let Some(upvars) = tables.upvar_list.get(&def_id) {
+                    if let Some(upvars) = tables.upvar_list.get(&def_id.to_def_id()) {
                         // Sometimes the index is beyond the number of upvars (seen
                         // for a generator).
                         if let Some((&var_hir_id, _)) = upvars.get_index(field) {
@@ -404,19 +402,27 @@ fn check_safe_pointer(
                 // Skip validation entirely for some external statics
                 let alloc_kind = self.ecx.tcx.alloc_map.lock().get(ptr.alloc_id);
                 if let Some(GlobalAlloc::Static(did)) = alloc_kind {
-                    // `extern static` cannot be validated as they have no body.
-                    // FIXME: Statics from other crates are also skipped.
-                    // They might be checked at a different type, but for now we
-                    // want to avoid recursing too deeply.  This is not sound!
-                    if !did.is_local() || self.ecx.tcx.is_foreign_item(did) {
-                        return Ok(());
-                    }
+                    // See const_eval::machine::MemoryExtra::can_access_statics for why
+                    // this check is so important.
+                    // This check is reachable when the const just referenced the static,
+                    // but never read it (so we never entered `before_access_global`).
+                    // We also need to do it here instead of going on to avoid running
+                    // into the `before_access_global` check during validation.
                     if !self.may_ref_to_static && self.ecx.tcx.is_static(did) {
                         throw_validation_failure!(
                             format_args!("a {} pointing to a static variable", kind),
                             self.path
                         );
                     }
+                    // `extern static` cannot be validated as they have no body.
+                    // FIXME: Statics from other crates are also skipped.
+                    // They might be checked at a different type, but for now we
+                    // want to avoid recursing too deeply.  We might miss const-invalid data,
+                    // but things are still sound otherwise (in particular re: consts
+                    // referring to statics).
+                    if !did.is_local() || self.ecx.tcx.is_foreign_item(did) {
+                        return Ok(());
+                    }
                 }
             }
             // Proceed recursively even for ZST, no reason to skip them!
@@ -484,8 +490,9 @@ fn try_visit_primitive(
                 // We are conservative with undef for integers, but try to
                 // actually enforce the strict rules for raw pointers (mostly because
                 // that lets us re-use `ref_to_mplace`).
-                let place = try_validation!(
+                let place = try_validation_pat!(
                     self.ecx.ref_to_mplace(self.ecx.read_immediate(value)?),
+                    _,
                     "uninitialized raw pointer",
                     self.path
                 );
@@ -610,7 +617,7 @@ fn visit_scalar(
     }
 }
 
-impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
+impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
     for ValidityVisitor<'rt, 'mir, 'tcx, M>
 {
     type V = OpTy<'tcx, M::PointerTag>;
@@ -813,7 +820,7 @@ fn visit_aggregate(
     }
 }
 
-impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
+impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     fn validate_operand_internal(
         &self,
         op: OpTy<'tcx, M::PointerTag>,