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

TyRoXx / NonlocalityOS / 20205372567

14 Dec 2025 08:39AM UTC coverage: 80.802% (+0.02%) from 80.786%
20205372567

Pull #368

github

web-flow
Merge a53bf7714 into 61c2f2555
Pull Request #368: Prolly tree polishing

140 of 155 new or added lines in 13 files covered. (90.32%)

2 existing lines in 1 file now uncovered.

5438 of 6730 relevant lines covered (80.8%)

33584.14 hits per line

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

97.61
/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, DeepTreeChildren},
7
    storage::StoreError,
8
    tree::{ReferenceIndex, TreeBlob, TreeSerializationError},
9
};
10
use lambda::{
11
    expressions::{DeepExpression, Expression},
12
    name::{Name, NamespaceId},
13
};
14
use pretty_assertions::assert_eq;
15
use serde::{Deserialize, Serialize};
16
use std::{collections::BTreeMap, sync::Arc};
17

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

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

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

76
pub fn type_to_deep_tree(deep_type: &DeepType) -> Result<DeepTree, TreeSerializationError> {
206✔
77
    let (body, children) = to_reference_type(deep_type);
618✔
78
    let mut children_as_deep_trees = Vec::new();
412✔
79
    for child in &children {
211✔
80
        children_as_deep_trees.push(type_to_deep_tree(child)?);
20✔
81
    }
82
    let children = match DeepTreeChildren::try_from(children_as_deep_trees) {
412✔
83
        Some(success) => success,
412✔
NEW
84
        None => return Err(TreeSerializationError::TooManyChildren),
×
85
    };
86
    let body_serialized = postcard::to_allocvec(&body).unwrap(/*TODO*/);
824✔
87
    Ok(DeepTree::new(
412✔
88
        TreeBlob::try_from(bytes::Bytes::from(body_serialized)).unwrap(/*TODO*/),
824✔
89
        children,
206✔
90
    ))
91
}
92

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

146
pub fn type_from_deep_tree(deep_tree: &DeepTree) -> DeepType {
27✔
147
    let body: GenericType<ReferenceIndex> =
54✔
148
        postcard::from_bytes(deep_tree.blob().as_slice()).unwrap(/*TODO*/);
81✔
149
    let children: Vec<_> = deep_tree
81✔
150
        .children()
151
        .references()
152
        .iter()
153
        .map(type_from_deep_tree)
27✔
154
        .collect();
155
    from_reference_type(&body, &children)
81✔
156
}
157

158
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
159
pub struct TypedExpression {
160
    pub expression: DeepExpression,
161
    pub type_: DeepType,
162
}
163

164
impl TypedExpression {
165
    pub fn new(expression: DeepExpression, type_: DeepType) -> Self {
420✔
166
        Self { expression, type_ }
167
    }
168
}
169

170
async fn check_tree_construction_or_argument_list(
29✔
171
    arguments: &[ast::Expression],
172
    environment_builder: &mut EnvironmentBuilder,
173
) -> Result<(CompilerOutput, Option<DeepTree>), StoreError> {
174
    let mut errors = Vec::new();
58✔
175
    let mut checked_arguments = Vec::new();
58✔
176
    let mut argument_types = Vec::new();
58✔
177
    for argument in arguments {
75✔
178
        let output = check_types(argument, environment_builder).await?;
184✔
179
        errors.extend(output.0.errors);
138✔
180
        if let Some(checked) = output.0.entry_point {
138✔
181
            checked_arguments.push(Arc::new(checked.expression));
230✔
182
            argument_types.push(checked.type_);
92✔
183
        } else {
184
            return Ok((CompilerOutput::new(None, errors), None));
×
185
        }
186
    }
187
    Ok((
29✔
188
        CompilerOutput {
29✔
189
            entry_point: Some(TypedExpression::new(
58✔
190
                lambda::expressions::DeepExpression(
29✔
191
                    lambda::expressions::Expression::ConstructTree(checked_arguments),
29✔
192
                ),
193
                DeepType(GenericType::TreeWithKnownChildTypes(argument_types)),
29✔
194
            )),
195
            errors,
29✔
196
        },
197
        /*TODO: support compile time tree construction*/ None,
29✔
198
    ))
199
}
200

201
#[derive(Debug, Clone, Copy)]
202
enum ParameterIndex {
203
    SingleParameter,
204
    GetChild(u16),
205
}
206

207
impl ParameterIndex {
208
    pub fn create_deep_expression(&self) -> lambda::expressions::DeepExpression {
63✔
209
        match self {
63✔
210
            ParameterIndex::SingleParameter => {
211
                lambda::expressions::DeepExpression(lambda::expressions::Expression::make_argument())
45✔
212
            }
213
            ParameterIndex::GetChild(index) => lambda::expressions::DeepExpression(
214
                lambda::expressions::Expression::make_get_child(
36✔
215
                    Arc::new(lambda::expressions::DeepExpression(
36✔
216
                        lambda::expressions::Expression::make_argument(),
18✔
217
                    )),
218
                    *index,
18✔
219
                ),
220
            ),
221
        }
222
    }
223
}
224

225
#[derive(Debug, Clone)]
226
struct LocalVariable {
227
    parameter_index: ParameterIndex,
228
    type_: DeepType,
229
    compile_time_value: Option<DeepTree>,
230
}
231

232
impl LocalVariable {
233
    pub fn new(
359✔
234
        parameter_index: ParameterIndex,
235
        type_: DeepType,
236
        compile_time_value: Option<DeepTree>,
237
    ) -> Self {
238
        Self {
239
            parameter_index,
240
            type_,
241
            compile_time_value,
242
        }
243
    }
244
}
245

246
#[derive(Debug, Clone)]
247
struct LambdaScope {
248
    names: BTreeMap<Name, LocalVariable>,
249
    captures: BTreeMap<TypedExpression, u16>,
250
}
251

252
impl LambdaScope {
253
    pub fn new_lambda_scope(parameters: &[TypeCheckedLambdaParameter]) -> Self {
80✔
254
        let mut names = BTreeMap::new();
160✔
255
        if parameters.len() == 1 {
128✔
256
            names.insert(
144✔
257
                parameters[0].name.clone(),
144✔
258
                LocalVariable::new(
96✔
259
                    ParameterIndex::SingleParameter,
96✔
260
                    parameters[0].type_.clone(),
144✔
261
                    parameters[0].compile_time_value.clone(),
48✔
262
                ),
263
            );
264
        } else {
265
            for (index, parameter) in parameters.iter().enumerate() {
165✔
266
                let checked_index: u16 = index.try_into().expect("TODO handle too many parameters");
161✔
267
                names.insert(
69✔
268
                    parameter.name.clone(),
69✔
269
                    LocalVariable::new(
46✔
270
                        ParameterIndex::GetChild(checked_index),
46✔
271
                        parameter.type_.clone(),
69✔
272
                        parameter.compile_time_value.clone(),
23✔
273
                    ),
274
                );
275
            }
276
        }
277
        Self {
278
            names,
279
            captures: BTreeMap::new(),
80✔
280
        }
281
    }
282

283
    pub fn new_constant_scope(name: Name, type_: DeepType, compile_time_value: DeepTree) -> Self {
288✔
284
        let mut names = BTreeMap::new();
576✔
285
        names.insert(
576✔
286
            name,
288✔
287
            LocalVariable::new(
288✔
288
                ParameterIndex::SingleParameter,
288✔
289
                type_.clone(),
576✔
290
                Some(compile_time_value),
288✔
291
            ),
292
        );
293
        Self {
294
            names,
295
            captures: BTreeMap::new(),
288✔
296
        }
297
    }
298

299
    pub fn find_parameter_index(
281✔
300
        &self,
301
        parameter_name: &Name,
302
    ) -> Option<(ParameterIndex, DeepType, Option<DeepTree>)> {
303
        self.names.get(parameter_name).map(|variable| {
1,219✔
304
            (
305
                variable.parameter_index,
190✔
306
                variable.type_.clone(),
285✔
307
                variable.compile_time_value.clone(),
95✔
308
            )
309
        })
310
    }
311

312
    pub fn capture(&mut self, expression: TypedExpression) -> CompilerOutput {
30✔
313
        let type_ = expression.type_.clone();
90✔
314
        let index = match self.captures.get(&expression) {
90✔
315
            Some(&already_exists) => already_exists,
20✔
316
            None => {
317
                let new_index = self
40✔
318
                    .captures
20✔
319
                    .len()
320
                    .try_into()
321
                    .expect("TODO handle too many captures");
322
                self.captures.insert(expression, new_index);
80✔
323
                new_index
20✔
324
            }
325
        };
326
        CompilerOutput::new(
327
            Some(TypedExpression::new(
60✔
328
                lambda::expressions::DeepExpression(
30✔
329
                    lambda::expressions::Expression::make_get_child(
60✔
330
                        Arc::new(DeepExpression(
60✔
331
                            lambda::expressions::Expression::make_environment(),
30✔
332
                        )),
333
                        index,
30✔
334
                    ),
335
                ),
336
                type_,
30✔
337
            )),
338
            Vec::new(),
30✔
339
        )
340
    }
341

342
    pub fn leave(self) -> Vec<TypedExpression> {
368✔
343
        let mut as_vec: Vec<(TypedExpression, u16)> = self.captures.into_iter().collect();
1,840✔
344
        as_vec.sort_by_key(|(_, index)| *index);
736✔
345
        // sanity check:
346
        for (expected_index, (_, actual_index)) in as_vec.iter().enumerate() {
772✔
347
            assert_eq!(expected_index, *actual_index as usize);
348
        }
349
        as_vec
368✔
350
            .into_iter()
351
            .map(|(expression, _)| expression)
368✔
352
            .collect()
353
    }
354
}
355

356
#[derive(Debug, Clone)]
357
pub struct EnvironmentBuilder {
358
    lambda_layers: Vec<LambdaScope>,
359
}
360

361
impl Default for EnvironmentBuilder {
362
    fn default() -> Self {
×
363
        Self::new()
×
364
    }
365
}
366

367
impl EnvironmentBuilder {
368
    pub fn new() -> Self {
48✔
369
        Self {
370
            lambda_layers: Vec::new(),
48✔
371
        }
372
    }
373

374
    pub fn is_empty(&self) -> bool {
48✔
375
        self.lambda_layers.is_empty()
96✔
376
    }
377

378
    pub fn enter_lambda_body(&mut self, parameters: &[TypeCheckedLambdaParameter]) {
80✔
379
        self.lambda_layers
80✔
380
            .push(LambdaScope::new_lambda_scope(parameters));
240✔
381
    }
382

383
    pub fn leave_lambda_body(&mut self) -> Vec<TypedExpression> {
368✔
384
        let top_scope = self.lambda_layers.pop().unwrap();
1,472✔
385
        top_scope.leave()
736✔
386
    }
387

388
    pub fn define_constant(&mut self, name: Name, type_: DeepType, compile_time_value: DeepTree) {
288✔
389
        self.lambda_layers.push(LambdaScope::new_constant_scope(
864✔
390
            name,
288✔
391
            type_,
288✔
392
            compile_time_value,
288✔
393
        ));
394
    }
395

396
    pub fn undefine_constant(&mut self) {
288✔
397
        let captures = self.leave_lambda_body();
864✔
398
        if !captures.is_empty() {
288✔
399
            todo!()
400
        }
401
    }
402

403
    pub fn read(
102✔
404
        &mut self,
405
        identifier: &Name,
406
        location: &SourceLocation,
407
    ) -> (CompilerOutput, Option<DeepTree>) {
408
        Self::read_down(&mut self.lambda_layers, identifier, location)
408✔
409
    }
410

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

483
pub struct TypeCheckedLambdaParameter {
484
    pub name: Name,
485
    pub source_location: SourceLocation,
486
    pub type_: DeepType,
487
    pub compile_time_value: Option<DeepTree>,
488
}
489

490
impl TypeCheckedLambdaParameter {
491
    pub fn new(
19✔
492
        name: Name,
493
        source_location: SourceLocation,
494
        type_: DeepType,
495
        compile_time_value: Option<DeepTree>,
496
    ) -> Self {
497
        Self {
498
            name,
499
            source_location,
500
            type_,
501
            compile_time_value,
502
        }
503
    }
504
}
505

506
fn is_type(type_of_value: &DeepType) -> bool {
28✔
507
    match &type_of_value.0 {
28✔
508
        GenericType::Any => false,
1✔
509
        GenericType::String => false,
1✔
510
        GenericType::TreeWithKnownChildTypes(_items) => false,
2✔
511
        GenericType::Function {
512
            parameters: _,
513
            return_type: _,
514
        } => false,
1✔
515
        GenericType::Type => true,
23✔
516
        GenericType::Named(_name) => false,
2✔
517
        GenericType::Integer => false,
×
518
    }
519
}
520

521
struct WithCompilerErrors<T> {
522
    output: T,
523
    errors: Vec<CompilerError>,
524
}
525

526
impl<T> WithCompilerErrors<T> {
527
    pub fn new(output: T, errors: Vec<CompilerError>) -> Self {
61✔
528
        Self { output, errors }
529
    }
530
}
531

532
async fn check_lambda_parameters(
61✔
533
    parameters: &[LambdaParameter],
534
    environment_builder: &mut EnvironmentBuilder,
535
) -> Result<WithCompilerErrors<Vec<TypeCheckedLambdaParameter>>, StoreError> {
536
    let mut checked_parameters = Vec::new();
122✔
537
    let mut errors: Vec<CompilerError> = Vec::new();
183✔
538
    for parameter in parameters {
113✔
539
        let parameter_type: DeepType = match &parameter.type_annotation {
156✔
540
            Some(type_annotation) => {
29✔
541
                let checked_type = check_types(type_annotation, environment_builder).await?;
116✔
542
                errors.extend(checked_type.0.errors);
87✔
543
                if let Some(checked) = checked_type.0.entry_point {
57✔
544
                    if is_type(&checked.type_) {
56✔
545
                        match checked_type.1 {
23✔
546
                            Some(compile_time_value) => type_from_deep_tree(&compile_time_value),
66✔
547
                            None => {
548
                                errors.push(CompilerError::new(
3✔
549
                                    "Type annotation must be a compile time constant".to_string(),
2✔
550
                                    parameter.source_location,
1✔
551
                                ));
552
                                // Fallback to Any if the type is not valid.
553
                                DeepType(GenericType::Any)
1✔
554
                            }
555
                        }
556
                    } else {
557
                        errors.push(CompilerError::new(
15✔
558
                            "Type annotation must be a type".to_string(),
10✔
559
                            parameter.source_location,
5✔
560
                        ));
561
                        // Fallback to Any if the type is not valid.
562
                        DeepType(GenericType::Any)
5✔
563
                    }
564
                } else {
565
                    // Fallback to Any if the type is not valid.
566
                    DeepType(GenericType::Any)
1✔
567
                }
568
            }
569
            None => {
570
                // If no type annotation is provided, we assume the type is `Any`.
571
                DeepType(GenericType::Any)
23✔
572
            }
573
        };
574
        checked_parameters.push(TypeCheckedLambdaParameter {
156✔
575
            name: parameter.name.clone(),
156✔
576
            source_location: parameter.source_location,
104✔
577
            type_: parameter_type,
52✔
578
            compile_time_value: None, // TODO?
52✔
579
        });
580
    }
581
    Ok(WithCompilerErrors::new(checked_parameters, errors))
122✔
582
}
583

584
pub async fn check_lambda(
61✔
585
    parameters: &[LambdaParameter],
586
    body: &ast::Expression,
587
    environment_builder: &mut EnvironmentBuilder,
588
) -> Result<CompilerOutput, StoreError> {
589
    let checked_parameters = check_lambda_parameters(parameters, environment_builder).await?;
244✔
590
    let mut errors = checked_parameters.errors;
122✔
591
    environment_builder.enter_lambda_body(&checked_parameters.output[..]);
183✔
592
    let body_result = check_types(body, environment_builder).await;
244✔
593
    // TODO: use RAII or something?
594
    let environment = environment_builder.leave_lambda_body();
183✔
595
    let environment_expressions = environment
122✔
596
        .into_iter()
597
        .map(|typed_expression| Arc::new(typed_expression.expression))
85✔
598
        .collect();
599
    let body_output = body_result?;
122✔
600
    errors.extend(body_output.0.errors);
183✔
601
    match body_output.0.entry_point {
61✔
602
        Some(body_checked) => Ok(CompilerOutput {
60✔
603
            entry_point: Some(TypedExpression::new(
60✔
604
                lambda::expressions::DeepExpression(lambda::expressions::Expression::Lambda {
60✔
605
                    environment: Arc::new(DeepExpression(Expression::make_construct_tree(
180✔
606
                        environment_expressions,
120✔
607
                    ))),
608
                    body: Arc::new(body_checked.expression),
60✔
609
                }),
610
                DeepType(GenericType::Function {
611
                    parameters: checked_parameters
60✔
612
                        .output
60✔
613
                        .into_iter()
60✔
614
                        .map(|parameter| parameter.type_)
60✔
615
                        .collect(),
60✔
616
                    return_type: Box::new(body_checked.type_),
120✔
617
                }),
618
            )),
619
            errors,
60✔
620
        }),
621
        None => Ok(CompilerOutput::new(None, errors)),
2✔
622
    }
623
}
624

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

674
async fn check_type_of(
7✔
675
    expression: &ast::Expression,
676
    environment_builder: &mut EnvironmentBuilder,
677
) -> Result<(CompilerOutput, Option<DeepTree>), StoreError> {
678
    // Copy environment builder to prevent captures by unevaluated expressions.
679
    // TODO: make this more efficient (clone could be expensive).
680
    let mut unevaluated_context = environment_builder.clone();
21✔
681
    let checked_expression = check_types(expression, &mut unevaluated_context).await?;
28✔
682
    drop(unevaluated_context);
14✔
683

684
    let type_ = match checked_expression.0.entry_point {
13✔
685
        Some(success) => success.type_,
12✔
686
        None => {
687
            assert!(!checked_expression.0.errors.is_empty());
688
            return Ok((CompilerOutput::new(None, checked_expression.0.errors), None));
3✔
689
        }
690
    };
691
    let errors = checked_expression.0.errors;
12✔
692
    let type_as_tree = match type_to_deep_tree(&type_) {
12✔
693
        Ok(tree) => tree,
12✔
NEW
694
        Err(error) => {
×
NEW
695
            let mut all_errors = errors;
×
NEW
696
            all_errors.push(CompilerError::new(
×
NEW
697
                format!("Could not serialize type to tree: {}", error),
×
NEW
698
                expression.source_location(),
×
699
            ));
NEW
700
            return Ok((CompilerOutput::new(None, all_errors), None));
×
701
        }
702
    };
703
    Ok((
6✔
704
        CompilerOutput::new(
12✔
705
            Some(TypedExpression::new(
18✔
706
                lambda::expressions::DeepExpression(lambda::expressions::Expression::Literal(
12✔
707
                    type_as_tree.clone(),
12✔
708
                )),
709
                DeepType(GenericType::Type),
12✔
710
            )),
711
            errors,
6✔
712
        ),
713
        Some(type_as_tree),
6✔
714
    ))
715
}
716

717
pub fn convert_implicitly(from: &DeepType, to: &DeepType) -> bool {
47✔
718
    match (&from.0, &to.0) {
94✔
719
        (_, GenericType::Any) => true,
12✔
720
        (GenericType::String, GenericType::String) => true,
15✔
721
        (
722
            GenericType::TreeWithKnownChildTypes(from_children),
3✔
723
            GenericType::TreeWithKnownChildTypes(to_children),
3✔
724
        ) => {
725
            if from_children.len() != to_children.len() {
9✔
726
                return false;
1✔
727
            }
728
            from_children
4✔
729
                .iter()
730
                .zip(to_children.iter())
4✔
731
                .all(|(from_child, to_child)| convert_implicitly(from_child, to_child))
11✔
732
        }
733
        (
734
            GenericType::Function {
735
                parameters: from_params,
7✔
736
                return_type: from_return,
7✔
737
            },
738
            GenericType::Function {
739
                parameters: to_params,
7✔
740
                return_type: to_return,
7✔
741
            },
742
        ) => {
743
            if from_params.len() != to_params.len() {
21✔
744
                return false;
1✔
745
            }
746
            from_params
12✔
747
                .iter()
12✔
748
                .zip(to_params.iter())
12✔
749
                .all(|(from_param, to_param)| convert_implicitly(to_param, from_param))
21✔
750
                && convert_implicitly(from_return.as_ref(), to_return.as_ref())
15✔
751
        }
752
        (GenericType::Type, GenericType::Type) => true,
1✔
753
        (GenericType::Named(from_name), GenericType::Named(to_name)) => from_name == to_name,
6✔
754
        (GenericType::Integer, GenericType::Integer) => true,
1✔
755
        _ => false,
6✔
756
    }
757
}
758

759
async fn check_integer_literal(
3✔
760
    value: i64,
761
) -> Result<(CompilerOutput, Option<DeepTree>), StoreError> {
762
    let serialized = postcard::to_allocvec(&value).unwrap(/*TODO*/);
12✔
763
    let tree = DeepTree::new(
764
        TreeBlob::try_from(bytes::Bytes::from(serialized)).expect("an integer will always fit"),
15✔
765
        DeepTreeChildren::empty(),
3✔
766
    );
767
    Ok((
3✔
768
        CompilerOutput::new(
6✔
769
            Some(TypedExpression::new(
9✔
770
                DeepExpression(lambda::expressions::Expression::Literal(tree.clone())),
6✔
771
                DeepType(GenericType::Integer),
6✔
772
            )),
773
            vec![],
3✔
774
        ),
775
        Some(tree),
3✔
776
    ))
777
}
778

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

975
pub async fn check_types_with_default_globals(
48✔
976
    syntax_tree: &ast::Expression,
977
    default_global_namespace: NamespaceId,
978
) -> Result<CompilerOutput, StoreError> {
979
    let mut environment_builder = EnvironmentBuilder::new();
96✔
980
    environment_builder.define_constant(
96✔
981
        Name::new(default_global_namespace, "Type".to_string()),
192✔
982
        DeepType(GenericType::Type),
48✔
983
        type_to_deep_tree(&DeepType(GenericType::Type))
96✔
984
            .expect("Serializing this constant should always work"),
96✔
985
    );
986
    environment_builder.define_constant(
96✔
987
        Name::new(default_global_namespace, "String".to_string()),
192✔
988
        DeepType(GenericType::Type),
48✔
989
        type_to_deep_tree(&DeepType(GenericType::String))
96✔
990
            .expect("Serializing this constant should always work"),
96✔
991
    );
992
    let bool_type = DeepType(GenericType::Named(Name::new(
144✔
993
        default_global_namespace,
96✔
994
        "Bool".to_string(),
48✔
995
    )));
996
    environment_builder.define_constant(
96✔
997
        Name::new(default_global_namespace, "Bool".to_string()),
192✔
998
        DeepType(GenericType::Type),
48✔
999
        type_to_deep_tree(&bool_type).expect("Serializing this constant should always work"),
192✔
1000
    );
1001
    environment_builder.define_constant(
96✔
1002
        Name::new(default_global_namespace, "true".to_string()),
192✔
1003
        bool_type.clone(),
96✔
1004
        DeepTree::new(
48✔
1005
            TreeBlob::try_from(bytes::Bytes::from_static(&[1u8]))
144✔
1006
                .expect("one byte will always fit"),
96✔
1007
            DeepTreeChildren::empty(),
48✔
1008
        ),
1009
    );
1010
    environment_builder.define_constant(
96✔
1011
        Name::new(default_global_namespace, "false".to_string()),
192✔
1012
        bool_type,
48✔
1013
        DeepTree::new(
48✔
1014
            TreeBlob::try_from(bytes::Bytes::from_static(&[0u8]))
144✔
1015
                .expect("one byte will always fit"),
96✔
1016
            DeepTreeChildren::empty(),
48✔
1017
        ),
1018
    );
1019
    environment_builder.define_constant(
96✔
1020
        Name::new(default_global_namespace, "Int".to_string()),
192✔
1021
        DeepType(GenericType::Type),
48✔
1022
        type_to_deep_tree(&DeepType(GenericType::Integer))
96✔
1023
            .expect("Serializing this constant should always work"),
96✔
1024
    );
1025
    let output = check_types(syntax_tree, &mut environment_builder).await;
192✔
1026
    environment_builder.undefine_constant();
96✔
1027
    environment_builder.undefine_constant();
96✔
1028
    environment_builder.undefine_constant();
96✔
1029
    environment_builder.undefine_constant();
96✔
1030
    environment_builder.undefine_constant();
96✔
1031
    environment_builder.undefine_constant();
96✔
1032
    assert!(environment_builder.is_empty());
1033
    output.map(|output| /*TODO: return compile time values*/ output.0)
96✔
1034
}
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