about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-05-13 10:49:29 +0000
committerbors <bors@rust-lang.org>2021-05-13 10:49:29 +0000
commit17b60b8738735d8d64d03601d1dad4001d1e5733 (patch)
tree3e60cd21ed4bc546ed496bcebdaa12ddf3fcd930
parent703f2e1685a63c9718bcc3b09eb33a24334a7541 (diff)
parent985fb4caa076e6c62c45de51aa43a03e97d828f3 (diff)
downloadrust-17b60b8738735d8d64d03601d1dad4001d1e5733.tar.gz
rust-17b60b8738735d8d64d03601d1dad4001d1e5733.zip
Auto merge of #83129 - LeSeulArtichaut:thir-unsafeck, r=nikomatsakis
Introduce the beginning of a THIR unsafety checker

This poses the foundations for the THIR unsafety checker, so that it can be implemented incrementally:
- implements a rudimentary `Visitor` for the THIR (which will definitely need some tweaking in the future)
- introduces a new `-Zthir-unsafeck` flag which tells the compiler to use THIR unsafeck instead of MIR unsafeck
- implements detection of unsafe functions
- adds revisions to the UI tests to test THIR unsafeck alongside MIR unsafeck

This uses a very simple query design, where bodies are unsafety-checked on a body per body basis. This however has some big flaws:
- the unsafety-checker builds the THIR itself, which means a lot of work is duplicated with MIR building constructing its own copy of the THIR
- unsafety-checking closures is currently completely wrong: closures should take into account the "safety context" in which they are created, here we are considering that closures are always a safe context

I had intended to fix these problems in follow-up PRs since they are always gated under the `-Zthir-unsafeck` flag (which is explicitely noted to be unsound).

r? `@nikomatsakis`
cc https://github.com/rust-lang/project-thir-unsafeck/issues/3 https://github.com/rust-lang/project-thir-unsafeck/issues/7
-rw-r--r--compiler/rustc_interface/src/passes.rs6
-rw-r--r--compiler/rustc_interface/src/tests.rs1
-rw-r--r--compiler/rustc_middle/src/query/mod.rs13
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs334
-rw-r--r--compiler/rustc_mir_build/src/lib.rs3
-rw-r--r--compiler/rustc_mir_build/src/thir/mod.rs1
-rw-r--r--compiler/rustc_mir_build/src/thir/visit.rs186
-rw-r--r--compiler/rustc_session/src/options.rs2
-rw-r--r--src/test/ui/async-await/async-unsafe-fn-call-in-safe.mir.stderr (renamed from src/test/ui/async-await/async-unsafe-fn-call-in-safe.stderr)8
-rw-r--r--src/test/ui/async-await/async-unsafe-fn-call-in-safe.rs2
-rw-r--r--src/test/ui/async-await/async-unsafe-fn-call-in-safe.thir.stderr35
-rw-r--r--src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.mir.stderr (renamed from src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.stderr)4
-rw-r--r--src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs3
-rw-r--r--src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.thir.stderr19
-rw-r--r--src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.mir.stderr (renamed from src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.stderr)2
-rw-r--r--src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.rs3
-rw-r--r--src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr11
-rw-r--r--src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr (renamed from src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.stderr)4
-rw-r--r--src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.rs3
-rw-r--r--src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr19
-rw-r--r--src/test/ui/error-codes/E0133.mir.stderr (renamed from src/test/ui/error-codes/E0133.stderr)2
-rw-r--r--src/test/ui/error-codes/E0133.rs3
-rw-r--r--src/test/ui/error-codes/E0133.thir.stderr (renamed from src/test/ui/unsafe/unsafe-fn-called-from-safe.stderr)2
-rw-r--r--src/test/ui/feature-gates/feature-gate-const_fn_transmute.mir.stderr (renamed from src/test/ui/feature-gates/feature-gate-const_fn_transmute.stderr)24
-rw-r--r--src/test/ui/feature-gates/feature-gate-const_fn_transmute.rs3
-rw-r--r--src/test/ui/feature-gates/feature-gate-const_fn_transmute.thir.stderr118
-rw-r--r--src/test/ui/foreign-unsafe-fn-called.mir.stderr (renamed from src/test/ui/foreign-unsafe-fn-called.stderr)2
-rw-r--r--src/test/ui/foreign-unsafe-fn-called.rs3
-rw-r--r--src/test/ui/foreign-unsafe-fn-called.thir.stderr11
-rw-r--r--src/test/ui/intrinsics/unchecked_math_unsafe.mir.stderr (renamed from src/test/ui/intrinsics/unchecked_math_unsafe.stderr)6
-rw-r--r--src/test/ui/intrinsics/unchecked_math_unsafe.rs3
-rw-r--r--src/test/ui/intrinsics/unchecked_math_unsafe.thir.stderr27
-rw-r--r--src/test/ui/issues/issue-28776.mir.stderr (renamed from src/test/ui/issues/issue-28776.stderr)2
-rw-r--r--src/test/ui/issues/issue-28776.rs3
-rw-r--r--src/test/ui/issues/issue-28776.thir.stderr11
-rw-r--r--src/test/ui/issues/issue-3080.mir.stderr (renamed from src/test/ui/issues/issue-3080.stderr)2
-rw-r--r--src/test/ui/issues/issue-3080.rs3
-rw-r--r--src/test/ui/issues/issue-3080.thir.stderr11
-rw-r--r--src/test/ui/issues/issue-48131.mir.stderr (renamed from src/test/ui/issues/issue-48131.stderr)6
-rw-r--r--src/test/ui/issues/issue-48131.rs3
-rw-r--r--src/test/ui/issues/issue-48131.thir.stderr20
-rw-r--r--src/test/ui/issues/issue-5844.mir.stderr (renamed from src/test/ui/issues/issue-5844.stderr)2
-rw-r--r--src/test/ui/issues/issue-5844.rs2
-rw-r--r--src/test/ui/issues/issue-5844.thir.stderr11
-rw-r--r--src/test/ui/threads-sendsync/issue-43733.mir.stderr (renamed from src/test/ui/threads-sendsync/issue-43733.stderr)6
-rw-r--r--src/test/ui/threads-sendsync/issue-43733.rs19
-rw-r--r--src/test/ui/threads-sendsync/issue-43733.thir.stderr19
-rw-r--r--src/test/ui/unsafe/unsafe-around-compiler-generated-unsafe.mir.stderr (renamed from src/test/ui/unsafe/unsafe-around-compiler-generated-unsafe.stderr)4
-rw-r--r--src/test/ui/unsafe/unsafe-around-compiler-generated-unsafe.rs3
-rw-r--r--src/test/ui/unsafe/unsafe-around-compiler-generated-unsafe.thir.stderr14
-rw-r--r--src/test/ui/unsafe/unsafe-const-fn.mir.stderr (renamed from src/test/ui/unsafe/unsafe-const-fn.stderr)2
-rw-r--r--src/test/ui/unsafe/unsafe-const-fn.rs3
-rw-r--r--src/test/ui/unsafe/unsafe-const-fn.thir.stderr11
-rw-r--r--src/test/ui/unsafe/unsafe-fn-called-from-safe.mir.stderr11
-rw-r--r--src/test/ui/unsafe/unsafe-fn-called-from-safe.rs3
-rw-r--r--src/test/ui/unsafe/unsafe-fn-called-from-safe.thir.stderr11
-rw-r--r--src/test/ui/unsafe/unsafe-fn-used-as-value.mir.stderr (renamed from src/test/ui/unsafe/unsafe-fn-used-as-value.stderr)2
-rw-r--r--src/test/ui/unsafe/unsafe-fn-used-as-value.rs3
-rw-r--r--src/test/ui/unsafe/unsafe-fn-used-as-value.thir.stderr11
-rw-r--r--src/tools/tidy/src/ui_tests.rs4
60 files changed, 1013 insertions, 52 deletions
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 9005325f0b4..06bec91501f 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -876,7 +876,11 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> {
 
     sess.time("MIR_effect_checking", || {
         for def_id in tcx.body_owners() {
-            mir::transform::check_unsafety::check_unsafety(tcx, def_id);
+            if tcx.sess.opts.debugging_opts.thir_unsafeck {
+                tcx.ensure().thir_check_unsafety(def_id);
+            } else {
+                mir::transform::check_unsafety::check_unsafety(tcx, def_id);
+            }
 
             if tcx.hir().body_const_context(def_id).is_some() {
                 tcx.ensure()
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 17dc6fd1d52..bea7d0fb81f 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -736,6 +736,7 @@ fn test_debugging_options_tracking_hash() {
     tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0));
     tracked!(teach, true);
     tracked!(thinlto, Some(true));
+    tracked!(thir_unsafeck, true);
     tracked!(tune_cpu, Some(String::from("abc")));
     tracked!(tls_model, Some(TlsModel::GeneralDynamic));
     tracked!(trap_unreachable, Some(false));
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 3c5440f5b68..fea2aec38a1 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -611,6 +611,19 @@ rustc_queries! {
         }
     }
 
+    /// Unsafety-check this `LocalDefId` with THIR unsafeck. This should be
+    /// used with `-Zthir-unsafeck`.
+    query thir_check_unsafety(key: LocalDefId) {
+        desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key.to_def_id()) }
+        cache_on_disk_if { true }
+    }
+    query thir_check_unsafety_for_const_arg(key: (LocalDefId, DefId)) {
+        desc {
+            |tcx| "unsafety-checking the const argument `{}`",
+            tcx.def_path_str(key.0.to_def_id())
+        }
+    }
+
     /// HACK: when evaluated, this reports a "unsafe derive on repr(packed)" error.
     ///
     /// Unsafety checking is executed for each method separately, but we only want
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
new file mode 100644
index 00000000000..933362578f9
--- /dev/null
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -0,0 +1,334 @@
+use crate::thir::visit::{self, Visitor};
+use crate::thir::*;
+
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
+use rustc_session::lint::Level;
+use rustc_span::def_id::{DefId, LocalDefId};
+use rustc_span::Span;
+
+struct UnsafetyVisitor<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    /// The `HirId` of the current scope, which would be the `HirId`
+    /// of the current HIR node, modulo adjustments. Used for lint levels.
+    hir_context: hir::HirId,
+    /// The current "safety context". This notably tracks whether we are in an
+    /// `unsafe` block, and whether it has been used.
+    safety_context: SafetyContext,
+    body_unsafety: BodyUnsafety,
+}
+
+impl<'tcx> UnsafetyVisitor<'tcx> {
+    fn in_safety_context<R>(
+        &mut self,
+        safety_context: SafetyContext,
+        f: impl FnOnce(&mut Self) -> R,
+    ) {
+        if let (
+            SafetyContext::UnsafeBlock { span: enclosing_span, .. },
+            SafetyContext::UnsafeBlock { span: block_span, hir_id, .. },
+        ) = (self.safety_context, safety_context)
+        {
+            self.warn_unused_unsafe(
+                hir_id,
+                block_span,
+                Some(self.tcx.sess.source_map().guess_head_span(enclosing_span)),
+            );
+            f(self);
+        } else {
+            let prev_context = self.safety_context;
+            self.safety_context = safety_context;
+
+            f(self);
+
+            if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context {
+                self.warn_unused_unsafe(hir_id, span, self.body_unsafety.unsafe_fn_sig_span());
+            }
+            self.safety_context = prev_context;
+            return;
+        }
+    }
+
+    fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
+        let (description, note) = kind.description_and_note();
+        let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
+        match self.safety_context {
+            SafetyContext::UnsafeBlock { ref mut used, .. } => {
+                if !self.body_unsafety.is_unsafe() || !unsafe_op_in_unsafe_fn_allowed {
+                    // Mark this block as useful
+                    *used = true;
+                }
+            }
+            SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
+            SafetyContext::UnsafeFn => {
+                // unsafe_op_in_unsafe_fn is disallowed
+                if kind == BorrowOfPackedField {
+                    // FIXME handle borrows of packed fields
+                } else {
+                    struct_span_err!(
+                        self.tcx.sess,
+                        span,
+                        E0133,
+                        "{} is unsafe and requires unsafe block",
+                        description,
+                    )
+                    .span_label(span, description)
+                    .note(note)
+                    .emit();
+                }
+            }
+            SafetyContext::Safe => {
+                if kind == BorrowOfPackedField {
+                    // FIXME handle borrows of packed fields
+                } else {
+                    let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" };
+                    struct_span_err!(
+                        self.tcx.sess,
+                        span,
+                        E0133,
+                        "{} is unsafe and requires unsafe{} block",
+                        description,
+                        fn_sugg,
+                    )
+                    .span_label(span, description)
+                    .note(note)
+                    .emit();
+                }
+            }
+        }
+    }
+
+    fn warn_unused_unsafe(
+        &self,
+        hir_id: hir::HirId,
+        block_span: Span,
+        enclosing_span: Option<Span>,
+    ) {
+        let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
+        self.tcx.struct_span_lint_hir(UNUSED_UNSAFE, hir_id, block_span, |lint| {
+            let msg = "unnecessary `unsafe` block";
+            let mut db = lint.build(msg);
+            db.span_label(block_span, msg);
+            if let Some(enclosing_span) = enclosing_span {
+                db.span_label(
+                    enclosing_span,
+                    format!("because it's nested under this `unsafe` block"),
+                );
+            }
+            db.emit();
+        });
+    }
+
+    /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
+    fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
+        self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
+    }
+}
+
+impl<'thir, 'tcx> Visitor<'thir, 'tcx> for UnsafetyVisitor<'tcx> {
+    fn visit_block(&mut self, block: &Block<'thir, 'tcx>) {
+        if let BlockSafety::ExplicitUnsafe(hir_id) = block.safety_mode {
+            self.in_safety_context(
+                SafetyContext::UnsafeBlock { span: block.span, hir_id, used: false },
+                |this| visit::walk_block(this, block),
+            );
+        } else {
+            visit::walk_block(self, block);
+        }
+    }
+
+    fn visit_expr(&mut self, expr: &'thir Expr<'thir, 'tcx>) {
+        match expr.kind {
+            ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
+                let prev_id = self.hir_context;
+                self.hir_context = hir_id;
+                self.visit_expr(value);
+                self.hir_context = prev_id;
+                return;
+            }
+            ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
+                if fun.ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
+                    self.requires_unsafe(expr.span, CallToUnsafeFunction);
+                }
+            }
+            _ => {}
+        }
+
+        visit::walk_expr(self, expr);
+    }
+}
+
+#[derive(Clone, Copy)]
+enum SafetyContext {
+    Safe,
+    UnsafeFn,
+    UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool },
+}
+
+#[derive(Clone, Copy)]
+enum BodyUnsafety {
+    /// The body is not unsafe.
+    Safe,
+    /// The body is an unsafe function. The span points to
+    /// the signature of the function.
+    Unsafe(Span),
+}
+
+impl BodyUnsafety {
+    /// Returns whether the body is unsafe.
+    fn is_unsafe(&self) -> bool {
+        matches!(self, BodyUnsafety::Unsafe(_))
+    }
+
+    /// If the body is unsafe, returns the `Span` of its signature.
+    fn unsafe_fn_sig_span(self) -> Option<Span> {
+        match self {
+            BodyUnsafety::Unsafe(span) => Some(span),
+            BodyUnsafety::Safe => None,
+        }
+    }
+}
+
+#[derive(Clone, Copy, PartialEq)]
+enum UnsafeOpKind {
+    CallToUnsafeFunction,
+    #[allow(dead_code)] // FIXME
+    UseOfInlineAssembly,
+    #[allow(dead_code)] // FIXME
+    InitializingTypeWith,
+    #[allow(dead_code)] // FIXME
+    CastOfPointerToInt,
+    #[allow(dead_code)] // FIXME
+    BorrowOfPackedField,
+    #[allow(dead_code)] // FIXME
+    UseOfMutableStatic,
+    #[allow(dead_code)] // FIXME
+    UseOfExternStatic,
+    #[allow(dead_code)] // FIXME
+    DerefOfRawPointer,
+    #[allow(dead_code)] // FIXME
+    AssignToDroppingUnionField,
+    #[allow(dead_code)] // FIXME
+    AccessToUnionField,
+    #[allow(dead_code)] // FIXME
+    MutationOfLayoutConstrainedField,
+    #[allow(dead_code)] // FIXME
+    BorrowOfLayoutConstrainedField,
+    #[allow(dead_code)] // FIXME
+    CallToFunctionWith,
+}
+
+use UnsafeOpKind::*;
+
+impl UnsafeOpKind {
+    pub fn description_and_note(&self) -> (&'static str, &'static str) {
+        match self {
+            CallToUnsafeFunction => (
+                "call to unsafe function",
+                "consult the function's documentation for information on how to avoid undefined \
+                 behavior",
+            ),
+            UseOfInlineAssembly => (
+                "use of inline assembly",
+                "inline assembly is entirely unchecked and can cause undefined behavior",
+            ),
+            InitializingTypeWith => (
+                "initializing type with `rustc_layout_scalar_valid_range` attr",
+                "initializing a layout restricted type's field with a value outside the valid \
+                 range is undefined behavior",
+            ),
+            CastOfPointerToInt => {
+                ("cast of pointer to int", "casting pointers to integers in constants")
+            }
+            BorrowOfPackedField => (
+                "borrow of packed field",
+                "fields of packed structs might be misaligned: dereferencing a misaligned pointer \
+                 or even just creating a misaligned reference is undefined behavior",
+            ),
+            UseOfMutableStatic => (
+                "use of mutable static",
+                "mutable statics can be mutated by multiple threads: aliasing violations or data \
+                 races will cause undefined behavior",
+            ),
+            UseOfExternStatic => (
+                "use of extern static",
+                "extern statics are not controlled by the Rust type system: invalid data, \
+                 aliasing violations or data races will cause undefined behavior",
+            ),
+            DerefOfRawPointer => (
+                "dereference of raw pointer",
+                "raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules \
+                 and cause data races: all of these are undefined behavior",
+            ),
+            AssignToDroppingUnionField => (
+                "assignment to union field that might need dropping",
+                "the previous content of the field will be dropped, which causes undefined \
+                 behavior if the field was not properly initialized",
+            ),
+            AccessToUnionField => (
+                "access to union field",
+                "the field may not be properly initialized: using uninitialized data will cause \
+                 undefined behavior",
+            ),
+            MutationOfLayoutConstrainedField => (
+                "mutation of layout constrained field",
+                "mutating layout constrained fields cannot statically be checked for valid values",
+            ),
+            BorrowOfLayoutConstrainedField => (
+                "borrow of layout constrained field with interior mutability",
+                "references to fields of layout constrained fields lose the constraints. Coupled \
+                 with interior mutability, the field can be changed to invalid values",
+            ),
+            CallToFunctionWith => (
+                "call to function with `#[target_feature]`",
+                "can only be called if the required target features are available",
+            ),
+        }
+    }
+}
+
+// FIXME: checking unsafety for closures should be handled by their parent body,
+// as they inherit their "safety context" from their declaration site.
+pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, thir: &Expr<'_, 'tcx>, hir_id: hir::HirId) {
+    let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| {
+        if fn_sig.header.unsafety == hir::Unsafety::Unsafe {
+            BodyUnsafety::Unsafe(fn_sig.span)
+        } else {
+            BodyUnsafety::Safe
+        }
+    });
+    let safety_context =
+        if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe };
+    let mut visitor = UnsafetyVisitor { tcx, safety_context, hir_context: hir_id, body_unsafety };
+    visitor.visit_expr(thir);
+}
+
+crate fn thir_check_unsafety_inner<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def: ty::WithOptConstParam<LocalDefId>,
+) {
+    let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
+    let body_id = tcx.hir().body_owned_by(hir_id);
+    let body = tcx.hir().body(body_id);
+
+    let arena = Arena::default();
+    let thir = cx::build_thir(tcx, def, &arena, &body.value);
+    check_unsafety(tcx, thir, hir_id);
+}
+
+crate fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
+    if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
+        tcx.thir_check_unsafety_for_const_arg(def)
+    } else {
+        thir_check_unsafety_inner(tcx, ty::WithOptConstParam::unknown(def_id))
+    }
+}
+
+crate fn thir_check_unsafety_for_const_arg<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    (did, param_did): (LocalDefId, DefId),
+) {
+    thir_check_unsafety_inner(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })
+}
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index da9a0b08e86..d4e9a0a3169 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -19,6 +19,7 @@ extern crate tracing;
 extern crate rustc_middle;
 
 mod build;
+mod check_unsafety;
 mod lints;
 pub mod thir;
 
@@ -28,4 +29,6 @@ pub fn provide(providers: &mut Providers) {
     providers.check_match = thir::pattern::check_match;
     providers.lit_to_const = thir::constant::lit_to_const;
     providers.mir_built = build::mir_built;
+    providers.thir_check_unsafety = check_unsafety::thir_check_unsafety;
+    providers.thir_check_unsafety_for_const_arg = check_unsafety::thir_check_unsafety_for_const_arg;
 }
diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs
index f4596d523d0..9bcb000920c 100644
--- a/compiler/rustc_mir_build/src/thir/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/mod.rs
@@ -29,6 +29,7 @@ mod arena;
 pub use arena::Arena;
 
 mod util;
+pub mod visit;
 
 #[derive(Copy, Clone, Debug)]
 pub enum LintLevel {
diff --git a/compiler/rustc_mir_build/src/thir/visit.rs b/compiler/rustc_mir_build/src/thir/visit.rs
new file mode 100644
index 00000000000..9c5b07e2b2a
--- /dev/null
+++ b/compiler/rustc_mir_build/src/thir/visit.rs
@@ -0,0 +1,186 @@
+use crate::thir::*;
+
+pub trait Visitor<'thir, 'tcx>: Sized {
+    fn visit_expr(&mut self, expr: &'thir Expr<'thir, 'tcx>) {
+        walk_expr(self, expr);
+    }
+
+    fn visit_stmt(&mut self, stmt: &'thir Stmt<'thir, 'tcx>) {
+        walk_stmt(self, stmt);
+    }
+
+    fn visit_block(&mut self, block: &Block<'thir, 'tcx>) {
+        walk_block(self, block);
+    }
+
+    fn visit_arm(&mut self, arm: &'thir Arm<'thir, 'tcx>) {
+        walk_arm(self, arm);
+    }
+
+    fn visit_const(&mut self, _cnst: &'tcx Const<'tcx>) {}
+}
+
+pub fn walk_expr<'thir, 'tcx, V: Visitor<'thir, 'tcx>>(
+    visitor: &mut V,
+    expr: &'thir Expr<'thir, 'tcx>,
+) {
+    use ExprKind::*;
+    match expr.kind {
+        Scope { value, region_scope: _, lint_level: _ } => visitor.visit_expr(value),
+        Box { value } => visitor.visit_expr(value),
+        If { cond, then, else_opt } => {
+            visitor.visit_expr(cond);
+            visitor.visit_expr(then);
+            if let Some(else_expr) = else_opt {
+                visitor.visit_expr(else_expr);
+            }
+        }
+        Call { fun, args, ty: _, from_hir_call: _, fn_span: _ } => {
+            visitor.visit_expr(fun);
+            for arg in args {
+                visitor.visit_expr(arg);
+            }
+        }
+        Deref { arg } => visitor.visit_expr(arg),
+        Binary { lhs, rhs, op: _ } | LogicalOp { lhs, rhs, op: _ } => {
+            visitor.visit_expr(lhs);
+            visitor.visit_expr(rhs);
+        }
+        Unary { arg, op: _ } => visitor.visit_expr(arg),
+        Cast { source } => visitor.visit_expr(source),
+        Use { source } => visitor.visit_expr(source),
+        NeverToAny { source } => visitor.visit_expr(source),
+        Pointer { source, cast: _ } => visitor.visit_expr(source),
+        Loop { body } => visitor.visit_expr(body),
+        Match { scrutinee, arms } => {
+            visitor.visit_expr(scrutinee);
+            for arm in arms {
+                visitor.visit_arm(arm);
+            }
+        }
+        Block { ref body } => visitor.visit_block(body),
+        Assign { lhs, rhs } | AssignOp { lhs, rhs, op: _ } => {
+            visitor.visit_expr(lhs);
+            visitor.visit_expr(rhs);
+        }
+        Field { lhs, name: _ } => visitor.visit_expr(lhs),
+        Index { lhs, index } => {
+            visitor.visit_expr(lhs);
+            visitor.visit_expr(index);
+        }
+        VarRef { id: _ } | UpvarRef { closure_def_id: _, var_hir_id: _ } => {}
+        Borrow { arg, borrow_kind: _ } => visitor.visit_expr(arg),
+        AddressOf { arg, mutability: _ } => visitor.visit_expr(arg),
+        Break { value, label: _ } => {
+            if let Some(value) = value {
+                visitor.visit_expr(value)
+            }
+        }
+        Continue { label: _ } => {}
+        Return { value } => {
+            if let Some(value) = value {
+                visitor.visit_expr(value)
+            }
+        }
+        ConstBlock { value } => visitor.visit_const(value),
+        Repeat { value, count } => {
+            visitor.visit_expr(value);
+            visitor.visit_const(count);
+        }
+        Array { fields } | Tuple { fields } => {
+            for field in fields {
+                visitor.visit_expr(field);
+            }
+        }
+        Adt { fields, ref base, adt_def: _, variant_index: _, substs: _, user_ty: _ } => {
+            for field in fields {
+                visitor.visit_expr(field.expr);
+            }
+            if let Some(base) = base {
+                visitor.visit_expr(base.base);
+            }
+        }
+        PlaceTypeAscription { source, user_ty: _ } | ValueTypeAscription { source, user_ty: _ } => {
+            visitor.visit_expr(source)
+        }
+        Closure { closure_id: _, substs: _, upvars: _, movability: _, fake_reads: _ } => {}
+        Literal { literal, user_ty: _, const_id: _ } => visitor.visit_const(literal),
+        StaticRef { literal, def_id: _ } => visitor.visit_const(literal),
+        InlineAsm { operands, template: _, options: _, line_spans: _ } => {
+            for op in operands {
+                use InlineAsmOperand::*;
+                match op {
+                    In { expr, reg: _ }
+                    | Out { expr: Some(expr), reg: _, late: _ }
+                    | InOut { expr, reg: _, late: _ }
+                    | SymFn { expr } => visitor.visit_expr(expr),
+                    SplitInOut { in_expr, out_expr, reg: _, late: _ } => {
+                        visitor.visit_expr(in_expr);
+                        if let Some(out_expr) = out_expr {
+                            visitor.visit_expr(out_expr);
+                        }
+                    }
+                    Out { expr: None, reg: _, late: _ }
+                    | Const { value: _, span: _ }
+                    | SymStatic { def_id: _ } => {}
+                }
+            }
+        }
+        ThreadLocalRef(_) => {}
+        LlvmInlineAsm { outputs, inputs, asm: _ } => {
+            for out_expr in outputs {
+                visitor.visit_expr(out_expr);
+            }
+            for in_expr in inputs {
+                visitor.visit_expr(in_expr);
+            }
+        }
+        Yield { value } => visitor.visit_expr(value),
+    }
+}
+
+pub fn walk_stmt<'thir, 'tcx, V: Visitor<'thir, 'tcx>>(
+    visitor: &mut V,
+    stmt: &'thir Stmt<'thir, 'tcx>,
+) {
+    match stmt.kind {
+        StmtKind::Expr { expr, scope: _ } => visitor.visit_expr(expr),
+        StmtKind::Let {
+            initializer,
+            remainder_scope: _,
+            init_scope: _,
+            pattern: _,
+            lint_level: _,
+        } => {
+            if let Some(init) = initializer {
+                visitor.visit_expr(init);
+            }
+        }
+    }
+}
+
+pub fn walk_block<'thir, 'tcx, V: Visitor<'thir, 'tcx>>(
+    visitor: &mut V,
+    block: &Block<'thir, 'tcx>,
+) {
+    for stmt in block.stmts {
+        visitor.visit_stmt(stmt);
+    }
+    if let Some(expr) = block.expr {
+        visitor.visit_expr(expr);
+    }
+}
+
+pub fn walk_arm<'thir, 'tcx, V: Visitor<'thir, 'tcx>>(
+    visitor: &mut V,
+    arm: &'thir Arm<'thir, 'tcx>,
+) {
+    match arm.guard {
+        Some(Guard::If(expr)) => visitor.visit_expr(expr),
+        Some(Guard::IfLet(ref _pat, expr)) => {
+            visitor.visit_expr(expr);
+        }
+        None => {}
+    }
+    visitor.visit_expr(arm.body);
+}
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index d2145b2865b..c9f95ed1224 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1254,6 +1254,8 @@ options! {
         "select processor to schedule for (`rustc --print target-cpus` for details)"),
     thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "enable ThinLTO when possible"),
+    thir_unsafeck: bool = (false, parse_bool, [TRACKED],
+        "use the work-in-progress THIR unsafety checker. NOTE: this is unsound (default: no)"),
     /// We default to 1 here since we want to behave like
     /// a sequential compiler for now. This'll likely be adjusted
     /// in the future. Note that -Zthreads=0 is the way to get
diff --git a/src/test/ui/async-await/async-unsafe-fn-call-in-safe.stderr b/src/test/ui/async-await/async-unsafe-fn-call-in-safe.mir.stderr
index c95fe173488..d22413beecb 100644
--- a/src/test/ui/async-await/async-unsafe-fn-call-in-safe.stderr
+++ b/src/test/ui/async-await/async-unsafe-fn-call-in-safe.mir.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/async-unsafe-fn-call-in-safe.rs:12:5
+  --> $DIR/async-unsafe-fn-call-in-safe.rs:14:5
    |
 LL |     S::f();
    |     ^^^^^^ call to unsafe function
@@ -7,7 +7,7 @@ LL |     S::f();
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/async-unsafe-fn-call-in-safe.rs:13:5
+  --> $DIR/async-unsafe-fn-call-in-safe.rs:15:5
    |
 LL |     f();
    |     ^^^ call to unsafe function
@@ -15,7 +15,7 @@ LL |     f();
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/async-unsafe-fn-call-in-safe.rs:17:5
+  --> $DIR/async-unsafe-fn-call-in-safe.rs:19:5
    |
 LL |     S::f();
    |     ^^^^^^ call to unsafe function
@@ -23,7 +23,7 @@ LL |     S::f();
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/async-unsafe-fn-call-in-safe.rs:18:5
+  --> $DIR/async-unsafe-fn-call-in-safe.rs:20:5
    |
 LL |     f();
    |     ^^^ call to unsafe function
diff --git a/src/test/ui/async-await/async-unsafe-fn-call-in-safe.rs b/src/test/ui/async-await/async-unsafe-fn-call-in-safe.rs
index ccc1b8553f0..2ed343b4a07 100644
--- a/src/test/ui/async-await/async-unsafe-fn-call-in-safe.rs
+++ b/src/test/ui/async-await/async-unsafe-fn-call-in-safe.rs
@@ -1,4 +1,6 @@
 // edition:2018
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
 
 struct S;
 
diff --git a/src/test/ui/async-await/async-unsafe-fn-call-in-safe.thir.stderr b/src/test/ui/async-await/async-unsafe-fn-call-in-safe.thir.stderr
new file mode 100644
index 00000000000..d22413beecb
--- /dev/null
+++ b/src/test/ui/async-await/async-unsafe-fn-call-in-safe.thir.stderr
@@ -0,0 +1,35 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/async-unsafe-fn-call-in-safe.rs:14:5
+   |
+LL |     S::f();
+   |     ^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/async-unsafe-fn-call-in-safe.rs:15:5
+   |
+LL |     f();
+   |     ^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/async-unsafe-fn-call-in-safe.rs:19:5
+   |
+LL |     S::f();
+   |     ^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/async-unsafe-fn-call-in-safe.rs:20:5
+   |
+LL |     f();
+   |     ^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.stderr b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.mir.stderr
index 190b4792ebc..2f9c7973b5a 100644
--- a/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.stderr
+++ b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.mir.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:12:23
+  --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:15:23
    |
 LL |     let result: i32 = foo(5, 5);
    |                       ^^^^^^^^^ call to unsafe function
@@ -7,7 +7,7 @@ LL |     let result: i32 = foo(5, 5);
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:21:23
+  --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:24:23
    |
 LL |     let result: i32 = foo(5, 5);
    |                       ^^^^^^^^^ call to unsafe function
diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs
index 76a0f291410..bdb3eb23c38 100644
--- a/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs
+++ b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs
@@ -1,3 +1,6 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 // Ensure we get unsafe function after coercion
 unsafe fn add(a: i32, b: i32) -> i32 {
     a + b
diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.thir.stderr b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.thir.stderr
new file mode 100644
index 00000000000..2f9c7973b5a
--- /dev/null
+++ b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.thir.stderr
@@ -0,0 +1,19 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:15:23
+   |
+LL |     let result: i32 = foo(5, 5);
+   |                       ^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:24:23
+   |
+LL |     let result: i32 = foo(5, 5);
+   |                       ^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.stderr b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.mir.stderr
index a1fb1c02e46..a60100ddaea 100644
--- a/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.stderr
+++ b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.mir.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/coerce-unsafe-closure-to-unsafe-fn-ptr.rs:2:31
+  --> $DIR/coerce-unsafe-closure-to-unsafe-fn-ptr.rs:5:31
    |
 LL |     let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); };
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
diff --git a/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.rs b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.rs
index 36777693fab..57358fbdd84 100644
--- a/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.rs
+++ b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.rs
@@ -1,3 +1,6 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 fn main() {
     let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); };
     //~^ ERROR E0133
diff --git a/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr
new file mode 100644
index 00000000000..a60100ddaea
--- /dev/null
+++ b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr
@@ -0,0 +1,11 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/coerce-unsafe-closure-to-unsafe-fn-ptr.rs:5:31
+   |
+LL |     let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); };
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.stderr b/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr
index 5196b8ee0a2..b643ecc0ce8 100644
--- a/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.stderr
+++ b/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/const-extern-fn-requires-unsafe.rs:8:5
+  --> $DIR/const-extern-fn-requires-unsafe.rs:11:5
    |
 LL |     foo();
    |     ^^^^^ call to unsafe function
@@ -7,7 +7,7 @@ LL |     foo();
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/const-extern-fn-requires-unsafe.rs:6:17
+  --> $DIR/const-extern-fn-requires-unsafe.rs:9:17
    |
 LL |     let a: [u8; foo()];
    |                 ^^^^^ call to unsafe function
diff --git a/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.rs b/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.rs
index 71e6c2cb858..1ce78147970 100644
--- a/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.rs
+++ b/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.rs
@@ -1,3 +1,6 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 #![feature(const_extern_fn)]
 
 const unsafe extern "C" fn foo() -> usize { 5 }
diff --git a/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr b/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr
new file mode 100644
index 00000000000..b643ecc0ce8
--- /dev/null
+++ b/src/test/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr
@@ -0,0 +1,19 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/const-extern-fn-requires-unsafe.rs:11:5
+   |
+LL |     foo();
+   |     ^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/const-extern-fn-requires-unsafe.rs:9:17
+   |
+LL |     let a: [u8; foo()];
+   |                 ^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/error-codes/E0133.stderr b/src/test/ui/error-codes/E0133.mir.stderr
index 1eb696506f3..b11d5e2c2fc 100644
--- a/src/test/ui/error-codes/E0133.stderr
+++ b/src/test/ui/error-codes/E0133.mir.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/E0133.rs:4:5
+  --> $DIR/E0133.rs:7:5
    |
 LL |     f();
    |     ^^^ call to unsafe function
diff --git a/src/test/ui/error-codes/E0133.rs b/src/test/ui/error-codes/E0133.rs
index 52494ce6078..dee1475ba21 100644
--- a/src/test/ui/error-codes/E0133.rs
+++ b/src/test/ui/error-codes/E0133.rs
@@ -1,3 +1,6 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 unsafe fn f() { return; }
 
 fn main() {
diff --git a/src/test/ui/unsafe/unsafe-fn-called-from-safe.stderr b/src/test/ui/error-codes/E0133.thir.stderr
index 80d2c6ced24..b11d5e2c2fc 100644
--- a/src/test/ui/unsafe/unsafe-fn-called-from-safe.stderr
+++ b/src/test/ui/error-codes/E0133.thir.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/unsafe-fn-called-from-safe.rs:4:5
+  --> $DIR/E0133.rs:7:5
    |
 LL |     f();
    |     ^^^ call to unsafe function
diff --git a/src/test/ui/feature-gates/feature-gate-const_fn_transmute.stderr b/src/test/ui/feature-gates/feature-gate-const_fn_transmute.mir.stderr
index 08ba14dc40e..04efea0b230 100644
--- a/src/test/ui/feature-gates/feature-gate-const_fn_transmute.stderr
+++ b/src/test/ui/feature-gates/feature-gate-const_fn_transmute.mir.stderr
@@ -1,5 +1,5 @@
 error[E0658]: `transmute` is not allowed in constant functions
-  --> $DIR/feature-gate-const_fn_transmute.rs:8:43
+  --> $DIR/feature-gate-const_fn_transmute.rs:11:43
    |
 LL | const fn transmute_fn() -> u32 { unsafe { mem::transmute(Foo(3)) } }
    |                                           ^^^^^^^^^^^^^^^^^^^^^^
@@ -9,7 +9,7 @@ LL | const fn transmute_fn() -> u32 { unsafe { mem::transmute(Foo(3)) } }
    = note: `transmute` is only allowed in constants and statics for now
 
 error[E0658]: `transmute` is not allowed in constant functions
-  --> $DIR/feature-gate-const_fn_transmute.rs:11:53
+  --> $DIR/feature-gate-const_fn_transmute.rs:14:53
    |
 LL | const fn transmute_fn_intrinsic() -> u32 { unsafe { std::intrinsics::transmute(Foo(3)) } }
    |                                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -19,7 +19,7 @@ LL | const fn transmute_fn_intrinsic() -> u32 { unsafe { std::intrinsics::transm
    = note: `transmute` is only allowed in constants and statics for now
 
 error[E0658]: `transmute` is not allowed in constant functions
-  --> $DIR/feature-gate-const_fn_transmute.rs:14:58
+  --> $DIR/feature-gate-const_fn_transmute.rs:17:58
    |
 LL | const fn transmute_fn_core_intrinsic() -> u32 { unsafe { core::intrinsics::transmute(Foo(3)) } }
    |                                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -29,7 +29,7 @@ LL | const fn transmute_fn_core_intrinsic() -> u32 { unsafe { core::intrinsics::
    = note: `transmute` is only allowed in constants and statics for now
 
 error[E0658]: `transmute` is not allowed in constant functions
-  --> $DIR/feature-gate-const_fn_transmute.rs:17:48
+  --> $DIR/feature-gate-const_fn_transmute.rs:20:48
    |
 LL | const unsafe fn unsafe_transmute_fn() -> u32 { mem::transmute(Foo(3)) }
    |                                                ^^^^^^^^^^^^^^^^^^^^^^
@@ -39,7 +39,7 @@ LL | const unsafe fn unsafe_transmute_fn() -> u32 { mem::transmute(Foo(3)) }
    = note: `transmute` is only allowed in constants and statics for now
 
 error[E0658]: `transmute` is not allowed in constant functions
-  --> $DIR/feature-gate-const_fn_transmute.rs:20:58
+  --> $DIR/feature-gate-const_fn_transmute.rs:23:58
    |
 LL | const unsafe fn unsafe_transmute_fn_intrinsic() -> u32 { std::intrinsics::transmute(Foo(3)) }
    |                                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -49,7 +49,7 @@ LL | const unsafe fn unsafe_transmute_fn_intrinsic() -> u32 { std::intrinsics::t
    = note: `transmute` is only allowed in constants and statics for now
 
 error[E0658]: `transmute` is not allowed in constant functions
-  --> $DIR/feature-gate-const_fn_transmute.rs:23:63
+  --> $DIR/feature-gate-const_fn_transmute.rs:26:63
    |
 LL | const unsafe fn unsafe_transmute_fn_core_intrinsic() -> u32 { core::intrinsics::transmute(Foo(3)) }
    |                                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -59,7 +59,7 @@ LL | const unsafe fn unsafe_transmute_fn_core_intrinsic() -> u32 { core::intrins
    = note: `transmute` is only allowed in constants and statics for now
 
 error[E0658]: `transmute` is not allowed in constant functions
-  --> $DIR/feature-gate-const_fn_transmute.rs:26:39
+  --> $DIR/feature-gate-const_fn_transmute.rs:29:39
    |
 LL | const fn safe_transmute_fn() -> u32 { mem::transmute(Foo(3)) }
    |                                       ^^^^^^^^^^^^^^^^^^^^^^
@@ -69,7 +69,7 @@ LL | const fn safe_transmute_fn() -> u32 { mem::transmute(Foo(3)) }
    = note: `transmute` is only allowed in constants and statics for now
 
 error[E0658]: `transmute` is not allowed in constant functions
-  --> $DIR/feature-gate-const_fn_transmute.rs:30:49
+  --> $DIR/feature-gate-const_fn_transmute.rs:33:49
    |
 LL | const fn safe_transmute_fn_intrinsic() -> u32 { std::intrinsics::transmute(Foo(3)) }
    |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -79,7 +79,7 @@ LL | const fn safe_transmute_fn_intrinsic() -> u32 { std::intrinsics::transmute(
    = note: `transmute` is only allowed in constants and statics for now
 
 error[E0658]: `transmute` is not allowed in constant functions
-  --> $DIR/feature-gate-const_fn_transmute.rs:34:54
+  --> $DIR/feature-gate-const_fn_transmute.rs:37:54
    |
 LL | const fn safe_transmute_fn_core_intrinsic() -> u32 { core::intrinsics::transmute(Foo(3)) }
    |                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -89,7 +89,7 @@ LL | const fn safe_transmute_fn_core_intrinsic() -> u32 { core::intrinsics::tran
    = note: `transmute` is only allowed in constants and statics for now
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/feature-gate-const_fn_transmute.rs:26:39
+  --> $DIR/feature-gate-const_fn_transmute.rs:29:39
    |
 LL | const fn safe_transmute_fn() -> u32 { mem::transmute(Foo(3)) }
    |                                       ^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
@@ -97,7 +97,7 @@ LL | const fn safe_transmute_fn() -> u32 { mem::transmute(Foo(3)) }
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/feature-gate-const_fn_transmute.rs:30:49
+  --> $DIR/feature-gate-const_fn_transmute.rs:33:49
    |
 LL | const fn safe_transmute_fn_intrinsic() -> u32 { std::intrinsics::transmute(Foo(3)) }
    |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
@@ -105,7 +105,7 @@ LL | const fn safe_transmute_fn_intrinsic() -> u32 { std::intrinsics::transmute(
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/feature-gate-const_fn_transmute.rs:34:54
+  --> $DIR/feature-gate-const_fn_transmute.rs:37:54
    |
 LL | const fn safe_transmute_fn_core_intrinsic() -> u32 { core::intrinsics::transmute(Foo(3)) }
    |                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
diff --git a/src/test/ui/feature-gates/feature-gate-const_fn_transmute.rs b/src/test/ui/feature-gates/feature-gate-const_fn_transmute.rs
index 9007e501bc2..9a45dbc51d4 100644
--- a/src/test/ui/feature-gates/feature-gate-const_fn_transmute.rs
+++ b/src/test/ui/feature-gates/feature-gate-const_fn_transmute.rs
@@ -1,3 +1,6 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 use std::mem;
 
 #[repr(transparent)]
diff --git a/src/test/ui/feature-gates/feature-gate-const_fn_transmute.thir.stderr b/src/test/ui/feature-gates/feature-gate-const_fn_transmute.thir.stderr
new file mode 100644
index 00000000000..04efea0b230
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-const_fn_transmute.thir.stderr
@@ -0,0 +1,118 @@
+error[E0658]: `transmute` is not allowed in constant functions
+  --> $DIR/feature-gate-const_fn_transmute.rs:11:43
+   |
+LL | const fn transmute_fn() -> u32 { unsafe { mem::transmute(Foo(3)) } }
+   |                                           ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #53605 <https://github.com/rust-lang/rust/issues/53605> for more information
+   = help: add `#![feature(const_fn_transmute)]` to the crate attributes to enable
+   = note: `transmute` is only allowed in constants and statics for now
+
+error[E0658]: `transmute` is not allowed in constant functions
+  --> $DIR/feature-gate-const_fn_transmute.rs:14:53
+   |
+LL | const fn transmute_fn_intrinsic() -> u32 { unsafe { std::intrinsics::transmute(Foo(3)) } }
+   |                                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #53605 <https://github.com/rust-lang/rust/issues/53605> for more information
+   = help: add `#![feature(const_fn_transmute)]` to the crate attributes to enable
+   = note: `transmute` is only allowed in constants and statics for now
+
+error[E0658]: `transmute` is not allowed in constant functions
+  --> $DIR/feature-gate-const_fn_transmute.rs:17:58
+   |
+LL | const fn transmute_fn_core_intrinsic() -> u32 { unsafe { core::intrinsics::transmute(Foo(3)) } }
+   |                                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #53605 <https://github.com/rust-lang/rust/issues/53605> for more information
+   = help: add `#![feature(const_fn_transmute)]` to the crate attributes to enable
+   = note: `transmute` is only allowed in constants and statics for now
+
+error[E0658]: `transmute` is not allowed in constant functions
+  --> $DIR/feature-gate-const_fn_transmute.rs:20:48
+   |
+LL | const unsafe fn unsafe_transmute_fn() -> u32 { mem::transmute(Foo(3)) }
+   |                                                ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #53605 <https://github.com/rust-lang/rust/issues/53605> for more information
+   = help: add `#![feature(const_fn_transmute)]` to the crate attributes to enable
+   = note: `transmute` is only allowed in constants and statics for now
+
+error[E0658]: `transmute` is not allowed in constant functions
+  --> $DIR/feature-gate-const_fn_transmute.rs:23:58
+   |
+LL | const unsafe fn unsafe_transmute_fn_intrinsic() -> u32 { std::intrinsics::transmute(Foo(3)) }
+   |                                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #53605 <https://github.com/rust-lang/rust/issues/53605> for more information
+   = help: add `#![feature(const_fn_transmute)]` to the crate attributes to enable
+   = note: `transmute` is only allowed in constants and statics for now
+
+error[E0658]: `transmute` is not allowed in constant functions
+  --> $DIR/feature-gate-const_fn_transmute.rs:26:63
+   |
+LL | const unsafe fn unsafe_transmute_fn_core_intrinsic() -> u32 { core::intrinsics::transmute(Foo(3)) }
+   |                                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #53605 <https://github.com/rust-lang/rust/issues/53605> for more information
+   = help: add `#![feature(const_fn_transmute)]` to the crate attributes to enable
+   = note: `transmute` is only allowed in constants and statics for now
+
+error[E0658]: `transmute` is not allowed in constant functions
+  --> $DIR/feature-gate-const_fn_transmute.rs:29:39
+   |
+LL | const fn safe_transmute_fn() -> u32 { mem::transmute(Foo(3)) }
+   |                                       ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #53605 <https://github.com/rust-lang/rust/issues/53605> for more information
+   = help: add `#![feature(const_fn_transmute)]` to the crate attributes to enable
+   = note: `transmute` is only allowed in constants and statics for now
+
+error[E0658]: `transmute` is not allowed in constant functions
+  --> $DIR/feature-gate-const_fn_transmute.rs:33:49
+   |
+LL | const fn safe_transmute_fn_intrinsic() -> u32 { std::intrinsics::transmute(Foo(3)) }
+   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #53605 <https://github.com/rust-lang/rust/issues/53605> for more information
+   = help: add `#![feature(const_fn_transmute)]` to the crate attributes to enable
+   = note: `transmute` is only allowed in constants and statics for now
+
+error[E0658]: `transmute` is not allowed in constant functions
+  --> $DIR/feature-gate-const_fn_transmute.rs:37:54
+   |
+LL | const fn safe_transmute_fn_core_intrinsic() -> u32 { core::intrinsics::transmute(Foo(3)) }
+   |                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #53605 <https://github.com/rust-lang/rust/issues/53605> for more information
+   = help: add `#![feature(const_fn_transmute)]` to the crate attributes to enable
+   = note: `transmute` is only allowed in constants and statics for now
+
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/feature-gate-const_fn_transmute.rs:29:39
+   |
+LL | const fn safe_transmute_fn() -> u32 { mem::transmute(Foo(3)) }
+   |                                       ^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/feature-gate-const_fn_transmute.rs:33:49
+   |
+LL | const fn safe_transmute_fn_intrinsic() -> u32 { std::intrinsics::transmute(Foo(3)) }
+   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/feature-gate-const_fn_transmute.rs:37:54
+   |
+LL | const fn safe_transmute_fn_core_intrinsic() -> u32 { core::intrinsics::transmute(Foo(3)) }
+   |                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to 12 previous errors
+
+Some errors have detailed explanations: E0133, E0658.
+For more information about an error, try `rustc --explain E0133`.
diff --git a/src/test/ui/foreign-unsafe-fn-called.stderr b/src/test/ui/foreign-unsafe-fn-called.mir.stderr
index afc9632de7c..d3cf5d84fdd 100644
--- a/src/test/ui/foreign-unsafe-fn-called.stderr
+++ b/src/test/ui/foreign-unsafe-fn-called.mir.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/foreign-unsafe-fn-called.rs:8:5
+  --> $DIR/foreign-unsafe-fn-called.rs:11:5
    |
 LL |     test::free();
    |     ^^^^^^^^^^^^ call to unsafe function
diff --git a/src/test/ui/foreign-unsafe-fn-called.rs b/src/test/ui/foreign-unsafe-fn-called.rs
index abbe462021e..de3de286fc9 100644
--- a/src/test/ui/foreign-unsafe-fn-called.rs
+++ b/src/test/ui/foreign-unsafe-fn-called.rs
@@ -1,3 +1,6 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 mod test {
     extern "C" {
         pub fn free();
diff --git a/src/test/ui/foreign-unsafe-fn-called.thir.stderr b/src/test/ui/foreign-unsafe-fn-called.thir.stderr
new file mode 100644
index 00000000000..d3cf5d84fdd
--- /dev/null
+++ b/src/test/ui/foreign-unsafe-fn-called.thir.stderr
@@ -0,0 +1,11 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/foreign-unsafe-fn-called.rs:11:5
+   |
+LL |     test::free();
+   |     ^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/intrinsics/unchecked_math_unsafe.stderr b/src/test/ui/intrinsics/unchecked_math_unsafe.mir.stderr
index 4066cf8efb8..26b2f9f2713 100644
--- a/src/test/ui/intrinsics/unchecked_math_unsafe.stderr
+++ b/src/test/ui/intrinsics/unchecked_math_unsafe.mir.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/unchecked_math_unsafe.rs:5:15
+  --> $DIR/unchecked_math_unsafe.rs:8:15
    |
 LL |     let add = std::intrinsics::unchecked_add(x, y);
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
@@ -7,7 +7,7 @@ LL |     let add = std::intrinsics::unchecked_add(x, y);
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/unchecked_math_unsafe.rs:6:15
+  --> $DIR/unchecked_math_unsafe.rs:9:15
    |
 LL |     let sub = std::intrinsics::unchecked_sub(x, y);
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
@@ -15,7 +15,7 @@ LL |     let sub = std::intrinsics::unchecked_sub(x, y);
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/unchecked_math_unsafe.rs:7:15
+  --> $DIR/unchecked_math_unsafe.rs:10:15
    |
 LL |     let mul = std::intrinsics::unchecked_mul(x, y);
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
diff --git a/src/test/ui/intrinsics/unchecked_math_unsafe.rs b/src/test/ui/intrinsics/unchecked_math_unsafe.rs
index a034b45f530..98d3a11ad02 100644
--- a/src/test/ui/intrinsics/unchecked_math_unsafe.rs
+++ b/src/test/ui/intrinsics/unchecked_math_unsafe.rs
@@ -1,3 +1,6 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 #![feature(core_intrinsics)]
 
 fn main() {
diff --git a/src/test/ui/intrinsics/unchecked_math_unsafe.thir.stderr b/src/test/ui/intrinsics/unchecked_math_unsafe.thir.stderr
new file mode 100644
index 00000000000..26b2f9f2713
--- /dev/null
+++ b/src/test/ui/intrinsics/unchecked_math_unsafe.thir.stderr
@@ -0,0 +1,27 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/unchecked_math_unsafe.rs:8:15
+   |
+LL |     let add = std::intrinsics::unchecked_add(x, y);
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/unchecked_math_unsafe.rs:9:15
+   |
+LL |     let sub = std::intrinsics::unchecked_sub(x, y);
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/unchecked_math_unsafe.rs:10:15
+   |
+LL |     let mul = std::intrinsics::unchecked_mul(x, y);
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/issues/issue-28776.stderr b/src/test/ui/issues/issue-28776.mir.stderr
index 7faac88e26a..1d470fb5e0f 100644
--- a/src/test/ui/issues/issue-28776.stderr
+++ b/src/test/ui/issues/issue-28776.mir.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/issue-28776.rs:4:5
+  --> $DIR/issue-28776.rs:7:5
    |
 LL |     (&ptr::write)(1 as *mut _, 42);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
diff --git a/src/test/ui/issues/issue-28776.rs b/src/test/ui/issues/issue-28776.rs
index e564ebcd110..19df3c4a425 100644
--- a/src/test/ui/issues/issue-28776.rs
+++ b/src/test/ui/issues/issue-28776.rs
@@ -1,3 +1,6 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 use std::ptr;
 
 fn main() {
diff --git a/src/test/ui/issues/issue-28776.thir.stderr b/src/test/ui/issues/issue-28776.thir.stderr
new file mode 100644
index 00000000000..1d470fb5e0f
--- /dev/null
+++ b/src/test/ui/issues/issue-28776.thir.stderr
@@ -0,0 +1,11 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/issue-28776.rs:7:5
+   |
+LL |     (&ptr::write)(1 as *mut _, 42);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/issues/issue-3080.stderr b/src/test/ui/issues/issue-3080.mir.stderr
index 138d6df679f..f395c30b815 100644
--- a/src/test/ui/issues/issue-3080.stderr
+++ b/src/test/ui/issues/issue-3080.mir.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/issue-3080.rs:7:5
+  --> $DIR/issue-3080.rs:10:5
    |
 LL |     X(()).with();
    |     ^^^^^^^^^^^^ call to unsafe function
diff --git a/src/test/ui/issues/issue-3080.rs b/src/test/ui/issues/issue-3080.rs
index 883f3bfd24e..2b5269dda8f 100644
--- a/src/test/ui/issues/issue-3080.rs
+++ b/src/test/ui/issues/issue-3080.rs
@@ -1,3 +1,6 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 struct X(());
 impl X {
     pub unsafe fn with(&self) { }
diff --git a/src/test/ui/issues/issue-3080.thir.stderr b/src/test/ui/issues/issue-3080.thir.stderr
new file mode 100644
index 00000000000..f395c30b815
--- /dev/null
+++ b/src/test/ui/issues/issue-3080.thir.stderr
@@ -0,0 +1,11 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/issue-3080.rs:10:5
+   |
+LL |     X(()).with();
+   |     ^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/issues/issue-48131.stderr b/src/test/ui/issues/issue-48131.mir.stderr
index 5acc4f16e9f..6817e8830c5 100644
--- a/src/test/ui/issues/issue-48131.stderr
+++ b/src/test/ui/issues/issue-48131.mir.stderr
@@ -1,17 +1,17 @@
 error: unnecessary `unsafe` block
-  --> $DIR/issue-48131.rs:9:9
+  --> $DIR/issue-48131.rs:12:9
    |
 LL |         unsafe { /* unnecessary */ }
    |         ^^^^^^ unnecessary `unsafe` block
    |
 note: the lint level is defined here
-  --> $DIR/issue-48131.rs:3:9
+  --> $DIR/issue-48131.rs:6:9
    |
 LL | #![deny(unused_unsafe)]
    |         ^^^^^^^^^^^^^
 
 error: unnecessary `unsafe` block
-  --> $DIR/issue-48131.rs:20:13
+  --> $DIR/issue-48131.rs:23:13
    |
 LL |             unsafe { /* unnecessary */ }
    |             ^^^^^^ unnecessary `unsafe` block
diff --git a/src/test/ui/issues/issue-48131.rs b/src/test/ui/issues/issue-48131.rs
index 85664e62ead..df98547084d 100644
--- a/src/test/ui/issues/issue-48131.rs
+++ b/src/test/ui/issues/issue-48131.rs
@@ -1,3 +1,6 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 // This note is annotated because the purpose of the test
 // is to ensure that certain other notes are not generated.
 #![deny(unused_unsafe)] //~ NOTE
diff --git a/src/test/ui/issues/issue-48131.thir.stderr b/src/test/ui/issues/issue-48131.thir.stderr
new file mode 100644
index 00000000000..6817e8830c5
--- /dev/null
+++ b/src/test/ui/issues/issue-48131.thir.stderr
@@ -0,0 +1,20 @@
+error: unnecessary `unsafe` block
+  --> $DIR/issue-48131.rs:12:9
+   |
+LL |         unsafe { /* unnecessary */ }
+   |         ^^^^^^ unnecessary `unsafe` block
+   |
+note: the lint level is defined here
+  --> $DIR/issue-48131.rs:6:9
+   |
+LL | #![deny(unused_unsafe)]
+   |         ^^^^^^^^^^^^^
+
+error: unnecessary `unsafe` block
+  --> $DIR/issue-48131.rs:23:13
+   |
+LL |             unsafe { /* unnecessary */ }
+   |             ^^^^^^ unnecessary `unsafe` block
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/issues/issue-5844.stderr b/src/test/ui/issues/issue-5844.mir.stderr
index ed5a3dc6b1e..6134d6889ff 100644
--- a/src/test/ui/issues/issue-5844.stderr
+++ b/src/test/ui/issues/issue-5844.mir.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/issue-5844.rs:6:5
+  --> $DIR/issue-5844.rs:8:5
    |
 LL |     issue_5844_aux::rand();
    |     ^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
diff --git a/src/test/ui/issues/issue-5844.rs b/src/test/ui/issues/issue-5844.rs
index b855e87e3f5..4f90a9c6645 100644
--- a/src/test/ui/issues/issue-5844.rs
+++ b/src/test/ui/issues/issue-5844.rs
@@ -1,4 +1,6 @@
 //aux-build:issue-5844-aux.rs
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
 
 extern crate issue_5844_aux;
 
diff --git a/src/test/ui/issues/issue-5844.thir.stderr b/src/test/ui/issues/issue-5844.thir.stderr
new file mode 100644
index 00000000000..6134d6889ff
--- /dev/null
+++ b/src/test/ui/issues/issue-5844.thir.stderr
@@ -0,0 +1,11 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/issue-5844.rs:8:5
+   |
+LL |     issue_5844_aux::rand();
+   |     ^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/threads-sendsync/issue-43733.stderr b/src/test/ui/threads-sendsync/issue-43733.mir.stderr
index c7b12a395a2..0f4b5936dd0 100644
--- a/src/test/ui/threads-sendsync/issue-43733.stderr
+++ b/src/test/ui/threads-sendsync/issue-43733.mir.stderr
@@ -7,10 +7,10 @@ LL |     __KEY.get(Default::default)
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/issue-43733.rs:21:5
+  --> $DIR/issue-43733.rs:20:42
    |
-LL |     std::thread::LocalKey::new(__getit);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+LL | static FOO: std::thread::LocalKey<Foo> = std::thread::LocalKey::new(__getit);
+   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
    |
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
diff --git a/src/test/ui/threads-sendsync/issue-43733.rs b/src/test/ui/threads-sendsync/issue-43733.rs
index 4d81d0a5d20..5434140cd61 100644
--- a/src/test/ui/threads-sendsync/issue-43733.rs
+++ b/src/test/ui/threads-sendsync/issue-43733.rs
@@ -1,3 +1,6 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 #![feature(thread_local)]
 #![feature(cfg_target_thread_local, thread_local_internals)]
 
@@ -5,26 +8,24 @@ type Foo = std::cell::RefCell<String>;
 
 #[cfg(target_thread_local)]
 #[thread_local]
-static __KEY: std::thread::__FastLocalKeyInner<Foo> =
-    std::thread::__FastLocalKeyInner::new();
+static __KEY: std::thread::__FastLocalKeyInner<Foo> = std::thread::__FastLocalKeyInner::new();
 
 #[cfg(not(target_thread_local))]
-static __KEY: std::thread::__OsLocalKeyInner<Foo> =
-    std::thread::__OsLocalKeyInner::new();
+static __KEY: std::thread::__OsLocalKeyInner<Foo> = std::thread::__OsLocalKeyInner::new();
 
-fn __getit() -> std::option::Option<&'static Foo>
-{
+fn __getit() -> std::option::Option<&'static Foo> {
     __KEY.get(Default::default) //~ ERROR call to unsafe function is unsafe
 }
 
-static FOO: std::thread::LocalKey<Foo> =
-    std::thread::LocalKey::new(__getit);
+static FOO: std::thread::LocalKey<Foo> = std::thread::LocalKey::new(__getit);
 //~^ ERROR call to unsafe function is unsafe
 
 fn main() {
     FOO.with(|foo| println!("{}", foo.borrow()));
     std::thread::spawn(|| {
         FOO.with(|foo| *foo.borrow_mut() += "foo");
-    }).join().unwrap();
+    })
+    .join()
+    .unwrap();
     FOO.with(|foo| println!("{}", foo.borrow()));
 }
diff --git a/src/test/ui/threads-sendsync/issue-43733.thir.stderr b/src/test/ui/threads-sendsync/issue-43733.thir.stderr
new file mode 100644
index 00000000000..0f4b5936dd0
--- /dev/null
+++ b/src/test/ui/threads-sendsync/issue-43733.thir.stderr
@@ -0,0 +1,19 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/issue-43733.rs:17:5
+   |
+LL |     __KEY.get(Default::default)
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/issue-43733.rs:20:42
+   |
+LL | static FOO: std::thread::LocalKey<Foo> = std::thread::LocalKey::new(__getit);
+   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/unsafe/unsafe-around-compiler-generated-unsafe.stderr b/src/test/ui/unsafe/unsafe-around-compiler-generated-unsafe.mir.stderr
index 0dba8496efd..68101326861 100644
--- a/src/test/ui/unsafe/unsafe-around-compiler-generated-unsafe.stderr
+++ b/src/test/ui/unsafe/unsafe-around-compiler-generated-unsafe.mir.stderr
@@ -1,11 +1,11 @@
 error: unnecessary `unsafe` block
-  --> $DIR/unsafe-around-compiler-generated-unsafe.rs:6:5
+  --> $DIR/unsafe-around-compiler-generated-unsafe.rs:9:5
    |
 LL |     unsafe { println!("foo"); }
    |     ^^^^^^ unnecessary `unsafe` block
    |
 note: the lint level is defined here
-  --> $DIR/unsafe-around-compiler-generated-unsafe.rs:3:9
+  --> $DIR/unsafe-around-compiler-generated-unsafe.rs:6:9
    |
 LL | #![deny(unused_unsafe)]
    |         ^^^^^^^^^^^^^
diff --git a/src/test/ui/unsafe/unsafe-around-compiler-generated-unsafe.rs b/src/test/ui/unsafe/unsafe-around-compiler-generated-unsafe.rs
index 817939e0757..08801f9ef59 100644
--- a/src/test/ui/unsafe/unsafe-around-compiler-generated-unsafe.rs
+++ b/src/test/ui/unsafe/unsafe-around-compiler-generated-unsafe.rs
@@ -1,5 +1,8 @@
 // issue #12418
 
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 #![deny(unused_unsafe)]
 
 fn main() {
diff --git a/src/test/ui/unsafe/unsafe-around-compiler-generated-unsafe.thir.stderr b/src/test/ui/unsafe/unsafe-around-compiler-generated-unsafe.thir.stderr
new file mode 100644
index 00000000000..68101326861
--- /dev/null
+++ b/src/test/ui/unsafe/unsafe-around-compiler-generated-unsafe.thir.stderr
@@ -0,0 +1,14 @@
+error: unnecessary `unsafe` block
+  --> $DIR/unsafe-around-compiler-generated-unsafe.rs:9:5
+   |
+LL |     unsafe { println!("foo"); }
+   |     ^^^^^^ unnecessary `unsafe` block
+   |
+note: the lint level is defined here
+  --> $DIR/unsafe-around-compiler-generated-unsafe.rs:6:9
+   |
+LL | #![deny(unused_unsafe)]
+   |         ^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/unsafe/unsafe-const-fn.stderr b/src/test/ui/unsafe/unsafe-const-fn.mir.stderr
index 370e1e673cf..3031be720f0 100644
--- a/src/test/ui/unsafe/unsafe-const-fn.stderr
+++ b/src/test/ui/unsafe/unsafe-const-fn.mir.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/unsafe-const-fn.rs:7:18
+  --> $DIR/unsafe-const-fn.rs:10:18
    |
 LL | const VAL: u32 = dummy(0xFFFF);
    |                  ^^^^^^^^^^^^^ call to unsafe function
diff --git a/src/test/ui/unsafe/unsafe-const-fn.rs b/src/test/ui/unsafe/unsafe-const-fn.rs
index 3b4becf17a7..65e3acf3063 100644
--- a/src/test/ui/unsafe/unsafe-const-fn.rs
+++ b/src/test/ui/unsafe/unsafe-const-fn.rs
@@ -1,5 +1,8 @@
 // A quick test of 'unsafe const fn' functionality
 
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 const unsafe fn dummy(v: u32) -> u32 {
     !v
 }
diff --git a/src/test/ui/unsafe/unsafe-const-fn.thir.stderr b/src/test/ui/unsafe/unsafe-const-fn.thir.stderr
new file mode 100644
index 00000000000..3031be720f0
--- /dev/null
+++ b/src/test/ui/unsafe/unsafe-const-fn.thir.stderr
@@ -0,0 +1,11 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/unsafe-const-fn.rs:10:18
+   |
+LL | const VAL: u32 = dummy(0xFFFF);
+   |                  ^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/unsafe/unsafe-fn-called-from-safe.mir.stderr b/src/test/ui/unsafe/unsafe-fn-called-from-safe.mir.stderr
new file mode 100644
index 00000000000..1d6fa4cbf40
--- /dev/null
+++ b/src/test/ui/unsafe/unsafe-fn-called-from-safe.mir.stderr
@@ -0,0 +1,11 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/unsafe-fn-called-from-safe.rs:7:5
+   |
+LL |     f();
+   |     ^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/unsafe/unsafe-fn-called-from-safe.rs b/src/test/ui/unsafe/unsafe-fn-called-from-safe.rs
index 5487a8ecc37..df12e441516 100644
--- a/src/test/ui/unsafe/unsafe-fn-called-from-safe.rs
+++ b/src/test/ui/unsafe/unsafe-fn-called-from-safe.rs
@@ -1,3 +1,6 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 unsafe fn f() { return; }
 
 fn main() {
diff --git a/src/test/ui/unsafe/unsafe-fn-called-from-safe.thir.stderr b/src/test/ui/unsafe/unsafe-fn-called-from-safe.thir.stderr
new file mode 100644
index 00000000000..1d6fa4cbf40
--- /dev/null
+++ b/src/test/ui/unsafe/unsafe-fn-called-from-safe.thir.stderr
@@ -0,0 +1,11 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/unsafe-fn-called-from-safe.rs:7:5
+   |
+LL |     f();
+   |     ^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/unsafe/unsafe-fn-used-as-value.stderr b/src/test/ui/unsafe/unsafe-fn-used-as-value.mir.stderr
index a7b73ec5342..b08a7109dda 100644
--- a/src/test/ui/unsafe/unsafe-fn-used-as-value.stderr
+++ b/src/test/ui/unsafe/unsafe-fn-used-as-value.mir.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
-  --> $DIR/unsafe-fn-used-as-value.rs:5:5
+  --> $DIR/unsafe-fn-used-as-value.rs:8:5
    |
 LL |     x();
    |     ^^^ call to unsafe function
diff --git a/src/test/ui/unsafe/unsafe-fn-used-as-value.rs b/src/test/ui/unsafe/unsafe-fn-used-as-value.rs
index 59b81477930..2af0786617b 100644
--- a/src/test/ui/unsafe/unsafe-fn-used-as-value.rs
+++ b/src/test/ui/unsafe/unsafe-fn-used-as-value.rs
@@ -1,3 +1,6 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 unsafe fn f() { return; }
 
 fn main() {
diff --git a/src/test/ui/unsafe/unsafe-fn-used-as-value.thir.stderr b/src/test/ui/unsafe/unsafe-fn-used-as-value.thir.stderr
new file mode 100644
index 00000000000..b08a7109dda
--- /dev/null
+++ b/src/test/ui/unsafe/unsafe-fn-used-as-value.thir.stderr
@@ -0,0 +1,11 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/unsafe-fn-used-as-value.rs:8:5
+   |
+LL |     x();
+   |     ^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 8334bc68ae7..7b42de0ec43 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -7,8 +7,8 @@ use std::path::Path;
 
 const ENTRY_LIMIT: usize = 1000;
 // FIXME: The following limits should be reduced eventually.
-const ROOT_ENTRY_LIMIT: usize = 1388;
-const ISSUES_ENTRY_LIMIT: usize = 2551;
+const ROOT_ENTRY_LIMIT: usize = 1370;
+const ISSUES_ENTRY_LIMIT: usize = 2555;
 
 fn check_entries(path: &Path, bad: &mut bool) {
     let dirs = walkdir::WalkDir::new(&path.join("test/ui"))