chalk_solve/clauses/builtin_traits/
unsize.rs1use 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 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 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 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
74struct 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 match (source_ty.kind(interner), target_ty.kind(interner)) {
204 (
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 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 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 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 if principal_a == principal_b || principal_b.is_none() {
275 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 return Some(bound);
292 };
293
294 if db.trait_datum(trait_id).is_auto_trait() {
297 return None;
298 }
299
300 assert_eq!(Some(trait_id), principal_a);
302
303 principal_b.is_some().then(|| bound)
306 })
307 .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 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 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 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 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 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 let eq_goal = EqGoal {
395 a: new_source_ty.cast(interner),
396 b: target_ty.clone().cast(interner),
397 }
398 .cast(interner);
399
400 builder
403 .push_clause(trait_ref.clone(), [eq_goal, lifetime_outlives_goal.clone()]);
404 }
405 }
406 }
407
408 (_, TyKind::Dyn(DynTy { bounds, lifetime })) => {
410 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 let source_ty_bounds = bounds
419 .clone()
420 .substitute(interner, &Substitution::from1(interner, source_ty.clone()));
421
422 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 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 (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 if adt_datum.kind == AdtKind::Enum {
470 return;
471 }
472
473 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 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 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 let substitution = Substitution::from_iter(
526 interner,
527 parameters_a.iter().enumerate().map(|(i, p)| {
528 if unsize_parameter_candidates.contains(&i) {
529 ¶meters_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 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 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 (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 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 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}