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

TyRoXx / NonlocalityOS / 15531419334

09 Jun 2025 09:32AM UTC coverage: 75.667% (+1.0%) from 74.71%
15531419334

push

github

TyRoXx
GH-286: test that parameter types do not capture variables

3884 of 5133 relevant lines covered (75.67%)

2156.81 hits per line

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

98.98
/lambda_compiler/src/type_checking.rs
1
use crate::{
2
    ast::{self, LambdaParameter},
3
    compilation::{CompilerError, CompilerOutput, SourceLocation},
4
};
5
use astraea::{
6
    deep_tree::DeepTree,
7
    storage::StoreError,
8
    tree::{ReferenceIndex, TreeBlob},
9
};
10
use lambda::{
11
    expressions::{DeepExpression, Expression},
12
    name::{Name, NamespaceId},
13
};
14
use serde::{Deserialize, Serialize};
15
use std::{collections::BTreeMap, sync::Arc};
16

17
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Serialize, Deserialize)]
18
pub enum GenericType<T>
19
where
20
    T: Clone,
21
{
22
    Any,
23
    String,
24
    TreeWithKnownChildTypes(Vec<T>),
25
    Function {
26
        parameters: Vec<T>,
27
        return_type: Box<T>,
28
    },
29
    Type,
30
    Named(Name),
31
}
32

33
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Clone)]
34
pub struct DeepType(pub GenericType<DeepType>);
35

36
pub fn to_reference_type(deep_type: &DeepType) -> (GenericType<ReferenceIndex>, Vec<DeepType>) {
149✔
37
    match deep_type.0 {
149✔
38
        GenericType::Any => (GenericType::Any, Vec::new()),
3✔
39
        GenericType::String => (GenericType::String, Vec::new()),
55✔
40
        GenericType::TreeWithKnownChildTypes(ref children) => (
2✔
41
            GenericType::TreeWithKnownChildTypes(
2✔
42
                (0u64..(children.len() as u64))
2✔
43
                    .map(ReferenceIndex)
2✔
44
                    .collect(),
2✔
45
            ),
46
            children.clone(),
2✔
47
        ),
48
        GenericType::Function {
49
            ref parameters,
1✔
50
            ref return_type,
1✔
51
        } => {
1✔
52
            let mut parameters_references = Vec::new();
1✔
53
            let mut children = Vec::new();
1✔
54
            for (index, parameter) in parameters.iter().enumerate() {
3✔
55
                parameters_references.push(ReferenceIndex(index as u64));
56
                children.push(parameter.clone());
57
            }
58
            let return_type_reference = ReferenceIndex(children.len() as u64);
1✔
59
            children.push(return_type.as_ref().clone());
1✔
60
            (
61
                GenericType::Function {
1✔
62
                    parameters: parameters_references,
1✔
63
                    return_type: Box::new(return_type_reference),
1✔
64
                },
65
                children,
1✔
66
            )
67
        }
68
        GenericType::Type => (GenericType::Type, Vec::new()),
44✔
69
        GenericType::Named(ref name) => (GenericType::Named(name.clone()), Vec::new()),
44✔
70
    }
71
}
72

73
pub fn type_to_deep_tree(deep_type: &DeepType) -> DeepTree {
149✔
74
    let (body, children) = to_reference_type(deep_type);
149✔
75
    let body_serialized = postcard::to_allocvec(&body).unwrap(/*TODO*/);
149✔
76
    DeepTree::new(
77
        TreeBlob::try_from(bytes::Bytes::from( body_serialized)).unwrap(/*TODO*/),
149✔
78
        children.iter().map(type_to_deep_tree).collect(),
149✔
79
    )
80
}
81

82
pub fn from_reference_type(body: &GenericType<ReferenceIndex>, children: &[DeepType]) -> DeepType {
23✔
83
    match body {
23✔
84
        GenericType::Any => DeepType(GenericType::Any),
×
85
        GenericType::String => DeepType(GenericType::String),
17✔
86
        GenericType::TreeWithKnownChildTypes(ref children_references) => {
2✔
87
            let mut resulting_children = Vec::new();
2✔
88
            for reference in children_references {
10✔
89
                let index = reference.0 as usize;
4✔
90
                if index < children.len() {
8✔
91
                    resulting_children.push(children[index].clone());
4✔
92
                } else {
93
                    // TODO error handling
94
                    // This should not happen if the tree is well-formed.
95
                    panic!("Reference index out of bounds: {index}");
96
                }
97
            }
98
            DeepType(GenericType::TreeWithKnownChildTypes(resulting_children))
2✔
99
        }
100
        GenericType::Function {
101
            ref parameters,
1✔
102
            ref return_type,
1✔
103
        } => {
1✔
104
            let mut resulting_parameters = Vec::new();
1✔
105
            for reference in parameters {
5✔
106
                let index: usize = reference.0.try_into().expect("TODO");
2✔
107
                if index < children.len() {
4✔
108
                    resulting_parameters.push(children[index].clone());
2✔
109
                } else {
110
                    // TODO error handling
111
                    // This should not happen if the tree is well-formed.
112
                    panic!("Reference index out of bounds: {index}");
113
                }
114
            }
115
            let resulting_return_type = {
1✔
116
                let index: usize = return_type.0.try_into().expect("TODO");
1✔
117
                if index < children.len() {
1✔
118
                    children[index].clone()
1✔
119
                } else {
120
                    // TODO error handling
121
                    panic!("Reference index out of bounds: {index}");
122
                }
123
            };
124
            DeepType(GenericType::Function {
1✔
125
                parameters: resulting_parameters,
1✔
126
                return_type: Box::new(resulting_return_type),
1✔
127
            })
128
        }
129
        GenericType::Type => DeepType(GenericType::Type),
1✔
130
        GenericType::Named(ref name) => DeepType(GenericType::Named(name.clone())),
2✔
131
    }
132
}
133

134
pub fn type_from_deep_tree(deep_tree: &DeepTree) -> DeepType {
23✔
135
    let body: GenericType<ReferenceIndex> =
23✔
136
        postcard::from_bytes(deep_tree.blob().as_slice()).unwrap(/*TODO*/);
23✔
137
    let children: Vec<_> = deep_tree
23✔
138
        .references()
139
        .iter()
140
        .map(type_from_deep_tree)
23✔
141
        .collect();
142
    from_reference_type(&body, &children)
23✔
143
}
144

145
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
146
pub struct TypedExpression {
147
    pub expression: DeepExpression,
148
    pub type_: DeepType,
149
}
150

151
impl TypedExpression {
152
    pub fn new(expression: DeepExpression, type_: DeepType) -> Self {
344✔
153
        Self { expression, type_ }
154
    }
155
}
156

157
async fn check_tree_construction_or_argument_list(
26✔
158
    arguments: &[ast::Expression],
159
    environment_builder: &mut EnvironmentBuilder,
160
) -> Result<(CompilerOutput, Option<DeepTree>), StoreError> {
161
    let mut errors = Vec::new();
26✔
162
    let mut checked_arguments = Vec::new();
26✔
163
    let mut argument_types = Vec::new();
26✔
164
    for argument in arguments {
112✔
165
        let output = check_types(argument, environment_builder).await?;
86✔
166
        errors.extend(output.0.errors);
167
        if let Some(checked) = output.0.entry_point {
43✔
168
            checked_arguments.push(Arc::new(checked.expression));
169
            argument_types.push(checked.type_);
170
        } else {
171
            return Ok((CompilerOutput::new(None, errors), None));
×
172
        }
173
    }
174
    Ok((
26✔
175
        CompilerOutput {
26✔
176
            entry_point: Some(TypedExpression::new(
26✔
177
                lambda::expressions::DeepExpression(
26✔
178
                    lambda::expressions::Expression::ConstructTree(checked_arguments),
26✔
179
                ),
180
                DeepType(GenericType::TreeWithKnownChildTypes(argument_types)),
26✔
181
            )),
182
            errors,
26✔
183
        },
184
        /*TODO: support compile time tree construction*/ None,
26✔
185
    ))
186
}
187

188
#[derive(Debug, Clone, Copy)]
189
enum ParameterIndex {
190
    SingleParameter,
191
    GetChild(u16),
192
}
193

194
impl ParameterIndex {
195
    pub fn create_deep_expression(&self) -> lambda::expressions::DeepExpression {
59✔
196
        match self {
59✔
197
            ParameterIndex::SingleParameter => {
198
                lambda::expressions::DeepExpression(lambda::expressions::Expression::make_argument())
43✔
199
            }
200
            ParameterIndex::GetChild(index) => lambda::expressions::DeepExpression(
201
                lambda::expressions::Expression::make_get_child(
16✔
202
                    Arc::new(lambda::expressions::DeepExpression(
16✔
203
                        lambda::expressions::Expression::make_argument(),
16✔
204
                    )),
205
                    *index,
16✔
206
                ),
207
            ),
208
        }
209
    }
210
}
211

212
#[derive(Debug, Clone)]
213
struct LocalVariable {
214
    parameter_index: ParameterIndex,
215
    type_: DeepType,
216
    compile_time_value: Option<DeepTree>,
217
}
218

219
impl LocalVariable {
220
    pub fn new(
282✔
221
        parameter_index: ParameterIndex,
222
        type_: DeepType,
223
        compile_time_value: Option<DeepTree>,
224
    ) -> Self {
225
        Self {
226
            parameter_index,
227
            type_,
228
            compile_time_value,
229
        }
230
    }
231
}
232

233
#[derive(Debug, Clone)]
234
struct LambdaScope {
235
    names: BTreeMap<Name, LocalVariable>,
236
    captures: BTreeMap<TypedExpression, u16>,
237
}
238

239
impl LambdaScope {
240
    pub fn new_lambda_scope(parameters: &[TypeCheckedLambdaParameter]) -> Self {
72✔
241
        let mut names = BTreeMap::new();
72✔
242
        if parameters.len() == 1 {
115✔
243
            names.insert(
43✔
244
                parameters[0].name.clone(),
43✔
245
                LocalVariable::new(
43✔
246
                    ParameterIndex::SingleParameter,
43✔
247
                    parameters[0].type_.clone(),
43✔
248
                    parameters[0].compile_time_value.clone(),
43✔
249
                ),
250
            );
251
        } else {
252
            for (index, parameter) in parameters.iter().enumerate() {
48✔
253
                let checked_index: u16 = index.try_into().expect("TODO handle too many parameters");
254
                names.insert(
255
                    parameter.name.clone(),
256
                    LocalVariable::new(
257
                        ParameterIndex::GetChild(checked_index),
258
                        parameter.type_.clone(),
259
                        parameter.compile_time_value.clone(),
260
                    ),
261
                );
262
            }
263
        }
264
        Self {
265
            names,
266
            captures: BTreeMap::new(),
72✔
267
        }
268
    }
269

270
    pub fn new_constant_scope(name: Name, type_: DeepType, compile_time_value: DeepTree) -> Self {
220✔
271
        let mut names = BTreeMap::new();
220✔
272
        names.insert(
220✔
273
            name,
220✔
274
            LocalVariable::new(
220✔
275
                ParameterIndex::SingleParameter,
220✔
276
                type_.clone(),
220✔
277
                Some(compile_time_value),
220✔
278
            ),
279
        );
280
        Self {
281
            names,
282
            captures: BTreeMap::new(),
220✔
283
        }
284
    }
285

286
    pub fn find_parameter_index(
211✔
287
        &self,
288
        parameter_name: &Name,
289
    ) -> Option<(ParameterIndex, DeepType, Option<DeepTree>)> {
290
        self.names.get(parameter_name).map(|variable| {
293✔
291
            (
292
                variable.parameter_index,
82✔
293
                variable.type_.clone(),
82✔
294
                variable.compile_time_value.clone(),
82✔
295
            )
296
        })
297
    }
298

299
    pub fn capture(&mut self, expression: TypedExpression) -> CompilerOutput {
31✔
300
        let type_ = expression.type_.clone();
31✔
301
        let index = match self.captures.get(&expression) {
62✔
302
            Some(&already_exists) => already_exists,
10✔
303
            None => {
304
                let new_index = self
21✔
305
                    .captures
21✔
306
                    .len()
307
                    .try_into()
308
                    .expect("TODO handle too many captures");
309
                self.captures.insert(expression, new_index);
21✔
310
                new_index
21✔
311
            }
312
        };
313
        CompilerOutput::new(
314
            Some(TypedExpression::new(
31✔
315
                lambda::expressions::DeepExpression(
31✔
316
                    lambda::expressions::Expression::make_get_child(
31✔
317
                        Arc::new(DeepExpression(
31✔
318
                            lambda::expressions::Expression::make_environment(),
31✔
319
                        )),
320
                        index,
31✔
321
                    ),
322
                ),
323
                type_,
31✔
324
            )),
325
            Vec::new(),
31✔
326
        )
327
    }
328

329
    pub fn leave(self) -> Vec<TypedExpression> {
292✔
330
        let mut as_vec: Vec<(TypedExpression, u16)> = self.captures.into_iter().collect();
292✔
331
        as_vec.sort_by_key(|(_, index)| *index);
300✔
332
        // sanity check:
333
        for (expected_index, (_, actual_index)) in as_vec.iter().enumerate() {
310✔
334
            assert_eq!(expected_index, *actual_index as usize);
335
        }
336
        as_vec
292✔
337
            .into_iter()
338
            .map(|(expression, _)| expression)
310✔
339
            .collect()
340
    }
341
}
342

343
#[derive(Debug, Clone)]
344
pub struct EnvironmentBuilder {
345
    lambda_layers: Vec<LambdaScope>,
346
}
347

348
impl Default for EnvironmentBuilder {
349
    fn default() -> Self {
×
350
        Self::new()
×
351
    }
352
}
353

354
impl EnvironmentBuilder {
355
    pub fn new() -> Self {
44✔
356
        Self {
357
            lambda_layers: Vec::new(),
44✔
358
        }
359
    }
360

361
    pub fn is_empty(&self) -> bool {
44✔
362
        self.lambda_layers.is_empty()
44✔
363
    }
364

365
    pub fn enter_lambda_body(&mut self, parameters: &[TypeCheckedLambdaParameter]) {
72✔
366
        self.lambda_layers
72✔
367
            .push(LambdaScope::new_lambda_scope(parameters));
72✔
368
    }
369

370
    pub fn leave_lambda_body(&mut self) -> Vec<TypedExpression> {
292✔
371
        let top_scope = self.lambda_layers.pop().unwrap();
292✔
372
        top_scope.leave()
292✔
373
    }
374

375
    pub fn define_constant(&mut self, name: Name, type_: DeepType, compile_time_value: DeepTree) {
220✔
376
        self.lambda_layers.push(LambdaScope::new_constant_scope(
220✔
377
            name,
220✔
378
            type_,
220✔
379
            compile_time_value,
220✔
380
        ));
381
    }
382

383
    pub fn undefine_constant(&mut self) {
220✔
384
        let captures = self.leave_lambda_body();
220✔
385
        if !captures.is_empty() {
220✔
386
            todo!()
387
        }
388
    }
389

390
    pub fn read(
89✔
391
        &mut self,
392
        identifier: &Name,
393
        location: &SourceLocation,
394
    ) -> (CompilerOutput, Option<DeepTree>) {
395
        Self::read_down(&mut self.lambda_layers, identifier, location)
89✔
396
    }
397

398
    fn read_down(
211✔
399
        layers: &mut [LambdaScope],
400
        identifier: &Name,
401
        location: &SourceLocation,
402
    ) -> (CompilerOutput, Option<DeepTree>) {
403
        let layer_count = layers.len();
211✔
404
        if let Some(last) = layers.last_mut() {
422✔
405
            if let Some((parameter_index, parameter_type, compile_time_value)) =
82✔
406
                last.find_parameter_index(identifier)
407
            {
408
                return match compile_time_value {
409
                    Some(value) => (
23✔
410
                        CompilerOutput::new(
23✔
411
                            Some(TypedExpression::new(
23✔
412
                                DeepExpression(lambda::expressions::Expression::make_literal(
23✔
413
                                    value.clone(),
23✔
414
                                )),
415
                                parameter_type,
23✔
416
                            )),
417
                            Vec::new(),
23✔
418
                        ),
419
                        Some(value),
23✔
420
                    ),
421
                    None => (
59✔
422
                        CompilerOutput::new(
59✔
423
                            Some(TypedExpression::new(
59✔
424
                                parameter_index.create_deep_expression(),
59✔
425
                                parameter_type,
59✔
426
                            )),
427
                            Vec::new(),
59✔
428
                        ),
429
                        None,
59✔
430
                    ),
431
                };
432
            } else if layer_count > 1 {
129✔
433
                let result = Self::read_down(&mut layers[..layer_count - 1], identifier, location);
122✔
434
                return match result.0.entry_point {
122✔
435
                    Some(success) => match result.1 {
93✔
436
                        Some(compile_time_value) => (
62✔
437
                            CompilerOutput::new(
62✔
438
                                Some(TypedExpression::new(
62✔
439
                                    DeepExpression(Expression::Literal(compile_time_value.clone())),
62✔
440
                                    success.type_,
62✔
441
                                )),
442
                                result.0.errors,
62✔
443
                            ),
444
                            Some(compile_time_value),
62✔
445
                        ),
446
                        None => {
447
                            let mut errors = result.0.errors;
31✔
448
                            let captured = layers.last_mut().unwrap().capture(success);
31✔
449
                            errors.extend(captured.errors);
31✔
450
                            (CompilerOutput::new(captured.entry_point, errors), None)
31✔
451
                        }
452
                    },
453
                    None => result,
29✔
454
                };
455
            }
456
        }
457
        (
458
            CompilerOutput::new(
7✔
459
                None,
7✔
460
                vec![crate::compilation::CompilerError::new(
7✔
461
                    format!("Identifier {identifier} not found"),
7✔
462
                    *location,
7✔
463
                )],
464
            ),
465
            None,
7✔
466
        )
467
    }
468
}
469

470
pub struct TypeCheckedLambdaParameter {
471
    pub name: Name,
472
    pub source_location: SourceLocation,
473
    pub type_: DeepType,
474
    pub compile_time_value: Option<DeepTree>,
475
}
476

477
impl TypeCheckedLambdaParameter {
478
    pub fn new(
16✔
479
        name: Name,
480
        source_location: SourceLocation,
481
        type_: DeepType,
482
        compile_time_value: Option<DeepTree>,
483
    ) -> Self {
484
        Self {
485
            name,
486
            source_location,
487
            type_,
488
            compile_time_value,
489
        }
490
    }
491
}
492

493
fn is_type(type_of_value: &DeepType) -> bool {
22✔
494
    match &type_of_value.0 {
22✔
495
        GenericType::Any => false,
1✔
496
        GenericType::String => false,
1✔
497
        GenericType::TreeWithKnownChildTypes(_items) => false,
1✔
498
        GenericType::Function {
499
            parameters: _,
500
            return_type: _,
501
        } => false,
1✔
502
        GenericType::Type => true,
17✔
503
        GenericType::Named(_name) => false,
1✔
504
    }
505
}
506

507
struct WithCompilerErrors<T> {
508
    output: T,
509
    errors: Vec<CompilerError>,
510
}
511

512
impl<T> WithCompilerErrors<T> {
513
    pub fn new(output: T, errors: Vec<CompilerError>) -> Self {
56✔
514
        Self { output, errors }
515
    }
516
}
517

518
async fn check_lambda_parameters(
56✔
519
    parameters: &[LambdaParameter],
520
    environment_builder: &mut EnvironmentBuilder,
521
) -> Result<WithCompilerErrors<Vec<TypeCheckedLambdaParameter>>, StoreError> {
522
    let mut checked_parameters = Vec::new();
56✔
523
    let mut errors: Vec<CompilerError> = Vec::new();
56✔
524
    for parameter in parameters {
148✔
525
        let parameter_type: DeepType = match &parameter.type_annotation {
92✔
526
            Some(type_annotation) => {
23✔
527
                let checked_type = check_types(type_annotation, environment_builder).await?;
46✔
528
                errors.extend(checked_type.0.errors);
529
                if let Some(checked) = checked_type.0.entry_point {
22✔
530
                    if is_type(&checked.type_) {
531
                        match checked_type.1 {
17✔
532
                            Some(compile_time_value) => type_from_deep_tree(&compile_time_value),
16✔
533
                            None => {
534
                                errors.push(CompilerError::new(
1✔
535
                                    "Type annotation must be a compile time constant".to_string(),
1✔
536
                                    parameter.source_location,
1✔
537
                                ));
538
                                // Fallback to Any if the type is not valid.
539
                                DeepType(GenericType::Any)
1✔
540
                            }
541
                        }
542
                    } else {
543
                        errors.push(CompilerError::new(
5✔
544
                            "Type annotation must be a type".to_string(),
5✔
545
                            parameter.source_location,
5✔
546
                        ));
547
                        // Fallback to Any if the type is not valid.
548
                        DeepType(GenericType::Any)
5✔
549
                    }
550
                } else {
551
                    // Fallback to Any if the type is not valid.
552
                    DeepType(GenericType::Any)
1✔
553
                }
554
            }
555
            None => {
556
                // If no type annotation is provided, we assume the type is `Any`.
557
                DeepType(GenericType::Any)
23✔
558
            }
559
        };
560
        checked_parameters.push(TypeCheckedLambdaParameter {
561
            name: parameter.name.clone(),
562
            source_location: parameter.source_location,
563
            type_: parameter_type,
564
            compile_time_value: None, // TODO?
565
        });
566
    }
567
    Ok(WithCompilerErrors::new(checked_parameters, errors))
56✔
568
}
569

570
pub async fn check_lambda(
56✔
571
    parameters: &[LambdaParameter],
572
    body: &ast::Expression,
573
    environment_builder: &mut EnvironmentBuilder,
574
) -> Result<CompilerOutput, StoreError> {
575
    let checked_parameters = check_lambda_parameters(parameters, environment_builder).await?;
112✔
576
    let mut errors = checked_parameters.errors;
577
    environment_builder.enter_lambda_body(&checked_parameters.output[..]);
578
    let body_result = check_types(body, environment_builder).await;
56✔
579
    // TODO: use RAII or something?
580
    let environment = environment_builder.leave_lambda_body();
56✔
581
    let environment_expressions = environment
56✔
582
        .into_iter()
583
        .map(|typed_expression| Arc::new(typed_expression.expression))
68✔
584
        .collect();
585
    let body_output = body_result?;
112✔
586
    errors.extend(body_output.0.errors);
587
    match body_output.0.entry_point {
588
        Some(body_checked) => Ok(CompilerOutput {
55✔
589
            entry_point: Some(TypedExpression::new(
55✔
590
                lambda::expressions::DeepExpression(lambda::expressions::Expression::Lambda {
55✔
591
                    environment: Arc::new(DeepExpression(Expression::make_construct_tree(
55✔
592
                        environment_expressions,
55✔
593
                    ))),
594
                    body: Arc::new(body_checked.expression),
55✔
595
                }),
596
                DeepType(GenericType::Function {
55✔
597
                    parameters: checked_parameters
55✔
598
                        .output
55✔
599
                        .into_iter()
55✔
600
                        .map(|parameter| parameter.type_)
100✔
601
                        .collect(),
55✔
602
                    return_type: Box::new(body_checked.type_),
55✔
603
                }),
604
            )),
605
            errors,
55✔
606
        }),
607
        None => Ok(CompilerOutput::new(None, errors)),
1✔
608
    }
609
}
610

611
pub async fn check_let(
18✔
612
    name: &Name,
613
    location: &SourceLocation,
614
    value: &ast::Expression,
615
    body: &ast::Expression,
616
    environment_builder: &mut EnvironmentBuilder,
617
) -> Result<CompilerOutput, StoreError> {
618
    let value_checked = check_types(value, environment_builder).await?;
36✔
619
    let value_checked_unwrapped = match value_checked.0.entry_point {
16✔
620
        Some(success) => success,
16✔
621
        None => return Ok(value_checked.0),
2✔
622
    };
623
    let checked_parameters = [TypeCheckedLambdaParameter::new(
16✔
624
        name.clone(),
16✔
625
        *location,
16✔
626
        value_checked_unwrapped.type_.clone(),
16✔
627
        value_checked.1,
16✔
628
    )];
629
    environment_builder.enter_lambda_body(&checked_parameters[..]);
16✔
630
    let body_result = check_types(body, environment_builder).await;
32✔
631
    // TODO: use RAII or something?
632
    let environment = environment_builder.leave_lambda_body();
16✔
633
    let environment_expressions = environment
16✔
634
        .into_iter()
635
        .map(|typed_expression| Arc::new(typed_expression.expression))
22✔
636
        .collect();
637
    let body_output = body_result?;
32✔
638
    match body_output.0.entry_point {
639
        Some(body_checked) => Ok(CompilerOutput {
15✔
640
            entry_point: Some(TypedExpression::new(
15✔
641
                lambda::expressions::DeepExpression(lambda::expressions::Expression::make_apply(
15✔
642
                    Arc::new(lambda::expressions::DeepExpression(
15✔
643
                        lambda::expressions::Expression::Lambda {
15✔
644
                            environment: Arc::new(DeepExpression(Expression::make_construct_tree(
15✔
645
                                environment_expressions,
15✔
646
                            ))),
647
                            body: Arc::new(body_checked.expression),
15✔
648
                        },
649
                    )),
650
                    Arc::new(value_checked_unwrapped.expression),
15✔
651
                )),
652
                body_checked.type_,
15✔
653
            )),
654
            errors: body_output.0.errors,
15✔
655
        }),
656
        None => Ok(CompilerOutput::new(None, body_output.0.errors)),
1✔
657
    }
658
}
659

660
async fn check_type_of(
8✔
661
    expression: &ast::Expression,
662
    environment_builder: &mut EnvironmentBuilder,
663
) -> Result<(CompilerOutput, Option<DeepTree>), StoreError> {
664
    // Copy environment builder to prevent captures by unevaluated expressions.
665
    // TODO: make this more efficient (clone could be expensive).
666
    let mut unevaluated_context = environment_builder.clone();
8✔
667
    let checked_expression = check_types(expression, &mut unevaluated_context).await?;
16✔
668
    drop(unevaluated_context);
669

670
    let type_ = match checked_expression.0.entry_point {
7✔
671
        Some(success) => success.type_,
7✔
672
        None => {
673
            assert!(!checked_expression.0.errors.is_empty());
674
            return Ok((CompilerOutput::new(None, checked_expression.0.errors), None));
1✔
675
        }
676
    };
677
    let errors = checked_expression.0.errors;
7✔
678
    let type_as_tree = type_to_deep_tree(&type_);
7✔
679
    Ok((
7✔
680
        CompilerOutput::new(
7✔
681
            Some(TypedExpression::new(
7✔
682
                lambda::expressions::DeepExpression(lambda::expressions::Expression::Literal(
7✔
683
                    type_as_tree.clone(),
7✔
684
                )),
685
                DeepType(GenericType::Type),
7✔
686
            )),
687
            errors,
7✔
688
        ),
689
        Some(type_as_tree),
7✔
690
    ))
691
}
692

693
pub async fn check_types(
237✔
694
    syntax_tree: &ast::Expression,
695
    environment_builder: &mut EnvironmentBuilder,
696
) -> Result<(CompilerOutput, Option<DeepTree>), StoreError> {
697
    Box::pin(async move {
474✔
698
        match syntax_tree {
237✔
699
            ast::Expression::Identifier(name, location) => {
89✔
700
                Ok(environment_builder.read(name, location))
89✔
701
            }
702
            ast::Expression::StringLiteral(value, source_location) => {
25✔
703
                let compile_time_value = match DeepTree::try_from_string(value) {
49✔
704
                    Some(tree) => tree,
24✔
705
                    None => {
706
                        return Ok((
1✔
707
                            CompilerOutput::new(
1✔
708
                                None,
1✔
709
                                vec![CompilerError::new(
1✔
710
                                    "String literal is too long".to_string(),
1✔
711
                                    *source_location,
1✔
712
                                )],
713
                            ),
714
                            None,
1✔
715
                        ))
716
                    }
717
                };
718
                Ok((
24✔
719
                    CompilerOutput::new(
24✔
720
                        Some(TypedExpression::new(
24✔
721
                            lambda::expressions::DeepExpression(
24✔
722
                                lambda::expressions::Expression::Literal(
24✔
723
                                    compile_time_value.clone(),
24✔
724
                                ),
725
                            ),
726
                            DeepType(GenericType::String),
24✔
727
                        )),
728
                        Vec::new(),
24✔
729
                    ),
730
                    Some(compile_time_value),
24✔
731
                ))
732
            }
733
            ast::Expression::Apply { callee, arguments } => {
14✔
734
                let callee_output = check_types(callee, environment_builder).await?;
28✔
735
                let argument_output = if arguments.len() == 1 {
14✔
736
                    // For N=1 we don't need an indirection.
737
                    check_types(&arguments[0], environment_builder).await?
6✔
738
                } else {
739
                    check_tree_construction_or_argument_list(&arguments[..], environment_builder)
8✔
740
                        .await?
8✔
741
                };
742
                let errors = callee_output
743
                    .0
744
                    .errors
745
                    .into_iter()
746
                    .chain(argument_output.0.errors)
747
                    .collect();
748
                match (callee_output.0.entry_point, argument_output.0.entry_point) {
749
                    (Some(callee_checked), Some(argument_checked)) => {
12✔
750
                        let return_type = match &callee_checked.type_.0 {
23✔
751
                            GenericType::Function { return_type, .. } => {
752
                                return_type.as_ref().clone()
753
                            }
754
                            _ => {
755
                                return Ok((
1✔
756
                                    CompilerOutput::new(
1✔
757
                                        None,
1✔
758
                                        vec![crate::compilation::CompilerError::new(
1✔
759
                                            "Callee is not a function".to_string(),
1✔
760
                                            callee.source_location(),
1✔
761
                                        )],
762
                                    ),
763
                                    None,
1✔
764
                                ))
765
                            }
766
                        };
767
                        // TODO: check argument types against callee parameter types
768
                        Ok((
769
                            CompilerOutput {
770
                                entry_point: Some(TypedExpression::new(
771
                                    lambda::expressions::DeepExpression(
772
                                        lambda::expressions::Expression::Apply {
773
                                            callee: Arc::new(callee_checked.expression),
774
                                            argument: Arc::new(argument_checked.expression),
775
                                        },
776
                                    ),
777
                                    return_type,
778
                                )),
779
                                errors,
780
                            },
781
                            /*TODO: compile time function calls*/ None,
782
                        ))
783
                    }
784
                    (None, _) | (_, None) => Ok((CompilerOutput::new(None, errors), None)),
2✔
785
                }
786
            }
787
            ast::Expression::Lambda { parameters, body } => {
56✔
788
                check_lambda(&parameters[..], body, environment_builder)
56✔
789
                    .await
56✔
790
                    .map(|output| (output, /*TODO: compile time function calls*/ None))
112✔
791
            }
792
            ast::Expression::ConstructTree(arguments) => {
18✔
793
                check_tree_construction_or_argument_list(&arguments[..], environment_builder).await
18✔
794
            }
795
            ast::Expression::Braces(expression) => {
9✔
796
                check_types(expression, environment_builder).await
9✔
797
            }
798
            ast::Expression::Let {
799
                name,
18✔
800
                location,
18✔
801
                value,
18✔
802
                body,
18✔
803
            } => check_let(name, location, value, body, environment_builder)
18✔
804
                .await
18✔
805
                .map(|output| {
36✔
806
                    (
807
                        output, /*TODO: compile time let or a const keyword*/ None,
18✔
808
                    )
809
                }),
810
            ast::Expression::TypeOf(expression) => {
8✔
811
                check_type_of(expression, environment_builder).await
8✔
812
            }
813
        }
814
    })
815
    .await
237✔
816
}
817

818
pub async fn check_types_with_default_globals(
44✔
819
    syntax_tree: &ast::Expression,
820
    default_global_namespace: NamespaceId,
821
) -> Result<CompilerOutput, StoreError> {
822
    let mut environment_builder = EnvironmentBuilder::new();
44✔
823
    environment_builder.define_constant(
44✔
824
        Name::new(default_global_namespace, "Type".to_string()),
44✔
825
        DeepType(GenericType::Type),
44✔
826
        type_to_deep_tree(&DeepType(GenericType::Type)),
44✔
827
    );
828
    environment_builder.define_constant(
44✔
829
        Name::new(default_global_namespace, "String".to_string()),
44✔
830
        DeepType(GenericType::Type),
44✔
831
        type_to_deep_tree(&DeepType(GenericType::String)),
44✔
832
    );
833
    let bool_type = DeepType(GenericType::Named(Name::new(
44✔
834
        default_global_namespace,
44✔
835
        "Bool".to_string(),
44✔
836
    )));
837
    environment_builder.define_constant(
44✔
838
        Name::new(default_global_namespace, "Bool".to_string()),
44✔
839
        DeepType(GenericType::Type),
44✔
840
        type_to_deep_tree(&bool_type),
44✔
841
    );
842
    environment_builder.define_constant(
44✔
843
        Name::new(default_global_namespace, "true".to_string()),
44✔
844
        bool_type.clone(),
44✔
845
        DeepTree::new(
44✔
846
            TreeBlob::try_from(bytes::Bytes::from_static(&[1u8]))
44✔
847
                .expect("one byte will always fit"),
44✔
848
            Vec::new(),
44✔
849
        ),
850
    );
851
    environment_builder.define_constant(
44✔
852
        Name::new(default_global_namespace, "false".to_string()),
44✔
853
        bool_type,
44✔
854
        DeepTree::new(
44✔
855
            TreeBlob::try_from(bytes::Bytes::from_static(&[0u8]))
44✔
856
                .expect("one byte will always fit"),
44✔
857
            Vec::new(),
44✔
858
        ),
859
    );
860
    let output = check_types(syntax_tree, &mut environment_builder).await;
88✔
861
    environment_builder.undefine_constant();
44✔
862
    environment_builder.undefine_constant();
44✔
863
    environment_builder.undefine_constant();
44✔
864
    environment_builder.undefine_constant();
44✔
865
    environment_builder.undefine_constant();
44✔
866
    assert!(environment_builder.is_empty());
867
    output.map(|output| /*TODO: return compile time values*/ output.0)
88✔
868
}
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