コンテンツにスキップ

Changelog — 0.0.x archive

0.0.x開発サイクルのアーカイブリリースノートです。

フォーマットはKeep a Changelogに従い、プロジェクトはSemantic Versioningに準拠しています。

このファイルは0.0.1から0.0.9までの静的アーカイブであり、プロジェクトのアーカイブルールに従ってメインのCHANGELOG.mdから移動されました: 先頭桁のバンプ後の最初のリリース(例: 0.0.x0.1.xバンプ後の最初のリリース0.1.1)の時点で、以前の桁の範囲全体がdocs/CHANGELOG-<old-prefix>.mdアーカイブファイルに移動されます

現在のサイクルのリリースノートはCHANGELOG.mdに存在します; v0.1.0のリリースノートもそこに残ります。次の先頭桁バンプ(例: 0.3.1でトリガーされる0.2.x0.3.x)が着地すると、0.2.xブロックが同じルールに従ってdocs/CHANGELOG-0.2.x.mdに移動します。

第9プレビュー。テーマ: キャッシュサーフェス(surface)の完成、型語彙の拡張、v0.1.0プラグイン契約(contract)に向けたパブリックAPIのロック。v0.0.9はすべての残存するpre-0.1.0サブストレートスライス(slice)を完了します: 永続キャッシュがrigor checkにエンドツーエンドで配線され(ウォーム実行はディスクバックのテーブルにヒットし; --cache-statsは実際のヒット / ミス / 書き込みカウントを報告し; --no-cacheでオフにできます)、型語彙は対補完~Tナローイング(narrowing)と補間 / + / * / <<を通じたliteral-stringフロー追跡を取り込み、RBS::Extendedディレクティブサーフェスはメソッド上のすべての認識済みディレクティブを1つのRigor::FlowContributionバンドルにまとめ、6つの新しい組み込みカタログがRandomStruct(+ Data)、EncodingRegexp / MatchDataProc / Method / UnboundMethodExceptionをカバーします。

0.0.9の次のリリースは0.1.0です——一桁バージョンコンポーネントポリシー、0.0.10なし。v0.1.0がプラグイン契約を本格的に開始します; v0.0.9はその契約が設計される基板をリリースします。

rigor checkに配線されたキャッシュレイヤー

Section titled “rigor checkに配線されたキャッシュレイヤー”
  • Analysis::Runner.cache_storeサーフェス + rigor check --no-cache。Runnerはデフォルトで.rigor/cacheをルートとするCache::Storeを使用します; CLIフラグはnilをスレッドして無効にします。Environment.for_project(cache_store:)はStoreを基礎のRbsLoaderまで引き渡します。
  • 最初のエンドツーエンドキャッシュプロデューサー——RbsLoader#constant_typeRbsConstantTableから読み取ります。コールドランは翻訳済み定数型テーブルを一度構築して永続化します;ウォームラン(および同じStoreを共有する別のローダー)は環境の走査を完全にスキップし、テーブルのMarshal.loadのみを支払います。
  • さらに5つのキャッシュプロデューサーRbsKnownClassNames(Set)、RbsClassAncestorTable(Hash<String, Array>)、RbsClassTypeParamNames(Hash<String, Array>)、RbsEnvironment(完全なRBS::Environment)。5番目のプロデューサー(RbsEnvironment)は最大のコールドスタートコスト——RBS::EnvironmentLoader#load + Environment.from_loader + resolve_type_names——を、rbsgemのC拡張RBS::Locationに最小限の_dump / _load Marshalフックを追加することでキャッシュします。パッチは純粋にアディティブかつべき等です; RBS::Locationはどの解析パスからも読まれないため、失われたソース位置メタデータは不活性です。
  • Cache::Store#stats + --cache-statsランタイム内訳fetch_or_compute内でバンプされるインプロセスのヒット / ミス / 書き込みカウンタ(プロデューサーごとの内訳); rigor check --cache-statsはオンディスクインベントリに続いて「このラン:」セクションを出力します。--no-cache下ではセクションは省略されます。
  • Cache::Store#fetch_or_compute(serialize:, deserialize:)呼び出し可能サーフェス。戻り値がMarshalクリーンでないプロデューサー(RBS::Locationメンバーを持つRBSネイティブオブジェクト、生のIOなど)はカスタムラウンドトリップ呼び出し可能オブジェクトを登録できます。デフォルトはMarshal.dump / Marshal.loadのまま。デシリアライザの例外はキャッシュミスになります。RbsEnvironmentはこのサーフェスを利用します。
  • 共有Rigor::Cache::RbsDescriptor。すべてのRBS由来プロデューサーは同じディスクリプタ(rbsgemのロックバージョン + signature_paths下のすべての.rbsファイルの:digestエントリー + rbs.libraries設定エントリー)を付与します。これにより、シグネチャの変更やrbsgemのバンプが一斉に無効化されます。
  • Refined[base, predicate]の対補完ナローイングType::Refined::COMPLEMENT_PAIRSが双方向ペアを登録します;ナローイングティアは不精確なDifference[base, refined]フォールバックの代わりにRefined[base, complement]を返します。v0.0.9では3つのペアが着地します: lowercase ↔ not_lowercaseuppercase ↔ not_uppercasenumeric ↔ not_numeric。ポジティブキャリア(carrier)non-lowercase-stringnon-uppercase-stringnon-numeric-stringBuiltins::ImportedRefinements::REGISTRYに追加され、直接書けるようになります。
  • literal-stringキャリアとnon-empty-literal-stringコンポジション。ソースコードリテラル(またはリテラルのコンポジション)から来ることが既知のString。文字列補間"#{...}"(すべての部分がリテラルベアリングの場合literal-stringにリフト)およびString#+String#*String#<<String#concatをカバーする新しいLiteralStringFoldingディスパッチャーティアを通じて追跡されます(すべてのオペランドがリテラルベアリングの場合にリフト)。
  • 6つの新しい組み込みカタログ — Random、Struct(+ Data)、Encoding、Regexp + MatchData、Proc / Method / UnboundMethod、Exception。各カタログはクラスごとの間接ミューテーターブロックリストでフォールドディスパッチャーを駆動します(RandomのMT状態を進めるメソッド、Regexpの$~を書くマッチャー、Proc / Methodの:call / :[実行パス、Exceptionのランタイム状態リーダーなど)。
  • Numeric#cloneの再分類numericトピックのc_index_pathsreferences/ruby/object.cが含まれるようになりました。Numeric#clonerb_immutable_obj_cloneへのエイリアスがCボディ分類器によって発見され、エントリーがpurity: unknownからpurity: leafに移動します。

pre-v0.1.0サブストレート(プラグイン契約が結び付くサーフェスをロック)

Section titled “pre-v0.1.0サブストレート(プラグイン契約が結び付くサーフェスをロック)”
  • Rigor::FlowContributionバンドル構造体。8つのコンテンツスロット(return_typetruthy_factsfalsey_factspost_return_factsmutationsinvalidationsexceptionalrole_conformance)とProvenance Dataキャリア(source_familyplugin_idnodedescriptor)。構築時に凍結;コレクションスロットはdupeして凍結。ADR-2 §「フロー貢献バンドル」に従うパブリック読み取り形状;要素リストのフラット化はそれを消費するコントリビューションマージャーとともにv0.1.0に延期。
  • Rigor::RbsExtended.read_flow_contribution(method_def)。単一のRBSメソッド上のすべての認識済みディレクティブ(predicate-if-(true|false)assert*return:)を1つのFlowContribution:rbs_extendedソースファミリー)にまとめます。内部ナローイングは型付きDataキャリアを消費し続けます;バンドルはv0.1.0のコントリビューションマージャーが読むパブリックパッケージングです。
  • Rigor::ScopeRigor::EnvironmentRigor::Type::CombinatorRigor::ReflectionのパブリックAPIドリフトスペックspec/rigor/public_api_drift_spec.rbのスナップショットスタイルスペックが各名前空間のインスタンス + シングルトンメソッドセットをピン留めし、偶発的なシグネチャ変更がサイレントな破壊ではなくテスト失敗として現れるようにします。4つの名前空間はv0.1.0プラグイン契約の接続点です。
  • docs/internal-spec/public-api.md。パブリック / 内部安定性境界が明示的に宣言されました: 今日どの名前空間がドリフトピン留めされているか(Scope / Environment / Type::Combinator / Reflection)、v0.1.0まで流動的なパブリック形状のもの(FlowContribution、Diagnostic、Cache::Store#fetch_or_compute、RbsExtendedディレクティブリーダー)、および厳密に内部のもの(Inference::、Analysis::FactStore / CheckRules / Runner、AST::バーチャル、Source / CLI / Configurationプラミング)。
  • キャッシュレイヤーのパブリック読み取り形状がdocs/internal-spec/cache.mdの全6プロデューサーをカバーするよう拡大: DescriptorStore(新しいserialize: / deserialize: kwargsとStore#statsを含む)、RbsConstantTableRbsKnownClassNamesRbsClassAncestorTableRbsClassTypeParamNamesRbsEnvironment、共有RbsDescriptorビルダー、RBS::Location Marshalパッチ。
  • Rigor::FlowContributiondocs/internal-spec/flow-contribution.mdに文書化されました(スロットテーブル、等値性 / to_h / empty?セマンティクス、RbsExtended.read_flow_contributionマッピング(predicate-if-* → truthy_facts / falsey_factsassert*post_return_factsreturn:return_type)、延期された要素リストフラット化のノートを含む)。

第8プレビュー。テーマ: 最初のキャッシュ関連コードスライス — v0.0.7のキャッシュスライス分類体系設計ドキュメントがスキーマを固定した永続化レイヤーを着地させ、Marshalクリーンなプロデューサーをエンドツーエンドで配線します。バックエンドの選択はADR-6によって固定: カスタム正規フォーマットを通じて書き込まれるバイナリエントリーのシャードディレクトリ、新しいgem依存関係ゼロ

  • Rigor::Cache::Descriptor値オブジェクトdocs/design/20260505-cache-slice-taxonomy.mdに従う純粋値の4スロットスキーマ(filesgemspluginsconfigs)。各スロットは型付きの凍結エントリーを保持します; FileEntryは比較器enum(:digest > :mtime > :exists)を検証します;その他はすでに正規化されたハッシュを受け入れます。Descriptor.compose(*descriptors)はキーでスロットを統合し、ファイル競合ではより厳格な比較器を優先し、値が一致しない場合はDescriptor::Conflictを発生させます。descriptor.cache_key_for(producer_id:, params:)は合成された入力に対して正規16進SHA-256を導出します; to_canonical_bytesはソート済みで決定的なJSONを生成し、等価なディスクリプタが同一バイトにラウンドトリップします。
  • Rigor::Cache::Storeファイルシステムバックエンド。シャードレイアウト<root>/<producer-id>/<2-prefix>/<62-suffix>.entry<root>/schema_version.txtのスキーマバージョンマーカー。カスタムバイナリエントリーフォーマット("RIGOR\x00\x01"マジック、varint接頭辞付きディスクリプタと値、末尾SHA-256整合性)。書き込みはデスティネーションのflock(LOCK_EX)と一時ファイルのfsyncを使ったリネームインプレースに従います。読み取りはあらゆる失敗(ファイルなし、不正マジック、不正SHA-256、不正varint、unmarshal不可なペイロード)をキャッシュミスに落とします。Store#fetch_or_compute(producer_id:, params:, descriptor:) { ... }が唯一のプロデューサー向けAPIです;プロデューサーIDはファイルシステム安全のために[a-z][a-z0-9._-]*に制約されます。
  • 最初のキャッシュプロデューサー——Rigor::Cache::RbsConstantTable。すべてのRBS宣言定数(例: "::Math::PI")をその翻訳済みRigor::TypeにマッピングするHash<String, Rigor::Type>をキャッシュします。ディスクリプタ: ロックバージョンを持つrbs gem、signature_paths下のすべての.rbsファイルの:digestエントリー、ライブラリリストの設定エントリー。スライス計画は最初のプロデューサーとして元々RBS環境ローダー(build_env)を指名していましたが、実装によりRBS::EnvironmentはMarshalクリーンでない(RBS::Location_dump_dataを持たないC拡張クラス)ことが発見されました。ADR-6 § 8がその発見を文書化しています;スライスは代わりに翻訳後のアーティファクトをキャッシュします。RbsLoader#constant_namesが追加され、プロデューサーがローダーのプライベート状態に触れることなく定数を列挙できます。
  • rigor check --cache-stats。ランの最終にオンディスクインベントリを出力します(プロデューサーごとのエントリーカウント、合計バイト、スキーマバージョンマーカー)。新しいRigor::Cache::Store.disk_inventory(root:)クラスメソッドから取得。ランごとのヒット / ミスカウンタはプロダクションコードがキャッシュを配線するまで延期。
  • rigor check --clear-cache。解析ランの前に.rigor/cacheディレクトリ(CWD相対)を削除します。Cleared cache: .rigor/cacheまたはCache already empty: .rigor/cacheを出力します。チェック自体は完了まで実行されます。
  • 診断ソースファミリー由来Rigor::Analysis::Diagnosticsource_family: kwarg(デフォルト:builtin)と非デフォルトファミリーには"#{source_family}.#{rule}"を、組み込み診断にはruleを返すqualified_ruleアクセサが追加されました。JSON出力(to_h)はsource_familyと生のruleの両方を並べて持ちます。プラグインAPI自体にコミットすることなくADR-2のプラグイン可観測性ストーリーを準備します; v0.0.8でデフォルト以外のソースファミリーを設定するプロダクション呼び出し元はありません。
  • 新しい規範的仕様docs/internal-spec/cache.mdがキャッシュレイヤーのパブリック読み取り形状を追跡します(Descriptor API、Store API、ファイルフォーマット、アトミシティとロック、スキーマバージョン不一致動作、ディスクインベントリ、診断由来)。

第7プレビュー。テーマ: プリプラグインカバレッジプッシュ — v0.1.0でこのサーフェスに対して設計されるプラグインAPIが結び付くための完全な基板を持てるよう、型言語と組み込みカバレッジ仕様がすでにコミットしているものと解析器が実際に実装しているものとのギャップを埋めます。リリースは深さより幅を重視: 多数の小さな追加、加えてpre-v0.1.0シーケンスにおける最初の設計出力。

  • key_of[T] / value_of[T]HashShapeTupleNominal[Hash, [K, V]]Nominal[Array, [E]]、有限境界Constant<Range>の既知キー(resp. 値)の型レベル和集合を射影します。RBS::Extendedディレクティブペイロードから到達可能。パーサはkebab-caseの精製とともにlower_snakeヘッドも受け入れ、名目的引数が独自の型引数を持てるようにします。key_of[Hash[Symbol, Integer]]Symbolにパースされます。
  • int_mask[1, 2, 4] / int_mask_of[T] — 有限整数リテラルセットに対するビット単位OR閉包を計算し、小さな閉包にはUnion[Constant<Integer>…]を返し、カーディナリティ上限を超えたら網羅するIntegerRangeを返します。整数リテラルがパーサ引数として受け入れられるようになりました。
  • T[K]インデックスアクセス演算子 — 構造化されたTからインデックス / キーKの型を射影します。パースされた型の後ろに続く[K]セグメントを通じてRBS::Extendedディレクティブペイロードから到達可能(連鎖したT[K1][K2]を含む)。パーサのトップレベルエントリーはクラス名ヘッドの型を直接受け入れるようになりました。Hash[Symbol, Integer][Symbol]Symbolにパースされます。
  • Rational / ComplexリテラルリフトPrism::ImaginaryNode1i)とPrism::RationalNode1.5r)はConstant<Complex> / Constant<Rational>として型付けされます; Kernel#Rational(num, den)Kernel#Complex(re, im)の呼び出しはすべての引数が数値Constantの場合に同じ精密な定数にフォールドします。foldable_constant_value?Rational / Complexを受け入れるよう拡張され、定数レシーバーに対するすべてのカタログティアRational#… / Complex#…フォールドのブロックを解除します。
  • Regexpリテラルリフト。非補間のPrism::RegularExpressionNodeConstant<Regexp>にリフトします(ソースとオプションフラグを保持);補間されたregexは保守的なNominal[Regexp]を保持します。新しいConstant<String>#scan(/regex/)フォールドパスをエンドツーエンドでアクティブにします。
  • Pathname委譲PathnameType::Constant::SCALAR_CLASSESに追加されます; Pathname.new(Constant<String>)MethodDispatcher#meta_new定数コンストラクタテーブルを通じてリフトします;厳選された14メソッドの単項 / 8メソッドの二項フォールドテーブルが純粋なパス操作をカバーします(to_sbasenamedirnameextnamecleanpath+join<=>==relative_path_fromなど)。ファイルシステムに触れるメソッド(exist?file?readstatなど)は意図的にフォールドしません。
  • to_aは有限整数範囲に対して位置ごとのTuple[…]にリフトします(16要素でキャップ); first / last / min / maxcount / size / lengthはオプショナルブロックバリアントの:block_dependent分類をバイパスして、引数なし形式の精密なConstant<Integer>値にフォールドします。

Tuple精密性(11の新しいShapeDispatchハンドラ)

Section titled “Tuple精密性(11の新しいShapeDispatchハンドラ)”
  • empty? / any? / all? / none?(ブロックなし、引数なし)はタプルのアリティと要素の真偽値に基づいてConstant[bool]にフォールドします。
  • include?(needle)はneedleがConstantでタプルの要素がすべてConstantの場合に精密なboolにフォールドします。
  • sum / min / maxはすべてのConstant要素に対して数値 / 比較可能な極値にフォールドします。
  • sort / reverseは適切な順序で位置ごとのTupleを返します。
  • to_aはレシーバーTupleをそのまま返します。
  • zipはレシーバーの位置ごとの要素を他の各Tupleシェイプ(shape)引数の位置ごとの要素とペアにします;短い他のTupleはConstant[nil]でパディングします;複数引数のzipはより広い位置ごとのTupleを生成します(8でキャップ)。
  • keys / valuesは宣言順を保持して位置ごとのTupleにフォールドします。
  • count / lengthは既存のsizeハンドラとマッチします。
  • empty? / any?(引数なし、ブロックなし)は形状の空き具合に基づいてConstant[bool]にフォールドします。
  • first / flatten / compact — オプショナルキーなしのクローズドシェイプに対して: firstは最初のペアの[k, v] 2-Tupleを返します; flattenは位置ごとの[k_1, v_1, k_2, v_2, …] Tupleを生成します; compactは値がConstant[nil]のすべてのエントリーを削除します。
  • Tuple ↔ HashShapeコンバージョンTuple#to_hHashShape#to_aHashShape#to_hHashShape#invert(Symbol / String値のシェイプのみ)、クローズドシェイプ × クローズドシェイプマージのHashShape#merge(other)
  • Tuple / HashShape引数に対するフォーマット文字列フォールド"%d / %d" % [1, 2]Constant<"1 / 2">にフォールドします; "%{name} is %{age}" % {name: "Alice", age: 30}Constant<"Alice is 30">にフォールドします。不正なフォーマット仕様は拒否されRBSティアが拡大します。
  • 配列を返すメソッドリフトs.chars / s.bytes / s.lines / s.split(引数なし、セパレータ、またはConstant<Regexp>パターン)/ s.scanは、すべての要素がフォールド可能なスカラーでカーディナリティが32以内の場合に、結果のArrayを位置ごとのTuple[Constant…]にリフトします。大きな結果は拒否されRBSティアが拡大します。
  • ~Refined[base, predicate]current_typeをそのまま返すフォールバックの代わりにDifference[base, refined]を通じてナローイングします。assert value is ~lowercase-stringStringDifference[String, lowercase-string]にナローイングするようになりました。Intersection精製のDe Morganコンポジションも引き締まります。
  • {}HashShape{}はv0.0.6の空配列リテラル変更を反映します。新しいHashShape射影がそれに対してフォールドします。
  • Array.new(n, value) / Array.new(n)nが小さなConstant<Integer>の場合(16でキャップ)に位置ごとのTuple[…]にリフトします。過大なnNominal[Array]にフォールバックします。
  • Rigor::Reflection読み取り側ファサード — Rigorの3つのリフレクションソース(ClassRegistry + RbsLoader + Scope発見ファクト(fact))を1つの読み取りAPIの下に結合します。9つのクエリ: class_known?class_orderingnominal_for_namesingleton_for_nameconstant_type_for(インソースがRBSとの衝突に勝つ)、instance_method_definitionsingleton_method_definitiondiscovered_class?discovered_method?。v0.1.0プラグインAPI準備のためのパブリック読み取り形状;仕様はdocs/internal-spec/reflection.md
  • リフレクションコンシューマー移行。5つのエンジン内部コーラー(Analysis::CheckRulesInference::NarrowingInference::StatementEvaluatorInference::MethodDispatcherInference::MethodParameterBinderInference::MethodDispatcher::RbsDispatch)が生のscope.environment.rbs_loaderアクセスからファサードに移行します。ファサードはrbs_class_known?instance_definition / singleton_definitionclass_type_param_names、ディスパッチャーコールパスのためにScopeを持たない場合にenvironment: kwargバリアントを獲得します。機械的なリファクタ;動作変更なし。
  • v0.1.0準備設計ドキュメントdocs/design/20260505-v0.1.0-readiness.md。すべてのADR-2サーフェスを今日の実装にマッピングし、7つの主要なpre-v0.1.0作業項目のシーケンスを定め、ADR-2の未解決の問題を調和させ、v0.0.xドットリリースとして着地できる項目を列挙します。
  • キャッシュスライス分類体系設計ドキュメントdocs/design/20260505-cache-slice-taxonomy.md。スロットごとのエントリー形状(:digest / :mtime / :exists比較器を持つFileEntryGemEntryPluginEntryConfigEntry)、コンポジションルール、正規キャッシュキー導出、粒度ガイダンス、スキーマバージョニングを固定します。v0.1.0でリリースされる永続化レイヤーの前提条件契約。

第6プレビュー。テーマ: 定数フォールドティアを通じてブロックを取るEnumerableメソッドをフォールドする — リテラルコレクションに対するイテレータ形状の式がRBSを通じて拡大するのではなく精密なキャリアを生成するよう。

  • 定数ブロック述語とフィルタに対するブロック形状フォールドディスパッチ[1, 2, 3].select { false }arr.all? { true }arr.any? { false }のような呼び出しは、ブロックの推論された戻り型がRubyの真値または偽値のConstantの場合に精密なエンドポイントに折り畳まれます。フィルタメソッド(select / filter / reject / take_while / drop_while)はレシーバーまたはTuple[]にフォールドします;述語メソッド(all? / any? / none?)は、Rubyのセマンティクスでレシーバーの空き具合 × ブロックの真偽値の組み合わせが無条件の場合(空レシーバーの空白真値ケースを含む)にConstant[true] / Constant[false]にフォールドします。レシーバーの空き具合はTupleHashShapeConstant<Array|Hash|String|Range>、インポートされたnon-empty-array[T]キャリア(Difference[Array, Tuple[]])に対して認識されます。
  • map / collect / filter_map / flat_map / find / detect / find_index / indexのTupleレシーバーに対する位置ごとのブロック再評価。ブロックボディは対応する要素がブロックパラメータにバインドされた状態でTupleの位置ごとに1回型チェックされ、その後メソッドごとに組み立てられます:
    • map / collectTuple[U_1..U_n]を生成します。[1, 2, 3].map { |n| n.to_s }Array["1" | "2" | "3"]の代わりに["1", "2", "3"]に解決されます。
    • filter_mapConstant[nil] / Constant[false]位置を削除し、残りをTupleに連結します。
    • flat_mapは位置ごとのTuple結果を連結し、位置ごとのConstantスカラーを単一要素の寄与として扱い、不透明なキャリアでは拒否します。
    • find / detectは最初の真値位置のレシーバー要素を返します(すべての位置が偽値の場合はConstant[nil])。
    • find_index / indexは最初の真値位置のインデックスを返します(Constant[nil]の場合あり)。値検索形式index(value) / find_index(value)は拒否されRBSティアがそれらを引き続き担当します。
  • 8要素のカーディナリティ上限までの短いConstant<Range>レシーバーに対する位置ごとのブロックフォールド。範囲内の各整数はブロックボディを対応するConstant<Integer>がパラメータにバインドされた状態で1回再型付けします。(1..3).map { |n| n.to_s }["1", "2", "3"]に解決し、(1..5).find { |n| n.even? }Constant[2]に解決します。大きな範囲は拒否されRBSティアが拡大し、ブロック型付けコストを制限します。
  • 式位置の条件式に対するブランチ除去。述語がType::Constantにフォールドするif / unless / 三項式は到達不能なブランチを削除し、生きているブランチの型を採用します。ステートメントレベルのブランチ除去はv0.0.3からすでに存在していました;このスライスは式位置での使用(例: 代入の右辺、引数式、ブロックボディ)をカバーします。位置ごとのフォールドと直接コンポーズするため、[1, 2, 3].filter_map { |n| n.even? ? n.to_s : nil }Tuple[Constant["2"]]に解決します。
  • Constant形状の左オペランドに対する&& / ||短絡除去&& / ||の左オペランドがType::Constantにフォールドする場合、結果型はRubyの実際の短絡セマンティクスに従います: Constant[truthy] && rhsは右オペランドの型、Constant[falsey] && rhsは左を保持、そして||にはデュアルルールが適用されます。非Constantの左オペランドは以前の両オペランドの和集合の動作を保持します。
  • find { false } / detect { false } / find_index { false } / index { false } / count { … }の短絡フォールド。ファインドファミリーのブロック形式偽値側はConstant[nil]にフォールドします; count { false }Constant[0]にフォールドします; count { true }は、レシーバーが有限サイズをピン留めしている場合(Tuple、HashShape、有限整数エンドポイントを持つConstant<Range>)にConstant[size]にフォールドします。値検索形式index(value) / count(value)は位置引数を持ち、RBSティアが引き続き回答するため拒否されます。
  • IntegerRange対応の三項フォールド——Comparable#between? / Comparable#clamp。2引数のtry_fold_ternaryパスはIntegerRangeレシーバーと2つのスカラーConstant<Integer>引数を受け入れるようになりました。int<3, 7>.between?(0, 10)Constant[true]にフォールドします; int<3, 7>.clamp(4, 6)int<4, 6>にフォールドします(積集合が単一点をピン留めする場合はConstantに折り畳まれます)。ブラケットが範囲と完全に交差しない場合——すべてのレシーバー値が一方のブラケット境界にスナップする場合——フォールドは拒否されRBSティアが拡大します。
  • 空配列リテラルキャリア——[]Tuple[]に解決。空の配列リテラルは以前Nominal[Array]として型付けされていましたが、v0.0.6では空のTuple[]キャリアに切り替わります。これにより位置ごとのブロックフォールドが[1, 2, 3].flat_map { |_| [] }のようなすべての空位置に対してクリーンに連結できます(現在Tuple[]にフォールドします)。両キャリアともRBS相互運用パスでプレーンなArrayに消去されます。
  • Pathnameカタログインポートdata/builtins/ruby_core/pathname.yml(102インスタンスメソッド、2シングルトン、5エイリアス)と対応するBuiltins::PATHNAME_CATALOGがカタログティアに加わります。PathnameはほぼFileおよびDirに委譲する薄いラッパーなので、ユーザーが見えるペイオフはNumericやStringより狭い——このインポートはPathname.new(...)のレシーバークラス認識、防御的な:initialize_copyブロックリストエントリー、唯一の:leafメソッド(<=>)に対するカタログフォールドを提供します。
  • tool/extract_builtin_catalog.rbのrescue-on-defクラシファイアークラッシュPreludeParser#analyse_bodyは以前、PrismがボディをStatementsNodeではなくBeginNodeでラップするrescue-on-defイディオム(def foo; …; rescue; …; end)を持つRubyメソッドでNoMethodErrorを発生させていました。クラシファイアーはそのケースのbeginブロックのstatementsに降りるようになりました。このバグはPathname(そのpreludeにdef initialize(path); @path = …; rescue TypeError; …; endがある)をインポートする際に表面化しました;すべてのカタログはmake extract-builtin-catalogs下でクリーンに再生成されます。
  • RationalとComplexの組み込みカタログインポート。新しいローダーRATIONAL_CATALOGCOMPLEX_CATALOGCATALOG_BY_CLASSテーブルに加わります;対応するYAMLがdata/builtins/ruby_core/{rational,complex}.yml下にtool/extract_builtin_catalog.rbを通じてreferences/ruby/{rational,complex}.cから生成されます。両クラスはRubyで完全に不変なので、クラスごとのmutating_selectorsブロックリストは従来の防御的な:initialize_copyエントリーのみを持ちます。Rigorには今日Constant<Rational> / Constant<Complex>リテラルリフト(Prism::ImaginaryNodeRational(...) / Complex(...)カーネル呼び出しフォールドは延期)がないため、カタログ配線は現在防御的なサーフェスです——すべてのフィクスチャアサーションはNominal[<class>]レシーバー上のRBSティア射影を通じて行われます。このブロックリストは将来のスライスが型付け器にこれらのリテラルをConstant<…>にリフトする方法を教えた時点でロードベアリングになります。

  • Const = Data.define(*Symbol)の発見Inference::ScopeIndexer.record_declarationsConst(囲むクラス / モジュールパスで修飾)を、定数がSingleton[<qualified-name>]に解決する発見されたクラスとして登録するようになりました。以前Const.new(...)は未ナローイングのDynamic[top]エンベロープを返していましたが、定数の登録によりmeta_newはそれを新しいNominal[<qualified-name>]に解決し、メンバーアクセサはユーザークラスフォールバックを通じて偽陽性なしで流れます。ベア形式Data.define(:x, :y)とブロックオーバーライド形式Data.define(:x, :y) do; def initialize(x:, y:); …; end endの両方が認識されます;非Symbol引数と非Dataレシーバーは拒否されます。実例: lib/rigor/analysis/fact_store.rbTargetFactsingleton(Rigor::Analysis::FactStore::Target)singleton(Rigor::Analysis::FactStore::Fact)として型付けされるようになりました。

  • Kernel#Array精密ティア(MethodDispatcher::KernelDispatch。新しい精密ティアディスパッチャーが、引数の値格子形状から要素型を証明できる場合はいつでもArray(arg)を精密なArray[E]にフォールドします。ルールはRubyの変換契約を反映します——Array(nil) -> []、既存のArray[E]はその要素を保持、TupleはArray[T1|T2|…]に実体化、Unionは要素単位で分配して統合します。不透明な形状(Top / Dynamic / Bot)は既存のRBSティアエンベロープにフォールスルーします。実例: lib/rigor/analysis/fact_store.rb#fact_targetsで、fact.target: Target | Array[Target]に対するArray(fact.target)は以前Array[Dynamic[top]]として型付けされていましたが、現在はArray[Target]として型付けされます。

  • 式位置の条件式に対するブランチ認識スコープ伝播Inference::ScopeIndexer.propagatePrism::IfNodePrism::UnlessNodeを特殊ケースとして扱うようになり、述語のナローイングされた真値 / 偽値スコープを対応するブランチサブツリーにスレッドします。以前、if / unlessが式位置(例: 呼び出し引数や[]=のRHS)にある場合、インデクサーはそれをeval_ifのナローイングパスに転送せず、内部ノードはナローイングなしのエントリースコープを継承し、下流のルール(possible-nil-receiver、type-ofプローブ)が偽のT | nilを見ていました。実例: cache[k] = if x; x.foo; else; default; endは真値ブランチ内でxがnilでないフラグメントにナローイングされるようになりました。これはステートメントレベル形式if x; cache[k] = x.foo; else; cache[k] = default; endの動作と一致します。

  • RbsLoader#instance_definition / #singleton_definitionuntyped?として宣言されるようになりました。以前のsig形式(untyped)は上記の真値ナローイングギャップの回避策でした;そのギャップが閉じたことで、sigが実装のnil-on-unknown-class戻り値契約を忠実に反映できます。

  • 2引数定数フォールドディスパッチMethodDispatcher::ConstantFolding#try_foldは以前args.sizeでスイッチし、0引数と1引数の形状のみを処理していました; Comparable#between?(min, max)Comparable#clamp(min, max)の明示的境界形式、Integer#pow(exp, mod)のような2引数のリーフメソッドはすべてRBS拡大ティアにベイルしていました。ディスパッチはtry_fold_ternaryを通じてルーティングされるwhen 2アームを獲得し、すべてのオペランドがConstant(またはUnion[Constant…])でカタログがメソッドを:leaf / :trivialと分類する場合にレシーバー × arg0 × arg1のデカルト積をフォールドします。バイナリパスを制限する同じUNION_FOLD_INPUT_LIMIT上限がデカルト爆発を防ぎます。IntegerRangeオペランドはフォローアップのために予約されています。実例: 5.between?(0, 10)Constant[true]にフォールド、100.clamp(0, 10)Constant[10]にフォールド、100.pow(50, 17)Constant[4]にフォールド。

  • tool/catalog_diff.rb + make catalog-diff。2つのdata/builtins/ruby_core/<topic>.ymlスナップショット間のサーフェスレベルの差分を出力します——クラスごとの追加 / 削除 / 純粋性変更 / cfuncリネーム / アリティ変更。動機となるユースケースは、完全なYAML差分が散文コメント、RBSプル、defined_at行番号をインターリーブするため見通しが悪いreferences/rubyサブモジュールのバンプです;このツールはレビュアーが確認すべきカタログセマンティクスの差分を抽出します。デフォルト呼び出し: make catalog-diff BEFORE=… AFTER=…

  • Cボディクラシファイアーが純粋なrb_check_frozenラッパーをミューテーターとして検出するようになりましたtime_modify(time) / time_gmtime(time)のようなボディ全体が1つ以上のrb_check_frozen(...)呼び出しのみのクラスごとのラッパーは、レシーバーのミューテーションゲートを集中管理しているにもかかわらず:leafとして分類されていました。CBodyIndex#mutator_helpersは純粋な凍結チェックパターンにマッチするボディを持つインデックス済みcfuncのセットを返すようになりました; CBodyClassifier.classifyはメソッドがそれらのヘルパーのいずれかを呼び出す時に:mutateエフェクトをフリップします。このパターンは意図的に狭く制限されています——単純な推移的伝播はArray#to_aのような正当な非ミューテーターを過剰にフラグ付けしたため、rb_check_frozen呼び出しのみで構成されるボディのみが適格です。再抽出により2つのTimeメソッド(#gmtime#utc、両方ともtime_gmtimeにバインド)が:leafから:mutates_selfに切り替わります;他のすべてのカタログはバイト同一で再生成されます。

  • include対応のモジュールカタログフォールスルーがComparable / EnumerableインポートをアクティブにしますMethodDispatcher::ConstantFolding#catalog_allows?がレシーバークラスのModule#ancestorsを歩き、プライマリクラスカタログにメソッドのエントリーがない場合にインポートされたモジュールカタログ(COMPARABLE_CATALOGENUMERABLE_CATALOG)を参照するようになりました。解決: プライマリクラスカタログが最初(そのrb_define_method登録はエントリーが:dispatchと分類されていても権威あります)、プライマリにエントリーがない場合のみモジュールカタログ。ユーザーが見えるペイオフ: 直接のrb_define_method登録なしに純粋にinclude Comparable / include Enumerableミックスインから来るメソッドがフォールドするようになりました。実例: 5.clamp(0..10)Constant[5]100.clamp(0..10)Constant[10]にフォールド。

  • ComparableとEnumerableモジュールカタログインポート。新しいdata/builtins/ruby_core/comparable.ymlenumerable.ymlInit_Comparable(compar.c)とInit_Enumerable(enum.c)からtool/extract_builtin_catalog.rbによって生成されました。カタログ統計: Comparableには7インスタンスメソッド(< / <= / == / >= / > / between? / clampファミリー); Enumerableには58インスタンスメソッド(47 :block_dependent、9 :leaf、2 :mutates_self)。対応するBuiltins::COMPARABLE_CATALOG / Builtins::ENUMERABLE_CATALOGシングルトンはブート時にロードされますが、モジュールはディスパッチャーがルーティングするレシーバークラスではないためMethodDispatcher::ConstantFolding::CATALOG_BY_CLASSに登録されません;データはレシーバーの祖先チェーンを歩く将来のinclude対応ルックアップのために用意されています。

  • tool/scaffold_builtin_catalog.rb --module。スキャフォールドスクリプトはモジュールモードを獲得しました。このモードはすべてが意味をなさないCATALOG_BY_CLASS行、フィクスチャスタブ、統合describeブロックをスキップします(include対応ディスパッチが着地するまで)。ローダーファイルはモジュール対応バナーを持ちます; require_relativeはシングルトンがアクセス可能なまま挿入されます。関連する抽出器アップグレード(MODULE_DEFINE_RE)はrb_mFoo = rb_define_module("Foo");登録を認識し、各トピックのclassesマップにparent: "Module"でモジュールを記録します。以前はドロップされていた2つのモジュール登録(Init_FileFileTestInit_StringUnicodeNormalize)がそれぞれのYAMLで空バケットクラスエントリーとして表面化するようになりました。

  • IntegerRangeとIntersectionへの~refinement否定の拡張Narrowing.narrow_not_refinementは以前Difference[base, Constant[v]]のみを処理していましたが、代数は2つのキャリア種類をさらにカバーします:

    • Type::IntegerRange[a, b] — 補完は2つの開いた半分int<min, a-1>int<b+1, max>で、それぞれcurrent_typeの整数ドメイン部分と積集合します。Unionレシーバーの非整数部分は変更されません。Integerに対するassert n is ~int<5, 10>int<11, max> | int<min, 4>にナローイングします。エンドツーエンドのフィクスチャ: spec/integration/fixtures/assert_negation_integer_range/
    • Type::Intersection[M1, M2, …] — De Morgan: D \ (M1 ∩ M2) = (D \ M1) ∪ (D \ M2)。各メンバーの補完は独立して計算されてユニオン(union、合併型とも)されます;代数が補完を計算できないメンバー(Refined、非Constant Difference)はcurrent_type自体を寄与するため、ユニオンは拡大することがあります。Stringに対する~non-empty-lowercase-stringは、述語対応の補完で得られるより厳密なConstant[""]ではなくConstant[""] | Nominal[String]を生成します。Refined[base, predicate]はその保守的なcurrent_type答えを保持します(述語補完は有限キャリアで表現できません)。
  • assert: / predicate-if-*:ディレクティブでの~refinement否定<target> is <RHS>右辺は精製アームで既存のクラス名アームに加えて~T否定プレフィックスを受け入れるようになりました。ナローイングティアはDifference + Constant除去形状のためにNarrowing.narrow_not_refinementを導入します: 現在の型のユニオンメンバーを歩き、精製のベースから分離された各部分を保持し、任意の現在のメンバーがそれをカバーする場合に除去された値Constantを正確に1回追加します。

    class Validator
    %a{rigor:v1:assert value is ~non-empty-string}
    def assert_empty!: (::String value) -> void
    end

    name: String | nilに対するv.assert_empty!(name)の後、ナローイングされた型はConstant[""] | NilClassです——non-empty-stringではないオリジナルのユニオンの唯一の住人たち。他の精製キャリア(RefinedIntersectionIntegerRange、および除去がConstantでないDifference)は今のところcurrent_typeを変更せずに返します;述語補完と境界範囲補完はフォローアップスライスです。

  • group_by / partition / each_slice / each_consブロックパラメータ射影(プレースホルダー;将来のプラグイン)。RBSはすでに汎用置換を通じてプレーンなArray[T] / Set[T] / Range[T]レシーバーに対してこれらのメソッドを正しくバインドします;新しいIteratorDispatchアームは、TupleおよびHashShape形状のレシーバーが射影されたArray[union]拡大ではなく精密な位置ごとの要素ユニオン(またはHashShapeのTuple[K, V]ペア)でブロックボディに到達するためのものです。

  • メモ型Enumerableブロックパラメータ射影IteratorDispatch#each_with_object(第2引数の実際の型にメモ型が従う(element, memo)をyield)と#inject / #reduce(memo, element)をyield)をカバーします。injectファミリーは3つの呼び出し形状を処理します:

    • inject(seed) { |memo, elem| … }[seed_type, element_type]
    • inject { |memo, elem| … } — 両方のブロックパラメータがレシーバーの要素型にバインドされます(Rubyの最初の要素をメモとするセマンティクス)。
    • inject(:+) / inject(seed, :+) — Symbol呼び出し形式にはブロックがありません;ディスパッチャーはそれらを認識して拒否します。
  • Date / DateTimeカタログインポート。新しいdata/builtins/ruby_core/date.ymlreferences/ruby/ext/date/date_core.cInit_date_corelib/date.rbプレリュードから生成されました。両クラスは単一トピックに着地します——DateTimeはDateから継承し、同じInit関数が両方を登録するため、tool/extract_builtin_catalog.rbは2つのRBSバインディング(date.rbsdate_time.rbs)を持つ1つのエントリーを持ちます。カタログ統計: 2クラス、96インスタンスメソッド、60シングルトンメソッド、149 :leaf / 2 :mutates_self / 3 :block_dependent分類。

  • tool/extract_builtin_catalog.rbの複数行ブロックコメントCInitParser#join_continuationsはInit関数ボディを1行ずつ走り、パレン深度を追跡して複数行の登録マクロを1つの論理行にマージします。以前のstrip_line_commentsヘルパーは1行に収まる/* … */の実行のみを除去していたため、(date_core.ccDateTime = rb_define_class("DateTime", cDate);の前に200行の/* … */ブロックがあるような、rb_define_class呼び出しの上によく見られる)複数行rdocブロックが深度カウンタへのアンバランスなパレンを寄与し、次のコード行がコメントバッファにマージされていました。修正は行ごとのインデックスが有効のまま残るよう改行を保持しながらCソース全体からブロックコメントを事前除去します。

第4プレビュー。テーマ: OQ3精製キャリア戦略の完成とRBS::Extendedディレクティブサーフェスの拡張

OQ3キャリアトリプル(v0.0.3からのType::Differenceに加えて新しいType::RefinedType::Intersection)はインポートされた組み込みカタログ(docs/type-specification/imported-built-in-types.md)に対して機能完成しています。作者は%a{rigor:v1:…}アノテーションから精製名のフルセットを表現でき、解析器はメソッドディスパッチ、受け入れ、argument-type-mismatchチェックルールを通じてそれらを対称的に射影します。

RBS::Extendedディレクティブサーフェスはrigor:v1:param:(コール境界とメソッドボディ内でのMethodParameterBinderを通じた両方)を取り込み、既存のassert* / predicate-if-*ファミリーは右辺で精製ペイロードを受け入れるようになりました。

組み込みカタログインポートパイプラインはさらに4クラス(Hash / Range / Set / Time)とtool/scaffold_builtin_catalog.rbスクリプトを獲得し、各新しいインポートの機械的な70 %を自動化します。

テスト数: 1148 → 1250例(+102)、RuboCopクリーン、bundle exec exe/rigor check libは0件の診断。

  • Type::Refinedキャリア(述語サブセット半分)Type::Differenceのシブリング。Type::Refined::PREDICATESから引かれたSymbolである(base, predicate_id)をラップします。構築はType::Combinator.refined(base, predicate_id)と以下に列挙するごとの名前ファクトリーを通じて行われます。RBS消去はキャリアをそのベース名目的型に折り戻します。漸進的(gradual)モードの受け入れは保守的なaccepts_differenceポリシーを反映します——同じ述語のRefinedと認識されたConstant値は:yes、その他の形状は:noを得ます。
  • Type::Intersectionキャリア——合成精製名Union / Difference / Refinedと並んでIntersectionピアを追加してOQ3キャリア戦略を完成させます。キャリアはメンバーの値セットの交わりを表します。構築はdocs/type-specification/value-lattice.mdの決定論的正規化を実行します——flatten / drop-Top / Bot-absorb / dedupe / sort / 0-1 collapse——2つの等しい積集合が構築順序に関わらず等しく比較されます。受け入れはLHSで連言的、RHSで選言的、加えてトップレベルの構造的等値性短絡。ShapeDispatch.dispatch_intersectionはすべての結果が境界整数の場合IntegerRangeの交わりを通じてメンバーごとの射影を組み合わせます。(non_empty_string ∩ lowercase_string).sizeはより緩いnon-negative-intではなくpositive-intに解決します。
  • 14のインポートされた組み込み精製名。すべてBuiltins::ImportedRefinements(およびType::Combinatorのごとの名前ファクトリー)を通じて解決可能:
    • ポイント除去(v0.0.3からすでに存在): non-empty-stringnon-zero-intnon-empty-array[T]non-empty-hash[K, V]
    • IntegerRangeエイリアス(v0.0.3からすでに存在): positive-intnon-negative-intnegative-intnon-positive-int
    • 述語(新規): lowercase-stringuppercase-stringnumeric-stringdecimal-int-stringoctal-int-stringhex-int-string。base-N int-string述語は設計上互いに素です——:octal_int:hex_intはその慣習的なプレフィックス(0o / 0O / 先頭0; 0x / 0X)を必要とするため、ベアな"755"decimal-int-stringでありoctal-int-stringではありません。
    • 合成積集合(新規): non-empty-lowercase-stringnon-empty-uppercase-string
  • Refined[String, …]に対するカタログティア射影String#downcase / String#upcaseは述語ごとにフォールドします: :lowercase / :uppercase / :numericと3つのbase-N int-string述語に対するケースフォールドの冪等性、加えてクロス呼び出しのためのlowercase ↔ uppercaseリフト。サイズティア射影は述語キャリアを通じても適用されるため、Refined[String, *]に対するString#sizenon-negative-intに引き締まります。

RBS::Extendedディレクティブサーフェス

Section titled “RBS::Extendedディレクティブサーフェス”
  • rigor:v1:return:はパラメータ化された精製ペイロードを受け入れます。ベア名形状に加えて、ディレクティブはnon-empty-array[T] / non-empty-hash[K, V]T / K / Vがkebab-case精製名またはCapitalisedなRBSクラス名であり得る型引数ペイロード)とint<min, max>(符号付き整数リテラルを持つ境界整数範囲)を受け入れるようになりました。パーシングは新しいBuiltins::ImportedRefinements::Parser再帰下降パーサに存在し、ImportedRefinements.parse(payload)を通じて公開されます。失敗はフェイルソフトです——パースミスはnilを返し、ディレクティブサイトはRBS宣言型にフォールバックします。

  • rigor:v1:param: <name> [is] <refinement>ディレクティブ。v0.0.3で着地したreturn:ルートに対称的で、メソッド境界の両側で機能完成:

    • コールサイト半分OverloadSelectorargument-type-mismatchチェックルールがRbsExtended.param_type_override_map(method_def)を参照し、RBS翻訳型よりオーバーライドを優先するため、広すぎるコールサイトがフラグ付けされます。
    • ボディ側半分MethodParameterBinderは同じオーバーライドマップを読み、RBS翻訳パラメータバインディングを精製で置き換えます。これにより、推論中にメソッドボディ内でキャリアを通じた射影(例: non-empty-stringパラメータに対するid.sizepositive-intに解決)が観察可能になります。

    オプショナルなisグルーワードは既存のassert / predicate-if-*サーフェスとマッチします;作者はparam: id non-empty-stringと書くことも可能です。

  • rigor:v1:assert:rigor:v1:predicate-if-*:が精製ペイロードを受け入れます<target> is <RHS>右辺はCapitalisedなクラス名(既存の動作)またはkebab-case精製ペイロードのいずれかにマッチするようになりました。AssertEffectPredicateEffectの両方がrefinement_typeフィールドを獲得します;ナローイングティアはそれが存在する場合にキャリアを置き換え、クラス名ディレクティブのためのレガシーnarrow_classパスを維持します。精製形式ディレクティブはまだ~T否定をサポートしません——差異対精製代数が必要であり、将来のスライスに予約されています。

  • accepts_nominalが精製キャリアをベースに射影しますDifferenceまたはRefinedを受け入れるNominalは以前、accepts_nominalのcaseステートメントに精製種類のブランチがなかったため:noにフォールスルーしていました。キャリアの値セットはそのベース名目的型のそれに含まれるため、other.baseに射影して受け入れを再実行することは健全(soundness)です——Intersectionコンジャンクションの配線中に潜在的なバグが表面化しました。
  • ネストされたDifferenceのprovably_disjoint_from_removed?Difference[A, R].accepts(Difference[B, R])は以前、内部差異のBASEがRから証明可能に非交叉であることを必要としていましたが、これは決して成り立ちません(Nominalベースは構築上において除去された値を含みます)。同じremovedで十分です。内部差異レイヤーで非交叉性が示されるからです。

第3プレビュー。v0.0.3は推論エンジンを「証明できる場所ではリテラル値を見る」ようにします。v0.0.2よりはるかに広いサーフェスに渡って: 積極的な定数フォールド(単項 + 二項 + Union[Constant]デカルト + 整数範囲算術 + Tupleシェイプdivmod)、PHPStan形式のインポートされた組み込み精製キャリア(non-empty-stringpositive-intnon-zero-intnon-empty-array[T]non-empty-hash[K, V]negative-intnon-positive-intnon-negative-int)、フォールドディスパッチャーを駆動する抽出済み組み込みメソッドカタログ(CRubyから自動抽出されたNumeric / String / Symbol / Array / IO / File)、イテレータブロックパラメータ型付け、スコープレベルの整数範囲ナローイング、case / when範囲ナローイング、証明可能な整数ゼロ除算のalways-raises診断、そしてRBS::Extendedの新しいrigor:v1:return:ディレクティブを通じた新しい精製キャリアのエンドツーエンドのオプトイン。

堅牢性原則(型のためのPostelの法則——戻り値は厳格に、パラメータは寛容に)は今や設計の根拠としてADR-5を持つ型仕様の規範的セクションです。

  • ユーザーメソッドを通じた積極的な定数フォールドRigor::Inference::MethodDispatcher::ConstantFoldingは、メソッドが厳選されたアローリストにあり、オペレーションがレシーバーのドメインで発生させることができず、結果がType::Combinator.constant_ofを通じてラウンドトリップするスカラーである場合はいつでも、Constantレシーバーと引数の実際のRubyメソッドを呼び出します。プロシージャ間推論(v0.0.2 #5)と組み合わさって:

    class Parity
    def is_odd(n) = n.odd?
    end
    Parity.new.is_odd(3) # v0.0.2では`false | true`
    # 現在は`Constant[true]`
  • Union[Constant…]に対するデカルトフォールド。二項算術と比較はUnionレシーバーと引数に対してペアワイズでフォールドし、重複を排除して精密なUnion[Constant…]結果を再構築します。UNION_FOLD_INPUT_LIMIT = 32UNION_FOLD_OUTPUT_LIMIT = 8で制限;整数のみの結果セットに対して出力上限を超えた場合、解析器は諦める代わりに正常に境界のIntegerRange[min, max]に拡大します。

  • Type::IntegerRangeキャリアと範囲算術。名前付きエイリアスを持つPHPStan形式のint<min, max>ファミリー——positive-int1..)、non-negative-int0..)、negative-int..-1)、non-positive-int..0)、int<a, b>。RBSではIntegerに消去されます。二項の+-*/%と単項のsucc / pred / abs / -@ / even? / odd? / bit_length / zero? / positive? / negative?がすべて精密にフォールドします。単一点積集合(int<5, 5>)はConstant[5]に折り畳まれます。

  • 比較と述語を通じたスコープレベル範囲ナローイングif x > 0 ... endは真値エッジでxpositive-intに、偽値エッジでnon-positive-intにナローイングします。<<=>=、反転形式(0 < x)、x.positive? / x.negative? / x.zero? / x.nonzero?x.between?(a, b)に対しても同様。ナローイングはすでにスコープ内にIntegerRange境界がある場合にそれと積集合を取ります。

  • case / when整数範囲ナローイングcase n when 1..10 then …はボディ内でnint<1, 10>にナローイングします; when 1...10int<1, 9>に(排他的終端); when (100..)int<100, max>に; when (..-1)negative-intに; when 0Constant[0]にナローイングします。

  • イテレータブロックパラメータ型付け5.times { |i| … }iint<0, 4>として型付けします; 1.times { |i| … }Constant[0]に折り畳まれます; 3.upto(7) { |i| … }7.downto(3) { |i| … }の両方がiint<3, 7>として型付けします。より広いIntegerレシーバー(Nominal[Integer]positive-int)はnon-negative-intにフォールバックします。

  • 証明可能に真値 / 偽値の述語に対するブランチ除去if 4.even? ; :even ; else ; :odd ; endは、述語のnarrow_truthy / narrow_falseyが一方をBotに折り畳む場合にConstant[:even]のみに解決します——デッドブランチはスキップされます。Constant[true] / Constant[false] / Nominal[Integer](常に真値)がすべて適格です; Union[true, false]は以前と同様に両方のブランチをアクティブに保ちます。

  • Tuple形状のInteger#divmod / Float#divmodフォールド5.divmod(3)Tuple[Constant[1], Constant[2]]にリフトするため、マルチターゲット分解が各スロットの型をローカルにスレッドします(q, r = 11.divmod(4)q: 2r: 3をバインドします)。Float / Integer-Floatミックスdivmodは混合Tuple[Constant<Integer>, Constant<Float>]を生成します。

  • 組み込みメソッドカタログ抽出パイプラインtool/extract_builtin_catalog.rbはCRubyのInit_<Topic>ブロック(Numeric / Integer / Float / String / Symbol / Array / IO / File)を解析し、各cfuncボディを静的に分類し(leaf / leaf-when-numeric / dispatch / block-dependent / mutates-self / raises / unknown)、対応するreferences/rbs/core/*.rbsシグネチャと結合します。出力はdata/builtins/ruby_core/<topic>.ymlに存在します(make extract-builtin-catalogsで再生成)。生成されたYAMLはgemとともにリリースされます。

  • Type::Differenceキャリア(OQ3ポイント除去半分)Difference[base, removed]baseから有限の除去値セットを引いた値を表し、「non-empty / non-zero / non-empty-array / non-empty-hash」ファミリーのすべてのインポートされた組み込み精製が使用する構造的プリミティブです。受け入れは保守的です: Constantと同じ除去のDifference候補のみが除去セットから非交叉と証明できるため、Difference[String, ""].accepts(Nominal[String])は正しくnoを返します(より広いNominalは""である可能性があります)。

  • Rigor::Builtins::ImportedRefinementsレジストリ。すべてのインポートされた組み込みkebab-case名を対応するRigor型キャリアにマッピングします。RBS::Extendedと将来のトークナイザースライスのための単一統合ポイントです。

  • rigor:v1:return: RBS::Extendedディレクティブ。メソッドのRBS宣言戻り型をインポートされた組み込み精製の1つでオーバーライドします:

    class User
    %a{rigor:v1:return: non-empty-string}
    def name: () -> String
    %a{rigor:v1:return: positive-int}
    def age: () -> Integer
    end

    コールサイトでオーバーライドが伝播します: User.new.name.sizepositive-intUser.new.name.empty?Constant[false]User.new.age.zero?Constant[false]。RBS消去はベース名目的型のまま残るため、通常のRBSへのラウンドトリップは影響を受けません。不明な精製名はRBS宣言戻り値に低下します(サイレントミス、クラッシュなし)。

  • always-raises診断ルール5 / 05 % 05.div(0)5.modulo(0)5.divmod(0)rand(100) / 0はすべてルールalways-raises(「常にZeroDivisionErrorを発生させる」)下で:error診断として表面化します。Float算術(5.0 / 0Infinityを返す)とInteger#fdiv(0)はサイレントのままです。# rigor:disable always-raisesで行ごとに抑制可能。

  • 堅牢性原則(PostelのTypeのための法則)。新しいADR(docs/adr/5-robustness-principle.md)と規範的仕様セクション(docs/type-specification/robustness-principle.md)が非対称な作成ルールを文書化します: Rigorが作成した戻り型は証明できる限り厳格であるべき; Rigorが作成したパラメータ型はボディの正しい動作が許す限り寛容であるべき。手書きのRBS作成が拘束し;この原則はRigorのデフォルトのみを指示します。

  • Rigor::Analysis::CheckRulesarity_eligible? / argument_check_eligible?は、RBS関数がRBS::Types::UntypedFunction(例: (?) ->または特定のstdlib可変長シグネチャ)の場合に発生させなくなりました。両方の述語は型なし関数に対して保守的な結果としてfalseを返します——ファイルの解析をクラッシュさせる代わりに。

  • ConstantFoldingのユニオンフォールドは、メソッドがサポートされていないメンバーをサイレントにドロップしなくなりました。以前の動作では、String#nil?STRING_UNARYになく、部分フォールドがStringペアをドロップしたため、Union[Constant[String], Constant[nil]].nil?Constant[true]にフォールドしていました。フォールドはすべてのレシーバーのメソッドがアローセット内にあることを要求するようになりました;部分カバレッジは間違った答えを生成する代わりにRBSにベイルします。

第2プレビュー。v0.0.2はv0.0.1パイプライン周りの必須エンベロープを閉じます: より豊富なRBS::Extendedディレクティブサーフェス(assert / assert-if-true / assert-if-false~T否定、target: self)、ユーザー定義メソッドのプロシージャ間推論、argument-type-mismatchルール、ルールごとの診断抑制(プロジェクトレベル + インソースコメント)、stdlibライブラリとシグネチャパスの設定パススルー、フェイルソフトフォールバックイベントを表面化する--explainモード。

  • rigor check --explainモード。エンジンがDynamic[Top]に低下した場所をユーザーが見られるよう、フェイルソフト推論フォールバックを:info診断として表面化します。各イベントはRigor::Inference::CoverageScannerによって駆動されるため、それをトリガーしたリーフノードに帰属できます(ProgramNode / StatementsNode / ParenthesesNodeのようなパススルーラッパーは二重カウントされません)。各診断はrule: "fallback"severity: :info、ノードクラスとエンジンがフォールバックした型を名前に含む短いメッセージを持ちます。Info診断は実行を失敗させません。

  • .rigor.ymllibraries:signature_paths:キー。設定レイヤーがRigor::Environment.for_projectにパススルーするようになりました:

    • libraries:Environment::DEFAULT_LIBRARIESに加えてロードするstdlibライブラリをリストします(例: ["csv", "set"])。各エントリーはRBS::EnvironmentLoader#has_library?が受け入れる名前でなければなりません;不明なライブラリはフェイルソフト。
    • signature_paths:sig/形式ディレクトリの明示的なリストです。キーを未設定(またはnull)にすると<root>/sigの自動検出デフォルトを保持します; []はプロジェクトRBSロードを完全に無効にします。

    rigor checkrigor type-ofrigor type-scanを通じて配線されます(後の2つはcheckと一致する--config=PATHオプションを獲得します)。

  • ルールごとの診断抑制。2つのメカニズムが合成されます:

    • プロジェクトレベル: .rigor.ymlの新しいdisable:キーがrigor checkルール識別子のリスト(undefined-methodwrong-arityargument-type-mismatchpossible-nil-receiverdump-typeassert-type)を受け入れます;マッチする診断はプロジェクト全体でサイレント化されます。
    • インソース: 問題のある行の末尾の# rigor:disable <rule>(または<rule1>, <rule2>)が行ごとにサイレント化します。# rigor:disable allはその行のすべてのルールを抑制します。

    Rigor::Analysis::Diagnosticはソースルールの安定した識別子を持つrule:フィールドを獲得します。パースエラー / パスエラー / 内部解析器エラーはrulenilとして残し、抑制不可のままです。

  • ユーザー定義メソッドのプロシージャ間推論。コールのレシーバーがRBSシグなしのユーザー定義クラスのNominal[T]でメソッドがインスタンスdefとして発見されている場合、エンジンはコールサイトでコールの引数型をパラメータにバインドしてメソッドのボディを再型付けし、ボディの最後の式の型を返します。user_methods.rb統合フィクスチャはRBSシグなしでParity.new.is_odd(3)false | trueに解決するようになりました(v0.0.1ではDynamic[top])。

    最初のイテレーションは最もシンプルなパラメータ形状(必須の位置引数、オプショナル / rest / キーワード / ブロックパラメータなし)のみを受け入れます;レシーバーはNominal(Singletonではない)でなければなりません;再帰はスレッドごとの推論スタックで防がれるため、相互再帰ヘルパーは無限ループの代わりにDynamic[Top]にフォールバックします。

  • argument-type-mismatchルール。すべての明示的レシーバーのPrism::CallNoderest_positionalsなし、必須キーワードなし、末尾の位置引数なし)に対して、ルールは各位置引数の推論された型をRigor::Inference::Acceptance.accepts(parameter, argument, mode: :gradual)を通じてルーティングし、パラメータが受け入れない最初の引数に:errorを発生させます。Dynamicとしてのみ既知の引数またはパラメータ型はチェックをスキップします(コールは静的に反証できません)。

  • Rigor::Inference::AcceptanceSingleton[T]ModuleClassObjectBasicObjectの部分型(subtype)として扱うようになりました。このルールなしに、パラメータがClass | Moduleとして型付けされているメソッド(例: Object#is_a?Module#define_method)はすべてのsingletonレシーバーを拒否し、lib/spec/の両方に全体的な偽陽性を生成していました。

  • RBS::Extendedtarget: selfディレクティブが実際にマッチするエッジでレシーバーローカルをナローイングするようになりました(以前: パーサは受け入れたがエンジンは破棄していた)。3つのルール形状をカバーします:

    • predicate-if-true self is LoggedInUser / predicate-if-false self is Userif / unless述語の真値 / 偽値エッジでレシーバーローカルをナローイングします。
    • assert-if-true self is AdminUser — 同じ形状、コールが真値述語として観察された場合に適用されます。
    • assert self is RegisteredUser — ポストコールスコープで無条件にレシーバーローカルをナローイングします。

    ナローイングはコールのレシーバーがPrism::LocalVariableReadNode(エンジンのナローイングサーフェス)であり、レシーバー型が静的に既知(Nominal / Singleton / Constant——どのクラスのメソッドがアノテーションを持つかを解決するためにエンジンが必要)の場合のみ発火します。

  • RBS::Extended~ClassName構文を通じて述語 / アサートディレクティブで否定を認識するようになりました:

    • predicate-if-true value is ~NilClassは真値エッジでvalueNilClassから離れる方向にナローイングします。
    • assert value is ~NilClassはポストコールスコープでvalueNilClassから離れる方向にナローイングします。
  • RBS::Extendedは3つの追加ディレクティブを認識します:

    • rigor:v1:assert <target> is <Class> — ポストコールスコープで無条件にマッチする引数のローカルを精製します。
    • rigor:v1:assert-if-true <target> is <Class> — コールが真値述語として観察された場合(例: if call_node)に引数を精製します。
    • rigor:v1:assert-if-false <target> is <Class> — 偽値のための対称形。
  • Rigor::Environment::DEFAULT_LIBRARIEStmpdirstringioforwardabledigestsecurerandomを含むようになりました。一般的なstdlib呼び出し(Dir.mktmpdirStringIO.newForwardable#def_delegatorDigest::SHA256.hexdigestSecureRandom.hex)はユーザーがライブラリを列挙することなくそれらのRBSシグを通じて解決します。

  • Rigor::Analysis::CheckRulesdump_type / assert_typeルールは、コールサイトのself_typeRigorまたはRigor::Testingの場合に抑制されます。Rigor自身のスタブ内の再帰的なTesting.dump_type(value) / Testing.assert_type(...)呼び出しはrigor check libで診断を表面化しなくなりました。

最初のプレビューリリース。Rigorは実際のRubyプロジェクトに向け、フローセンシティブ(flow-sensitive)なスコープを通じてエンドツーエンドで型を推論し、小さいが実用的なルールカタログに対して診断を発行できます。

gemはRubyGemsにrigortypeとして公開されています(rigorの名前はすでに使われていました)。Rubyモジュール名はRigorのままなので、ユーザーコードはrequire "rigor"を使いRigor::ScopeRigor::Testingなどを参照します——gem install / Gemfileの行のみがrigortypeを使います。

  • rigor checkエンドツーエンドパイプライン。Prismを通じてRubyを解析し、ノードごとのスコープインデックスを構築し、3ルールカタログに対して実行します:
    • 型付きレシーバーでの未定義メソッド、
    • 位置引数の数の誤り、
    • 可能性があるnilレシーバー(セーフナビゲーションと早期リターンナローイングの除外を含む)。 再開クラス、define_methodで定義されたメソッド、定数宣言エイリアスクラス(YAMLPsych)、動的 / 未知のレシーバーに対する偽陽性は抑制されます。
  • rigor type-of FILE:LINE:COL — 任意のソース位置での推論型をプローブします。
  • rigor type-scan PATH... — ツリーに対するカバレッジレポート。
  • rigor init — ヘッダーコメント付きの.rigor.ymlを書き込みます。
  • 型モデルTopBotDynamic[T]Constant[v]Nominal[Class, type_args]Singleton[Class]Union[A, B, ...]Tuple[T1, ..., Tn]、required / optional / read-onlyキーポリシーを持つHashShapeキャリア。Trinaryyes / no / maybe)とAcceptsResult
  • 推論エンジンRigor::Scopeを通じて追跡されるローカル、インスタンス、クラス、グローバル変数バインディング。クロスメソッドivar / cvarアキュムレータがScopeIndexerプレパスで設定されます;プログラム全体のグローバル。
  • 複合書き込み||=&&=+=-=*=など)がMethodDispatcherを通じた演算子ディスパッチとともに、すべての変数種類のスコープを通じてスレッドされます。
  • self型付け。クラスとメソッドボディの境界がSingleton[T] / Nominal[T]を注入します;暗黙的自己コールディスパッチが囲むクラスのRBSを通じてルーティングされます。
  • 語彙的定数ルックアップ。プロジェクトsig、RBSコア、一般的なstdlibバンドル(pathname、optparse、json、yaml、fileutils、tempfile、uri、logger、date、prism、rbs)、インソースクラス発見、インソース定数値追跡。
  • 述語ナローイング。真偽値、nil?is_a? / kind_of? / instance_of?、有限リテラル等値、Class / Module / Range / Regexpに対する===(ケース等値)、case / when統合。
  • ブロックパラメータバインディング — 分解(|(a, b), c|)と番号付きパラメータ(_1_2など)を含む。ジェネリックメソッドを通じたブロック戻り型アップリフトにより[1, 2, 3].map { |n| n.to_s }Array[String]に解決します。
  • クロージャエスケープ解析。コアとstdlibのブロックを受け入れるメソッドのカタログが:non_escaping(Array#each / map / select / …)、:escaping(Module#define_method、Thread.new、Proc.new、…)、:unknownとして分類されます。エスケープコールはブロックが再バインドできるキャプチャされた外部ローカルのナローイングされた型をドロップし、closure_escapeファクトをFactStoreに記録します。
  • RBS::Extended述語効果。RBSシグネチャに%a{rigor:v1:predicate-if-true target is T} / predicate-if-falseアノテーションを持つメソッドは対応するエッジでマッチする引数をナローイングします。
  • PHPStan形式の型ヘルパーRigor::Testing.dump_typeは推論された型を:info診断として表面化します; Rigor::Testing.assert_type("expected", value)は推論された型の短い説明がマッチしない場合にエラーを発生させます。フィクスチャを自己アサーティングにするために使用します。
  • 自己アサーティング統合スイートspec/integration/fixtures/下のフィクスチャ駆動の例——等価性 / case-when / 複合書き込み / is_a?ナローイング / TupleとHashShapeアクセス / Array#mapブロック戻り型アップリフト / 早期リターンナローイング / RBS::Extended述語 / ユーザー定義メソッドディスパッチをカバーします。

既知の制限事項(v0.0.2に延期)

Section titled “既知の制限事項(v0.0.2に延期)”
  • ユーザー定義メソッドのプロシージャ間推論。def is_odd(n) = n.odd?のようなヘルパーはdef内で正しく型付けされますが、呼び出し元はRBSシグが提供されるまでDynamic[top]を観察します。
  • RBS::Extendedは述語効果サーフェスのみをリリースします。assert / assert-if-true / assert-if-false、否定(~T)、自己ターゲットナローイング、積集合 / ユニオン精製、param / return / conforms-toディレクティブは延期されます。
  • 永続キャッシュなし——すべてのrigor check実行がプロジェクトを再解析して再型付けします。
  • バンドルされたRBS::Extendedリーダーを超えたプラグイン貢献レイヤーなし。
  • ルールごとの重大度は:errorにハードコードされています(dump_type用の:infoを予約);ルールごとの設定と抑制コメントは延期されます。

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