Repository: microsoft/DirectXShaderCompiler (2026-04-01T10:54:04Z)

Issue:
[SPIR-V] generated SPIR-V is invalid: Result type (OpTypeStruct) does not match the type that results from indexing into the composite (OpTypeStruct).
**Description**
While trying to compile my shader for SPIR-V I get an error regarding two OpTypeStruct that don't match. I get it when trying to assigning a ConstantBuffer<T> to a T in the shader.  See Godbolt link for more details.

**Steps to Reproduce**
See Godbolt link: https://godbolt.org/z/vT9jzazfr

It seems related to the fact there is a nested struct inside another struct used in a ConstantBuffer. They are then incompatible. 

**Actual Behavior**
```
fatal error: generated SPIR-V is invalid: Result type (OpTypeStruct) does not match the type that results from indexing into the composite (OpTypeStruct).
  %33 = OpCompositeExtract %DirectionalLight_0 %32 0
```

**Environment**
- DXC version 1.8.2505 (Godbolt link), also on 1.9.2602
- Host Operating System: Windows 11 25H2



Discussion:
The problem is that the result of the extract expects a type that is not explicitly laidout, but the input type is explicitly laidout. Somewhere we are not propagating the layout type correctly when generating the spir-v.

```
               OpMemberDecorate %DirectionalLight 0 Offset 0
               OpMemberDecorate %DirectionalLight 1 Offset 12
               OpMemberDecorate %DirectionalLight 2 Offset 16
...
%type_ConstantBuffer_Scene = OpTypeStruct %DirectionalLight
...
         %32 = OpLoad %type_ConstantBuffer_Scene %31
         %33 = OpCompositeExtract %DirectionalLight_0 %32 0
```
This issue is fixed, please try recompiling from head or wait for the next DXC release
~Reopened as there are more fixes to be made for the issue. The outputted SPIR-V doesn't pass the SPIR-V validator~

NVM! the HLSL should now compile successfully, and spir-v validator does run by default (it was the `-fcgl` flag that failed the validation).



Patch:
diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp
index e96a416d3d..01cfee8761 100644
--- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp
+++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp
@@ -1068,8 +1068,10 @@ DeclResultIdMapper::createFnParam(const ParmVarDecl *param,
   (void)getTypeAndCreateCounterForPotentialAliasVar(param, &isAlias);
   fnParamInstr->setContainsAliasComponent(isAlias);
 
-  if (isConstantTextureBuffer(type))
+  if (isConstantBuffer(type))
     fnParamInstr->setLayoutRule(spirvOptions.cBufferLayoutRule);
+  if (isTextureBuffer(type))
+    fnParamInstr->setLayoutRule(spirvOptions.tBufferLayoutRule);
 
   assert(astDecls[param].instr == nullptr);
   registerVariableForDecl(param, fnParamInstr);
@@ -1118,6 +1120,11 @@ DeclResultIdMapper::createFnVar(const VarDecl *var,
       spvBuilder.addFnVar(type, loc, name, isPrecise, isNointerp,
                           init.hasValue() ? init.getValue() : nullptr);
 
+  if (isConstantBuffer(type))
+    varInstr->setLayoutRule(spirvOptions.cBufferLayoutRule);
+  if (isTextureBuffer(type))
+    varInstr->setLayoutRule(spirvOptions.tBufferLayoutRule);
+
   bool isAlias = false;
   (void)getTypeAndCreateCounterForPotentialAliasVar(var, &isAlias);
   varInstr->setContainsAliasComponent(isAlias);


Test patch:
diff --git a/tools/clang/test/CodeGenSPIRV/type.constant-buffer.fn-var.hlsl b/tools/clang/test/CodeGenSPIRV/type.constant-buffer.fn-var.hlsl
new file mode 100644
index 0000000000..fb8f335d06
--- /dev/null
+++ b/tools/clang/test/CodeGenSPIRV/type.constant-buffer.fn-var.hlsl
@@ -0,0 +1,40 @@
+// RUN: %dxc -T vs_6_0 -E main -fcgl %s -spirv | FileCheck %s
+
+// CHECK-DAG: OpMemberDecorate %Inner 0 Offset 0
+
+// CHECK-DAG:                   %Inner = OpTypeStruct %float
+// CHECK-DAG: %type_ConstantBuffer_CBS = OpTypeStruct %Inner
+// CHECK-DAG:                 %Inner_0 = OpTypeStruct %float
+// CHECK-DAG:                     %CBS = OpTypeStruct %Inner_0
+struct Inner
+{
+    float field;
+};
+
+struct CBS
+{
+    Inner entry;
+};
+
+ConstantBuffer<CBS> input;
+
+float foo()
+{
+    ConstantBuffer<CBS> local = input;
+    CBS alias = local;
+// CHECK: %local = OpVariable %_ptr_Function_type_ConstantBuffer_CBS Function
+// CHECK: [[loadedInput:%[0-9]+]] = OpLoad %type_ConstantBuffer_CBS %input
+// CHECK: OpStore %local [[loadedInput]]
+// CHECK: [[loadedLocal:%[0-9]+]] = OpLoad %type_ConstantBuffer_CBS %local
+// CHECK:   [[e1:%[0-9]+]] = OpCompositeExtract %Inner [[loadedLocal]]
+// CHECK:   [[e2:%[0-9]+]] = OpCompositeExtract %float [[e1]]
+// CHECK:   [[c2:%[0-9]+]] = OpCompositeConstruct %Inner_0 [[e2]]
+// CHECK:   [[c1:%[0-9]+]] = OpCompositeConstruct %CBS [[c2]]
+// CHECK:                    OpStore %alias [[c1]]
+    return alias.entry.field;
+}
+
+float main() : A
+{
+    return foo();
+}
diff --git a/tools/clang/test/CodeGenSPIRV/type.texture-buffer.fn-var.hlsl b/tools/clang/test/CodeGenSPIRV/type.texture-buffer.fn-var.hlsl
new file mode 100644
index 0000000000..d90471e8aa
--- /dev/null
+++ b/tools/clang/test/CodeGenSPIRV/type.texture-buffer.fn-var.hlsl
@@ -0,0 +1,34 @@
+// RUN: %dxc -T vs_6_0 -E main -fvk-use-gl-layout -fcgl %s -spirv | FileCheck %s
+
+// CHECK-DAG:  OpDecorate %_arr_float_uint_2 ArrayStride 4
+// CHECK-DAG:  %type_TextureBuffer_CBS = OpTypeStruct %_arr_float_uint_2
+// CHECK-DAG:  %CBS = OpTypeStruct %_arr_float_uint_2_0
+
+struct CBS
+{
+    float entry[2];
+};
+
+TextureBuffer<CBS> input;
+
+float foo()
+{
+    TextureBuffer<CBS> local = input;
+    CBS alias = local;
+// CHECK: %local = OpVariable %_ptr_Function_type_TextureBuffer_CBS Function
+// CHECK: [[loadedInput:%[0-9]+]] = OpLoad %type_TextureBuffer_CBS %input
+// CHECK: OpStore %local [[loadedInput]]
+// CHECK: [[loadedLocal:%[0-9]+]] = OpLoad %type_TextureBuffer_CBS %local
+// CHECK:   [[e1:%[0-9]+]] = OpCompositeExtract %_arr_float_uint_2 [[loadedLocal]]
+// CHECK:   [[e2:%[0-9]+]] = OpCompositeExtract %float [[e1]] 0
+// CHECK:   [[e3:%[0-9]+]] = OpCompositeExtract %float [[e1]] 1
+// CHECK:   [[c1:%[0-9]+]] = OpCompositeConstruct %_arr_float_uint_2_0 [[e2]] [[e3]]
+// CHECK:   [[c2:%[0-9]+]] = OpCompositeConstruct %CBS [[c1]]
+// CHECK:                    OpStore %alias [[c2]]
+    return alias.entry[1];
+}
+
+float main() : A
+{
+    return foo();
+}
diff --git a/tools/clang/test/CodeGenSPIRV/type.texture-buffer.pass.hlsl b/tools/clang/test/CodeGenSPIRV/type.texture-buffer.pass.hlsl
index 04f5505ea4..7877e202b5 100644
--- a/tools/clang/test/CodeGenSPIRV/type.texture-buffer.pass.hlsl
+++ b/tools/clang/test/CodeGenSPIRV/type.texture-buffer.pass.hlsl
@@ -1,31 +1,26 @@
-// RUN: %dxc -T vs_6_0 -E main -fcgl  %s -spirv | FileCheck %s
+// RUN: %dxc -T vs_6_0 -E main -fvk-use-gl-layout -fcgl %s -spirv | FileCheck %s
 
-// CHECK-DAG: OpMemberDecorate %Inner 0 Offset 0
-
-// CHECK-DAG:                   %Inner = OpTypeStruct %float
-// CHECK-DAG:  %type_TextureBuffer_CBS = OpTypeStruct %Inner
-// CHECK-DAG:                 %Inner_0 = OpTypeStruct %float
-// CHECK-DAG:                     %CBS = OpTypeStruct %Inner_0
-struct Inner
-{
-    float field;
-};
+// CHECK-DAG:  OpDecorate %_arr_float_uint_2 ArrayStride 4
+// CHECK-DAG:  %type_TextureBuffer_CBS = OpTypeStruct %_arr_float_uint_2
+// CHECK-DAG:  %CBS = OpTypeStruct %_arr_float_uint_2_0
 
 struct CBS
 {
-    Inner entry;
+    float entry[2];
 };
 
 float foo(TextureBuffer<CBS> param)
 {
     CBS alias = param;
+// CHECK: %param = OpFunctionParameter %_ptr_Function_type_TextureBuffer_CBS
 // CHECK: [[copy:%[0-9]+]] = OpLoad %type_TextureBuffer_CBS %param
-// CHECK:   [[e1:%[0-9]+]] = OpCompositeExtract %Inner [[copy]]
-// CHECK:   [[e2:%[0-9]+]] = OpCompositeExtract %float [[e1]]
-// CHECK:   [[c2:%[0-9]+]] = OpCompositeConstruct %Inner_0 [[e2]]
-// CHECK:   [[c1:%[0-9]+]] = OpCompositeConstruct %CBS [[c2]]
-// CHECK:                    OpStore %alias [[c1]]
-    return alias.entry.field;
+// CHECK:   [[e1:%[0-9]+]] = OpCompositeExtract %_arr_float_uint_2 [[copy]]
+// CHECK:   [[e2:%[0-9]+]] = OpCompositeExtract %float [[e1]] 0
+// CHECK:   [[e3:%[0-9]+]] = OpCompositeExtract %float [[e1]] 1
+// CHECK:   [[c1:%[0-9]+]] = OpCompositeConstruct %_arr_float_uint_2_0 [[e2]] [[e3]]
+// CHECK:   [[c2:%[0-9]+]] = OpCompositeConstruct %CBS [[c1]]
+// CHECK:                    OpStore %alias [[c2]]
+    return alias.entry[1];
 }
 
 TextureBuffer<CBS> input;