@@ -12,13 +12,249 @@ use check::regionck::{self, Rcx};
1212
1313use middle:: infer;
1414use middle:: region;
15- use middle:: subst;
15+ use middle:: subst:: { self , Subst } ;
1616use middle:: ty:: { self , Ty } ;
1717use util:: ppaux:: { Repr , UserString } ;
1818
1919use syntax:: ast;
20- use syntax:: codemap:: Span ;
20+ use syntax:: codemap:: { self , Span } ;
21+
22+ /// check_drop_impl confirms that the Drop implementation identfied by
23+ /// `drop_impl_did` is not any more specialized than the type it is
24+ /// attached to (Issue #8142).
25+ ///
26+ /// This means:
27+ ///
28+ /// 1. The self type must be nominal (this is already checked during
29+ /// coherence),
30+ ///
31+ /// 2. The generic region/type parameters of the impl's self-type must
32+ /// all be parameters of the Drop impl itself (i.e. no
33+ /// specialization like `impl Drop for Foo<i32>`), and,
34+ ///
35+ /// 3. Any bounds on the generic parameters must be reflected in the
36+ /// struct/enum definition for the nominal type itself (i.e.
37+ /// cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`).
38+ ///
39+ pub fn check_drop_impl ( tcx : & ty:: ctxt , drop_impl_did : ast:: DefId ) -> Result < ( ) , ( ) > {
40+ let ty:: TypeScheme { generics : ref dtor_generics,
41+ ty : ref dtor_self_type } = ty:: lookup_item_type ( tcx, drop_impl_did) ;
42+ let dtor_predicates = ty:: lookup_predicates ( tcx, drop_impl_did) ;
43+ match dtor_self_type. sty {
44+ ty:: ty_enum( self_type_did, self_to_impl_substs) |
45+ ty:: ty_struct( self_type_did, self_to_impl_substs) |
46+ ty:: ty_closure( self_type_did, self_to_impl_substs) => {
47+ try!( ensure_drop_params_and_item_params_correspond ( tcx,
48+ drop_impl_did,
49+ dtor_generics,
50+ dtor_self_type,
51+ self_type_did) ) ;
52+
53+ ensure_drop_predicates_are_implied_by_item_defn ( tcx,
54+ drop_impl_did,
55+ & dtor_predicates,
56+ self_type_did,
57+ self_to_impl_substs)
58+ }
59+ _ => {
60+ // Destructors only work on nominal types. This was
61+ // already checked by coherence, so we can panic here.
62+ let span = tcx. map . def_id_span ( drop_impl_did, codemap:: DUMMY_SP ) ;
63+ tcx. sess . span_bug (
64+ span, & format ! ( "should have been rejected by coherence check: {}" ,
65+ dtor_self_type. repr( tcx) ) ) ;
66+ }
67+ }
68+ }
69+
70+ fn ensure_drop_params_and_item_params_correspond < ' tcx > (
71+ tcx : & ty:: ctxt < ' tcx > ,
72+ drop_impl_did : ast:: DefId ,
73+ drop_impl_generics : & ty:: Generics < ' tcx > ,
74+ drop_impl_ty : & ty:: Ty < ' tcx > ,
75+ self_type_did : ast:: DefId ) -> Result < ( ) , ( ) >
76+ {
77+ // New strategy based on review suggestion from nikomatsakis.
78+ //
79+ // (In the text and code below, "named" denotes "struct/enum", and
80+ // "generic params" denotes "type and region params")
81+ //
82+ // 1. Create fresh skolemized type/region "constants" for each of
83+ // the named type's generic params. Instantiate the named type
84+ // with the fresh constants, yielding `named_skolem`.
85+ //
86+ // 2. Create unification variables for each of the Drop impl's
87+ // generic params. Instantiate the impl's Self's type with the
88+ // unification-vars, yielding `drop_unifier`.
89+ //
90+ // 3. Attempt to unify Self_unif with Type_skolem. If unification
91+ // succeeds, continue (i.e. with the predicate checks).
92+
93+ let ty:: TypeScheme { generics : ref named_type_generics,
94+ ty : named_type } =
95+ ty:: lookup_item_type ( tcx, self_type_did) ;
96+
97+ let infcx = infer:: new_infer_ctxt ( tcx) ;
98+ infcx. try ( |snapshot| {
99+ let ( named_type_to_skolem, skol_map) =
100+ infcx. construct_skolemized_subst ( named_type_generics, snapshot) ;
101+ let named_type_skolem = named_type. subst ( tcx, & named_type_to_skolem) ;
102+
103+ let drop_impl_span = tcx. map . def_id_span ( drop_impl_did, codemap:: DUMMY_SP ) ;
104+ let drop_to_unifier =
105+ infcx. fresh_substs_for_generics ( drop_impl_span, drop_impl_generics) ;
106+ let drop_unifier = drop_impl_ty. subst ( tcx, & drop_to_unifier) ;
107+
108+ if let Ok ( ( ) ) = infer:: mk_eqty ( & infcx, true , infer:: TypeOrigin :: Misc ( drop_impl_span) ,
109+ named_type_skolem, drop_unifier) {
110+ // Even if we did manage to equate the types, the process
111+ // may have just gathered unsolvable region constraints
112+ // like `R == 'static` (represented as a pair of subregion
113+ // constraints) for some skolemization constant R.
114+ //
115+ // However, the leak_check method allows us to confirm
116+ // that no skolemized regions escaped (i.e. were related
117+ // to other regions in the constraint graph).
118+ if let Ok ( ( ) ) = infcx. leak_check ( & skol_map, snapshot) {
119+ return Ok ( ( ) )
120+ }
121+ }
122+
123+ span_err ! ( tcx. sess, drop_impl_span, E0366 ,
124+ "Implementations of Drop cannot be specialized" ) ;
125+ let item_span = tcx. map . span ( self_type_did. node ) ;
126+ tcx. sess . span_note ( item_span,
127+ "Use same sequence of generic type and region \
128+ parameters that is on the struct/enum definition") ;
129+ return Err ( ( ) ) ;
130+ } )
131+ }
132+
133+ /// Confirms that every predicate imposed by dtor_predicates is
134+ /// implied by assuming the predicates attached to self_type_did.
135+ fn ensure_drop_predicates_are_implied_by_item_defn < ' tcx > (
136+ tcx : & ty:: ctxt < ' tcx > ,
137+ drop_impl_did : ast:: DefId ,
138+ dtor_predicates : & ty:: GenericPredicates < ' tcx > ,
139+ self_type_did : ast:: DefId ,
140+ self_to_impl_substs : & subst:: Substs < ' tcx > ) -> Result < ( ) , ( ) > {
141+
142+ // Here is an example, analogous to that from
143+ // `compare_impl_method`.
144+ //
145+ // Consider a struct type:
146+ //
147+ // struct Type<'c, 'b:'c, 'a> {
148+ // x: &'a Contents // (contents are irrelevant;
149+ // y: &'c Cell<&'b Contents>, // only the bounds matter for our purposes.)
150+ // }
151+ //
152+ // and a Drop impl:
153+ //
154+ // impl<'z, 'y:'z, 'x:'y> Drop for P<'z, 'y, 'x> {
155+ // fn drop(&mut self) { self.y.set(self.x); } // (only legal if 'x: 'y)
156+ // }
157+ //
158+ // We start out with self_to_impl_substs, that maps the generic
159+ // parameters of Type to that of the Drop impl.
160+ //
161+ // self_to_impl_substs = {'c => 'z, 'b => 'y, 'a => 'x}
162+ //
163+ // Applying this to the predicates (i.e. assumptions) provided by the item
164+ // definition yields the instantiated assumptions:
165+ //
166+ // ['y : 'z]
167+ //
168+ // We then check all of the predicates of the Drop impl:
169+ //
170+ // ['y:'z, 'x:'y]
171+ //
172+ // and ensure each is in the list of instantiated
173+ // assumptions. Here, `'y:'z` is present, but `'x:'y` is
174+ // absent. So we report an error that the Drop impl injected a
175+ // predicate that is not present on the struct definition.
176+
177+ assert_eq ! ( self_type_did. krate, ast:: LOCAL_CRATE ) ;
178+
179+ let drop_impl_span = tcx. map . def_id_span ( drop_impl_did, codemap:: DUMMY_SP ) ;
180+
181+ // We can assume the predicates attached to struct/enum definition
182+ // hold.
183+ let generic_assumptions = ty:: lookup_predicates ( tcx, self_type_did) ;
184+
185+ let assumptions_in_impl_context = generic_assumptions. instantiate ( tcx, & self_to_impl_substs) ;
186+ assert ! ( assumptions_in_impl_context. predicates. is_empty_in( subst:: SelfSpace ) ) ;
187+ assert ! ( assumptions_in_impl_context. predicates. is_empty_in( subst:: FnSpace ) ) ;
188+ let assumptions_in_impl_context =
189+ assumptions_in_impl_context. predicates . get_slice ( subst:: TypeSpace ) ;
190+
191+ // An earlier version of this code attempted to do this checking
192+ // via the traits::fulfill machinery. However, it ran into trouble
193+ // since the fulfill machinery merely turns outlives-predicates
194+ // 'a:'b and T:'b into region inference constraints. It is simpler
195+ // just to look for all the predicates directly.
196+
197+ assert ! ( dtor_predicates. predicates. is_empty_in( subst:: SelfSpace ) ) ;
198+ assert ! ( dtor_predicates. predicates. is_empty_in( subst:: FnSpace ) ) ;
199+ let predicates = dtor_predicates. predicates . get_slice ( subst:: TypeSpace ) ;
200+ for predicate in predicates {
201+ // (We do not need to worry about deep analysis of type
202+ // expressions etc because the Drop impls are already forced
203+ // to take on a structure that is roughly a alpha-renaming of
204+ // the generic parameters of the item definition.)
205+
206+ // This path now just checks *all* predicates via the direct
207+ // lookup, rather than using fulfill machinery.
208+ //
209+ // However, it may be more efficient in the future to batch
210+ // the analysis together via the fulfill , rather than the
211+ // repeated `contains` calls.
212+
213+ if !assumptions_in_impl_context. contains ( & predicate) {
214+ let item_span = tcx. map . span ( self_type_did. node ) ;
215+ let req = predicate. user_string ( tcx) ;
216+ span_err ! ( tcx. sess, drop_impl_span, E0367 ,
217+ "The requirement `{}` is added only by the Drop impl." , req) ;
218+ tcx. sess . span_note ( item_span,
219+ "The same requirement must be part of \
220+ the struct/enum definition") ;
221+ }
222+ }
223+
224+ if tcx. sess . has_errors ( ) {
225+ return Err ( ( ) ) ;
226+ }
227+ Ok ( ( ) )
228+ }
21229
230+ /// check_safety_of_destructor_if_necessary confirms that the type
231+ /// expression `typ` conforms to the "Drop Check Rule" from the Sound
232+ /// Generic Drop (RFC 769).
233+ ///
234+ /// ----
235+ ///
236+ /// The Drop Check Rule is the following:
237+ ///
238+ /// Let `v` be some value (either temporary or named) and 'a be some
239+ /// lifetime (scope). If the type of `v` owns data of type `D`, where
240+ ///
241+ /// (1.) `D` has a lifetime- or type-parametric Drop implementation, and
242+ /// (2.) the structure of `D` can reach a reference of type `&'a _`, and
243+ /// (3.) either:
244+ ///
245+ /// (A.) the Drop impl for `D` instantiates `D` at 'a directly,
246+ /// i.e. `D<'a>`, or,
247+ ///
248+ /// (B.) the Drop impl for `D` has some type parameter with a
249+ /// trait bound `T` where `T` is a trait that has at least
250+ /// one method,
251+ ///
252+ /// then 'a must strictly outlive the scope of v.
253+ ///
254+ /// ----
255+ ///
256+ /// This function is meant to by applied to the type for every
257+ /// expression in the program.
22258pub fn check_safety_of_destructor_if_necessary < ' a , ' tcx > ( rcx : & mut Rcx < ' a , ' tcx > ,
23259 typ : ty:: Ty < ' tcx > ,
24260 span : Span ,
0 commit comments