Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 43 additions & 92 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,40 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
})
}

fn pretty_print_closure_inner(
&mut self,
did: DefId,
args: ty::GenericArgsRef<'tcx>,
) -> Result<(), PrintError> {
if self.should_truncate() {
write!(self, "@...")
} else if self.tcx().sess.opts.unstable_opts.span_free_formats {
write!(self, "@")?;
self.print_def_path(did, args)
} else if let Some(did) = did.as_local() {
let span = self.tcx().def_span(did);
let loc = if with_forced_trimmed_paths() {
self.tcx()
.sess
.source_map()
.span_to_short_string(span, RemapPathScopeComponents::DIAGNOSTICS)
} else {
self.tcx().sess.source_map().span_to_diagnostic_string(span)
};
write!(
self,
"@{}",
// This may end up in stderr diagnostics but it may also be
// emitted into MIR. Hence we use the remapped path if
// available
loc
)
} else {
write!(self, "@")?;
self.print_def_path(did, args)
}
}

fn pretty_print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> {
match *ty.kind() {
ty::Bool => write!(self, "bool")?,
Expand Down Expand Up @@ -904,22 +938,12 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
//
// This will look like:
// {async fn body of some_fn()}
let did_of_the_fn_item = self.tcx().parent(did);
write!(self, " of ")?;
let did_of_the_fn_item = self.tcx().parent(did);
self.print_def_path(did_of_the_fn_item, args)?;
write!(self, "()")?;
} else if let Some(local_did) = did.as_local() {
let span = self.tcx().def_span(local_did);
write!(
self,
"@{}",
// This may end up in stderr diagnostics but it may also be emitted
// into MIR. Hence we use the remapped path if available
self.tcx().sess.source_map().span_to_diagnostic_string(span)
)?;
} else {
write!(self, "@")?;
self.print_def_path(did, args)?;
self.pretty_print_closure_inner(did, args)?;
}
} else {
self.print_def_path(did, args)?;
Expand All @@ -937,63 +961,19 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
}
ty::CoroutineWitness(did, args) => {
write!(self, "{{")?;
if !self.tcx().sess.verbose_internals() {
if !self.should_print_verbose() {
write!(self, "coroutine witness")?;
if let Some(did) = did.as_local() {
let span = self.tcx().def_span(did);
write!(
self,
"@{}",
// This may end up in stderr diagnostics but it may also be emitted
// into MIR. Hence we use the remapped path if available
self.tcx().sess.source_map().span_to_diagnostic_string(span)
)?;
} else {
write!(self, "@")?;
self.print_def_path(did, args)?;
}
self.pretty_print_closure_inner(did, args)?;
} else {
self.print_def_path(did, args)?;
}

write!(self, "}}")?
}
ty::Closure(did, args) => {
write!(self, "{{")?;
if !self.should_print_verbose() {
write!(self, "closure")?;
if self.should_truncate() {
write!(self, "@...}}")?;
return Ok(());
} else {
if let Some(did) = did.as_local() {
if self.tcx().sess.opts.unstable_opts.span_free_formats {
write!(self, "@")?;
self.print_def_path(did.to_def_id(), args)?;
} else {
let span = self.tcx().def_span(did);
let loc = if with_forced_trimmed_paths() {
self.tcx().sess.source_map().span_to_short_string(
span,
RemapPathScopeComponents::DIAGNOSTICS,
)
} else {
self.tcx().sess.source_map().span_to_diagnostic_string(span)
};
write!(
self,
"@{}",
// This may end up in stderr diagnostics but it may also be
// emitted into MIR. Hence we use the remapped path if
// available
loc
)?;
}
} else {
write!(self, "@")?;
self.print_def_path(did, args)?;
}
}
self.pretty_print_closure_inner(did, args)?;
} else {
self.print_def_path(did, args)?;
write!(self, " closure_kind_ty=")?;
Expand All @@ -1011,43 +991,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
match self.tcx().coroutine_kind(self.tcx().coroutine_for_closure(did)).unwrap()
{
hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Async,
hir::CoroutineSource::Closure,
) => write!(self, "async closure")?,
hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::AsyncGen,
desugaring,
hir::CoroutineSource::Closure,
) => write!(self, "async gen closure")?,
hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Gen,
hir::CoroutineSource::Closure,
) => write!(self, "gen closure")?,
) => write!(self, "{desugaring}closure")?,
_ => unreachable!(
"coroutine from coroutine-closure should have CoroutineSource::Closure"
),
}
if let Some(did) = did.as_local() {
if self.tcx().sess.opts.unstable_opts.span_free_formats {
write!(self, "@")?;
self.print_def_path(did.to_def_id(), args)?;
} else {
let span = self.tcx().def_span(did);
// This may end up in stderr diagnostics but it may also be emitted
// into MIR. Hence we use the remapped path if available
let loc = if with_forced_trimmed_paths() {
self.tcx().sess.source_map().span_to_short_string(
span,
RemapPathScopeComponents::DIAGNOSTICS,
)
} else {
self.tcx().sess.source_map().span_to_diagnostic_string(span)
};
write!(self, "@{loc}")?;
}
} else {
write!(self, "@")?;
self.print_def_path(did, args)?;
}
};
self.pretty_print_closure_inner(did, args)?;
} else {
self.print_def_path(did, args)?;
write!(self, " closure_kind_ty=")?;
Expand Down
170 changes: 77 additions & 93 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1651,49 +1651,15 @@ fn confirm_closure_candidate<'cx, 'tcx>(
// I didn't see a good way to unify those.
ty::CoroutineClosure(def_id, args) => {
let args = args.as_coroutine_closure();
let kind_ty = args.kind_ty();
Unnormalized::new_wip(args.coroutine_closure_sig().map_bound(|sig| {
// If we know the kind and upvars, use that directly.
// Otherwise, defer to `AsyncFnKindHelper::Upvars` to delay
// the projection, like the `AsyncFn*` traits do.
let output_ty = if let Some(_) = kind_ty.to_opt_closure_kind()
// Fall back to projection if upvars aren't constrained
&& !args.tupled_upvars_ty().is_ty_var()
{
sig.to_coroutine_given_kind_and_upvars(
tcx,
args.parent_args(),
tcx.coroutine_for_closure(def_id),
ty::ClosureKind::FnOnce,
tcx.lifetimes.re_static,
args.tupled_upvars_ty(),
args.coroutine_captures_by_ref_ty(),
)
} else {
let upvars_projection_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindUpvars, obligation.cause.span);
let tupled_upvars_ty = Ty::new_projection(
tcx,
ty::IsRigid::No,
upvars_projection_def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce).into(),
tcx.lifetimes.re_static.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
sig.to_coroutine(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
)
};

let output_ty = coroutine_closure_output_coroutine(
tcx,
obligation,
ty::ClosureKind::FnOnce,
tcx.lifetimes.re_static,
def_id,
args,
);
tcx.mk_fn_sig([sig.tupled_inputs_ty], output_ty, sig.fn_sig_kind)
}))
}
Expand Down Expand Up @@ -1770,60 +1736,12 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
let poly_cache_entry = match *self_ty.kind() {
ty::CoroutineClosure(def_id, args) => {
let args = args.as_coroutine_closure();
let kind_ty = args.kind_ty();
let sig = args.coroutine_closure_sig().skip_binder();

let term = match item_name {
sym::CallOnceFuture | sym::CallRefFuture => {
if let Some(closure_kind) = kind_ty.to_opt_closure_kind()
// Fall back to projection if upvars aren't constrained
&& !args.tupled_upvars_ty().is_ty_var()
{
if !closure_kind.extends(goal_kind) {
bug!("we should not be confirming if the closure kind is not met");
}
sig.to_coroutine_given_kind_and_upvars(
tcx,
args.parent_args(),
tcx.coroutine_for_closure(def_id),
goal_kind,
env_region,
args.tupled_upvars_ty(),
args.coroutine_captures_by_ref_ty(),
)
} else {
let upvars_projection_def_id = tcx
.require_lang_item(LangItem::AsyncFnKindUpvars, obligation.cause.span);
// When we don't know the closure kind (and therefore also the closure's upvars,
// which are computed at the same time), we must delay the computation of the
// generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
// goal functions similarly to the old `ClosureKind` predicate, and ensures that
// the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
// will project to the right upvars for the generator, appending the inputs and
// coroutine upvars respecting the closure kind.
// N.B. No need to register a `AsyncFnKindHelper` goal here, it's already in `nested`.
let tupled_upvars_ty = Ty::new_projection(
tcx,
ty::IsRigid::No,
upvars_projection_def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, goal_kind).into(),
env_region.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
sig.to_coroutine(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
)
}
}
sym::CallOnceFuture | sym::CallRefFuture => coroutine_closure_output_coroutine(
tcx, obligation, goal_kind, env_region, def_id, args,
),
sym::Output => sig.return_ty,
name => bug!("no such associated type: {name}"),
};
Expand Down Expand Up @@ -1912,6 +1830,72 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
.with_addl_obligations(nested)
}

/// Given a `CoroutineClosure(def_id, args)`, interpret it as a closure,
/// and return its output type for the given `goal_kind` and `env_region`.
fn coroutine_closure_output_coroutine<'tcx>(
tcx: TyCtxt<'tcx>,
obligation: &ProjectionTermObligation<'tcx>,
goal_kind: ty::ClosureKind,
env_region: ty::Region<'tcx>,
def_id: DefId,
args: ty::CoroutineClosureArgs<TyCtxt<'tcx>>,
) -> Ty<'tcx> {
let kind_ty = args.kind_ty();
let sig = args.coroutine_closure_sig().skip_binder();

// If we know the kind and upvars, use that directly.
// Otherwise, defer to `AsyncFnKindHelper::Upvars` to delay
// the projection, like the `AsyncFn*` traits do.
if let Some(closure_kind) = kind_ty.to_opt_closure_kind()
// Fall back to projection if upvars aren't constrained
&& !args.tupled_upvars_ty().is_ty_var()
{
if !closure_kind.extends(goal_kind) {
bug!("we should not be confirming if the closure kind is not met");
}
sig.to_coroutine_given_kind_and_upvars(
tcx,
args.parent_args(),
tcx.coroutine_for_closure(def_id),
goal_kind,
env_region,
args.tupled_upvars_ty(),
args.coroutine_captures_by_ref_ty(),
)
} else {
let upvars_projection_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindUpvars, obligation.cause.span);
// When we don't know the closure kind (and therefore also the closure's upvars,
// which are computed at the same time), we must delay the computation of the
// generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
// goal functions similarly to the old `ClosureKind` predicate, and ensures that
// the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
// will project to the right upvars for the generator, appending the inputs and
// coroutine upvars respecting the closure kind.
// N.B. No need to register a `AsyncFnKindHelper` goal here, it's already in `nested`.
let tupled_upvars_ty = Ty::new_projection(
tcx,
ty::IsRigid::No,
upvars_projection_def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, goal_kind).into(),
env_region.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
sig.to_coroutine(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
)
}
}

fn confirm_async_fn_kind_helper_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTermObligation<'tcx>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ LL | | return 0u8;
LL | | }
| |_^ expected `u8`, found `()`

error[E0271]: expected `{async block@$DIR/async-block-control-flow-static-semantics.rs:23:17: 23:22}` to be a future that resolves to `()`, but it resolves to `u8`
error[E0271]: expected `{async block@async-block-control-flow-static-semantics.rs:23:17}` to be a future that resolves to `()`, but it resolves to `u8`
--> $DIR/async-block-control-flow-static-semantics.rs:26:39
|
LL | let _: &dyn Future<Output = ()> = &block;
Expand All @@ -26,7 +26,7 @@ LL | fn return_targets_async_block_not_fn() -> u8 {
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0271]: expected `{async block@$DIR/async-block-control-flow-static-semantics.rs:14:17: 14:22}` to be a future that resolves to `()`, but it resolves to `u8`
error[E0271]: expected `{async block@async-block-control-flow-static-semantics.rs:14:17}` to be a future that resolves to `()`, but it resolves to `u8`
--> $DIR/async-block-control-flow-static-semantics.rs:17:39
|
LL | let _: &dyn Future<Output = ()> = &block;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0271]: expected `{async closure@is-not-fn.rs:8:14}` to return `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}`
error[E0271]: expected `{async closure@is-not-fn.rs:8:14}` to return `()`, but it returns `{async closure body@is-not-fn.rs:8:23}`
--> $DIR/is-not-fn.rs:8:14
|
LL | needs_fn(async || {});
Expand Down
Loading
Loading