about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_error_codes/src/error_codes.rs1
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0791.md41
-rw-r--r--compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl2
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs21
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs2
-rw-r--r--src/test/ui/linkage-attr/linkage2.rs2
-rw-r--r--src/test/ui/linkage-attr/linkage2.stderr3
7 files changed, 67 insertions, 5 deletions
diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs
index 1e86d159668..31a709c36d4 100644
--- a/compiler/rustc_error_codes/src/error_codes.rs
+++ b/compiler/rustc_error_codes/src/error_codes.rs
@@ -494,6 +494,7 @@ E0786: include_str!("./error_codes/E0786.md"),
 E0787: include_str!("./error_codes/E0787.md"),
 E0788: include_str!("./error_codes/E0788.md"),
 E0790: include_str!("./error_codes/E0790.md"),
+E0791: include_str!("./error_codes/E0791.md"),
 ;
 //  E0006, // merged with E0005
 //  E0008, // cannot bind by-move into a pattern guard
diff --git a/compiler/rustc_error_codes/src/error_codes/E0791.md b/compiler/rustc_error_codes/src/error_codes/E0791.md
new file mode 100644
index 00000000000..61d2f511a34
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0791.md
@@ -0,0 +1,41 @@
+Static variables with the `#[linkage]` attribute within external blocks
+must have one of the following types, which are equivalent to a nullable
+pointer in C:
+
+* `*mut T` or `*const T`, where `T` may be any type.
+
+* An enumerator type with no `#[repr]` attribute and with two variants, where
+  one of the variants has no fields, and the other has a single field of one of
+  the following non-nullable types:
+  * Reference type
+  * Function pointer type
+
+  The variants can appear in either order.
+
+For example, the following declaration is invalid:
+
+```compile_fail,E0791
+#![feature(linkage)]
+
+extern "C" {
+    #[linkage = "extern_weak"]
+    static foo: i8;
+}
+```
+
+The following declarations are valid:
+
+```
+#![feature(linkage)]
+
+extern "C" {
+    #[linkage = "extern_weak"]
+    static foo: Option<unsafe extern "C" fn()>;
+
+    #[linkage = "extern_weak"]
+    static bar: Option<&'static i8>;
+
+    #[linkage = "extern_weak"]
+    static baz: *mut i8;
+}
+```
diff --git a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
index 86a51b2d310..a4910dacd5f 100644
--- a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
@@ -115,4 +115,4 @@ hir_analysis_self_in_impl_self =
     .note = replace `Self` with a different type
 
 hir_analysis_linkage_type =
-    must have type `*const T` or `*mut T` due to `#[linkage]` attribute
+    invalid type for variable with `#[linkage]` attribute
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 7345e604d0b..fc0ca62090d 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -21,7 +21,7 @@ use rustc_middle::middle::stability::EvalResult;
 use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_middle::ty::util::{Discr, IntTypeExt};
-use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
+use rustc_middle::ty::{self, AdtDef, ParamEnv, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
 use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS};
 use rustc_span::symbol::sym;
 use rustc_span::{self, Span};
@@ -479,10 +479,29 @@ fn check_opaque_meets_bounds<'tcx>(
     let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
 }
 
+fn is_enum_of_nonnullable_ptr<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    adt_def: AdtDef<'tcx>,
+    substs: SubstsRef<'tcx>,
+) -> bool {
+    if adt_def.repr().inhibit_enum_layout_opt() {
+        return false;
+    }
+
+    let [var_one, var_two] = &adt_def.variants().raw[..] else {
+        return false;
+    };
+    let (([], [field]) | ([field], [])) = (&var_one.fields[..], &var_two.fields[..]) else {
+        return false;
+    };
+    matches!(field.ty(tcx, substs).kind(), ty::FnPtr(..) | ty::Ref(..))
+}
+
 fn check_static_linkage<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
     if tcx.codegen_fn_attrs(def_id).import_linkage.is_some() {
         if match tcx.type_of(def_id).kind() {
             ty::RawPtr(_) => false,
+            ty::Adt(adt_def, substs) => !is_enum_of_nonnullable_ptr(tcx, *adt_def, *substs),
             _ => true,
         } {
             tcx.sess.emit_err(LinkageType { span: tcx.def_span(def_id) });
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 02943e7b887..1ac53fb6ddf 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -287,7 +287,7 @@ pub struct SelfInImplSelf {
 }
 
 #[derive(Diagnostic)]
-#[diag(hir_analysis_linkage_type)]
+#[diag(hir_analysis_linkage_type, code = "E0791")]
 pub(crate) struct LinkageType {
     #[primary_span]
     pub span: Span,
diff --git a/src/test/ui/linkage-attr/linkage2.rs b/src/test/ui/linkage-attr/linkage2.rs
index 3bc6634f18a..aa42874f7ba 100644
--- a/src/test/ui/linkage-attr/linkage2.rs
+++ b/src/test/ui/linkage-attr/linkage2.rs
@@ -5,7 +5,7 @@
 extern "C" {
     #[linkage = "extern_weak"]
     static foo: i32;
-//~^ ERROR: must have type `*const T` or `*mut T` due to `#[linkage]` attribute
+//~^ ERROR: invalid type for variable with `#[linkage]` attribute
 }
 
 fn main() {
diff --git a/src/test/ui/linkage-attr/linkage2.stderr b/src/test/ui/linkage-attr/linkage2.stderr
index 44e8eaaef51..7265f711fd0 100644
--- a/src/test/ui/linkage-attr/linkage2.stderr
+++ b/src/test/ui/linkage-attr/linkage2.stderr
@@ -1,4 +1,4 @@
-error: must have type `*const T` or `*mut T` due to `#[linkage]` attribute
+error[E0791]: invalid type for variable with `#[linkage]` attribute
   --> $DIR/linkage2.rs:7:5
    |
 LL |     static foo: i32;
@@ -6,3 +6,4 @@ LL |     static foo: i32;
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0791`.