Skip to content

[Bug] nint と nuint の定数を左辺に置いた演算が、診断なく 32 ビットで計算される #251

Closed

Description

@routersys

領域

シェーダーのHLSLへの変換

パッケージの版数

main の c050699d(PR #248 の併合直後)

診断ID

無し。構築は警告0件、エラー0件で通ります。

期待した挙動

nint と nuint は生成器が写す HLSL の型集合に無く、局所変数に書けば CMPW0050 が断ります。定数に書いた場合も同じ経路で断ることを期待します。PR #248 が long と ulong の定数に対してそうしました。

実際の挙動

const nint か const nuint のフィールドは、値の桁だけで #define に書き出され、診断は出ません。シェーダーはその定数を32ビットの int として計算しますが、C# は x64 の工程で nint を64ビットで計算します。結果が32ビットに収まらないとき、値が食い違います。

生成器を直に動かして読んだ結果です。土台は c050699d です。

書いた式 診断 生成 HLSL
const nint Own = 5; と (float)(Own + this.signed) 無し (float)(__Shaders_Shader__Own + __reserved__signed)、定義は #define __Shaders_Shader__Own 5
const nuint UnsignedOwn = 5; と (float)(UnsignedOwn + this.unsigned) 無し 同じ形
別の型の const nint をメンバーアクセスで読む 無し 同じ形
静的フィールドの初期化子の中の const nint 無し static const float Seeded = __Shaders_Helper__Native + 1
(float)Own 無し (float)__Shaders_Shader__Own
(float)(this.signed + Own)(対照、右辺) CMPW0125 断られる
nint wide = this.signed;(対照、局所変数) CMPW0050 断られる

再現手順

using ComputeWeave;

namespace Shaders;

[ThreadGroupSize(DefaultThreadGroupSizes.X)]
[GeneratedComputeShaderDescriptor]
internal readonly partial struct Shader : IComputeShader
{
    private const nint Own = 5;

    private const nuint UnsignedOwn = 5;

    private readonly ReadWriteBuffer<float> buffer;

    private readonly int signed;

    private readonly uint unsigned;

    public void Execute()
    {
        this.buffer[0] = (float)(Own + this.signed);
        this.buffer[1] = (float)(Own * this.signed);
        this.buffer[2] = (float)(UnsignedOwn + this.unsigned);
    }
}

signed が int.MaxValue、unsigned が uint.MaxValue のとき、x64 の C# では buffer[0] が 2147483652、buffer[1] が 10737418235、buffer[2] が 4294967300 を単精度へ丸めた値です。

測ったこと

上の3行を実機と WARP で走らせました。両方の装置で同じ値です。

式 C# の値(x64) 装置が返した値
(float)(Own + signed) 2147483648(2147483652 を丸めた値) -2147483648(-2147483644 を丸めた値)
(float)(Own * signed) 1.0737418E+10(10737418235 を丸めた値) 2147483648(2147483643 を丸めた値)
(float)(UnsignedOwn + unsigned) 4294967296(4294967300 を丸めた値) 4

装置の値は32ビットで折り返した整数を単精度へ丸めたものです。

なぜ既存の断りが捕まえないか

定数を #define へ書き出す補助メソッドは、定数の値から綴りを決めます。long と ulong の値は64ビットの整数として箱詰めされるので、PR #248 はそこで断ちました。nint と nuint の定数は、C# が定数の値を32ビットの範囲に限るため、32ビットの整数として箱詰めされます。値だけを見る判定は int の定数と見分けられず、桁をそのまま書き出します。

CMPW0125 は左辺の変換だけを読みます。定数を左辺に置くと変換が無く、右辺の変換は読まれません。右辺に置いた形は左辺の int から nint への変換で断られます。

どこまで広がるか

定数の型のうち、生成器が値から書き出す型と、断つ型はこう分かれます。bool、char、8ビットと16ビットの整数、int、uint、float、double、decimal は書き出します。8ビットと16ビットの整数と char は C# が演算のたびに int へ広げるので、32ビットで計算しても値は同じです。decimal は浮動小数点の綴りへ書き出す既存の扱いです。long、ulong、文字列は PR #248 が断ちました。残るのは nint と nuint だけで、これを断つと定数の型の族は閉じます。列挙型のメンバーは生成物に名前のまま残り、シェーダーコンパイラが落とします(CMPW0046)。

対応の案

定数を #define へ書き出す補助メソッドで、フィールドの型がネイティブ整数なら値を見ずに型を追跡します。報告は局所変数と long の定数と同じ CMPW0050(Direct2D 側は CMPWD2D0041)です。値では見分けられないので、この1つだけは型で見分けます。

測っていないこと

Direct2D 側の生成器で同じ定数を建てていません。書き換え器は共有なので同じ経路で書き出されると読んでいます。利用者のシェーダーにこの形がどれだけ在るかは分かりません。

デバイス

統合GPU, WARPソフトウェアアダプター

アダプターとドライバー

Intel(R) Iris(R) Xe Graphics, 32.0.101.7085

Windowsの版数

Windows 11 Pro 24H2, 26200

.NET SDKの版数

10.0.400

提出の前に

Activity

  1. added
    bugSomething isn't working / 不具合が起きている
    analyzer or generatorAnalyzer or generator / アナライザーまたはジェネレーター
    on Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions