about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2020-10-24 16:13:39 +0200
committerRalf Jung <post@ralfj.de>2020-10-24 16:15:42 +0200
commit1333206eb32a78ef6ffe0defd4acd164e47146b7 (patch)
tree1432bb73ab8de8e8f543c7f5ebb3b2de767f68bc
parent2e8a54af60df63034e41359acfc923e5c5769a91 (diff)
downloadrust-1333206eb32a78ef6ffe0defd4acd164e47146b7.tar.gz
rust-1333206eb32a78ef6ffe0defd4acd164e47146b7.zip
ensure that statics are inhabited
-rw-r--r--compiler/rustc_session/src/lint/builtin.rs30
-rw-r--r--compiler/rustc_typeck/src/check/check.rs51
-rw-r--r--src/test/ui/statics/uninhabited-static.rs12
-rw-r--r--src/test/ui/statics/uninhabited-static.stderr27
4 files changed, 114 insertions, 6 deletions
diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs
index fef3164de59..d12eb0edd4d 100644
--- a/compiler/rustc_session/src/lint/builtin.rs
+++ b/compiler/rustc_session/src/lint/builtin.rs
@@ -2647,6 +2647,35 @@ declare_lint! {
     };
 }
 
+declare_lint! {
+    /// The `uninhabited_static` lint detects uninhbaited statics.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// enum Void {}
+    /// extern {
+    ///     static EXTERN: Void;
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Statics with an uninhabited type can never be initialized, so they are impossible to define.
+    /// However, this can be side-stepped with an `extern static`, leading to problems later in the
+    /// compiler which assumes that there are no initialized uninhabited places (such as locals or
+    /// statics). This was accientally allowed, but is being phased out.
+    pub UNINHABITED_STATIC,
+    Warn,
+    "uninhabited static",
+    @future_incompatible = FutureIncompatibleInfo {
+        reference: "issue #74840 <https://github.com/rust-lang/rust/issues/74840>",
+        edition: None,
+    };
+}
+
 declare_tool_lint! {
     pub rustc::INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
     Deny,
@@ -2732,6 +2761,7 @@ declare_lint_pass! {
         CENUM_IMPL_DROP_CAST,
         CONST_EVALUATABLE_UNCHECKED,
         INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
+        UNINHABITED_STATIC,
     ]
 }
 
diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs
index 3d8653b4a6a..fb6746c85d3 100644
--- a/compiler/rustc_typeck/src/check/check.rs
+++ b/compiler/rustc_typeck/src/check/check.rs
@@ -14,8 +14,9 @@ use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_middle::ty::util::{Discr, IntTypeExt, Representability};
-use rustc_middle::ty::{self, RegionKind, ToPredicate, Ty, TyCtxt};
+use rustc_middle::ty::{self, ParamEnv, RegionKind, ToPredicate, Ty, TyCtxt};
 use rustc_session::config::EntryFnType;
+use rustc_session::lint::builtin::UNINHABITED_STATIC;
 use rustc_span::symbol::sym;
 use rustc_span::{self, MultiSpan, Span};
 use rustc_target::spec::abi::Abi;
@@ -338,7 +339,7 @@ pub(super) fn check_struct(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
     check_packed(tcx, span, def);
 }
 
-pub(super) fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
+fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
     let def_id = tcx.hir().local_def_id(id);
     let def = tcx.adt_def(def_id);
     def.destructor(tcx); // force the destructor to be evaluated
@@ -349,7 +350,7 @@ pub(super) fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
 }
 
 /// Check that the fields of the `union` do not need dropping.
-pub(super) fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool {
+fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool {
     let item_type = tcx.type_of(item_def_id);
     if let ty::Adt(def, substs) = item_type.kind() {
         assert!(def.is_union());
@@ -377,6 +378,36 @@ pub(super) fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: Local
     true
 }
 
+/// Check that a `static` is inhabited.
+fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) {
+    // Make sure statics are inhabited.
+    // Other parts of the compiler assume that there are no uninhabited places. In principle it
+    // would be enugh to check this for `extern` statics, as statics with an initializer will
+    // have UB during initialization if they are uninhabited, but there also seems to be no good
+    // reason to allow any statics to be uninhabited.
+    let ty = tcx.type_of(def_id);
+    let layout = match tcx.layout_of(ParamEnv::reveal_all().and(ty)) {
+        Ok(l) => l,
+        Err(_) => {
+            // Generic statics are rejected, but we still reach this case.
+            tcx.sess.delay_span_bug(span, "generic static must be rejected");
+            return;
+        }
+    };
+    if layout.abi.is_uninhabited() {
+        tcx.struct_span_lint_hir(
+            UNINHABITED_STATIC,
+            tcx.hir().local_def_id_to_hir_id(def_id),
+            span,
+            |lint| {
+                lint.build("static of uninhabited type")
+                .note("uninhabited statics cannot be initialized, and any access would be an immediate error")
+                .emit();
+            },
+        );
+    }
+}
+
 /// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo`
 /// projections that would result in "inheriting lifetimes".
 pub(super) fn check_opaque<'tcx>(
@@ -609,6 +640,7 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) {
             let def_id = tcx.hir().local_def_id(it.hir_id);
             tcx.ensure().typeck(def_id);
             maybe_check_static_with_link_section(tcx, def_id, it.span);
+            check_static_inhabited(tcx, def_id, it.span);
         }
         hir::ItemKind::Const(..) => {
             tcx.ensure().typeck(tcx.hir().local_def_id(it.hir_id));
@@ -691,7 +723,8 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) {
                 }
             } else {
                 for item in m.items {
-                    let generics = tcx.generics_of(tcx.hir().local_def_id(item.hir_id));
+                    let def_id = tcx.hir().local_def_id(item.hir_id);
+                    let generics = tcx.generics_of(def_id);
                     let own_counts = generics.own_counts();
                     if generics.params.len() - own_counts.lifetimes != 0 {
                         let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts) {
@@ -722,8 +755,14 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) {
                         .emit();
                     }
 
-                    if let hir::ForeignItemKind::Fn(ref fn_decl, _, _) = item.kind {
-                        require_c_abi_if_c_variadic(tcx, fn_decl, m.abi, item.span);
+                    match item.kind {
+                        hir::ForeignItemKind::Fn(ref fn_decl, _, _) => {
+                            require_c_abi_if_c_variadic(tcx, fn_decl, m.abi, item.span);
+                        }
+                        hir::ForeignItemKind::Static(..) => {
+                            check_static_inhabited(tcx, def_id, item.span);
+                        }
+                        _ => {}
                     }
                 }
             }
diff --git a/src/test/ui/statics/uninhabited-static.rs b/src/test/ui/statics/uninhabited-static.rs
new file mode 100644
index 00000000000..61189b0e0cd
--- /dev/null
+++ b/src/test/ui/statics/uninhabited-static.rs
@@ -0,0 +1,12 @@
+#![feature(never_type)]
+#![deny(uninhabited_static)]
+
+enum Void {}
+extern {
+    static VOID: Void; //~ ERROR static of uninhabited type
+    //~| WARN: previously accepted
+    static NEVER: !; //~ ERROR static of uninhabited type
+    //~| WARN: previously accepted
+}
+
+fn main() {}
diff --git a/src/test/ui/statics/uninhabited-static.stderr b/src/test/ui/statics/uninhabited-static.stderr
new file mode 100644
index 00000000000..475578b3e7c
--- /dev/null
+++ b/src/test/ui/statics/uninhabited-static.stderr
@@ -0,0 +1,27 @@
+error: static of uninhabited type
+  --> $DIR/uninhabited-static.rs:6:5
+   |
+LL |     static VOID: Void;
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/uninhabited-static.rs:2:9
+   |
+LL | #![deny(uninhabited_static)]
+   |         ^^^^^^^^^^^^^^^^^^
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #74840 <https://github.com/rust-lang/rust/issues/74840>
+   = note: uninhabited statics cannot be initialized, and any access would be an immediate error
+
+error: static of uninhabited type
+  --> $DIR/uninhabited-static.rs:8:5
+   |
+LL |     static NEVER: !;
+   |     ^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #74840 <https://github.com/rust-lang/rust/issues/74840>
+   = note: uninhabited statics cannot be initialized, and any access would be an immediate error
+
+error: aborting due to 2 previous errors
+