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

TyRoXx / NonlocalityOS / 15650805231

14 Jun 2025 09:49AM UTC coverage: 76.604% (+0.3%) from 76.336%
15650805231

Pull #293

github

web-flow
Merge 7c8143176 into b9c75376a
Pull Request #293: GH-292: Integers

77 of 95 new or added lines in 8 files covered. (81.05%)

6 existing lines in 2 files now uncovered.

4142 of 5407 relevant lines covered (76.6%)

2239.66 hits per line

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

98.59
/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
    Integer,
32
}
33

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

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

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

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

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

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

154
impl TypedExpression {
155
    pub fn new(expression: DeepExpression, type_: DeepType) -> Self {
420✔
156
        Self { expression, type_ }
157
    }
158
}
159

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

191
#[derive(Debug, Clone, Copy)]
192
enum ParameterIndex {
193
    SingleParameter,
194
    GetChild(u16),
195
}
196

197
impl ParameterIndex {
198
    pub fn create_deep_expression(&self) -> lambda::expressions::DeepExpression {
63✔
199
        match self {
63✔
200
            ParameterIndex::SingleParameter => {
201
                lambda::expressions::DeepExpression(lambda::expressions::Expression::make_argument())
45✔
202
            }
203
            ParameterIndex::GetChild(index) => lambda::expressions::DeepExpression(
204
                lambda::expressions::Expression::make_get_child(
18✔
205
                    Arc::new(lambda::expressions::DeepExpression(
18✔
206
                        lambda::expressions::Expression::make_argument(),
18✔
207
                    )),
208
                    *index,
18✔
209
                ),
210
            ),
211
        }
212
    }
213
}
214

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

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

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

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

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

289
    pub fn find_parameter_index(
281✔
290
        &self,
291
        parameter_name: &Name,
292
    ) -> Option<(ParameterIndex, DeepType, Option<DeepTree>)> {
293
        self.names.get(parameter_name).map(|variable| {
376✔
294
            (
295
                variable.parameter_index,
95✔
296
                variable.type_.clone(),
95✔
297
                variable.compile_time_value.clone(),
95✔
298
            )
299
        })
300
    }
301

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

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

346
#[derive(Debug, Clone)]
347
pub struct EnvironmentBuilder {
348
    lambda_layers: Vec<LambdaScope>,
349
}
350

351
impl Default for EnvironmentBuilder {
352
    fn default() -> Self {
×
353
        Self::new()
×
354
    }
355
}
356

357
impl EnvironmentBuilder {
358
    pub fn new() -> Self {
48✔
359
        Self {
360
            lambda_layers: Vec::new(),
48✔
361
        }
362
    }
363

364
    pub fn is_empty(&self) -> bool {
48✔
365
        self.lambda_layers.is_empty()
48✔
366
    }
367

368
    pub fn enter_lambda_body(&mut self, parameters: &[TypeCheckedLambdaParameter]) {
80✔
369
        self.lambda_layers
80✔
370
            .push(LambdaScope::new_lambda_scope(parameters));
80✔
371
    }
372

373
    pub fn leave_lambda_body(&mut self) -> Vec<TypedExpression> {
368✔
374
        let top_scope = self.lambda_layers.pop().unwrap();
368✔
375
        top_scope.leave()
368✔
376
    }
377

378
    pub fn define_constant(&mut self, name: Name, type_: DeepType, compile_time_value: DeepTree) {
288✔
379
        self.lambda_layers.push(LambdaScope::new_constant_scope(
288✔
380
            name,
288✔
381
            type_,
288✔
382
            compile_time_value,
288✔
383
        ));
384
    }
385

386
    pub fn undefine_constant(&mut self) {
288✔
387
        let captures = self.leave_lambda_body();
288✔
388
        if !captures.is_empty() {
288✔
389
            todo!()
390
        }
391
    }
392

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

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

473
pub struct TypeCheckedLambdaParameter {
474
    pub name: Name,
475
    pub source_location: SourceLocation,
476
    pub type_: DeepType,
477
    pub compile_time_value: Option<DeepTree>,
478
}
479

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

496
fn is_type(type_of_value: &DeepType) -> bool {
28✔
497
    match &type_of_value.0 {
28✔
498
        GenericType::Any => false,
1✔
499
        GenericType::String => false,
1✔
500
        GenericType::TreeWithKnownChildTypes(_items) => false,
1✔
501
        GenericType::Function {
502
            parameters: _,
503
            return_type: _,
504
        } => false,
1✔
505
        GenericType::Type => true,
23✔
506
        GenericType::Named(_name) => false,
1✔
NEW
507
        GenericType::Integer => false,
×
508
    }
509
}
510

511
struct WithCompilerErrors<T> {
512
    output: T,
513
    errors: Vec<CompilerError>,
514
}
515

516
impl<T> WithCompilerErrors<T> {
517
    pub fn new(output: T, errors: Vec<CompilerError>) -> Self {
61✔
518
        Self { output, errors }
519
    }
520
}
521

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

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

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

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

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

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

739
async fn check_integer_literal(
3✔
740
    value: i64,
741
) -> Result<(CompilerOutput, Option<DeepTree>), StoreError> {
742
    let serialized = postcard::to_allocvec(&value).unwrap(/*TODO*/);
3✔
743
    let tree = DeepTree::new(
744
        TreeBlob::try_from(bytes::Bytes::from(serialized)).expect("an integer will always fit"),
3✔
745
        vec![],
3✔
746
    );
747
    Ok((
3✔
748
        CompilerOutput::new(
3✔
749
            Some(TypedExpression::new(
3✔
750
                DeepExpression(lambda::expressions::Expression::Literal(tree.clone())),
3✔
751
                DeepType(GenericType::Integer),
3✔
752
            )),
753
            vec![],
3✔
754
        ),
755
        Some(tree),
3✔
756
    ))
757
}
758

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

955
pub async fn check_types_with_default_globals(
48✔
956
    syntax_tree: &ast::Expression,
957
    default_global_namespace: NamespaceId,
958
) -> Result<CompilerOutput, StoreError> {
959
    let mut environment_builder = EnvironmentBuilder::new();
48✔
960
    environment_builder.define_constant(
48✔
961
        Name::new(default_global_namespace, "Type".to_string()),
48✔
962
        DeepType(GenericType::Type),
48✔
963
        type_to_deep_tree(&DeepType(GenericType::Type)),
48✔
964
    );
965
    environment_builder.define_constant(
48✔
966
        Name::new(default_global_namespace, "String".to_string()),
48✔
967
        DeepType(GenericType::Type),
48✔
968
        type_to_deep_tree(&DeepType(GenericType::String)),
48✔
969
    );
970
    let bool_type = DeepType(GenericType::Named(Name::new(
48✔
971
        default_global_namespace,
48✔
972
        "Bool".to_string(),
48✔
973
    )));
974
    environment_builder.define_constant(
48✔
975
        Name::new(default_global_namespace, "Bool".to_string()),
48✔
976
        DeepType(GenericType::Type),
48✔
977
        type_to_deep_tree(&bool_type),
48✔
978
    );
979
    environment_builder.define_constant(
48✔
980
        Name::new(default_global_namespace, "true".to_string()),
48✔
981
        bool_type.clone(),
48✔
982
        DeepTree::new(
48✔
983
            TreeBlob::try_from(bytes::Bytes::from_static(&[1u8]))
48✔
984
                .expect("one byte will always fit"),
48✔
985
            Vec::new(),
48✔
986
        ),
987
    );
988
    environment_builder.define_constant(
48✔
989
        Name::new(default_global_namespace, "false".to_string()),
48✔
990
        bool_type,
48✔
991
        DeepTree::new(
48✔
992
            TreeBlob::try_from(bytes::Bytes::from_static(&[0u8]))
48✔
993
                .expect("one byte will always fit"),
48✔
994
            Vec::new(),
48✔
995
        ),
996
    );
997
    environment_builder.define_constant(
48✔
998
        Name::new(default_global_namespace, "Int".to_string()),
48✔
999
        DeepType(GenericType::Type),
48✔
1000
        type_to_deep_tree(&DeepType(GenericType::Integer)),
48✔
1001
    );
1002
    let output = check_types(syntax_tree, &mut environment_builder).await;
96✔
1003
    environment_builder.undefine_constant();
48✔
1004
    environment_builder.undefine_constant();
48✔
1005
    environment_builder.undefine_constant();
48✔
1006
    environment_builder.undefine_constant();
48✔
1007
    environment_builder.undefine_constant();
48✔
1008
    environment_builder.undefine_constant();
48✔
1009
    assert!(environment_builder.is_empty());
1010
    output.map(|output| /*TODO: return compile time values*/ output.0)
96✔
1011
}
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