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 ¤t.kind {
90 ExprKind::Apply {
91 callee,
92 arg,
93 evidence,
94 instantiation,
95 } => {
96 frames.push(Frame::Apply {
97 ty: ¤t.ty,
98 arg,
99 evidence,
100 instantiation,
101 });
102 current = callee;
103 }
104 ExprKind::Select { base, field } => {
105 frames.push(Frame::Select {
106 ty: ¤t.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: ¤t.ty,
117 tag,
118 });
119 current = value;
120 }
121 ExprKind::BindHere { expr } => {
122 frames.push(Frame::BindHere { ty: ¤t.ty });
123 current = expr;
124 }
125 ExprKind::Thunk {
126 effect,
127 value,
128 expr,
129 } => {
130 frames.push(Frame::Thunk {
131 ty: ¤t.ty,
132 effect,
133 value,
134 });
135 current = expr;
136 }
137 ExprKind::LocalPushId { id, body } => {
138 frames.push(Frame::LocalPushId {
139 ty: ¤t.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: ¤t.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: ¤t.ty,
161 from,
162 to,
163 });
164 current = expr;
165 }
166 ExprKind::Pack { var, expr } => {
167 frames.push(Frame::Pack {
168 ty: ¤t.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}