diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs index b20673fe8da..7a91bfad483 100644 --- a/compiler/rustc_middle/src/middle/lang_items.rs +++ b/compiler/rustc_middle/src/middle/lang_items.rs @@ -68,6 +68,17 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Given a [`ty::ClosureKind`], get the [`DefId`] of its corresponding `Fn`-family + /// trait, if it is defined. + pub fn async_fn_trait_kind_to_def_id(self, kind: ty::ClosureKind) -> Option { + let items = self.lang_items(); + match kind { + ty::ClosureKind::Fn => items.async_fn_trait(), + ty::ClosureKind::FnMut => items.async_fn_mut_trait(), + ty::ClosureKind::FnOnce => items.async_fn_once_trait(), + } + } + /// Returns `true` if `id` is a `DefId` of [`Fn`], [`FnMut`] or [`FnOnce`] traits. pub fn is_fn_trait(self, id: DefId) -> bool { self.fn_trait_kind_from_def_id(id).is_some() diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 039c988f5c9..cd4123f0a3f 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -16,8 +16,8 @@ use rustc_hir::definitions::{DefKey, DefPathDataName}; use rustc_macros::{Lift, extension}; use rustc_session::Limit; use rustc_session::cstore::{ExternCrate, ExternCrateSource}; -use rustc_span::FileNameDisplayPreference; use rustc_span::symbol::{Ident, Symbol, kw}; +use rustc_span::{FileNameDisplayPreference, sym}; use rustc_type_ir::{Upcast as _, elaborate}; use smallvec::SmallVec; @@ -26,8 +26,8 @@ use super::*; use crate::mir::interpret::{AllocRange, GlobalAlloc, Pointer, Provenance, Scalar}; use crate::query::{IntoQueryParam, Providers}; use crate::ty::{ - ConstInt, Expr, GenericArgKind, ParamConst, ScalarInt, Term, TermKind, TypeFoldable, - TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + ConstInt, Expr, GenericArgKind, ParamConst, ScalarInt, Term, TermKind, TraitPredicate, + TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; macro_rules! p { @@ -993,10 +993,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { match bound_predicate.skip_binder() { ty::ClauseKind::Trait(pred) => { - let trait_ref = bound_predicate.rebind(pred.trait_ref); - // Don't print `+ Sized`, but rather `+ ?Sized` if absent. - if tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) { + if tcx.is_lang_item(pred.def_id(), LangItem::Sized) { match pred.polarity { ty::PredicatePolarity::Positive => { has_sized_bound = true; @@ -1007,24 +1005,22 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } self.insert_trait_and_projection( - trait_ref, - pred.polarity, + bound_predicate.rebind(pred), None, &mut traits, &mut fn_traits, ); } ty::ClauseKind::Projection(pred) => { - let proj_ref = bound_predicate.rebind(pred); - let trait_ref = proj_ref.required_poly_trait_ref(tcx); - - // Projection type entry -- the def-id for naming, and the ty. - let proj_ty = (proj_ref.projection_def_id(), proj_ref.term()); + let proj = bound_predicate.rebind(pred); + let trait_ref = proj.map_bound(|proj| TraitPredicate { + trait_ref: proj.projection_term.trait_ref(tcx), + polarity: ty::PredicatePolarity::Positive, + }); self.insert_trait_and_projection( trait_ref, - ty::PredicatePolarity::Positive, - Some(proj_ty), + Some((proj.projection_def_id(), proj.term())), &mut traits, &mut fn_traits, ); @@ -1042,88 +1038,66 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // Insert parenthesis around (Fn(A, B) -> C) if the opaque ty has more than one other trait let paren_needed = fn_traits.len() > 1 || traits.len() > 0 || !has_sized_bound; - for (fn_once_trait_ref, entry) in fn_traits { + for ((bound_args, is_async), entry) in fn_traits { write!(self, "{}", if first { "" } else { " + " })?; write!(self, "{}", if paren_needed { "(" } else { "" })?; - self.wrap_binder(&fn_once_trait_ref, |trait_ref, cx| { - define_scoped_cx!(cx); - // Get the (single) generic ty (the args) of this FnOnce trait ref. - let generics = tcx.generics_of(trait_ref.def_id); - let own_args = generics.own_args_no_defaults(tcx, trait_ref.args); + let trait_def_id = if is_async { + tcx.async_fn_trait_kind_to_def_id(entry.kind).expect("expected AsyncFn lang items") + } else { + tcx.fn_trait_kind_to_def_id(entry.kind).expect("expected Fn lang items") + }; - match (entry.return_ty, own_args[0].expect_ty()) { - // We can only print `impl Fn() -> ()` if we have a tuple of args and we recorded - // a return type. - (Some(return_ty), arg_tys) if matches!(arg_tys.kind(), ty::Tuple(_)) => { - let name = if entry.fn_trait_ref.is_some() { - "Fn" - } else if entry.fn_mut_trait_ref.is_some() { - "FnMut" - } else { - "FnOnce" - }; + if let Some(return_ty) = entry.return_ty { + self.wrap_binder(&bound_args, |args, cx| { + define_scoped_cx!(cx); + p!(write("{}", tcx.item_name(trait_def_id))); + p!("("); - p!(write("{}(", name)); - - for (idx, ty) in arg_tys.tuple_fields().iter().enumerate() { - if idx > 0 { - p!(", "); - } - p!(print(ty)); + for (idx, ty) in args.iter().enumerate() { + if idx > 0 { + p!(", "); } - - p!(")"); - if let Some(ty) = return_ty.skip_binder().as_type() { - if !ty.is_unit() { - p!(" -> ", print(return_ty)); - } - } - p!(write("{}", if paren_needed { ")" } else { "" })); - - first = false; + p!(print(ty)); } - // If we got here, we can't print as a `impl Fn(A, B) -> C`. Just record the - // trait_refs we collected in the OpaqueFnEntry as normal trait refs. - _ => { - if entry.has_fn_once { - traits - .entry((fn_once_trait_ref, ty::PredicatePolarity::Positive)) - .or_default() - .extend( - // Group the return ty with its def id, if we had one. - entry.return_ty.map(|ty| { - (tcx.require_lang_item(LangItem::FnOnceOutput, None), ty) - }), - ); - } - if let Some(trait_ref) = entry.fn_mut_trait_ref { - traits.entry((trait_ref, ty::PredicatePolarity::Positive)).or_default(); - } - if let Some(trait_ref) = entry.fn_trait_ref { - traits.entry((trait_ref, ty::PredicatePolarity::Positive)).or_default(); + + p!(")"); + if let Some(ty) = return_ty.skip_binder().as_type() { + if !ty.is_unit() { + p!(" -> ", print(return_ty)); } } - } + p!(write("{}", if paren_needed { ")" } else { "" })); - Ok(()) - })?; + first = false; + Ok(()) + })?; + } else { + // Otherwise, render this like a regular trait. + traits.insert( + bound_args.map_bound(|args| ty::TraitPredicate { + polarity: ty::PredicatePolarity::Positive, + trait_ref: ty::TraitRef::new(tcx, trait_def_id, [Ty::new_tup(tcx, args)]), + }), + FxIndexMap::default(), + ); + } } // Print the rest of the trait types (that aren't Fn* family of traits) - for ((trait_ref, polarity), assoc_items) in traits { + for (trait_pred, assoc_items) in traits { write!(self, "{}", if first { "" } else { " + " })?; - self.wrap_binder(&trait_ref, |trait_ref, cx| { + self.wrap_binder(&trait_pred, |trait_pred, cx| { define_scoped_cx!(cx); - if polarity == ty::PredicatePolarity::Negative { + if trait_pred.polarity == ty::PredicatePolarity::Negative { p!("!"); } - p!(print(trait_ref.print_only_trait_name())); + p!(print(trait_pred.trait_ref.print_only_trait_name())); - let generics = tcx.generics_of(trait_ref.def_id); - let own_args = generics.own_args_no_defaults(tcx, trait_ref.args); + let generics = tcx.generics_of(trait_pred.def_id()); + let own_args = generics.own_args_no_defaults(tcx, trait_pred.trait_ref.args); if !own_args.is_empty() || !assoc_items.is_empty() { let mut first = true; @@ -1230,51 +1204,48 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { /// traits map or fn_traits map, depending on if the trait is in the Fn* family of traits. fn insert_trait_and_projection( &mut self, - trait_ref: ty::PolyTraitRef<'tcx>, - polarity: ty::PredicatePolarity, + trait_pred: ty::PolyTraitPredicate<'tcx>, proj_ty: Option<(DefId, ty::Binder<'tcx, Term<'tcx>>)>, traits: &mut FxIndexMap< - (ty::PolyTraitRef<'tcx>, ty::PredicatePolarity), + ty::PolyTraitPredicate<'tcx>, FxIndexMap>>, >, - fn_traits: &mut FxIndexMap, OpaqueFnEntry<'tcx>>, + fn_traits: &mut FxIndexMap< + (ty::Binder<'tcx, &'tcx ty::List>>, bool), + OpaqueFnEntry<'tcx>, + >, ) { - let trait_def_id = trait_ref.def_id(); + let tcx = self.tcx(); + let trait_def_id = trait_pred.def_id(); - // If our trait_ref is FnOnce or any of its children, project it onto the parent FnOnce - // super-trait ref and record it there. - // We skip negative Fn* bounds since they can't use parenthetical notation anyway. - if polarity == ty::PredicatePolarity::Positive - && let Some(fn_once_trait) = self.tcx().lang_items().fn_once_trait() + let fn_trait_and_async = if let Some(kind) = tcx.fn_trait_kind_from_def_id(trait_def_id) { + Some((kind, false)) + } else if let Some(kind) = tcx.async_fn_trait_kind_from_def_id(trait_def_id) { + Some((kind, true)) + } else { + None + }; + + if trait_pred.polarity() == ty::PredicatePolarity::Positive + && let Some((kind, is_async)) = fn_trait_and_async + && let ty::Tuple(types) = *trait_pred.skip_binder().trait_ref.args.type_at(1).kind() { - // If we have a FnOnce, then insert it into - if trait_def_id == fn_once_trait { - let entry = fn_traits.entry(trait_ref).or_default(); - // Optionally insert the return_ty as well. - if let Some((_, ty)) = proj_ty { - entry.return_ty = Some(ty); - } - entry.has_fn_once = true; - return; - } else if self.tcx().is_lang_item(trait_def_id, LangItem::FnMut) { - let super_trait_ref = elaborate::supertraits(self.tcx(), trait_ref) - .find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait) - .unwrap(); - - fn_traits.entry(super_trait_ref).or_default().fn_mut_trait_ref = Some(trait_ref); - return; - } else if self.tcx().is_lang_item(trait_def_id, LangItem::Fn) { - let super_trait_ref = elaborate::supertraits(self.tcx(), trait_ref) - .find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait) - .unwrap(); - - fn_traits.entry(super_trait_ref).or_default().fn_trait_ref = Some(trait_ref); - return; + let entry = fn_traits + .entry((trait_pred.rebind(types), is_async)) + .or_insert_with(|| OpaqueFnEntry { kind, return_ty: None }); + if kind.extends(entry.kind) { + entry.kind = kind; } + if let Some((proj_def_id, proj_ty)) = proj_ty + && tcx.item_name(proj_def_id) == sym::Output + { + entry.return_ty = Some(proj_ty); + } + return; } // Otherwise, just group our traits and projection types. - traits.entry((trait_ref, polarity)).or_default().extend(proj_ty); + traits.entry(trait_pred).or_default().extend(proj_ty); } fn pretty_print_inherent_projection( @@ -3189,10 +3160,10 @@ define_print_and_forward_display! { TraitRefPrintSugared<'tcx> { if !with_reduced_queries() - && let Some(kind) = cx.tcx().fn_trait_kind_from_def_id(self.0.def_id) + && cx.tcx().trait_def(self.0.def_id).paren_sugar && let ty::Tuple(args) = self.0.args.type_at(1).kind() { - p!(write("{}", kind.as_str()), "("); + p!(write("{}", cx.tcx().item_name(self.0.def_id)), "("); for (i, arg) in args.iter().enumerate() { if i > 0 { p!(", "); @@ -3415,11 +3386,7 @@ pub fn provide(providers: &mut Providers) { *providers = Providers { trimmed_def_paths, ..*providers }; } -#[derive(Default)] pub struct OpaqueFnEntry<'tcx> { - // The trait ref is already stored as a key, so just track if we have it as a real predicate - has_fn_once: bool, - fn_mut_trait_ref: Option>, - fn_trait_ref: Option>, + kind: ty::ClosureKind, return_ty: Option>>, } diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 8a8e624e72a..5e3a3df0038 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -684,19 +684,6 @@ impl ty::Binder> { self.skip_binder().projection_term.trait_def_id(cx) } - /// Get the trait ref required for this projection to be well formed. - /// Note that for generic associated types the predicates of the associated - /// type also need to be checked. - #[inline] - pub fn required_poly_trait_ref(&self, cx: I) -> ty::Binder> { - // Note: unlike with `TraitRef::to_poly_trait_ref()`, - // `self.0.trait_ref` is permitted to have escaping regions. - // This is because here `self` has a `Binder` and so does our - // return value, so we are preserving the number of binding - // levels. - self.map_bound(|predicate| predicate.projection_term.trait_ref(cx)) - } - pub fn term(&self) -> ty::Binder { self.map_bound(|predicate| predicate.term) } diff --git a/tests/ui/async-await/async-closures/fn-exception-target-features.stderr b/tests/ui/async-await/async-closures/fn-exception-target-features.stderr index 396167f4070..e965c40fb5b 100644 --- a/tests/ui/async-await/async-closures/fn-exception-target-features.stderr +++ b/tests/ui/async-await/async-closures/fn-exception-target-features.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `fn() -> Pin + 'static)>> {target_feature}: AsyncFn<()>` is not satisfied +error[E0277]: the trait bound `fn() -> Pin + 'static)>> {target_feature}: AsyncFn()` is not satisfied --> $DIR/fn-exception-target-features.rs:16:10 | LL | test(target_feature); - | ---- ^^^^^^^^^^^^^^ the trait `AsyncFn<()>` is not implemented for fn item `fn() -> Pin + 'static)>> {target_feature}` + | ---- ^^^^^^^^^^^^^^ the trait `AsyncFn()` is not implemented for fn item `fn() -> Pin + 'static)>> {target_feature}` | | | required by a bound introduced by this call | diff --git a/tests/ui/async-await/async-closures/fn-exception.stderr b/tests/ui/async-await/async-closures/fn-exception.stderr index bacd079af0f..20132e42833 100644 --- a/tests/ui/async-await/async-closures/fn-exception.stderr +++ b/tests/ui/async-await/async-closures/fn-exception.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `unsafe fn() -> Pin + 'static)>> {unsafety}: AsyncFn<()>` is not satisfied +error[E0277]: the trait bound `unsafe fn() -> Pin + 'static)>> {unsafety}: AsyncFn()` is not satisfied --> $DIR/fn-exception.rs:19:10 | LL | test(unsafety); - | ---- ^^^^^^^^ the trait `AsyncFn<()>` is not implemented for fn item `unsafe fn() -> Pin + 'static)>> {unsafety}` + | ---- ^^^^^^^^ the trait `AsyncFn()` is not implemented for fn item `unsafe fn() -> Pin + 'static)>> {unsafety}` | | | required by a bound introduced by this call | @@ -12,11 +12,11 @@ note: required by a bound in `test` LL | fn test(f: impl async Fn()) {} | ^^^^^^^^^^ required by this bound in `test` -error[E0277]: the trait bound `extern "C" fn() -> Pin + 'static)>> {abi}: AsyncFn<()>` is not satisfied +error[E0277]: the trait bound `extern "C" fn() -> Pin + 'static)>> {abi}: AsyncFn()` is not satisfied --> $DIR/fn-exception.rs:20:10 | LL | test(abi); - | ---- ^^^ the trait `AsyncFn<()>` is not implemented for fn item `extern "C" fn() -> Pin + 'static)>> {abi}` + | ---- ^^^ the trait `AsyncFn()` is not implemented for fn item `extern "C" fn() -> Pin + 'static)>> {abi}` | | | required by a bound introduced by this call | diff --git a/tests/ui/async-await/async-closures/pretty-async-fn-opaque.rs b/tests/ui/async-await/async-closures/pretty-async-fn-opaque.rs new file mode 100644 index 00000000000..2e7cf1b09fd --- /dev/null +++ b/tests/ui/async-await/async-closures/pretty-async-fn-opaque.rs @@ -0,0 +1,14 @@ +//@ edition: 2021 + +#![feature(async_closure)] + +use std::ops::AsyncFnMut; + +fn produce() -> impl AsyncFnMut() -> &'static str { + async || "" +} + +fn main() { + let x: i32 = produce(); + //~^ ERROR mismatched types +} diff --git a/tests/ui/async-await/async-closures/pretty-async-fn-opaque.stderr b/tests/ui/async-await/async-closures/pretty-async-fn-opaque.stderr new file mode 100644 index 00000000000..863e61eb35a --- /dev/null +++ b/tests/ui/async-await/async-closures/pretty-async-fn-opaque.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/pretty-async-fn-opaque.rs:12:18 + | +LL | fn produce() -> impl AsyncFnMut() -> &'static str { + | --------------------------------- the found opaque type +... +LL | let x: i32 = produce(); + | --- ^^^^^^^^^ expected `i32`, found opaque type + | | + | expected due to this + | + = note: expected type `i32` + found opaque type `impl AsyncFnMut() -> &'static str` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr b/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr index caaac5434c5..6f0f287fe12 100644 --- a/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr +++ b/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr @@ -74,7 +74,7 @@ error[E0061]: this function takes 0 arguments but 1 argument was supplied --> $DIR/opaque-used-in-extraneous-argument.rs:20:5 | LL | open_parent(&old_path) - | ^^^^^^^^^^^ --------- unexpected argument of type `&impl FnOnce<{type error}, Output = {type error}> + Fn<{type error}> + 'static` + | ^^^^^^^^^^^ --------- unexpected argument of type `&impl Fn<{type error}> + FnOnce<{type error}, Output = {type error}> + 'static` | note: function defined here --> $DIR/opaque-used-in-extraneous-argument.rs:12:4