Skip to content

Commit 5642733

Browse files
Fix suggestions when returning a bare trait from an async fn.
Don't assume we always return a trait object and suggest the dyn keyword. Suggest adding impl instead.
1 parent dd51276 commit 5642733

File tree

6 files changed

+85
-45
lines changed

6 files changed

+85
-45
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3365,6 +3365,7 @@ dependencies = [
33653365
"rustc_fluent_macro",
33663366
"rustc_graphviz",
33673367
"rustc_hir",
3368+
"rustc_hir_analysis",
33683369
"rustc_index",
33693370
"rustc_infer",
33703371
"rustc_lexer",

compiler/rustc_borrowck/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ rustc_errors = { path = "../rustc_errors" }
1313
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
1414
rustc_graphviz = { path = "../rustc_graphviz" }
1515
rustc_hir = { path = "../rustc_hir" }
16+
rustc_hir_analysis = { path = "../rustc_hir_analysis" }
1617
rustc_index = { path = "../rustc_index" }
1718
rustc_infer = { path = "../rustc_infer" }
1819
rustc_lexer = { path = "../rustc_lexer" }

compiler/rustc_borrowck/src/diagnostics/region_name.rs

+4-40
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ use rustc_data_structures::fx::IndexEntry;
88
use rustc_errors::Diag;
99
use rustc_hir as hir;
1010
use rustc_hir::def::{DefKind, Res};
11+
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
12+
use rustc_middle::bug;
1113
use rustc_middle::ty::print::RegionHighlightMode;
1214
use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, RegionVid, Ty};
13-
use rustc_middle::{bug, span_bug};
14-
use rustc_span::symbol::{Symbol, kw, sym};
15+
use rustc_span::symbol::{Symbol, kw};
1516
use rustc_span::{DUMMY_SP, Span};
1617
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
1718
use tracing::{debug, instrument};
@@ -722,7 +723,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
722723
.output;
723724
span = output.span();
724725
if let hir::FnRetTy::Return(ret) = output {
725-
hir_ty = Some(self.get_future_inner_return_ty(ret));
726+
hir_ty = <dyn HirTyLowerer<'_>>::get_future_inner_return_ty(ret);
726727
}
727728
" of async function"
728729
}
@@ -816,43 +817,6 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
816817
})
817818
}
818819

819-
/// From the [`hir::Ty`] of an async function's lowered return type,
820-
/// retrieve the `hir::Ty` representing the type the user originally wrote.
821-
///
822-
/// e.g. given the function:
823-
///
824-
/// ```
825-
/// async fn foo() -> i32 { 2 }
826-
/// ```
827-
///
828-
/// this function, given the lowered return type of `foo`, an [`OpaqueDef`] that implements `Future<Output=i32>`,
829-
/// returns the `i32`.
830-
///
831-
/// [`OpaqueDef`]: hir::TyKind::OpaqueDef
832-
fn get_future_inner_return_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
833-
let hir::TyKind::OpaqueDef(opaque_ty, _) = hir_ty.kind else {
834-
span_bug!(
835-
hir_ty.span,
836-
"lowered return type of async fn is not OpaqueDef: {:?}",
837-
hir_ty
838-
);
839-
};
840-
if let hir::OpaqueTy { bounds: [hir::GenericBound::Trait(trait_ref)], .. } = opaque_ty
841-
&& let Some(segment) = trait_ref.trait_ref.path.segments.last()
842-
&& let Some(args) = segment.args
843-
&& let [constraint] = args.constraints
844-
&& constraint.ident.name == sym::Output
845-
&& let Some(ty) = constraint.ty()
846-
{
847-
ty
848-
} else {
849-
span_bug!(
850-
hir_ty.span,
851-
"bounds from lowered return type of async fn did not match expected format: {opaque_ty:?}",
852-
);
853-
}
854-
}
855-
856820
#[instrument(level = "trace", skip(self))]
857821
fn give_name_if_anonymous_region_appears_in_yield_ty(
858822
&self,

compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs

+53-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_hir as hir;
55
use rustc_hir::def::{DefKind, Res};
66
use rustc_lint_defs::Applicability;
77
use rustc_lint_defs::builtin::BARE_TRAIT_OBJECTS;
8-
use rustc_span::Span;
8+
use rustc_span::{Span, sym};
99
use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName;
1010

1111
use super::HirTyLowerer;
@@ -181,14 +181,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
181181
/// Make sure that we are in the condition to suggest `impl Trait`.
182182
fn maybe_suggest_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) -> bool {
183183
let tcx = self.tcx();
184-
let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id;
184+
let parent_node = tcx.hir_node_by_def_id(tcx.hir().get_parent_item(self_ty.hir_id).def_id);
185185
// FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0<Ty = Trait1>`
186186
// and suggest `Trait0<Ty = impl Trait1>`.
187187
// Functions are found in three different contexts.
188188
// 1. Independent functions
189189
// 2. Functions inside trait blocks
190190
// 3. Functions inside impl blocks
191-
let (sig, generics) = match tcx.hir_node_by_def_id(parent_id) {
191+
let (sig, generics) = match parent_node {
192192
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. }) => {
193193
(sig, generics)
194194
}
@@ -223,10 +223,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
223223
tcx.parent_hir_node(self_ty.hir_id),
224224
hir::Node::Ty(hir::Ty { kind: hir::TyKind::Ref(..), .. })
225225
);
226+
let is_non_trait_object = |ty: &'tcx hir::Ty<'_>| {
227+
if sig.header.is_async() {
228+
Self::get_future_inner_return_ty(ty).map_or(false, |ty| ty.hir_id == self_ty.hir_id)
229+
} else {
230+
ty.peel_refs().hir_id == self_ty.hir_id
231+
}
232+
};
226233

227234
// Suggestions for function return type.
228235
if let hir::FnRetTy::Return(ty) = sig.decl.output
229-
&& ty.peel_refs().hir_id == self_ty.hir_id
236+
&& is_non_trait_object(ty)
230237
{
231238
let pre = if !is_dyn_compatible {
232239
format!("`{trait_name}` is dyn-incompatible, ")
@@ -311,10 +318,21 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
311318
}
312319

313320
fn maybe_suggest_assoc_ty_bound(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) {
314-
let mut parents = self.tcx().hir().parent_iter(self_ty.hir_id);
321+
let mut parents = self.tcx().hir().parent_iter(self_ty.hir_id).peekable();
322+
let is_async_fn = if let Some((_, parent)) = parents.peek()
323+
&& let Some(sig) = parent.fn_sig()
324+
&& sig.header.is_async()
325+
&& let hir::FnRetTy::Return(ty) = sig.decl.output
326+
&& Self::get_future_inner_return_ty(ty).is_some()
327+
{
328+
true
329+
} else {
330+
false
331+
};
315332

316333
if let Some((_, hir::Node::AssocItemConstraint(constraint))) = parents.next()
317334
&& let Some(obj_ty) = constraint.ty()
335+
&& !is_async_fn
318336
{
319337
if let Some((_, hir::Node::TraitRef(..))) = parents.next()
320338
&& let Some((_, hir::Node::Ty(ty))) = parents.next()
@@ -343,4 +361,34 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
343361
);
344362
}
345363
}
364+
365+
/// From the [`hir::Ty`] of an async function's lowered return type,
366+
/// retrieve the `hir::Ty` representing the type the user originally wrote.
367+
///
368+
/// e.g. given the function:
369+
///
370+
/// ```
371+
/// async fn foo() -> i32 { 2 }
372+
/// ```
373+
///
374+
/// this function, given the lowered return type of `foo`, an [`OpaqueDef`] that implements `Future<Output=i32>`,
375+
/// returns the `i32`.
376+
///
377+
/// [`OpaqueDef`]: hir::TyKind::OpaqueDef
378+
pub fn get_future_inner_return_ty<'a>(hir_ty: &'a hir::Ty<'a>) -> Option<&'a hir::Ty<'a>> {
379+
let hir::TyKind::OpaqueDef(opaque_ty, _) = hir_ty.kind else {
380+
return None;
381+
};
382+
if let hir::OpaqueTy { bounds: [hir::GenericBound::Trait(trait_ref)], .. } = opaque_ty
383+
&& let Some(segment) = trait_ref.trait_ref.path.segments.last()
384+
&& let Some(args) = segment.args
385+
&& let [constraint] = args.constraints
386+
&& constraint.ident.name == sym::Output
387+
&& let Some(ty) = constraint.ty()
388+
{
389+
Some(ty)
390+
} else {
391+
None
392+
}
393+
}
346394
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//@ edition:2021
2+
trait Trait {}
3+
4+
async fn fun() -> Trait { //~ ERROR expected a type, found a trait
5+
todo!()
6+
}
7+
8+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0782]: expected a type, found a trait
2+
--> $DIR/suggest-impl-on-bare-trait-return.rs:4:13
3+
|
4+
LL | fn fun() -> Trait {
5+
| ^^^^^
6+
|
7+
help: use `impl Trait` to return an opaque type, as long as you return a single underlying type
8+
|
9+
LL | fn fun() -> impl Trait {
10+
| ++++
11+
help: alternatively, you can return an owned trait object
12+
|
13+
LL | fn fun() -> Box<dyn Trait> {
14+
| +++++++ +
15+
16+
error: aborting due to 1 previous error
17+
18+
For more information about this error, try `rustc --explain E0782`.

0 commit comments

Comments
 (0)