1 use std
::collections
::HashSet
;
3 use std
::ops
::ControlFlow
;
5 use crate::clauses
::ClauseBuilder
;
6 use crate::rust_ir
::AdtKind
;
7 use crate::{Interner, RustIrDatabase, TraitRef, WellKnownTrait}
;
10 interner
::HasInterner
,
11 visit
::{SuperVisit, Visit, Visitor}
,
12 Binders
, Const
, ConstValue
, DebruijnIndex
, DomainGoal
, DynTy
, EqGoal
, Goal
, LifetimeOutlives
,
13 QuantifiedWhereClauses
, Substitution
, TraitId
, Ty
, TyKind
, TypeOutlives
, WhereClause
,
16 struct UnsizeParameterCollector
<I
: Interner
> {
18 // FIXME should probably use a bitset instead
19 parameters
: HashSet
<usize>,
22 impl<I
: Interner
> Visitor
<I
> for UnsizeParameterCollector
<I
> {
25 fn as_dyn(&mut self) -> &mut dyn Visitor
<I
, BreakTy
= Self::BreakTy
> {
29 fn visit_ty(&mut self, ty
: &Ty
<I
>, outer_binder
: DebruijnIndex
) -> ControlFlow
<()> {
30 let interner
= self.interner
;
32 match ty
.kind(interner
) {
33 TyKind
::BoundVar(bound_var
) => {
34 // check if bound var refers to the outermost binder
35 if bound_var
.debruijn
.shifted_in() == outer_binder
{
36 self.parameters
.insert(bound_var
.index
);
38 ControlFlow
::Continue(())
40 _
=> ty
.super_visit_with(self, outer_binder
),
44 fn visit_const(&mut self, constant
: &Const
<I
>, outer_binder
: DebruijnIndex
) -> ControlFlow
<()> {
45 let interner
= self.interner
;
47 if let ConstValue
::BoundVar(bound_var
) = constant
.data(interner
).value
{
48 // check if bound var refers to the outermost binder
49 if bound_var
.debruijn
.shifted_in() == outer_binder
{
50 self.parameters
.insert(bound_var
.index
);
53 ControlFlow
::Continue(())
56 fn interner(&self) -> I
{
61 fn outer_binder_parameters_used
<I
: Interner
>(
63 v
: &Binders
<impl Visit
<I
> + HasInterner
>,
65 let mut visitor
= UnsizeParameterCollector
{
67 parameters
: HashSet
::new(),
69 v
.visit_with(&mut visitor
, DebruijnIndex
::INNERMOST
);
73 // has nothing to do with occurs check
74 struct ParameterOccurenceCheck
<'p
, I
: Interner
> {
76 parameters
: &'p HashSet
<usize>,
79 impl<'p
, I
: Interner
> Visitor
<I
> for ParameterOccurenceCheck
<'p
, I
> {
82 fn as_dyn(&mut self) -> &mut dyn Visitor
<I
, BreakTy
= Self::BreakTy
> {
86 fn visit_ty(&mut self, ty
: &Ty
<I
>, outer_binder
: DebruijnIndex
) -> ControlFlow
<()> {
87 let interner
= self.interner
;
89 match ty
.kind(interner
) {
90 TyKind
::BoundVar(bound_var
) => {
91 if bound_var
.debruijn
.shifted_in() == outer_binder
92 && self.parameters
.contains(&bound_var
.index
)
94 ControlFlow
::Break(())
96 ControlFlow
::Continue(())
99 _
=> ty
.super_visit_with(self, outer_binder
),
103 fn visit_const(&mut self, constant
: &Const
<I
>, outer_binder
: DebruijnIndex
) -> ControlFlow
<()> {
104 let interner
= self.interner
;
106 match constant
.data(interner
).value
{
107 ConstValue
::BoundVar(bound_var
) => {
108 if bound_var
.debruijn
.shifted_in() == outer_binder
109 && self.parameters
.contains(&bound_var
.index
)
111 ControlFlow
::Break(())
113 ControlFlow
::Continue(())
116 _
=> ControlFlow
::Continue(()),
120 fn interner(&self) -> I
{
125 fn uses_outer_binder_params
<I
: Interner
>(
127 v
: &Binders
<impl Visit
<I
> + HasInterner
>,
128 parameters
: &HashSet
<usize>,
130 let mut visitor
= ParameterOccurenceCheck
{
135 let flow
= v
.visit_with(&mut visitor
, DebruijnIndex
::INNERMOST
);
136 matches
!(flow
, ControlFlow
::Break(_
))
139 fn principal_id
<I
: Interner
>(
140 db
: &dyn RustIrDatabase
<I
>,
141 bounds
: &Binders
<QuantifiedWhereClauses
<I
>>,
142 ) -> Option
<TraitId
<I
>> {
143 let interner
= db
.interner();
148 .filter_map(|b
| b
.trait_id())
149 .find(|&id
| !db
.trait_datum(id
).is_auto_trait())
152 fn auto_trait_ids
<'a
, I
: Interner
>(
153 db
: &'a
dyn RustIrDatabase
<I
>,
154 bounds
: &'a Binders
<QuantifiedWhereClauses
<I
>>,
155 ) -> impl Iterator
<Item
= TraitId
<I
>> + 'a
{
156 let interner
= db
.interner();
161 .filter_map(|clause
| clause
.trait_id())
162 .filter(move |&id
| db
.trait_datum(id
).is_auto_trait())
165 pub fn add_unsize_program_clauses
<I
: Interner
>(
166 db
: &dyn RustIrDatabase
<I
>,
167 builder
: &mut ClauseBuilder
<'_
, I
>,
168 trait_ref
: TraitRef
<I
>,
171 let interner
= db
.interner();
173 let source_ty
= trait_ref
.self_type_parameter(interner
);
174 let target_ty
= trait_ref
177 .assert_ty_ref(interner
)
180 let unsize_trait_id
= trait_ref
.trait_id
;
182 // N.B. here rustc asserts that `TraitRef` is not a higher-ranked bound
183 // i.e. `for<'a> &'a T: Unsize<dyn Trait+'a>` is never provable.
185 // In chalk it would be awkward to implement and I am not sure
186 // there is a need for it, the original comment states that this restriction
189 // for more info visit `fn assemble_candidates_for_unsizing` and
190 // `fn confirm_builtin_unisize_candidate` in rustc.
192 match (source_ty
.kind(interner
), target_ty
.kind(interner
)) {
193 // dyn Trait + AutoX + 'a -> dyn Trait + AutoY + 'b
197 lifetime
: lifetime_a
,
201 lifetime
: lifetime_b
,
204 let principal_a
= principal_id(db
, bounds_a
);
205 let principal_b
= principal_id(db
, bounds_b
);
207 let auto_trait_ids_a
: Vec
<_
> = auto_trait_ids(db
, bounds_a
).collect();
208 let auto_trait_ids_b
: Vec
<_
> = auto_trait_ids(db
, bounds_b
).collect();
210 let may_apply
= principal_a
== principal_b
213 .all(|id_b
| auto_trait_ids_a
.iter().any(|id_a
| id_a
== id_b
));
219 // COMMENT FROM RUSTC:
220 // ------------------
221 // Require that the traits involved in this upcast are **equal**;
222 // only the **lifetime bound** is changed.
224 // This condition is arguably too strong -- it would
225 // suffice for the source trait to be a *subtype* of the target
226 // trait. In particular, changing from something like
227 // `for<'a, 'b> Foo<'a, 'b>` to `for<'a> Foo<'a, 'a>` should be
230 // I've modified this to `.eq` because I want to continue rejecting
231 // that [`old-lub-glb-object.rs`] test (as we have
232 // done for quite some time) before we are firmly comfortable
233 // with what our behavior should be there. -nikomatsakis
234 // ------------------
236 // Construct a new trait object type by taking the source ty,
237 // filtering out auto traits of source that are not present in target
238 // and changing source lifetime to target lifetime.
240 // In order for the coercion to be valid, this new type
241 // 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(
246 bounds
.iter(interner
).filter(|bound
| {
247 let trait_id
= match bound
.trait_id() {
252 if auto_trait_ids_a
.iter().all(|&id_a
| id_a
!= trait_id
) {
255 auto_trait_ids_b
.iter().any(|&id_b
| id_b
== trait_id
)
259 lifetime
: lifetime_b
.clone(),
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
),
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(),
277 builder
.push_clause(trait_ref
, [eq_goal
, lifetime_outlives_goal
].iter());
280 // T -> dyn Trait + 'a
281 (_
, TyKind
::Dyn(DynTy { bounds, lifetime }
)) => {
282 // Check if all traits in trait object are object safe
283 let object_safe_goals
= bounds
286 .filter_map(|bound
| bound
.trait_id())
287 .map(|id
| DomainGoal
::ObjectSafe(id
).cast(interner
));
289 // Check that T implements all traits of the trait object
290 let source_ty_bounds
= bounds
292 .substitute(interner
, &Substitution
::from1(interner
, source_ty
.clone()));
294 // Check that T is sized because we can only make
295 // a trait object from a sized type
296 let self_sized_goal
: WhereClause
<_
> = TraitRef
{
298 .well_known_trait_id(WellKnownTrait
::Sized
)
299 .expect("Expected Sized to be defined when proving Unsize"),
300 substitution
: Substitution
::from1(interner
, source_ty
.clone()),
304 // Check that `source_ty` outlives `'a`
305 let source_ty_outlives
: Goal
<_
> = WhereClause
::TypeOutlives(TypeOutlives
{
307 lifetime
: lifetime
.clone(),
315 .map(|bound
| bound
.clone().cast
::<Goal
<I
>>(interner
))
316 .chain(object_safe_goals
)
317 .chain(iter
::once(self_sized_goal
.cast(interner
)))
318 .chain(iter
::once(source_ty_outlives
)),
322 (TyKind
::Array(array_ty
, _array_const
), TyKind
::Slice(slice_ty
)) => {
323 let eq_goal
= EqGoal
{
324 a
: array_ty
.clone().cast(interner
),
325 b
: slice_ty
.clone().cast(interner
),
328 builder
.push_clause(trait_ref
, iter
::once(eq_goal
));
332 (TyKind
::Adt(adt_id_a
, substitution_a
), TyKind
::Adt(adt_id_b
, substitution_b
)) => {
333 if adt_id_a
!= adt_id_b
{
337 let adt_id
= *adt_id_a
;
338 let adt_datum
= db
.adt_datum(adt_id
);
340 // Unsizing of enums is not allowed
341 if adt_datum
.kind
== AdtKind
::Enum
{
345 // We have a `struct` so we're guaranteed a single variant
346 let fields_len
= adt_datum
359 let adt_tail_field
= adt_datum
361 .map_ref(|bound
| bound
.variants
.last().unwrap().fields
.last().unwrap())
364 // Collect unsize parameters that last field contains and
365 // ensure there at least one of them.
366 let unsize_parameter_candidates
=
367 outer_binder_parameters_used(interner
, &adt_tail_field
);
369 if unsize_parameter_candidates
.is_empty() {
372 // Ensure none of the other fields mention the parameters used
374 // We specifically want variables specified by the outermost binder
375 // i.e. the struct generic arguments binder.
376 if uses_outer_binder_params(
380 .map_ref(|bound
| &bound
.variants
.last().unwrap().fields
[..fields_len
- 1]),
381 &unsize_parameter_candidates
,
386 let parameters_a
= substitution_a
.as_slice(interner
);
387 let parameters_b
= substitution_b
.as_slice(interner
);
388 // Check that the source adt with the target's
389 // unsizing parameters is equal to the target.
390 // We construct a new substitution where if a parameter is used in the
391 // coercion (i.e. it's a non-lifetime struct parameter used by it's last field),
392 // then we take that parameter from target substitution, otherwise we take
393 // it from the source substitution.
395 // In order for the coercion to be valid, target struct and
396 // struct with this newly constructed substitution applied to it should be equal.
397 let substitution
= Substitution
::from_iter(
399 parameters_a
.iter().enumerate().map(|(i
, p
)| {
400 if unsize_parameter_candidates
.contains(&i
) {
408 let eq_goal
= EqGoal
{
409 a
: TyKind
::Adt(adt_id
, substitution
)
412 b
: target_ty
.clone().cast(interner
),
416 // Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`.
417 let source_tail_field
= adt_tail_field
.clone().substitute(interner
, substitution_a
);
418 let target_tail_field
= adt_tail_field
.substitute(interner
, substitution_b
);
420 // Check that `TailField<T>: Unsize<TailField<U>>`
421 let last_field_unsizing_goal
: Goal
<I
> = TraitRef
{
422 trait_id
: unsize_trait_id
,
423 substitution
: Substitution
::from_iter(
425 [source_tail_field
, target_tail_field
].iter().cloned(),
430 builder
.push_clause(trait_ref
, [eq_goal
, last_field_unsizing_goal
].iter());
433 // (.., T) -> (.., U)
434 (TyKind
::Tuple(arity_a
, substitution_a
), TyKind
::Tuple(arity_b
, substitution_b
)) => {
435 if arity_a
!= arity_b
|| *arity_a
== 0 {
440 let tail_ty_a
= substitution_a
.iter(interner
).last().unwrap();
441 let tail_ty_b
= substitution_b
.iter(interner
).last().unwrap();
443 // Check that the source tuple with the target's
444 // last element is equal to the target.
445 let new_tuple
= TyKind
::Tuple(
447 Substitution
::from_iter(
452 .chain(iter
::once(tail_ty_b
)),
458 let eq_goal
: Goal
<I
> = EqGoal
{
459 a
: new_tuple
.cast(interner
),
460 b
: target_ty
.clone().cast(interner
),
464 // Check that `T: Unsize<U>`
465 let last_field_unsizing_goal
: Goal
<I
> = TraitRef
{
466 trait_id
: unsize_trait_id
,
467 substitution
: Substitution
::from_iter(
469 [tail_ty_a
, tail_ty_b
].iter().cloned(),
474 builder
.push_clause(trait_ref
, [eq_goal
, last_field_unsizing_goal
].iter());