diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs
index 012d97ef106c7..96e9a40df3685 100644
--- a/compiler/rustc_passes/src/intrinsicck.rs
+++ b/compiler/rustc_passes/src/intrinsicck.rs
@@ -139,6 +139,7 @@ impl ExprVisitor<'tcx> {
         reg: InlineAsmRegOrRegClass,
         expr: &hir::Expr<'tcx>,
         template: &[InlineAsmTemplatePiece],
+        is_input: bool,
         tied_input: Option<(&hir::Expr<'tcx>, Option<InlineAsmType>)>,
     ) -> Option<InlineAsmType> {
         // Check the type against the allowed types for inline asm.
@@ -150,7 +151,9 @@ impl ExprVisitor<'tcx> {
             _ => unreachable!(),
         };
         let asm_ty = match *ty.kind() {
-            ty::Never | ty::Error(_) => return None,
+            // `!` is allowed for input but not for output (issue #87802)
+            ty::Never if is_input => return None,
+            ty::Error(_) => return None,
             ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8),
             ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16),
             ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32),
@@ -350,24 +353,26 @@ impl ExprVisitor<'tcx> {
         for (idx, (op, _)) in asm.operands.iter().enumerate() {
             match *op {
                 hir::InlineAsmOperand::In { reg, ref expr } => {
-                    self.check_asm_operand_type(idx, reg, expr, asm.template, None);
+                    self.check_asm_operand_type(idx, reg, expr, asm.template, true, None);
                 }
                 hir::InlineAsmOperand::Out { reg, late: _, ref expr } => {
                     if let Some(expr) = expr {
-                        self.check_asm_operand_type(idx, reg, expr, asm.template, None);
+                        self.check_asm_operand_type(idx, reg, expr, asm.template, false, None);
                     }
                 }
                 hir::InlineAsmOperand::InOut { reg, late: _, ref expr } => {
-                    self.check_asm_operand_type(idx, reg, expr, asm.template, None);
+                    self.check_asm_operand_type(idx, reg, expr, asm.template, false, None);
                 }
                 hir::InlineAsmOperand::SplitInOut { reg, late: _, ref in_expr, ref out_expr } => {
-                    let in_ty = self.check_asm_operand_type(idx, reg, in_expr, asm.template, None);
+                    let in_ty =
+                        self.check_asm_operand_type(idx, reg, in_expr, asm.template, true, None);
                     if let Some(out_expr) = out_expr {
                         self.check_asm_operand_type(
                             idx,
                             reg,
                             out_expr,
                             asm.template,
+                            false,
                             Some((in_expr, in_ty)),
                         );
                     }
diff --git a/src/test/ui/asm/issue-87802.rs b/src/test/ui/asm/issue-87802.rs
new file mode 100644
index 0000000000000..b1fc13b6a7e8c
--- /dev/null
+++ b/src/test/ui/asm/issue-87802.rs
@@ -0,0 +1,17 @@
+// only-x86_64
+// Make sure rustc doesn't ICE on asm! when output type is !.
+
+#![feature(asm)]
+
+fn hmm() -> ! {
+    let x;
+    unsafe {
+        asm!("/* {0} */", out(reg) x);
+        //~^ ERROR cannot use value of type `!` for inline assembly
+    }
+    x
+}
+
+fn main() {
+    hmm();
+}
diff --git a/src/test/ui/asm/issue-87802.stderr b/src/test/ui/asm/issue-87802.stderr
new file mode 100644
index 0000000000000..1eb72b68a7f04
--- /dev/null
+++ b/src/test/ui/asm/issue-87802.stderr
@@ -0,0 +1,10 @@
+error: cannot use value of type `!` for inline assembly
+  --> $DIR/issue-87802.rs:9:36
+   |
+LL |         asm!("/* {0} */", out(reg) x);
+   |                                    ^
+   |
+   = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
+
+error: aborting due to previous error
+