Skip to content

Commit 1ee0400

Browse files
Do not coerce places if they do not constitute reads
1 parent ae30eaa commit 1ee0400

File tree

5 files changed

+88
-36
lines changed

5 files changed

+88
-36
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

+14-7
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ struct Coerce<'a, 'tcx> {
8181
/// See #47489 and #48598
8282
/// See docs on the "AllowTwoPhase" type for a more detailed discussion
8383
allow_two_phase: AllowTwoPhase,
84+
coerce_never: bool,
8485
}
8586

8687
impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> {
@@ -124,8 +125,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
124125
fcx: &'f FnCtxt<'f, 'tcx>,
125126
cause: ObligationCause<'tcx>,
126127
allow_two_phase: AllowTwoPhase,
128+
coerce_never: bool,
127129
) -> Self {
128-
Coerce { fcx, cause, allow_two_phase, use_lub: false }
130+
Coerce { fcx, cause, allow_two_phase, use_lub: false, coerce_never }
129131
}
130132

131133
fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
@@ -176,7 +178,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
176178

177179
// Coercing from `!` to any type is allowed:
178180
if a.is_never() {
179-
return success(simple(Adjust::NeverToAny)(b), b, vec![]);
181+
if self.coerce_never {
182+
return success(simple(Adjust::NeverToAny)(b), b, vec![]);
183+
} else {
184+
return self.unify_and(a, b, identity);
185+
}
180186
}
181187

182188
// Coercing *from* an unresolved inference variable means that
@@ -978,7 +984,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
978984
/// The expressions *must not* have any preexisting adjustments.
979985
pub fn coerce(
980986
&self,
981-
expr: &hir::Expr<'_>,
987+
expr: &'tcx hir::Expr<'tcx>,
982988
expr_ty: Ty<'tcx>,
983989
mut target: Ty<'tcx>,
984990
allow_two_phase: AllowTwoPhase,
@@ -995,7 +1001,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9951001

9961002
let cause =
9971003
cause.unwrap_or_else(|| self.cause(expr.span, ObligationCauseCode::ExprAssignable));
998-
let coerce = Coerce::new(self, cause, allow_two_phase);
1004+
let coerce =
1005+
Coerce::new(self, cause, allow_two_phase, self.expr_is_rvalue_for_divergence(expr));
9991006
let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?;
10001007

10011008
let (adjustments, _) = self.register_infer_ok_obligations(ok);
@@ -1018,7 +1025,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10181025

10191026
let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable);
10201027
// We don't ever need two-phase here since we throw out the result of the coercion
1021-
let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
1028+
let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true);
10221029
self.probe(|_| {
10231030
let Ok(ok) = coerce.coerce(source, target) else {
10241031
return false;
@@ -1035,7 +1042,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10351042
pub fn deref_steps(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> Option<usize> {
10361043
let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable);
10371044
// We don't ever need two-phase here since we throw out the result of the coercion
1038-
let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
1045+
let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true);
10391046
coerce
10401047
.autoderef(DUMMY_SP, expr_ty)
10411048
.find_map(|(ty, steps)| self.probe(|_| coerce.unify(ty, target)).ok().map(|_| steps))
@@ -1192,7 +1199,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11921199
// probably aren't processing function arguments here and even if we were,
11931200
// they're going to get autorefed again anyway and we can apply 2-phase borrows
11941201
// at that time.
1195-
let mut coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No);
1202+
let mut coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No, true);
11961203
coerce.use_lub = true;
11971204

11981205
// First try to coerce the new expression to the type of the previous ones,

compiler/rustc_hir_typeck/src/expr.rs

+67-23
Original file line numberDiff line numberDiff line change
@@ -239,29 +239,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
239239

240240
// Any expression that produces a value of type `!` must have diverged,
241241
// unless it's the place of a raw ref expr, or a scrutinee of a match.
242-
if ty.is_never() {
243-
if matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Deref, _)) {
244-
match self.tcx.parent_hir_node(expr.hir_id) {
245-
hir::Node::Expr(hir::Expr {
246-
kind: hir::ExprKind::AddrOf(hir::BorrowKind::Raw, ..),
247-
..
248-
}) => {}
249-
hir::Node::Expr(hir::Expr {
250-
kind: hir::ExprKind::Let(hir::LetExpr { init: target, .. }),
251-
..
252-
})
253-
| hir::Node::Expr(hir::Expr {
254-
kind: hir::ExprKind::Match(target, _, _), ..
255-
})
256-
| hir::Node::LetStmt(hir::LetStmt { init: Some(target), .. })
257-
if expr.hir_id == target.hir_id => {}
258-
_ => {
259-
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
260-
}
261-
}
262-
} else {
263-
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
264-
}
242+
if ty.is_never() && self.expr_is_rvalue_for_divergence(expr) {
243+
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
265244
}
266245

267246
// Record the type, which applies it effects.
@@ -278,6 +257,71 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
278257
ty
279258
}
280259

260+
// FIXME: built-in indexing should be supported here.
261+
/// THIS IS SUBTLE BUT I DONT WANT TO EXPLAIN IT YET.
262+
pub(super) fn expr_is_rvalue_for_divergence(&self, expr: &'tcx hir::Expr<'tcx>) -> bool {
263+
match expr.kind {
264+
ExprKind::Path(QPath::Resolved(
265+
_,
266+
hir::Path {
267+
res: Res::Local(..) | Res::Def(DefKind::Static { .. }, _) | Res::Err,
268+
..
269+
},
270+
))
271+
| ExprKind::Unary(hir::UnOp::Deref, _)
272+
| ExprKind::Field(..)
273+
| ExprKind::Index(..) => {
274+
// All places.
275+
}
276+
277+
_ => return true,
278+
}
279+
280+
// If this expression has any adjustments, they may constitute reads.
281+
if !self.typeck_results.borrow().expr_adjustments(expr).is_empty() {
282+
return true;
283+
}
284+
285+
fn pat_does_read(pat: &hir::Pat<'_>) -> bool {
286+
let mut does_read = false;
287+
pat.walk(|pat| {
288+
if matches!(
289+
pat.kind,
290+
hir::PatKind::Wild | hir::PatKind::Never | hir::PatKind::Or(_)
291+
) {
292+
true
293+
} else {
294+
does_read = true;
295+
false
296+
}
297+
});
298+
does_read
299+
}
300+
301+
match self.tcx.parent_hir_node(expr.hir_id) {
302+
hir::Node::Expr(hir::Expr {
303+
kind: hir::ExprKind::AddrOf(hir::BorrowKind::Raw, ..),
304+
..
305+
}) => false,
306+
hir::Node::Expr(hir::Expr {
307+
kind: hir::ExprKind::Assign(target, _, _) | hir::ExprKind::Field(target, _),
308+
..
309+
}) if expr.hir_id == target.hir_id => false,
310+
hir::Node::LetStmt(hir::LetStmt { init: Some(target), pat, .. })
311+
| hir::Node::Expr(hir::Expr {
312+
kind: hir::ExprKind::Let(hir::LetExpr { init: target, pat, .. }),
313+
..
314+
}) if expr.hir_id == target.hir_id && !pat_does_read(*pat) => false,
315+
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Match(target, arms, _), .. })
316+
if expr.hir_id == target.hir_id
317+
&& !arms.iter().any(|arm| pat_does_read(arm.pat)) =>
318+
{
319+
false
320+
}
321+
_ => true,
322+
}
323+
}
324+
281325
#[instrument(skip(self, expr), level = "debug")]
282326
fn check_expr_kind(
283327
&self,

compiler/rustc_hir_typeck/src/pat.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
279279

280280
// All other patterns constitute a read, which causes us to diverge
281281
// if the type is never.
282-
if !matches!(pat.kind, PatKind::Wild | PatKind::Never) {
282+
if !matches!(pat.kind, PatKind::Wild | PatKind::Never | PatKind::Or(_)) {
283283
if ty.is_never() {
284284
self.diverges.set(self.diverges.get() | Diverges::always(pat.span));
285285
}

tests/ui/reachable/expr_assign.stderr

+5-4
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ LL | #![deny(unreachable_code)]
1414
| ^^^^^^^^^^^^^^^^
1515

1616
error: unreachable expression
17-
--> $DIR/expr_assign.rs:20:14
17+
--> $DIR/expr_assign.rs:20:9
1818
|
1919
LL | *p = return;
20-
| -- ^^^^^^ unreachable expression
21-
| |
22-
| any code following this expression is unreachable
20+
| ^^^^^------
21+
| | |
22+
| | any code following this expression is unreachable
23+
| unreachable expression
2324

2425
error: unreachable expression
2526
--> $DIR/expr_assign.rs:26:15

tests/ui/reachable/unwarned-match-on-never.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: unreachable expression
22
--> $DIR/unwarned-match-on-never.rs:10:5
33
|
44
LL | match x {}
5-
| - any code following this expression is unreachable
5+
| ---------- any code following this expression is unreachable
66
LL | // But matches in unreachable code are warned.
77
LL | match x {}
88
| ^^^^^^^^^^ unreachable expression

0 commit comments

Comments
 (0)