From 087a571e708560bad84b1dd8de107c8fdc2a3044 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 14 Sep 2023 22:38:07 +0000 Subject: [PATCH 1/2] Record asyncness span in HIR --- compiler/rustc_ast_lowering/src/item.rs | 2 +- .../src/diagnostics/region_name.rs | 2 +- compiler/rustc_hir/src/hir.rs | 18 +++++------ .../src/check/compare_impl_item.rs | 2 +- .../rustc_hir_analysis/src/check/entry.rs | 4 +-- .../src/collect/resolve_bound_vars.rs | 2 +- compiler/rustc_hir_pretty/src/lib.rs | 2 +- .../src/fn_ctxt/suggestions.rs | 4 +-- compiler/rustc_lint/src/builtin.rs | 3 +- compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- compiler/rustc_metadata/src/rmeta/table.rs | 6 ++-- compiler/rustc_middle/src/query/erase.rs | 1 + compiler/rustc_middle/src/query/mod.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 13 ++++++++ compiler/rustc_middle/src/ty/parameterized.rs | 1 + .../error_reporting/on_unimplemented.rs | 6 ++-- compiler/rustc_ty_utils/src/ty.rs | 7 ++-- src/librustdoc/clean/types.rs | 8 +++-- src/librustdoc/html/format.rs | 2 +- .../src/redundant_closure_call.rs | 7 ++-- src/tools/clippy/clippy_utils/src/lib.rs | 6 ++-- tests/ui/stats/hir-stats.stderr | 32 +++++++++---------- 22 files changed, 78 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index a59c83de0f4..edc1e2f0b84 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1308,7 +1308,7 @@ fn error_on_invalid_abi(&self, abi: StrLit, err: abi::AbiUnsupported) { fn lower_asyncness(&mut self, a: Async) -> hir::IsAsync { match a { - Async::Yes { .. } => hir::IsAsync::Async, + Async::Yes { span, .. } => hir::IsAsync::Async(span), Async::No => hir::IsAsync::NotAsync, } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index b40e89e471d..096bf826cfb 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -302,7 +302,7 @@ fn give_name_from_error_region(&self, fr: RegionVid) -> Option { if free_region.bound_region.is_named() { // A named region that is actually named. Some(RegionName { name, source: RegionNameSource::NamedFreeRegion(span) }) - } else if let hir::IsAsync::Async = tcx.asyncness(self.mir_hir_id().owner) { + } else if tcx.asyncness(self.mir_hir_id().owner).is_async() { // If we spuriously thought that the region is named, we should let the // system generate a true name for error messages. Currently this can // happen if we have an elided name in an async fn for example: the diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 0c4a0e5d4a5..3eec66611ed 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2853,13 +2853,13 @@ pub fn has_implicit_self(&self) -> bool { #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)] #[derive(HashStable_Generic)] pub enum IsAsync { - Async, + Async(Span), NotAsync, } impl IsAsync { pub fn is_async(self) -> bool { - self == IsAsync::Async + matches!(self, IsAsync::Async(_)) } } @@ -3296,7 +3296,7 @@ pub struct FnHeader { impl FnHeader { pub fn is_async(&self) -> bool { - matches!(&self.asyncness, IsAsync::Async) + matches!(&self.asyncness, IsAsync::Async(_)) } pub fn is_const(&self) -> bool { @@ -4091,10 +4091,10 @@ mod size_asserts { static_assert_size!(GenericBound<'_>, 48); static_assert_size!(Generics<'_>, 56); static_assert_size!(Impl<'_>, 80); - static_assert_size!(ImplItem<'_>, 80); - static_assert_size!(ImplItemKind<'_>, 32); - static_assert_size!(Item<'_>, 80); - static_assert_size!(ItemKind<'_>, 48); + static_assert_size!(ImplItem<'_>, 88); + static_assert_size!(ImplItemKind<'_>, 40); + static_assert_size!(Item<'_>, 88); + static_assert_size!(ItemKind<'_>, 56); static_assert_size!(Local<'_>, 64); static_assert_size!(Param<'_>, 32); static_assert_size!(Pat<'_>, 72); @@ -4105,8 +4105,8 @@ mod size_asserts { static_assert_size!(Res, 12); static_assert_size!(Stmt<'_>, 32); static_assert_size!(StmtKind<'_>, 16); - static_assert_size!(TraitItem<'_>, 80); - static_assert_size!(TraitItemKind<'_>, 40); + static_assert_size!(TraitItem<'_>, 88); + static_assert_size!(TraitItemKind<'_>, 48); static_assert_size!(Ty<'_>, 48); static_assert_size!(TyKind<'_>, 32); // tidy-alphabetical-end diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 38f19aa09ad..7be18a36d63 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -595,7 +595,7 @@ fn compare_asyncness<'tcx>( trait_m: ty::AssocItem, delay: bool, ) -> Result<(), ErrorGuaranteed> { - if tcx.asyncness(trait_m.def_id) == hir::IsAsync::Async { + if tcx.asyncness(trait_m.def_id).is_async() { match tcx.fn_sig(impl_m.def_id).skip_binder().skip_binder().output().kind() { ty::Alias(ty::Opaque, ..) => { // allow both `async fn foo()` and `fn foo() -> impl Future` diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index fcaefe0261b..cede7493093 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -112,7 +112,7 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option { } let main_asyncness = tcx.asyncness(main_def_id); - if let hir::IsAsync::Async = main_asyncness { + if main_asyncness.is_async() { let asyncness_span = main_fn_asyncness_span(tcx, main_def_id); tcx.sess.emit_err(errors::MainFunctionAsync { span: main_span, asyncness: asyncness_span }); error = true; @@ -212,7 +212,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) { }); error = true; } - if let hir::IsAsync::Async = sig.header.asyncness { + if sig.header.asyncness.is_async() { let span = tcx.def_span(it.owner_id); tcx.sess.emit_err(errors::StartAsync { span: span }); error = true; diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 6869c869603..a39cfd7b6e1 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1213,7 +1213,7 @@ fn resolve_lifetime_ref( && let Some(generics) = self.tcx.hir().get_generics(self.tcx.local_parent(param_id)) && let Some(param) = generics.params.iter().find(|p| p.def_id == param_id) && param.is_elided_lifetime() - && let hir::IsAsync::NotAsync = self.tcx.asyncness(lifetime_ref.hir_id.owner.def_id) + && !self.tcx.asyncness(lifetime_ref.hir_id.owner.def_id).is_async() && !self.tcx.features().anonymous_lifetime_in_impl_trait { let mut diag = rustc_session::parse::feature_err( diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 89efdc269c4..8587b009f25 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -2304,7 +2304,7 @@ pub fn print_fn_header_info(&mut self, header: hir::FnHeader) { match header.asyncness { hir::IsAsync::NotAsync => {} - hir::IsAsync::Async => self.word_nbsp("async"), + hir::IsAsync::Async(_) => self.word_nbsp("async"), } self.print_unsafety(header.unsafety); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index d2a53ee8b5e..41f815a812a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -987,10 +987,10 @@ pub(in super::super) fn suggest_missing_break_or_return_expr( let bound_vars = self.tcx.late_bound_vars(fn_id); let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); let ty = match self.tcx.asyncness(fn_id.owner) { - hir::IsAsync::Async => self.get_impl_future_output_ty(ty).unwrap_or_else(|| { + ty::Asyncness::Yes => self.get_impl_future_output_ty(ty).unwrap_or_else(|| { span_bug!(fn_decl.output.span(), "failed to get output type of async function") }), - hir::IsAsync::NotAsync => ty, + ty::Asyncness::No => ty, }; let ty = self.normalize(expr.span, ty); if self.can_coerce(found, ty) { diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index de228bdb850..d38c6b2642a 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -41,7 +41,6 @@ }, EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, }; -use hir::IsAsync; use rustc_ast::attr; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::visit::{FnCtxt, FnKind}; @@ -1294,7 +1293,7 @@ fn check_fn( span: Span, def_id: LocalDefId, ) { - if fn_kind.asyncness() == IsAsync::Async + if fn_kind.asyncness().is_async() && !cx.tcx.features().async_fn_track_caller // Now, check if the function has the `#[track_caller]` attribute && let Some(attr) = cx.tcx.get_attr(def_id, sym::track_caller) diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 71269779d42..f2875bb11b1 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -439,7 +439,7 @@ fn encode(&self, buf: &mut FileEncoder) -> LazyTables { coerce_unsized_info: Table>, mir_const_qualif: Table>, rendered_const: Table>, - asyncness: Table, + asyncness: Table, fn_arg_names: Table>, generator_kind: Table>, trait_def: Table>, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 35987072ed6..d105dab4814 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -205,9 +205,9 @@ impl FixedSizeEncoding for Option<$ty> { } fixed_size_enum! { - hir::IsAsync { - ( NotAsync ) - ( Async ) + ty::Asyncness { + ( Yes ) + ( No ) } } diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index d41b38a8b4b..8ba3764bcc3 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -265,6 +265,7 @@ impl EraseType for $ty { rustc_middle::ty::adjustment::CoerceUnsizedInfo, rustc_middle::ty::AssocItem, rustc_middle::ty::AssocItemContainer, + rustc_middle::ty::Asyncness, rustc_middle::ty::BoundVariableKind, rustc_middle::ty::DeducedParamAttrs, rustc_middle::ty::Destructor, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0241820ab72..07160e41c94 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -731,7 +731,7 @@ separate_provide_extern } - query asyncness(key: DefId) -> hir::IsAsync { + query asyncness(key: DefId) -> ty::Asyncness { desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) } separate_provide_extern } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index dbff13fdd8e..b050208b4a6 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -280,6 +280,19 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } } +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable, Debug)] +#[derive(TypeFoldable, TypeVisitable)] +pub enum Asyncness { + Yes, + No, +} + +impl Asyncness { + pub fn is_async(self) -> bool { + matches!(self, Asyncness::Yes) + } +} + #[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, Encodable, Decodable, HashStable)] pub enum Visibility { /// Visible everywhere (including in other crates). diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index f1c38984296..f1093e88312 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -62,6 +62,7 @@ impl $crate::ty::ParameterizedOverTcx for $ty { crate::middle::resolve_bound_vars::ObjectLifetimeDefault, crate::mir::ConstQualifs, ty::AssocItemContainer, + ty::Asyncness, ty::DeducedParamAttrs, ty::Generics, ty::ImplPolarity, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 4086db2ab55..d645dc033b8 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -104,7 +104,9 @@ fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => { self.describe_generator(*body_id).or_else(|| { Some(match sig.header { - hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async function", + hir::FnHeader { asyncness: hir::IsAsync::Async(_), .. } => { + "an async function" + } _ => "a function", }) }) @@ -118,7 +120,7 @@ fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> { .. }) => self.describe_generator(*body_id).or_else(|| { Some(match sig.header { - hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async method", + hir::FnHeader { asyncness: hir::IsAsync::Async(_), .. } => "an async method", _ => "a method", }) }), diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 2288d36df17..6c23589b39a 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -296,9 +296,12 @@ fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option, def_id: LocalDefId) -> hir::IsAsync { +fn asyncness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Asyncness { let node = tcx.hir().get_by_def_id(def_id); - node.fn_sig().map_or(hir::IsAsync::NotAsync, |sig| sig.header.asyncness) + node.fn_sig().map_or(ty::Asyncness::No, |sig| match sig.header.asyncness { + hir::IsAsync::Async(_) => ty::Asyncness::Yes, + hir::IsAsync::NotAsync => ty::Asyncness::No, + }) } fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> BitSet { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index ef7794cc41e..b665f684167 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -31,7 +31,7 @@ use rustc_session::Session; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{self, FileName, Loc}; +use rustc_span::{self, FileName, Loc, DUMMY_SP}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; @@ -622,7 +622,7 @@ pub(crate) fn fn_header(&self, tcx: TyCtxt<'_>) -> Option { fn build_fn_header( def_id: DefId, tcx: TyCtxt<'_>, - asyncness: hir::IsAsync, + asyncness: ty::Asyncness, ) -> hir::FnHeader { let sig = tcx.fn_sig(def_id).skip_binder(); let constness = @@ -631,6 +631,10 @@ fn build_fn_header( } else { hir::Constness::NotConst }; + let asyncness = match asyncness { + ty::Asyncness::Yes => hir::IsAsync::Async(DUMMY_SP), + ty::Asyncness::No => hir::IsAsync::NotAsync, + }; hir::FnHeader { unsafety: sig.unsafety(), abi: sig.abi(), constness, asyncness } } let header = match *self.kind { diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 2f611c31a07..170e559e698 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1599,7 +1599,7 @@ fn print_with_space(&self) -> &str { impl PrintWithSpace for hir::IsAsync { fn print_with_space(&self) -> &str { match self { - hir::IsAsync::Async => "async ", + hir::IsAsync::Async(_) => "async ", hir::IsAsync::NotAsync => "", } } diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index fc49b58e0a7..f42836611ca 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -10,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -84,7 +85,7 @@ fn find_innermost_closure<'tcx>( cx: &LateContext<'tcx>, mut expr: &'tcx hir::Expr<'tcx>, mut steps: usize, -) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::FnDecl<'tcx>, hir::IsAsync)> { +) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::FnDecl<'tcx>, ty::Asyncness)> { let mut data = None; while let hir::ExprKind::Closure(closure) = expr.kind @@ -98,9 +99,9 @@ fn find_innermost_closure<'tcx>( { expr = body.value; data = Some((body.value, closure.fn_decl, if is_async_closure(body) { - hir::IsAsync::Async + ty::Asyncness::Yes } else { - hir::IsAsync::NotAsync + ty::Asyncness::No })); steps -= 1; } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 1e464db8087..be1c46319c2 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -90,7 +90,7 @@ use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{ self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Destination, Expr, - ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, + ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, }; @@ -1958,8 +1958,8 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, /// Checks if the given function kind is an async function. pub fn is_async_fn(kind: FnKind<'_>) -> bool { match kind { - FnKind::ItemFn(_, _, header) => header.asyncness == IsAsync::Async, - FnKind::Method(_, sig) => sig.header.asyncness == IsAsync::Async, + FnKind::ItemFn(_, _, header) => header.asyncness .is_async(), + FnKind::Method(_, sig) => sig.header.asyncness.is_async(), FnKind::Closure => false, } } diff --git a/tests/ui/stats/hir-stats.stderr b/tests/ui/stats/hir-stats.stderr index d723ff538a8..813e65e45a2 100644 --- a/tests/ui/stats/hir-stats.stderr +++ b/tests/ui/stats/hir-stats.stderr @@ -146,33 +146,33 @@ hir-stats - Trait 192 ( 2.1%) 4 hir-stats WherePredicate 192 ( 2.1%) 3 64 hir-stats - BoundPredicate 192 ( 2.1%) 3 hir-stats Block 288 ( 3.2%) 6 48 -hir-stats Pat 360 ( 4.0%) 5 72 +hir-stats Pat 360 ( 3.9%) 5 72 hir-stats - Wild 72 ( 0.8%) 1 hir-stats - Struct 72 ( 0.8%) 1 hir-stats - Binding 216 ( 2.4%) 3 hir-stats GenericParam 400 ( 4.4%) 5 80 -hir-stats Generics 560 ( 6.2%) 10 56 -hir-stats Ty 720 ( 8.0%) 15 48 +hir-stats Generics 560 ( 6.1%) 10 56 +hir-stats Ty 720 ( 7.9%) 15 48 hir-stats - Ptr 48 ( 0.5%) 1 hir-stats - Ref 48 ( 0.5%) 1 -hir-stats - Path 624 ( 6.9%) 13 -hir-stats Expr 768 ( 8.5%) 12 64 +hir-stats - Path 624 ( 6.8%) 13 +hir-stats Expr 768 ( 8.4%) 12 64 hir-stats - Path 64 ( 0.7%) 1 hir-stats - Struct 64 ( 0.7%) 1 hir-stats - Match 64 ( 0.7%) 1 hir-stats - InlineAsm 64 ( 0.7%) 1 hir-stats - Lit 128 ( 1.4%) 2 hir-stats - Block 384 ( 4.2%) 6 -hir-stats Item 880 ( 9.7%) 11 80 -hir-stats - Trait 80 ( 0.9%) 1 -hir-stats - Enum 80 ( 0.9%) 1 -hir-stats - ExternCrate 80 ( 0.9%) 1 -hir-stats - ForeignMod 80 ( 0.9%) 1 -hir-stats - Impl 80 ( 0.9%) 1 -hir-stats - Fn 160 ( 1.8%) 2 -hir-stats - Use 320 ( 3.5%) 4 -hir-stats Path 1_240 (13.7%) 31 40 -hir-stats PathSegment 1_920 (21.2%) 40 48 +hir-stats Item 968 (10.6%) 11 88 +hir-stats - Trait 88 ( 1.0%) 1 +hir-stats - Enum 88 ( 1.0%) 1 +hir-stats - ExternCrate 88 ( 1.0%) 1 +hir-stats - ForeignMod 88 ( 1.0%) 1 +hir-stats - Impl 88 ( 1.0%) 1 +hir-stats - Fn 176 ( 1.9%) 2 +hir-stats - Use 352 ( 3.9%) 4 +hir-stats Path 1_240 (13.6%) 31 40 +hir-stats PathSegment 1_920 (21.0%) 40 48 hir-stats ---------------------------------------------------------------- -hir-stats Total 9_048 +hir-stats Total 9_136 hir-stats From 9072415252d6dde7f23acc21a6781f27c703ac9d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 15 Sep 2023 05:00:39 +0000 Subject: [PATCH 2/2] Suggest desugaring to RPITIT when AFIT is required to be an auto trait --- .../src/traits/error_reporting/mod.rs | 1 + .../src/traits/error_reporting/suggestions.rs | 136 ++++++++++++++++++ .../in-trait/auxiliary/foreign-async-fn.rs | 7 + .../in-trait/missing-send-bound.stderr | 5 + .../in-trait/send-on-async-fn-in-trait.fixed | 20 +++ .../in-trait/send-on-async-fn-in-trait.rs | 20 +++ .../in-trait/send-on-async-fn-in-trait.stderr | 43 ++++++ .../send-on-foreign-async-fn-in-trait.rs | 15 ++ .../send-on-foreign-async-fn-in-trait.stderr | 23 +++ 9 files changed, 270 insertions(+) create mode 100644 tests/ui/async-await/in-trait/auxiliary/foreign-async-fn.rs create mode 100644 tests/ui/async-await/in-trait/send-on-async-fn-in-trait.fixed create mode 100644 tests/ui/async-await/in-trait/send-on-async-fn-in-trait.rs create mode 100644 tests/ui/async-await/in-trait/send-on-async-fn-in-trait.stderr create mode 100644 tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.rs create mode 100644 tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 7d754cbafa3..d3e0d3b8b18 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -987,6 +987,7 @@ fn report_selection_error( } self.explain_hrtb_projection(&mut err, trait_predicate, obligation.param_env, &obligation.cause); + self.suggest_desugaring_async_fn_in_trait(&mut err, trait_ref); // Return early if the trait is Debug or Display and the invocation // originates within a standard library macro, because the output diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index a08ebe5a9ea..17e0e4fcb48 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -414,6 +414,12 @@ fn explain_hrtb_projection( param_env: ty::ParamEnv<'tcx>, cause: &ObligationCause<'tcx>, ); + + fn suggest_desugaring_async_fn_in_trait( + &self, + err: &mut Diagnostic, + trait_ref: ty::PolyTraitRef<'tcx>, + ); } fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) { @@ -4100,6 +4106,136 @@ fn explain_hrtb_projection( }); } } + + fn suggest_desugaring_async_fn_in_trait( + &self, + err: &mut Diagnostic, + trait_ref: ty::PolyTraitRef<'tcx>, + ) { + // Don't suggest if RTN is active -- we should prefer a where-clause bound instead. + if self.tcx.features().return_type_notation { + return; + } + + let trait_def_id = trait_ref.def_id(); + + // Only suggest specifying auto traits + if !self.tcx.trait_is_auto(trait_def_id) { + return; + } + + // Look for an RPITIT + let ty::Alias(ty::Projection, alias_ty) = trait_ref.self_ty().skip_binder().kind() else { + return; + }; + let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id }) = + self.tcx.opt_rpitit_info(alias_ty.def_id) + else { + return; + }; + + let auto_trait = self.tcx.def_path_str(trait_def_id); + // ... which is a local function + let Some(fn_def_id) = fn_def_id.as_local() else { + // If it's not local, we can at least mention that the method is async, if it is. + if self.tcx.asyncness(fn_def_id).is_async() { + err.span_note( + self.tcx.def_span(fn_def_id), + format!( + "`{}::{}` is an `async fn` in trait, which does not \ + automatically imply that its future is `{auto_trait}`", + alias_ty.trait_ref(self.tcx), + self.tcx.item_name(fn_def_id) + ), + ); + } + return; + }; + let Some(hir::Node::TraitItem(item)) = self.tcx.hir().find_by_def_id(fn_def_id) else { + return; + }; + + // ... whose signature is `async` (i.e. this is an AFIT) + let (sig, body) = item.expect_fn(); + let hir::IsAsync::Async(async_span) = sig.header.asyncness else { + return; + }; + let Ok(async_span) = + self.tcx.sess.source_map().span_extend_while(async_span, |c| c.is_whitespace()) + else { + return; + }; + let hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::OpaqueDef(def, ..), .. }) = + sig.decl.output + else { + // This should never happen, but let's not ICE. + return; + }; + + // Check that this is *not* a nested `impl Future` RPIT in an async fn + // (i.e. `async fn foo() -> impl Future`) + if def.owner_id.to_def_id() != opaque_def_id { + return; + } + + let future = self.tcx.hir().item(*def).expect_opaque_ty(); + let Some(hir::GenericBound::LangItemTrait(_, _, _, generics)) = future.bounds.get(0) else { + // `async fn` should always lower to a lang item bound... but don't ICE. + return; + }; + let Some(hir::TypeBindingKind::Equality { term: hir::Term::Ty(future_output_ty) }) = + generics.bindings.get(0).map(|binding| binding.kind) + else { + // Also should never happen. + return; + }; + + let function_name = self.tcx.def_path_str(fn_def_id); + + let mut sugg = if future_output_ty.span.is_empty() { + vec![ + (async_span, String::new()), + ( + future_output_ty.span, + format!(" -> impl std::future::Future + {auto_trait}"), + ), + ] + } else { + vec![ + ( + future_output_ty.span.shrink_to_lo(), + "impl std::future::Future + {auto_trait}")), + (async_span, String::new()), + ] + }; + + // If there's a body, we also need to wrap it in `async {}` + if let hir::TraitFn::Provided(body) = body { + let body = self.tcx.hir().body(*body); + let body_span = body.value.span; + let body_span_without_braces = + body_span.with_lo(body_span.lo() + BytePos(1)).with_hi(body_span.hi() - BytePos(1)); + if body_span_without_braces.is_empty() { + sugg.push((body_span_without_braces, " async {} ".to_owned())); + } else { + sugg.extend([ + (body_span_without_braces.shrink_to_lo(), "async {".to_owned()), + (body_span_without_braces.shrink_to_hi(), "} ".to_owned()), + ]); + } + } + + err.multipart_suggestion( + format!( + "`{auto_trait}` can be made part of the associated future's \ + guarantees for all implementations of `{function_name}`" + ), + sugg, + Applicability::MachineApplicable, + ); + } } /// Add a hint to add a missing borrow or remove an unnecessary one. diff --git a/tests/ui/async-await/in-trait/auxiliary/foreign-async-fn.rs b/tests/ui/async-await/in-trait/auxiliary/foreign-async-fn.rs new file mode 100644 index 00000000000..bba886f175e --- /dev/null +++ b/tests/ui/async-await/in-trait/auxiliary/foreign-async-fn.rs @@ -0,0 +1,7 @@ +// edition:2021 + +#![feature(async_fn_in_trait)] + +pub trait Foo { + async fn test(); +} diff --git a/tests/ui/async-await/in-trait/missing-send-bound.stderr b/tests/ui/async-await/in-trait/missing-send-bound.stderr index 18185b75554..7e59d94d456 100644 --- a/tests/ui/async-await/in-trait/missing-send-bound.stderr +++ b/tests/ui/async-await/in-trait/missing-send-bound.stderr @@ -15,6 +15,11 @@ note: required by a bound in `assert_is_send` | LL | fn assert_is_send(_: impl Send) {} | ^^^^ required by this bound in `assert_is_send` +help: `Send` can be made part of the associated future's guarantees for all implementations of `Foo::bar` + | +LL - async fn bar(); +LL + fn bar() -> impl std::future::Future + Send; + | error: aborting due to previous error diff --git a/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.fixed b/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.fixed new file mode 100644 index 00000000000..33c00587439 --- /dev/null +++ b/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.fixed @@ -0,0 +1,20 @@ +// run-rustfix +// edition: 2021 + +#![feature(async_fn_in_trait, return_position_impl_trait_in_trait)] +#![allow(unused)] + +trait Foo { + fn test() -> impl std::future::Future + Send { async {} } + fn test2() -> impl std::future::Future + Send {async { 1 + 2 } } +} + +fn bar() { + fn needs_send(_: impl Send) {} + needs_send(T::test()); + //~^ ERROR `impl Future` cannot be sent between threads safely + needs_send(T::test2()); + //~^ ERROR `impl Future` cannot be sent between threads safely +} + +fn main() {} diff --git a/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.rs b/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.rs new file mode 100644 index 00000000000..96b623d6988 --- /dev/null +++ b/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.rs @@ -0,0 +1,20 @@ +// run-rustfix +// edition: 2021 + +#![feature(async_fn_in_trait, return_position_impl_trait_in_trait)] +#![allow(unused)] + +trait Foo { + async fn test() -> () {} + async fn test2() -> i32 { 1 + 2 } +} + +fn bar() { + fn needs_send(_: impl Send) {} + needs_send(T::test()); + //~^ ERROR `impl Future` cannot be sent between threads safely + needs_send(T::test2()); + //~^ ERROR `impl Future` cannot be sent between threads safely +} + +fn main() {} diff --git a/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.stderr b/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.stderr new file mode 100644 index 00000000000..4319a14118b --- /dev/null +++ b/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.stderr @@ -0,0 +1,43 @@ +error[E0277]: `impl Future` cannot be sent between threads safely + --> $DIR/send-on-async-fn-in-trait.rs:14:16 + | +LL | needs_send(T::test()); + | ---------- ^^^^^^^^^ `impl Future` cannot be sent between threads safely + | | + | required by a bound introduced by this call + | + = help: the trait `Send` is not implemented for `impl Future` +note: required by a bound in `needs_send` + --> $DIR/send-on-async-fn-in-trait.rs:13:27 + | +LL | fn needs_send(_: impl Send) {} + | ^^^^ required by this bound in `needs_send` +help: `Send` can be made part of the associated future's guarantees for all implementations of `Foo::test` + | +LL - async fn test() -> () {} +LL + fn test() -> impl std::future::Future + Send { async {} } + | + +error[E0277]: `impl Future` cannot be sent between threads safely + --> $DIR/send-on-async-fn-in-trait.rs:16:16 + | +LL | needs_send(T::test2()); + | ---------- ^^^^^^^^^^ `impl Future` cannot be sent between threads safely + | | + | required by a bound introduced by this call + | + = help: the trait `Send` is not implemented for `impl Future` +note: required by a bound in `needs_send` + --> $DIR/send-on-async-fn-in-trait.rs:13:27 + | +LL | fn needs_send(_: impl Send) {} + | ^^^^ required by this bound in `needs_send` +help: `Send` can be made part of the associated future's guarantees for all implementations of `Foo::test2` + | +LL - async fn test2() -> i32 { 1 + 2 } +LL + fn test2() -> impl std::future::Future + Send {async { 1 + 2 } } + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.rs b/tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.rs new file mode 100644 index 00000000000..83b69d72a96 --- /dev/null +++ b/tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.rs @@ -0,0 +1,15 @@ +// aux-build:foreign-async-fn.rs +// edition:2021 + +#![feature(async_fn_in_trait)] + +extern crate foreign_async_fn; +use foreign_async_fn::Foo; + +fn bar() { + fn needs_send(_: impl Send) {} + needs_send(T::test()); + //~^ ERROR `impl Future` cannot be sent between threads safely +} + +fn main() {} diff --git a/tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.stderr b/tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.stderr new file mode 100644 index 00000000000..f337a04ba19 --- /dev/null +++ b/tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.stderr @@ -0,0 +1,23 @@ +error[E0277]: `impl Future` cannot be sent between threads safely + --> $DIR/send-on-foreign-async-fn-in-trait.rs:11:16 + | +LL | needs_send(T::test()); + | ---------- ^^^^^^^^^ `impl Future` cannot be sent between threads safely + | | + | required by a bound introduced by this call + | + = help: the trait `Send` is not implemented for `impl Future` +note: `::test` is an `async fn` in trait, which does not automatically imply that its future is `Send` + --> $DIR/auxiliary/foreign-async-fn.rs:6:5 + | +LL | async fn test(); + | ^^^^^^^^^^^^^^^^ +note: required by a bound in `needs_send` + --> $DIR/send-on-foreign-async-fn-in-trait.rs:10:27 + | +LL | fn needs_send(_: impl Send) {} + | ^^^^ required by this bound in `needs_send` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.