Class: Rigor::FlowContribution
- Inherits:
-
Object
- Object
- Rigor::FlowContribution
- Defined in:
- lib/rigor/flow_contribution.rb,
lib/rigor/flow_contribution/fact.rb,
lib/rigor/flow_contribution/merger.rb,
lib/rigor/flow_contribution/element.rb,
lib/rigor/flow_contribution/conflict.rb,
lib/rigor/flow_contribution/merge_result.rb
Overview
The public packaging of a flow contribution at a single call edge. Plugins, ‘RBS::Extended` annotations, and built-in narrowing rules all hand the analyzer this same bundle shape; the inference engine merges contributions through the policy described in [ADR-2 § “Plugin Contribution Merging”](../../docs/adr/2-extension-api.md) rather than letting any one source override another silently.
Eight content slots plus a Provenance block. A slot left as ‘nil` (or, for collection-shaped slots, an empty collection) means the contribution does not assert anything in that dimension; the merge policy treats it as absent.
The struct is the only shape plugin authors need to learn. Richer or more permissive shapes are not part of the first public contract — see ADR-2 § “Flow Contribution Bundle” for the binding definition.
The element-list flattening (‘to_element_list`) ADR-2 mentions is intentionally not implemented yet: it is the analyzer-internal bookkeeping behind the merge policy and will land alongside the plugin contribution merger in v0.1.0. Plugin authors should not rely on it.
Defined Under Namespace
Modules: Merger Classes: Conflict, Element, Fact, MergeResult, Provenance
Constant Summary collapse
- SLOT_NAMES =
%i[ return_type truthy_facts falsey_facts post_return_facts mutations invalidations exceptional role_conformance ].freeze
- FACT_VALID_TARGET_KINDS =
Canonical slot payload for the four edge-aware fact slots (‘truthy_facts`, `falsey_facts`, `post_return_facts`, plus the equivalent under `mutations` / `invalidations` / `role_conformance` once those carriers grow Fact-shaped variants).
ADR-7 § “Slice 4-A” pins this object as the **single canonical translation target** for the four parallel contribution carriers the engine has carried so far:
-
Built-in narrowing rules’ direct fact emission (Inference::Narrowing#predicate_scopes).
-
RBS::Extended ‘predicate-if-*` directives (`Rigor::RbsExtended::PredicateEffect`).
-
RBS::Extended ‘assert*` directives (`Rigor::RbsExtended::AssertEffect`).
-
Future plugin contributions (slice 5 emission protocol).
Each of those four carriers translates to / from Fact at its boundary; downstream of #to_element_list and Merger.merge, every slot payload is a Fact (or a value that the merger compares by equality and never inspects). The typed ‘RbsExtended::*Effect` carriers stay internal to the parser side — they hold the source-text shape, but lose their identity at the `read_flow_contribution` boundary.
## Field set
-
‘target_kind`: `:parameter` (call-site argument) or `:self` (receiver). Future slices may extend the set (`:local`, `:ivar`, `:result`); the merger is agnostic to the concrete kinds and only requires equality.
-
‘target_name`: a `Symbol`. For `:parameter` it’s the declared parameter name. For ‘:self` it is the literal `:self` symbol so the field stays non-nil and the merge key is well-defined.
-
‘type`: a `Rigor::Type::*` (Nominal, Refined, IntegerRange, Difference, …) the fact narrows the target toward (when `negative` is false) or away from (when `negative` is true).
-
‘negative`: `true` for the `~T` negation form (`predicate-if-true x is ~Integer`), `false` for the plain positive form. Mirrors the `negative` field on `PredicateEffect` / `AssertEffect`.
The ‘target` accessor returns `:self` for self-targeted facts and `[:parameter, name]` otherwise — that’s the value Element#target keys on, so two facts that narrow the same parameter from different contribution sources land in the same merge bucket.
-
%i[parameter self].freeze
- ELEMENT_VALID_EDGES =
Tagged element flattening of a Rigor::FlowContribution bundle —the analyzer-internal representation [ADR-2 § “Flow Contribution Bundle”](../../../docs/adr/2-extension-api.md) routes through the Merger.
The flattening is **mechanical, deterministic, and round- trippable** with the bundle: every non-empty slot expands into one or more elements keyed by ‘(target, edge, kind)`, and an array of elements rebuilds an equivalent bundle when routed through `Merger.merge`.
Plugin authors should not depend on the element shape — the bundle is the public contract; the element list is the implementation surface the merge policy operates over.
%i[normal truthy falsey post_return exceptional].freeze
- ELEMENT_VALID_KINDS =
%i[ return_type truthy_fact falsey_fact post_return_fact mutation invalidation exception role ].freeze
- CONFLICT_VALID_REASONS =
Records a contradiction between two or more flow contributions detected during Merger.merge. Carried on MergeResult#conflicts so the analyzer / formatter can surface a ‘:contribution_merge` diagnostic per ADR-2 § “Plugin Contribution Merging”.
ADR-2 § “Plugin Contribution Merging” rules out first-wins / last-wins behaviour: when contributions conflict, both sources are reported and the merger falls back to the nearest non- conflicting higher-tier (or default) value for the affected ‘(target, edge, kind)` slot. The conflict object is the carrier of that report.
Slice-3 conflict reasons:
-
‘:return_type_collapse` — two return-type contributions intersect to `bot`.
-
‘:exceptional_disagreement` — two contributions assert incompatible non-`nil` exceptional effects.
-
‘:lower_tier_contradiction` — a lower-tier contribution would weaken or contradict a higher-tier proof.
-
%i[ return_type_collapse exceptional_disagreement lower_tier_contradiction ].freeze
Instance Method Summary collapse
- #==(other) ⇒ Object (also: #eql?)
-
#empty? ⇒ Boolean
True when every content slot is unset (nil or an empty collection).
- #hash ⇒ Object
-
#initialize(return_type: nil, truthy_facts: nil, falsey_facts: nil, post_return_facts: nil, mutations: nil, invalidations: nil, exceptional: nil, role_conformance: nil, provenance: Provenance.builtin) ⇒ FlowContribution
constructor
rubocop:disable Metrics/ParameterLists.
-
#to_element_list ⇒ Array<Element>
Flattens this bundle into a tagged element list keyed by ‘(target, edge, kind)`.
- #to_h ⇒ Object
Constructor Details
#initialize(return_type: nil, truthy_facts: nil, falsey_facts: nil, post_return_facts: nil, mutations: nil, invalidations: nil, exceptional: nil, role_conformance: nil, provenance: Provenance.builtin) ⇒ FlowContribution
rubocop:disable Metrics/ParameterLists
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/rigor/flow_contribution.rb', line 76 def initialize(return_type: nil, truthy_facts: nil, falsey_facts: nil, post_return_facts: nil, mutations: nil, invalidations: nil, exceptional: nil, role_conformance: nil, provenance: Provenance.builtin) # rubocop:enable Metrics/ParameterLists @return_type = return_type @truthy_facts = freeze_collection(truthy_facts) @falsey_facts = freeze_collection(falsey_facts) @post_return_facts = freeze_collection(post_return_facts) @mutations = freeze_collection(mutations) @invalidations = freeze_collection(invalidations) @exceptional = exceptional @role_conformance = freeze_collection(role_conformance) @provenance = provenance freeze end |
Instance Method Details
#==(other) ⇒ Object Also known as: eql?
140 141 142 |
# File 'lib/rigor/flow_contribution.rb', line 140 def ==(other) other.is_a?(FlowContribution) && to_h == other.to_h end |
#empty? ⇒ Boolean
Returns true when every content slot is unset (nil or an empty collection). Provenance does not count toward emptiness — an empty bundle still carries source attribution.
96 97 98 |
# File 'lib/rigor/flow_contribution.rb', line 96 def empty? SLOT_NAMES.all? { |slot| slot_empty?(public_send(slot)) } end |
#hash ⇒ Object
145 146 147 |
# File 'lib/rigor/flow_contribution.rb', line 145 def hash to_h.hash end |
#to_element_list ⇒ Array<Element>
Flattens this bundle into a tagged element list keyed by ‘(target, edge, kind)`. The flattening is mechanical and round-trippable through Rigor::FlowContribution::Merger.merge: feeding the result back through the merger produces an equivalent bundle.
Layout:
| slot | edge | kind | target | | ——————–|—————|———————|————————-| | return_type | normal | return_type | :return | | truthy_facts | truthy | truthy_fact | (per-fact target) | | falsey_facts | falsey | falsey_fact | (per-fact target) | | post_return_facts | post_return | post_return_fact | (per-fact target) | | mutations | normal | mutation | (per-mutation target) | | invalidations | normal | invalidation | (per-fact target) | | exceptional | exceptional | exception | :raise | | role_conformance | normal | role | (per-role target) |
125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/rigor/flow_contribution.rb', line 125 def to_element_list # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity elements = [] elements << element_for(:return, :normal, :return_type, return_type) unless return_type.nil? Array(truthy_facts).each { |fact| elements << element_for(fact_target(fact), :truthy, :truthy_fact, fact) } Array(falsey_facts).each { |fact| elements << element_for(fact_target(fact), :falsey, :falsey_fact, fact) } Array(post_return_facts).each do |fact| elements << element_for(fact_target(fact), :post_return, :post_return_fact, fact) end Array(mutations).each { |m| elements << element_for(fact_target(m), :normal, :mutation, m) } Array(invalidations).each { |i| elements << element_for(fact_target(i), :normal, :invalidation, i) } elements << element_for(:raise, :exceptional, :exception, exceptional) unless exceptional.nil? Array(role_conformance).each { |r| elements << element_for(fact_target(r), :normal, :role, r) } elements.freeze end |
#to_h ⇒ Object
100 101 102 103 104 |
# File 'lib/rigor/flow_contribution.rb', line 100 def to_h SLOT_NAMES.each_with_object(provenance: provenance.to_h) do |slot, acc| acc[slot] = public_send(slot) end end |