@@ -2,7 +2,7 @@ use std::collections::HashSet;
22use std:: iter;
33use std:: ops:: ControlFlow ;
44
5- use crate :: clauses:: ClauseBuilder ;
5+ use crate :: clauses:: { super_traits :: super_traits , ClauseBuilder } ;
66use crate :: rust_ir:: AdtKind ;
77use crate :: { Interner , RustIrDatabase , TraitRef , WellKnownTrait } ;
88use chalk_ir:: {
@@ -136,17 +136,27 @@ fn uses_outer_binder_params<I: Interner>(
136136 matches ! ( flow, ControlFlow :: Break ( _) )
137137}
138138
139- fn principal_id < I : Interner > (
139+ fn principal_trait_ref < I : Interner > (
140140 db : & dyn RustIrDatabase < I > ,
141141 bounds : & Binders < QuantifiedWhereClauses < I > > ,
142- ) -> Option < TraitId < I > > {
143- let interner = db. interner ( ) ;
144-
142+ ) -> Option < Binders < Binders < TraitRef < I > > > > {
145143 bounds
146- . skip_binders ( )
147- . iter ( interner)
148- . filter_map ( |b| b. trait_id ( ) )
149- . find ( |& id| !db. trait_datum ( id) . is_auto_trait ( ) )
144+ . map_ref ( |b| b. iter ( db. interner ( ) ) )
145+ . into_iter ( )
146+ . find_map ( |b| {
147+ b. filter_map ( |qwc| {
148+ qwc. as_ref ( ) . filter_map ( |wc| match wc {
149+ WhereClause :: Implemented ( trait_ref) => {
150+ if db. trait_datum ( trait_ref. trait_id ) . is_auto_trait ( ) {
151+ None
152+ } else {
153+ Some ( trait_ref. clone ( ) )
154+ }
155+ }
156+ _ => None ,
157+ } )
158+ } )
159+ } )
150160}
151161
152162fn auto_trait_ids < ' a , I : Interner > (
@@ -191,6 +201,7 @@ pub fn add_unsize_program_clauses<I: Interner>(
191201
192202 match ( source_ty. kind ( interner) , target_ty. kind ( interner) ) {
193203 // dyn Trait + AutoX + 'a -> dyn Trait + AutoY + 'b
204+ // dyn TraitA + AutoX + 'a -> dyn TraitB + AutoY + 'b (upcasting)
194205 (
195206 TyKind :: Dyn ( DynTy {
196207 bounds : bounds_a,
@@ -201,21 +212,30 @@ pub fn add_unsize_program_clauses<I: Interner>(
201212 lifetime : lifetime_b,
202213 } ) ,
203214 ) => {
204- let principal_a = principal_id ( db, bounds_a) ;
205- let principal_b = principal_id ( db, bounds_b) ;
215+ let principal_trait_ref_a = principal_trait_ref ( db, bounds_a) ;
216+ let principal_a = principal_trait_ref_a
217+ . as_ref ( )
218+ . map ( |trait_ref| trait_ref. skip_binders ( ) . skip_binders ( ) . trait_id ) ;
219+ let principal_b = principal_trait_ref ( db, bounds_b)
220+ . map ( |trait_ref| trait_ref. skip_binders ( ) . skip_binders ( ) . trait_id ) ;
206221
207222 let auto_trait_ids_a: Vec < _ > = auto_trait_ids ( db, bounds_a) . collect ( ) ;
208223 let auto_trait_ids_b: Vec < _ > = auto_trait_ids ( db, bounds_b) . collect ( ) ;
209224
210- let may_apply = principal_a == principal_b
211- && auto_trait_ids_b
212- . iter ( )
213- . all ( |id_b| auto_trait_ids_a. iter ( ) . any ( |id_a| id_a == id_b) ) ;
214-
215- if !may_apply {
225+ let auto_traits_compatible = auto_trait_ids_a
226+ . iter ( )
227+ . all ( |id_b| auto_trait_ids_a. contains ( & id_b) ) ;
228+ if !auto_traits_compatible {
216229 return ;
217230 }
218231
232+ // Check that source lifetime outlives target lifetime
233+ let lifetime_outlives_goal: Goal < I > = WhereClause :: LifetimeOutlives ( LifetimeOutlives {
234+ a : lifetime_a. clone ( ) ,
235+ b : lifetime_b. clone ( ) ,
236+ } )
237+ . cast ( interner) ;
238+
219239 // COMMENT FROM RUSTC:
220240 // ------------------
221241 // Require that the traits involved in this upcast are **equal**;
@@ -239,42 +259,108 @@ pub fn add_unsize_program_clauses<I: Interner>(
239259 //
240260 // In order for the coercion to be valid, this new type
241261 // should be equal to target type.
242- let new_source_ty = TyKind :: Dyn ( DynTy {
243- bounds : bounds_a. map_ref ( |bounds| {
244- QuantifiedWhereClauses :: from_iter (
245- interner,
246- bounds. iter ( interner) . filter ( |bound| {
247- let trait_id = match bound. trait_id ( ) {
248- Some ( id) => id,
249- None => return true ,
250- } ;
251-
252- if auto_trait_ids_a. iter ( ) . all ( |& id_a| id_a != trait_id) {
253- return true ;
254- }
255- auto_trait_ids_b. iter ( ) . any ( |& id_b| id_b == trait_id)
262+ if principal_a == principal_b {
263+ let new_source_ty = TyKind :: Dyn ( DynTy {
264+ bounds : bounds_a. map_ref ( |bounds| {
265+ QuantifiedWhereClauses :: from_iter (
266+ interner,
267+ bounds. iter ( interner) . filter ( |bound| {
268+ let trait_id = match bound. trait_id ( ) {
269+ Some ( id) => id,
270+ None => return true ,
271+ } ;
272+
273+ if !auto_trait_ids_a. contains ( & trait_id) {
274+ return true ;
275+ }
276+ auto_trait_ids_b. contains ( & trait_id)
277+ } ) ,
278+ )
279+ } ) ,
280+ lifetime : lifetime_b. clone ( ) ,
281+ } )
282+ . intern ( interner) ;
283+
284+ // Check that new source is equal to target
285+ let eq_goal = EqGoal {
286+ a : new_source_ty. cast ( interner) ,
287+ b : target_ty. clone ( ) . cast ( interner) ,
288+ }
289+ . cast ( interner) ;
290+
291+ builder. push_clause ( trait_ref, [ eq_goal, lifetime_outlives_goal] ) ;
292+ } else if let ( Some ( principal_a) , Some ( principal_b) ) = ( principal_a, principal_b) {
293+ let principal_trait_ref_a = principal_trait_ref_a. unwrap ( ) ;
294+ let applicable_super_traits =
295+ super_traits ( db, principal_a)
296+ . into_iter ( )
297+ . filter ( |trait_ref| {
298+ trait_ref. skip_binders ( ) . skip_binders ( ) . trait_id == principal_b
299+ } ) ;
300+
301+ for super_trait_ref in applicable_super_traits {
302+ // `super_trait_ref` is, at this point, quantified over generic params of
303+ // `principal_a` and relevant higher-ranked lifetimes that come from super
304+ // trait elaboration (see comments on `super_traits()`).
305+ //
306+ // So if we have `trait Trait<'a, T>: for<'b> Super<'a, 'b, T> {}`,
307+ // `super_trait_ref` can be something like
308+ // `for<Self, 'a, T> for<'b> Self: Super<'a, 'b, T>`.
309+ //
310+ // We need to convert it into a bound for `DynTy`. We do this by substituting
311+ // bound vars of `principal_trait_ref_a` and then fusing inner binders for
312+ // higher-ranked lifetimes.
313+ let rebound_super_trait_ref = principal_trait_ref_a. map_ref ( |q_trait_ref_a| {
314+ q_trait_ref_a
315+ . map_ref ( |trait_ref_a| {
316+ super_trait_ref. substitute ( interner, & trait_ref_a. substitution )
317+ } )
318+ . fuse_binders ( interner)
319+ } ) ;
320+
321+ // Skip `for<Self>` binder. We'll rebind it immediately below.
322+ let new_principal_trait_ref = rebound_super_trait_ref
323+ . into_value_and_skipped_binders ( )
324+ . 0
325+ . map ( |it| it. cast ( interner) ) ;
326+
327+ // Swap trait ref for `principal_a` with the new trait ref, drop the auto
328+ // traits not included in the upcast target.
329+ let new_source_ty = TyKind :: Dyn ( DynTy {
330+ bounds : bounds_a. map_ref ( |bounds| {
331+ QuantifiedWhereClauses :: from_iter (
332+ interner,
333+ bounds. iter ( interner) . cloned ( ) . filter_map ( |bound| {
334+ let trait_id = match bound. trait_id ( ) {
335+ Some ( id) => id,
336+ None => return Some ( bound) ,
337+ } ;
338+
339+ if principal_a == trait_id {
340+ Some ( new_principal_trait_ref. clone ( ) )
341+ } else {
342+ auto_trait_ids_b. contains ( & trait_id) . then_some ( bound)
343+ }
344+ } ) ,
345+ )
256346 } ) ,
257- )
258- } ) ,
259- lifetime : lifetime_b. clone ( ) ,
260- } )
261- . intern ( interner) ;
347+ lifetime : lifetime_b. clone ( ) ,
348+ } )
349+ . intern ( interner) ;
350+
351+ // Check that new source is equal to target
352+ let eq_goal = EqGoal {
353+ a : new_source_ty. cast ( interner) ,
354+ b : target_ty. clone ( ) . cast ( interner) ,
355+ }
356+ . cast ( interner) ;
262357
263- // Check that new source is equal to target
264- let eq_goal = EqGoal {
265- a : new_source_ty. cast ( interner) ,
266- b : target_ty. clone ( ) . cast ( interner) ,
358+ // We don't push goal for `principal_b`'s object safety because it's implied by
359+ // `principal_a`'s object safety.
360+ builder
361+ . push_clause ( trait_ref. clone ( ) , [ eq_goal, lifetime_outlives_goal. clone ( ) ] ) ;
362+ }
267363 }
268- . cast ( interner) ;
269-
270- // Check that source lifetime outlives target lifetime
271- let lifetime_outlives_goal: Goal < I > = WhereClause :: LifetimeOutlives ( LifetimeOutlives {
272- a : lifetime_a. clone ( ) ,
273- b : lifetime_b. clone ( ) ,
274- } )
275- . cast ( interner) ;
276-
277- builder. push_clause ( trait_ref, [ eq_goal, lifetime_outlives_goal] . iter ( ) ) ;
278364 }
279365
280366 // T -> dyn Trait + 'a
0 commit comments