@@ -2,7 +2,7 @@ use std::collections::HashSet;
2
2
use std:: iter;
3
3
use std:: ops:: ControlFlow ;
4
4
5
- use crate :: clauses:: ClauseBuilder ;
5
+ use crate :: clauses:: { super_traits :: super_traits , ClauseBuilder } ;
6
6
use crate :: rust_ir:: AdtKind ;
7
7
use crate :: { Interner , RustIrDatabase , TraitRef , WellKnownTrait } ;
8
8
use chalk_ir:: {
@@ -136,17 +136,27 @@ fn uses_outer_binder_params<I: Interner>(
136
136
matches ! ( flow, ControlFlow :: Break ( _) )
137
137
}
138
138
139
- fn principal_id < I : Interner > (
139
+ fn principal_trait_ref < I : Interner > (
140
140
db : & dyn RustIrDatabase < I > ,
141
141
bounds : & Binders < QuantifiedWhereClauses < I > > ,
142
- ) -> Option < TraitId < I > > {
143
- let interner = db. interner ( ) ;
144
-
142
+ ) -> Option < Binders < Binders < TraitRef < I > > > > {
145
143
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
+ } )
150
160
}
151
161
152
162
fn auto_trait_ids < ' a , I : Interner > (
@@ -191,6 +201,7 @@ pub fn add_unsize_program_clauses<I: Interner>(
191
201
192
202
match ( source_ty. kind ( interner) , target_ty. kind ( interner) ) {
193
203
// dyn Trait + AutoX + 'a -> dyn Trait + AutoY + 'b
204
+ // dyn TraitA + AutoX + 'a -> dyn TraitB + AutoY + 'b (upcasting)
194
205
(
195
206
TyKind :: Dyn ( DynTy {
196
207
bounds : bounds_a,
@@ -201,21 +212,30 @@ pub fn add_unsize_program_clauses<I: Interner>(
201
212
lifetime : lifetime_b,
202
213
} ) ,
203
214
) => {
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 ) ;
206
221
207
222
let auto_trait_ids_a: Vec < _ > = auto_trait_ids ( db, bounds_a) . collect ( ) ;
208
223
let auto_trait_ids_b: Vec < _ > = auto_trait_ids ( db, bounds_b) . collect ( ) ;
209
224
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 {
216
229
return ;
217
230
}
218
231
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
+
219
239
// COMMENT FROM RUSTC:
220
240
// ------------------
221
241
// Require that the traits involved in this upcast are **equal**;
@@ -239,42 +259,108 @@ pub fn add_unsize_program_clauses<I: Interner>(
239
259
//
240
260
// In order for the coercion to be valid, this new type
241
261
// 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
+ )
256
346
} ) ,
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) ;
262
357
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
+ }
267
363
}
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 ( ) ) ;
278
364
}
279
365
280
366
// T -> dyn Trait + 'a
0 commit comments