Skip to content

静的構築子が書く静的フィールドを診断で断つ - #257

Merged
routersys merged 8 commits into
mainfrom
dev/ComputeWeave.StaticConstructorAssignment
Sep 19, 2026
Merged

routersys merged 8 commits into
mainfrom
dev/ComputeWeave.StaticConstructorAssignment

Conversation

@routersys

@routersys routersys commented Sep 15, 2026 •

Copy link
Copy Markdown
Owner

Summary / 概要

静的構築子が値を入れる静的フィールドを取り込む形を、CMPW0130、Direct2D では CMPWD2D0103 で断ります。課題 #250 を直します。

C# は型が最初に触れられたときに静的構築子を走らせ、静的フィールドの初期化子の後に本体を実行します。静的構築子が書いた静的フィールドは、その後ずっとその値を持ちます。生成HLSLは静的構築子を持たず、フィールドを初期化子の値で、初期化子が無ければ初期化子を持たない静的変数として書き出します。どちらのシェーダーコンパイラもそれを断らずに 0 として読むので、シェーダーは C# が計算しない値を黙って計算します。

歩きは、シェーダー型と、取り込んだメソッド・構築子・静的フィールドが属する型のそれぞれについて静的構築子を辿り、呼び出しが解決するメソッド、局所関数、構築子を初期化子の歩きと同じ形で追います。生成HLSLが宣言する静的フィールド(シェーダー自身のものと取り込んだもの)への書きを、どの型の静的構築子からであっても報告し、書いた位置とその構築子の型を名指しします。読みは報告しません。

Linked issue / 関連する課題

Closes #250

Kind / 種別

  • Bug fix / 不具合修正
  • Analyzer or generator / アナライザーまたはジェネレーター

Behavior change / 挙動の変更

これまで診断なく通っていた次の形が、CMPW0130 / CMPWD2D0103 の誤りになります。いずれも C# と違う値を計算していた形です。

  • 静的構築子が書く静的フィールド(初期化子の有無、readonly かどうかを問わず)を、初期化子や本文が読む形
  • 静的構築子が呼ぶメソッドの中で、または out 引数を通じて書かれる形
  • シェーダー自身の静的構築子がシェーダーの静的フィールドを書く形
  • 本文がメソッドを取り込んだ型の静的構築子が、シェーダーの静的フィールドを書く形

書きと見るのは、代入(単純代入と複合代入)、増分と減分、ref と out の引数です。ref は呼び先が書けるので書きに数えます。静的構築子がフィールドを読むだけの形、生成HLSLが宣言しないフィールドを書く形は報告しません。

診断が2つ増えるので、同梱する文書が述べる診断の件数は、この枝の土台で CMPW が112から113へ、CMPWD2D が100から101へ動き、PR #254 が入った origin/main を併合した先端では114と102です。上のどの形も持たない入力の生成物は変わりません。

Verification / 検証

dotnet build tests/ComputeWeave.Tests.SourceGenerators/ComputeWeave.Tests.SourceGenerators.csproj -c Release -p:Platform=x64
dotnet tests/ComputeWeave.Tests.SourceGenerators/bin/x64/Release/net10.0/ComputeWeave.Tests.SourceGenerators.dll
dotnet build tests/ComputeWeave.D2D1.Tests.SourceGenerators/ComputeWeave.D2D1.Tests.SourceGenerators.csproj -c Release -p:Platform=x64
dotnet tests/ComputeWeave.D2D1.Tests.SourceGenerators/bin/x64/Release/net10.0/ComputeWeave.D2D1.Tests.SourceGenerators.dll
pwsh build/verify-markdown-tables.ps1
pwsh build/verify-readme-counts.ps1
pwsh build/verify-analyzer-releases.ps1
pwsh build/audit-upstream-divergence.ps1

土台は 730eab57、実装は af839246、試験は a6c8dd57、台帳は c301dd42 です。計算側の生成器は土台の588件に対して598件、Direct2D側の生成器は236件に対して237件で、いずれも失敗0件です。土台の値は PR #243 の併合の先端で測ったものです。計算側で増える10件は足した行10本、Direct2D側の1件は足した試験1本です。表の検査は21文書409行が描画で欠けず、件数の検査は4文書とも一致、出荷の記録の検査は6 file の216項目がそれぞれ1つの履歴を持つと述べ、乖離の監査は待ち行列が空です。

直す前の形を測りました

生成器へ通した形と、生成物と同じ形の手書きの HLSL を同梱の DXC(1.7.2308.7)と SDK 10.0.26100.0 の FXC へ当てて格納される値を読んだもの、C# をコンソールで走らせた値です。実機では読んでいません。

形 診断 C# DXC FXC
readonly の静的フィールドへ静的構築子が 5 を書き、本文が読む 無し 5 0 0
可変の静的フィールドへ静的構築子が 5 を書き、本文が読む 無し 5 0 0

生成HLSLは static const float V; と static float V; で、初期化子を持たない定数もどちらのコンパイラも断りません。

変異

実装をコミットしてから、歩きの判断を1つずつ壊しました。当てた木は c301dd42 で、変異を当てない対照では2つのスイートとも失敗0件です。狙った試験は計算側75件、Direct2D側10件で、初期化子の歩きの試験を含みます。

壊した判断 計算側 Direct2D側
計算側の生成器が静的構築子の歩きを呼ばない 7 0
Direct2D側の生成器が静的構築子の歩きを呼ばない 0 1
代入を書きと見ない 6 1
out と ref の引数を書きと見ない 1 0
静的構築子の読みも報告する 1 0
取り込んだ宣言の型の静的構築子を歩かない 6 1
シェーダー自身の静的フィールドへの書きを報告しない 2 0
静的構築子の本体を歩かない 7 1
呼び出しが解決するメソッドの本体を辿らない(初期化子の歩きと共有) 27 6

生成物を全数で突き合わせました

解の11 project を -t:Rebuild と -p:EmitCompilerGeneratedFiles=true で1本ずつ建て直し、書き出された file を土台で同じ道具で採った値と突き合わせました。11 project とも誤り0件で建ちます。両方に在る file は1080で、そのうち1078が1バイト同一、内容が違うものが2、片方にしか無いものは0です。違う2件は Direct2D の記述子で、同じコミットを2回建てても動く DXBC の検査値です。この木に既に在るシェーダーの生成物は1件も変わらず、新しい報告に当たるものもありません。

origin/main の併合

この枝を出したあとに PR #254 が既定ブランチへ入ったので、fa7f369e の origin/main を併合しました(6bdad9cd)。衝突は両側の出荷前の記録で、#254 の CMPW0129 と CMPWD2D0101 の行とこの枝の行を1つの表に並べ、同梱する文書の件数を114と102へ数え直しました。#254 は呼び出しの記録から再帰を断つ処理で、この枝の歩きと重なる箇所はありません。併合の先端で生成器の試験2本は計算側626件と Direct2D側241件がいずれも失敗0件で、生成物の全数調査は origin/main と突き合わせて1080 file のうち1078が1バイト同一、違う2件は同じ既知の DXBC の検査値、片方にしか無い file は0です。

Direct2D 側の番号の変更

この枝を出したあとに開いた PR #258 も Direct2D 側に CMPWD2D0102 を足していて、2本を併合した木は文字どおりには衝突せず、記述子が2つとも CMPWD2D0102 のまま誤りも警告も出さずに建ちます。この枝の側を CMPWD2D0103 へ移しました。記述子と出荷前の記録は cf1905fe、試験は af5eb90c、乖離台帳の行は ae9f4e9c です。移した先端で計算側の生成器は626件、Direct2D 側は241件が失敗0件で、表、件数、出荷の記録、乖離の4つの検査は通ります。同じ識別子を2つの記述子が持つことを試験で断つ PR #260 を別に出しました。その後に #258 が既定ブランチへ入ったので、4e654727 の origin/main を併合しました(0ef04d1f)。衝突は Direct2D 側の出荷前の記録の行で、#258 の CMPWD2D0102 とこの枝の CMPWD2D0103 を並べ、同梱する文書の Direct2D 側の件数を103へ数え直しました。この先端で生成器の試験2本は計算側626件と Direct2D 側248件が失敗0件で、生成物の全数調査は origin/main と突き合わせて1080 file のうち1078が1バイト同一、違う2件は同じ既知の DXBC の検査値、片方にしか無い file は0です。

走らせていないもの

ComputeWeave.Tests、ComputeWeave.D2D1.Tests、ComputeWeave.Tests.Internals、ComputeWeave.Tests.DeviceLost、ComputeWeave.Tests.DebugLayer、ComputeWeave.Tests.GlobalStatements、ComputeWeave.D2D1.Tests.AssemblyLevelAttributes は手元では走らせていません。変更は生成器の歩き1つと診断の追加に閉じており、上の全数比較のとおり生成物が1件も変わらないので、生成物を照合する試験と実行時の面に届く経路がありません。CI は併合の先端 0ef04d1f でビルドとテストの工程を通しました。そこで走った試験は ComputeWeave.Tests 4189、ComputeWeave.Tests.Internals 1085、ComputeWeave.Tests.DeviceLost 43、ComputeWeave.D2D1.Tests 206、ComputeWeave.D2D1.Tests.AssemblyLevelAttributes 4で、生成器の2本は626と248で、いずれも失敗0件です。

Checklist / 確認

  • The pull request is one logical change, with no unrelated refactoring or formatting. / 一つの論理的な変更にまとまっており、無関係な整理や整形を含まない。
  • Every commit builds on its own. / 各コミットが単独でビルドできる。
  • Implementation changes and their verification tests are separate commits. / 実装の変更と検証テストを別のコミットに分けた。
  • No commit already merged into the default branch or referenced by a release tag was rewritten. / 既定ブランチへ入った、またはリリースタグが指すコミットを書き換えていない。
  • The change follows the implementation pattern already established in the subsystem. / 変更は、その部分で既に確立された実装パターンに従っている。
  • Public and protected APIs carry the XML documentation the repository requires. / public と protected のAPIに、リポジトリが求めるXMLドキュメントを付けた。
  • Existing documentation comments were preserved. / 既存のドキュメントコメントを維持した。
  • No silent fallback behavior or compatibility shim was introduced. / 暗黙の代替動作や互換性のための繕いを持ち込んでいない。
  • No unrelated dependency was updated. / 無関係な依存関係を更新していない。
  • If a file inherited from upstream changed, the divergence ledger in CONTRIBUTING.md was updated, or the change was judged not to be a divergence. / 上流から受け継いだファイルを変更した場合、CONTRIBUTING.md の乖離台帳を更新したか、乖離ではないと判断した。

If this touches a guarded area / 慎重を要する箇所に触れる場合

  • Tests exercise the guarded behavior itself, and I confirmed they fail when that behavior is deliberately broken. / 試験が守るべき挙動そのものを動かしており、その挙動を意図的に壊すと落ちることを確認した。
  • For analyzer diagnostics that produce build errors, the complete solution was verified in addition to the analyzer tests. / ビルドエラーを出すアナライザー診断は、アナライザーの試験に加えて解全体を検証した。
  • For public API or generated descriptor changes, the compatibility, deterministic-generation or golden-data checks of the affected subsystem were run. / 公開APIまたは生成される記述子を変えた場合、該当部分の互換性、生成の決定性、または基準データの検査を走らせた。

Notes for the reviewer / 査読者への注記

静的構築子を持つ型を丸ごと断らなかった理由

静的構築子を持つ型の静的フィールドを全て断つ形は簡単ですが、構築子が触れないフィールドまで断ります。初期化子を持つフィールドは静的構築子が書き換えない限り生成HLSLと一致するので、断る根拠がありません。書きを見る形なら、断る条件と測った表がちょうど一致します。

触れる型を取り込んだ宣言の型まで広げた理由

C# は型が最初に触れられたときに静的構築子を走らせます。シェーダーが触れる型は、シェーダー型と、取り込んだメソッド、構築子、静的フィールドが属する型です。フィールドの型の静的構築子だけを見ると、本文がメソッドを取り込んだ型の静的構築子がシェーダーの静的フィールドへ書く形を見逃します。触れる型をこの4つから集めれば、静的構築子が書きうる生成HLSLの静的フィールドは全て覆えます。書きの対象を生成HLSLが宣言するフィールドに限るので、取り込まれないフィールドへの書きは報告しません。

初期化子の歩きと1つの歩きを共有した理由

呼び出しが解決するメソッド、局所関数、構築子を辿る部分は、初期化子の歩き(PR #243)と同じです。2箇所に同じ辿り方を書くと片方だけ直す形が生まれるので、辿る部分を1つの型にまとめ、初期化子の歩きはその上に、他の型のフィールドの初期化子へ入る辺と報告を載せ、静的構築子の歩きは書きの報告だけを載せる形にしました。初期化子の歩きの試験と変異はそのまま通ります。

触らなかったもの

静的構築子がシェーダーの静的フィールド以外へ与える効果は写しません。静的構築子の本体を生成HLSLへ書き出す道は無く、生成HLSLが宣言する静的フィールドに効かない効果はシェーダーの値に現れないので、報告もしません。

静的構築子の本体の中で、文の順や条件によって書きが起きない形も、書きとして報告します。歩きは文の順を追いません。

ref 引数で渡した静的フィールドを呼び先が書かない形も、書きとして報告します。呼び先が書けることが判定で、書いたかどうかは見ていません。

歩く静的構築子は触れる型のものだけです。触れた型の静的構築子がさらに別の型を触れ、その型の静的構築子が生成HLSLの静的フィールドを書く連鎖は、その別の型を取り込んでいなければ歩きません。取り込んだ宣言から先の連鎖は、呼び出しが解決する宣言を辿る形で届きます。

前提が崩れたときの戻し方

歩きは1つの処理で、両生成器から1行ずつ呼ばれています。呼び出しを外せば報告は消え、書き出しは変わりません。誤検出が出るなら、触れる型の集め方、書きの判定、歩きの辺がそれぞれ1箇所です。

測っていないこと

実機の値。Direct2D 側は生成器の試験1本で診断が出ることを確かめ、FXC の値は手書きの HLSL で読んだにとどまります。

@routersys routersys added bug Something isn't working / 不具合が起きている analyzer or generator Analyzer or generator / アナライザーまたはジェネレーター labels Sep 15, 2026
@github-actions

github-actions Bot commented Sep 15, 2026 •

Copy link
Copy Markdown

PRレビュー / PR review

変更 14 ファイル、コミット 8 件。 / 14 files, 8 commits.

検証の要否 / Verification required

以下を走らせ、失敗した試験の名前と失敗の様子を変更前と突き合わせてください。総数だけで判断しないでください。
Run the following and compare the names and failure modes against the same suites before your change. Do not judge by totals alone.

dotnet build ComputeWeave.sln -c Release -p:Platform=x64
dotnet test tests/ComputeWeave.Tests.SourceGenerators/ComputeWeave.Tests.SourceGenerators.csproj -c Release -p:Platform=x64
dotnet test tests/ComputeWeave.Tests.Internals/ComputeWeave.Tests.Internals.csproj -c Release -p:Platform=x64
dotnet test tests/ComputeWeave.Tests/ComputeWeave.Tests.csproj -c Release -p:Platform=x64
dotnet test tests/ComputeWeave.Tests.DeviceLost/ComputeWeave.Tests.DeviceLost.csproj -c Release -p:Platform=x64
dotnet test tests/ComputeWeave.D2D1.Tests.SourceGenerators/ComputeWeave.D2D1.Tests.SourceGenerators.csproj -c Release -p:Platform=x64
dotnet test tests/ComputeWeave.D2D1.Tests/ComputeWeave.D2D1.Tests.csproj -c Release -p:Platform=x64
dotnet test tests/ComputeWeave.D2D1.Tests.AssemblyLevelAttributes/ComputeWeave.D2D1.Tests.AssemblyLevelAttributes.csproj -c Release -p:Platform=x64

ビルドエラーを出す診断を変えた場合は、アナライザーの試験に加えて解全体の検証が要ります。
If a diagnostic that produces build errors changed, verify the complete solution as well.

慎重を要する箇所 / Guarded areas touched

  • 寿命と破棄 / lifetime and disposal
    • src/ComputeWeave.D2D1.SourceGenerators/AnalyzerReleases.Unshipped.md
    • src/ComputeWeave.SourceGeneration.Hlsl/SyntaxProcessors/HlslDefinitionsSyntaxProcessor.cs
    • src/ComputeWeave.SourceGenerators/AnalyzerReleases.Unshipped.md
  • 記述子と診断 / descriptors and diagnostics
    • src/ComputeWeave.D2D1.SourceGenerators/AnalyzerReleases.Unshipped.md
    • src/ComputeWeave.D2D1.SourceGenerators/D2DPixelShaderDescriptorGenerator.HlslSource.cs
    • src/ComputeWeave.D2D1.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs
    • src/ComputeWeave.SourceGenerators/AnalyzerReleases.Unshipped.md
    • src/ComputeWeave.SourceGenerators/ComputeShaderDescriptorGenerator.HlslSource.cs
    • ほか 1 件 / 1 more

寿命の追跡と危険の追跡は別々の保証です。一方を保っても他方が保たれるとは限りません。
Lifetime tracking and hazard tracking are separate guarantees; preserving one does not preserve the other.

公開APIまたは生成される記述子を変えた場合は、該当部分の互換性、生成の決定性、基準データの検査を走らせてください。
For public API or generated descriptor changes, run the compatibility, deterministic-generation or golden-data checks of the affected subsystem.

確認できた点 / Confirmed

  • 概要が書かれています。 / A summary is present.
  • 検証の結果が書かれています。 / Verification results are present.
  • 関連する課題が示されています。 / A linked issue is referenced.
  • 実装と検証テストが別のコミットに分かれています。 / Implementation and tests are in separate commits.

これは自動の確認で、査読の代わりにはなりません。指摘は判断の材料であって、従う義務はありません。
This is an automated check, not a substitute for review. Findings are input to a decision, not obligations.

…icConstructorAssignment

# Conflicts:
#	src/ComputeWeave.D2D1.SourceGenerators/AnalyzerReleases.Unshipped.md
@routersys
routersys merged commit 6a77e22 into main Sep 19, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

analyzer or generator Analyzer or generator / アナライザーまたはジェネレーター bug Something isn't working / 不具合が起きている

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 静的構築子で初期化される静的フィールドを取り込むと、生成HLSLは零として書き出す

1 participant