Class: Rigor::Effects::EnvelopeIndex
- Inherits:
-
Object
- Object
- Rigor::Effects::EnvelopeIndex
- Defined in:
- lib/rigor/effects/envelope_index.rb
Overview
The envelopes a call site may import as a ≤ bound, looked up by the callee the typer named
(ADR-103 WD6; #386; normative in docs/type-specification/effect-labels.md § The declared lane at
call sites).
EnvelopeCheck asks "what bounds THIS method's body?" and answers it from the project's own declarations alone, because that is the stratum a contract can be checked against. This index asks the other question — "what does the thing I am calling promise?" — and so reads one stratum more: an accepted signature's annotation, from the built RBS environment. WD6's trust ladder puts both on the discharging side, and the two questions are kept in two objects because only the first one may ever produce a finding.
Four sources, nearest-first, exactly the check's precedence with the accepted stratum appended:
per-method annotation > class-level annotation > `effects.envelopes:` > accepted signature
The carrier is nominal: the lookup is by the receiver's static class name as the typer
projected it (or, for an implicit-self call, by the unit's own class), never by walking ancestors.
A structural interface erases to Dynamic[top] today and has nothing to attach a bound to
(ADR-103 WD6); an inherited envelope reaches an override
through effect.liskov-widened instead, which is a judgment rather than an import.
Two bounds of the slice, both deliberate:
- An
effects.envelopes:entry selected bymatch:does not participate here. A path glob is a fact about where a class is defined, which a per-file collection window cannot see without the whole-project class-source table;namespace:needs only the name and does participate. The entry still bounds its own classes' methods for EnvelopeCheck and the Liskov check. - Lookup is by the exact owner.
repo.findon a receiver typedPgRepodoes not importRepo#find's bound.
Values are Marshal-clean (Envelope over frozen Strings and LabelSets), because the index is built per process — the parent and each fork-pool worker build their own from the same configuration and the same signature content, so the two agree without a channel to keep in sync.
Class Method Summary collapse
-
.build(configuration:, environment: nil, plugin_facts: nil) ⇒ EnvelopeIndex
Reads every stratum this index serves, once per process.
-
.empty ⇒ Object
The index a run with no declaration of any kind uses — and the fail-soft answer for a build that raised.
Instance Method Summary collapse
-
#[](owner, singleton, selector) ⇒ Envelope?
The envelope bounding
owner'sselector, or nil. -
#empty? ⇒ Boolean
Whether no stratum has anything to say.
-
#initialize(method_envelopes: NO_ENVELOPES, class_envelopes: NO_ENVELOPES, config_entries: NO_ENTRIES, accepted: NO_ENVELOPES) ⇒ EnvelopeIndex
constructor
A new instance of EnvelopeIndex.
Constructor Details
#initialize(method_envelopes: NO_ENVELOPES, class_envelopes: NO_ENVELOPES, config_entries: NO_ENTRIES, accepted: NO_ENVELOPES) ⇒ EnvelopeIndex
Returns a new instance of EnvelopeIndex.
107 108 109 110 111 112 113 114 115 |
# File 'lib/rigor/effects/envelope_index.rb', line 107 def initialize(method_envelopes: NO_ENVELOPES, class_envelopes: NO_ENVELOPES, config_entries: NO_ENTRIES, accepted: NO_ENVELOPES) @method_envelopes = method_envelopes.freeze @class_envelopes = class_envelopes.freeze @config_entries = config_entries.reject { |entry| entry.namespace.nil? }.freeze @accepted = accepted.freeze @config_cache = {} freeze end |
Class Method Details
.build(configuration:, environment: nil, plugin_facts: nil) ⇒ EnvelopeIndex
Reads every stratum this index serves, once per process.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/rigor/effects/envelope_index.rb', line 68 def self.build(configuration:, environment: nil, plugin_facts: nil) # Required here rather than at the top of the file: the reader pulls in the whole # `RbsExtended` surface, and this build runs only under an `effects:` block. require_relative "../rbs_extended/envelope_scanner" registry = Registry.for_configuration(configuration, plugin_facts: plugin_facts) loader = environment&.rbs_loader scan = RbsExtended::EnvelopeScanner.scan( sources: SignatureSources.collect( signature_paths: configuration.signature_paths, virtual_rbs: loader&.virtual_rbs ), registry: registry ) new( method_envelopes: scan.method_envelopes, class_envelopes: scan.class_envelopes, config_entries: ConfigEnvelopes.build(entries: configuration.effects_envelopes, registry: registry), accepted: accepted_for(loader, registry) ) rescue StandardError empty end |
.empty ⇒ Object
The index a run with no declaration of any kind uses — and the fail-soft answer for a build that
raised. Every lookup on it is one empty? read.
53 54 55 |
# File 'lib/rigor/effects/envelope_index.rb', line 53 def self.empty @empty ||= new end |
Instance Method Details
#[](owner, singleton, selector) ⇒ Envelope?
The envelope bounding owner's selector, or nil.
130 131 132 133 134 135 136 137 138 139 |
# File 'lib/rigor/effects/envelope_index.rb', line 130 def [](owner, singleton, selector) return nil if owner.nil? || empty? key = "#{owner}#{singleton ? '.' : '#'}#{selector}" envelope = @method_envelopes[key] || @class_envelopes[owner] || config_envelope(owner) || @accepted[key] return nil if envelope.nil? || envelope.top? envelope end |
#empty? ⇒ Boolean
Whether no stratum has anything to say. The scan's fast path: a project with no envelope of any kind pays one predicate per call site and nothing else.
119 120 121 |
# File 'lib/rigor/effects/envelope_index.rb', line 119 def empty? @method_envelopes.empty? && @class_envelopes.empty? && @config_entries.empty? && @accepted.empty? end |