Author: Deric C.
Date: 2026-02-18T19:05:21Z
New Revision: 76ddcdf1202fac52ba9b1c630289553055d2f480

URL: 
https://github.com/llvm/llvm-project/commit/76ddcdf1202fac52ba9b1c630289553055d2f480
DIFF: 
https://github.com/llvm/llvm-project/commit/76ddcdf1202fac52ba9b1c630289553055d2f480.diff

LOG: [Sema][HLSL][Matrix] Make matrices initializable by a single vector and 
vice-versa (#177486)

Fixes #169561

The problem was that the logic for determining whether or not to convert
constructor syntax into list initialization did not account for cases
where the constructor only had a single argument. Thus, the constructor
for a matrix with a single vector, or a vector with a single matrix did
not convert to list initialization despite being valid (in DXC).

This PR allows constructor syntax to be converted into list
initialization when the number of arguments in the constructor is 1, but
if and only if the destination type is a matrix/vector and the source
type is a vector/matrix.

Added: 
    clang/test/CodeGenHLSL/BasicFeatures/MatrixToAndFromVectorConstructors.hlsl

Modified: 
    clang/lib/Sema/SemaInit.cpp
    clang/test/SemaHLSL/Types/BuiltinMatrix/MatrixSplatErrors.hlsl

Removed: 
    


################################################################################
diff  --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 498ffd0887630..b79c22603494c 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -6947,10 +6947,39 @@ void InitializationSequence::InitializeFrom(Sema &S,
   // For HLSL ext vector types we allow list initialization behavior for C++
   // functional cast expressions which look like constructor syntax. This is
   // accomplished by converting initialization arguments to InitListExpr.
-  if (S.getLangOpts().HLSL && Args.size() > 1 &&
-      (DestType->isExtVectorType() || DestType->isConstantMatrixType()) &&
-      (SourceType.isNull() ||
-       !Context.hasSameUnqualifiedType(SourceType, DestType))) {
+  auto ShouldTryListInitialization = [&]() -> bool {
+    // Only try list initialization for HLSL.
+    if (!S.getLangOpts().HLSL)
+      return false;
+
+    bool DestIsVec = DestType->isExtVectorType();
+    bool DestIsMat = DestType->isConstantMatrixType();
+
+    // If the destination type is neither a vector nor a matrix, then don't try
+    // list initialization.
+    if (!DestIsVec && !DestIsMat)
+      return false;
+
+    // If there is only a single source argument, then only try list
+    // initialization if initializing a matrix with a vector or vice versa.
+    if (Args.size() == 1) {
+      assert(!SourceType.isNull() &&
+             "Source QualType should not be null when arg size is exactly 1");
+      bool SourceIsVec = SourceType->isExtVectorType();
+      bool SourceIsMat = SourceType->isConstantMatrixType();
+
+      if (DestIsMat && !SourceIsVec)
+        return false;
+      if (DestIsVec && !SourceIsMat)
+        return false;
+    }
+
+    // Try list initialization if the source type is null or if the
+    // destination and source types 
diff er.
+    return SourceType.isNull() ||
+           !Context.hasSameUnqualifiedType(SourceType, DestType);
+  };
+  if (ShouldTryListInitialization()) {
     InitListExpr *ILE = new (Context)
         InitListExpr(S.getASTContext(), Args.front()->getBeginLoc(), Args,
                      Args.back()->getEndLoc());

diff  --git 
a/clang/test/CodeGenHLSL/BasicFeatures/MatrixToAndFromVectorConstructors.hlsl 
b/clang/test/CodeGenHLSL/BasicFeatures/MatrixToAndFromVectorConstructors.hlsl
new file mode 100644
index 0000000000000..8b1fb9038bedd
--- /dev/null
+++ 
b/clang/test/CodeGenHLSL/BasicFeatures/MatrixToAndFromVectorConstructors.hlsl
@@ -0,0 +1,121 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -disable-llvm-passes 
-emit-llvm -finclude-default-header -o - -fmatrix-memory-layout=column-major %s 
| FileCheck %s --check-prefixes=CHECK,COL-CHECK
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -disable-llvm-passes 
-emit-llvm -finclude-default-header -o - -fmatrix-memory-layout=row-major %s | 
FileCheck %s --check-prefixes=CHECK,ROW-CHECK
+
+// CHECK-LABEL: define hidden noundef nofpclass(nan inf) <4 x float> 
@_Z2fnu11matrix_typeILm2ELm2EfE(
+// CHECK-SAME: <4 x float> noundef nofpclass(nan inf) [[M:%.*]]) 
#[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[M_ADDR:%.*]] = alloca [2 x <2 x float>], align 4
+// CHECK-NEXT:    [[V:%.*]] = alloca <4 x float>, align 16
+// CHECK-NEXT:    store <4 x float> [[M]], ptr [[M_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load <4 x float>, ptr [[M_ADDR]], align 4
+// CHECK-NEXT:    [[MATRIXEXT:%.*]] = extractelement <4 x float> [[TMP0]], i32 0
+// CHECK-NEXT:    [[VECINIT:%.*]] = insertelement <4 x float> poison, float 
[[MATRIXEXT]], i32 0
+// CHECK-NEXT:    [[TMP1:%.*]] = load <4 x float>, ptr [[M_ADDR]], align 4
+// COL-CHECK-NEXT:    [[MATRIXEXT1:%.*]] = extractelement <4 x float> 
[[TMP1]], i32 2
+// ROW-CHECK-NEXT:    [[MATRIXEXT1:%.*]] = extractelement <4 x float> 
[[TMP1]], i32 1
+// CHECK-NEXT:    [[VECINIT2:%.*]] = insertelement <4 x float> [[VECINIT]], 
float [[MATRIXEXT1]], i32 1
+// CHECK-NEXT:    [[TMP2:%.*]] = load <4 x float>, ptr [[M_ADDR]], align 4
+// COL-CHECK-NEXT:    [[MATRIXEXT3:%.*]] = extractelement <4 x float> 
[[TMP2]], i32 1
+// ROW-CHECK-NEXT:    [[MATRIXEXT3:%.*]] = extractelement <4 x float> 
[[TMP2]], i32 2
+// CHECK-NEXT:    [[VECINIT4:%.*]] = insertelement <4 x float> [[VECINIT2]], 
float [[MATRIXEXT3]], i32 2
+// CHECK-NEXT:    [[TMP3:%.*]] = load <4 x float>, ptr [[M_ADDR]], align 4
+// CHECK-NEXT:    [[MATRIXEXT5:%.*]] = extractelement <4 x float> [[TMP3]], 
i32 3
+// CHECK-NEXT:    [[VECINIT6:%.*]] = insertelement <4 x float> [[VECINIT4]], 
float [[MATRIXEXT5]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT6]], ptr [[V]], align 16
+// CHECK-NEXT:    [[TMP4:%.*]] = load <4 x float>, ptr [[V]], align 16
+// CHECK-NEXT:    ret <4 x float> [[TMP4]]
+//
+float4 fn(float2x2 m) {
+    float4 v = m;
+    return v;
+}
+
+// CHECK-LABEL: define hidden noundef <4 x i32> @_Z2fnDv4_i(
+// CHECK-SAME: <4 x i32> noundef [[V:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca <4 x i32>, align 16
+// CHECK-NEXT:    [[M:%.*]] = alloca [2 x <2 x i32>], align 4
+// CHECK-NEXT:    store <4 x i32> [[V]], ptr [[V_ADDR]], align 16
+// CHECK-NEXT:    [[TMP0:%.*]] = load <4 x i32>, ptr [[V_ADDR]], align 16
+// CHECK-NEXT:    [[VECEXT:%.*]] = extractelement <4 x i32> [[TMP0]], i64 0
+// CHECK-NEXT:    [[VECINIT:%.*]] = insertelement <4 x i32> poison, i32 
[[VECEXT]], i32 0
+// CHECK-NEXT:    [[TMP1:%.*]] = load <4 x i32>, ptr [[V_ADDR]], align 16
+// CHECK-NEXT:    [[VECEXT1:%.*]] = extractelement <4 x i32> [[TMP1]], i64 2
+// CHECK-NEXT:    [[VECINIT2:%.*]] = insertelement <4 x i32> [[VECINIT]], i32 
[[VECEXT1]], i32 1
+// CHECK-NEXT:    [[TMP2:%.*]] = load <4 x i32>, ptr [[V_ADDR]], align 16
+// CHECK-NEXT:    [[VECEXT3:%.*]] = extractelement <4 x i32> [[TMP2]], i64 1
+// CHECK-NEXT:    [[VECINIT4:%.*]] = insertelement <4 x i32> [[VECINIT2]], i32 
[[VECEXT3]], i32 2
+// CHECK-NEXT:    [[TMP3:%.*]] = load <4 x i32>, ptr [[V_ADDR]], align 16
+// CHECK-NEXT:    [[VECEXT5:%.*]] = extractelement <4 x i32> [[TMP3]], i64 3
+// CHECK-NEXT:    [[VECINIT6:%.*]] = insertelement <4 x i32> [[VECINIT4]], i32 
[[VECEXT5]], i32 3
+// CHECK-NEXT:    store <4 x i32> [[VECINIT6]], ptr [[M]], align 4
+// CHECK-NEXT:    [[TMP4:%.*]] = load <4 x i32>, ptr [[M]], align 4
+// CHECK-NEXT:    ret <4 x i32> [[TMP4]]
+//
+int2x2 fn(int4 v) {
+    int2x2 m = v;
+    return m;
+}
+
+// CHECK-LABEL: define hidden noundef <2 x i32> @_Z3fn1Dv2_i(
+// CHECK-SAME: <2 x i32> noundef [[V:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca <2 x i32>, align 8
+// CHECK-NEXT:    store <2 x i32> [[V]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load <2 x i32>, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[VECEXT:%.*]] = extractelement <2 x i32> [[TMP0]], i64 0
+// CHECK-NEXT:    [[VECINIT:%.*]] = insertelement <2 x i32> poison, i32 
[[VECEXT]], i32 0
+// CHECK-NEXT:    [[TMP1:%.*]] = load <2 x i32>, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[VECEXT1:%.*]] = extractelement <2 x i32> [[TMP1]], i64 1
+// CHECK-NEXT:    [[VECINIT2:%.*]] = insertelement <2 x i32> [[VECINIT]], i32 
[[VECEXT1]], i32 1
+// CHECK-NEXT:    ret <2 x i32> [[VECINIT2]]
+//
+int1x2 fn1(int2 v) {
+    return v;
+}
+
+// CHECK-LABEL: define hidden noundef <3 x i1> @_Z3fn2Dv3_b(
+// CHECK-SAME: <3 x i1> noundef [[B:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca <3 x i32>, align 16
+// CHECK-NEXT:    [[TMP0:%.*]] = zext <3 x i1> [[B]] to <3 x i32>
+// CHECK-NEXT:    store <3 x i32> [[TMP0]], ptr [[B_ADDR]], align 16
+// CHECK-NEXT:    [[TMP1:%.*]] = load <3 x i32>, ptr [[B_ADDR]], align 16
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc <3 x i32> [[TMP1]] to <3 x i1>
+// CHECK-NEXT:    [[VECEXT:%.*]] = extractelement <3 x i1> [[LOADEDV]], i64 0
+// CHECK-NEXT:    [[VECINIT:%.*]] = insertelement <3 x i1> poison, i1 
[[VECEXT]], i32 0
+// CHECK-NEXT:    [[TMP2:%.*]] = load <3 x i32>, ptr [[B_ADDR]], align 16
+// CHECK-NEXT:    [[LOADEDV1:%.*]] = trunc <3 x i32> [[TMP2]] to <3 x i1>
+// CHECK-NEXT:    [[VECEXT2:%.*]] = extractelement <3 x i1> [[LOADEDV1]], i64 1
+// CHECK-NEXT:    [[VECINIT3:%.*]] = insertelement <3 x i1> [[VECINIT]], i1 
[[VECEXT2]], i32 1
+// CHECK-NEXT:    [[TMP3:%.*]] = load <3 x i32>, ptr [[B_ADDR]], align 16
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc <3 x i32> [[TMP3]] to <3 x i1>
+// CHECK-NEXT:    [[VECEXT5:%.*]] = extractelement <3 x i1> [[LOADEDV4]], i64 2
+// CHECK-NEXT:    [[VECINIT6:%.*]] = insertelement <3 x i1> [[VECINIT3]], i1 
[[VECEXT5]], i32 2
+// CHECK-NEXT:    ret <3 x i1> [[VECINIT6]]
+//
+bool3x1 fn2(bool3 b) {
+    return b;
+}
+
+// CHECK-LABEL: define hidden noundef <3 x i32> 
@_Z3fn3u11matrix_typeILm1ELm3EbE(
+// CHECK-SAME: <3 x i1> noundef [[B:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// COL-CHECK-NEXT:    [[B_ADDR:%.*]] = alloca [3 x <1 x i32>], align 4
+// ROW-CHECK-NEXT:    [[B_ADDR:%.*]] = alloca [1 x <3 x i32>], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = zext <3 x i1> [[B]] to <3 x i32>
+// CHECK-NEXT:    store <3 x i32> [[TMP0]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load <3 x i32>, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[MATRIXEXT:%.*]] = extractelement <3 x i32> [[TMP1]], i32 0
+// CHECK-NEXT:    [[VECINIT:%.*]] = insertelement <3 x i32> poison, i32 
[[MATRIXEXT]], i32 0
+// CHECK-NEXT:    [[TMP2:%.*]] = load <3 x i32>, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[MATRIXEXT1:%.*]] = extractelement <3 x i32> [[TMP2]], i32 1
+// CHECK-NEXT:    [[VECINIT2:%.*]] = insertelement <3 x i32> [[VECINIT]], i32 
[[MATRIXEXT1]], i32 1
+// CHECK-NEXT:    [[TMP3:%.*]] = load <3 x i32>, ptr [[B_ADDR]], align 4
+// CHECK-NEXT:    [[MATRIXEXT3:%.*]] = extractelement <3 x i32> [[TMP3]], i32 2
+// CHECK-NEXT:    [[VECINIT4:%.*]] = insertelement <3 x i32> [[VECINIT2]], i32 
[[MATRIXEXT3]], i32 2
+// CHECK-NEXT:    ret <3 x i32> [[VECINIT4]]
+//
+int3 fn3(bool1x3 b) {
+    return b;
+}

diff  --git a/clang/test/SemaHLSL/Types/BuiltinMatrix/MatrixSplatErrors.hlsl 
b/clang/test/SemaHLSL/Types/BuiltinMatrix/MatrixSplatErrors.hlsl
index 0c2e53d382180..fc3f8e7adc050 100644
--- a/clang/test/SemaHLSL/Types/BuiltinMatrix/MatrixSplatErrors.hlsl
+++ b/clang/test/SemaHLSL/Types/BuiltinMatrix/MatrixSplatErrors.hlsl
@@ -1,8 +1,13 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library 
-finclude-default-header -std=hlsl202x -verify %s
 
-void SplatOfVectortoMat(int4 V){
+void SplatOfUndersizedVectortoMat(int3 V){
     int2x2 M = V;
-    // expected-error@-1 {{cannot initialize a variable of type 'int2x2' (aka 
'matrix<int, 2, 2>') with an lvalue of type 'int4' (aka 'vector<int, 4>')}}
+    // expected-error@-1 {{too few initializers in list for type 'int2x2' (aka 
'matrix<int, 2, 2>') (expected 4 but found 3)}}
+}
+
+void SplatOfOversizedVectortoMat(int3 V){
+    int1x2 M = V;
+    // expected-error@-1 {{too many initializers in list for type 'int1x2' 
(aka 'matrix<int, 1, 2>') (expected 2 but found 3)}}
 }
 
 void SplatOfMattoMat(int4x3 N){


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to