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
use super::{Field, FlagsVisitor, Type, TypeExt, Walker};

use bitflags::bitflags;

bitflags! {
    #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
    pub struct Flags: u8 {
        const HAS_VARIABLES = 1 << 0;
        const HAS_SKOLEMS = 1 << 1;
        const HAS_GENERICS = 1 << 2;
        const HAS_FORALL = 1 << 3;
        const HAS_IDENTS = 1 << 4;
        const HAS_IMPLICIT = 1 << 5;


        const NEEDS_GENERALIZE =
            Flags::HAS_VARIABLES.bits | Flags::HAS_SKOLEMS.bits;
    }
}

pub(crate) trait AddFlags {
    fn add_flags(&self, flags: &mut Flags);
}

impl<T> AddFlags for [T]
where
    T: AddFlags,
{
    fn add_flags(&self, flags: &mut Flags) {
        for t in self {
            t.add_flags(flags);
        }
    }
}

impl<Id, T> AddFlags for Field<Id, T>
where
    T: AddFlags,
{
    fn add_flags(&self, flags: &mut Flags) {
        self.typ.add_flags(flags);
    }
}

impl<T> AddFlags for T
where
    T: TypeExt,
{
    fn add_flags(&self, flags: &mut Flags) {
        *flags |= self.flags()
    }
}

impl<Id, T> AddFlags for Type<Id, T>
where
    T: AddFlags + TypeExt<Id = Id>,
    Id: PartialEq,
{
    fn add_flags(&self, flags: &mut Flags) {
        match self {
            Type::Function(_, arg, ret) => {
                arg.add_flags(flags);
                ret.add_flags(flags);
            }
            Type::App(ref f, ref args) => {
                f.add_flags(flags);
                args.add_flags(flags);
            }
            Type::Record(ref typ) | Type::Variant(ref typ) | Type::Effect(ref typ) => {
                typ.add_flags(flags)
            }
            Type::Forall(ref params, ref typ) => {
                *flags |= Flags::HAS_FORALL;
                typ.add_flags(flags);

                let mut unbound_generic = false;
                FlagsVisitor(Flags::HAS_GENERICS, |typ: &T| match &**typ {
                    Type::Generic(gen) => {
                        unbound_generic |= params.iter().all(|param| param.id != gen.id)
                    }
                    _ => (),
                })
                .walk(typ);
                if !unbound_generic {
                    flags.remove(Flags::HAS_GENERICS);
                }
            }
            Type::Skolem(_) => *flags |= Flags::HAS_SKOLEMS,
            Type::ExtendRow { fields, rest } => {
                fields[..].add_flags(flags);
                rest.add_flags(flags);
            }
            Type::ExtendTypeRow { rest, .. } => {
                rest.add_flags(flags);
            }
            Type::Variable(_) => *flags |= Flags::HAS_VARIABLES,
            Type::Generic(_) => *flags |= Flags::HAS_GENERICS,
            Type::Ident(_) => *flags |= Flags::HAS_IDENTS,
            Type::Hole
            | Type::Opaque
            | Type::Error
            | Type::Builtin(..)
            | Type::Projection(_)
            | Type::Alias(_)
            | Type::EmptyRow => (),
        }
    }
}

impl Flags {
    pub fn from_type<Id, T>(typ: &Type<Id, T>) -> Self
    where
        T: TypeExt + TypeExt<Id = Id>,
        Id: PartialEq,
    {
        let mut flags = Flags::empty();
        typ.add_flags(&mut flags);
        flags
    }
}