summaryrefslogtreecommitdiffstats
path: root/vendor/chalk-solve/src/clauses/builtin_traits/sized.rs
blob: 3ed46d42521c23a2acffaaae0037acc4351e0ac1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use std::iter;

use crate::clauses::builtin_traits::needs_impl_for_tys;
use crate::clauses::ClauseBuilder;
use crate::rust_ir::AdtKind;
use crate::{Interner, RustIrDatabase, TraitRef};
use chalk_ir::{
    AdtId, CanonicalVarKinds, Floundered, Substitution, TyKind, TyVariableKind, VariableKind,
};

fn push_adt_sized_conditions<I: Interner>(
    db: &dyn RustIrDatabase<I>,
    builder: &mut ClauseBuilder<'_, I>,
    trait_ref: TraitRef<I>,
    adt_id: AdtId<I>,
    substitution: &Substitution<I>,
) {
    let adt_datum = db.adt_datum(adt_id);

    // WF ensures that all enums are Sized, so we only have to consider structs.
    if adt_datum.kind != AdtKind::Struct {
        builder.push_fact(trait_ref);
        return;
    }

    let interner = db.interner();

    // To check if a struct S<..> is Sized, we only have to look at its last field.
    // This is because the WF checks for ADTs require that all the other fields must be Sized.
    let last_field_ty = adt_datum
        .binders
        .map_ref(|b| b.variants.clone())
        .substitute(interner, substitution)
        .into_iter()
        .take(1) // We have a struct so we're guaranteed one variant
        .flat_map(|mut v| v.fields.pop());

    needs_impl_for_tys(db, builder, trait_ref, last_field_ty);
}

fn push_tuple_sized_conditions<I: Interner>(
    db: &dyn RustIrDatabase<I>,
    builder: &mut ClauseBuilder<'_, I>,
    trait_ref: TraitRef<I>,
    arity: usize,
    substitution: &Substitution<I>,
) {
    // Empty tuples are always Sized
    if arity == 0 {
        builder.push_fact(trait_ref);
        return;
    }

    let interner = db.interner();

    // To check if a tuple is Sized, we only have to look at its last element.
    // This is because the WF checks for tuples require that all the other elements must be Sized.
    let last_elem_ty = substitution
        .iter(interner)
        .last()
        .unwrap()
        .ty(interner)
        .unwrap()
        .clone();

    needs_impl_for_tys(db, builder, trait_ref, iter::once(last_elem_ty));
}

pub fn add_sized_program_clauses<I: Interner>(
    db: &dyn RustIrDatabase<I>,
    builder: &mut ClauseBuilder<'_, I>,
    trait_ref: TraitRef<I>,
    ty: TyKind<I>,
    binders: &CanonicalVarKinds<I>,
) -> Result<(), Floundered> {
    match ty {
        TyKind::Adt(adt_id, ref substitution) => {
            push_adt_sized_conditions(db, builder, trait_ref, adt_id, substitution)
        }
        TyKind::Tuple(arity, ref substitution) => {
            push_tuple_sized_conditions(db, builder, trait_ref, arity, substitution)
        }
        TyKind::Array(_, _)
        | TyKind::Never
        | TyKind::Closure(_, _)
        | TyKind::FnDef(_, _)
        | TyKind::Scalar(_)
        | TyKind::Raw(_, _)
        | TyKind::Generator(_, _)
        | TyKind::GeneratorWitness(_, _)
        | TyKind::Ref(_, _, _) => builder.push_fact(trait_ref),

        TyKind::AssociatedType(_, _)
        | TyKind::Slice(_)
        | TyKind::OpaqueType(_, _)
        | TyKind::Str
        | TyKind::Foreign(_)
        | TyKind::Error => {}

        TyKind::Function(_)
        | TyKind::InferenceVar(_, TyVariableKind::Float)
        | TyKind::InferenceVar(_, TyVariableKind::Integer) => builder.push_fact(trait_ref),

        TyKind::BoundVar(bound_var) => {
            let var_kind = &binders.at(db.interner(), bound_var.index).kind;
            match var_kind {
                VariableKind::Ty(TyVariableKind::Integer)
                | VariableKind::Ty(TyVariableKind::Float) => builder.push_fact(trait_ref),

                // Don't know enough
                VariableKind::Ty(TyVariableKind::General) => return Err(Floundered),

                VariableKind::Const(_) | VariableKind::Lifetime => {}
            }
        }

        // We don't know enough here
        TyKind::InferenceVar(_, TyVariableKind::General) => return Err(Floundered),

        // These would be handled elsewhere
        TyKind::Placeholder(_) | TyKind::Dyn(_) | TyKind::Alias(_) => {}
    }
    Ok(())
}