1use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
10use std::time::Duration;
11
12use yulang_typed_ir as typed_ir;
13
14use crate::diagnostic::{RuntimeError, RuntimeResult};
15use crate::invariant::{RuntimeStage, check_runtime_invariants, check_strict_runtime_value_types};
16use crate::ir::{
17 Binding, Expr, ExprKind, HandleArm, HandleEffect, JoinEvidence, MatchArm, Module, Pattern,
18 RecordExprField, RecordPatternField, RecordSpreadExpr, RecordSpreadPattern, ResumeBinding,
19 Root, Stmt, Type as RuntimeType, TypeInstantiation,
20};
21use crate::monomorphize::{
22 DemandEvidenceProfile, DemandQueueProfile, DemandSpecialization, demand_monomorphize_module,
23 reset_demand_evidence_profile, snapshot_demand_evidence_profile,
24};
25use crate::refine::refine_module_types_with_report;
26use crate::types::{
27 collect_expr_type_vars, collect_hir_type_vars, collect_type_vars as collect_core_type_vars,
28 core_type_has_vars, effect_is_empty, effect_paths_match, hir_type_has_vars,
29 project_runtime_effect, project_runtime_type_with_vars, runtime_core_type, should_thunk_effect,
30 substitute_apply_evidence, substitute_join_evidence, substitute_scheme, substitute_type,
31};
32use crate::validate::validate_module;
33
34mod audit;
35mod canonicalize;
36mod handler_boundary;
37mod local_refresh;
38mod locals;
39mod metadata;
40mod normalize;
41mod paths;
42mod principal_elaborate;
43mod principal_unify;
44mod reachability;
45mod shape;
46mod substitute;
47mod type_projection_metrics;
48mod type_surface;
49
50use audit::*;
51use canonicalize::*;
52use handler_boundary::*;
53use local_refresh::*;
54use locals::*;
55use metadata::*;
56use normalize::*;
57use paths::*;
58use principal_elaborate::*;
59use principal_unify::*;
60use reachability::*;
61use shape::*;
62use substitute::*;
63use type_surface::*;
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum MonomorphizeMode {
67 PrincipalElaborate,
68 LegacyDemandFixpoint,
69}
70
71impl MonomorphizeMode {
72 fn detect() -> Self {
73 if std::env::var_os("YULANG_LEGACY_MONO_FIXPOINT").is_some() {
74 Self::LegacyDemandFixpoint
75 } else {
76 Self::PrincipalElaborate
77 }
78 }
79
80 pub fn name(self) -> &'static str {
81 match self {
82 Self::PrincipalElaborate => "principal-elaborate",
83 Self::LegacyDemandFixpoint => "legacy-demand-fixpoint",
84 }
85 }
86}
87
88impl Default for MonomorphizeMode {
89 fn default() -> Self {
90 Self::PrincipalElaborate
91 }
92}
93
94pub fn monomorphize_module(module: Module) -> RuntimeResult<Module> {
95 crate::monomorphize::effect_hole_metrics::reset();
96 type_projection_metrics::reset();
97 let lowered = run_mono_pipeline_unprofiled(module)?;
98 let lowered = normalize_monomorphized_metadata(lowered);
99 audit_monomorphized_module(&lowered)?;
100 check_runtime_invariants(&lowered, RuntimeStage::Monomorphized)?;
101 check_strict_monomorphized_runtime_types_if_requested(&lowered)?;
102 validate_module(&lowered)?;
103 crate::monomorphize::effect_hole_metrics::report_if_requested("monomorphize_module");
104 type_projection_metrics::report_if_requested("monomorphize_module");
105 Ok(lowered)
106}
107
108pub fn monomorphize_module_profiled(
109 module: Module,
110) -> RuntimeResult<(Module, MonomorphizeProfile)> {
111 crate::monomorphize::effect_hole_metrics::reset();
112 type_projection_metrics::reset();
113 let (lowered, profile) = run_mono_pipeline(module)?;
114 let lowered = normalize_monomorphized_metadata(lowered);
115 audit_monomorphized_module(&lowered)?;
116 check_runtime_invariants(&lowered, RuntimeStage::Monomorphized)?;
117 check_strict_monomorphized_runtime_types_if_requested(&lowered)?;
118 validate_module(&lowered)?;
119 crate::monomorphize::effect_hole_metrics::report_if_requested("monomorphize_module_profiled");
120 type_projection_metrics::report_if_requested("monomorphize_module_profiled");
121 Ok((lowered, profile))
122}
123
124#[derive(Debug, Clone, PartialEq, Eq)]
125pub struct MonomorphizeProfile {
126 pub mode: MonomorphizeMode,
127 pub passes: Vec<MonomorphizePassProfile>,
128 pub demand_evidence: DemandEvidenceProfile,
129}
130
131impl Default for MonomorphizeProfile {
132 fn default() -> Self {
133 Self {
134 mode: MonomorphizeMode::default(),
135 passes: Vec::new(),
136 demand_evidence: DemandEvidenceProfile::default(),
137 }
138 }
139}
140
141impl MonomorphizeProfile {
142 pub fn pass_count(&self) -> usize {
143 self.passes.len()
144 }
145
146 pub fn effective_pass_count(&self) -> usize {
147 self.passes
148 .iter()
149 .filter(|pass| pass.progress.changed())
150 .count()
151 }
152
153 pub fn added_specializations(&self) -> usize {
154 self.passes
155 .iter()
156 .map(|pass| pass.progress.added_specializations)
157 .sum()
158 }
159
160 pub fn demand_queue_profile(&self) -> DemandQueueProfile {
161 self.passes
162 .iter()
163 .fold(DemandQueueProfile::default(), |mut profile, pass| {
164 profile.merge(pass.demand_queue);
165 profile
166 })
167 }
168}
169
170#[derive(Debug, Clone, PartialEq, Eq)]
171pub struct MonomorphizePassProfile {
172 pub name: &'static str,
173 pub duration: Duration,
174 pub bindings_before: usize,
175 pub bindings_after: usize,
176 pub roots_before: usize,
177 pub roots_after: usize,
178 pub progress: MonomorphizeProgress,
179 pub demand_queue: DemandQueueProfile,
180 pub principal_elaborate: SubstitutionSpecializeProfile,
181 pub added_binding_paths: Vec<typed_ir::Path>,
182 pub added_specializations: Vec<DemandSpecialization>,
183}
184
185#[derive(Debug, Default, Clone, PartialEq, Eq)]
186pub struct SubstitutionSpecializeProfile {
187 pub stats: HashMap<&'static str, usize>,
188 pub timings: HashMap<&'static str, Duration>,
189 pub target_skips: Vec<SubstitutionSpecializeTargetSkips>,
190 pub target_inferences: Vec<SubstitutionSpecializeTargetInferences>,
191 pub target_rewrites: Vec<SubstitutionSpecializeTargetRewrites>,
192}
193
194#[derive(Debug, Clone, PartialEq, Eq)]
195pub struct SubstitutionSpecializeTargetSkips {
196 pub target: typed_ir::Path,
197 pub survives_final_prune: Option<bool>,
198 pub actionable: bool,
199 pub reasons: Vec<SubstitutionSpecializeSkipCount>,
200 pub missing_vars: Vec<SubstitutionSpecializeMissingVarCount>,
201 pub no_complete_causes: Vec<SubstitutionSpecializeSkipCount>,
202}
203
204#[derive(Debug, Clone, PartialEq, Eq)]
205pub struct SubstitutionSpecializeSkipCount {
206 pub reason: &'static str,
207 pub count: usize,
208}
209
210#[derive(Debug, Clone, PartialEq, Eq)]
211pub struct SubstitutionSpecializeMissingVarCount {
212 pub var: typed_ir::TypeVar,
213 pub count: usize,
214}
215
216#[derive(Debug, Clone, PartialEq, Eq)]
217pub struct SubstitutionSpecializeTargetRewrites {
218 pub target: typed_ir::Path,
219 pub total_apply_visits: usize,
220 pub rewrites: usize,
221 pub cached_incomplete: usize,
222 pub incomplete: usize,
223 pub max_specialization_depth: usize,
224 pub contexts: Vec<SubstitutionSpecializeRewriteContextCount>,
225 pub phases: Vec<SubstitutionSpecializeRewritePhaseTiming>,
226 pub expr_kinds: Vec<SubstitutionSpecializeRewriteExprKindTiming>,
227}
228
229#[derive(Debug, Clone, PartialEq, Eq)]
230pub struct SubstitutionSpecializeRewriteContextCount {
231 pub context: &'static str,
232 pub count: usize,
233}
234
235#[derive(Debug, Clone, PartialEq, Eq)]
236pub struct SubstitutionSpecializeRewritePhaseTiming {
237 pub phase: &'static str,
238 pub duration: Duration,
239}
240
241#[derive(Debug, Clone, PartialEq, Eq)]
242pub struct SubstitutionSpecializeRewriteExprKindTiming {
243 pub kind: &'static str,
244 pub count: usize,
245 pub duration: Duration,
246}
247
248#[derive(Debug, Clone, PartialEq, Eq)]
249pub struct SubstitutionSpecializeTargetInferences {
250 pub target: typed_ir::Path,
251 pub sources: Vec<SubstitutionSpecializeInferenceCount>,
252}
253
254#[derive(Debug, Clone, PartialEq, Eq)]
255pub struct SubstitutionSpecializeInferenceCount {
256 pub source: &'static str,
257 pub count: usize,
258}
259
260#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
261pub struct MonomorphizeProgress {
262 pub changed_bindings: usize,
263 pub changed_roots: usize,
264 pub added_specializations: usize,
265}
266
267impl MonomorphizeProgress {
268 pub fn changed(self) -> bool {
269 self.changed_bindings > 0 || self.changed_roots > 0 || self.added_specializations > 0
270 }
271}
272
273#[derive(Debug, Clone, Copy, PartialEq, Eq)]
274enum MonoPass {
275 PrincipalElaborate,
276 DemandSpecialize,
277 RefineTypes,
278 RefreshClosedSchemes,
279 CanonicalizeSpecializations,
280 InlinePolymorphicWrappers,
281 PruneUnreachableSpecializations,
282 PruneUnreachable,
283}
284
285impl MonoPass {
286 fn name(self) -> &'static str {
287 match self {
288 MonoPass::PrincipalElaborate => "principal-elaborate",
289 MonoPass::DemandSpecialize => "demand-specialize",
290 MonoPass::RefineTypes => "refine-types",
291 MonoPass::RefreshClosedSchemes => "refresh-closed-schemes",
292 MonoPass::CanonicalizeSpecializations => "canonicalize-specializations",
293 MonoPass::InlinePolymorphicWrappers => "inline-polymorphic-wrappers",
294 MonoPass::PruneUnreachableSpecializations => "prune-unreachable-specializations",
295 MonoPass::PruneUnreachable => "prune-unreachable",
296 }
297 }
298}
299
300#[derive(Debug, Clone, Copy, PartialEq, Eq)]
301enum MonoStage {
302 Pass(MonoPass),
303 Repeat {
304 name: &'static str,
305 passes: &'static [MonoPass],
306 times: usize,
307 },
308 Fixpoint {
309 name: &'static str,
310 passes: &'static [MonoPass],
311 limit: usize,
312 },
313}
314
315const INITIAL_FIXPOINT: &[MonoPass] = &[MonoPass::DemandSpecialize, MonoPass::RefineTypes];
316
317const SPECIALIZATION_FIXPOINT: &[MonoPass] = &[
318 MonoPass::DemandSpecialize,
319 MonoPass::RefineTypes,
320 MonoPass::RefreshClosedSchemes,
321 MonoPass::PruneUnreachableSpecializations,
322];
323
324const MONO_PIPELINE: &[MonoStage] = &[
325 MonoStage::Repeat {
326 name: "initial-specialization",
327 passes: INITIAL_FIXPOINT,
328 times: 1,
329 },
330 MonoStage::Pass(MonoPass::CanonicalizeSpecializations),
331 MonoStage::Pass(MonoPass::InlinePolymorphicWrappers),
332 MonoStage::Fixpoint {
333 name: "role-specialization",
334 passes: SPECIALIZATION_FIXPOINT,
335 limit: 8,
336 },
337 MonoStage::Pass(MonoPass::PruneUnreachable),
338];
339
340fn run_mono_pipeline(module: Module) -> RuntimeResult<(Module, MonomorphizeProfile)> {
341 let mode = MonomorphizeMode::detect();
342 reset_demand_evidence_profile();
343 let (module, mut profile) = match mode {
344 MonomorphizeMode::PrincipalElaborate => run_principal_elaborate_pipeline(module)?,
345 MonomorphizeMode::LegacyDemandFixpoint => run_legacy_demand_fixpoint_pipeline(module)?,
346 };
347 profile.mode = mode;
348 profile.demand_evidence = snapshot_demand_evidence_profile();
349 annotate_substitution_skip_reachability(&mut profile, &module);
350 Ok((module, profile))
351}
352
353fn run_principal_elaborate_pipeline(
354 module: Module,
355) -> RuntimeResult<(Module, MonomorphizeProfile)> {
356 let debug = std::env::var_os("YULANG_DEBUG_MONO_PIPELINE").is_some();
357 let mut module = module;
358 let mut profile = MonomorphizeProfile::default();
359 let step = run_profiled_mono_pass(
360 module,
361 MonoPass::InlinePolymorphicWrappers,
362 &mut profile,
363 debug,
364 )?;
365 module = step.module;
366 let step = run_profiled_mono_pass(module, MonoPass::PrincipalElaborate, &mut profile, debug)?;
367 module = step.module;
368 let step = run_profiled_mono_pass(module, MonoPass::RefreshClosedSchemes, &mut profile, debug)?;
369 module = step.module;
370 let step = run_profiled_mono_pass(module, MonoPass::PruneUnreachable, &mut profile, debug)?;
371 module = step.module;
372 if std::env::var_os("YULANG_PRINCIPAL_ELABORATE_STRICT").is_some()
373 && let Some(context) = principal_elaborate_strict_failure(&module)
374 {
375 return Err(RuntimeError::InvariantViolation {
376 stage: "principal-elaborate",
377 context,
378 message: "principal elaboration plan incomplete",
379 });
380 }
381 Ok((module, profile))
382}
383
384fn check_strict_monomorphized_runtime_types_if_requested(module: &Module) -> RuntimeResult<()> {
385 if std::env::var_os("YULANG_STRICT_MONO_RUNTIME_TYPES").is_some() {
386 check_strict_runtime_value_types(module, RuntimeStage::Monomorphized)?;
387 }
388 Ok(())
389}
390
391fn run_legacy_demand_fixpoint_pipeline(
392 module: Module,
393) -> RuntimeResult<(Module, MonomorphizeProfile)> {
394 let debug = std::env::var_os("YULANG_DEBUG_MONO_PIPELINE").is_some();
395 let mut module = module;
396 let mut profile = MonomorphizeProfile::default();
397 for stage in MONO_PIPELINE {
398 match *stage {
399 MonoStage::Pass(pass) => {
400 let step = run_profiled_mono_pass(module, pass, &mut profile, debug)?;
401 module = step.module;
402 }
403 MonoStage::Repeat {
404 name,
405 passes,
406 times,
407 } => {
408 module = run_mono_repeat(module, name, passes, times, &mut profile, debug)?;
409 }
410 MonoStage::Fixpoint {
411 name,
412 passes,
413 limit,
414 } => {
415 module = run_mono_fixpoint(module, name, passes, limit, &mut profile, debug)?;
416 }
417 }
418 }
419 Ok((module, profile))
420}
421
422fn run_mono_pipeline_unprofiled(module: Module) -> RuntimeResult<Module> {
423 match MonomorphizeMode::detect() {
424 MonomorphizeMode::PrincipalElaborate => run_principal_elaborate_pipeline_unprofiled(module),
425 MonomorphizeMode::LegacyDemandFixpoint => {
426 run_mono_pipeline(module).map(|(module, _profile)| module)
427 }
428 }
429}
430
431fn run_principal_elaborate_pipeline_unprofiled(module: Module) -> RuntimeResult<Module> {
432 let mut module = inline_polymorphic_wrappers(module);
433 module = principal_elaborate_module(module);
434 module = refresh_closed_specialized_schemes(module);
435 module = prune_unreachable_bindings(module);
436 if std::env::var_os("YULANG_PRINCIPAL_ELABORATE_STRICT").is_some()
437 && let Some(context) = principal_elaborate_strict_failure(&module)
438 {
439 return Err(RuntimeError::InvariantViolation {
440 stage: "principal-elaborate",
441 context,
442 message: "principal elaboration plan incomplete",
443 });
444 }
445 Ok(module)
446}
447
448fn run_mono_repeat(
449 mut module: Module,
450 name: &'static str,
451 passes: &'static [MonoPass],
452 times: usize,
453 profile: &mut MonomorphizeProfile,
454 debug: bool,
455) -> RuntimeResult<Module> {
456 for round in 0..times {
457 let mut round_progress = MonoProgress::default();
458 for pass in passes {
459 let step = run_profiled_mono_pass(module, *pass, profile, debug)?;
460 module = step.module;
461 round_progress.merge(step.progress);
462 }
463 if debug {
464 eprintln!(
465 "mono repeat {name} round {round}: progress {}",
466 round_progress.format()
467 );
468 }
469 if !round_progress.changed() {
470 break;
471 }
472 }
473 Ok(module)
474}
475
476fn run_profiled_mono_pass(
477 module: Module,
478 pass: MonoPass,
479 profile: &mut MonomorphizeProfile,
480 debug: bool,
481) -> RuntimeResult<MonoStep> {
482 let before_for_changed_debug = std::env::var_os("YULANG_DEBUG_MONO_CHANGED")
483 .is_some()
484 .then(|| module.clone());
485 let before = MonoStats::from_module(&module);
486 let started = std::time::Instant::now();
487 let step = apply_mono_pass(module, pass)?;
488 let duration = started.elapsed();
489 let after = MonoStats::from_module(&step.module);
490 let progress = step.progress.to_public();
491 profile.passes.push(MonomorphizePassProfile {
492 name: pass.name(),
493 duration,
494 bindings_before: before.bindings,
495 bindings_after: after.bindings,
496 roots_before: before.roots,
497 roots_after: after.roots,
498 progress,
499 demand_queue: step.demand_queue,
500 principal_elaborate: step.principal_elaborate.clone(),
501 added_binding_paths: step.added_binding_paths.clone(),
502 added_specializations: step.added_specializations.clone(),
503 });
504 if debug {
505 eprintln!(
506 "mono pass {:>38}: bindings {} -> {}, roots {} -> {}, progress {}",
507 pass.name(),
508 before.bindings,
509 after.bindings,
510 before.roots,
511 after.roots,
512 step.progress.format()
513 );
514 }
515 if let Some(before) = before_for_changed_debug {
516 debug_mono_changed_items(pass.name(), &before, &step.module);
517 }
518 Ok(step)
519}
520
521fn run_mono_fixpoint(
522 mut module: Module,
523 name: &'static str,
524 passes: &'static [MonoPass],
525 limit: usize,
526 profile: &mut MonomorphizeProfile,
527 debug: bool,
528) -> RuntimeResult<Module> {
529 for round in 0..limit {
530 let mut round_progress = MonoProgress::default();
531 for pass in passes {
532 let step = run_profiled_mono_pass(module, *pass, profile, debug)?;
533 module = step.module;
534 round_progress.merge(step.progress);
535 }
536 if !round_progress.changed() {
537 if debug {
538 eprintln!("mono fixpoint {name} converged after {round} rounds");
539 }
540 return Ok(module);
541 } else if debug {
542 eprintln!(
543 "mono fixpoint {name} round {round}: progress {}",
544 round_progress.format()
545 );
546 }
547 }
548 if debug {
549 eprintln!("mono fixpoint {name} reached round limit");
550 }
551 Ok(module)
552}
553
554fn apply_mono_pass(module: Module, pass: MonoPass) -> RuntimeResult<MonoStep> {
555 match pass {
556 MonoPass::PrincipalElaborate => {
557 let bindings_before = module.bindings.len();
558 let roots_before = module.root_exprs.len();
559 let (module, principal_elaborate) = principal_elaborate_module_profiled(module);
560 let added_binding_paths = module
561 .bindings
562 .iter()
563 .skip(bindings_before)
564 .map(|binding| binding.name.clone())
565 .collect::<Vec<_>>();
566 let changed_bindings = principal_elaborate
567 .stats
568 .get("principal-unify-rewrite")
569 .copied()
570 .unwrap_or_default()
571 + principal_elaborate
572 .stats
573 .get("principal-unify-rewrite-surviving-template-binding")
574 .copied()
575 .unwrap_or_default();
576 let progress = MonoProgress {
577 changed_bindings,
578 changed_roots: module.root_exprs.len().abs_diff(roots_before),
579 added_specializations: module.bindings.len().saturating_sub(bindings_before),
580 };
581 Ok(MonoStep {
582 module,
583 progress,
584 demand_queue: DemandQueueProfile::default(),
585 principal_elaborate,
586 added_binding_paths,
587 added_specializations: Vec::new(),
588 })
589 }
590 MonoPass::DemandSpecialize => demand_specialize_module(module),
591 MonoPass::RefineTypes => refine_module_types_for_mono(module),
592 MonoPass::RefreshClosedSchemes => {
593 run_tracked_infallible_pass(module, refresh_closed_specialized_schemes)
594 }
595 MonoPass::CanonicalizeSpecializations => {
596 run_tracked_infallible_pass(module, canonicalize_equivalent_specializations)
597 }
598 MonoPass::InlinePolymorphicWrappers => {
599 run_tracked_infallible_pass(module, inline_polymorphic_wrappers)
600 }
601 MonoPass::PruneUnreachableSpecializations => {
602 run_tracked_infallible_pass(module, prune_unreachable_specializations)
603 }
604 MonoPass::PruneUnreachable => {
605 run_tracked_infallible_pass(module, prune_unreachable_bindings)
606 }
607 }
608}
609
610fn demand_specialize_module(module: Module) -> RuntimeResult<MonoStep> {
611 let before = module.clone();
612 let output =
613 demand_monomorphize_module(module).map_err(|error| RuntimeError::InvariantViolation {
614 stage: "monomorphization",
615 context: format!("{error:?}"),
616 message: "demand-driven specialization failed",
617 })?;
618 validate_module(&output.module)?;
619 let progress = MonoProgress::from_modules(&before, &output.module);
620 let added_binding_paths = added_binding_paths(&before, &output.module);
621 let added_specializations = output.profile.emitted_specializations;
622 Ok(MonoStep {
623 module: output.module,
624 progress,
625 demand_queue: output.profile.queue,
626 principal_elaborate: SubstitutionSpecializeProfile::default(),
627 added_binding_paths,
628 added_specializations,
629 })
630}
631
632struct MonoStats {
633 bindings: usize,
634 roots: usize,
635}
636
637impl MonoStats {
638 fn from_module(module: &Module) -> Self {
639 Self {
640 bindings: module.bindings.len(),
641 roots: module.root_exprs.len(),
642 }
643 }
644}
645
646fn debug_mono_changed_items(pass: &str, before: &Module, after: &Module) {
647 if std::env::var_os("YULANG_DEBUG_MONO_CHANGED").is_none() {
648 return;
649 }
650 let changed_bindings = changed_binding_names(before, after);
651 if !changed_bindings.is_empty() {
652 eprintln!("mono changed {pass} bindings: {changed_bindings:?}");
653 }
654 let changed_roots = changed_root_indexes(before, after);
655 if !changed_roots.is_empty() {
656 eprintln!("mono changed {pass} roots: {changed_roots:?}");
657 }
658}
659
660fn changed_binding_names(before: &Module, after: &Module) -> Vec<typed_ir::Path> {
661 let before_by_name = before
662 .bindings
663 .iter()
664 .map(|binding| (&binding.name, binding))
665 .collect::<HashMap<_, _>>();
666 let mut names = Vec::new();
667 for binding in &after.bindings {
668 if before_by_name
669 .get(&binding.name)
670 .is_none_or(|before| *before != binding)
671 {
672 names.push(binding.name.clone());
673 }
674 }
675 for binding in &before.bindings {
676 if !after
677 .bindings
678 .iter()
679 .any(|after| after.name == binding.name)
680 {
681 names.push(binding.name.clone());
682 }
683 }
684 names
685}
686
687fn changed_root_indexes(before: &Module, after: &Module) -> Vec<usize> {
688 let len = before.root_exprs.len().max(after.root_exprs.len());
689 (0..len)
690 .filter(|index| before.root_exprs.get(*index) != after.root_exprs.get(*index))
691 .collect()
692}
693
694struct MonoStep {
695 module: Module,
696 progress: MonoProgress,
697 demand_queue: DemandQueueProfile,
698 principal_elaborate: SubstitutionSpecializeProfile,
699 added_binding_paths: Vec<typed_ir::Path>,
700 added_specializations: Vec<DemandSpecialization>,
701}
702
703#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
704struct MonoProgress {
705 changed_bindings: usize,
706 changed_roots: usize,
707 added_specializations: usize,
708}
709
710impl MonoProgress {
711 fn changed(self) -> bool {
712 self.changed_bindings > 0 || self.changed_roots > 0 || self.added_specializations > 0
713 }
714
715 fn merge(&mut self, other: Self) {
716 self.changed_bindings += other.changed_bindings;
717 self.changed_roots += other.changed_roots;
718 self.added_specializations += other.added_specializations;
719 }
720
721 fn from_modules(before: &Module, after: &Module) -> Self {
722 let changed_roots = changed_item_count(&before.root_exprs, &after.root_exprs);
723 let changed_bindings = changed_item_count(&before.bindings, &after.bindings);
724 Self {
725 changed_bindings,
726 changed_roots,
727 added_specializations: after.bindings.len().saturating_sub(before.bindings.len()),
728 }
729 }
730
731 fn from_stats(before: MonoStats, after: MonoStats) -> Self {
732 Self {
733 changed_bindings: before.bindings.abs_diff(after.bindings),
734 changed_roots: before.roots.abs_diff(after.roots),
735 added_specializations: after.bindings.saturating_sub(before.bindings),
736 }
737 }
738
739 fn format(self) -> String {
740 if !self.changed() {
741 return "none".to_string();
742 }
743 format!(
744 "bindings={}, roots={}, new-specializations={}",
745 self.changed_bindings, self.changed_roots, self.added_specializations
746 )
747 }
748
749 fn to_public(self) -> MonomorphizeProgress {
750 MonomorphizeProgress {
751 changed_bindings: self.changed_bindings,
752 changed_roots: self.changed_roots,
753 added_specializations: self.added_specializations,
754 }
755 }
756}
757
758fn changed_item_count<T: PartialEq>(before: &[T], after: &[T]) -> usize {
759 let pair_changes = before
760 .iter()
761 .zip(after.iter())
762 .filter(|(before, after)| before != after)
763 .count();
764 let length_delta = before.len().abs_diff(after.len());
765 pair_changes + length_delta
766}
767
768fn run_tracked_pass<F>(module: Module, f: F) -> RuntimeResult<MonoStep>
769where
770 F: FnOnce(Module) -> RuntimeResult<Module>,
771{
772 if std::env::var_os("YULANG_LEGACY_MONO_FIXPOINT").is_some()
773 || std::env::var_os("YULANG_MONO_ACCURATE_PROGRESS").is_some()
774 {
775 let before = module.clone();
776 let module = f(module)?;
777 let progress = MonoProgress::from_modules(&before, &module);
778 let added_binding_paths = added_binding_paths(&before, &module);
779 return Ok(MonoStep {
780 module,
781 progress,
782 demand_queue: DemandQueueProfile::default(),
783 principal_elaborate: SubstitutionSpecializeProfile::default(),
784 added_binding_paths,
785 added_specializations: Vec::new(),
786 });
787 }
788
789 let before = MonoStats::from_module(&module);
790 let before_paths = module
791 .bindings
792 .iter()
793 .map(|binding| binding.name.clone())
794 .collect::<HashSet<_>>();
795 let module = f(module)?;
796 let progress = MonoProgress::from_stats(before, MonoStats::from_module(&module));
797 let added_binding_paths = module
798 .bindings
799 .iter()
800 .filter(|binding| !before_paths.contains(&binding.name))
801 .map(|binding| binding.name.clone())
802 .collect();
803 Ok(MonoStep {
804 module,
805 progress,
806 demand_queue: DemandQueueProfile::default(),
807 principal_elaborate: SubstitutionSpecializeProfile::default(),
808 added_binding_paths,
809 added_specializations: Vec::new(),
810 })
811}
812
813fn run_tracked_infallible_pass<F>(module: Module, f: F) -> RuntimeResult<MonoStep>
814where
815 F: FnOnce(Module) -> Module,
816{
817 run_tracked_pass(module, |module| Ok(f(module)))
818}
819
820fn refine_module_types_for_mono(module: Module) -> RuntimeResult<MonoStep> {
821 let output = refine_module_types_with_report(module)?;
822 let progress = MonoProgress {
823 changed_bindings: output.report.changed_bindings,
824 changed_roots: output.report.changed_roots,
825 added_specializations: 0,
826 };
827 Ok(MonoStep {
828 module: output.module,
829 progress,
830 demand_queue: DemandQueueProfile::default(),
831 principal_elaborate: SubstitutionSpecializeProfile::default(),
832 added_binding_paths: Vec::new(),
833 added_specializations: Vec::new(),
834 })
835}
836
837fn added_binding_paths(before: &Module, after: &Module) -> Vec<typed_ir::Path> {
838 let before_paths = before
839 .bindings
840 .iter()
841 .map(|binding| binding.name.clone())
842 .collect::<HashSet<_>>();
843 after
844 .bindings
845 .iter()
846 .filter(|binding| !before_paths.contains(&binding.name))
847 .map(|binding| binding.name.clone())
848 .collect()
849}
850
851fn annotate_substitution_skip_reachability(
852 profile: &mut MonomorphizeProfile,
853 final_module: &Module,
854) {
855 let surviving_bindings = final_reachable_binding_paths(final_module);
856 for pass in &mut profile.passes {
857 for target in &mut pass.principal_elaborate.target_skips {
858 target.survives_final_prune = Some(surviving_bindings.contains(&target.target));
859 }
860 }
861}