chalk_solve/clauses/builtin_traits/
unsize.rs

1use std::collections::HashSet;
2use std::iter;
3use std::ops::ControlFlow;
4
5use crate::clauses::super_traits::super_traits;
6use crate::clauses::ClauseBuilder;
7use crate::rust_ir::AdtKind;
8use crate::{Interner, RustIrDatabase, TraitRef, WellKnownTrait};
9use chalk_ir::{
10    cast::Cast,
11    interner::HasInterner,
12    visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
13    Binders, Const, ConstValue, DebruijnIndex, DomainGoal, DynTy, EqGoal, Goal, LifetimeOutlives,
14    QuantifiedWhereClauses, Substitution, TraitId, Ty, TyKind, TypeOutlives, WhereClause,
15};
16
17struct UnsizeParameterCollector<I: Interner> {
18    interner: I,
19    // FIXME should probably use a bitset instead
20    parameters: HashSet<usize>,
21}
22
23impl<I: Interner> TypeVisitor<I> for UnsizeParameterCollector<I> {
24    type BreakTy = ();
25
26    fn as_dyn(&mut self) -> &mut dyn TypeVisitor<I, BreakTy = Self::BreakTy> {
27        self
28    }
29
30    fn visit_ty(&mut self, ty: &Ty<I>, outer_binder: DebruijnIndex) -> ControlFlow<()> {
31        let interner = self.interner;
32
33        match ty.kind(interner) {
34            TyKind::BoundVar(bound_var) => {
35                // check if bound var refers to the outermost binder
36                if bound_var.debruijn.shifted_in() == outer_binder {
37                    self.parameters.insert(bound_var.index);
38                }
39                ControlFlow::Continue(())
40            }
41            _ => ty.super_visit_with(self, outer_binder),
42        }
43    }
44
45    fn visit_const(&mut self, constant: &Const<I>, outer_binder: DebruijnIndex) -> ControlFlow<()> {
46        let interner = self.interner;
47
48        if let ConstValue::BoundVar(bound_var) = constant.data(interner).value {
49            // check if bound var refers to the outermost binder
50            if bound_var.debruijn.shifted_in() == outer_binder {
51                self.parameters.insert(bound_var.index);
52            }
53        }
54        ControlFlow::Continue(())
55    }
56
57    fn interner(&self) -> I {
58        self.interner
59    }
60}
61
62fn outer_binder_parameters_used<I: Interner>(
63    interner: I,
64    v: &Binders<impl TypeVisitable<I> + HasInterner>,
65) -> HashSet<usize> {
66    let mut visitor = UnsizeParameterCollector {
67        interner,
68        parameters: HashSet::new(),
69    };
70    v.visit_with(&mut visitor, DebruijnIndex::INNERMOST);
71    visitor.parameters
72}
73
74// has nothing to do with occurs check
75struct ParameterOccurenceCheck<'p, I: Interner> {
76    interner: I,
77    parameters: &'p HashSet<usize>,
78}
79
80impl<'p, I: Interner> TypeVisitor<I> for ParameterOccurenceCheck<'p, I> {
81    type BreakTy = ();
82
83    fn as_dyn(&mut self) -> &mut dyn TypeVisitor<I, BreakTy = Self::BreakTy> {
84        self
85    }
86
87    fn visit_ty(&mut self, ty: &Ty<I>, outer_binder: DebruijnIndex) -> ControlFlow<()> {
88        let interner = self.interner;
89
90        match ty.kind(interner) {
91            TyKind::BoundVar(bound_var) => {
92                if bound_var.debruijn.shifted_in() == outer_binder
93                    && self.parameters.contains(&bound_var.index)
94                {
95                    ControlFlow::Break(())
96                } else {
97                    ControlFlow::Continue(())
98                }
99            }
100            _ => ty.super_visit_with(self, outer_binder),
101        }
102    }
103
104    fn visit_const(&mut self, constant: &Const<I>, outer_binder: DebruijnIndex) -> ControlFlow<()> {
105        let interner = self.interner;
106
107        match constant.data(interner).value {
108            ConstValue::BoundVar(bound_var) => {
109                if bound_var.debruijn.shifted_in() == outer_binder
110                    && self.parameters.contains(&bound_var.index)
111                {
112                    ControlFlow::Break(())
113                } else {
114                    ControlFlow::Continue(())
115                }
116            }
117            _ => ControlFlow::Continue(()),
118        }
119    }
120
121    fn interner(&self) -> I {
122        self.interner
123    }
124}
125
126fn uses_outer_binder_params<I: Interner>(
127    interner: I,
128    v: &Binders<impl TypeVisitable<I> + HasInterner>,
129    parameters: &HashSet<usize>,
130) -> bool {
131    let mut visitor = ParameterOccurenceCheck {
132        interner,
133        parameters,
134    };
135
136    let flow = v.visit_with(&mut visitor, DebruijnIndex::INNERMOST);
137    matches!(flow, ControlFlow::Break(_))
138}
139
140fn principal_trait_ref<I: Interner>(
141    db: &dyn RustIrDatabase<I>,
142    bounds: &Binders<QuantifiedWhereClauses<I>>,
143) -> Option<Binders<Binders<TraitRef<I>>>> {
144    bounds
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        })
161}
162
163fn auto_trait_ids<'a, I: Interner>(
164    db: &'a dyn RustIrDatabase<I>,
165    bounds: &'a Binders<QuantifiedWhereClauses<I>>,
166) -> impl Iterator<Item = TraitId<I>> + 'a {
167    let interner = db.interner();
168
169    bounds
170        .skip_binders()
171        .iter(interner)
172        .filter_map(|clause| clause.trait_id())
173        .filter(move |&id| db.trait_datum(id).is_auto_trait())
174}
175
176pub fn add_unsize_program_clauses<I: Interner>(
177    db: &dyn RustIrDatabase<I>,
178    builder: &mut ClauseBuilder<'_, I>,
179    trait_ref: TraitRef<I>,
180    _ty: TyKind<I>,
181) {
182    let interner = db.interner();
183
184    let source_ty = trait_ref.self_type_parameter(interner);
185    let target_ty = trait_ref
186        .substitution
187        .at(interner, 1)
188        .assert_ty_ref(interner)
189        .clone();
190
191    let unsize_trait_id = trait_ref.trait_id;
192
193    // N.B. here rustc asserts that `TraitRef` is not a higher-ranked bound
194    // i.e. `for<'a> &'a T: Unsize<dyn Trait+'a>` is never provable.
195    //
196    // In chalk it would be awkward to implement and I am not sure
197    // there is a need for it, the original comment states that this restriction
198    // could be lifted.
199    //
200    // for more info visit `fn assemble_candidates_for_unsizing` and
201    // `fn confirm_builtin_unsize_candidate` in rustc.
202
203    match (source_ty.kind(interner), target_ty.kind(interner)) {
204        // dyn TraitA + AutoA + 'a -> dyn TraitB + AutoB + 'b
205        (
206            TyKind::Dyn(DynTy {
207                bounds: bounds_a,
208                lifetime: lifetime_a,
209            }),
210            TyKind::Dyn(DynTy {
211                bounds: bounds_b,
212                lifetime: lifetime_b,
213            }),
214        ) => {
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);
221
222            // Include super traits in a list of auto traits for A,
223            // to allow `dyn Trait -> dyn Trait + X` if `Trait: X`.
224            let auto_trait_ids_a: Vec<_> = auto_trait_ids(db, bounds_a)
225                .chain(principal_a.into_iter().flat_map(|principal_a| {
226                    super_traits(db, principal_a)
227                        .into_value_and_skipped_binders()
228                        .0
229                         .0
230                        .into_iter()
231                        .map(|x| x.skip_binders().trait_id)
232                        .filter(|&x| db.trait_datum(x).is_auto_trait())
233                }))
234                .collect();
235
236            let auto_trait_ids_b: Vec<_> = auto_trait_ids(db, bounds_b).collect();
237
238            // If B has a principal, then A must as well
239            // (i.e. we allow dropping principal, but not creating a principal out of thin air).
240            // `AutoB` must be a subset of `AutoA`.
241            let may_apply = principal_a.is_some() >= principal_b.is_some()
242                && auto_trait_ids_b
243                    .iter()
244                    .all(|id_b| auto_trait_ids_a.iter().any(|id_a| id_a == id_b));
245
246            if !may_apply {
247                return;
248            }
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
257            // COMMENT FROM RUSTC:
258            // ------------------
259            // Require that the traits involved in this upcast are **equal**;
260            // only the **lifetime bound** is changed.
261            //
262            // This condition is arguably too strong -- it would
263            // suffice for the source trait to be a *subtype* of the target
264            // trait. In particular, changing from something like
265            // `for<'a, 'b> Foo<'a, 'b>` to `for<'a> Foo<'a, 'a>` should be
266            // permitted.
267            // <...>
268            // I've modified this to `.eq` because I want to continue rejecting
269            // that [`old-lub-glb-object.rs`] test (as we have
270            // done for quite some time) before we are firmly comfortable
271            // with what our behavior should be there. -nikomatsakis
272            // ------------------
273
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)
359                            })
360                            .fuse_binders(interner)
361                    });
362
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);
399
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            }
406        }
407
408        // T -> dyn Trait + 'a
409        (_, TyKind::Dyn(DynTy { bounds, lifetime })) => {
410            // Check if all traits in trait object are object safe
411            let object_safe_goals = bounds
412                .skip_binders()
413                .iter(interner)
414                .filter_map(|bound| bound.trait_id())
415                .map(|id| DomainGoal::ObjectSafe(id).cast(interner));
416
417            // Check that T implements all traits of the trait object
418            let source_ty_bounds = bounds
419                .clone()
420                .substitute(interner, &Substitution::from1(interner, source_ty.clone()));
421
422            // Check that T is sized because we can only make
423            // a trait object from a sized type
424            let self_sized_goal: WhereClause<_> = TraitRef {
425                trait_id: db
426                    .well_known_trait_id(WellKnownTrait::Sized)
427                    .expect("Expected Sized to be defined when proving Unsize"),
428                substitution: Substitution::from1(interner, source_ty.clone()),
429            }
430            .cast(interner);
431
432            // Check that `source_ty` outlives `'a`
433            let source_ty_outlives: Goal<_> = WhereClause::TypeOutlives(TypeOutlives {
434                ty: source_ty,
435                lifetime: lifetime.clone(),
436            })
437            .cast(interner);
438
439            builder.push_clause(
440                trait_ref,
441                source_ty_bounds
442                    .iter(interner)
443                    .map(|bound| bound.clone().cast::<Goal<I>>(interner))
444                    .chain(object_safe_goals)
445                    .chain(iter::once(self_sized_goal.cast(interner)))
446                    .chain(iter::once(source_ty_outlives)),
447            );
448        }
449
450        (TyKind::Array(array_ty, _array_const), TyKind::Slice(slice_ty)) => {
451            let eq_goal = EqGoal {
452                a: array_ty.clone().cast(interner),
453                b: slice_ty.clone().cast(interner),
454            };
455
456            builder.push_clause(trait_ref, iter::once(eq_goal));
457        }
458
459        // Adt<T> -> Adt<U>
460        (TyKind::Adt(adt_id_a, substitution_a), TyKind::Adt(adt_id_b, substitution_b)) => {
461            if adt_id_a != adt_id_b {
462                return;
463            }
464
465            let adt_id = *adt_id_a;
466            let adt_datum = db.adt_datum(adt_id);
467
468            // Unsizing of enums is not allowed
469            if adt_datum.kind == AdtKind::Enum {
470                return;
471            }
472
473            // We have a `struct` so we're guaranteed a single variant
474            let fields_len = adt_datum
475                .binders
476                .skip_binders()
477                .variants
478                .last()
479                .unwrap()
480                .fields
481                .len();
482
483            if fields_len == 0 {
484                return;
485            }
486
487            let adt_tail_field = adt_datum
488                .binders
489                .map_ref(|bound| bound.variants.last().unwrap().fields.last().unwrap())
490                .cloned();
491
492            // Collect unsize parameters that last field contains and
493            // ensure there at least one of them.
494            let unsize_parameter_candidates =
495                outer_binder_parameters_used(interner, &adt_tail_field);
496
497            if unsize_parameter_candidates.is_empty() {
498                return;
499            }
500            // Ensure none of the other fields mention the parameters used
501            // in unsizing.
502            // We specifically want variables specified by the outermost binder
503            // i.e. the struct generic arguments binder.
504            if uses_outer_binder_params(
505                interner,
506                &adt_datum
507                    .binders
508                    .map_ref(|bound| &bound.variants.last().unwrap().fields[..fields_len - 1]),
509                &unsize_parameter_candidates,
510            ) {
511                return;
512            }
513
514            let parameters_a = substitution_a.as_slice(interner);
515            let parameters_b = substitution_b.as_slice(interner);
516            // Check that the source adt with the target's
517            // unsizing parameters is equal to the target.
518            // We construct a new substitution where if a parameter is used in the
519            // coercion (i.e. it's a non-lifetime struct parameter used by it's last field),
520            // then we take that parameter from target substitution, otherwise we take
521            // it from the source substitution.
522            //
523            // In order for the coercion to be valid, target struct and
524            // struct with this newly constructed substitution applied to it should be equal.
525            let substitution = Substitution::from_iter(
526                interner,
527                parameters_a.iter().enumerate().map(|(i, p)| {
528                    if unsize_parameter_candidates.contains(&i) {
529                        &parameters_b[i]
530                    } else {
531                        p
532                    }
533                }),
534            );
535
536            let eq_goal = EqGoal {
537                a: TyKind::Adt(adt_id, substitution)
538                    .intern(interner)
539                    .cast(interner),
540                b: target_ty.clone().cast(interner),
541            }
542            .cast(interner);
543
544            // Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`.
545            let source_tail_field = adt_tail_field.clone().substitute(interner, substitution_a);
546            let target_tail_field = adt_tail_field.substitute(interner, substitution_b);
547
548            // Check that `TailField<T>: Unsize<TailField<U>>`
549            let last_field_unsizing_goal: Goal<I> = TraitRef {
550                trait_id: unsize_trait_id,
551                substitution: Substitution::from_iter(
552                    interner,
553                    [source_tail_field, target_tail_field].iter().cloned(),
554                ),
555            }
556            .cast(interner);
557
558            builder.push_clause(trait_ref, [eq_goal, last_field_unsizing_goal].iter());
559        }
560
561        // (.., T) -> (.., U)
562        (TyKind::Tuple(arity_a, substitution_a), TyKind::Tuple(arity_b, substitution_b)) => {
563            if arity_a != arity_b || *arity_a == 0 {
564                return;
565            }
566            let arity = arity_a;
567
568            let tail_ty_a = substitution_a.iter(interner).last().unwrap();
569            let tail_ty_b = substitution_b.iter(interner).last().unwrap();
570
571            // Check that the source tuple with the target's
572            // last element is equal to the target.
573            let new_tuple = TyKind::Tuple(
574                *arity,
575                Substitution::from_iter(
576                    interner,
577                    substitution_a
578                        .iter(interner)
579                        .take(arity - 1)
580                        .chain(iter::once(tail_ty_b)),
581                ),
582            )
583            .cast(interner)
584            .intern(interner);
585
586            let eq_goal: Goal<I> = EqGoal {
587                a: new_tuple.cast(interner),
588                b: target_ty.clone().cast(interner),
589            }
590            .cast(interner);
591
592            // Check that `T: Unsize<U>`
593            let last_field_unsizing_goal: Goal<I> = TraitRef {
594                trait_id: unsize_trait_id,
595                substitution: Substitution::from_iter(
596                    interner,
597                    [tail_ty_a, tail_ty_b].iter().cloned(),
598                ),
599            }
600            .cast(interner);
601
602            builder.push_clause(trait_ref, [eq_goal, last_field_unsizing_goal].iter());
603        }
604
605        _ => (),
606    }
607}