From cb024ba6e386242c5bea20fcc613c7edbc48f290 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 11 Feb 2024 21:36:12 +0000 Subject: [PATCH 1/2] is_closure_like --- .../src/diagnostics/conflict_errors.rs | 4 ++-- .../src/diagnostics/mutability_errors.rs | 11 +++++------ .../src/type_check/input_output.rs | 2 +- compiler/rustc_codegen_llvm/src/attributes.rs | 2 +- compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 6 +++--- .../src/transform/check_consts/mod.rs | 2 +- compiler/rustc_hir/src/def.rs | 6 ++++++ compiler/rustc_hir_typeck/src/check.rs | 3 +-- compiler/rustc_hir_typeck/src/gather_locals.rs | 2 +- compiler/rustc_middle/src/mir/pretty.rs | 2 +- compiler/rustc_middle/src/ty/closure.rs | 4 ++-- compiler/rustc_middle/src/ty/instance.rs | 7 ++----- compiler/rustc_middle/src/ty/util.rs | 16 +++++++++------- .../src/coverage/spans/from_mir.rs | 4 ++-- compiler/rustc_monomorphize/src/collector.rs | 2 +- compiler/rustc_passes/src/upvars.rs | 2 +- .../clippy/clippy_lints/src/redundant_locals.rs | 2 +- 17 files changed, 40 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index f87269960bc84..c6d312c922cd4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3161,7 +3161,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> Option> { // Define a fallback for when we can't match a closure. let fallback = || { - let is_closure = self.infcx.tcx.is_closure_or_coroutine(self.mir_def_id().to_def_id()); + let is_closure = self.infcx.tcx.is_closure_like(self.mir_def_id().to_def_id()); if is_closure { None } else { @@ -3372,7 +3372,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { sig: ty::PolyFnSig<'tcx>, ) -> Option> { debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig); - let is_closure = self.infcx.tcx.is_closure_or_coroutine(did.to_def_id()); + let is_closure = self.infcx.tcx.is_closure_like(did.to_def_id()); let fn_hir_id = self.infcx.tcx.local_def_id_to_hir_id(did); let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?; diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 9e7fd45ec19ad..350645e07341b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -67,7 +67,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { local, projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)], } => { - debug_assert!(is_closure_or_coroutine( + debug_assert!(is_closure_like( Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty )); @@ -126,9 +126,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { { item_msg = access_place_desc; debug_assert!(self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_ref()); - debug_assert!(is_closure_or_coroutine( - the_place_err.ty(self.body, self.infcx.tcx).ty - )); + debug_assert!(is_closure_like(the_place_err.ty(self.body, self.infcx.tcx).ty)); reason = if self.is_upvar_field_projection(access_place.as_ref()).is_some() { ", as it is a captured variable in a `Fn` closure".to_string() @@ -389,7 +387,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { local, projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)], } => { - debug_assert!(is_closure_or_coroutine( + debug_assert!(is_closure_like( Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty )); @@ -1474,7 +1472,8 @@ fn suggest_ampmut<'tcx>( } } -fn is_closure_or_coroutine(ty: Ty<'_>) -> bool { +/// If the type is a `Coroutine`, `Closure`, or `CoroutineClosure` +fn is_closure_like(ty: Ty<'_>) -> bool { ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() } diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index a5a7ce4ea3e86..af5b635ae6688 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -29,7 +29,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { pub(super) fn check_signature_annotation(&mut self, body: &Body<'tcx>) { let mir_def_id = body.source.def_id().expect_local(); - if !self.tcx().is_closure_or_coroutine(mir_def_id.to_def_id()) { + if !self.tcx().is_closure_like(mir_def_id.to_def_id()) { return; } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 07c83f1aa089a..f9eaa0d94cbbd 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -479,7 +479,7 @@ pub fn from_fn_attrs<'ll, 'tcx>( // `+multivalue` feature because the purpose of the wasm abi is to match // the WebAssembly specification, which has this feature. This won't be // needed when LLVM enables this `multivalue` feature by default. - if !cx.tcx.is_closure_or_coroutine(instance.def_id()) { + if !cx.tcx.is_closure_like(instance.def_id()) { let abi = cx.tcx.fn_sig(instance.def_id()).skip_binder().abi(); if abi == Abi::Wasm { function_features.push("+multivalue".to_string()); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index b387d0b2258b0..9e23757fceef0 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -229,7 +229,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL, sym::track_caller => { - let is_closure = tcx.is_closure_or_coroutine(did.to_def_id()); + let is_closure = tcx.is_closure_like(did.to_def_id()); if !is_closure && let Some(fn_sig) = fn_sig() @@ -274,7 +274,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } sym::target_feature => { - if !tcx.is_closure_or_coroutine(did.to_def_id()) + if !tcx.is_closure_like(did.to_def_id()) && let Some(fn_sig) = fn_sig() && fn_sig.skip_binder().unsafety() == hir::Unsafety::Normal { @@ -529,7 +529,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // would result in this closure being compiled without the inherited target features, but this // is probably a poor usage of `#[inline(always)]` and easily avoided by not using the attribute. if tcx.features().target_feature_11 - && tcx.is_closure_or_coroutine(did.to_def_id()) + && tcx.is_closure_like(did.to_def_id()) && codegen_fn_attrs.inline != InlineAttr::Always { let owner_id = tcx.parent(did.to_def_id()); diff --git a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs index 98276ff2e68d6..0eaeb8b808909 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs @@ -72,7 +72,7 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> { pub fn fn_sig(&self) -> PolyFnSig<'tcx> { let did = self.def_id().to_def_id(); - if self.tcx.is_closure_or_coroutine(did) { + if self.tcx.is_closure_like(did) { let ty = self.tcx.type_of(did).instantiate_identity(); let ty::Closure(_, args) = ty.kind() else { bug!("type_of closure not ty::Closure") }; args.as_closure().sig() diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 81ec7ddb629e3..a0f7a30c2c31b 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -115,6 +115,12 @@ pub enum DefKind { Impl { of_trait: bool, }, + /// A closure, coroutine, or coroutine-closure. + /// + /// These are all represented with the same `ExprKind::Closure` in the AST and HIR, + /// which makes it difficult to distinguish these during def collection. Therefore, + /// we treat them all the same, and code which needs to distinguish them can match + /// or `hir::ClosureKind` or `type_of`. Closure, } diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index aab78465f8c85..be68b598e7668 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -90,8 +90,7 @@ pub(super) fn check_fn<'a, 'tcx>( // ty.span == binding_span iff this is a closure parameter with no type ascription, // or if it's an implicit `self` parameter traits::SizedArgumentType( - if ty_span == Some(param.span) && tcx.is_closure_or_coroutine(fn_def_id.into()) - { + if ty_span == Some(param.span) && tcx.is_closure_like(fn_def_id.into()) { None } else { ty.map(|ty| ty.hir_id) diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index f9af357f0e79e..4d37f725c9400 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -153,7 +153,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { // ascription, or if it's an implicit `self` parameter traits::SizedArgumentType( if ty_span == ident.span - && self.fcx.tcx.is_closure_or_coroutine(self.fcx.body_id.into()) + && self.fcx.tcx.is_closure_like(self.fcx.body_id.into()) { None } else { diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index b601b465668e9..a011f6114de96 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -491,7 +491,7 @@ fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io: let kind = tcx.def_kind(def_id); let is_function = match kind { DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true, - _ => tcx.is_closure_or_coroutine(def_id), + _ => tcx.is_closure_like(def_id), }; match (kind, body.source.promoted) { (_, Some(i)) => write!(w, "{i:?} in ")?, diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 8ff5b135acae8..7db64504f85ea 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -197,7 +197,7 @@ pub struct ClosureTypeInfo<'tcx> { } fn closure_typeinfo<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ClosureTypeInfo<'tcx> { - debug_assert!(tcx.is_closure_or_coroutine(def.to_def_id())); + debug_assert!(tcx.is_closure_like(def.to_def_id())); let typeck_results = tcx.typeck(def); let user_provided_sig = typeck_results.user_provided_sigs[&def]; let captures = typeck_results.closure_min_captures_flattened(def); @@ -217,7 +217,7 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn closure_captures(self, def_id: LocalDefId) -> &'tcx [&'tcx ty::CapturedPlace<'tcx>] { - if !self.is_closure_or_coroutine(def_id.to_def_id()) { + if !self.is_closure_like(def_id.to_def_id()) { return &[]; }; self.closure_typeinfo(def_id).captures diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 8848d216f5b49..01b7be98b1866 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -465,10 +465,7 @@ impl<'tcx> Instance<'tcx> { ) -> Option> { debug!("resolve(def_id={:?}, args={:?})", def_id, args); // Use either `resolve_closure` or `resolve_for_vtable` - assert!( - !tcx.is_closure_or_coroutine(def_id), - "Called `resolve_for_fn_ptr` on closure: {def_id:?}" - ); + assert!(!tcx.is_closure_like(def_id), "Called `resolve_for_fn_ptr` on closure: {def_id:?}"); Instance::resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| { match resolved.def { InstanceDef::Item(def) if resolved.def.requires_caller_location(tcx) => { @@ -530,7 +527,7 @@ impl<'tcx> Instance<'tcx> { }) ) { - if tcx.is_closure_or_coroutine(def) { + if tcx.is_closure_like(def) { debug!(" => vtable fn pointer created for closure with #[track_caller]: {:?} for method {:?} {:?}", def, def_id, args); diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 4feaeb0dd0523..c674a868d9fa2 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -541,13 +541,15 @@ impl<'tcx> TyCtxt<'tcx> { Ok(()) } - /// Returns `true` if `def_id` refers to a closure (e.g., `|x| x * 2`). Note - /// that closures have a `DefId`, but the closure *expression* also - /// has a `HirId` that is located within the context where the - /// closure appears (and, sadly, a corresponding `NodeId`, since - /// those are not yet phased out). The parent of the closure's - /// `DefId` will also be the context where it appears. - pub fn is_closure_or_coroutine(self, def_id: DefId) -> bool { + /// Returns `true` if `def_id` refers to a closure, coroutine, or coroutine-closure + /// (i.e. an async closure). These are all represented by `hir::Closure`, and all + /// have the same `DefKind`. + /// + /// Note that closures have a `DefId`, but the closure *expression* also has a + // `HirId` that is located within the context where the closure appears (and, sadly, + // a corresponding `NodeId`, since those are not yet phased out). The parent of + // the closure's `DefId` will also be the context where it appears. + pub fn is_closure_like(self, def_id: DefId) -> bool { matches!(self.def_kind(def_id), DefKind::Closure) } diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 01fae7c0bec86..4ac8dde03a6cf 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -138,7 +138,7 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>( let (span, visible_macro) = unexpand_into_body_span_with_visible_macro(expn_span, body_span)?; - Some(SpanFromMir::new(span, visible_macro, bcb, is_closure_or_coroutine(statement))) + Some(SpanFromMir::new(span, visible_macro, bcb, is_closure_like(statement))) }); let terminator_span = Some(data.terminator()).into_iter().filter_map(move |terminator| { @@ -153,7 +153,7 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>( }) } -fn is_closure_or_coroutine(statement: &Statement<'_>) -> bool { +fn is_closure_like(statement: &Statement<'_>) -> bool { match statement.kind { StatementKind::Assign(box (_, Rvalue::Aggregate(box ref agg_kind, _))) => match agg_kind { AggregateKind::Closure(_, _) diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 3376af986531e..a771fa86eb7d1 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1154,7 +1154,7 @@ fn create_fn_mono_item<'tcx>( let def_id = instance.def_id(); if tcx.sess.opts.unstable_opts.profile_closures && def_id.is_local() - && tcx.is_closure_or_coroutine(def_id) + && tcx.is_closure_like(def_id) { crate::util::dump_closure_profile(tcx, instance); } diff --git a/compiler/rustc_passes/src/upvars.rs b/compiler/rustc_passes/src/upvars.rs index ded20c38543d4..4d44e8762b0d1 100644 --- a/compiler/rustc_passes/src/upvars.rs +++ b/compiler/rustc_passes/src/upvars.rs @@ -11,7 +11,7 @@ use rustc_span::Span; pub fn provide(providers: &mut Providers) { providers.upvars_mentioned = |tcx, def_id| { - if !tcx.is_closure_or_coroutine(def_id) { + if !tcx.is_closure_like(def_id) { return None; } diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs index 700a5dd4a8511..6528a7b369f74 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs @@ -101,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals { fn is_by_value_closure_capture(cx: &LateContext<'_>, redefinition: HirId, root_variable: HirId) -> bool { let closure_def_id = cx.tcx.hir().enclosing_body_owner(redefinition); - cx.tcx.is_closure_or_coroutine(closure_def_id.to_def_id()) + cx.tcx.is_closure_like(closure_def_id.to_def_id()) && cx.tcx.closure_captures(closure_def_id).iter().any(|c| { matches!(c.info.capture_kind, UpvarCapture::ByValue) && matches!(c.place.base, PlaceBase::Upvar(upvar) if upvar.var_path.hir_id == root_variable) From 87816378ab4190583df1b74bcfeacb8bc5dd4d70 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 11 Feb 2024 22:09:28 +0000 Subject: [PATCH 2/2] Fix async closures in CTFE --- .../rustc_const_eval/src/interpret/util.rs | 1 + .../src/interpret/validity.rs | 4 +- src/tools/miri/tests/pass/async-closure.rs | 40 +++++++++++++++++++ .../miri/tests/pass/async-closure.stdout | 3 ++ 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 src/tools/miri/tests/pass/async-closure.rs create mode 100644 src/tools/miri/tests/pass/async-closure.stdout diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 416443f5f4d22..52b79203d2b46 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -34,6 +34,7 @@ where match *ty.kind() { ty::Param(_) => ControlFlow::Break(FoundParam), ty::Closure(def_id, args) + | ty::CoroutineClosure(def_id, args, ..) | ty::Coroutine(def_id, args, ..) | ty::FnDef(def_id, args) => { let instance = ty::InstanceDef::Item(def_id); diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 38aeace02ba4b..dd14542ad8f22 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -236,8 +236,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // Now we know we are projecting to a field, so figure out which one. match layout.ty.kind() { - // coroutines and closures. - ty::Closure(def_id, _) | ty::Coroutine(def_id, _) => { + // coroutines, closures, and coroutine-closures all have upvars that may be named. + ty::Closure(def_id, _) | ty::Coroutine(def_id, _) | ty::CoroutineClosure(def_id, _) => { let mut name = None; // FIXME this should be more descriptive i.e. CapturePlace instead of CapturedVar // https://github.com/rust-lang/project-rfc-2229/issues/46 diff --git a/src/tools/miri/tests/pass/async-closure.rs b/src/tools/miri/tests/pass/async-closure.rs new file mode 100644 index 0000000000000..9b2fc2948bf45 --- /dev/null +++ b/src/tools/miri/tests/pass/async-closure.rs @@ -0,0 +1,40 @@ +#![feature(async_closure, noop_waker, async_fn_traits)] + +use std::future::Future; +use std::pin::pin; +use std::task::*; + +pub fn block_on(fut: impl Future) -> T { + let mut fut = pin!(fut); + let ctx = &mut Context::from_waker(Waker::noop()); + + loop { + match fut.as_mut().poll(ctx) { + Poll::Pending => {} + Poll::Ready(t) => break t, + } + } +} + +async fn call_once(f: impl async FnOnce(DropMe)) { + f(DropMe("world")).await; +} + +#[derive(Debug)] +struct DropMe(&'static str); + +impl Drop for DropMe { + fn drop(&mut self) { + println!("{}", self.0); + } +} + +pub fn main() { + block_on(async { + let b = DropMe("hello"); + let async_closure = async move |a: DropMe| { + println!("{a:?} {b:?}"); + }; + call_once(async_closure).await; + }); +} diff --git a/src/tools/miri/tests/pass/async-closure.stdout b/src/tools/miri/tests/pass/async-closure.stdout new file mode 100644 index 0000000000000..34cfdedc44ace --- /dev/null +++ b/src/tools/miri/tests/pass/async-closure.stdout @@ -0,0 +1,3 @@ +DropMe("world") DropMe("hello") +world +hello