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

TyRoXx / NonlocalityOS / 15533777943

09 Jun 2025 11:47AM UTC coverage: 76.336% (+0.3%) from 75.988%
15533777943

push

github

TyRoXx
GH-290: check argument count and types

89 of 91 new or added lines in 5 files covered. (97.8%)

1 existing line in 1 file now uncovered.

4042 of 5295 relevant lines covered (76.34%)

2221.01 hits per line

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

98.73
/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>) {
155✔
37
    match deep_type.0 {
155✔
38
        GenericType::Any => (GenericType::Any, Vec::new()),
3✔
39
        GenericType::String => (GenericType::String, Vec::new()),
56✔
40
        GenericType::TreeWithKnownChildTypes(ref children) => (
1✔
41
            GenericType::TreeWithKnownChildTypes(
1✔
42
                (0u64..(children.len() as u64))
1✔
43
                    .map(ReferenceIndex)
1✔
44
                    .collect(),
1✔
45
            ),
46
            children.clone(),
1✔
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()),
47✔
69
        GenericType::Named(ref name) => (GenericType::Named(name.clone()), Vec::new()),
47✔
70
    }
71
}
72

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

82
pub fn from_reference_type(body: &GenericType<ReferenceIndex>, children: &[DeepType]) -> DeepType {
26✔
83
    match body {
26✔
84
        GenericType::Any => DeepType(GenericType::Any),
×
85
        GenericType::String => DeepType(GenericType::String),
21✔
86
        GenericType::TreeWithKnownChildTypes(ref children_references) => {
1✔
87
            let mut resulting_children = Vec::new();
1✔
88
            for reference in children_references {
5✔
89
                let index = reference.0 as usize;
2✔
90
                if index < children.len() {
4✔
91
                    resulting_children.push(children[index].clone());
2✔
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))
1✔
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 {
26✔
135
    let body: GenericType<ReferenceIndex> =
26✔
136
        postcard::from_bytes(deep_tree.blob().as_slice()).unwrap(/*TODO*/);
26✔
137
    let children: Vec<_> = deep_tree
26✔
138
        .references()
139
        .iter()
140
        .map(type_from_deep_tree)
26✔
141
        .collect();
142
    from_reference_type(&body, &children)
26✔
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 {
378✔
153
        Self { expression, type_ }
154
    }
155
}
156

157
async fn check_tree_construction_or_argument_list(
28✔
158
    arguments: &[ast::Expression],
159
    environment_builder: &mut EnvironmentBuilder,
160
) -> Result<(CompilerOutput, Option<DeepTree>), StoreError> {
161
    let mut errors = Vec::new();
28✔
162
    let mut checked_arguments = Vec::new();
28✔
163
    let mut argument_types = Vec::new();
28✔
164
    for argument in arguments {
114✔
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((
28✔
175
        CompilerOutput {
28✔
176
            entry_point: Some(TypedExpression::new(
28✔
177
                lambda::expressions::DeepExpression(
28✔
178
                    lambda::expressions::Expression::ConstructTree(checked_arguments),
28✔
179
                ),
180
                DeepType(GenericType::TreeWithKnownChildTypes(argument_types)),
28✔
181
            )),
182
            errors,
28✔
183
        },
184
        /*TODO: support compile time tree construction*/ None,
28✔
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 {
61✔
196
        match self {
61✔
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(
18✔
202
                    Arc::new(lambda::expressions::DeepExpression(
18✔
203
                        lambda::expressions::Expression::make_argument(),
18✔
204
                    )),
205
                    *index,
18✔
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(
302✔
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 {
75✔
241
        let mut names = BTreeMap::new();
75✔
242
        if parameters.len() == 1 {
119✔
243
            names.insert(
44✔
244
                parameters[0].name.clone(),
44✔
245
                LocalVariable::new(
44✔
246
                    ParameterIndex::SingleParameter,
44✔
247
                    parameters[0].type_.clone(),
44✔
248
                    parameters[0].compile_time_value.clone(),
44✔
249
                ),
250
            );
251
        } else {
252
            for (index, parameter) in parameters.iter().enumerate() {
54✔
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(),
75✔
267
        }
268
    }
269

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

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

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

329
    pub fn leave(self) -> Vec<TypedExpression> {
310✔
330
        let mut as_vec: Vec<(TypedExpression, u16)> = self.captures.into_iter().collect();
310✔
331
        as_vec.sort_by_key(|(_, index)| *index);
318✔
332
        // sanity check:
333
        for (expected_index, (_, actual_index)) in as_vec.iter().enumerate() {
328✔
334
            assert_eq!(expected_index, *actual_index as usize);
335
        }
336
        as_vec
310✔
337
            .into_iter()
338
            .map(|(expression, _)| expression)
328✔
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 {
47✔
356
        Self {
357
            lambda_layers: Vec::new(),
47✔
358
        }
359
    }
360

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

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

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

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

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

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

398
    fn read_down(
242✔
399
        layers: &mut [LambdaScope],
400
        identifier: &Name,
401
        location: &SourceLocation,
402
    ) -> (CompilerOutput, Option<DeepTree>) {
403
        let layer_count = layers.len();
242✔
404
        if let Some(last) = layers.last_mut() {
484✔
405
            if let Some((parameter_index, parameter_type, compile_time_value)) =
90✔
406
                last.find_parameter_index(identifier)
407
            {
408
                return match compile_time_value {
409
                    Some(value) => (
29✔
410
                        CompilerOutput::new(
29✔
411
                            Some(TypedExpression::new(
29✔
412
                                DeepExpression(lambda::expressions::Expression::make_literal(
29✔
413
                                    value.clone(),
29✔
414
                                )),
415
                                parameter_type,
29✔
416
                            )),
417
                            Vec::new(),
29✔
418
                        ),
419
                        Some(value),
29✔
420
                    ),
421
                    None => (
61✔
422
                        CompilerOutput::new(
61✔
423
                            Some(TypedExpression::new(
61✔
424
                                parameter_index.create_deep_expression(),
61✔
425
                                parameter_type,
61✔
426
                            )),
427
                            Vec::new(),
61✔
428
                        ),
429
                        None,
61✔
430
                    ),
431
                };
432
            } else if layer_count > 1 {
152✔
433
                let result = Self::read_down(&mut layers[..layer_count - 1], identifier, location);
145✔
434
                return match result.0.entry_point {
145✔
435
                    Some(success) => match result.1 {
116✔
436
                        Some(compile_time_value) => (
86✔
437
                            CompilerOutput::new(
86✔
438
                                Some(TypedExpression::new(
86✔
439
                                    DeepExpression(Expression::Literal(compile_time_value.clone())),
86✔
440
                                    success.type_,
86✔
441
                                )),
442
                                result.0.errors,
86✔
443
                            ),
444
                            Some(compile_time_value),
86✔
445
                        ),
446
                        None => {
447
                            let mut errors = result.0.errors;
30✔
448
                            let captured = layers.last_mut().unwrap().capture(success);
30✔
449
                            errors.extend(captured.errors);
30✔
450
                            (CompilerOutput::new(captured.entry_point, errors), None)
30✔
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 {
27✔
494
    match &type_of_value.0 {
27✔
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,
22✔
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 {
59✔
514
        Self { output, errors }
515
    }
516
}
517

518
async fn check_lambda_parameters(
59✔
519
    parameters: &[LambdaParameter],
520
    environment_builder: &mut EnvironmentBuilder,
521
) -> Result<WithCompilerErrors<Vec<TypeCheckedLambdaParameter>>, StoreError> {
522
    let mut checked_parameters = Vec::new();
59✔
523
    let mut errors: Vec<CompilerError> = Vec::new();
59✔
524
    for parameter in parameters {
161✔
525
        let parameter_type: DeepType = match &parameter.type_annotation {
102✔
526
            Some(type_annotation) => {
28✔
527
                let checked_type = check_types(type_annotation, environment_builder).await?;
56✔
528
                errors.extend(checked_type.0.errors);
529
                if let Some(checked) = checked_type.0.entry_point {
27✔
530
                    if is_type(&checked.type_) {
531
                        match checked_type.1 {
22✔
532
                            Some(compile_time_value) => type_from_deep_tree(&compile_time_value),
21✔
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))
59✔
568
}
569

570
pub async fn check_lambda(
59✔
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?;
118✔
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;
59✔
579
    // TODO: use RAII or something?
580
    let environment = environment_builder.leave_lambda_body();
59✔
581
    let environment_expressions = environment
59✔
582
        .into_iter()
583
        .map(|typed_expression| Arc::new(typed_expression.expression))
71✔
584
        .collect();
585
    let body_output = body_result?;
118✔
586
    errors.extend(body_output.0.errors);
587
    match body_output.0.entry_point {
588
        Some(body_checked) => Ok(CompilerOutput {
58✔
589
            entry_point: Some(TypedExpression::new(
58✔
590
                lambda::expressions::DeepExpression(lambda::expressions::Expression::Lambda {
58✔
591
                    environment: Arc::new(DeepExpression(Expression::make_construct_tree(
58✔
592
                        environment_expressions,
58✔
593
                    ))),
594
                    body: Arc::new(body_checked.expression),
58✔
595
                }),
596
                DeepType(GenericType::Function {
58✔
597
                    parameters: checked_parameters
58✔
598
                        .output
58✔
599
                        .into_iter()
58✔
600
                        .map(|parameter| parameter.type_)
108✔
601
                        .collect(),
58✔
602
                    return_type: Box::new(body_checked.type_),
58✔
603
                }),
604
            )),
605
            errors,
58✔
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(
7✔
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();
7✔
667
    let checked_expression = check_types(expression, &mut unevaluated_context).await?;
14✔
668
    drop(unevaluated_context);
669

670
    let type_ = match checked_expression.0.entry_point {
6✔
671
        Some(success) => success.type_,
6✔
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;
6✔
678
    let type_as_tree = type_to_deep_tree(&type_);
6✔
679
    Ok((
6✔
680
        CompilerOutput::new(
6✔
681
            Some(TypedExpression::new(
6✔
682
                lambda::expressions::DeepExpression(lambda::expressions::Expression::Literal(
6✔
683
                    type_as_tree.clone(),
6✔
684
                )),
685
                DeepType(GenericType::Type),
6✔
686
            )),
687
            errors,
6✔
688
        ),
689
        Some(type_as_tree),
6✔
690
    ))
691
}
692

693
pub fn convert_implicitly(from: &DeepType, to: &DeepType) -> bool {
46✔
694
    match (&from.0, &to.0) {
46✔
695
        (_, GenericType::Any) => true,
12✔
696
        (GenericType::String, GenericType::String) => true,
15✔
697
        (
698
            GenericType::TreeWithKnownChildTypes(from_children),
3✔
699
            GenericType::TreeWithKnownChildTypes(to_children),
3✔
700
        ) => {
3✔
701
            if from_children.len() != to_children.len() {
3✔
702
                return false;
1✔
703
            }
704
            from_children
2✔
705
                .iter()
706
                .zip(to_children.iter())
2✔
707
                .all(|(from_child, to_child)| convert_implicitly(from_child, to_child))
5✔
708
        }
709
        (
710
            GenericType::Function {
711
                parameters: from_params,
7✔
712
                return_type: from_return,
7✔
713
            },
714
            GenericType::Function {
7✔
715
                parameters: to_params,
7✔
716
                return_type: to_return,
7✔
717
            },
718
        ) => {
7✔
719
            if from_params.len() != to_params.len() {
7✔
720
                return false;
1✔
721
            }
722
            from_params
6✔
723
                .iter()
6✔
724
                .zip(to_params.iter())
6✔
725
                .all(|(from_param, to_param)| convert_implicitly(to_param, from_param))
11✔
726
                && convert_implicitly(from_return.as_ref(), to_return.as_ref())
5✔
727
        }
728
        (GenericType::Type, GenericType::Type) => true,
1✔
729
        (GenericType::Named(from_name), GenericType::Named(to_name)) => from_name == to_name,
2✔
730
        _ => false,
6✔
731
    }
732
}
733

734
pub async fn check_types(
254✔
735
    syntax_tree: &ast::Expression,
736
    environment_builder: &mut EnvironmentBuilder,
737
) -> Result<(CompilerOutput, Option<DeepTree>), StoreError> {
738
    Box::pin(async move {
508✔
739
        match syntax_tree {
254✔
740
            ast::Expression::Identifier(name, location) => {
97✔
741
                Ok(environment_builder.read(name, location))
97✔
742
            }
743
            ast::Expression::StringLiteral(value, source_location) => {
25✔
744
                let compile_time_value = match DeepTree::try_from_string(value) {
49✔
745
                    Some(tree) => tree,
24✔
746
                    None => {
747
                        return Ok((
1✔
748
                            CompilerOutput::new(
1✔
749
                                None,
1✔
750
                                vec![CompilerError::new(
1✔
751
                                    "String literal is too long".to_string(),
1✔
752
                                    *source_location,
1✔
753
                                )],
754
                            ),
755
                            None,
1✔
756
                        ))
757
                    }
758
                };
759
                Ok((
24✔
760
                    CompilerOutput::new(
24✔
761
                        Some(TypedExpression::new(
24✔
762
                            lambda::expressions::DeepExpression(
24✔
763
                                lambda::expressions::Expression::Literal(
24✔
764
                                    compile_time_value.clone(),
24✔
765
                                ),
766
                            ),
767
                            DeepType(GenericType::String),
24✔
768
                        )),
769
                        Vec::new(),
24✔
770
                    ),
771
                    Some(compile_time_value),
24✔
772
                ))
773
            }
774
            ast::Expression::Apply { callee, arguments } => {
16✔
775
                let callee_output = check_types(callee, environment_builder).await?;
32✔
776
                let argument_output = if arguments.len() == 1 {
16✔
777
                    // For N=1 we don't need an indirection.
778
                    check_types(&arguments[0], environment_builder).await?
8✔
779
                } else {
780
                    check_tree_construction_or_argument_list(&arguments[..], environment_builder)
8✔
781
                        .await?
8✔
782
                };
783
                let errors = callee_output
784
                    .0
785
                    .errors
786
                    .into_iter()
787
                    .chain(argument_output.0.errors)
788
                    .collect();
789
                match (callee_output.0.entry_point, argument_output.0.entry_point) {
790
                    (Some(callee_checked), Some(argument_checked)) => {
14✔
791
                        let (return_type, parameter_types) = match &callee_checked.type_.0 {
27✔
792
                            GenericType::Function {
793
                                return_type,
794
                                parameters,
795
                            } => (return_type.as_ref().clone(), parameters),
796
                            _ => {
797
                                return Ok((
1✔
798
                                    CompilerOutput::new(
1✔
799
                                        None,
1✔
800
                                        vec![crate::compilation::CompilerError::new(
1✔
801
                                            "Callee is not a function".to_string(),
1✔
802
                                            callee.source_location(),
1✔
803
                                        )],
804
                                    ),
805
                                    None,
1✔
806
                                ))
807
                            }
808
                        };
809
                        if parameter_types.len() != arguments.len() {
810
                            return Ok((
1✔
811
                                CompilerOutput::new(
1✔
812
                                    None,
1✔
813
                                    vec![CompilerError::new(
1✔
814
                                        format!(
1✔
815
                                            "Expected {} arguments, but got {}",
1✔
816
                                            parameter_types.len(),
1✔
817
                                            arguments.len()
1✔
818
                                        ),
819
                                        callee.source_location(),
1✔
820
                                    )],
821
                                ),
822
                                None,
1✔
823
                            ));
824
                        }
825
                        if parameter_types.len() == 1 {
12✔
826
                            if !convert_implicitly(&argument_checked.type_, &parameter_types[0]) {
4✔
827
                                return Ok((
1✔
828
                                    CompilerOutput::new(
1✔
829
                                        None,
1✔
830
                                        vec![CompilerError::new(
1✔
831
                                            format!(
1✔
832
                                                "Argument type '{:?}' is not convertible into parameter type '{:?}'",
1✔
833
                                                argument_checked.type_, parameter_types[0]
1✔
834
                                            ),
835
                                            arguments[0].source_location(),
1✔
836
                                        )],
837
                                    ),
838
                                    None,
1✔
839
                                ));
840
                            }
841
                        } else {
842
                            match argument_checked.type_.0 {
8✔
843
                                GenericType::TreeWithKnownChildTypes(argument_types) => {
8✔
844
                                    assert_eq!(
8✔
845
                                        argument_types.len(),
8✔
846
                                        parameter_types.len(),
8✔
NEW
847
                                        "Argument count mismatch"
×
848
                                    );
849
                                    for (index, (argument_type, parameter_type)) in argument_types
23✔
850
                                        .iter()
8✔
851
                                        .zip(parameter_types.iter())
8✔
852
                                        .enumerate()
8✔
853
                                    {
854
                                        if !convert_implicitly(argument_type, parameter_type) {
15✔
855
                                            return Ok((
1✔
856
                                                CompilerOutput::new(
1✔
857
                                                    None,
1✔
858
                                                    vec![CompilerError::new(
1✔
859
                                                        format!(
1✔
860
                                                            "Argument {} type '{:?}' is not convertible into parameter type '{:?}'",
1✔
861
                                                            index + 1, argument_type, parameter_type
1✔
862
                                                        ),
863
                                                        arguments[index].source_location(),
1✔
864
                                                    )],
865
                                                ),
866
                                                None,
1✔
867
                                            ));
868
                                        }
869
                                    }
870
                                }
NEW
871
                                _ => unreachable!()
×
872
                            }
873
                        }
874
                        Ok((
10✔
875
                            CompilerOutput {
10✔
876
                                entry_point: Some(TypedExpression::new(
10✔
877
                                    lambda::expressions::DeepExpression(
10✔
878
                                        lambda::expressions::Expression::Apply {
10✔
879
                                            callee: Arc::new(callee_checked.expression),
10✔
880
                                            argument: Arc::new(argument_checked.expression),
10✔
881
                                        },
882
                                    ),
883
                                    return_type,
10✔
884
                                )),
885
                                errors,
10✔
886
                            },
887
                            /*TODO: compile time function calls*/ None,
10✔
888
                        ))
889
                    }
890
                    (None, _) | (_, None) => Ok((CompilerOutput::new(None, errors), None)),
2✔
891
                }
892
            }
893
            ast::Expression::Lambda { parameters, body } => {
59✔
894
                check_lambda(&parameters[..], body, environment_builder)
59✔
895
                    .await
59✔
896
                    .map(|output| (output, /*TODO: compile time function calls*/ None))
118✔
897
            }
898
            ast::Expression::ConstructTree(arguments, _) => {
20✔
899
                check_tree_construction_or_argument_list(&arguments[..], environment_builder).await
20✔
900
            }
901
            ast::Expression::Braces(expression) => {
9✔
902
                check_types(expression, environment_builder).await
9✔
903
            }
904
            ast::Expression::Let {
905
                name,
18✔
906
                location,
18✔
907
                value,
18✔
908
                body,
18✔
909
            } => check_let(name, location, value, body, environment_builder)
18✔
910
                .await
18✔
911
                .map(|output| {
36✔
912
                    (
913
                        output, /*TODO: compile time let or a const keyword*/ None,
18✔
914
                    )
915
                }),
916
            ast::Expression::TypeOf(expression) => {
7✔
917
                check_type_of(expression, environment_builder).await
7✔
918
            }
919
            ast::Expression::Comment(_, expression, _) => {
3✔
920
                check_types(expression, environment_builder).await
3✔
921
            }
922
        }
923
    })
924
    .await
254✔
925
}
926

927
pub async fn check_types_with_default_globals(
47✔
928
    syntax_tree: &ast::Expression,
929
    default_global_namespace: NamespaceId,
930
) -> Result<CompilerOutput, StoreError> {
931
    let mut environment_builder = EnvironmentBuilder::new();
47✔
932
    environment_builder.define_constant(
47✔
933
        Name::new(default_global_namespace, "Type".to_string()),
47✔
934
        DeepType(GenericType::Type),
47✔
935
        type_to_deep_tree(&DeepType(GenericType::Type)),
47✔
936
    );
937
    environment_builder.define_constant(
47✔
938
        Name::new(default_global_namespace, "String".to_string()),
47✔
939
        DeepType(GenericType::Type),
47✔
940
        type_to_deep_tree(&DeepType(GenericType::String)),
47✔
941
    );
942
    let bool_type = DeepType(GenericType::Named(Name::new(
47✔
943
        default_global_namespace,
47✔
944
        "Bool".to_string(),
47✔
945
    )));
946
    environment_builder.define_constant(
47✔
947
        Name::new(default_global_namespace, "Bool".to_string()),
47✔
948
        DeepType(GenericType::Type),
47✔
949
        type_to_deep_tree(&bool_type),
47✔
950
    );
951
    environment_builder.define_constant(
47✔
952
        Name::new(default_global_namespace, "true".to_string()),
47✔
953
        bool_type.clone(),
47✔
954
        DeepTree::new(
47✔
955
            TreeBlob::try_from(bytes::Bytes::from_static(&[1u8]))
47✔
956
                .expect("one byte will always fit"),
47✔
957
            Vec::new(),
47✔
958
        ),
959
    );
960
    environment_builder.define_constant(
47✔
961
        Name::new(default_global_namespace, "false".to_string()),
47✔
962
        bool_type,
47✔
963
        DeepTree::new(
47✔
964
            TreeBlob::try_from(bytes::Bytes::from_static(&[0u8]))
47✔
965
                .expect("one byte will always fit"),
47✔
966
            Vec::new(),
47✔
967
        ),
968
    );
969
    let output = check_types(syntax_tree, &mut environment_builder).await;
94✔
970
    environment_builder.undefine_constant();
47✔
971
    environment_builder.undefine_constant();
47✔
972
    environment_builder.undefine_constant();
47✔
973
    environment_builder.undefine_constant();
47✔
974
    environment_builder.undefine_constant();
47✔
975
    assert!(environment_builder.is_empty());
976
    output.map(|output| /*TODO: return compile time values*/ output.0)
94✔
977
}
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