verilization_compiler/
type_check.rs

1use crate::model::*;
2use num_bigint::BigUint;
3use num_traits::One;
4use std::collections::HashSet;
5
6#[derive(Debug)]
7pub enum TypeCheckError {
8    TypeNotDefined(QualifiedName),
9    TypeAddedInNewerVersion(QualifiedName, BigUint),
10    CouldNotFindLastVersion(QualifiedName),
11    ArityMismatch(usize, usize),
12    TypeNotFinal(QualifiedName),
13    DuplicateLiteral(QualifiedName),
14}
15
16struct TypeCheck<'model> {
17    model: &'model Verilization,
18    scope: Scope<'model>,
19}
20
21impl <'model> TypeCheck<'model> {
22
23    fn check_type(&self, version: &BigUint, t: &Type) -> Result<(), TypeCheckError> {
24        match self.scope.lookup(t.name.clone()) {
25            ScopeLookup::NamedType(name) => {
26                let named_type_def = match self.model.get_type(&name) {
27                    Some(t) => t,
28                    None => return Err(TypeCheckError::TypeNotDefined(name)),
29                };
30
31                if !named_type_def.has_version(version) {
32                    return Err(TypeCheckError::TypeAddedInNewerVersion(name, version.clone()));
33                }
34    
35                let arity = named_type_def.arity();
36                if arity != t.args.len() {
37                    return Err(TypeCheckError::ArityMismatch(arity, t.args.len()));
38                }
39    
40                for arg in &t.args {
41                    self.check_type(version, &arg)?;
42                }
43
44                Ok(())
45            },
46            ScopeLookup::TypeParameter(_) => {
47                if t.args.len() != 0 {
48                    return Err(TypeCheckError::ArityMismatch(0, t.args.len()));
49                }
50
51                Ok(())
52            }
53        }
54    }
55
56    fn check_is_final(&self, t: &Type, version: &BigUint) -> Result<bool, TypeCheckError> {
57        Ok(match self.scope.lookup(t.name.clone()) {
58            ScopeLookup::NamedType(name) => {
59                match self.model.get_type(&name).ok_or_else(|| TypeCheckError::TypeNotDefined(name.clone()))? {
60                    NamedTypeDefinition::StructType(type_def) | NamedTypeDefinition::EnumType(type_def) => {
61                        if !type_def.is_final() {
62                            return Ok(false);
63                        }
64                        
65                        if !(type_def.last_explicit_version().ok_or_else(|| TypeCheckError::CouldNotFindLastVersion(name.clone()))? <= version) {
66                            return Ok(false);
67                        }
68                    },
69
70                    NamedTypeDefinition::ExternType(_) => (),
71                }
72
73                for arg in &t.args {
74                    if !self.check_is_final(&arg, version)? {
75                        return Ok(false);
76                    }
77                }
78
79                true
80            },
81            ScopeLookup::TypeParameter(_) => true,
82        })
83    }
84    
85}
86
87fn type_check_versioned_type<'model>(model: &'model Verilization, t: Named<'model, VersionedTypeDefinitionData>) -> Result<(), TypeCheckError> {
88    let tc = TypeCheck {
89        model: model,
90        scope: t.scope(),
91    };
92
93    for ver in t.versions() {
94        for (_, field) in ver.ver_type.fields() {
95            tc.check_type(&ver.version, &field.field_type)?;
96        }
97    }
98
99    if t.is_final() {
100        if let Some(last_ver) = t.versions().last() {
101            for (_, field) in last_ver.ver_type.fields() {
102                if !tc.check_is_final(&field.field_type, &last_ver.version)? {
103                    return Err(TypeCheckError::TypeNotFinal(t.name().clone()))
104                }
105            }
106        }
107    }
108
109    Ok(())
110}
111
112fn type_check_extern_type<'model>(model: &'model Verilization, t: Named<'model, ExternTypeDefinitionData>) -> Result<(), TypeCheckError> {
113    let tc = TypeCheck {
114        model: model,
115        scope: t.scope(),
116    };
117
118    let mut has_integer = false;
119    let mut has_string = false;
120    let mut has_sequence = false;
121    let mut literal_cases = HashSet::new();
122    let mut has_record = false;
123
124    for literal in t.literals() {
125        match literal {
126            ExternLiteralSpecifier::Integer(_, _, _, _) if has_integer => return Err(TypeCheckError::DuplicateLiteral(t.name().clone())),
127            ExternLiteralSpecifier::Integer(_, _, _, _) => has_integer = true,
128            ExternLiteralSpecifier::String if has_string => return Err(TypeCheckError::DuplicateLiteral(t.name().clone())),
129            ExternLiteralSpecifier::String => has_string = true,
130            ExternLiteralSpecifier::Sequence(_) if has_sequence => return Err(TypeCheckError::DuplicateLiteral(t.name().clone())),
131            ExternLiteralSpecifier::Sequence(inner) => {
132                has_sequence = true;
133                tc.check_type(&BigUint::one(), inner)?;
134            },
135            ExternLiteralSpecifier::Case(name, params) => {
136                if !literal_cases.insert(name) {
137                    return Err(TypeCheckError::DuplicateLiteral(t.name().clone()));
138                }
139
140                for param in params {
141                    tc.check_type(&BigUint::one(), param)?;
142                }
143            },
144            ExternLiteralSpecifier::Record(_) if has_record => return Err(TypeCheckError::DuplicateLiteral(t.name().clone())),
145            ExternLiteralSpecifier::Record(fields) => {
146                has_record = true;
147                for (_, field) in fields {
148                    tc.check_type(&BigUint::one(), &field.field_type)?;
149                }
150            },
151        }
152    }
153
154    Ok(())
155}
156
157pub fn type_check_verilization(model: &Verilization) -> Result<(), TypeCheckError> {
158    
159    for t in model.types() {
160        match t {
161            NamedTypeDefinition::StructType(t) | NamedTypeDefinition::EnumType(t) => type_check_versioned_type(model, t)?,
162            NamedTypeDefinition::ExternType(t) => type_check_extern_type(model, t)?,
163        }
164    }
165
166    Ok(())
167}
168
169