Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 2fe936f

Browse files
Stop doing expensive work in opt_suggest_box_span eagerly
1 parent 10a7aa1 commit 2fe936f

File tree

11 files changed

+217
-191
lines changed

11 files changed

+217
-191
lines changed

‎compiler/rustc_hir_typeck/messages.ftl‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ hir_typeck_return_stmt_outside_of_fn_body =
122122
.encl_body_label = the {$statement_kind} is part of this body...
123123
.encl_fn_label = ...not the enclosing function body
124124
125+
hir_typeck_rpit_box_return_expr = if you change the return type to expect trait objects, box the returned expressions
126+
127+
hir_typeck_rpit_change_return_type = you could change the return type to be a boxed trait object
128+
125129
hir_typeck_rustcall_incorrect_args =
126130
functions with the "rust-call" ABI must take a single non-self tuple argument
127131

‎compiler/rustc_hir_typeck/src/_match.rs‎

Lines changed: 37 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
use crate::coercion::{AsCoercionSite, CoerceMany};
22
use crate::{Diverges, Expectation, FnCtxt, Needs};
33
use rustc_errors::{Applicability, Diag};
4-
use rustc_hir::{
5-
self as hir,
6-
def::{CtorOf, DefKind, Res},
7-
ExprKind, PatKind,
8-
};
4+
use rustc_hir::def::{CtorOf, DefKind, Res};
5+
use rustc_hir::def_id::LocalDefId;
6+
use rustc_hir::{self as hir, ExprKind, PatKind};
97
use rustc_hir_pretty::ty_to_string;
108
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
11-
use rustc_infer::traits::Obligation;
129
use rustc_middle::ty::{self, Ty};
1310
use rustc_span::Span;
14-
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
1511
use rustc_trait_selection::traits::{
1612
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
1713
};
@@ -91,10 +87,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9187

9288
let arm_ty = self.check_expr_with_expectation(arm.body, expected);
9389
all_arms_diverge &= self.diverges.get();
94-
95-
let opt_suggest_box_span = prior_arm.and_then(|(_, prior_arm_ty, _)| {
96-
self.opt_suggest_box_span(prior_arm_ty, arm_ty, orig_expected)
97-
});
90+
let tail_defines_return_position_impl_trait =
91+
self.return_position_impl_trait_from_match_expectation(orig_expected);
9892

9993
let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind {
10094
(Some(blk.hir_id), self.find_block_span(blk))
@@ -120,7 +114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
120114
scrut_span: scrut.span,
121115
source: match_src,
122116
prior_non_diverging_arms: prior_non_diverging_arms.clone(),
123-
opt_suggest_box_span,
117+
tail_defines_return_position_impl_trait,
124118
})),
125119
),
126120
};
@@ -422,7 +416,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
422416
else_expr: &'tcx hir::Expr<'tcx>,
423417
then_ty: Ty<'tcx>,
424418
else_ty: Ty<'tcx>,
425-
opt_suggest_box_span: Option<Span>,
419+
tail_defines_return_position_impl_trait: Option<LocalDefId>,
426420
) -> ObligationCause<'tcx> {
427421
let mut outer_span = if self.tcx.sess.source_map().is_multiline(span) {
428422
// The `if`/`else` isn't in one line in the output, include some context to make it
@@ -513,7 +507,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
513507
then_ty,
514508
else_ty,
515509
outer_span,
516-
opt_suggest_box_span,
510+
tail_defines_return_position_impl_trait,
517511
})),
518512
)
519513
}
@@ -593,96 +587,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
593587
}
594588
}
595589

596-
/// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
597-
/// we check if the different arms would work with boxed trait objects instead and
598-
/// provide a structured suggestion in that case.
599-
pub(crate) fn opt_suggest_box_span(
590+
// Does the expectation of the match define an RPIT?
591+
// (e.g. we're in the tail of a function body)
592+
//
593+
// Returns the `LocalDefId` of the RPIT, which is always identity-substituted.
594+
pub fn return_position_impl_trait_from_match_expectation(
600595
&self,
601-
first_ty: Ty<'tcx>,
602-
second_ty: Ty<'tcx>,
603-
orig_expected: Expectation<'tcx>,
604-
) -> Option<Span> {
605-
// FIXME(compiler-errors): This really shouldn't need to be done during the
606-
// "good" path of typeck, but here we are.
607-
match orig_expected {
608-
Expectation::ExpectHasType(expected) => {
609-
let TypeVariableOrigin {
610-
span,
611-
kind: TypeVariableOriginKind::OpaqueTypeInference(rpit_def_id),
612-
..
613-
} = self.type_var_origin(expected)?
614-
else {
615-
return None;
616-
};
617-
618-
let Some(rpit_local_def_id) = rpit_def_id.as_local() else {
619-
return None;
620-
};
621-
if !matches!(
622-
self.tcx.hir().expect_item(rpit_local_def_id).expect_opaque_ty().origin,
623-
hir::OpaqueTyOrigin::FnReturn(..)
624-
) {
625-
return None;
626-
}
627-
628-
let sig = self.body_fn_sig()?;
629-
630-
let args = sig.output().walk().find_map(|arg| {
631-
if let ty::GenericArgKind::Type(ty) = arg.unpack()
632-
&& let ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) = *ty.kind()
633-
&& def_id == rpit_def_id
634-
{
635-
Some(args)
636-
} else {
637-
None
638-
}
639-
})?;
640-
641-
if !self.can_coerce(first_ty, expected) || !self.can_coerce(second_ty, expected) {
642-
return None;
643-
}
644-
645-
for ty in [first_ty, second_ty] {
646-
for (clause, _) in self
647-
.tcx
648-
.explicit_item_super_predicates(rpit_def_id)
649-
.iter_instantiated_copied(self.tcx, args)
650-
{
651-
let pred = clause.kind().rebind(match clause.kind().skip_binder() {
652-
ty::ClauseKind::Trait(trait_pred) => {
653-
assert!(matches!(
654-
*trait_pred.trait_ref.self_ty().kind(),
655-
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args: alias_args, .. })
656-
if def_id == rpit_def_id && args == alias_args
657-
));
658-
ty::ClauseKind::Trait(trait_pred.with_self_ty(self.tcx, ty))
659-
}
660-
ty::ClauseKind::Projection(mut proj_pred) => {
661-
assert!(matches!(
662-
*proj_pred.projection_ty.self_ty().kind(),
663-
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args: alias_args, .. })
664-
if def_id == rpit_def_id && args == alias_args
665-
));
666-
proj_pred = proj_pred.with_self_ty(self.tcx, ty);
667-
ty::ClauseKind::Projection(proj_pred)
668-
}
669-
_ => continue,
670-
});
671-
if !self.predicate_must_hold_modulo_regions(&Obligation::new(
672-
self.tcx,
673-
ObligationCause::misc(span, self.body_id),
674-
self.param_env,
675-
pred,
676-
)) {
677-
return None;
678-
}
679-
}
680-
}
681-
682-
Some(span)
683-
}
684-
_ => None,
596+
expectation: Expectation<'tcx>,
597+
) -> Option<LocalDefId> {
598+
let expected_ty = expectation.to_option(self)?;
599+
let (def_id, args) = match *expected_ty.kind() {
600+
// FIXME: Could also check that the RPIT is not defined
601+
ty::Alias(ty::Opaque, alias_ty) => (alias_ty.def_id.as_local()?, alias_ty.args),
602+
// FIXME(-Znext-solver): Remove this branch once `replace_opaque_types_with_infer` is gone.
603+
ty::Infer(ty::TyVar(_)) => self
604+
.inner
605+
.borrow()
606+
.iter_opaque_types()
607+
.find(|(_, v)| v.ty == expected_ty)
608+
.map(|(k, _)| (k.def_id, k.args))?,
609+
_ => return None,
610+
};
611+
let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = self.tcx.opaque_type_origin(def_id)
612+
else {
613+
return None;
614+
};
615+
if &args[0..self.tcx.generics_of(parent_def_id).count()]
616+
!= ty::GenericArgs::identity_for_item(self.tcx, parent_def_id).as_slice()
617+
{
618+
return None;
685619
}
620+
Some(def_id)
686621
}
687622
}
688623

‎compiler/rustc_hir_typeck/src/coercion.rs‎

Lines changed: 139 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,18 @@
3535
//! // and are then unable to coerce `&7i32` to `&mut i32`.
3636
//! ```
3737
38+
use crate::errors::SuggestBoxingForReturnImplTrait;
3839
use crate::FnCtxt;
3940
use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan};
4041
use rustc_hir as hir;
41-
use rustc_hir::def_id::DefId;
42+
use rustc_hir::def_id::{DefId,LocalDefId};
4243
use rustc_hir::intravisit::{self, Visitor};
4344
use rustc_hir::Expr;
4445
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
4546
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
4647
use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
47-
use rustc_infer::traits::TraitEngine;
4848
use rustc_infer::traits::TraitEngineExt as _;
49+
use rustc_infer::traits::{IfExpressionCause, MatchExpressionArmCause, TraitEngine};
4950
use rustc_infer::traits::{Obligation, PredicateObligation};
5051
use rustc_middle::lint::in_external_macro;
5152
use rustc_middle::traits::BuiltinImplSource;
@@ -59,6 +60,7 @@ use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt};
5960
use rustc_session::parse::feature_err;
6061
use rustc_span::symbol::sym;
6162
use rustc_span::DesugaringKind;
63+
use rustc_span::{BytePos, Span};
6264
use rustc_target::spec::abi::Abi;
6365
use rustc_trait_selection::infer::InferCtxtExt as _;
6466
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
@@ -1638,6 +1640,77 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
16381640
unsized_return = self.is_return_ty_definitely_unsized(fcx);
16391641
}
16401642
}
1643+
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
1644+
arm_span,
1645+
arm_ty,
1646+
prior_arm_ty,
1647+
ref prior_non_diverging_arms,
1648+
tail_defines_return_position_impl_trait: Some(rpit_def_id),
1649+
..
1650+
}) => {
1651+
err = fcx.err_ctxt().report_mismatched_types(
1652+
cause,
1653+
expected,
1654+
found,
1655+
coercion_error,
1656+
);
1657+
// Check that we're actually in the second or later arm
1658+
if prior_non_diverging_arms.len() > 0 {
1659+
self.suggest_boxing_tail_for_return_position_impl_trait(
1660+
fcx,
1661+
&mut err,
1662+
rpit_def_id,
1663+
arm_ty,
1664+
prior_arm_ty,
1665+
prior_non_diverging_arms
1666+
.iter()
1667+
.chain(std::iter::once(&arm_span))
1668+
.copied(),
1669+
);
1670+
}
1671+
}
1672+
ObligationCauseCode::IfExpression(box IfExpressionCause {
1673+
then_id,
1674+
else_id,
1675+
then_ty,
1676+
else_ty,
1677+
tail_defines_return_position_impl_trait: Some(rpit_def_id),
1678+
..
1679+
}) => {
1680+
err = fcx.err_ctxt().report_mismatched_types(
1681+
cause,
1682+
expected,
1683+
found,
1684+
coercion_error,
1685+
);
1686+
let then_span = fcx.find_block_span_from_hir_id(then_id);
1687+
let else_span = fcx.find_block_span_from_hir_id(else_id);
1688+
// don't suggest wrapping either blocks in `if .. {} else {}`
1689+
let is_empty_arm = |id| {
1690+
let hir::Node::Block(blk) = fcx.tcx.hir_node(id) else {
1691+
return false;
1692+
};
1693+
if blk.expr.is_some() || !blk.stmts.is_empty() {
1694+
return false;
1695+
}
1696+
let Some((_, hir::Node::Expr(expr))) =
1697+
fcx.tcx.hir().parent_iter(id).nth(1)
1698+
else {
1699+
return false;
1700+
};
1701+
matches!(expr.kind, hir::ExprKind::If(..))
1702+
};
1703+
if !is_empty_arm(then_id) && !is_empty_arm(else_id) {
1704+
self.suggest_boxing_tail_for_return_position_impl_trait(
1705+
fcx,
1706+
&mut err,
1707+
rpit_def_id,
1708+
then_ty,
1709+
else_ty,
1710+
[then_span, else_span].into_iter(),
1711+
);
1712+
}
1713+
}
16411714
_ => {
16421715
err = fcx.err_ctxt().report_mismatched_types(
16431716
cause,
@@ -1677,6 +1750,70 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
16771750
}
16781751
}
16791752

1753+
fn suggest_boxing_tail_for_return_position_impl_trait(
1754+
&self,
1755+
fcx: &FnCtxt<'_, 'tcx>,
1756+
err: &mut Diag<'_>,
1757+
rpit_def_id: LocalDefId,
1758+
a_ty: Ty<'tcx>,
1759+
b_ty: Ty<'tcx>,
1760+
arm_spans: impl Iterator<Item = Span>,
1761+
) {
1762+
let compatible = |ty: Ty<'tcx>| {
1763+
fcx.probe(|_| {
1764+
let ocx = ObligationCtxt::new(fcx);
1765+
ocx.register_obligations(
1766+
fcx.tcx
1767+
.item_super_predicates(rpit_def_id)
1768+
.instantiate_identity_iter()
1769+
.filter_map(|clause| {
1770+
let predicate = clause
1771+
.kind()
1772+
.map_bound(|clause| match clause {
1773+
ty::ClauseKind::Trait(trait_pred) => Some(
1774+
ty::ClauseKind::Trait(trait_pred.with_self_ty(fcx.tcx, ty)),
1775+
),
1776+
ty::ClauseKind::Projection(proj_pred) => {
1777+
Some(ty::ClauseKind::Projection(
1778+
proj_pred.with_self_ty(fcx.tcx, ty),
1779+
))
1780+
}
1781+
_ => None,
1782+
})
1783+
.transpose()?;
1784+
Some(Obligation::new(
1785+
fcx.tcx,
1786+
ObligationCause::dummy(),
1787+
fcx.param_env,
1788+
predicate,
1789+
))
1790+
}),
1791+
);
1792+
ocx.select_where_possible().is_empty()
1793+
})
1794+
};
1795+
1796+
if !compatible(a_ty) || !compatible(b_ty) {
1797+
return;
1798+
}
1799+
1800+
let rpid_def_span = fcx.tcx.def_span(rpit_def_id);
1801+
err.subdiagnostic(
1802+
fcx.tcx.dcx(),
1803+
SuggestBoxingForReturnImplTrait::ChangeReturnType {
1804+
start_sp: rpid_def_span.with_hi(rpid_def_span.lo() + BytePos(4)),
1805+
end_sp: rpid_def_span.shrink_to_hi(),
1806+
},
1807+
);
1808+
1809+
let (starts, ends) =
1810+
arm_spans.map(|span| (span.shrink_to_lo(), span.shrink_to_hi())).unzip();
1811+
err.subdiagnostic(
1812+
fcx.tcx.dcx(),
1813+
SuggestBoxingForReturnImplTrait::BoxReturnExpr { starts, ends },
1814+
);
1815+
}
1816+
16801817
fn note_unreachable_loop_return(
16811818
&self,
16821819
err: &mut Diag<'_>,

‎compiler/rustc_hir_typeck/src/errors.rs‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,3 +621,21 @@ pub struct NoteCallerChoosesTyForTyParam<'tcx> {
621621
pub ty_param_name: Symbol,
622622
pub found_ty: Ty<'tcx>,
623623
}
624+
625+
#[derive(Subdiagnostic)]
626+
pub enum SuggestBoxingForReturnImplTrait {
627+
#[multipart_suggestion(hir_typeck_rpit_change_return_type, applicability = "maybe-incorrect")]
628+
ChangeReturnType {
629+
#[suggestion_part(code = "Box<dyn")]
630+
start_sp: Span,
631+
#[suggestion_part(code = ">")]
632+
end_sp: Span,
633+
},
634+
#[multipart_suggestion(hir_typeck_rpit_box_return_expr, applicability = "maybe-incorrect")]
635+
BoxReturnExpr {
636+
#[suggestion_part(code = "Box::new(")]
637+
starts: Vec<Span>,
638+
#[suggestion_part(code = ")")]
639+
ends: Vec<Span>,
640+
},
641+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /