@@ -137,17 +137,27 @@ fn uses_outer_binder_params<I: Interner>(
137
137
matches ! ( flow, ControlFlow :: Break ( _) )
138
138
}
139
139
140
- fn principal_id < I : Interner > (
140
+ fn principal_trait_ref < I : Interner > (
141
141
db : & dyn RustIrDatabase < I > ,
142
142
bounds : & Binders < QuantifiedWhereClauses < I > > ,
143
- ) -> Option < TraitId < I > > {
144
- let interner = db. interner ( ) ;
145
-
143
+ ) -> Option < Binders < Binders < TraitRef < I > > > > {
146
144
bounds
147
- . skip_binders ( )
148
- . iter ( interner)
149
- . filter_map ( |b| b. trait_id ( ) )
150
- . find ( |& id| !db. trait_datum ( id) . is_auto_trait ( ) )
145
+ . map_ref ( |b| b. iter ( db. interner ( ) ) )
146
+ . into_iter ( )
147
+ . find_map ( |b| {
148
+ b. filter_map ( |qwc| {
149
+ qwc. as_ref ( ) . filter_map ( |wc| match wc {
150
+ WhereClause :: Implemented ( trait_ref) => {
151
+ if db. trait_datum ( trait_ref. trait_id ) . is_auto_trait ( ) {
152
+ None
153
+ } else {
154
+ Some ( trait_ref. clone ( ) )
155
+ }
156
+ }
157
+ _ => None ,
158
+ } )
159
+ } )
160
+ } )
151
161
}
152
162
153
163
fn auto_trait_ids < ' a , I : Interner > (
@@ -202,8 +212,12 @@ pub fn add_unsize_program_clauses<I: Interner>(
202
212
lifetime : lifetime_b,
203
213
} ) ,
204
214
) => {
205
- let principal_a = principal_id ( db, bounds_a) ;
206
- 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 ) ;
207
221
208
222
// Include super traits in a list of auto traits for A,
209
223
// to allow `dyn Trait -> dyn Trait + X` if `Trait: X`.
@@ -233,6 +247,13 @@ pub fn add_unsize_program_clauses<I: Interner>(
233
247
return ;
234
248
}
235
249
250
+ // Check that source lifetime outlives target lifetime
251
+ let lifetime_outlives_goal: Goal < I > = WhereClause :: LifetimeOutlives ( LifetimeOutlives {
252
+ a : lifetime_a. clone ( ) ,
253
+ b : lifetime_b. clone ( ) ,
254
+ } )
255
+ . cast ( interner) ;
256
+
236
257
// COMMENT FROM RUSTC:
237
258
// ------------------
238
259
// Require that the traits involved in this upcast are **equal**;
@@ -250,67 +271,138 @@ pub fn add_unsize_program_clauses<I: Interner>(
250
271
// with what our behavior should be there. -nikomatsakis
251
272
// ------------------
252
273
253
- // Construct a new trait object type by taking the source ty,
254
- // replacing auto traits of source with those of target,
255
- // and changing source lifetime to target lifetime.
256
- //
257
- // In order for the coercion to be valid, this new type
258
- // should be equal to target type.
259
- let new_source_ty = TyKind :: Dyn ( DynTy {
260
- bounds : bounds_a. map_ref ( |bounds| {
261
- QuantifiedWhereClauses :: from_iter (
262
- interner,
263
- bounds
264
- . iter ( interner)
265
- . cloned ( )
266
- . filter_map ( |bound| {
267
- let Some ( trait_id) = bound. trait_id ( ) else {
268
- // Keep non-"implements" bounds as-is
269
- return Some ( bound) ;
270
- } ;
271
-
272
- // Auto traits are already checked above, ignore them
273
- // (we'll use the ones from B below)
274
- if db. trait_datum ( trait_id) . is_auto_trait ( ) {
275
- return None ;
276
- }
277
-
278
- // The only "implements" bound that is not an auto trait, is the principal
279
- assert_eq ! ( Some ( trait_id) , principal_a) ;
280
-
281
- // Only include principal_a if the principal_b is also present
282
- // (this allows dropping principal, `dyn Tr+A -> dyn A`)
283
- principal_b. is_some ( ) . then ( || bound)
274
+ if principal_a == principal_b || principal_b. is_none ( ) {
275
+ // Construct a new trait object type by taking the source ty,
276
+ // replacing auto traits of source with those of target,
277
+ // and changing source lifetime to target lifetime.
278
+ //
279
+ // In order for the coercion to be valid, this new type
280
+ // should be equal to target type.
281
+ let new_source_ty = TyKind :: Dyn ( DynTy {
282
+ bounds : bounds_a. map_ref ( |bounds| {
283
+ QuantifiedWhereClauses :: from_iter (
284
+ interner,
285
+ bounds
286
+ . iter ( interner)
287
+ . cloned ( )
288
+ . filter_map ( |bound| {
289
+ let Some ( trait_id) = bound. trait_id ( ) else {
290
+ // Keep non-"implements" bounds as-is
291
+ return Some ( bound) ;
292
+ } ;
293
+
294
+ // Auto traits are already checked above, ignore them
295
+ // (we'll use the ones from B below)
296
+ if db. trait_datum ( trait_id) . is_auto_trait ( ) {
297
+ return None ;
298
+ }
299
+
300
+ // The only "implements" bound that is not an auto trait, is the principal
301
+ assert_eq ! ( Some ( trait_id) , principal_a) ;
302
+
303
+ // Only include principal_a if the principal_b is also present
304
+ // (this allows dropping principal, `dyn Tr+A -> dyn A`)
305
+ principal_b. is_some ( ) . then ( || bound)
306
+ } )
307
+ // Add auto traits from B (again, they are already checked above).
308
+ . chain ( bounds_b. skip_binders ( ) . iter ( interner) . cloned ( ) . filter (
309
+ |bound| {
310
+ bound. trait_id ( ) . is_some_and ( |trait_id| {
311
+ db. trait_datum ( trait_id) . is_auto_trait ( )
312
+ } )
313
+ } ,
314
+ ) ) ,
315
+ )
316
+ } ) ,
317
+ lifetime : lifetime_b. clone ( ) ,
318
+ } )
319
+ . intern ( interner) ;
320
+
321
+ // Check that new source is equal to target
322
+ let eq_goal = EqGoal {
323
+ a : new_source_ty. cast ( interner) ,
324
+ b : target_ty. clone ( ) . cast ( interner) ,
325
+ }
326
+ . cast ( interner) ;
327
+
328
+ builder. push_clause ( trait_ref, [ eq_goal, lifetime_outlives_goal] . iter ( ) ) ;
329
+ } else {
330
+ // Conditions above imply that both of these are always `Some`
331
+ // (b != None, b is Some iff a is Some).
332
+ let principal_a = principal_a. unwrap ( ) ;
333
+ let principal_b = principal_b. unwrap ( ) ;
334
+
335
+ let principal_trait_ref_a = principal_trait_ref_a. unwrap ( ) ;
336
+ let applicable_super_traits = super_traits ( db, principal_a)
337
+ . map ( |( super_trait_refs, _) | super_trait_refs)
338
+ . into_iter ( )
339
+ . filter ( |trait_ref| {
340
+ trait_ref. skip_binders ( ) . skip_binders ( ) . trait_id == principal_b
341
+ } ) ;
342
+
343
+ for super_trait_ref in applicable_super_traits {
344
+ // `super_trait_ref` is, at this point, quantified over generic params of
345
+ // `principal_a` and relevant higher-ranked lifetimes that come from super
346
+ // trait elaboration (see comments on `super_traits()`).
347
+ //
348
+ // So if we have `trait Trait<'a, T>: for<'b> Super<'a, 'b, T> {}`,
349
+ // `super_trait_ref` can be something like
350
+ // `for<Self, 'a, T> for<'b> Self: Super<'a, 'b, T>`.
351
+ //
352
+ // We need to convert it into a bound for `DynTy`. We do this by substituting
353
+ // bound vars of `principal_trait_ref_a` and then fusing inner binders for
354
+ // higher-ranked lifetimes.
355
+ let rebound_super_trait_ref = principal_trait_ref_a. map_ref ( |q_trait_ref_a| {
356
+ q_trait_ref_a
357
+ . map_ref ( |trait_ref_a| {
358
+ super_trait_ref. substitute ( interner, & trait_ref_a. substitution )
284
359
} )
285
- // Add auto traits from B (again, they are already checked above).
286
- . chain ( bounds_b. skip_binders ( ) . iter ( interner) . cloned ( ) . filter (
287
- |bound| {
288
- bound. trait_id ( ) . is_some_and ( |trait_id| {
289
- db. trait_datum ( trait_id) . is_auto_trait ( )
290
- } )
291
- } ,
292
- ) ) ,
293
- )
294
- } ) ,
295
- lifetime : lifetime_b. clone ( ) ,
296
- } )
297
- . intern ( interner) ;
360
+ . fuse_binders ( interner)
361
+ } ) ;
298
362
299
- // Check that new source is equal to target
300
- let eq_goal = EqGoal {
301
- a : new_source_ty. cast ( interner) ,
302
- b : target_ty. clone ( ) . cast ( interner) ,
303
- }
304
- . cast ( interner) ;
305
-
306
- // Check that source lifetime outlives target lifetime
307
- let lifetime_outlives_goal: Goal < I > = WhereClause :: LifetimeOutlives ( LifetimeOutlives {
308
- a : lifetime_a. clone ( ) ,
309
- b : lifetime_b. clone ( ) ,
310
- } )
311
- . cast ( interner) ;
363
+ // Skip `for<Self>` binder. We'll rebind it immediately below.
364
+ let new_principal_trait_ref = rebound_super_trait_ref
365
+ . into_value_and_skipped_binders ( )
366
+ . 0
367
+ . map ( |it| it. cast ( interner) ) ;
368
+
369
+ // Swap trait ref for `principal_a` with the new trait ref, drop the auto
370
+ // traits not included in the upcast target.
371
+ let new_source_ty = TyKind :: Dyn ( DynTy {
372
+ bounds : bounds_a. map_ref ( |bounds| {
373
+ QuantifiedWhereClauses :: from_iter (
374
+ interner,
375
+ bounds. iter ( interner) . cloned ( ) . filter_map ( |bound| {
376
+ let trait_id = match bound. trait_id ( ) {
377
+ Some ( id) => id,
378
+ None => return Some ( bound) ,
379
+ } ;
380
+
381
+ if principal_a == trait_id {
382
+ Some ( new_principal_trait_ref. clone ( ) )
383
+ } else {
384
+ auto_trait_ids_b. contains ( & trait_id) . then_some ( bound)
385
+ }
386
+ } ) ,
387
+ )
388
+ } ) ,
389
+ lifetime : lifetime_b. clone ( ) ,
390
+ } )
391
+ . intern ( interner) ;
392
+
393
+ // Check that new source is equal to target
394
+ let eq_goal = EqGoal {
395
+ a : new_source_ty. cast ( interner) ,
396
+ b : target_ty. clone ( ) . cast ( interner) ,
397
+ }
398
+ . cast ( interner) ;
312
399
313
- builder. push_clause ( trait_ref, [ eq_goal, lifetime_outlives_goal] . iter ( ) ) ;
400
+ // We don't push goal for `principal_b`'s object safety because it's implied by
401
+ // `principal_a`'s object safety.
402
+ builder
403
+ . push_clause ( trait_ref. clone ( ) , [ eq_goal, lifetime_outlives_goal. clone ( ) ] ) ;
404
+ }
405
+ }
314
406
}
315
407
316
408
// T -> dyn Trait + 'a
0 commit comments