• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TyRoXx / NonlocalityOS / 15326180314

29 May 2025 02:28PM UTC coverage: 72.983% (+0.03%) from 72.958%
15326180314

push

github

TyRoXx
Generalizing the version check for podman.

3374 of 4623 relevant lines covered (72.98%)

2227.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.18
/lambda_compiler/src/type_checking.rs
1
use crate::{
2
    ast::{self, LambdaParameter},
3
    compilation::{CompilerOutput, SourceLocation},
4
};
5
use astraea::{storage::StoreError, tree::Tree};
6
use lambda::{
7
    expressions::{DeepExpression, Expression},
8
    name::Name,
9
};
10
use std::{collections::BTreeMap, sync::Arc};
11

12
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
13
pub enum Type {
14
    Any,
15
    String,
16
    TreeWithKnownChildTypes(Vec<Type>),
17
    Function {
18
        parameters: Vec<Type>,
19
        return_type: Box<Type>,
20
    },
21
}
22

23
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
24
pub struct TypedExpression {
25
    pub expression: DeepExpression,
26
    pub type_: Type,
27
}
28

29
impl TypedExpression {
30
    pub fn new(expression: DeepExpression, type_: Type) -> Self {
149✔
31
        Self { expression, type_ }
32
    }
33
}
34

35
fn check_tree_construction_or_argument_list(
18✔
36
    arguments: &[ast::Expression],
37
    environment_builder: &mut EnvironmentBuilder,
38
) -> Result<CompilerOutput, StoreError> {
39
    let mut errors = Vec::new();
18✔
40
    let mut checked_arguments = Vec::new();
18✔
41
    let mut argument_types = Vec::new();
18✔
42
    for argument in arguments {
78✔
43
        let output = check_types(argument, environment_builder)?;
60✔
44
        errors.extend(output.errors);
45
        if let Some(checked) = output.entry_point {
30✔
46
            checked_arguments.push(Arc::new(checked.expression));
47
            argument_types.push(checked.type_);
48
        } else {
49
            return Ok(CompilerOutput::new(None, errors));
×
50
        }
51
    }
52
    Ok(CompilerOutput {
18✔
53
        entry_point: Some(TypedExpression::new(
18✔
54
            lambda::expressions::DeepExpression(lambda::expressions::Expression::ConstructTree(
18✔
55
                checked_arguments,
18✔
56
            )),
57
            Type::TreeWithKnownChildTypes(argument_types),
18✔
58
        )),
59
        errors,
18✔
60
    })
61
}
62

63
#[derive(Debug, Clone, Copy)]
64
enum ParameterIndex {
65
    SingleParameter,
66
    GetChild(u16),
67
}
68

69
impl ParameterIndex {
70
    pub fn create_deep_expression(&self) -> lambda::expressions::DeepExpression {
32✔
71
        match self {
32✔
72
            ParameterIndex::SingleParameter => {
73
                lambda::expressions::DeepExpression(lambda::expressions::Expression::make_argument())
24✔
74
            }
75
            ParameterIndex::GetChild(index) => lambda::expressions::DeepExpression(
76
                lambda::expressions::Expression::make_get_child(
8✔
77
                    Arc::new(lambda::expressions::DeepExpression(
8✔
78
                        lambda::expressions::Expression::make_argument(),
8✔
79
                    )),
80
                    *index,
8✔
81
                ),
82
            ),
83
        }
84
    }
85
}
86

87
struct LocalVariable {
88
    parameter_index: ParameterIndex,
89
    type_: Type,
90
}
91

92
impl LocalVariable {
93
    pub fn new(parameter_index: ParameterIndex, type_: Type) -> Self {
27✔
94
        Self {
95
            parameter_index,
96
            type_,
97
        }
98
    }
99
}
100

101
struct LambdaScope {
102
    names: BTreeMap<Name, LocalVariable>,
103
    captures: BTreeMap<TypedExpression, u16>,
104
}
105

106
impl LambdaScope {
107
    pub fn new(parameters: &[TypeCheckedLambdaParameter]) -> Self {
39✔
108
        let mut names = BTreeMap::new();
39✔
109
        if parameters.len() == 1 {
56✔
110
            names.insert(
17✔
111
                parameters[0].name.clone(),
17✔
112
                LocalVariable::new(ParameterIndex::SingleParameter, parameters[0].type_.clone()),
17✔
113
            );
114
        } else {
115
            for (index, parameter) in parameters.iter().enumerate() {
32✔
116
                let checked_index: u16 = index.try_into().expect("TODO handle too many parameters");
117
                names.insert(
118
                    parameter.name.clone(),
119
                    LocalVariable::new(
120
                        ParameterIndex::GetChild(checked_index),
121
                        parameter.type_.clone(),
122
                    ),
123
                );
124
            }
125
        }
126
        Self {
127
            names,
128
            captures: BTreeMap::new(),
39✔
129
        }
130
    }
131

132
    pub fn find_parameter_index(&self, parameter_name: &Name) -> Option<(ParameterIndex, Type)> {
55✔
133
        self.names
55✔
134
            .get(parameter_name)
55✔
135
            .map(|variable| (variable.parameter_index, variable.type_.clone()))
87✔
136
    }
137

138
    pub fn capture(&mut self, expression: TypedExpression) -> CompilerOutput {
23✔
139
        let type_ = expression.type_.clone();
23✔
140
        let index = match self.captures.get(&expression) {
46✔
141
            Some(&already_exists) => already_exists,
10✔
142
            None => {
143
                let new_index = self
13✔
144
                    .captures
13✔
145
                    .len()
146
                    .try_into()
147
                    .expect("TODO handle too many captures");
148
                self.captures.insert(expression, new_index);
13✔
149
                new_index
13✔
150
            }
151
        };
152
        CompilerOutput::new(
153
            Some(TypedExpression::new(
23✔
154
                lambda::expressions::DeepExpression(
23✔
155
                    lambda::expressions::Expression::make_get_child(
23✔
156
                        Arc::new(DeepExpression(
23✔
157
                            lambda::expressions::Expression::make_environment(),
23✔
158
                        )),
159
                        index,
23✔
160
                    ),
161
                ),
162
                type_,
23✔
163
            )),
164
            Vec::new(),
23✔
165
        )
166
    }
167

168
    pub fn leave(self) -> Vec<TypedExpression> {
39✔
169
        let mut as_vec: Vec<(TypedExpression, u16)> = self.captures.into_iter().collect();
39✔
170
        as_vec.sort_by_key(|(_, index)| *index);
47✔
171
        // sanity check:
172
        for (expected_index, (_, actual_index)) in as_vec.iter().enumerate() {
52✔
173
            assert_eq!(expected_index, *actual_index as usize);
174
        }
175
        as_vec
39✔
176
            .into_iter()
177
            .map(|(expression, _)| expression)
52✔
178
            .collect()
179
    }
180
}
181

182
pub struct EnvironmentBuilder {
183
    lambda_layers: Vec<LambdaScope>,
184
}
185

186
impl Default for EnvironmentBuilder {
187
    fn default() -> Self {
×
188
        Self::new()
×
189
    }
190
}
191

192
impl EnvironmentBuilder {
193
    pub fn new() -> Self {
52✔
194
        Self {
195
            lambda_layers: Vec::new(),
52✔
196
        }
197
    }
198

199
    pub fn is_empty(&self) -> bool {
28✔
200
        self.lambda_layers.is_empty()
28✔
201
    }
202

203
    pub fn enter_lambda_body(&mut self, parameters: &[TypeCheckedLambdaParameter]) {
39✔
204
        self.lambda_layers.push(LambdaScope::new(parameters));
39✔
205
    }
206

207
    pub fn leave_lambda_body(&mut self) -> Vec<TypedExpression> {
39✔
208
        let top_scope = self.lambda_layers.pop().unwrap();
39✔
209
        top_scope.leave()
39✔
210
    }
211

212
    pub fn read(&mut self, identifier: &Name, location: &SourceLocation) -> CompilerOutput {
34✔
213
        Self::read_down(&mut self.lambda_layers, identifier, location)
34✔
214
    }
215

216
    fn read_down(
57✔
217
        layers: &mut [LambdaScope],
218
        identifier: &Name,
219
        location: &SourceLocation,
220
    ) -> CompilerOutput {
221
        let layer_count = layers.len();
57✔
222
        if let Some(last) = layers.last_mut() {
112✔
223
            if let Some((parameter_index, parameter_type)) = last.find_parameter_index(identifier) {
32✔
224
                return CompilerOutput::new(
225
                    Some(TypedExpression::new(
226
                        parameter_index.create_deep_expression(),
227
                        parameter_type,
228
                    )),
229
                    Vec::new(),
230
                );
231
            } else if layer_count > 1 {
23✔
232
                let result = Self::read_down(&mut layers[..layer_count - 1], identifier, location);
23✔
233
                if result.entry_point.is_some() {
23✔
234
                    return layers
23✔
235
                        .last_mut()
23✔
236
                        .unwrap()
23✔
237
                        .capture(result.entry_point.unwrap());
23✔
238
                }
239
                return result;
×
240
            }
241
        }
242
        CompilerOutput::new(
243
            None,
2✔
244
            vec![crate::compilation::CompilerError::new(
2✔
245
                format!("Identifier {identifier} not found"),
2✔
246
                *location,
2✔
247
            )],
248
        )
249
    }
250
}
251

252
pub fn evaluate_type_at_compile_time(_expression: &DeepExpression) -> Type {
×
253
    todo!()
254
}
255

256
pub struct TypeCheckedLambdaParameter {
257
    pub name: Name,
258
    pub source_location: SourceLocation,
259
    pub type_: Type,
260
}
261

262
impl TypeCheckedLambdaParameter {
263
    pub fn new(name: Name, source_location: SourceLocation, type_: Type) -> Self {
3✔
264
        Self {
265
            name,
266
            source_location,
267
            type_,
268
        }
269
    }
270
}
271

272
pub fn check_lambda_parameters(
36✔
273
    parameters: &[LambdaParameter],
274
) -> Result<Vec<TypeCheckedLambdaParameter>, StoreError> {
275
    let mut checked_parameters = Vec::new();
36✔
276
    for parameter in parameters {
84✔
277
        let mut environment_builder = EnvironmentBuilder::new();
24✔
278
        let parameter_type: Type = match &parameter.type_annotation {
48✔
279
            Some(type_annotation) => {
×
280
                let checked_type = check_types(type_annotation, &mut environment_builder)?;
×
281
                assert!(environment_builder.is_empty());
282
                if let Some(checked) = checked_type.entry_point {
×
283
                    evaluate_type_at_compile_time(&checked.expression)
284
                } else {
285
                    todo!()
286
                }
287
            }
288
            None => {
289
                // If no type annotation is provided, we assume the type is `Any`.
290
                Type::Any
24✔
291
            }
292
        };
293
        checked_parameters.push(TypeCheckedLambdaParameter {
294
            name: parameter.name.clone(),
295
            source_location: parameter.source_location,
296
            type_: parameter_type,
297
        });
298
    }
299
    Ok(checked_parameters)
36✔
300
}
301

302
pub fn check_lambda(
36✔
303
    parameters: &[LambdaParameter],
304
    body: &ast::Expression,
305
    environment_builder: &mut EnvironmentBuilder,
306
) -> Result<CompilerOutput, StoreError> {
307
    let checked_parameters = check_lambda_parameters(parameters)?;
72✔
308
    environment_builder.enter_lambda_body(&checked_parameters[..]);
309
    let body_result = check_types(body, environment_builder);
310
    // TODO: use RAII or something?
311
    let environment = environment_builder.leave_lambda_body();
312
    let environment_expressions = environment
313
        .into_iter()
314
        .map(|typed_expression| Arc::new(typed_expression.expression))
12✔
315
        .collect();
316
    let body_output = body_result?;
36✔
317
    match body_output.entry_point {
318
        Some(body_checked) => Ok(CompilerOutput {
35✔
319
            entry_point: Some(TypedExpression::new(
35✔
320
                lambda::expressions::DeepExpression(lambda::expressions::Expression::Lambda {
35✔
321
                    environment: Arc::new(DeepExpression(Expression::make_construct_tree(
35✔
322
                        environment_expressions,
35✔
323
                    ))),
324
                    body: Arc::new(body_checked.expression),
35✔
325
                }),
326
                Type::Function {
35✔
327
                    parameters: checked_parameters
35✔
328
                        .into_iter()
35✔
329
                        .map(|parameter| parameter.type_)
58✔
330
                        .collect(),
35✔
331
                    return_type: Box::new(body_checked.type_),
35✔
332
                },
333
            )),
334
            errors: body_output.errors,
35✔
335
        }),
336
        None => Ok(CompilerOutput::new(None, body_output.errors)),
1✔
337
    }
338
}
339

340
pub fn check_braces(
×
341
    expression: &[ast::Expression],
342
    environment_builder: &mut EnvironmentBuilder,
343
) -> Result<CompilerOutput, StoreError> {
344
    if expression.len() != 1 {
×
345
        todo!()
346
    }
347
    check_types(&expression[0], environment_builder)
×
348
}
349

350
pub fn check_let(
3✔
351
    name: &Name,
352
    location: &SourceLocation,
353
    value: &ast::Expression,
354
    body: &ast::Expression,
355
    environment_builder: &mut EnvironmentBuilder,
356
) -> Result<CompilerOutput, StoreError> {
357
    let value_checked = check_types(value, environment_builder)?;
6✔
358
    if !value_checked.errors.is_empty() {
359
        todo!()
360
    }
361
    let value_checked_unwrapped = value_checked.entry_point.unwrap();
3✔
362
    let checked_parameters = [TypeCheckedLambdaParameter::new(
3✔
363
        name.clone(),
3✔
364
        *location,
3✔
365
        value_checked_unwrapped.type_.clone(),
3✔
366
    )];
367
    environment_builder.enter_lambda_body(&checked_parameters[..]);
3✔
368
    let body_result = check_types(body, environment_builder);
3✔
369
    // TODO: use RAII or something?
370
    let environment = environment_builder.leave_lambda_body();
3✔
371
    let environment_expressions = environment
3✔
372
        .into_iter()
373
        .map(|typed_expression| Arc::new(typed_expression.expression))
4✔
374
        .collect();
375
    let body_output = body_result?;
6✔
376
    match body_output.entry_point {
377
        Some(body_checked) => Ok(CompilerOutput {
3✔
378
            entry_point: Some(TypedExpression::new(
3✔
379
                lambda::expressions::DeepExpression(lambda::expressions::Expression::make_apply(
3✔
380
                    Arc::new(lambda::expressions::DeepExpression(
3✔
381
                        lambda::expressions::Expression::Lambda {
3✔
382
                            environment: Arc::new(DeepExpression(Expression::make_construct_tree(
3✔
383
                                environment_expressions,
3✔
384
                            ))),
385
                            body: Arc::new(body_checked.expression),
3✔
386
                        },
387
                    )),
388
                    Arc::new(value_checked_unwrapped.expression),
3✔
389
                )),
390
                body_checked.type_,
3✔
391
            )),
392
            errors: body_output.errors,
3✔
393
        }),
394
        None => Ok(CompilerOutput::new(None, body_output.errors)),
×
395
    }
396
}
397

398
pub fn check_types(
113✔
399
    syntax_tree: &ast::Expression,
400
    environment_builder: &mut EnvironmentBuilder,
401
) -> Result<CompilerOutput, StoreError> {
402
    match syntax_tree {
113✔
403
        ast::Expression::Identifier(name, location) => Ok(environment_builder.read(name, location)),
34✔
404
        ast::Expression::StringLiteral(value) => Ok(CompilerOutput::new(
14✔
405
            Some(TypedExpression::new(
14✔
406
                lambda::expressions::DeepExpression(lambda::expressions::Expression::Literal(
14✔
407
                    Tree::from_string(value).unwrap(/*TODO*/),
14✔
408
                )),
409
                Type::String,
14✔
410
            )),
411
            Vec::new(),
14✔
412
        )),
413
        ast::Expression::Apply { callee, arguments } => {
6✔
414
            let callee_output = check_types(callee, environment_builder)?;
12✔
415
            let argument_output = if arguments.len() == 1 {
6✔
416
                // For N=1 we don't need an indirection.
417
                check_types(&arguments[0], environment_builder)?
3✔
418
            } else {
419
                check_tree_construction_or_argument_list(&arguments[..], environment_builder)?
3✔
420
            };
421
            let errors = callee_output
422
                .errors
423
                .into_iter()
424
                .chain(argument_output.errors)
425
                .collect();
426
            match (callee_output.entry_point, argument_output.entry_point) {
427
                (Some(callee_checked), Some(argument_checked)) => {
5✔
428
                    let return_type = match &callee_checked.type_ {
9✔
429
                        Type::Function { return_type, .. } => return_type.as_ref().clone(),
430
                        _ => {
431
                            return Ok(CompilerOutput::new(
1✔
432
                                None,
1✔
433
                                vec![crate::compilation::CompilerError::new(
1✔
434
                                    "Callee is not a function".to_string(),
1✔
435
                                    callee.source_location(),
1✔
436
                                )],
437
                            ))
438
                        }
439
                    };
440
                    // TODO: check argument types against callee parameter types
441
                    Ok(CompilerOutput {
442
                        entry_point: Some(TypedExpression::new(
443
                            lambda::expressions::DeepExpression(
444
                                lambda::expressions::Expression::Apply {
445
                                    callee: Arc::new(callee_checked.expression),
446
                                    argument: Arc::new(argument_checked.expression),
447
                                },
448
                            ),
449
                            return_type,
450
                        )),
451
                        errors,
452
                    })
453
                }
454
                (None, _) | (_, None) => Ok(CompilerOutput::new(None, errors)),
1✔
455
            }
456
        }
457
        ast::Expression::Lambda { parameters, body } => {
36✔
458
            check_lambda(&parameters[..], body, environment_builder)
36✔
459
        }
460
        ast::Expression::ConstructTree(arguments) => {
15✔
461
            check_tree_construction_or_argument_list(&arguments[..], environment_builder)
15✔
462
        }
463
        ast::Expression::Braces(expression) => check_types(expression, environment_builder),
5✔
464
        ast::Expression::Let {
465
            name,
3✔
466
            location,
3✔
467
            value,
3✔
468
            body,
3✔
469
        } => check_let(name, location, value, body, environment_builder),
3✔
470
    }
471
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc