about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2023-07-17 12:58:54 +0200
committerGitHub <noreply@github.com>2023-07-17 12:58:54 +0200
commit93b9812be840ec218bf499dcc5c6f81f20ea3002 (patch)
tree99f59b3ad68e0fcc2e60459b7def45ba2d92c656
parenteca9c0101c98df239e02c34f7b802cf775b86d08 (diff)
parent56633b3f51967f544c988dcabbcd1b99f4cc3558 (diff)
Rollup merge of #113770 - dtolnay:derivevoid, r=compiler-errors,nnethercote
Generate safe stable code for derives on empty enums

Generate `match *self {}` instead of `unsafe { core::intrinsics::unreachable() }`.

This is:

1. safe
2. stable

for the benefit of everyone looking at these derived impls through `cargo expand`.

[Both expansions compile to the same code at all optimization levels (including `0`).](https://rust.godbolt.org/z/P79joGMh3)
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs18
-rw-r--r--tests/ui/deriving/deriving-all-codegen.stdout14
2 files changed, 19 insertions, 13 deletions
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 4ba09335cb7..9865b6a72ee 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -1134,9 +1134,14 @@ impl<'a> MethodDef<'a> {
         trait_: &TraitDef<'b>,
         enum_def: &'b EnumDef,
         type_ident: Ident,
-        selflike_args: ThinVec<P<Expr>>,
+        mut selflike_args: ThinVec<P<Expr>>,
         nonselflike_args: &[P<Expr>],
     ) -> BlockOrExpr {
+        assert!(
+            !selflike_args.is_empty(),
+            "static methods must use `expand_static_enum_method_body`",
+        );
+
         let span = trait_.span;
         let variants = &enum_def.variants;
 
@@ -1144,10 +1149,15 @@ impl<'a> MethodDef<'a> {
         let unify_fieldless_variants =
             self.fieldless_variants_strategy == FieldlessVariantsStrategy::Unify;
 
-        // There is no sensible code to be generated for *any* deriving on a
-        // zero-variant enum. So we just generate a failing expression.
+        // For zero-variant enum, this function body is unreachable. Generate
+        // `match *self {}`. This produces machine code identical to `unsafe {
+        // core::intrinsics::unreachable() }` while being safe and stable.
         if variants.is_empty() {
-            return BlockOrExpr(ThinVec::new(), Some(deriving::call_unreachable(cx, span)));
+            selflike_args.truncate(1);
+            let match_arg = cx.expr_deref(span, selflike_args.pop().unwrap());
+            let match_arms = ThinVec::new();
+            let expr = cx.expr_match(span, match_arg, match_arms);
+            return BlockOrExpr(ThinVec::new(), Some(expr));
         }
 
         let prefixes = iter::once("__self".to_string())
diff --git a/tests/ui/deriving/deriving-all-codegen.stdout b/tests/ui/deriving/deriving-all-codegen.stdout
index d6a2c80cc06..6bfc859bfe8 100644
--- a/tests/ui/deriving/deriving-all-codegen.stdout
+++ b/tests/ui/deriving/deriving-all-codegen.stdout
@@ -798,14 +798,14 @@ impl ::core::marker::Copy for Enum0 { }
 #[automatically_derived]
 impl ::core::fmt::Debug for Enum0 {
     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-        unsafe { ::core::intrinsics::unreachable() }
+        match *self {}
     }
 }
 #[automatically_derived]
 impl ::core::hash::Hash for Enum0 {
     #[inline]
     fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
-        unsafe { ::core::intrinsics::unreachable() }
+        match *self {}
     }
 }
 #[automatically_derived]
@@ -813,9 +813,7 @@ impl ::core::marker::StructuralPartialEq for Enum0 { }
 #[automatically_derived]
 impl ::core::cmp::PartialEq for Enum0 {
     #[inline]
-    fn eq(&self, other: &Enum0) -> bool {
-        unsafe { ::core::intrinsics::unreachable() }
-    }
+    fn eq(&self, other: &Enum0) -> bool { match *self {} }
 }
 #[automatically_derived]
 impl ::core::marker::StructuralEq for Enum0 { }
@@ -831,15 +829,13 @@ impl ::core::cmp::PartialOrd for Enum0 {
     #[inline]
     fn partial_cmp(&self, other: &Enum0)
         -> ::core::option::Option<::core::cmp::Ordering> {
-        unsafe { ::core::intrinsics::unreachable() }
+        match *self {}
     }
 }
 #[automatically_derived]
 impl ::core::cmp::Ord for Enum0 {
     #[inline]
-    fn cmp(&self, other: &Enum0) -> ::core::cmp::Ordering {
-        unsafe { ::core::intrinsics::unreachable() }
-    }
+    fn cmp(&self, other: &Enum0) -> ::core::cmp::Ordering { match *self {} }
 }
 
 // A single-variant enum.