Skip to content

Commit b9a94c9

Browse files
committed
Auto merge of #78324 - RalfJung:uninhabited-statics, r=oli-obk
ensure that statics are inhabited Fixes #74840 r? `@oli-obk`
2 parents 69e68cf + 3bd5cc9 commit b9a94c9

File tree

4 files changed

+139
-6
lines changed

4 files changed

+139
-6
lines changed

compiler/rustc_session/src/lint/builtin.rs

+30
Original file line numberDiff line numberDiff line change
@@ -2647,6 +2647,35 @@ declare_lint! {
26472647
};
26482648
}
26492649

2650+
declare_lint! {
2651+
/// The `uninhabited_static` lint detects uninhbaited statics.
2652+
///
2653+
/// ### Example
2654+
///
2655+
/// ```rust
2656+
/// enum Void {}
2657+
/// extern {
2658+
/// static EXTERN: Void;
2659+
/// }
2660+
/// ```
2661+
///
2662+
/// {{produces}}
2663+
///
2664+
/// ### Explanation
2665+
///
2666+
/// Statics with an uninhabited type can never be initialized, so they are impossible to define.
2667+
/// However, this can be side-stepped with an `extern static`, leading to problems later in the
2668+
/// compiler which assumes that there are no initialized uninhabited places (such as locals or
2669+
/// statics). This was accientally allowed, but is being phased out.
2670+
pub UNINHABITED_STATIC,
2671+
Warn,
2672+
"uninhabited static",
2673+
@future_incompatible = FutureIncompatibleInfo {
2674+
reference: "issue #74840 <https://github.com/rust-lang/rust/issues/74840>",
2675+
edition: None,
2676+
};
2677+
}
2678+
26502679
declare_tool_lint! {
26512680
pub rustc::INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
26522681
Deny,
@@ -2732,6 +2761,7 @@ declare_lint_pass! {
27322761
CENUM_IMPL_DROP_CAST,
27332762
CONST_EVALUATABLE_UNCHECKED,
27342763
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
2764+
UNINHABITED_STATIC,
27352765
]
27362766
}
27372767

compiler/rustc_typeck/src/check/check.rs

+45-6
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
1414
use rustc_middle::ty::fold::TypeFoldable;
1515
use rustc_middle::ty::subst::GenericArgKind;
1616
use rustc_middle::ty::util::{Discr, IntTypeExt, Representability};
17-
use rustc_middle::ty::{self, RegionKind, ToPredicate, Ty, TyCtxt};
17+
use rustc_middle::ty::{self, ParamEnv, RegionKind, ToPredicate, Ty, TyCtxt};
1818
use rustc_session::config::EntryFnType;
19+
use rustc_session::lint::builtin::UNINHABITED_STATIC;
1920
use rustc_span::symbol::sym;
2021
use rustc_span::{self, MultiSpan, Span};
2122
use rustc_target::spec::abi::Abi;
@@ -338,7 +339,7 @@ pub(super) fn check_struct(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
338339
check_packed(tcx, span, def);
339340
}
340341

341-
pub(super) fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
342+
fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
342343
let def_id = tcx.hir().local_def_id(id);
343344
let def = tcx.adt_def(def_id);
344345
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) {
349350
}
350351

351352
/// Check that the fields of the `union` do not need dropping.
352-
pub(super) fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool {
353+
fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool {
353354
let item_type = tcx.type_of(item_def_id);
354355
if let ty::Adt(def, substs) = item_type.kind() {
355356
assert!(def.is_union());
@@ -377,6 +378,36 @@ pub(super) fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: Local
377378
true
378379
}
379380

381+
/// Check that a `static` is inhabited.
382+
fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) {
383+
// Make sure statics are inhabited.
384+
// Other parts of the compiler assume that there are no uninhabited places. In principle it
385+
// would be enough to check this for `extern` statics, as statics with an initializer will
386+
// have UB during initialization if they are uninhabited, but there also seems to be no good
387+
// reason to allow any statics to be uninhabited.
388+
let ty = tcx.type_of(def_id);
389+
let layout = match tcx.layout_of(ParamEnv::reveal_all().and(ty)) {
390+
Ok(l) => l,
391+
Err(_) => {
392+
// Generic statics are rejected, but we still reach this case.
393+
tcx.sess.delay_span_bug(span, "generic static must be rejected");
394+
return;
395+
}
396+
};
397+
if layout.abi.is_uninhabited() {
398+
tcx.struct_span_lint_hir(
399+
UNINHABITED_STATIC,
400+
tcx.hir().local_def_id_to_hir_id(def_id),
401+
span,
402+
|lint| {
403+
lint.build("static of uninhabited type")
404+
.note("uninhabited statics cannot be initialized, and any access would be an immediate error")
405+
.emit();
406+
},
407+
);
408+
}
409+
}
410+
380411
/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo`
381412
/// projections that would result in "inheriting lifetimes".
382413
pub(super) fn check_opaque<'tcx>(
@@ -609,6 +640,7 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) {
609640
let def_id = tcx.hir().local_def_id(it.hir_id);
610641
tcx.ensure().typeck(def_id);
611642
maybe_check_static_with_link_section(tcx, def_id, it.span);
643+
check_static_inhabited(tcx, def_id, it.span);
612644
}
613645
hir::ItemKind::Const(..) => {
614646
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>) {
691723
}
692724
} else {
693725
for item in m.items {
694-
let generics = tcx.generics_of(tcx.hir().local_def_id(item.hir_id));
726+
let def_id = tcx.hir().local_def_id(item.hir_id);
727+
let generics = tcx.generics_of(def_id);
695728
let own_counts = generics.own_counts();
696729
if generics.params.len() - own_counts.lifetimes != 0 {
697730
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>) {
722755
.emit();
723756
}
724757

725-
if let hir::ForeignItemKind::Fn(ref fn_decl, _, _) = item.kind {
726-
require_c_abi_if_c_variadic(tcx, fn_decl, m.abi, item.span);
758+
match item.kind {
759+
hir::ForeignItemKind::Fn(ref fn_decl, _, _) => {
760+
require_c_abi_if_c_variadic(tcx, fn_decl, m.abi, item.span);
761+
}
762+
hir::ForeignItemKind::Static(..) => {
763+
check_static_inhabited(tcx, def_id, item.span);
764+
}
765+
_ => {}
727766
}
728767
}
729768
}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![feature(never_type)]
2+
#![deny(uninhabited_static)]
3+
4+
enum Void {}
5+
extern {
6+
static VOID: Void; //~ ERROR static of uninhabited type
7+
//~| WARN: previously accepted
8+
static NEVER: !; //~ ERROR static of uninhabited type
9+
//~| WARN: previously accepted
10+
}
11+
12+
static VOID2: Void = unsafe { std::mem::transmute(()) }; //~ ERROR static of uninhabited type
13+
//~| WARN: previously accepted
14+
static NEVER2: Void = unsafe { std::mem::transmute(()) }; //~ ERROR static of uninhabited type
15+
//~| WARN: previously accepted
16+
17+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
error: static of uninhabited type
2+
--> $DIR/uninhabited-static.rs:6:5
3+
|
4+
LL | static VOID: Void;
5+
| ^^^^^^^^^^^^^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/uninhabited-static.rs:2:9
9+
|
10+
LL | #![deny(uninhabited_static)]
11+
| ^^^^^^^^^^^^^^^^^^
12+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
13+
= note: for more information, see issue #74840 <https://github.com/rust-lang/rust/issues/74840>
14+
= note: uninhabited statics cannot be initialized, and any access would be an immediate error
15+
16+
error: static of uninhabited type
17+
--> $DIR/uninhabited-static.rs:8:5
18+
|
19+
LL | static NEVER: !;
20+
| ^^^^^^^^^^^^^^^^
21+
|
22+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
23+
= note: for more information, see issue #74840 <https://github.com/rust-lang/rust/issues/74840>
24+
= note: uninhabited statics cannot be initialized, and any access would be an immediate error
25+
26+
error: static of uninhabited type
27+
--> $DIR/uninhabited-static.rs:12:1
28+
|
29+
LL | static VOID2: Void = unsafe { std::mem::transmute(()) };
30+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
31+
|
32+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
33+
= note: for more information, see issue #74840 <https://github.com/rust-lang/rust/issues/74840>
34+
= note: uninhabited statics cannot be initialized, and any access would be an immediate error
35+
36+
error: static of uninhabited type
37+
--> $DIR/uninhabited-static.rs:14:1
38+
|
39+
LL | static NEVER2: Void = unsafe { std::mem::transmute(()) };
40+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
41+
|
42+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
43+
= note: for more information, see issue #74840 <https://github.com/rust-lang/rust/issues/74840>
44+
= note: uninhabited statics cannot be initialized, and any access would be an immediate error
45+
46+
error: aborting due to 4 previous errors
47+

0 commit comments

Comments
 (0)