Skip to content

Commit 99c3257

Browse files
Do if-expression obligation stuff less eagerly
1 parent 3d9dd68 commit 99c3257

File tree

11 files changed

+411
-350
lines changed

11 files changed

+411
-350
lines changed

compiler/rustc_hir/src/hir.rs

+10
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,16 @@ pub struct Block<'hir> {
954954
pub targeted_by_break: bool,
955955
}
956956

957+
impl<'hir> Block<'hir> {
958+
pub fn peel_blocks(&self) -> &Block<'hir> {
959+
let mut block = self;
960+
while let Some(Expr { kind: ExprKind::Block(inner_block, _), .. }) = block.expr {
961+
block = inner_block;
962+
}
963+
block
964+
}
965+
}
966+
957967
#[derive(Debug, HashStable_Generic)]
958968
pub struct Pat<'hir> {
959969
#[stable_hasher(ignore)]

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+297-38
Large diffs are not rendered by default.

compiler/rustc_middle/src/traits/mod.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ pub enum ObligationCauseCode<'tcx> {
351351
ConstPatternStructural,
352352

353353
/// Computing common supertype in an if expression
354-
IfExpression(Box<IfExpressionCause>),
354+
IfExpression(Box<IfExpressionCause<'tcx>>),
355355

356356
/// Computing common supertype of an if expression with no else counter-part
357357
IfExpressionWithNoElse,
@@ -498,12 +498,14 @@ pub struct MatchExpressionArmCause<'tcx> {
498498
pub opt_suggest_box_span: Option<Span>,
499499
}
500500

501-
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
502-
pub struct IfExpressionCause {
503-
pub then: Span,
504-
pub else_sp: Span,
505-
pub outer: Option<Span>,
506-
pub semicolon: Option<(Span, StatementAsExpression)>,
501+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
502+
#[derive(Lift, TypeFoldable, TypeVisitable)]
503+
pub struct IfExpressionCause<'tcx> {
504+
pub then_id: hir::HirId,
505+
pub else_id: hir::HirId,
506+
pub then_ty: Ty<'tcx>,
507+
pub else_ty: Ty<'tcx>,
508+
pub outer_span: Option<Span>,
507509
pub opt_suggest_box_span: Option<Span>,
508510
}
509511

compiler/rustc_middle/src/traits/structural_impls.rs

-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceConstDestructData<N> {
130130
// Lift implementations
131131

132132
TrivialTypeTraversalAndLiftImpls! {
133-
super::IfExpressionCause,
134133
super::ImplSourceDiscriminantKindData,
135134
super::ImplSourcePointeeData,
136135
}

compiler/rustc_typeck/src/check/_match.rs

+55-69
Original file line numberDiff line numberDiff line change
@@ -216,13 +216,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
216216
) -> (Span, Option<(Span, StatementAsExpression)>) {
217217
let arm = &arms[i];
218218
let (arm_span, mut semi_span) = if let hir::ExprKind::Block(blk, _) = &arm.body.kind {
219-
self.find_block_span(blk, prior_arm_ty)
219+
(
220+
self.find_block_span(blk),
221+
prior_arm_ty
222+
.and_then(|prior_arm_ty| self.could_remove_semicolon(blk, prior_arm_ty)),
223+
)
220224
} else {
221225
(arm.body.span, None)
222226
};
223227
if semi_span.is_none() && i > 0 {
224228
if let hir::ExprKind::Block(blk, _) = &arms[i - 1].body.kind {
225-
let (_, semi_span_prev) = self.find_block_span(blk, Some(arm_ty));
229+
let semi_span_prev = self.could_remove_semicolon(blk, arm_ty);
226230
semi_span = semi_span_prev;
227231
}
228232
}
@@ -313,7 +317,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
313317
else_ty: Ty<'tcx>,
314318
opt_suggest_box_span: Option<Span>,
315319
) -> ObligationCause<'tcx> {
316-
let mut outer_sp = if self.tcx.sess.source_map().is_multiline(span) {
320+
let mut outer_span = if self.tcx.sess.source_map().is_multiline(span) {
317321
// The `if`/`else` isn't in one line in the output, include some context to make it
318322
// clear it is an if/else expression:
319323
// ```
@@ -339,69 +343,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
339343
None
340344
};
341345

342-
let mut remove_semicolon = None;
343-
let error_sp = if let ExprKind::Block(block, _) = &else_expr.kind {
344-
let (error_sp, semi_sp) = self.find_block_span(block, Some(then_ty));
345-
remove_semicolon = semi_sp;
346-
if block.expr.is_none() && block.stmts.is_empty() {
347-
// Avoid overlapping spans that aren't as readable:
348-
// ```
349-
// 2 | let x = if true {
350-
// | _____________-
351-
// 3 | | 3
352-
// | | - expected because of this
353-
// 4 | | } else {
354-
// | |____________^
355-
// 5 | ||
356-
// 6 | || };
357-
// | || ^
358-
// | ||_____|
359-
// | |______if and else have incompatible types
360-
// | expected integer, found `()`
361-
// ```
362-
// by not pointing at the entire expression:
363-
// ```
364-
// 2 | let x = if true {
365-
// | ------- `if` and `else` have incompatible types
366-
// 3 | 3
367-
// | - expected because of this
368-
// 4 | } else {
369-
// | ____________^
370-
// 5 | |
371-
// 6 | | };
372-
// | |_____^ expected integer, found `()`
373-
// ```
374-
if outer_sp.is_some() {
375-
outer_sp = Some(self.tcx.sess.source_map().guess_head_span(span));
376-
}
346+
let (error_sp, else_id) = if let ExprKind::Block(block, _) = &else_expr.kind {
347+
let block = block.peel_blocks();
348+
349+
// Avoid overlapping spans that aren't as readable:
350+
// ```
351+
// 2 | let x = if true {
352+
// | _____________-
353+
// 3 | | 3
354+
// | | - expected because of this
355+
// 4 | | } else {
356+
// | |____________^
357+
// 5 | ||
358+
// 6 | || };
359+
// | || ^
360+
// | ||_____|
361+
// | |______if and else have incompatible types
362+
// | expected integer, found `()`
363+
// ```
364+
// by not pointing at the entire expression:
365+
// ```
366+
// 2 | let x = if true {
367+
// | ------- `if` and `else` have incompatible types
368+
// 3 | 3
369+
// | - expected because of this
370+
// 4 | } else {
371+
// | ____________^
372+
// 5 | |
373+
// 6 | | };
374+
// | |_____^ expected integer, found `()`
375+
// ```
376+
if block.expr.is_none() && block.stmts.is_empty()
377+
&& let Some(outer_span) = &mut outer_span
378+
{
379+
*outer_span = self.tcx.sess.source_map().guess_head_span(*outer_span);
377380
}
378-
error_sp
381+
382+
(self.find_block_span(block), block.hir_id)
379383
} else {
380-
// shouldn't happen unless the parser has done something weird
381-
else_expr.span
384+
(else_expr.span, else_expr.hir_id)
382385
};
383386

384-
// Compute `Span` of `then` part of `if`-expression.
385-
let then_sp = if let ExprKind::Block(block, _) = &then_expr.kind {
386-
let (then_sp, semi_sp) = self.find_block_span(block, Some(else_ty));
387-
remove_semicolon = remove_semicolon.or(semi_sp);
387+
let then_id = if let ExprKind::Block(block, _) = &then_expr.kind {
388+
let block = block.peel_blocks();
389+
// Exclude overlapping spans
388390
if block.expr.is_none() && block.stmts.is_empty() {
389-
outer_sp = None; // same as in `error_sp`; cleanup output
391+
outer_span = None;
390392
}
391-
then_sp
393+
block.hir_id
392394
} else {
393-
// shouldn't happen unless the parser has done something weird
394-
then_expr.span
395+
then_expr.hir_id
395396
};
396397

397398
// Finally construct the cause:
398399
self.cause(
399400
error_sp,
400401
ObligationCauseCode::IfExpression(Box::new(IfExpressionCause {
401-
then: then_sp,
402-
else_sp: error_sp,
403-
outer: outer_sp,
404-
semicolon: remove_semicolon,
402+
else_id,
403+
then_id,
404+
then_ty,
405+
else_ty,
406+
outer_span,
405407
opt_suggest_box_span,
406408
})),
407409
)
@@ -482,22 +484,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
482484
}
483485
}
484486

485-
fn find_block_span(
486-
&self,
487-
block: &'tcx hir::Block<'tcx>,
488-
expected_ty: Option<Ty<'tcx>>,
489-
) -> (Span, Option<(Span, StatementAsExpression)>) {
490-
if let Some(expr) = &block.expr {
491-
(expr.span, None)
492-
} else if let Some(stmt) = block.stmts.last() {
493-
// possibly incorrect trailing `;` in the else arm
494-
(stmt.span, expected_ty.and_then(|ty| self.could_remove_semicolon(block, ty)))
495-
} else {
496-
// empty block; point at its entirety
497-
(block.span, None)
498-
}
499-
}
500-
501487
// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
502488
// we check if the different arms would work with boxed trait objects instead and
503489
// provide a structured suggestion in that case.

compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs

+2-82
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,15 @@ use rustc_middle::ty::{
3030
};
3131
use rustc_session::lint;
3232
use rustc_span::hygiene::DesugaringKind;
33-
use rustc_span::source_map::{original_sp, DUMMY_SP};
3433
use rustc_span::symbol::{kw, sym, Ident};
35-
use rustc_span::{self, BytePos, Span};
34+
use rustc_span::{Span, DUMMY_SP};
3635
use rustc_trait_selection::infer::InferCtxtExt as _;
3736
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
3837
use rustc_trait_selection::traits::{
39-
self, ObligationCause, ObligationCauseCode, StatementAsExpression, TraitEngine, TraitEngineExt,
38+
self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt,
4039
};
4140

4241
use std::collections::hash_map::Entry;
43-
use std::iter;
4442
use std::slice;
4543

4644
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -1059,84 +1057,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10591057
));
10601058
}
10611059

1062-
pub(in super::super) fn could_remove_semicolon(
1063-
&self,
1064-
blk: &'tcx hir::Block<'tcx>,
1065-
expected_ty: Ty<'tcx>,
1066-
) -> Option<(Span, StatementAsExpression)> {
1067-
// Be helpful when the user wrote `{... expr;}` and
1068-
// taking the `;` off is enough to fix the error.
1069-
let last_stmt = blk.stmts.last()?;
1070-
let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
1071-
return None;
1072-
};
1073-
let last_expr_ty = self.node_ty(last_expr.hir_id);
1074-
let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
1075-
(ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _))
1076-
if last_def_id == exp_def_id =>
1077-
{
1078-
StatementAsExpression::CorrectType
1079-
}
1080-
(ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
1081-
debug!(
1082-
"both opaque, likely future {:?} {:?} {:?} {:?}",
1083-
last_def_id, last_bounds, exp_def_id, exp_bounds
1084-
);
1085-
1086-
let last_local_id = last_def_id.as_local()?;
1087-
let exp_local_id = exp_def_id.as_local()?;
1088-
1089-
match (
1090-
&self.tcx.hir().expect_item(last_local_id).kind,
1091-
&self.tcx.hir().expect_item(exp_local_id).kind,
1092-
) {
1093-
(
1094-
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
1095-
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
1096-
) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
1097-
match (left, right) {
1098-
(
1099-
hir::GenericBound::Trait(tl, ml),
1100-
hir::GenericBound::Trait(tr, mr),
1101-
) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
1102-
&& ml == mr =>
1103-
{
1104-
true
1105-
}
1106-
(
1107-
hir::GenericBound::LangItemTrait(langl, _, _, argsl),
1108-
hir::GenericBound::LangItemTrait(langr, _, _, argsr),
1109-
) if langl == langr => {
1110-
// FIXME: consider the bounds!
1111-
debug!("{:?} {:?}", argsl, argsr);
1112-
true
1113-
}
1114-
_ => false,
1115-
}
1116-
}) =>
1117-
{
1118-
StatementAsExpression::NeedsBoxing
1119-
}
1120-
_ => StatementAsExpression::CorrectType,
1121-
}
1122-
}
1123-
_ => StatementAsExpression::CorrectType,
1124-
};
1125-
if (matches!(last_expr_ty.kind(), ty::Error(_))
1126-
|| self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err())
1127-
&& matches!(needs_box, StatementAsExpression::CorrectType)
1128-
{
1129-
return None;
1130-
}
1131-
let span = if last_stmt.span.from_expansion() {
1132-
let mac_call = original_sp(last_stmt.span, blk.span);
1133-
self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
1134-
} else {
1135-
last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
1136-
};
1137-
Some((span, needs_box))
1138-
}
1139-
11401060
// Instantiates the given path, which must refer to an item with the given
11411061
// number of type parameters and type.
11421062
#[instrument(skip(self, span), level = "debug")]

0 commit comments

Comments
 (0)