Skip to main content

geam_core/plan/
execution.rs

1pub(crate) mod constant;
2mod explain;
3pub(crate) mod function;
4pub(crate) mod graph;
5pub(crate) mod host;
6mod lowering;
7pub(crate) mod runtime;
8pub(crate) mod type_;
9
10use self::constant::ProfiledConstantTable;
11#[cfg(test)]
12use self::constant::{ConstantId, ConstantValue};
13#[cfg(test)]
14use self::function::ExecutableFunction;
15#[cfg(test)]
16use self::function::{
17    BitArrayFunctionFunctionId, BitArrayFunctionId, BitArrayListFunctionId, BoolFunctionFunctionId,
18    BoolFunctionId, BoolListFunctionId, CustomFunctionFunctionId, CustomFunctionId,
19    CustomListFunctionId, ExecutionBitArrayFunctionBody, ExecutionBitArrayFunctionFunctionBody,
20    ExecutionBoolFunctionBody, ExecutionBoolFunctionFunctionBody,
21    ExecutionCoreListFunctionFunctionBody, ExecutionCustomFunctionBody,
22    ExecutionCustomFunctionFunctionBody, ExecutionFloatFunctionFunctionBody,
23    ExecutionFunctionFunctionFunctionBody, ExecutionGenericFunctionFunctionBody,
24    ExecutionIntFunctionBody, ExecutionIntFunctionFunctionBody, ExecutionNeverFunctionFunctionBody,
25    ExecutionNilFunctionBody, ExecutionNilFunctionFunctionBody,
26    ExecutionStringFunctionFunctionBody, ExecutionTupleFunctionBody,
27    ExecutionTupleFunctionFunctionBody, ExecutionUtfCodepointFunctionFunctionBody,
28    FloatFunctionFunctionId, FloatListFunctionId, FunctionFunctionFunctionId,
29    FunctionListFunctionId, GenericFunctionFunctionId, IntFunctionFunctionId, IntFunctionId,
30    IntListFunctionId, ListListFunctionId, NeverFunctionFunctionId, NilFunctionFunctionId,
31    NilFunctionId, NilListFunctionId, ParameterListFunctionId, ParameterListListFunctionId,
32    ProfiledListFunctionFunctionId, StringFunctionFunctionId, StringListFunctionId,
33    TupleFunctionFunctionId, TupleFunctionId, TupleListFunctionId, UtfCodepointFunctionFunctionId,
34    UtfCodepointListFunctionId,
35};
36use self::function::{
37    ExecutionGraphProfile, ExecutionProfile, FunctionLabelSource, FunctionTables,
38    ProfiledRuntimeFunctionId,
39};
40#[cfg(test)]
41use self::type_::{
42    CustomConstructorId, CustomConstructorRefinement, CustomTypeId, CustomValueShape,
43    CustomValueShapeId, FunctionListTypeId, FunctionType, ListListTypeId, ListStorageTypeId,
44    ListTypeId, TupleListTypeId, ValueShapeId, ValueType,
45};
46use self::type_::{CustomTypeTable, ExternalTypeTable, ListTypeTable, ValueShapeTable};
47use crate::host::HostProfile;
48use crate::plan::{HostedModulePlan, ModuleId, ModulePlan, SourceContext};
49use ecow::EcoString;
50pub use explain::ExecutionPlanExplanation;
51pub use host::{HostSpecializationError, HostSpecializationErrorReason};
52use std::convert::Infallible;
53
54pub struct ExecutionPlan {
55    program: ExecutionProgram<Infallible>,
56}
57
58pub(crate) struct LibraryFunctionEntry<Function> {
59    function: Function,
60    inputs: LibraryInputConstructions,
61}
62
63pub(crate) struct LibraryInputConstructions {
64    variants: Box<[[type_::CustomConstructorId; 2]]>,
65    lists: LibraryListConstructions,
66}
67
68#[derive(Default)]
69pub(crate) struct LibraryListConstructions {
70    pub(crate) ints: Vec<type_::IntListTypeId>,
71    pub(crate) floats: Vec<type_::FloatListTypeId>,
72    pub(crate) strings: Vec<type_::StringListTypeId>,
73    pub(crate) bit_arrays: Vec<type_::BitArrayListTypeId>,
74    pub(crate) utf_codepoints: Vec<type_::UtfCodepointListTypeId>,
75    pub(crate) customs: Vec<type_::CustomListTypeId>,
76    pub(crate) bools: Vec<type_::BoolListTypeId>,
77    pub(crate) nils: Vec<type_::NilListTypeId>,
78    pub(crate) tuples: Vec<type_::TupleListTypeId>,
79    pub(crate) lists: Vec<type_::ListListTypeId>,
80}
81
82pub(crate) struct LibraryFunctionEntries {
83    pub(crate) ints: Box<[LibraryFunctionEntry<function::IntFunctionId>]>,
84    pub(crate) floats: Box<[LibraryFunctionEntry<function::FloatFunctionId>]>,
85    pub(crate) strings: Box<[LibraryFunctionEntry<function::StringFunctionId>]>,
86    pub(crate) bit_arrays: Box<[LibraryFunctionEntry<function::BitArrayFunctionId>]>,
87    pub(crate) utf_codepoints: Box<[LibraryFunctionEntry<function::UtfCodepointFunctionId>]>,
88    pub(crate) customs: Box<[LibraryFunctionEntry<function::CustomFunctionId>]>,
89    pub(crate) bools: Box<[LibraryFunctionEntry<function::BoolFunctionId>]>,
90    pub(crate) nils: Box<[LibraryFunctionEntry<function::NilFunctionId>]>,
91    pub(crate) tuples: Box<[LibraryFunctionEntry<function::TupleFunctionId>]>,
92    pub(crate) lists: Box<[LibraryFunctionEntry<function::ProfiledListFunctionId<Infallible>>]>,
93}
94
95impl<Function> LibraryFunctionEntry<Function> {
96    pub(in crate::plan::execution) fn new(
97        function: Function,
98        inputs: LibraryInputConstructions,
99    ) -> Self {
100        Self { function, inputs }
101    }
102
103    pub(crate) fn function(&self) -> &Function {
104        &self.function
105    }
106
107    pub(crate) fn inputs(&self) -> &LibraryInputConstructions {
108        &self.inputs
109    }
110}
111
112impl LibraryInputConstructions {
113    pub(in crate::plan::execution) fn new(
114        variants: Vec<[type_::CustomConstructorId; 2]>,
115        lists: LibraryListConstructions,
116    ) -> Self {
117        Self {
118            variants: variants.into_boxed_slice(),
119            lists,
120        }
121    }
122
123    pub(crate) fn variants(&self) -> &[[type_::CustomConstructorId; 2]] {
124        &self.variants
125    }
126
127    pub(crate) fn lists(&self) -> &LibraryListConstructions {
128        &self.lists
129    }
130}
131
132pub struct HostedExecution<Profile: HostProfile> {
133    program: ExecutionProgram<host::HostedExecutionProfile>,
134    host_functions: host::HostFunctionTables<Profile>,
135    external_stores: Profile::ExternalStores,
136}
137
138pub(crate) struct ExecutionProgram<Profile: ExecutionProfile> {
139    common: ExecutionProgramCommon<Profile::Graph>,
140    functions: FunctionTables<Profile>,
141}
142
143struct ExecutionProgramCommon<Graph: ExecutionGraphProfile> {
144    root: ModuleId,
145    modules: Box<[ExecutionModuleContext]>,
146    main: ProfiledRuntimeFunctionId<Graph>,
147    constants: ProfiledConstantTable<Graph>,
148    list_types: ListTypeTable,
149    custom_types: CustomTypeTable,
150    external_types: ExternalTypeTable,
151    value_shapes: ValueShapeTable,
152}
153
154struct ExecutionModuleContext {
155    module: EcoString,
156    source_context: Option<SourceContext>,
157}
158
159impl ExecutionModuleContext {
160    fn new(module: EcoString, source_context: Option<SourceContext>) -> Self {
161        Self {
162            module,
163            source_context,
164        }
165    }
166}
167
168impl explain::Explain for ExecutionPlan {
169    fn write_explanation(&self, context: &mut explain::ExplainContext<'_, '_>) {
170        context.push_str("module ");
171        context.push_str(self.module());
172        context.push_str("\nmain ");
173        self.program
174            .common
175            .main
176            .function_label()
177            .write(context.output());
178        context.push('\n');
179        context.write(&self.program.functions);
180        context.write(&self.program.common.constants);
181    }
182}
183
184impl<Profile: HostProfile> explain::Explain for HostedExecution<Profile> {
185    fn write_explanation(&self, context: &mut explain::ExplainContext<'_, '_>) {
186        context.push_str("module ");
187        context.push_str(&self.program.common.modules[self.program.common.root.index()].module);
188        context.push_str("\nmain ");
189        self.program
190            .common
191            .main
192            .function_label()
193            .write(context.output());
194        context.push('\n');
195        context.write(&function::HostedFunctionTablesExplanation::new(
196            &self.program.functions,
197            &self.host_functions,
198        ));
199        context.write(&self.program.common.constants);
200    }
201}
202
203impl ExecutionPlan {
204    pub fn from_module_plan(module_plan: ModulePlan) -> Self {
205        Self {
206            program: lowering::lower(module_plan),
207        }
208    }
209
210    pub(crate) fn from_library_plan(
211        module_plan: crate::plan::LibraryModulePlan,
212        first: crate::plan::LibraryEntry,
213        remaining: Vec<crate::plan::LibraryEntry>,
214    ) -> (Self, LibraryFunctionEntries) {
215        let (program, entries) = lowering::lower_library(module_plan, first, remaining);
216        (Self { program }, entries)
217    }
218
219    pub fn module(&self) -> &EcoString {
220        &self.program.common.modules[self.program.common.root.index()].module
221    }
222
223    pub fn source_context(&self) -> Option<&SourceContext> {
224        self.program.common.modules[self.program.common.root.index()]
225            .source_context
226            .as_ref()
227    }
228
229    pub fn explain(&self) -> ExecutionPlanExplanation<'_> {
230        ExecutionPlanExplanation::new(self)
231    }
232}
233
234impl<Profile: HostProfile> HostedExecution<Profile> {
235    /// Seals all entry-reachable host specializations into executable storage.
236    ///
237    /// A linked but unused provider does not participate in sealing.
238    pub fn try_from_module_plan(
239        module_plan: HostedModulePlan<Profile>,
240    ) -> Result<Self, HostSpecializationError> {
241        let (program, host_functions) = lowering::lower_hosted(module_plan)?;
242        Ok(Self {
243            program,
244            host_functions,
245            external_stores: Profile::ExternalStores::default(),
246        })
247    }
248
249    pub(crate) fn try_from_library_plan(
250        module_plan: crate::plan::HostedLibraryModulePlan<Profile>,
251        first: crate::plan::LibraryEntry,
252        remaining: Vec<crate::plan::LibraryEntry>,
253    ) -> Result<(Self, LibraryFunctionEntries), HostSpecializationError> {
254        let (program, host_functions, entries) =
255            lowering::lower_hosted_library(module_plan, first, remaining)?;
256        Ok((
257            Self {
258                program,
259                host_functions,
260                external_stores: Profile::ExternalStores::default(),
261            },
262            entries,
263        ))
264    }
265
266    pub fn run_main(
267        &self,
268        state: &mut Profile::RunState,
269        echo: &mut dyn crate::EchoSink,
270    ) -> Result<crate::Value, crate::ExecutionError> {
271        crate::runtime::run_hosted_main(self, state, echo)
272    }
273
274    pub fn explain(&self) -> ExecutionPlanExplanation<'_> {
275        ExecutionPlanExplanation::new_hosted(self)
276    }
277
278    pub(crate) fn host_value_function<Body>(
279        &self,
280        id: &host::HostFunctionId<Body>,
281    ) -> &host::HostedValueFunction<Profile>
282    where
283        Body: function::ExecutionFunctionBody,
284    {
285        self.host_functions.value(id)
286    }
287
288    pub(crate) fn host_never_function(
289        &self,
290        id: host::HostNeverFunctionId,
291    ) -> &host::HostedNeverFunction<Profile> {
292        self.host_functions.never(id)
293    }
294
295    pub(crate) fn external_stores(&self) -> &Profile::ExternalStores {
296        &self.external_stores
297    }
298}
299
300#[cfg(test)]
301impl ExecutionPlan {
302    pub(crate) fn main_runtime(&self) -> self::function::RuntimeFunctionId {
303        self.program.common.main.runtime_id()
304    }
305
306    pub(crate) fn constant<Return: ConstantValue>(
307        &self,
308        id: ConstantId<Return>,
309    ) -> &self::constant::ProfiledConstantProgram<Return, Infallible> {
310        self.program.common.constants.get(id)
311    }
312
313    pub(crate) fn list_value_type(&self, id: ListTypeId) -> crate::plan::ValueType {
314        self.program.common.list_types.list_value_type(
315            id,
316            &self.program.common.custom_types,
317            &self.program.common.external_types,
318        )
319    }
320
321    #[cfg(test)]
322    pub(crate) fn list_storage_type(&self, id: ListTypeId) -> ListStorageTypeId {
323        self.program.common.list_types.storage_type(id)
324    }
325
326    pub(crate) fn tuple_list_item_type(&self, id: TupleListTypeId) -> Vec<crate::plan::ValueType> {
327        self.program.common.list_types.tuple_item_type(
328            id,
329            &self.program.common.custom_types,
330            &self.program.common.external_types,
331        )
332    }
333
334    pub(crate) fn nested_list_item_type(&self, id: ListListTypeId) -> crate::plan::ValueType {
335        self.program.common.list_types.nested_list_item_type(
336            id,
337            &self.program.common.custom_types,
338            &self.program.common.external_types,
339        )
340    }
341
342    pub(crate) fn function_list_item_type(
343        &self,
344        id: FunctionListTypeId,
345    ) -> crate::plan::FunctionType {
346        self.program.common.list_types.function_item_type(
347            id,
348            &self.program.common.custom_types,
349            &self.program.common.external_types,
350        )
351    }
352
353    pub(crate) fn function_type(&self, type_: &FunctionType) -> crate::plan::FunctionType {
354        self.program.common.list_types.function_type(
355            type_,
356            &self.program.common.custom_types,
357            &self.program.common.external_types,
358        )
359    }
360
361    pub(crate) fn custom_value_type(&self, id: CustomTypeId) -> crate::plan::CustomType {
362        self.program.common.custom_types.value_type(id)
363    }
364
365    #[cfg(test)]
366    pub(crate) fn custom_shape_refinement(
367        &self,
368        shape: &CustomValueShape,
369    ) -> CustomConstructorRefinement {
370        self.program
371            .common
372            .value_shapes
373            .custom(shape.shape_id())
374            .constructor()
375    }
376
377    #[cfg(test)]
378    pub(crate) fn custom_shape_value_type(
379        &self,
380        shape: &CustomValueShape,
381    ) -> crate::plan::CustomType {
382        self.custom_shape_type(shape.shape_id())
383    }
384
385    #[cfg(test)]
386    fn custom_shape_type(&self, id: CustomValueShapeId) -> crate::plan::CustomType {
387        let shape = self.program.common.value_shapes.custom(id);
388        let nominal = self.program.common.custom_types.value_type(shape.type_id());
389        crate::plan::CustomType::new(
390            nominal.type_name().clone(),
391            shape
392                .arguments()
393                .iter()
394                .map(|argument| {
395                    self.program.common.list_types.value_type(
396                        self.program.common.value_shapes.value_type(*argument),
397                        &self.program.common.custom_types,
398                        &self.program.common.external_types,
399                    )
400                })
401                .collect(),
402        )
403    }
404
405    pub(crate) fn shape_value_type(&self, id: ValueShapeId) -> ValueType {
406        self.program.common.value_shapes.value_type(id).clone()
407    }
408
409    pub(crate) fn custom_constructor(
410        &self,
411        id: CustomConstructorId,
412    ) -> &type_::CustomConstructorDescriptor {
413        self.program.common.custom_types.constructor(id)
414    }
415
416    #[cfg(test)]
417    pub(crate) fn custom_constructor_id(
418        &self,
419        type_index: usize,
420        constructor_index: usize,
421    ) -> CustomConstructorId {
422        self.program
423            .common
424            .custom_types
425            .constructor_id(type_index, constructor_index)
426    }
427
428    pub(crate) fn int_function(
429        &self,
430        id: IntFunctionId,
431    ) -> &ExecutableFunction<ExecutionIntFunctionBody<Infallible>> {
432        self.program.functions.int_function(id)
433    }
434
435    pub(crate) fn bit_array_function(
436        &self,
437        id: BitArrayFunctionId,
438    ) -> &ExecutableFunction<ExecutionBitArrayFunctionBody<Infallible>> {
439        self.program.functions.bit_array_function(id)
440    }
441
442    pub(crate) fn custom_function(
443        &self,
444        id: CustomFunctionId,
445    ) -> &ExecutableFunction<ExecutionCustomFunctionBody<Infallible>> {
446        self.program.functions.custom_function(id)
447    }
448
449    #[cfg(test)]
450    pub(crate) fn custom_function_id(&self, index: usize) -> CustomFunctionId {
451        self.program.functions.custom_function_id(index)
452    }
453
454    pub(crate) fn bool_function(
455        &self,
456        id: BoolFunctionId,
457    ) -> &ExecutableFunction<ExecutionBoolFunctionBody<Infallible>> {
458        self.program.functions.bool_function(id)
459    }
460
461    pub(crate) fn nil_function(
462        &self,
463        id: NilFunctionId,
464    ) -> &ExecutableFunction<ExecutionNilFunctionBody<Infallible>> {
465        self.program.functions.nil_function(id)
466    }
467
468    pub(crate) fn tuple_function(
469        &self,
470        id: TupleFunctionId,
471    ) -> &ExecutableFunction<ExecutionTupleFunctionBody<Infallible>> {
472        self.program.functions.tuple_function(id)
473    }
474
475    #[cfg(test)]
476    pub(crate) fn parameter_list_function_id(&self, index: usize) -> ParameterListFunctionId {
477        self.program.functions.parameter_list_function_id(index)
478    }
479
480    #[cfg(test)]
481    pub(crate) fn parameter_list_list_function_id(
482        &self,
483        index: usize,
484    ) -> ParameterListListFunctionId {
485        self.program
486            .functions
487            .parameter_list_list_function_id(index)
488    }
489
490    #[cfg(test)]
491    pub(crate) fn int_list_function_id(&self, index: usize) -> IntListFunctionId {
492        self.program.functions.int_list_function_id(index)
493    }
494
495    #[cfg(test)]
496    pub(crate) fn string_list_function_id(&self, index: usize) -> StringListFunctionId {
497        self.program.functions.string_list_function_id(index)
498    }
499
500    #[cfg(test)]
501    pub(crate) fn bit_array_list_function_id(&self, index: usize) -> BitArrayListFunctionId {
502        self.program.functions.bit_array_list_function_id(index)
503    }
504
505    #[cfg(test)]
506    pub(crate) fn utf_codepoint_list_function_id(
507        &self,
508        index: usize,
509    ) -> UtfCodepointListFunctionId {
510        self.program.functions.utf_codepoint_list_function_id(index)
511    }
512
513    #[cfg(test)]
514    pub(crate) fn custom_list_function_id(&self, index: usize) -> CustomListFunctionId {
515        self.program.functions.custom_list_function_id(index)
516    }
517
518    #[cfg(test)]
519    pub(crate) fn float_list_function_id(&self, index: usize) -> FloatListFunctionId {
520        self.program.functions.float_list_function_id(index)
521    }
522
523    #[cfg(test)]
524    pub(crate) fn bool_list_function_id(&self, index: usize) -> BoolListFunctionId {
525        self.program.functions.bool_list_function_id(index)
526    }
527
528    #[cfg(test)]
529    pub(crate) fn nil_list_function_id(&self, index: usize) -> NilListFunctionId {
530        self.program.functions.nil_list_function_id(index)
531    }
532
533    #[cfg(test)]
534    pub(crate) fn tuple_list_function_id(&self, index: usize) -> TupleListFunctionId {
535        self.program.functions.tuple_list_function_id(index)
536    }
537
538    #[cfg(test)]
539    pub(crate) fn list_list_function_id(&self, index: usize) -> ListListFunctionId {
540        self.program.functions.list_list_function_id(index)
541    }
542
543    #[cfg(test)]
544    pub(crate) fn function_list_function_id(&self, index: usize) -> FunctionListFunctionId {
545        self.program.functions.function_list_function_id(index)
546    }
547
548    pub(crate) fn int_function_function(
549        &self,
550        id: IntFunctionFunctionId,
551    ) -> &ExecutableFunction<ExecutionIntFunctionFunctionBody<Infallible>> {
552        self.program.functions.int_function_function(id)
553    }
554
555    pub(crate) fn float_function_function(
556        &self,
557        id: FloatFunctionFunctionId,
558    ) -> &ExecutableFunction<ExecutionFloatFunctionFunctionBody<Infallible>> {
559        self.program.functions.float_function_function(id)
560    }
561
562    pub(crate) fn string_function_function(
563        &self,
564        id: StringFunctionFunctionId,
565    ) -> &ExecutableFunction<ExecutionStringFunctionFunctionBody<Infallible>> {
566        self.program.functions.string_function_function(id)
567    }
568
569    pub(crate) fn bit_array_function_function(
570        &self,
571        id: BitArrayFunctionFunctionId,
572    ) -> &ExecutableFunction<ExecutionBitArrayFunctionFunctionBody<Infallible>> {
573        self.program.functions.bit_array_function_function(id)
574    }
575
576    pub(crate) fn utf_codepoint_function_function(
577        &self,
578        id: UtfCodepointFunctionFunctionId,
579    ) -> &ExecutableFunction<ExecutionUtfCodepointFunctionFunctionBody<Infallible>> {
580        self.program.functions.utf_codepoint_function_function(id)
581    }
582
583    pub(crate) fn custom_function_function(
584        &self,
585        id: &CustomFunctionFunctionId,
586    ) -> &ExecutableFunction<ExecutionCustomFunctionFunctionBody<Infallible>> {
587        self.program.functions.custom_function_function(id)
588    }
589
590    pub(crate) fn generic_function_function(
591        &self,
592        id: &GenericFunctionFunctionId,
593    ) -> &ExecutableFunction<ExecutionGenericFunctionFunctionBody<Infallible>> {
594        self.program.functions.generic_function_function(id)
595    }
596
597    pub(crate) fn never_function_function(
598        &self,
599        id: &NeverFunctionFunctionId,
600    ) -> &ExecutableFunction<ExecutionNeverFunctionFunctionBody<Infallible>> {
601        self.program.functions.never_function_function(id)
602    }
603
604    pub(crate) fn bool_function_function(
605        &self,
606        id: BoolFunctionFunctionId,
607    ) -> &ExecutableFunction<ExecutionBoolFunctionFunctionBody<Infallible>> {
608        self.program.functions.bool_function_function(id)
609    }
610
611    pub(crate) fn nil_function_function(
612        &self,
613        id: NilFunctionFunctionId,
614    ) -> &ExecutableFunction<ExecutionNilFunctionFunctionBody<Infallible>> {
615        self.program.functions.nil_function_function(id)
616    }
617
618    pub(crate) fn tuple_function_function(
619        &self,
620        id: TupleFunctionFunctionId,
621    ) -> &ExecutableFunction<ExecutionTupleFunctionFunctionBody<Infallible>> {
622        self.program.functions.tuple_function_function(id)
623    }
624
625    pub(crate) fn core_list_function_function(
626        &self,
627        id: &ProfiledListFunctionFunctionId<Infallible>,
628    ) -> &ExecutableFunction<ExecutionCoreListFunctionFunctionBody<Infallible>> {
629        self.program.functions.core_list_function_function(id)
630    }
631
632    pub(crate) fn function_function_function(
633        &self,
634        id: &FunctionFunctionFunctionId,
635    ) -> &ExecutableFunction<ExecutionFunctionFunctionFunctionBody<Infallible>> {
636        self.program.functions.function_function_function(id)
637    }
638
639    #[cfg(test)]
640    pub(crate) fn function_function_function_id(&self, index: usize) -> FunctionFunctionFunctionId {
641        self.program.functions.function_function_function_id(index)
642    }
643}
644
645#[cfg(test)]
646mod tests {
647    use super::HostedExecution;
648    use crate::plan::execution::explain;
649    use crate::plan::execution::function::IntFunctionId;
650    use crate::{
651        HostModule, HostProviderSet, ModuleSource, PackageSource, compile_typed_host_program,
652        compile_typed_module, plan_host_program, plan_module,
653    };
654    use num_bigint::BigInt;
655    use std::convert::Infallible;
656
657    #[test]
658    fn plain_execution_program_keeps_host_targets_uninhabited() {
659        let typed = compile_typed_module("main", "main.gleam", "pub fn main() { 1 }")
660            .expect("source should compile");
661        let plan = plan_module(typed).expect("source should plan");
662        let execution = super::ExecutionPlan::from_module_plan(plan);
663        let function: &super::ExecutableFunction<
664            super::function::ExecutionIntFunctionBody<Infallible>,
665        > = execution.program.functions.int_function(IntFunctionId(0));
666
667        assert_eq!(function.body().block_graph().blocks().len(), 1);
668    }
669
670    #[test]
671    fn exposes_the_root_module_and_source_context() {
672        let source = "pub fn main() { 1 }";
673        let typed =
674            compile_typed_module("sample", "sample.gleam", source).expect("source should compile");
675        let context = crate::SourceContext::new("sample.gleam", source);
676        let module =
677            crate::plan_module_with_source(typed, context.clone()).expect("source should plan");
678        let execution = super::ExecutionPlan::from_module_plan(module);
679
680        assert_eq!(execution.module(), "sample");
681        assert_eq!(execution.source_context(), Some(&context));
682    }
683
684    #[test]
685    fn writes_the_complete_execution_plan() {
686        let source = "pub fn main() { 1 }";
687        let expected = concat!(
688            "module main\n",
689            "main int#0\n",
690            "\n",
691            "function int#0\n",
692            "  entry b0 params=[] captures=[]\n",
693            "  block b0 params=[]\n",
694            "    %int#0:shape#0(Int) = int.value 1\n",
695            "    return %int#0\n",
696        );
697
698        explain::assert_rendered(source, expected, |plan, output| {
699            let mut context = explain::ExplainContext::new(plan, output);
700            context.write(plan);
701        });
702    }
703
704    #[test]
705    fn writes_the_complete_hosted_execution_plan() {
706        let math = HostModule::new("host_support", "host/math")
707            .expect("host module should be valid")
708            .with_function("add", <BigInt as std::ops::Add>::add)
709            .expect("host function should be valid");
710        let hosts = HostProviderSet::new([math]).expect("host modules should be unique");
711        let source = "import host/math\npub fn main() { math.add(1, 2) }";
712        let typed = compile_typed_host_program(
713            "application",
714            "main",
715            [PackageSource::new(
716                "application",
717                ["host_support"],
718                [ModuleSource::new("main", "main.gleam", source)],
719            )],
720            hosts,
721        )
722        .expect("host source should compile");
723        let plan = plan_host_program(typed).expect("host source should plan");
724        let execution =
725            HostedExecution::try_from_module_plan(plan).expect("hosted execution should seal");
726        let expected = concat!(
727            "module main\n",
728            "main int#0\n",
729            "\nfunction int#0\n",
730            "  entry b0 params=[] captures=[]\n",
731            "  block b0 params=[]\n",
732            "    %int#0:shape#0(Int) = int.value 1\n",
733            "    %int#1:shape#0(Int) = int.value 2\n",
734            "    tail int#1 args=[%int#0, %int#1]\n",
735            "\nfunction int#1\n",
736            "  host host_support::host/math.add signature=fn(Int, Int) -> Int\n",
737        );
738
739        assert_eq!(execution.explain().to_string(), expected);
740    }
741}