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 }