Skip to main content

yulang_runtime/
ir.rs

1use yulang_typed_ir as typed_ir;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct Module {
5    pub path: typed_ir::Path,
6    pub bindings: Vec<Binding>,
7    pub root_exprs: Vec<Expr>,
8    pub roots: Vec<Root>,
9    pub role_impls: Vec<typed_ir::RoleImplGraphNode>,
10}
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct Binding {
14    pub name: typed_ir::Path,
15    pub type_params: Vec<typed_ir::TypeVar>,
16    pub scheme: typed_ir::Scheme,
17    pub body: Expr,
18}
19
20#[derive(Debug, PartialEq, Eq)]
21pub struct Expr {
22    pub ty: Type,
23    pub kind: ExprKind,
24}
25
26impl Expr {
27    pub fn typed(kind: ExprKind, ty: impl Into<Type>) -> Self {
28        Self {
29            ty: ty.into(),
30            kind,
31        }
32    }
33}
34
35impl Clone for Expr {
36    fn clone(&self) -> Self {
37        clone_expr_without_apply_spine_recursion(self)
38    }
39}
40
41fn clone_expr_without_apply_spine_recursion(expr: &Expr) -> Expr {
42    enum Frame<'a> {
43        Apply {
44            ty: &'a Type,
45            arg: &'a Expr,
46            evidence: &'a Option<typed_ir::ApplyEvidence>,
47            instantiation: &'a Option<TypeInstantiation>,
48        },
49        Select {
50            ty: &'a Type,
51            field: &'a typed_ir::Name,
52        },
53        Variant {
54            ty: &'a Type,
55            tag: &'a typed_ir::Name,
56        },
57        BindHere {
58            ty: &'a Type,
59        },
60        Thunk {
61            ty: &'a Type,
62            effect: &'a typed_ir::Type,
63            value: &'a Type,
64        },
65        LocalPushId {
66            ty: &'a Type,
67            id: EffectIdVar,
68        },
69        AddId {
70            ty: &'a Type,
71            id: EffectIdRef,
72            allowed: &'a typed_ir::Type,
73            active: bool,
74        },
75        Coerce {
76            ty: &'a Type,
77            from: &'a typed_ir::Type,
78            to: &'a typed_ir::Type,
79        },
80        Pack {
81            ty: &'a Type,
82            var: &'a typed_ir::TypeVar,
83        },
84    }
85
86    let mut current = expr;
87    let mut frames = Vec::new();
88    loop {
89        match &current.kind {
90            ExprKind::Apply {
91                callee,
92                arg,
93                evidence,
94                instantiation,
95            } => {
96                frames.push(Frame::Apply {
97                    ty: &current.ty,
98                    arg,
99                    evidence,
100                    instantiation,
101                });
102                current = callee;
103            }
104            ExprKind::Select { base, field } => {
105                frames.push(Frame::Select {
106                    ty: &current.ty,
107                    field,
108                });
109                current = base;
110            }
111            ExprKind::Variant {
112                tag,
113                value: Some(value),
114            } => {
115                frames.push(Frame::Variant {
116                    ty: &current.ty,
117                    tag,
118                });
119                current = value;
120            }
121            ExprKind::BindHere { expr } => {
122                frames.push(Frame::BindHere { ty: &current.ty });
123                current = expr;
124            }
125            ExprKind::Thunk {
126                effect,
127                value,
128                expr,
129            } => {
130                frames.push(Frame::Thunk {
131                    ty: &current.ty,
132                    effect,
133                    value,
134                });
135                current = expr;
136            }
137            ExprKind::LocalPushId { id, body } => {
138                frames.push(Frame::LocalPushId {
139                    ty: &current.ty,
140                    id: *id,
141                });
142                current = body;
143            }
144            ExprKind::AddId {
145                id,
146                allowed,
147                active,
148                thunk,
149            } => {
150                frames.push(Frame::AddId {
151                    ty: &current.ty,
152                    id: *id,
153                    allowed,
154                    active: *active,
155                });
156                current = thunk;
157            }
158            ExprKind::Coerce { from, to, expr } => {
159                frames.push(Frame::Coerce {
160                    ty: &current.ty,
161                    from,
162                    to,
163                });
164                current = expr;
165            }
166            ExprKind::Pack { var, expr } => {
167                frames.push(Frame::Pack {
168                    ty: &current.ty,
169                    var,
170                });
171                current = expr;
172            }
173            _ => break,
174        }
175    }
176
177    let mut cloned = Expr {
178        ty: current.ty.clone(),
179        kind: current.kind.clone(),
180    };
181    for frame in frames.into_iter().rev() {
182        cloned = match frame {
183            Frame::Apply {
184                ty,
185                arg,
186                evidence,
187                instantiation,
188            } => Expr {
189                ty: ty.clone(),
190                kind: ExprKind::Apply {
191                    callee: Box::new(cloned),
192                    arg: Box::new(arg.clone()),
193                    evidence: evidence.clone(),
194                    instantiation: instantiation.clone(),
195                },
196            },
197            Frame::Select { ty, field } => Expr {
198                ty: ty.clone(),
199                kind: ExprKind::Select {
200                    base: Box::new(cloned),
201                    field: field.clone(),
202                },
203            },
204            Frame::Variant { ty, tag } => Expr {
205                ty: ty.clone(),
206                kind: ExprKind::Variant {
207                    tag: tag.clone(),
208                    value: Some(Box::new(cloned)),
209                },
210            },
211            Frame::BindHere { ty } => Expr {
212                ty: ty.clone(),
213                kind: ExprKind::BindHere {
214                    expr: Box::new(cloned),
215                },
216            },
217            Frame::Thunk { ty, effect, value } => Expr {
218                ty: ty.clone(),
219                kind: ExprKind::Thunk {
220                    effect: effect.clone(),
221                    value: value.clone(),
222                    expr: Box::new(cloned),
223                },
224            },
225            Frame::LocalPushId { ty, id } => Expr {
226                ty: ty.clone(),
227                kind: ExprKind::LocalPushId {
228                    id,
229                    body: Box::new(cloned),
230                },
231            },
232            Frame::AddId {
233                ty,
234                id,
235                allowed,
236                active,
237            } => Expr {
238                ty: ty.clone(),
239                kind: ExprKind::AddId {
240                    id,
241                    allowed: allowed.clone(),
242                    active,
243                    thunk: Box::new(cloned),
244                },
245            },
246            Frame::Coerce { ty, from, to } => Expr {
247                ty: ty.clone(),
248                kind: ExprKind::Coerce {
249                    from: from.clone(),
250                    to: to.clone(),
251                    expr: Box::new(cloned),
252                },
253            },
254            Frame::Pack { ty, var } => Expr {
255                ty: ty.clone(),
256                kind: ExprKind::Pack {
257                    var: var.clone(),
258                    expr: Box::new(cloned),
259                },
260            },
261        };
262    }
263    cloned
264}
265
266#[derive(Debug, PartialEq, Eq, Hash)]
267pub enum Type {
268    Unknown,
269    Core(typed_ir::Type),
270    Fun {
271        param: Box<Type>,
272        ret: Box<Type>,
273    },
274    Thunk {
275        effect: typed_ir::Type,
276        value: Box<Type>,
277    },
278}
279
280impl Type {
281    pub fn unknown() -> Self {
282        Self::Unknown
283    }
284
285    pub fn core(ty: typed_ir::Type) -> Self {
286        Self::Core(ty)
287    }
288
289    pub fn fun(param: Type, ret: Type) -> Self {
290        Self::Fun {
291            param: Box::new(param),
292            ret: Box::new(ret),
293        }
294    }
295
296    pub fn thunk(effect: typed_ir::Type, value: Type) -> Self {
297        Self::Thunk {
298            effect,
299            value: Box::new(value),
300        }
301    }
302
303    pub fn as_core(&self) -> Option<&typed_ir::Type> {
304        match self {
305            Type::Core(ty) => Some(ty),
306            Type::Unknown | Type::Fun { .. } | Type::Thunk { .. } => None,
307        }
308    }
309}
310
311impl Clone for Type {
312    fn clone(&self) -> Self {
313        clone_type_without_fun_spine_recursion(self)
314    }
315}
316
317fn clone_type_without_fun_spine_recursion(ty: &Type) -> Type {
318    enum Frame<'a> {
319        Fun { param: &'a Type },
320        Thunk { effect: &'a typed_ir::Type },
321    }
322
323    let mut current = ty;
324    let mut frames = Vec::new();
325    loop {
326        match current {
327            Type::Fun { param, ret } => {
328                frames.push(Frame::Fun { param });
329                current = ret;
330            }
331            Type::Thunk { effect, value } => {
332                frames.push(Frame::Thunk { effect });
333                current = value;
334            }
335            Type::Unknown => {
336                let mut cloned = Type::Unknown;
337                for frame in frames.into_iter().rev() {
338                    cloned = match frame {
339                        Frame::Fun { param } => Type::fun(param.clone(), cloned),
340                        Frame::Thunk { effect } => Type::thunk(effect.clone(), cloned),
341                    };
342                }
343                return cloned;
344            }
345            Type::Core(core) => {
346                let mut cloned = Type::Core(core.clone());
347                for frame in frames.into_iter().rev() {
348                    cloned = match frame {
349                        Frame::Fun { param } => Type::fun(param.clone(), cloned),
350                        Frame::Thunk { effect } => Type::thunk(effect.clone(), cloned),
351                    };
352                }
353                return cloned;
354            }
355        }
356    }
357}
358
359impl From<typed_ir::Type> for Type {
360    fn from(ty: typed_ir::Type) -> Self {
361        Type::Core(ty)
362    }
363}
364
365#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
366pub struct EffectIdVar(pub usize);
367
368#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
369pub enum EffectIdRef {
370    Var(EffectIdVar),
371    Peek,
372}
373
374#[derive(Debug, Clone, PartialEq, Eq)]
375pub enum ExprKind {
376    Var(typed_ir::Path),
377    EffectOp(typed_ir::Path),
378    PrimitiveOp(typed_ir::PrimitiveOp),
379    Lit(typed_ir::Lit),
380    Lambda {
381        param: typed_ir::Name,
382        param_effect_annotation: Option<typed_ir::ParamEffectAnnotation>,
383        param_function_allowed_effects: Option<typed_ir::FunctionSigAllowedEffects>,
384        body: Box<Expr>,
385    },
386    Apply {
387        callee: Box<Expr>,
388        arg: Box<Expr>,
389        evidence: Option<typed_ir::ApplyEvidence>,
390        instantiation: Option<TypeInstantiation>,
391    },
392    If {
393        cond: Box<Expr>,
394        then_branch: Box<Expr>,
395        else_branch: Box<Expr>,
396        evidence: Option<JoinEvidence>,
397    },
398    Tuple(Vec<Expr>),
399    Record {
400        fields: Vec<RecordExprField>,
401        spread: Option<RecordSpreadExpr>,
402    },
403    Variant {
404        tag: typed_ir::Name,
405        value: Option<Box<Expr>>,
406    },
407    Select {
408        base: Box<Expr>,
409        field: typed_ir::Name,
410    },
411    Match {
412        scrutinee: Box<Expr>,
413        arms: Vec<MatchArm>,
414        evidence: JoinEvidence,
415    },
416    Block {
417        stmts: Vec<Stmt>,
418        tail: Option<Box<Expr>>,
419    },
420    Handle {
421        body: Box<Expr>,
422        arms: Vec<HandleArm>,
423        evidence: JoinEvidence,
424        handler: HandleEffect,
425    },
426    BindHere {
427        expr: Box<Expr>,
428    },
429    Thunk {
430        effect: typed_ir::Type,
431        value: Type,
432        expr: Box<Expr>,
433    },
434    LocalPushId {
435        id: EffectIdVar,
436        body: Box<Expr>,
437    },
438    PeekId,
439    FindId {
440        id: EffectIdRef,
441    },
442    AddId {
443        id: EffectIdRef,
444        allowed: typed_ir::Type,
445        active: bool,
446        thunk: Box<Expr>,
447    },
448    Coerce {
449        from: typed_ir::Type,
450        to: typed_ir::Type,
451        expr: Box<Expr>,
452    },
453    Pack {
454        var: typed_ir::TypeVar,
455        expr: Box<Expr>,
456    },
457}
458
459#[derive(Debug, Clone, PartialEq, Eq)]
460pub struct JoinEvidence {
461    pub result: typed_ir::Type,
462}
463
464#[derive(Debug, Clone, PartialEq, Eq)]
465pub struct TypeInstantiation {
466    pub target: typed_ir::Path,
467    pub args: Vec<TypeSubstitution>,
468}
469
470#[derive(Debug, Clone, PartialEq, Eq)]
471pub struct TypeSubstitution {
472    pub var: typed_ir::TypeVar,
473    pub ty: typed_ir::Type,
474}
475
476#[derive(Debug, Clone, PartialEq, Eq)]
477pub enum Stmt {
478    Let { pattern: Pattern, value: Expr },
479    Expr(Expr),
480    Module { def: typed_ir::Path, body: Expr },
481}
482
483#[derive(Debug, Clone, PartialEq, Eq)]
484pub enum Pattern {
485    Wildcard {
486        ty: Type,
487    },
488    Bind {
489        name: typed_ir::Name,
490        ty: Type,
491    },
492    Lit {
493        lit: typed_ir::Lit,
494        ty: Type,
495    },
496    Tuple {
497        items: Vec<Pattern>,
498        ty: Type,
499    },
500    List {
501        prefix: Vec<Pattern>,
502        spread: Option<Box<Pattern>>,
503        suffix: Vec<Pattern>,
504        ty: Type,
505    },
506    Record {
507        fields: Vec<RecordPatternField>,
508        spread: Option<RecordSpreadPattern>,
509        ty: Type,
510    },
511    Variant {
512        tag: typed_ir::Name,
513        value: Option<Box<Pattern>>,
514        ty: Type,
515    },
516    Or {
517        left: Box<Pattern>,
518        right: Box<Pattern>,
519        ty: Type,
520    },
521    As {
522        pattern: Box<Pattern>,
523        name: typed_ir::Name,
524        ty: Type,
525    },
526}
527
528#[derive(Debug, Clone, PartialEq, Eq)]
529pub struct RecordExprField {
530    pub name: typed_ir::Name,
531    pub value: Expr,
532}
533
534#[derive(Debug, Clone, PartialEq, Eq)]
535pub enum RecordSpreadExpr {
536    Head(Box<Expr>),
537    Tail(Box<Expr>),
538}
539
540#[derive(Debug, Clone, PartialEq, Eq)]
541pub struct RecordPatternField {
542    pub name: typed_ir::Name,
543    pub pattern: Pattern,
544    pub default: Option<Expr>,
545}
546
547#[derive(Debug, Clone, PartialEq, Eq)]
548pub enum RecordSpreadPattern {
549    Head(Box<Pattern>),
550    Tail(Box<Pattern>),
551}
552
553#[derive(Debug, Clone, PartialEq, Eq)]
554pub struct MatchArm {
555    pub pattern: Pattern,
556    pub guard: Option<Expr>,
557    pub body: Expr,
558}
559
560#[derive(Debug, Clone, PartialEq, Eq)]
561pub struct HandleArm {
562    pub effect: typed_ir::Path,
563    pub payload: Pattern,
564    pub resume: Option<ResumeBinding>,
565    pub guard: Option<Expr>,
566    pub body: Expr,
567}
568
569#[derive(Debug, Clone, PartialEq, Eq)]
570pub struct ResumeBinding {
571    pub name: typed_ir::Name,
572    pub ty: Type,
573}
574
575#[derive(Debug, Clone, PartialEq, Eq)]
576pub struct HandleEffect {
577    pub consumes: Vec<typed_ir::Path>,
578    pub residual_before: Option<typed_ir::Type>,
579    pub residual_after: Option<typed_ir::Type>,
580}
581
582#[derive(Debug, Clone, PartialEq, Eq)]
583pub enum Root {
584    Binding(typed_ir::Path),
585    Expr(usize),
586}
587
588#[cfg(test)]
589mod tests {
590    use super::*;
591
592    #[test]
593    fn clones_deep_runtime_adapter_spine_without_recursing() {
594        let mut expr = Expr::typed(ExprKind::Lit(typed_ir::Lit::Unit), Type::unknown());
595        for index in 0..20_000 {
596            expr = match index % 3 {
597                0 => Expr::typed(
598                    ExprKind::BindHere {
599                        expr: Box::new(expr),
600                    },
601                    Type::unknown(),
602                ),
603                1 => Expr::typed(
604                    ExprKind::Thunk {
605                        effect: typed_ir::Type::Unknown,
606                        value: Type::unknown(),
607                        expr: Box::new(expr),
608                    },
609                    Type::unknown(),
610                ),
611                _ => Expr::typed(
612                    ExprKind::Coerce {
613                        from: typed_ir::Type::Unknown,
614                        to: typed_ir::Type::Any,
615                        expr: Box::new(expr),
616                    },
617                    Type::unknown(),
618                ),
619            };
620        }
621
622        let cloned = expr.clone();
623
624        assert_eq!(cloned.ty, Type::unknown());
625        std::mem::forget(expr);
626        std::mem::forget(cloned);
627    }
628}