1use std::collections::{BTreeMap, BTreeSet, HashMap};
10
11use yulang_typed_ir as typed_ir;
12
13use crate::diagnostic::{
14 RuntimeCalleeLabel, RuntimeError, RuntimeResult, TypeMismatchContext, TypeMismatchPhase,
15 TypeSource,
16};
17use crate::invariant::{RuntimeStage, check_runtime_invariants};
18use crate::ir::{
19 Binding, EffectIdRef, EffectIdVar, Expr, ExprKind, HandleArm, HandleEffect, JoinEvidence,
20 MatchArm, Module, Pattern, RecordExprField, RecordPatternField, RecordSpreadExpr,
21 RecordSpreadPattern, ResumeBinding, Root, Stmt, Type as RuntimeType, TypeInstantiation,
22 TypeSubstitution,
23};
24use crate::types::{
25 BoundsChoice, TypeChoice, choose_bounds_type, choose_core_type, choose_optional_core_type,
26 collect_hir_type_vars, collect_type_vars, contains_non_runtime_effect_type,
27 contains_non_runtime_type, core_type_has_vars, core_type_is_imprecise_runtime_slot,
28 core_types_compatible, diagnostic_core_type, effect_compatible, effect_is_empty, effect_path,
29 effect_paths, effect_paths_match, effect_row_from_items, hir_type_imprecision_count,
30 hir_type_is_hole, infer_type_substitutions, infer_type_substitutions_prefer_non_never,
31 infer_type_substitutions_prefer_non_never_skip_empty_effects, is_qualified_runtime_path,
32 needs_runtime_coercion, project_runtime_bounds, project_runtime_effect,
33 project_runtime_hir_type_with_vars, project_runtime_type_with_vars, runtime_core_type,
34 runtime_type_contains_unknown, runtime_type_is_imprecise_runtime_slot, should_thunk_effect,
35 strict_core_type as core_type, substitute_bounds, substitute_hir_type, substitute_type,
36 thunk_effect, type_compatible, wildcard_effect_type,
37};
38use crate::validate::validate_module;
39
40mod core_shape;
41mod diagnostics;
42mod effects;
43mod evidence;
44mod expr;
45mod function;
46mod hints;
47mod lowerer;
48mod patterns;
49mod primitive_types;
50mod substitutions;
51mod thunk;
52
53pub use core_shape::CoreShapeProfile;
54use core_shape::*;
55use diagnostics::*;
56use effects::*;
57use evidence::*;
58use expr::*;
59use function::*;
60use hints::*;
61use patterns::*;
62use primitive_types::*;
63use substitutions::*;
64use thunk::*;
65
66pub struct RuntimeLowerOutput {
67 pub module: Module,
68 pub profile: RuntimeLowerProfile,
69}
70
71#[derive(Debug, Default, Clone, PartialEq, Eq)]
72pub struct RuntimeLowerProfile {
73 pub core_shape: CoreShapeProfile,
74 pub expected_arg_evidence: ExpectedArgEvidenceProfile,
75 pub expected_adapter_evidence: ExpectedAdapterEvidenceProfile,
76 pub derived_expected_evidence: DerivedExpectedEvidenceProfile,
77 pub runtime_adapters: RuntimeAdapterProfile,
78}
79
80#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
81pub struct ExpectedArgEvidenceProfile {
82 pub present: usize,
83 pub converted: usize,
84 pub usable_by_table: usize,
85 pub usable_by_bounds: usize,
86 pub used_as_arg_type_hint: usize,
87 pub used_as_lowering_expected: usize,
88 pub ignored_no_expected_arg: usize,
89 pub ignored_not_convertible: usize,
90 pub ignored_table_open: usize,
91 pub ignored_table_uninformative: usize,
92 pub ignored_table_not_runtime_usable: usize,
93 pub ignored_bounds_unusable: usize,
94 pub ignored_unusable: usize,
95 pub ignored_no_push: usize,
96}
97
98#[derive(Debug, Default, Clone, PartialEq, Eq)]
99pub struct RuntimeAdapterProfile {
100 pub collect_events: bool,
101 pub value_to_thunk: usize,
102 pub thunk_to_value: usize,
103 pub bind_here: usize,
104 pub apply_evidence_value_to_thunk: usize,
105 pub apply_evidence_thunk_to_value: usize,
106 pub apply_evidence_bind_here: usize,
107 pub apply_evidence_adapter_with_evidence: usize,
108 pub apply_evidence_adapter_with_source_edge: usize,
109 pub apply_evidence_adapter_without_evidence: usize,
110 pub apply_evidence_value_to_thunk_with_source_edge: usize,
111 pub apply_evidence_thunk_to_value_with_source_edge: usize,
112 pub apply_evidence_bind_here_with_source_edge: usize,
113 pub apply_lower_callee_value_to_thunk: usize,
114 pub apply_lower_callee_thunk_to_value: usize,
115 pub apply_lower_callee_bind_here: usize,
116 pub apply_lower_argument_value_to_thunk: usize,
117 pub apply_lower_argument_thunk_to_value: usize,
118 pub apply_lower_argument_bind_here: usize,
119 pub apply_prepare_final_argument_value_to_thunk: usize,
120 pub apply_prepare_final_argument_thunk_to_value: usize,
121 pub apply_prepare_final_argument_bind_here: usize,
122 pub apply_prepare_effect_operation_argument_value_to_thunk: usize,
123 pub apply_prepare_effect_operation_argument_thunk_to_value: usize,
124 pub apply_prepare_effect_operation_argument_bind_here: usize,
125 pub reused_thunk: usize,
126 pub forced_effect_thunk: usize,
127 pub matched_expected_adapter: usize,
128 pub unmatched_expected_adapter: usize,
129 pub unmatched_value_to_thunk: usize,
130 pub unmatched_thunk_to_value: usize,
131 pub unmatched_bind_here: usize,
132 pub matched_derived_expected_edge_parent: usize,
133 pub unmatched_derived_expected_edge_parent: usize,
134 pub observed_adapter_with_source_expected_edge: usize,
135 pub observed_adapter_without_source_expected_edge: usize,
136 pub observed_adapter_source_application_callee: usize,
137 pub observed_adapter_source_application_argument: usize,
138 pub observed_adapter_source_other_expected_edge: usize,
139 pub observed_adapter_source_with_derived_parent: usize,
140 pub observed_adapter_source_without_derived_parent: usize,
141 pub events: Vec<RuntimeAdapterEvent>,
142 pub observed_adapter_evidence: Vec<ObservedAdapterEvidence>,
143}
144
145#[derive(Debug, Clone, PartialEq, Eq)]
146pub struct ObservedAdapterEvidence {
147 pub kind: ObservedAdapterEvidenceKind,
148 pub phase: RuntimeApplyAdapterPhase,
149 pub owner: Option<typed_ir::Path>,
150 pub apply_target: Option<typed_ir::Path>,
151 pub source_expected_edge: Option<u32>,
152 pub actual: RuntimeType,
153 pub expected: RuntimeType,
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
157pub enum ObservedAdapterEvidenceKind {
158 ValueToThunk,
159 ForceThunkToValue,
160}
161
162#[derive(Debug, Clone, PartialEq, Eq)]
163pub struct RuntimeAdapterEvent {
164 pub kind: RuntimeAdapterEventKind,
165 pub phase: RuntimeApplyAdapterPhase,
166 pub owner: Option<typed_ir::Path>,
167 pub apply_target: Option<typed_ir::Path>,
168 pub callee_source_edge: Option<u32>,
169 pub arg_source_edge: Option<u32>,
170 pub actual: RuntimeType,
171 pub expected: RuntimeType,
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
175pub enum RuntimeAdapterEventKind {
176 ValueToThunk,
177 ThunkToValue,
178 BindHere,
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
182pub enum RuntimeApplyAdapterPhase {
183 LowerCallee,
184 LowerArgument,
185 PrepareFinalArgument,
186 PrepareEffectOperationArgument,
187}
188
189#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
190pub struct ExpectedAdapterEvidenceProfile {
191 pub total: usize,
192 pub runtime_usable: usize,
193 pub closed: usize,
194 pub informative: usize,
195 pub effect_operation_argument: usize,
196 pub value_to_thunk: usize,
197 pub thunk_to_value: usize,
198 pub bind_here: usize,
199 pub handler_residual: usize,
200 pub handler_return: usize,
201 pub resume_argument: usize,
202}
203
204#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
205pub struct DerivedExpectedEvidenceProfile {
206 pub total: usize,
207 pub record_field: usize,
208 pub tuple_item: usize,
209 pub variant_payload: usize,
210 pub function_param: usize,
211 pub function_return: usize,
212 pub covariant: usize,
213 pub contravariant: usize,
214 pub invariant: usize,
215}
216
217pub fn lower_core_program(program: typed_ir::CoreProgram) -> RuntimeResult<Module> {
218 let graph = program.graph;
219 let evidence = program.evidence;
220 lower_principal_module_with_graph_and_evidence(program.program, &graph, &evidence)
221}
222
223pub fn lower_core_program_profiled(
224 program: typed_ir::CoreProgram,
225) -> RuntimeResult<RuntimeLowerOutput> {
226 let core_shape = profile_core_program(&program);
227 let graph = program.graph;
228 let evidence = program.evidence;
229 lower_principal_module_with_graph_and_evidence_profiled(
230 program.program,
231 &graph,
232 &evidence,
233 core_shape,
234 )
235}
236
237pub fn lower_principal_module(module: typed_ir::PrincipalModule) -> RuntimeResult<Module> {
238 lower_principal_module_with_graph(module, &typed_ir::CoreGraphView::default())
239}
240
241pub fn lower_principal_module_with_graph(
242 module: typed_ir::PrincipalModule,
243 graph: &typed_ir::CoreGraphView,
244) -> RuntimeResult<Module> {
245 let evidence = typed_ir::PrincipalEvidence::default();
246 lower_principal_module_with_graph_and_evidence(module, graph, &evidence)
247}
248
249fn lower_principal_module_with_graph_and_evidence(
250 module: typed_ir::PrincipalModule,
251 graph: &typed_ir::CoreGraphView,
252 evidence: &typed_ir::PrincipalEvidence,
253) -> RuntimeResult<Module> {
254 lower_principal_module_with_graph_and_evidence_inner(
255 module,
256 graph,
257 evidence,
258 CoreShapeProfile::default(),
259 false,
260 )
261 .map(|output| output.module)
262}
263
264fn lower_principal_module_with_graph_and_evidence_profiled(
265 module: typed_ir::PrincipalModule,
266 graph: &typed_ir::CoreGraphView,
267 evidence: &typed_ir::PrincipalEvidence,
268 core_shape: CoreShapeProfile,
269) -> RuntimeResult<RuntimeLowerOutput> {
270 lower_principal_module_with_graph_and_evidence_inner(module, graph, evidence, core_shape, true)
271}
272
273fn lower_principal_module_with_graph_and_evidence_inner(
274 module: typed_ir::PrincipalModule,
275 graph: &typed_ir::CoreGraphView,
276 evidence: &typed_ir::PrincipalEvidence,
277 core_shape: CoreShapeProfile,
278 collect_profile: bool,
279) -> RuntimeResult<RuntimeLowerOutput> {
280 let principal_vars = principal_module_type_vars(&module);
281 let mut binding_infos = module
282 .bindings
283 .iter()
284 .map(|binding| {
285 let ty = project_runtime_hir_type_with_vars(&binding.scheme.body, &principal_vars);
286 (
287 binding.name.clone(),
288 BindingInfo {
289 type_params: {
290 if is_constructor_variant_expr(&binding.body) {
291 principal_runtime_type_params(&binding.scheme.body, &ty, true)
292 } else {
293 principal_runtime_type_params(&binding.scheme.body, &ty, false)
294 }
295 },
296 ty,
297 requirements: binding.scheme.requirements.clone(),
298 },
299 )
300 })
301 .collect::<HashMap<_, _>>();
302 let mut env = module
303 .bindings
304 .iter()
305 .map(|binding| {
306 (
307 binding.name.clone(),
308 binding_infos
309 .get(&binding.name)
310 .map(|info| info.ty.clone())
311 .unwrap_or_else(|| {
312 project_runtime_hir_type_with_vars(&binding.scheme.body, &principal_vars)
313 }),
314 )
315 })
316 .collect::<HashMap<_, _>>();
317 normalize_initial_alias_types(&module.bindings, &mut env, &mut binding_infos);
318 let aliases = direct_aliases(&module.bindings);
319 let expected_edges_by_id = evidence
320 .expected_edges
321 .iter()
322 .map(|edge| (edge.id, edge))
323 .collect();
324 let mut lowerer = Lowerer {
325 env,
326 binding_infos,
327 aliases,
328 graph,
329 runtime_symbols: graph
330 .runtime_symbols
331 .iter()
332 .map(|symbol| (symbol.path.clone(), symbol.kind))
333 .collect(),
334 primitive_paths: RuntimePrimitivePathTable::from_graph(graph),
335 principal_vars,
336 expected_edges_by_id,
337 use_expected_arg_evidence: std::env::var_os("YULANG_USE_EXPECTED_ARG_EVIDENCE").is_some(),
338 use_principal_elaboration: std::env::var_os("YULANG_DISABLE_PRINCIPAL_ELABORATE").is_none(),
339 expected_arg_evidence_profile: ExpectedArgEvidenceProfile::default(),
340 runtime_adapter_profile: RuntimeAdapterProfile::default(),
341 local_param_boundaries: HashMap::new(),
342 handler_body_depth: 0,
343 current_function_boundary: None,
344 current_binding: None,
345 current_runtime_adapter_source: None,
346 next_synthetic_type_var: 0,
347 next_effect_id_var: 0,
348 };
349 lowerer.runtime_adapter_profile.collect_events = collect_profile;
350 let path = module.path;
351 let bindings = module
352 .bindings
353 .into_iter()
354 .map(|binding| lowerer.lower_binding(binding))
355 .collect::<RuntimeResult<Vec<_>>>()?;
356 let root_exprs = module
357 .root_exprs
358 .into_iter()
359 .enumerate()
360 .map(|(index, expr)| lowerer.lower_root_expr(index, expr))
361 .collect::<RuntimeResult<Vec<_>>>()?;
362 if std::env::var_os("YULANG_DEBUG_EXPECTED_ARG_EVIDENCE").is_some() {
363 eprintln!(
364 "expected-arg evidence: present={} converted={} usable-by-table={} usable-by-bounds={} used-as-arg-type-hint={} used-as-lowering-expected={} ignored-no-expected-arg={} ignored-not-convertible={} ignored-table-open={} ignored-table-uninformative={} ignored-table-not-runtime-usable={} ignored-bounds-unusable={} ignored-unusable={} ignored-no-push={}",
365 lowerer.expected_arg_evidence_profile.present,
366 lowerer.expected_arg_evidence_profile.converted,
367 lowerer.expected_arg_evidence_profile.usable_by_table,
368 lowerer.expected_arg_evidence_profile.usable_by_bounds,
369 lowerer.expected_arg_evidence_profile.used_as_arg_type_hint,
370 lowerer
371 .expected_arg_evidence_profile
372 .used_as_lowering_expected,
373 lowerer
374 .expected_arg_evidence_profile
375 .ignored_no_expected_arg,
376 lowerer
377 .expected_arg_evidence_profile
378 .ignored_not_convertible,
379 lowerer.expected_arg_evidence_profile.ignored_table_open,
380 lowerer
381 .expected_arg_evidence_profile
382 .ignored_table_uninformative,
383 lowerer
384 .expected_arg_evidence_profile
385 .ignored_table_not_runtime_usable,
386 lowerer
387 .expected_arg_evidence_profile
388 .ignored_bounds_unusable,
389 lowerer.expected_arg_evidence_profile.ignored_unusable,
390 lowerer.expected_arg_evidence_profile.ignored_no_push,
391 );
392 }
393 if std::env::var_os("YULANG_DEBUG_RUNTIME_ADAPTERS").is_some() {
394 eprintln!(
395 "runtime adapters: value-to-thunk={} thunk-to-value={} bind-here={} apply-evidence-value-to-thunk={} apply-evidence-thunk-to-value={} apply-evidence-bind-here={} reused-thunk={} forced-effect-thunk={}",
396 lowerer.runtime_adapter_profile.value_to_thunk,
397 lowerer.runtime_adapter_profile.thunk_to_value,
398 lowerer.runtime_adapter_profile.bind_here,
399 lowerer
400 .runtime_adapter_profile
401 .apply_evidence_value_to_thunk,
402 lowerer
403 .runtime_adapter_profile
404 .apply_evidence_thunk_to_value,
405 lowerer.runtime_adapter_profile.apply_evidence_bind_here,
406 lowerer.runtime_adapter_profile.reused_thunk,
407 lowerer.runtime_adapter_profile.forced_effect_thunk,
408 );
409 }
410 let roots = module
411 .roots
412 .into_iter()
413 .map(|root| match root {
414 typed_ir::PrincipalRoot::Binding(path) => Root::Binding(path),
415 typed_ir::PrincipalRoot::Expr(index) => Root::Expr(index),
416 })
417 .collect();
418 let module = Module {
419 path,
420 bindings,
421 root_exprs,
422 roots,
423 role_impls: graph.role_impls.clone(),
424 };
425 check_runtime_invariants(&module, RuntimeStage::Lowered)?;
426 validate_module(&module)?;
427 let mut runtime_adapters = lowerer.runtime_adapter_profile;
428 if collect_profile {
429 profile_runtime_adapter_expected_matches(&mut runtime_adapters, evidence);
430 profile_runtime_adapter_derived_parent_matches(&mut runtime_adapters, evidence);
431 collect_observed_adapter_evidence(&mut runtime_adapters, evidence);
432 }
433 Ok(RuntimeLowerOutput {
434 module,
435 profile: RuntimeLowerProfile {
436 core_shape,
437 expected_arg_evidence: lowerer.expected_arg_evidence_profile,
438 expected_adapter_evidence: collect_profile
439 .then(|| expected_adapter_evidence_profile(evidence))
440 .unwrap_or_default(),
441 derived_expected_evidence: collect_profile
442 .then(|| derived_expected_evidence_profile(evidence))
443 .unwrap_or_default(),
444 runtime_adapters,
445 },
446 })
447}
448
449fn is_constructor_variant_expr(expr: &typed_ir::Expr) -> bool {
450 matches!(
451 expr,
452 typed_ir::Expr::Variant {
453 source: typed_ir::VariantExprSource::Constructor,
454 ..
455 }
456 )
457}
458
459fn collect_observed_adapter_evidence(
460 profile: &mut RuntimeAdapterProfile,
461 evidence: &typed_ir::PrincipalEvidence,
462) {
463 profile.observed_adapter_evidence = profile
464 .events
465 .iter()
466 .filter_map(observed_adapter_evidence_from_event)
467 .collect();
468 profile_observed_adapter_source_coverage(profile, evidence);
469}
470
471fn profile_observed_adapter_source_coverage(
472 profile: &mut RuntimeAdapterProfile,
473 evidence: &typed_ir::PrincipalEvidence,
474) {
475 let observed = profile.observed_adapter_evidence.clone();
476 for observed in observed {
477 let Some(source_expected_edge) = observed.source_expected_edge else {
478 profile.observed_adapter_without_source_expected_edge += 1;
479 continue;
480 };
481 profile.observed_adapter_with_source_expected_edge += 1;
482 match evidence
483 .expected_edge(source_expected_edge)
484 .map(|edge| edge.kind)
485 {
486 Some(typed_ir::ExpectedEdgeKind::ApplicationCallee) => {
487 profile.observed_adapter_source_application_callee += 1;
488 }
489 Some(typed_ir::ExpectedEdgeKind::ApplicationArgument) => {
490 profile.observed_adapter_source_application_argument += 1;
491 }
492 Some(_) | None => {
493 profile.observed_adapter_source_other_expected_edge += 1;
494 }
495 }
496 if evidence
497 .derived_expected_edges_for_parent(source_expected_edge)
498 .next()
499 .is_some()
500 {
501 profile.observed_adapter_source_with_derived_parent += 1;
502 } else {
503 profile.observed_adapter_source_without_derived_parent += 1;
504 }
505 }
506}
507
508fn observed_adapter_evidence_from_event(
509 event: &RuntimeAdapterEvent,
510) -> Option<ObservedAdapterEvidence> {
511 let kind = match event.kind {
512 RuntimeAdapterEventKind::ValueToThunk => ObservedAdapterEvidenceKind::ValueToThunk,
513 RuntimeAdapterEventKind::ThunkToValue => ObservedAdapterEvidenceKind::ForceThunkToValue,
514 RuntimeAdapterEventKind::BindHere => return None,
515 };
516 Some(ObservedAdapterEvidence {
517 kind,
518 phase: event.phase,
519 owner: event.owner.clone(),
520 apply_target: event.apply_target.clone(),
521 source_expected_edge: runtime_adapter_event_source_edge(event),
522 actual: event.actual.clone(),
523 expected: event.expected.clone(),
524 })
525}
526
527fn profile_runtime_adapter_expected_matches(
528 profile: &mut RuntimeAdapterProfile,
529 evidence: &typed_ir::PrincipalEvidence,
530) {
531 for event in &profile.events {
532 if expected_adapter_event_match(evidence, event) {
533 profile.matched_expected_adapter += 1;
534 } else {
535 profile.unmatched_expected_adapter += 1;
536 match event.kind {
537 RuntimeAdapterEventKind::ValueToThunk => {
538 profile.unmatched_value_to_thunk += 1;
539 }
540 RuntimeAdapterEventKind::ThunkToValue => {
541 profile.unmatched_thunk_to_value += 1;
542 }
543 RuntimeAdapterEventKind::BindHere => {
544 profile.unmatched_bind_here += 1;
545 }
546 }
547 }
548 }
549}
550
551fn profile_runtime_adapter_derived_parent_matches(
552 profile: &mut RuntimeAdapterProfile,
553 evidence: &typed_ir::PrincipalEvidence,
554) {
555 for event in &profile.events {
556 let Some(source_edge) = runtime_adapter_event_source_edge(event) else {
557 continue;
558 };
559 if evidence
560 .derived_expected_edges_for_parent(source_edge)
561 .next()
562 .is_some()
563 {
564 profile.matched_derived_expected_edge_parent += 1;
565 } else {
566 profile.unmatched_derived_expected_edge_parent += 1;
567 }
568 }
569}
570
571fn expected_adapter_event_match(
572 evidence: &typed_ir::PrincipalEvidence,
573 event: &RuntimeAdapterEvent,
574) -> bool {
575 evidence
576 .expected_adapter_edges
577 .iter()
578 .any(|edge| expected_adapter_edge_matches_event(edge, event))
579}
580
581fn expected_adapter_edge_matches_event(
582 edge: &typed_ir::ExpectedAdapterEdgeEvidence,
583 event: &RuntimeAdapterEvent,
584) -> bool {
585 if expected_adapter_event_kind(edge.kind) != Some(event.kind) {
586 return false;
587 }
588 if let Some(source_edge) = runtime_adapter_event_source_edge(event)
589 && edge.source_expected_edge != Some(source_edge)
590 {
591 return false;
592 }
593 true
594}
595
596fn runtime_adapter_event_source_edge(event: &RuntimeAdapterEvent) -> Option<u32> {
597 match event.phase {
598 RuntimeApplyAdapterPhase::LowerCallee => event.callee_source_edge,
599 RuntimeApplyAdapterPhase::LowerArgument
600 | RuntimeApplyAdapterPhase::PrepareFinalArgument
601 | RuntimeApplyAdapterPhase::PrepareEffectOperationArgument => event.arg_source_edge,
602 }
603}
604
605fn expected_adapter_event_kind(
606 kind: typed_ir::ExpectedAdapterEdgeKind,
607) -> Option<RuntimeAdapterEventKind> {
608 match kind {
609 typed_ir::ExpectedAdapterEdgeKind::ValueToThunk => {
610 Some(RuntimeAdapterEventKind::ValueToThunk)
611 }
612 typed_ir::ExpectedAdapterEdgeKind::ThunkToValue => {
613 Some(RuntimeAdapterEventKind::ThunkToValue)
614 }
615 typed_ir::ExpectedAdapterEdgeKind::BindHere => Some(RuntimeAdapterEventKind::BindHere),
616 typed_ir::ExpectedAdapterEdgeKind::EffectOperationArgument
617 | typed_ir::ExpectedAdapterEdgeKind::HandlerResidual
618 | typed_ir::ExpectedAdapterEdgeKind::HandlerReturn
619 | typed_ir::ExpectedAdapterEdgeKind::ResumeArgument => None,
620 }
621}
622
623fn expected_adapter_evidence_profile(
624 evidence: &typed_ir::PrincipalEvidence,
625) -> ExpectedAdapterEvidenceProfile {
626 let mut profile = ExpectedAdapterEvidenceProfile::default();
627 for edge in &evidence.expected_adapter_edges {
628 profile.total += 1;
629 if edge.runtime_usable {
630 profile.runtime_usable += 1;
631 }
632 if edge.closed {
633 profile.closed += 1;
634 }
635 if edge.informative {
636 profile.informative += 1;
637 }
638 match edge.kind {
639 typed_ir::ExpectedAdapterEdgeKind::EffectOperationArgument => {
640 profile.effect_operation_argument += 1;
641 }
642 typed_ir::ExpectedAdapterEdgeKind::ValueToThunk => {
643 profile.value_to_thunk += 1;
644 }
645 typed_ir::ExpectedAdapterEdgeKind::ThunkToValue => {
646 profile.thunk_to_value += 1;
647 }
648 typed_ir::ExpectedAdapterEdgeKind::BindHere => {
649 profile.bind_here += 1;
650 }
651 typed_ir::ExpectedAdapterEdgeKind::HandlerResidual => {
652 profile.handler_residual += 1;
653 }
654 typed_ir::ExpectedAdapterEdgeKind::HandlerReturn => {
655 profile.handler_return += 1;
656 }
657 typed_ir::ExpectedAdapterEdgeKind::ResumeArgument => {
658 profile.resume_argument += 1;
659 }
660 }
661 }
662 profile
663}
664
665fn derived_expected_evidence_profile(
666 evidence: &typed_ir::PrincipalEvidence,
667) -> DerivedExpectedEvidenceProfile {
668 let mut profile = DerivedExpectedEvidenceProfile::default();
669 for edge in &evidence.derived_expected_edges {
670 profile.total += 1;
671 match edge.kind {
672 typed_ir::DerivedExpectedEdgeKind::RecordField => profile.record_field += 1,
673 typed_ir::DerivedExpectedEdgeKind::TupleItem => profile.tuple_item += 1,
674 typed_ir::DerivedExpectedEdgeKind::VariantPayload => profile.variant_payload += 1,
675 typed_ir::DerivedExpectedEdgeKind::FunctionParam => profile.function_param += 1,
676 typed_ir::DerivedExpectedEdgeKind::FunctionReturn => profile.function_return += 1,
677 }
678 match edge.polarity {
679 typed_ir::EdgePolarity::Covariant => profile.covariant += 1,
680 typed_ir::EdgePolarity::Contravariant => profile.contravariant += 1,
681 typed_ir::EdgePolarity::Invariant => profile.invariant += 1,
682 }
683 }
684 profile
685}
686
687fn normalize_initial_alias_types(
688 bindings: &[typed_ir::PrincipalBinding],
689 env: &mut HashMap<typed_ir::Path, RuntimeType>,
690 binding_infos: &mut HashMap<typed_ir::Path, BindingInfo>,
691) {
692 for _ in 0..bindings.len() {
693 let mut changed = false;
694 for binding in bindings {
695 let typed_ir::Expr::Var(target) = &binding.body else {
696 continue;
697 };
698 let Some(current_ty) = env.get(&binding.name).cloned() else {
699 continue;
700 };
701 let Some(target_ty) = env.get(target).cloned() else {
702 continue;
703 };
704 if !prefer_alias_target_runtime_type(¤t_ty, &target_ty) {
705 continue;
706 }
707 if current_ty == target_ty {
708 continue;
709 }
710 env.insert(binding.name.clone(), target_ty.clone());
711 if let Some(info) = binding_infos.get_mut(&binding.name) {
712 info.ty = target_ty.clone();
713 info.type_params = principal_hir_type_params(&target_ty);
714 }
715 changed = true;
716 }
717 if !changed {
718 break;
719 }
720 }
721}
722
723fn direct_aliases(
724 bindings: &[typed_ir::PrincipalBinding],
725) -> HashMap<typed_ir::Path, typed_ir::Path> {
726 bindings
727 .iter()
728 .filter_map(|binding| match &binding.body {
729 typed_ir::Expr::Var(target) if target != &binding.name => {
730 Some((binding.name.clone(), target.clone()))
731 }
732 _ => None,
733 })
734 .collect()
735}
736
737struct Lowerer<'a> {
738 env: HashMap<typed_ir::Path, RuntimeType>,
739 binding_infos: HashMap<typed_ir::Path, BindingInfo>,
740 aliases: HashMap<typed_ir::Path, typed_ir::Path>,
741 graph: &'a typed_ir::CoreGraphView,
742 runtime_symbols: HashMap<typed_ir::Path, typed_ir::RuntimeSymbolKind>,
743 primitive_paths: RuntimePrimitivePathTable,
744 principal_vars: BTreeSet<typed_ir::TypeVar>,
745 expected_edges_by_id: HashMap<u32, &'a typed_ir::ExpectedEdgeEvidence>,
746 use_expected_arg_evidence: bool,
747 use_principal_elaboration: bool,
748 expected_arg_evidence_profile: ExpectedArgEvidenceProfile,
749 runtime_adapter_profile: RuntimeAdapterProfile,
750 local_param_boundaries: HashMap<typed_ir::Path, LocalParamBoundary>,
751 handler_body_depth: usize,
752 current_function_boundary: Option<EffectIdVar>,
753 current_binding: Option<typed_ir::Path>,
754 current_runtime_adapter_source: Option<RuntimeAdapterSource>,
755 next_synthetic_type_var: usize,
756 next_effect_id_var: usize,
757}
758
759#[derive(Clone)]
760struct BindingInfo {
761 ty: RuntimeType,
762 type_params: Vec<typed_ir::TypeVar>,
763 requirements: Vec<typed_ir::RoleRequirement>,
764}
765
766#[derive(Debug, Clone)]
767struct LocalParamBoundary {
768 id: EffectIdVar,
769 effect: typed_ir::Type,
770 applies_to_thunk_var: bool,
771 applies_to_call_outside_handler: bool,
772}
773
774#[cfg(test)]
775mod tests;