@@ -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,109 @@ 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+ . map ( |( super_trait_refs, _) | super_trait_refs)
297+ . into_iter ( )
298+ . filter ( |trait_ref| {
299+ trait_ref. skip_binders ( ) . skip_binders ( ) . trait_id == principal_b
300+ } ) ;
301+
302+ for super_trait_ref in applicable_super_traits {
303+ // `super_trait_ref` is, at this point, quantified over generic params of
304+ // `principal_a` and relevant higher-ranked lifetimes that come from super
305+ // trait elaboration (see comments on `super_traits()`).
306+ //
307+ // So if we have `trait Trait<'a, T>: for<'b> Super<'a, 'b, T> {}`,
308+ // `super_trait_ref` can be something like
309+ // `for<Self, 'a, T> for<'b> Self: Super<'a, 'b, T>`.
310+ //
311+ // We need to convert it into a bound for `DynTy`. We do this by substituting
312+ // bound vars of `principal_trait_ref_a` and then fusing inner binders for
313+ // higher-ranked lifetimes.
314+ let rebound_super_trait_ref = principal_trait_ref_a. map_ref ( |q_trait_ref_a| {
315+ q_trait_ref_a
316+ . map_ref ( |trait_ref_a| {
317+ super_trait_ref. substitute ( interner, & trait_ref_a. substitution )
318+ } )
319+ . fuse_binders ( interner)
320+ } ) ;
321+
322+ // Skip `for<Self>` binder. We'll rebind it immediately below.
323+ let new_principal_trait_ref = rebound_super_trait_ref
324+ . into_value_and_skipped_binders ( )
325+ . 0
326+ . map ( |it| it. cast ( interner) ) ;
327+
328+ // Swap trait ref for `principal_a` with the new trait ref, drop the auto
329+ // traits not included in the upcast target.
330+ let new_source_ty = TyKind :: Dyn ( DynTy {
331+ bounds : bounds_a. map_ref ( |bounds| {
332+ QuantifiedWhereClauses :: from_iter (
333+ interner,
334+ bounds. iter ( interner) . cloned ( ) . filter_map ( |bound| {
335+ let trait_id = match bound. trait_id ( ) {
336+ Some ( id) => id,
337+ None => return Some ( bound) ,
338+ } ;
339+
340+ if principal_a == trait_id {
341+ Some ( new_principal_trait_ref. clone ( ) )
342+ } else {
343+ auto_trait_ids_b. contains ( & trait_id) . then_some ( bound)
344+ }
345+ } ) ,
346+ )
256347 } ) ,
257- )
258- } ) ,
259- lifetime : lifetime_b. clone ( ) ,
260- } )
261- . intern ( interner) ;
348+ lifetime : lifetime_b. clone ( ) ,
349+ } )
350+ . intern ( interner) ;
351+
352+ // Check that new source is equal to target
353+ let eq_goal = EqGoal {
354+ a : new_source_ty. cast ( interner) ,
355+ b : target_ty. clone ( ) . cast ( interner) ,
356+ }
357+ . cast ( interner) ;
262358
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) ,
359+ // We don't push goal for `principal_b`'s object safety because it's implied by
360+ // `principal_a`'s object safety.
361+ builder
362+ . push_clause ( trait_ref. clone ( ) , [ eq_goal, lifetime_outlives_goal. clone ( ) ] ) ;
363+ }
267364 }
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 ( ) ) ;
278365 }
279366
280367 // T -> dyn Trait + 'a
0 commit comments