Skip to content

Commit 4397ee2

Browse files
camsteffencjgillot
authored andcommitted
Check for uninhabited types in typeck
1 parent 6e3f9e7 commit 4397ee2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+586
-238
lines changed

compiler/rustc_hir_typeck/src/diverges.rs

+1
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,6 @@ impl Diverges<'_> {
7171
#[derive(Clone, Copy, Debug)]
7272
pub enum DivergeReason {
7373
AllArmsDiverge,
74+
Uninhabited,
7475
Other,
7576
}

compiler/rustc_hir_typeck/src/expr.rs

+73-4
Original file line numberDiff line numberDiff line change
@@ -253,16 +253,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
253253
// diverging expression (e.g. it arose from desugaring of `try { return }`),
254254
// we skip issuing a warning because it is autogenerated code.
255255
ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {}
256-
ExprKind::Call(callee, _) => self.warn_if_unreachable(expr.hir_id, callee.span, "call"),
256+
ExprKind::Call(callee, _) => {
257+
let emit_warning = if let ExprKind::Path(ref qpath) = callee.kind {
258+
// Do not emit a warning for a call to a constructor.
259+
let res = self.typeck_results.borrow().qpath_res(qpath, callee.hir_id);
260+
!matches!(res, Res::Def(DefKind::Ctor(..), _))
261+
} else {
262+
true
263+
};
264+
if emit_warning {
265+
self.warn_if_unreachable(expr.hir_id, callee.span, "call")
266+
}
267+
}
257268
ExprKind::MethodCall(segment, ..) => {
258269
self.warn_if_unreachable(expr.hir_id, segment.ident.span, "call")
259270
}
271+
// allow field access when the struct and the field are both uninhabited
272+
ExprKind::Field(..)
273+
if matches!(
274+
self.diverges.get(),
275+
Diverges::Always(DivergeReason::Uninhabited, _)
276+
) && self.ty_is_uninhabited(ty) => {}
260277
_ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"),
261278
}
262279

263-
// Any expression that produces a value of type `!` must have diverged
264-
if ty.is_never() {
265-
self.diverges.set(self.diverges.get() | Diverges::Always(DivergeReason::Other, expr));
280+
if !self.diverges.get().is_always() {
281+
if ty.is_never() {
282+
// Any expression that produces a value of type `!` must have diverged.
283+
self.diverges.set(Diverges::Always(DivergeReason::Other, expr));
284+
} else if expr_may_be_uninhabited(expr) && self.ty_is_uninhabited(ty) {
285+
// This expression produces a value of uninhabited type.
286+
// This means it has diverged somehow.
287+
self.diverges.set(Diverges::Always(DivergeReason::Uninhabited, expr));
288+
}
266289
}
267290

268291
// Record the type, which applies it effects.
@@ -279,6 +302,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
279302
ty
280303
}
281304

305+
fn ty_is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
306+
let ty = self.resolve_vars_if_possible(ty);
307+
// Freshen the type as `is_inhabited_from` may call a query on `ty`.
308+
let ty = self.freshen(ty);
309+
!ty.is_inhabited_from(self.tcx, self.parent_module, self.param_env)
310+
}
311+
282312
#[instrument(skip(self, expr), level = "debug")]
283313
fn check_expr_kind(
284314
&self,
@@ -3206,3 +3236,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
32063236
self.tcx.types.usize
32073237
}
32083238
}
3239+
3240+
fn expr_may_be_uninhabited(expr: &hir::Expr<'_>) -> bool {
3241+
match expr.kind {
3242+
ExprKind::Call(..)
3243+
| ExprKind::MethodCall(..)
3244+
| ExprKind::Cast(..)
3245+
| ExprKind::Unary(hir::UnOp::Deref, _)
3246+
| ExprKind::Field(..)
3247+
| ExprKind::Path(..)
3248+
| ExprKind::Struct(..) => true,
3249+
ExprKind::ConstBlock(..)
3250+
| ExprKind::Array(..)
3251+
| ExprKind::Tup(..)
3252+
| ExprKind::Binary(..)
3253+
| ExprKind::Unary(hir::UnOp::Neg | hir::UnOp::Not, _)
3254+
| ExprKind::Lit(..)
3255+
| ExprKind::Type(..)
3256+
| ExprKind::DropTemps(..)
3257+
| ExprKind::OffsetOf(..)
3258+
| ExprKind::Let(..)
3259+
| ExprKind::If(..)
3260+
| ExprKind::Loop(..)
3261+
| ExprKind::Match(..)
3262+
| ExprKind::Closure(..)
3263+
| ExprKind::Block(..)
3264+
| ExprKind::Assign(..)
3265+
| ExprKind::AssignOp(..)
3266+
| ExprKind::Index(..)
3267+
| ExprKind::AddrOf(..)
3268+
| ExprKind::Break(..)
3269+
| ExprKind::Continue(..)
3270+
| ExprKind::Ret(..)
3271+
| ExprKind::Become(..)
3272+
| ExprKind::InlineAsm(..)
3273+
| ExprKind::Repeat(..)
3274+
| ExprKind::Yield(..)
3275+
| ExprKind::Err(_) => false,
3276+
}
3277+
}

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+18-4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use rustc_trait_selection::traits::{
4040
self, NormalizeExt, ObligationCauseCode, ObligationCtxt, StructurallyNormalizeExt,
4141
};
4242

43+
use std::borrow::Cow;
4344
use std::collections::hash_map::Entry;
4445
use std::slice;
4546

@@ -62,15 +63,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6263

6364
self.diverges.set(Diverges::WarnedAlways);
6465

66+
if matches!(reason, DivergeReason::Uninhabited) {
67+
if let Some(impl_of) = self.tcx.impl_of_method(self.body_id.to_def_id()) {
68+
if self.tcx.has_attr(impl_of, sym::automatically_derived) {
69+
// Built-in derives are generated before typeck,
70+
// so they may contain unreachable code if there are uninhabited types
71+
return;
72+
}
73+
}
74+
}
75+
6576
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
6677

6778
let msg = format!("unreachable {}", kind);
6879
self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, msg.clone(), |lint| {
6980
let label = match reason {
70-
DivergeReason::AllArmsDiverge => {
71-
"any code following this `match` expression is unreachable, as all arms diverge"
72-
}
73-
DivergeReason::Other => "any code following this expression is unreachable",
81+
DivergeReason::AllArmsDiverge =>
82+
Cow::Borrowed("any code following this `match` expression is unreachable, as all arms diverge"),
83+
DivergeReason::Uninhabited => format!(
84+
"this expression has type `{}`, which is uninhabited",
85+
self.typeck_results.borrow().node_type(diverging_expr.hir_id)
86+
).into(),
87+
DivergeReason::Other => Cow::Borrowed("any code following this expression is unreachable"),
7488
};
7589
lint.span_label(span, msg).span_label(diverging_expr.span, label)
7690
});

compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ pub struct FnCtxt<'a, 'tcx> {
4848
/// eventually).
4949
pub(super) param_env: ty::ParamEnv<'tcx>,
5050

51+
pub(super) parent_module: DefId,
52+
5153
/// Number of errors that had been reported when we started
5254
/// checking this function. On exit, if we find that *more* errors
5355
/// have been reported, we will skip regionck and other work that
@@ -122,6 +124,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
122124
FnCtxt {
123125
body_id,
124126
param_env,
127+
parent_module: inh.tcx.parent_module_from_def_id(body_id).to_def_id(),
125128
err_count_on_creation: inh.tcx.sess.err_count(),
126129
ret_coercion: None,
127130
ret_coercion_span: Cell::new(None),

compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,7 @@ impl<'tcx> InhabitedPredicate<'tcx> {
6262
Some(1..) => Ok(false),
6363
},
6464
Self::NotInModule(id) => in_module(id).map(|in_mod| !in_mod),
65-
// `t` may be a projection, for which `inhabited_predicate` returns a `GenericType`. As
66-
// we have a param_env available, we can do better.
67-
Self::GenericType(t) => {
68-
let normalized_pred = tcx
69-
.try_normalize_erasing_regions(param_env, t)
70-
.map_or(self, |t| t.inhabited_predicate(tcx));
71-
match normalized_pred {
72-
// We don't have more information than we started with, so consider inhabited.
73-
Self::GenericType(_) => Ok(true),
74-
pred => pred.apply_inner(tcx, param_env, in_module),
75-
}
76-
}
65+
Self::GenericType(_) => Ok(true),
7766
Self::And([a, b]) => try_and(a, b, |x| x.apply_inner(tcx, param_env, in_module)),
7867
Self::Or([a, b]) => try_or(a, b, |x| x.apply_inner(tcx, param_env, in_module)),
7968
}

compiler/rustc_passes/src/liveness.rs

+12-57
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ use rustc_hir::intravisit::{self, Visitor};
9595
use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet};
9696
use rustc_index::IndexVec;
9797
use rustc_middle::query::Providers;
98-
use rustc_middle::ty::{self, RootVariableMinCaptureList, Ty, TyCtxt};
98+
use rustc_middle::ty::{self, RootVariableMinCaptureList, TyCtxt};
9999
use rustc_session::lint;
100100
use rustc_span::symbol::{kw, sym, Symbol};
101101
use rustc_span::DUMMY_SP;
@@ -121,8 +121,8 @@ rustc_index::newtype_index! {
121121
#[derive(Copy, Clone, PartialEq, Debug)]
122122
enum LiveNodeKind {
123123
UpvarNode(Span),
124-
ExprNode(Span, HirId),
125-
VarDefNode(Span, HirId),
124+
ExprNode(Span),
125+
VarDefNode(Span),
126126
ClosureNode,
127127
ExitNode,
128128
}
@@ -131,8 +131,8 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
131131
let sm = tcx.sess.source_map();
132132
match lnk {
133133
UpvarNode(s) => format!("Upvar node [{}]", sm.span_to_diagnostic_string(s)),
134-
ExprNode(s, _) => format!("Expr node [{}]", sm.span_to_diagnostic_string(s)),
135-
VarDefNode(s, _) => format!("Var def node [{}]", sm.span_to_diagnostic_string(s)),
134+
ExprNode(s) => format!("Expr node [{}]", sm.span_to_diagnostic_string(s)),
135+
VarDefNode(s) => format!("Var def node [{}]", sm.span_to_diagnostic_string(s)),
136136
ClosureNode => "Closure node".to_owned(),
137137
ExitNode => "Exit node".to_owned(),
138138
}
@@ -354,7 +354,7 @@ impl<'tcx> IrMaps<'tcx> {
354354
let shorthand_field_ids = self.collect_shorthand_field_ids(pat);
355355

356356
pat.each_binding(|_, hir_id, _, ident| {
357-
self.add_live_node_for_node(hir_id, VarDefNode(ident.span, hir_id));
357+
self.add_live_node_for_node(hir_id, VarDefNode(ident.span));
358358
self.add_variable(Local(LocalInfo {
359359
id: hir_id,
360360
name: ident.name,
@@ -368,7 +368,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
368368
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
369369
self.add_from_pat(&local.pat);
370370
if local.els.is_some() {
371-
self.add_live_node_for_node(local.hir_id, ExprNode(local.span, local.hir_id));
371+
self.add_live_node_for_node(local.hir_id, ExprNode(local.span));
372372
}
373373
intravisit::walk_local(self, local);
374374
}
@@ -403,14 +403,14 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
403403
hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
404404
debug!("expr {}: path that leads to {:?}", expr.hir_id, path.res);
405405
if let Res::Local(_var_hir_id) = path.res {
406-
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id));
406+
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
407407
}
408408
intravisit::walk_expr(self, expr);
409409
}
410410
hir::ExprKind::Closure(closure) => {
411411
// Interesting control flow (for loops can contain labeled
412412
// breaks or continues)
413-
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id));
413+
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
414414

415415
// Make a live_node for each mentioned variable, with the span
416416
// being the location that the variable is used. This results
@@ -438,11 +438,11 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
438438
| hir::ExprKind::Match(..)
439439
| hir::ExprKind::Loop(..)
440440
| hir::ExprKind::Yield(..) => {
441-
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id));
441+
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
442442
intravisit::walk_expr(self, expr);
443443
}
444444
hir::ExprKind::Binary(op, ..) if op.node.is_lazy() => {
445-
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id));
445+
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
446446
intravisit::walk_expr(self, expr);
447447
}
448448

@@ -1288,52 +1288,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
12881288
fn check_is_ty_uninhabited(&mut self, expr: &Expr<'_>, succ: LiveNode) -> LiveNode {
12891289
let ty = self.typeck_results.expr_ty(expr);
12901290
let m = self.ir.tcx.parent_module(expr.hir_id).to_def_id();
1291-
if ty.is_inhabited_from(self.ir.tcx, m, self.param_env) {
1292-
return succ;
1293-
}
1294-
match self.ir.lnks[succ] {
1295-
LiveNodeKind::ExprNode(succ_span, succ_id) => {
1296-
self.warn_about_unreachable(expr.span, ty, succ_span, succ_id, "expression");
1297-
}
1298-
LiveNodeKind::VarDefNode(succ_span, succ_id) => {
1299-
self.warn_about_unreachable(expr.span, ty, succ_span, succ_id, "definition");
1300-
}
1301-
_ => {}
1302-
};
1303-
self.exit_ln
1304-
}
1305-
1306-
fn warn_about_unreachable<'desc>(
1307-
&mut self,
1308-
orig_span: Span,
1309-
orig_ty: Ty<'tcx>,
1310-
expr_span: Span,
1311-
expr_id: HirId,
1312-
descr: &'desc str,
1313-
) {
1314-
if !orig_ty.is_never() {
1315-
// Unreachable code warnings are already emitted during type checking.
1316-
// However, during type checking, full type information is being
1317-
// calculated but not yet available, so the check for diverging
1318-
// expressions due to uninhabited result types is pretty crude and
1319-
// only checks whether ty.is_never(). Here, we have full type
1320-
// information available and can issue warnings for less obviously
1321-
// uninhabited types (e.g. empty enums). The check above is used so
1322-
// that we do not emit the same warning twice if the uninhabited type
1323-
// is indeed `!`.
1324-
1325-
self.ir.tcx.emit_spanned_lint(
1326-
lint::builtin::UNREACHABLE_CODE,
1327-
expr_id,
1328-
expr_span,
1329-
errors::UnreachableDueToUninhabited {
1330-
expr: expr_span,
1331-
orig: orig_span,
1332-
descr,
1333-
ty: orig_ty,
1334-
},
1335-
);
1336-
}
1291+
if ty.is_inhabited_from(self.ir.tcx, m, self.param_env) { succ } else { self.exit_ln }
13371292
}
13381293
}
13391294

src/tools/miri/src/shims/unix/dlsym.rs

+2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
4646
match dlsym {
4747
Dlsym::Android(dlsym) =>
4848
android::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
49+
#[allow(unreachable_code)]
4950
Dlsym::FreeBsd(dlsym) =>
5051
freebsd::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
52+
#[allow(unreachable_code)]
5153
Dlsym::Linux(dlsym) => linux::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
5254
Dlsym::MacOs(dlsym) => macos::EvalContextExt::call_dlsym(this, dlsym, args, dest, ret),
5355
}

src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs

+2
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ impl Constructor {
470470
(IntRange(self_range), IntRange(other_range)) => self_range.is_covered_by(other_range),
471471
(FloatRange(void), FloatRange(..)) => match *void {},
472472
(Str(void), Str(..)) => match *void {},
473+
#[allow(unreachable_code)]
473474
(Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice),
474475

475476
// We are trying to inspect an opaque constant. Thus we skip the row.
@@ -502,6 +503,7 @@ impl Constructor {
502503
.iter()
503504
.filter_map(|c| c.as_int_range())
504505
.any(|other| range.is_covered_by(other)),
506+
#[allow(unreachable_code)]
505507
Slice(slice) => used_ctors
506508
.iter()
507509
.filter_map(|c| c.as_slice())

0 commit comments

Comments
 (0)