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

TyRoXx / NonlocalityOS / 15517294876

08 Jun 2025 10:01AM UTC coverage: 74.495% (+0.3%) from 74.179%
15517294876

push

github

TyRoXx
GH-280: clippy

2 of 2 new or added lines in 1 file covered. (100.0%)

26 existing lines in 5 files now uncovered.

3765 of 5054 relevant lines covered (74.5%)

2083.08 hits per line

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

85.88
/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
}
31

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

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

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

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

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

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

148
impl TypedExpression {
149
    pub fn new(expression: DeepExpression, type_: DeepType) -> Self {
195✔
150
        Self { expression, type_ }
151
    }
152
}
153

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

185
#[derive(Debug, Clone, Copy)]
186
enum ParameterIndex {
187
    SingleParameter,
188
    GetChild(u16),
189
}
190

191
impl ParameterIndex {
192
    pub fn create_deep_expression(&self) -> lambda::expressions::DeepExpression {
38✔
193
        match self {
38✔
194
            ParameterIndex::SingleParameter => {
195
                lambda::expressions::DeepExpression(lambda::expressions::Expression::make_argument())
28✔
196
            }
197
            ParameterIndex::GetChild(index) => lambda::expressions::DeepExpression(
198
                lambda::expressions::Expression::make_get_child(
10✔
199
                    Arc::new(lambda::expressions::DeepExpression(
10✔
200
                        lambda::expressions::Expression::make_argument(),
10✔
201
                    )),
202
                    *index,
10✔
203
                ),
204
            ),
205
        }
206
    }
207
}
208

209
struct LocalVariable {
210
    parameter_index: ParameterIndex,
211
    type_: DeepType,
212
    compile_time_value: Option<DeepTree>,
213
}
214

215
impl LocalVariable {
216
    pub fn new(
100✔
217
        parameter_index: ParameterIndex,
218
        type_: DeepType,
219
        compile_time_value: Option<DeepTree>,
220
    ) -> Self {
221
        Self {
222
            parameter_index,
223
            type_,
224
            compile_time_value,
225
        }
226
    }
227
}
228

229
struct LambdaScope {
230
    names: BTreeMap<Name, LocalVariable>,
231
    captures: BTreeMap<TypedExpression, u16>,
232
}
233

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

265
    pub fn new_constant_scope(name: Name, type_: DeepType, compile_time_value: DeepTree) -> Self {
64✔
266
        let mut names = BTreeMap::new();
64✔
267
        names.insert(
64✔
268
            name,
64✔
269
            LocalVariable::new(
64✔
270
                ParameterIndex::SingleParameter,
64✔
271
                type_.clone(),
64✔
272
                Some(compile_time_value),
64✔
273
            ),
274
        );
275
        Self {
276
            names,
277
            captures: BTreeMap::new(),
64✔
278
        }
279
    }
280

281
    pub fn find_parameter_index(
95✔
282
        &self,
283
        parameter_name: &Name,
284
    ) -> Option<(ParameterIndex, DeepType, Option<DeepTree>)> {
285
        self.names.get(parameter_name).map(|variable| {
144✔
286
            (
287
                variable.parameter_index,
49✔
288
                variable.type_.clone(),
49✔
289
                variable.compile_time_value.clone(),
49✔
290
            )
291
        })
292
    }
293

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

324
    pub fn leave(self) -> Vec<TypedExpression> {
111✔
325
        let mut as_vec: Vec<(TypedExpression, u16)> = self.captures.into_iter().collect();
111✔
326
        as_vec.sort_by_key(|(_, index)| *index);
119✔
327
        // sanity check:
328
        for (expected_index, (_, actual_index)) in as_vec.iter().enumerate() {
125✔
329
            assert_eq!(expected_index, *actual_index as usize);
330
        }
331
        as_vec
111✔
332
            .into_iter()
333
            .map(|(expression, _)| expression)
125✔
334
            .collect()
335
    }
336
}
337

338
pub struct EnvironmentBuilder {
339
    lambda_layers: Vec<LambdaScope>,
340
}
341

342
impl Default for EnvironmentBuilder {
UNCOV
343
    fn default() -> Self {
×
UNCOV
344
        Self::new()
×
345
    }
346
}
347

348
impl EnvironmentBuilder {
349
    pub fn new() -> Self {
32✔
350
        Self {
351
            lambda_layers: Vec::new(),
32✔
352
        }
353
    }
354

355
    pub fn is_empty(&self) -> bool {
32✔
356
        self.lambda_layers.is_empty()
32✔
357
    }
358

359
    pub fn enter_lambda_body(&mut self, parameters: &[TypeCheckedLambdaParameter]) {
47✔
360
        self.lambda_layers
47✔
361
            .push(LambdaScope::new_lambda_scope(parameters));
47✔
362
    }
363

364
    pub fn leave_lambda_body(&mut self) -> Vec<TypedExpression> {
111✔
365
        let top_scope = self.lambda_layers.pop().unwrap();
111✔
366
        top_scope.leave()
111✔
367
    }
368

369
    pub fn define_constant(&mut self, name: Name, type_: DeepType, compile_time_value: DeepTree) {
64✔
370
        self.lambda_layers.push(LambdaScope::new_constant_scope(
64✔
371
            name,
64✔
372
            type_,
64✔
373
            compile_time_value,
64✔
374
        ));
375
    }
376

377
    pub fn undefine_constant(&mut self) {
64✔
378
        let captures = self.leave_lambda_body();
64✔
379
        if !captures.is_empty() {
64✔
380
            todo!()
381
        }
382
    }
383

384
    pub fn read(
53✔
385
        &mut self,
386
        identifier: &Name,
387
        location: &SourceLocation,
388
    ) -> (CompilerOutput, Option<DeepTree>) {
389
        Self::read_down(&mut self.lambda_layers, identifier, location)
53✔
390
    }
391

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

464
pub struct TypeCheckedLambdaParameter {
465
    pub name: Name,
466
    pub source_location: SourceLocation,
467
    pub type_: DeepType,
468
    pub compile_time_value: Option<DeepTree>,
469
}
470

471
impl TypeCheckedLambdaParameter {
472
    pub fn new(
6✔
473
        name: Name,
474
        source_location: SourceLocation,
475
        type_: DeepType,
476
        compile_time_value: Option<DeepTree>,
477
    ) -> Self {
478
        Self {
479
            name,
480
            source_location,
481
            type_,
482
            compile_time_value,
483
        }
484
    }
485
}
486

487
fn is_type(type_of_value: &DeepType) -> bool {
10✔
488
    match &type_of_value.0 {
10✔
UNCOV
489
        GenericType::Any => false,
×
UNCOV
490
        GenericType::String => false,
×
491
        GenericType::TreeWithKnownChildTypes(_items) => false,
1✔
492
        GenericType::Function {
493
            parameters: _,
494
            return_type: _,
UNCOV
495
        } => false,
×
496
        GenericType::Type => true,
9✔
497
    }
498
}
499

500
struct WithCompilerErrors<T> {
501
    output: T,
502
    errors: Vec<CompilerError>,
503
}
504

505
impl<T> WithCompilerErrors<T> {
506
    pub fn new(output: T, errors: Vec<CompilerError>) -> Self {
41✔
507
        Self { output, errors }
508
    }
509
}
510

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

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

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

653
pub async fn check_types(
146✔
654
    syntax_tree: &ast::Expression,
655
    environment_builder: &mut EnvironmentBuilder,
656
) -> Result<(CompilerOutput, Option<DeepTree>), StoreError> {
657
    Box::pin(async move {
292✔
658
        match syntax_tree {
146✔
659
            ast::Expression::Identifier(name, location) => {
53✔
660
                Ok(environment_builder.read(name, location))
53✔
661
            }
662
            ast::Expression::StringLiteral(value, source_location) => {
15✔
663
                let compile_time_value = match DeepTree::try_from_string(value) {
29✔
664
                    Some(tree) => tree,
14✔
665
                    None => {
666
                        return Ok((
1✔
667
                            CompilerOutput::new(
1✔
668
                                None,
1✔
669
                                vec![CompilerError::new(
1✔
670
                                    "String literal is too long".to_string(),
1✔
671
                                    *source_location,
1✔
672
                                )],
673
                            ),
674
                            None,
1✔
675
                        ))
676
                    }
677
                };
678
                Ok((
14✔
679
                    CompilerOutput::new(
14✔
680
                        Some(TypedExpression::new(
14✔
681
                            lambda::expressions::DeepExpression(
14✔
682
                                lambda::expressions::Expression::Literal(
14✔
683
                                    compile_time_value.clone(),
14✔
684
                                ),
685
                            ),
686
                            DeepType(GenericType::String),
14✔
687
                        )),
688
                        Vec::new(),
14✔
689
                    ),
690
                    Some(compile_time_value),
14✔
691
                ))
692
            }
693
            ast::Expression::Apply { callee, arguments } => {
7✔
694
                let callee_output = check_types(callee, environment_builder).await?;
14✔
695
                let argument_output = if arguments.len() == 1 {
7✔
696
                    // For N=1 we don't need an indirection.
697
                    check_types(&arguments[0], environment_builder).await?
3✔
698
                } else {
699
                    check_tree_construction_or_argument_list(&arguments[..], environment_builder)
4✔
700
                        .await?
4✔
701
                };
702
                let errors = callee_output
703
                    .0
704
                    .errors
705
                    .into_iter()
706
                    .chain(argument_output.0.errors)
707
                    .collect();
708
                match (callee_output.0.entry_point, argument_output.0.entry_point) {
709
                    (Some(callee_checked), Some(argument_checked)) => {
6✔
710
                        let return_type = match &callee_checked.type_.0 {
11✔
711
                            GenericType::Function { return_type, .. } => {
712
                                return_type.as_ref().clone()
713
                            }
714
                            _ => {
715
                                return Ok((
1✔
716
                                    CompilerOutput::new(
1✔
717
                                        None,
1✔
718
                                        vec![crate::compilation::CompilerError::new(
1✔
719
                                            "Callee is not a function".to_string(),
1✔
720
                                            callee.source_location(),
1✔
721
                                        )],
722
                                    ),
723
                                    None,
1✔
724
                                ))
725
                            }
726
                        };
727
                        // TODO: check argument types against callee parameter types
728
                        Ok((
729
                            CompilerOutput {
730
                                entry_point: Some(TypedExpression::new(
731
                                    lambda::expressions::DeepExpression(
732
                                        lambda::expressions::Expression::Apply {
733
                                            callee: Arc::new(callee_checked.expression),
734
                                            argument: Arc::new(argument_checked.expression),
735
                                        },
736
                                    ),
737
                                    return_type,
738
                                )),
739
                                errors,
740
                            },
741
                            /*TODO: compile time function calls*/ None,
742
                        ))
743
                    }
744
                    (None, _) | (_, None) => Ok((CompilerOutput::new(None, errors), None)),
1✔
745
                }
746
            }
747
            ast::Expression::Lambda { parameters, body } => {
41✔
748
                check_lambda(&parameters[..], body, environment_builder)
41✔
749
                    .await
41✔
750
                    .map(|output| (output, /*TODO: compile time function calls*/ None))
82✔
751
            }
752
            ast::Expression::ConstructTree(arguments) => {
16✔
753
                check_tree_construction_or_argument_list(&arguments[..], environment_builder).await
16✔
754
            }
755
            ast::Expression::Braces(expression) => {
6✔
756
                check_types(expression, environment_builder).await
6✔
757
            }
758
            ast::Expression::Let {
759
                name,
8✔
760
                location,
8✔
761
                value,
8✔
762
                body,
8✔
763
            } => check_let(name, location, value, body, environment_builder)
8✔
764
                .await
8✔
765
                .map(|output| {
16✔
766
                    (
767
                        output, /*TODO: compile time let or a const keyword*/ None,
8✔
768
                    )
769
                }),
770
        }
771
    })
772
    .await
146✔
773
}
774

775
pub async fn check_types_with_default_globals(
32✔
776
    syntax_tree: &ast::Expression,
777
    default_global_namespace: NamespaceId,
778
) -> Result<CompilerOutput, StoreError> {
779
    let mut environment_builder = EnvironmentBuilder::new();
32✔
780
    environment_builder.define_constant(
32✔
781
        Name::new(default_global_namespace, "Type".to_string()),
32✔
782
        DeepType(GenericType::Type),
32✔
783
        type_to_deep_tree(&DeepType(GenericType::Type)),
32✔
784
    );
785
    environment_builder.define_constant(
32✔
786
        Name::new(default_global_namespace, "String".to_string()),
32✔
787
        DeepType(GenericType::Type),
32✔
788
        type_to_deep_tree(&DeepType(GenericType::String)),
32✔
789
    );
790
    let output = check_types(syntax_tree, &mut environment_builder).await;
64✔
791
    environment_builder.undefine_constant();
32✔
792
    environment_builder.undefine_constant();
32✔
793
    assert!(environment_builder.is_empty());
794
    output.map(|output| /*TODO: return compile time values*/ output.0)
64✔
795
}
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