lib/choir/src/composition/module/verification/variant.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const model = @import("../root.zig");
2 const source = @import("../../source/root.zig");
3 const verification = @import("root.zig");
4
5 pub fn verifySpec(
6 module: *const model.CompositionModule,
7 spec: model.CompositionVariantSpec,
8 ) verification.Error!void {
9 if (spec.choice.target.stage.len == 0) return error.EmptyTargetIdentity;
10 if (spec.choice.policy.stage.len == 0) return error.EmptyPolicyIdentity;
11 if (module.variant(spec.id) != null) return error.DuplicateCompositionVariant;
12 if (module.selectVariant(spec.choice) != null) return error.DuplicateTargetChoice;
13 if (spec.fragments.len != module.source_module.partitions.items.len) return error.MissingPartitionFragment;
14 for (spec.fragments, 0..) |fragment_id, index| {
15 const fragment_value = module.fragment(fragment_id) orelse return error.MissingFragment;
16 for (spec.fragments[0..index]) |previous_id| {
17 if (previous_id.value == fragment_id.value) return error.DuplicateFragment;
18 const previous = module.fragment(previous_id) orelse return error.MissingFragment;
19 if (previous.partition.value == fragment_value.partition.value) return error.DuplicatePartitionFragment;
20 }
21 }
22 if (spec.call_sites.len != module.source_module.call_sites.items.len) return error.MissingSemanticCallSite;
23 for (spec.call_sites, 0..) |call_spec, index| {
24 for (spec.call_sites[0..index]) |previous| {
25 if (previous.id.value == call_spec.id.value) return error.DuplicateCallSite;
26 }
27 if (!containsFragment(spec.fragments, call_spec.caller) or
28 !containsFragment(spec.fragments, call_spec.callee))
29 {
30 return error.FragmentOutsideVariant;
31 }
32 try verification.call.verifySpec(module, call_spec);
33 }
34 }
35
36 pub fn verifyValue(
37 module: *const model.CompositionModule,
38 variant_value: *const model.CompositionVariant,
39 ) verification.Error!void {
40 if (variant_value.choice.target.stage.len == 0) return error.EmptyTargetIdentity;
41 if (variant_value.choice.policy.stage.len == 0) return error.EmptyPolicyIdentity;
42 if (variant_value.fragments.len != module.source_module.partitions.items.len) return error.MissingPartitionFragment;
43 for (variant_value.fragments, 0..) |fragment_id, index| {
44 const fragment_value = module.fragment(fragment_id) orelse return error.MissingFragment;
45 for (variant_value.fragments[0..index]) |previous_id| {
46 if (previous_id.value == fragment_id.value) return error.DuplicateFragment;
47 const previous = module.fragment(previous_id) orelse return error.MissingFragment;
48 if (previous.partition.value == fragment_value.partition.value) return error.DuplicatePartitionFragment;
49 }
50 }
51 if (variant_value.call_sites.len != module.source_module.call_sites.items.len) return error.MissingSemanticCallSite;
52 for (variant_value.call_sites, 0..) |*call_site, index| {
53 for (variant_value.call_sites[0..index]) |previous| {
54 if (previous.id.value == call_site.id.value) return error.DuplicateCallSite;
55 }
56 if (!variant_value.containsFragment(call_site.caller) or
57 !variant_value.containsFragment(call_site.callee))
58 {
59 return error.FragmentOutsideVariant;
60 }
61 try verification.call.verifySpec(module, .{
62 .id = call_site.id,
63 .name = call_site.name,
64 .caller = call_site.caller,
65 .caller_export = call_site.caller_export,
66 .runtime_import = call_site.runtime_import,
67 .callee = call_site.callee,
68 .callee_export = call_site.callee_export,
69 .inputs = call_site.inputs,
70 .outputs = call_site.outputs,
71 .abi_version = call_site.abi_version,
72 .provenance = .{
73 .path = call_site.provenance.path,
74 .symbol = call_site.provenance.symbol,
75 .line = call_site.provenance.line,
76 .column = call_site.provenance.column,
77 },
78 });
79 }
80 }
81
82 fn containsFragment(fragments: []const source.FragmentId, id: source.FragmentId) bool {
83 for (fragments) |fragment_id| {
84 if (fragment_id.value == id.value) return true;
85 }
86 return false;
87 }