@@ -40,13 +40,16 @@ use rustc_hir::def_id::DefId;
4040use rustc_hir:: def_id:: LocalDefId ;
4141use rustc_hir:: intravisit:: { self , NestedVisitorMap , Visitor } ;
4242use rustc_infer:: infer:: UpvarRegion ;
43- use rustc_middle:: hir:: place:: { Place , PlaceBase , PlaceWithHirId , ProjectionKind } ;
43+ use rustc_middle:: hir:: place:: { Place , PlaceBase , PlaceWithHirId , Projection , ProjectionKind } ;
4444use rustc_middle:: ty:: fold:: TypeFoldable ;
4545use rustc_middle:: ty:: { self , Ty , TyCtxt , TypeckResults , UpvarSubsts } ;
4646use rustc_session:: lint;
4747use rustc_span:: sym;
4848use rustc_span:: { MultiSpan , Span , Symbol } ;
4949
50+ use rustc_index:: vec:: Idx ;
51+ use rustc_target:: abi:: VariantIdx ;
52+
5053/// Describe the relationship between the paths of two places
5154/// eg:
5255/// - `foo` is ancestor of `foo.bar.baz`
@@ -537,18 +540,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
537540 span : Span ,
538541 body : & ' tcx hir:: Body < ' tcx > ,
539542 ) {
540- let need_migrations = self . compute_2229_migrations_first_pass (
543+ let need_migrations_first_pass = self . compute_2229_migrations_first_pass (
541544 closure_def_id,
542545 span,
543546 capture_clause,
544547 body,
545548 self . typeck_results . borrow ( ) . closure_min_captures . get ( & closure_def_id) ,
546549 ) ;
547550
548- if !need_migrations. is_empty ( ) {
549- let need_migrations_hir_id = need_migrations. iter ( ) . map ( |m| m. 0 ) . collect :: < Vec < _ > > ( ) ;
551+ let need_migrations = self . compute_2229_migrations_precise_pass (
552+ closure_def_id,
553+ span,
554+ self . typeck_results . borrow ( ) . closure_min_captures . get ( & closure_def_id) ,
555+ & need_migrations_first_pass,
556+ ) ;
550557
551- let migrations_text = migration_suggestion_for_2229 ( self . tcx , & need_migrations_hir_id) ;
558+ if !need_migrations. is_empty ( ) {
559+ let migrations_text = migration_suggestion_for_2229 ( self . tcx , & need_migrations) ;
552560
553561 let local_def_id = closure_def_id. expect_local ( ) ;
554562 let closure_hir_id = self . tcx . hir ( ) . local_def_id_to_hir_id ( local_def_id) ;
@@ -642,6 +650,310 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
642650 need_migrations
643651 }
644652
653+ fn compute_2229_migrations_precise_pass (
654+ & self ,
655+ closure_def_id : DefId ,
656+ closure_span : Span ,
657+ min_captures : Option < & ty:: RootVariableMinCaptureList < ' tcx > > ,
658+ need_migrations : & [ ( hir:: HirId , Ty < ' tcx > ) ] ,
659+ ) -> Vec < hir:: HirId > {
660+ // Need migrations -- second pass
661+ let mut need_migrations_2 = Vec :: new ( ) ;
662+
663+ for ( hir_id, ty) in need_migrations {
664+ let projections_list = min_captures
665+ . and_then ( |m| m. get ( hir_id) )
666+ . into_iter ( )
667+ . flatten ( )
668+ . filter_map ( |captured_place| match captured_place. info . capture_kind {
669+ // Only care about captures that are moved into the closure
670+ ty:: UpvarCapture :: ByValue ( ..) => {
671+ Some ( captured_place. place . projections . as_slice ( ) )
672+ }
673+ ty:: UpvarCapture :: ByRef ( ..) => None ,
674+ } )
675+ . collect ( ) ;
676+
677+ if self . has_significant_drop_outside_of_captures (
678+ closure_def_id,
679+ closure_span,
680+ ty,
681+ projections_list,
682+ ) {
683+ need_migrations_2. push ( * hir_id) ;
684+ }
685+ }
686+
687+ need_migrations_2
688+ }
689+
690+ /// This is a helper function to `compute_2229_migrations_precise_pass`. Provided the type
691+ /// of a root variable and a list of captured paths starting at this root variable (expressed
692+ /// using list of `Projection` slices), it returns true if there is a path that is not
693+ /// captured starting at this root variable that implements Drop.
694+ ///
695+ /// FIXME(project-rfc-2229#35): This should return true only for significant drops.
696+ /// A drop is significant if it's implemented by the user or does
697+ /// anything that will have any observable behavior (other than
698+ /// freeing up memory).
699+ ///
700+ /// The way this function works is at a given call it looks at type `base_path_ty` of some base
701+ /// path say P and then vector of projection slices which represent the different captures
702+ /// starting off of P.
703+ ///
704+ /// This will make more sense with an example:
705+ ///
706+ /// ```rust
707+ /// #![feature(capture_disjoint_fields)]
708+ ///
709+ /// struct FancyInteger(i32); // This implements Drop
710+ ///
711+ /// struct Point { x: FancyInteger, y: FancyInteger }
712+ /// struct Color;
713+ ///
714+ /// struct Wrapper { p: Point, c: Color }
715+ ///
716+ /// fn f(w: Wrapper) {
717+ /// let c = || {
718+ /// // Closure captures w.p.x and w.c by move.
719+ /// };
720+ ///
721+ /// c();
722+ /// }
723+ /// ```
724+ ///
725+ /// If `capture_disjoint_fields` wasn't enabled the closure would've moved `w` instead of the
726+ /// precise paths. If we look closely `w.p.y` isn't captured which implements Drop and
727+ /// therefore Drop ordering would change and we want this function to return true.
728+ ///
729+ /// Call stack to figure out if we need to migrate for `w` would look as follows:
730+ ///
731+ /// Our initial base path is just `w`, and the paths captured from it are `w[p, x]` and
732+ /// `w[c]`.
733+ /// Notation:
734+ /// - Ty(place): Type of place
735+ /// - `(a, b)`: Represents the function parameters `base_path_ty` and `captured_projs`
736+ /// respectively.
737+ /// ```
738+ /// (Ty(w), [ &[p, x], &[c] ])
739+ /// |
740+ /// ----------------------------
741+ /// | |
742+ /// v v
743+ /// (Ty(w.p), [ &[x] ]) (Ty(w.c), [ &[] ]) // I(1)
744+ /// | |
745+ /// v v
746+ /// (Ty(w.p), [ &[x] ]) false
747+ /// |
748+ /// |
749+ /// -------------------------------
750+ /// | |
751+ /// v v
752+ /// (Ty((w.p).x), [ &[] ]) (Ty((w.p).y), []) // IMP 2
753+ /// | |
754+ /// v v
755+ /// false NeedsDrop(Ty(w.p.y))
756+ /// |
757+ /// v
758+ /// true
759+ /// ```
760+ ///
761+ /// IMP 1 `(Ty(w.c), [ &[] ])`: Notice the single empty slice inside `captured_projs`.
762+ /// This implies that the `w.c` is completely captured by the closure.
763+ /// Since drop for this path will be called when the closure is
764+ /// dropped we don't need to migrate for it.
765+ ///
766+ /// IMP 2 `(Ty((w.p).y), [])`: Notice that `captured_projs` is empty. This implies that this
767+ /// path wasn't captured by the closure. Also note that even
768+ /// though we didn't capture this path, the function visits it,
769+ /// which is kind of the point of this function. We then return
770+ /// if the type of `w.p.y` implements Drop, which in this case is
771+ /// true.
772+ ///
773+ /// Consider another example:
774+ ///
775+ /// ```rust
776+ /// struct X;
777+ /// impl Drop for X {}
778+ ///
779+ /// struct Y(X);
780+ /// impl Drop for Y {}
781+ ///
782+ /// fn foo() {
783+ /// let y = Y(X);
784+ /// let c = || move(y.0);
785+ /// }
786+ /// ```
787+ ///
788+ /// Note that `y.0` is captured by the closure. When this function is called for `y`, it will
789+ /// return true, because even though all paths starting at `y` are captured, `y` itself
790+ /// implements Drop which will be affected since `y` isn't completely captured.
791+ fn has_significant_drop_outside_of_captures (
792+ & self ,
793+ closure_def_id : DefId ,
794+ closure_span : Span ,
795+ base_path_ty : Ty < ' tcx > ,
796+ captured_projs : Vec < & [ Projection < ' tcx > ] > ,
797+ ) -> bool {
798+ let needs_drop = |ty : Ty < ' tcx > | {
799+ ty. needs_drop ( self . tcx , self . tcx . param_env ( closure_def_id. expect_local ( ) ) )
800+ } ;
801+
802+ let is_drop_defined_for_ty = |ty : Ty < ' tcx > | {
803+ let drop_trait = self . tcx . require_lang_item ( hir:: LangItem :: Drop , Some ( closure_span) ) ;
804+ let ty_params = self . tcx . mk_substs_trait ( base_path_ty, & [ ] ) ;
805+ self . tcx . type_implements_trait ( (
806+ drop_trait,
807+ ty,
808+ ty_params,
809+ self . tcx . param_env ( closure_def_id. expect_local ( ) ) ,
810+ ) )
811+ } ;
812+
813+ let is_drop_defined_for_ty = is_drop_defined_for_ty ( base_path_ty) ;
814+
815+ // If there is a case where no projection is applied on top of current place
816+ // then there must be exactly one capture corresponding to such a case. Note that this
817+ // represents the case of the path being completely captured by the variable.
818+ //
819+ // eg. If `a.b` is captured and we are processing `a.b`, then we can't have the closure also
820+ // capture `a.b.c`, because that voilates min capture.
821+ let is_completely_captured = captured_projs. iter ( ) . any ( |projs| projs. is_empty ( ) ) ;
822+
823+ assert ! ( !is_completely_captured || ( captured_projs. len( ) == 1 ) ) ;
824+
825+ if is_drop_defined_for_ty {
826+ // If drop is implemented for this type then we need it to be fully captured, or
827+ // it will require migration.
828+ return !is_completely_captured;
829+ }
830+
831+ if is_completely_captured {
832+ // The place is captured entirely, so doesn't matter if needs dtor, it will be drop
833+ // when the closure is dropped.
834+ return false ;
835+ }
836+
837+ match base_path_ty. kind ( ) {
838+ _ if captured_projs. is_empty ( ) => needs_drop ( base_path_ty) ,
839+
840+ // Observations:
841+ // - `captured_projs` is not empty. Therefore we can call
842+ // `captured_projs.first().unwrap()` safely.
843+ // - All entries in `captured_projs` have atleast one projection.
844+ // Therefore we can call `captured_projs.first().unwrap().first().unwrap()` safely.
845+ ty:: Adt ( def, _) if def. is_box ( ) => {
846+ // We must deref to access paths on top of a Box.
847+ assert ! (
848+ captured_projs
849+ . iter( )
850+ . all( |projs| matches!( projs. first( ) . unwrap( ) . kind, ProjectionKind :: Deref ) )
851+ ) ;
852+
853+ let next_ty = captured_projs. first ( ) . unwrap ( ) . first ( ) . unwrap ( ) . ty ;
854+ let captured_projs = captured_projs. iter ( ) . map ( |projs| & projs[ 1 ..] ) . collect ( ) ;
855+ self . has_significant_drop_outside_of_captures (
856+ closure_def_id,
857+ closure_span,
858+ next_ty,
859+ captured_projs,
860+ )
861+ }
862+
863+ ty:: Adt ( def, substs) => {
864+ // Multi-varaint enums are captured in entirety,
865+ // which would've been handled in the case of single empty slice in `captured_projs`.
866+ assert_eq ! ( def. variants. len( ) , 1 ) ;
867+
868+ // Only Field projections can be applied to a non-box Adt.
869+ assert ! (
870+ captured_projs. iter( ) . all( |projs| matches!(
871+ projs. first( ) . unwrap( ) . kind,
872+ ProjectionKind :: Field ( ..)
873+ ) )
874+ ) ;
875+ def. variants . get ( VariantIdx :: new ( 0 ) ) . unwrap ( ) . fields . iter ( ) . enumerate ( ) . any (
876+ |( i, field) | {
877+ let paths_using_field = captured_projs
878+ . iter ( )
879+ . filter_map ( |projs| {
880+ if let ProjectionKind :: Field ( field_idx, _) =
881+ projs. first ( ) . unwrap ( ) . kind
882+ {
883+ if ( field_idx as usize ) == i { Some ( & projs[ 1 ..] ) } else { None }
884+ } else {
885+ unreachable ! ( ) ;
886+ }
887+ } )
888+ . collect ( ) ;
889+
890+ let after_field_ty = field. ty ( self . tcx , substs) ;
891+ self . has_significant_drop_outside_of_captures (
892+ closure_def_id,
893+ closure_span,
894+ after_field_ty,
895+ paths_using_field,
896+ )
897+ } ,
898+ )
899+ }
900+
901+ ty:: Tuple ( ..) => {
902+ // Only Field projections can be applied to a tuple.
903+ assert ! (
904+ captured_projs. iter( ) . all( |projs| matches!(
905+ projs. first( ) . unwrap( ) . kind,
906+ ProjectionKind :: Field ( ..)
907+ ) )
908+ ) ;
909+
910+ base_path_ty. tuple_fields ( ) . enumerate ( ) . any ( |( i, element_ty) | {
911+ let paths_using_field = captured_projs
912+ . iter ( )
913+ . filter_map ( |projs| {
914+ if let ProjectionKind :: Field ( field_idx, _) = projs. first ( ) . unwrap ( ) . kind
915+ {
916+ if ( field_idx as usize ) == i { Some ( & projs[ 1 ..] ) } else { None }
917+ } else {
918+ unreachable ! ( ) ;
919+ }
920+ } )
921+ . collect ( ) ;
922+
923+ self . has_significant_drop_outside_of_captures (
924+ closure_def_id,
925+ closure_span,
926+ element_ty,
927+ paths_using_field,
928+ )
929+ } )
930+ }
931+
932+ ty:: Ref ( _, deref_ty, _) => {
933+ // Only Derefs can be applied to a Ref
934+ assert ! (
935+ captured_projs
936+ . iter( )
937+ . all( |projs| matches!( projs. first( ) . unwrap( ) . kind, ProjectionKind :: Deref ) )
938+ ) ;
939+
940+ let captured_projs = captured_projs. iter ( ) . map ( |projs| & projs[ 1 ..] ) . collect ( ) ;
941+ self . has_significant_drop_outside_of_captures (
942+ closure_def_id,
943+ closure_span,
944+ deref_ty,
945+ captured_projs,
946+ )
947+ }
948+
949+ // Unsafe Ptrs are captured in their entirety, which would've have been handled in
950+ // the case of single empty slice in `captured_projs`.
951+ ty:: RawPtr ( ..) => unreachable ! ( ) ,
952+
953+ _ => unreachable ! ( ) ,
954+ }
955+ }
956+
645957 fn init_capture_kind (
646958 & self ,
647959 capture_clause : hir:: CaptureBy ,
0 commit comments