コンテンツにスキップ

Flow Contribution Merger (slice 3)

ステータス: v0.1.0スライス3規範文書。組み込みナローイング(narrowing)ルール・RBS::Extendedアノテーション・プラグインからのFlowContributionバンドルを単一のMergeResultにまとめるマージポリシーを固定します。設計サーフェス(surface)の根拠: ADR-2 § “Plugin Contribution Merging”

同一の呼び出しに対して複数のフローコントリビューションが発生しうる場合があります。組み込みナローイングルールとプラグイン提供ファクト(fact)が同一サイトに適用されることも、2つのプラグインが同一レシーバーファミリーに登録されることも、RBS::Extendedアノテーションが独自のファクトを追加することもあります。ADR-2は先着優先/後着優先の動作を禁じています。コントリビューションは決定論的にマージされ、コントリビューション間の矛盾は黙って上書きするのではなく診断として表面化しなければなりません。

スライス3はスタンドアロンのマージャーを提供します。スライス4は組み込みナローイングをマージャー経由でルーティングし、スライス5はプラグイン診断の来歴を結果に通し、スライス6はマージャーの{provenances, conflicts}を使ってプラグイン側のキャッシュエントリーを帰属させます。

公開名前空間(ドリフトピン済み)

Section titled “公開名前空間(ドリフトピン済み)”

以下の各名前空間はspec/rigor/public_api_drift_spec.rbによってロックされています。

バンドル → 要素リストへの展開。空でないスロットをすべて走査し、(target, edge, kind)をキーとする{Element}値オブジェクトを1つ以上生成します。

スロットエッジkindターゲット
return_typenormalreturn_type:return
truthy_factstruthytruthy_factファクトごと
falsey_factsfalseyfalsey_factファクトごと
post_return_factspost_returnpost_return_factファクトごと
mutationsnormalmutation変更ごと
invalidationsnormalinvalidationファクトごと
exceptionalexceptionalexception:raise
role_conformancenormalroleロールごと

ファクトごとのターゲットは、ペイロードに#targetアクセサが存在する場合(型付きファクトキャリア(carrier)、変更エフェクト)はそこから取得し、ない場合はペイロード自体がマージキーになります。この展開は機械的・決定論的で、逆変換可能です。結果をMerger.mergeに戻すと同等のバンドルが得られます。

フリーズされたData.define(:target, :edge, :kind, :payload, :provenance)値オブジェクト。コンストラクタはedgekindELEMENT_VALID_EDGES / ELEMENT_VALID_KINDSのenumに照らして検証します。#merge_keyはマージャーがグルーピングに使う[target, edge, kind]タプルを返します。

フリーズされたData.define(:target, :edge, :kind, :reason, :provenances, :message)reasonはスライス3enumのいずれかです。

  • :return_type_collapse — インターセクションが空になる同一ティアの戻り型。
  • :exceptional_disagreement — 同一ティアで非nilの例外エフェクトが一致しない。
  • :lower_tier_contradiction — 下位ティアのコントリビューションが上位ティアの証明を弱めるまたは矛盾する。

provenancesにはすべての貢献した{FlowContribution::Provenance}が入ります(通常は2つ — 上位ティアと矛盾する側)。#to_hは診断/フォーマッタ出力向けにコンフリクトをレンダリングします。

#to_diagnostic(path:, line:, column:, severity: :error)(スライス5-C)はコンフリクトをRigor::Analysis::Diagnosticに変換します。source_family: :contribution_mergeと、コンフリクト理由から派生したケバブケースのrulereturn_type_collapsereturn-type-collapse)を持ちます。スライス4の配線がコンフリクトを発行すると、qualified ruleは標準のrigor checkテキストストリームで[contribution_merge.return-type-collapse]としてレンダリングされます。

FlowContributionの8つのコンテンツスロット(return_typetruthy_factsfalsey_factspost_return_factsmutationsinvalidationsexceptionalrole_conformance)、provenances(貢献した全来歴の順序付きリスト)、conflicts(収集したConflict行)を持つフリーズ済み値オブジェクト。#conflict?#empty?は述語メソッドで、#to_hは診断向けに結果をレンダリングします。

ステートレスなモジュールレベルのエントリーポイント。2つの公開メソッドを持ちます。

  • Merger.merge(contributions) — バンドルの配列をマージポリシーに沿って畳み込み、MergeResultを返します。
  • Merger.tier_for(provenance) — マージャーが内部で使用するティアマッピングを公開します(診断フォーマッタに有用)。

Rigor::FlowContribution::Fact(スライス4-A)

Section titled “Rigor::FlowContribution::Fact(スライス4-A)”

4つのエッジ対応ファクトスロット(truthy_factsfalsey_factspost_return_facts、および将来のロール/変更のFactシェイプ(shape)バリアント)向けの標準スロットペイロード。ADR-7 § “Slice 4-A”でピン留めされており、4つの並列コントリビューションキャリアを単一の比較可能なシェイプに統一することで、マージャーの重複排除/インターセクションルールが均質なペイロード型の上で動作できるようになります。

フィールド役割
target_kind:parameterまたは:self。将来のkind(:local:ivar:result)はマージャーを変更せずに追加可能。
target_nameSymbol — 宣言されたパラメータ名、またはリテラル:self。非nilなので#targetは常に定義済みです。
typeRigor::Type::* — ターゲットがナローイングされる型(negativeがtrueの場合は除外方向)。
negative~T形式(predicate-if-true x is ~Integer)の場合はtrue、通常の正形式の場合はfalse

#targetはself対象のファクトには:selfを返し、それ以外には[:parameter, name]を返します。この値はElement#targetに入り、マージバケットキーになります。つまり異なるソースファミリーから同じパラメータをナローイングする2つのファクトは、ソースファミリーに関係なく同じグループにまとめられます。

4つの並列キャリアはFactとの間で変換されます。

  • Rigor::RbsExtended::PredicateEffect#to_fact — クラス名エフェクトはNominal[<class>]型のファクトに昇格します。絞り込みフォームのエフェクトはrefinement_typeをそのまま渡します。edgeフィールドは残りません。結果のファクトが入るスロット(truthy_facts / falsey_facts)がそれをエンコードします。
  • Rigor::RbsExtended::AssertEffect#to_fact — 同じシェイプです。conditionフィールド(:always / :if_truthy_return / :if_falsey_return)はread_flow_contribution境界でスロットをルーティングし(:alwayspost_return_facts:if_truthy_returntruthy_facts:if_falsey_returnfalsey_facts)、Fact自体には現れません。
  • 組み込みナローイングファクト — スライス4の実装者がInference::Narrowingをマージャー経由で配線する際に変換を追加します。
  • プラグインコントリビューション — スライス5の発行プロトコルは、truthy_facts / falsey_factsスロットがすでにFact配列になっているFlowContributionバンドルを返します。
ティアソースファミリー備考
0:builtinRubyのコアセマンティクス+受け入れ済みRBS契約(contract)。権威あるもの。
1:rbs_extendedRBS::Extendedディレクティブバンドル(v0.0.9グループD参照実装)。
1:generated生成されたシグネチャ/メタデータ。
2:pluginplugin.<id>プラグインコントリビューション。
3その他すべて不明 — 報告されますが最低ティアとして扱われます。

ティア内では、コントリビューションは決定論的な順序でマージされます。来歴が提供するplugin_idのアルファベット順(nil plugin idは先頭にソートされ、:rbs_extended / :generatedのプレプラグインコントリビューションを安定させます)、その後は元の入力位置が最終的なタイブレークになります。

  • :return_typeRigor::Type::Combinator.intersectionでインターセクトします。マージャーは相互accepts三値論理でコラプスを検出します。どちらの側も相手を受け入れない(a.accepts(b).no? && b.accepts(a).no?)場合、値のドメインは互いに素でインターセクションは空です。同一ティアでのコラプスは:return_type_collapseを、下位ティアがトリガーしたコラプスは:lower_tier_contradictionを発生させます。結果は上位ティアの値をスロットに保持します。
  • :truthy_fact / :falsey_fact / :post_return_fact。エッジローカル。プラグインのtrueエッジのファクトはfalseエッジの補集合を意味しません。同一ティアおよびクロスティアのファクトは、ペイロード等値性による重複排除をしながら蓄積されます。
  • :mutation / :invalidation / :role。ユニオン(union、合併型とも)。等値性による重複排除。
  • :exception。単値。等しい例外エフェクトは黙ってコラプスします。等しくないエフェクトは:exceptional_disagreement(同一ティア)または:lower_tier_contradiction(下位ティアが上位ティアに挑戦)のいずれかを発生させます。

スライス3が意図的に行わないこと

Section titled “スライス3が意図的に行わないこと”
  • 組み込みナローイングをマージャー経由でルーティングすること。スライス4の仕事です。
  • コンフリクトを:contribution_merge Diagnostic行として診断すること。スライス5がプラグイン診断の来歴をフォーマッタに通します。スライス4が解析中にコンフリクトを表面化させます。
  • マージ結果からCache::Descriptor行を合成すること。スライス6がプラグイン側のキャッシュプロデューサーと並行してそれを担います。
  • より豊かな戻り型コラプスケースの検出。スライス3のヒューリスティックはaccepts三値論理を使います。非名前的キャリア(コラプスするタプルインターセクション、すべての定数を排除する絞り込み述語との構造的インターセクション)は今のところ非コラプスとして処理されます。スライス4がキャリアの全行列を検証し、見逃したケースをマージャーにフォールドバックします。

この展開は可逆に実装されています。

contribution = Rigor::FlowContribution.new(...)
elements = contribution.to_element_list
merged = Rigor::FlowContribution::Merger.merge([contribution])
# merged は `contribution` と同じスロットを持ち、来歴と空のコンフリクトリストを加えます。

スライス4はこのラウンドトリップをアナライザーの既存のナローイング呼び出しサイトと並行して検証します。

© 2026 TypedDuck. Licensed under CC BY-SA 4.0.