lib/choir/src/composition/module/composition.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const choir = @import("../../root.zig");
3 const composition = @import("../root.zig");
4 const model = @import("root.zig");
5 const verification = @import("verification/root.zig");
6
7 const Allocator = std.mem.Allocator;
8 const Artifact = choir.backends.artifact.Artifact;
9 const backends = choir.backends;
10 const bytecode = choir.bytecode;
11 const product = choir.product;
12 const source = composition.source;
13
14 pub const VerificationError = verification.Error;
15
16 pub const DiagnosticPath = struct {
17 source: *const source.Provenance,
18 variant: *const model.CompositionVariant,
19 partition: *const source.Partition,
20 pipeline_input: product.ProductKey,
21 fragment: *const model.Fragment,
22 fragment_export: *const model.Export,
23 call_site: *const model.CallSite,
24 artifact: *const Artifact,
25 };
26
27 /// Owns the serializable whole-program product: source contracts, fragments, and variants.
28 /// `init` clones its source module so the result has one independent lifetime.
29 pub const CompositionModule = struct {
30 allocator: Allocator,
31 source_module: source.PartitionedModule,
32 fragments: std.ArrayList(model.Fragment) = .empty,
33 variants: std.ArrayList(model.CompositionVariant) = .empty,
34
35 pub fn init(allocator: Allocator, partitioned: *const source.PartitionedModule) Allocator.Error!CompositionModule {
36 return .{
37 .allocator = allocator,
38 .source_module = try partitioned.clone(allocator),
39 };
40 }
41
42 pub fn deinit(self: *CompositionModule) void {
43 var variant_index = self.variants.items.len;
44 while (variant_index != 0) {
45 variant_index -= 1;
46 self.variants.items[variant_index].deinit(self.allocator);
47 }
48 self.variants.deinit(self.allocator);
49 var fragment_index = self.fragments.items.len;
50 while (fragment_index != 0) {
51 fragment_index -= 1;
52 self.fragments.items[fragment_index].deinit(self.allocator);
53 }
54 self.fragments.deinit(self.allocator);
55 self.source_module.deinit();
56 self.* = undefined;
57 }
58
59 pub fn addFragment(self: *CompositionModule, spec: model.FragmentSpec) (Allocator.Error || VerificationError)!void {
60 if (self.fragment(spec.id) != null) return error.DuplicateFragment;
61 const partition_value = self.source_module.partition(spec.partition) orelse return error.MissingPartition;
62 if (partition_value.pipeline != spec.pipeline) return error.PipelineMismatch;
63 if (!partition_value.product.eql(spec.pipeline_input.ref)) return error.ProductIdentityMismatch;
64 try verification.verifyFragmentSpec(spec);
65 var fragment_value = try model.Fragment.init(self.allocator, spec);
66 errdefer fragment_value.deinit(self.allocator);
67 try self.fragments.append(self.allocator, fragment_value);
68 }
69
70 pub fn addVariant(self: *CompositionModule, spec: model.CompositionVariantSpec) (Allocator.Error || VerificationError)!void {
71 try verification.verifyCompositionVariantSpec(self, spec);
72 var variant_value = try model.CompositionVariant.init(self.allocator, spec);
73 errdefer variant_value.deinit(self.allocator);
74 try self.variants.append(self.allocator, variant_value);
75 }
76
77 pub fn verify(self: *const CompositionModule) VerificationError!void {
78 try verification.verifyModule(self);
79 }
80
81 pub fn fragment(self: *const CompositionModule, id: source.FragmentId) ?*const model.Fragment {
82 for (self.fragments.items) |*fragment_value| {
83 if (fragment_value.id.value == id.value) return fragment_value;
84 }
85 return null;
86 }
87
88 pub fn variant(self: *const CompositionModule, id: model.CompositionVariantId) ?*const model.CompositionVariant {
89 for (self.variants.items) |*variant_value| {
90 if (variant_value.id.value == id.value) return variant_value;
91 }
92 return null;
93 }
94
95 pub fn selectVariant(self: *const CompositionModule, choice: model.TargetChoiceSpec) ?*const model.CompositionVariant {
96 for (self.variants.items) |*variant_value| {
97 if (variant_value.choice.eql(choice)) return variant_value;
98 }
99 return null;
100 }
101
102 pub fn diagnosticPath(
103 self: *const CompositionModule,
104 variant_id: model.CompositionVariantId,
105 id: source.CallSiteId,
106 ) VerificationError!DiagnosticPath {
107 const variant_value = self.variant(variant_id) orelse return error.MissingCompositionVariant;
108 const call_site = variant_value.callSite(id) orelse return error.MissingSemanticCallSite;
109 const fragment_value = self.fragment(call_site.callee) orelse return error.MissingFragment;
110 const partition_value = self.source_module.partition(fragment_value.partition) orelse return error.MissingPartition;
111 const semantic = self.source_module.callSite(id) orelse return error.MissingSemanticCallSite;
112 const export_value = fragment_value.exportByName(call_site.callee_export) orelse return error.MissingExport;
113 const artifact_value = artifactProviding(fragment_value, export_value.symbol) orelse return error.MissingProvidedSymbol;
114 return .{
115 .source = &semantic.provenance,
116 .variant = variant_value,
117 .partition = partition_value,
118 .pipeline_input = fragment_value.pipeline_input,
119 .fragment = fragment_value,
120 .fragment_export = export_value,
121 .call_site = call_site,
122 .artifact = artifact_value,
123 };
124 }
125
126 pub fn bytecodeResourceCount(self: *const CompositionModule) usize {
127 var count: usize = 0;
128 for (self.fragments.items) |fragment_value| {
129 for (fragment_value.artifacts) |artifact_value| count += backends.artifact.resource.count(artifact_value);
130 }
131 return count;
132 }
133
134 pub fn writeBytecodeResources(
135 self: *const CompositionModule,
136 out: []bytecode.Resource,
137 ) backends.artifact.resource.Error![]const bytecode.Resource {
138 const required = self.bytecodeResourceCount();
139 if (out.len < required) return error.NoSpaceLeft;
140 var offset: usize = 0;
141 for (self.fragments.items) |fragment_value| {
142 for (fragment_value.artifacts) |artifact_value| {
143 const resources = try backends.artifact.resource.write(artifact_value, out[offset..]);
144 offset += resources.len;
145 }
146 }
147 return out[0..offset];
148 }
149 };
150
151 fn artifactProviding(fragment_value: *const model.Fragment, symbol: []const u8) ?*const Artifact {
152 for (fragment_value.artifacts) |*artifact_value| {
153 if (artifact_value.linkage.hasProvided(symbol)) return artifact_value;
154 }
155 return null;
156 }