From 1333206eb32a78ef6ffe0defd4acd164e47146b7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 24 Oct 2020 16:13:39 +0200 Subject: [PATCH 1/3] ensure that statics are inhabited --- compiler/rustc_session/src/lint/builtin.rs | 30 +++++++++++ compiler/rustc_typeck/src/check/check.rs | 51 ++++++++++++++++--- src/test/ui/statics/uninhabited-static.rs | 12 +++++ src/test/ui/statics/uninhabited-static.stderr | 27 ++++++++++ 4 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 src/test/ui/statics/uninhabited-static.rs create mode 100644 src/test/ui/statics/uninhabited-static.stderr diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs index fef3164de59be..d12eb0edd4d39 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 ", + 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 3d8653b4a6a47..fb6746c85d3e4 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 0000000000000..61189b0e0cd2a --- /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 0000000000000..475578b3e7c76 --- /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 + = 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 + = note: uninhabited statics cannot be initialized, and any access would be an immediate error + +error: aborting due to 2 previous errors + From 5d624929cf194fafdb5c6a49f0bf6304010ef5ba Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 24 Oct 2020 20:39:04 +0200 Subject: [PATCH 2/3] fix typo Co-authored-by: BlackHoleFox --- compiler/rustc_typeck/src/check/check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index fb6746c85d3e4..8f2537404c5cc 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -382,7 +382,7 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b 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 + // would be enough 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); From 3bd5cc900c55722c38cf5aa51793cc133c8d741a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 25 Oct 2020 15:01:32 +0100 Subject: [PATCH 3/3] also test non-extern uninhabited statics --- src/test/ui/statics/uninhabited-static.rs | 5 +++++ src/test/ui/statics/uninhabited-static.stderr | 22 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/test/ui/statics/uninhabited-static.rs b/src/test/ui/statics/uninhabited-static.rs index 61189b0e0cd2a..cc78f6cfa53f7 100644 --- a/src/test/ui/statics/uninhabited-static.rs +++ b/src/test/ui/statics/uninhabited-static.rs @@ -9,4 +9,9 @@ extern { //~| WARN: previously accepted } +static VOID2: Void = unsafe { std::mem::transmute(()) }; //~ ERROR static of uninhabited type +//~| WARN: previously accepted +static NEVER2: Void = unsafe { std::mem::transmute(()) }; //~ 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 index 475578b3e7c76..5d95b29993827 100644 --- a/src/test/ui/statics/uninhabited-static.stderr +++ b/src/test/ui/statics/uninhabited-static.stderr @@ -23,5 +23,25 @@ LL | static NEVER: !; = note: for more information, see issue #74840 = note: uninhabited statics cannot be initialized, and any access would be an immediate error -error: aborting due to 2 previous errors +error: static of uninhabited type + --> $DIR/uninhabited-static.rs:12:1 + | +LL | static VOID2: Void = unsafe { std::mem::transmute(()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 + = note: uninhabited statics cannot be initialized, and any access would be an immediate error + +error: static of uninhabited type + --> $DIR/uninhabited-static.rs:14:1 + | +LL | static NEVER2: Void = unsafe { std::mem::transmute(()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 + = note: uninhabited statics cannot be initialized, and any access would be an immediate error + +error: aborting due to 4 previous errors