https://github.com/hekota updated 
https://github.com/llvm/llvm-project/pull/205996

>From 8e8d54219ed36bc352fd3a0b57919722e4fe2851 Mon Sep 17 00:00:00 2001
From: Helena Kotas <[email protected]>
Date: Fri, 26 Jun 2026 00:05:44 -0700
Subject: [PATCH 1/2] [HLSL] Enable conversion of ConstantBuffer<T> to T

---
 clang/include/clang/Sema/Initialization.h     |   6 +-
 clang/include/clang/Sema/SemaHLSL.h           |   3 +-
 clang/lib/Sema/SemaExpr.cpp                   |  18 +-
 clang/lib/Sema/SemaExprMember.cpp             |   2 +-
 clang/lib/Sema/SemaHLSL.cpp                   |   8 +-
 clang/lib/Sema/SemaInit.cpp                   |  30 ++-
 .../AST/HLSL/ConstantBuffers-AST-error.hlsl   |  24 --
 clang/test/AST/HLSL/ConstantBuffers-AST.hlsl  |  31 +++
 .../test/CodeGenHLSL/cbuffer_copy_layout.hlsl |  23 --
 .../ConstantBufferT-struct-passing.hlsl       | 255 ++++++++++++++++++
 .../SemaHLSL/Resources/ConstantBuffers.hlsl   |   3 +-
 11 files changed, 342 insertions(+), 61 deletions(-)
 delete mode 100644 clang/test/AST/HLSL/ConstantBuffers-AST-error.hlsl
 delete mode 100644 clang/test/CodeGenHLSL/cbuffer_copy_layout.hlsl
 create mode 100644 
clang/test/CodeGenHLSL/resources/ConstantBufferT-struct-passing.hlsl

diff --git a/clang/include/clang/Sema/Initialization.h 
b/clang/include/clang/Sema/Initialization.h
index 96ea4fae4437c..2c2df1f03747b 100644
--- a/clang/include/clang/Sema/Initialization.h
+++ b/clang/include/clang/Sema/Initialization.h
@@ -979,7 +979,9 @@ class InitializationSequence {
 
     /// Initialize an aggreagate with parenthesized list of values.
     /// This is a C++20 feature.
-    SK_ParenthesizedListInit
+    SK_ParenthesizedListInit,
+
+    SK_HLSLBufferConversion
   };
 
   /// A single step in the initialization sequence.
@@ -1434,6 +1436,8 @@ class InitializationSequence {
   /// single element and rewrap it at the end.
   void RewrapReferenceInitList(QualType T, InitListExpr *Syntactic);
 
+  void AddHLSLBufferConversionStep(QualType T);
+
   /// Note that this initialization sequence failed.
   void SetFailed(FailureKind Failure) {
     SequenceKind = FailedSequence;
diff --git a/clang/include/clang/Sema/SemaHLSL.h 
b/clang/include/clang/Sema/SemaHLSL.h
index 4a95cb8b2d181..8928524e49783 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -148,8 +148,7 @@ class SemaHLSL : public SemaBase {
   // Returns the result of converting ConstantBuffer<T> to
   // `const hlsl_constant T&`. If `BaseExpr`'s type is not ConstantBuffer<T>
   // then the return value is `std::nullopt`.
-  std::optional<ExprResult>
-  tryPerformConstantBufferConversion(ExprResult &BaseExpr);
+  std::optional<ExprResult> tryPerformConstantBufferConversion(Expr *BaseExpr);
 
   // Returns the conversion operator to convert `RD` to `const hlsl_constant
   // Type&`. Returns `nullptr` if it could not be found.
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 7c868d176e803..1fa42a0093fe2 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -16135,10 +16135,20 @@ ExprResult Sema::BuildBinOp(Scope *S, SourceLocation 
OpLoc,
     RHSExpr = resolvedRHS.get();
   }
 
-  if (getLangOpts().HLSL && (LHSExpr->getType()->isHLSLResourceRecord() ||
-                             LHSExpr->getType()->isHLSLResourceRecordArray())) 
{
-    if (!HLSL().CheckResourceBinOp(Opc, LHSExpr, RHSExpr, OpLoc))
-      return ExprError();
+  if (getLangOpts().HLSL) {
+    if (LHSExpr->getType()->isHLSLResourceRecord() ||
+        LHSExpr->getType()->isHLSLResourceRecordArray()) {
+      if (!HLSL().CheckResourceBinOp(Opc, LHSExpr, RHSExpr, OpLoc))
+        return ExprError();
+    } else if (RHSExpr->getType()->isHLSLResourceRecord()) {
+      std::optional<ExprResult> ConvRHS =
+          HLSL().tryPerformConstantBufferConversion(RHSExpr);
+      if (ConvRHS && Context.hasSameUnqualifiedType(
+                         LHSExpr->getType(), ConvRHS->get()->getType())) {
+        assert(!ConvRHS->isInvalid());
+        RHSExpr = ConvRHS->get();
+      }
+    }
   }
 
   if (getLangOpts().CPlusPlus) {
diff --git a/clang/lib/Sema/SemaExprMember.cpp 
b/clang/lib/Sema/SemaExprMember.cpp
index 851d58c49f7b9..62ecc52c0374b 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -1298,7 +1298,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult 
&R,
   // to access the member.
   if (S.getLangOpts().HLSL && BaseType->isHLSLResourceRecord()) {
     if (std::optional<ExprResult> ConvBase =
-            S.HLSL().tryPerformConstantBufferConversion(BaseExpr)) {
+            S.HLSL().tryPerformConstantBufferConversion(BaseExpr.get())) {
       assert(!ConvBase->isInvalid());
       BaseExpr = *ConvBase;
       BaseType = BaseExpr.get()->getType();
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 075dc97b0aef2..1d4deedcbbf7d 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3279,8 +3279,8 @@ NamedDecl 
*SemaHLSL::getConstantBufferConversionFunction(QualType Type,
 }
 
 std::optional<ExprResult>
-SemaHLSL::tryPerformConstantBufferConversion(ExprResult &BaseExpr) {
-  QualType BaseType = BaseExpr.get()->getType();
+SemaHLSL::tryPerformConstantBufferConversion(Expr *BaseExpr) {
+  QualType BaseType = BaseExpr->getType();
   const HLSLAttributedResourceType *ResTy =
       HLSLAttributedResourceType::findHandleTypeOnResource(
           BaseType.getTypePtr());
@@ -3297,7 +3297,7 @@ SemaHLSL::tryPerformConstantBufferConversion(ExprResult 
&BaseExpr) {
   auto *ConversionDecl =
       cast<CXXConversionDecl>(NamedConversionDecl->getUnderlyingDecl());
 
-  return SemaRef.BuildCXXMemberCallExpr(BaseExpr.get(), NamedConversionDecl,
+  return SemaRef.BuildCXXMemberCallExpr(BaseExpr, NamedConversionDecl,
                                         ConversionDecl,
                                         /*HadMultipleCandidates=*/false);
 }
@@ -5146,7 +5146,7 @@ ExprResult SemaHLSL::ActOnOutParamExpr(ParmVarDecl 
*Param, Expr *Arg) {
 
   // Writebacks are performed with `=` binary operator, which allows for
   // overload resolution on writeback result expressions.
-  Res = SemaRef.ActOnBinOp(SemaRef.getCurScope(), Param->getBeginLoc(),
+  Res = SemaRef.ActOnBinOp(SemaRef.getCurScope(), Arg->getBeginLoc(),
                            tok::equal, ArgOpV, OpV);
 
   if (Res.isInvalid())
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 8f685feac4beb..4f549f3eca108 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -3977,6 +3977,7 @@ void InitializationSequence::Step::Destroy() {
   case SK_OCLSamplerInit:
   case SK_OCLZeroOpaqueType:
   case SK_ParenthesizedListInit:
+  case SK_HLSLBufferConversion:
     break;
 
   case SK_ConversionSequence:
@@ -4306,6 +4307,13 @@ void 
InitializationSequence::RewrapReferenceInitList(QualType T,
   Steps.push_back(S);
 }
 
+void InitializationSequence::AddHLSLBufferConversionStep(QualType T) {
+  Step S;
+  S.Kind = SK_HLSLBufferConversion;
+  S.Type = T;
+  Steps.push_back(S);
+}
+
 void InitializationSequence::SetOverloadFailure(FailureKind Failure,
                                                 OverloadingResult Result) {
   setSequenceKind(FailedSequence);
@@ -6349,6 +6357,13 @@ static void TryUserDefinedConversion(Sema &S,
                                  HadMultipleCandidates);
 
   if (ConvType->isRecordType()) {
+    if (S.getLangOpts().HLSL &&
+        ConvType.getAddressSpace() == LangAS::hlsl_constant &&
+        S.Context.hasSameUnqualifiedType(ConvType, DestType)) {
+      Sequence.AddHLSLBufferConversionStep(ConvType);
+      return;
+    }
+
     //   The call is used to direct-initialize [...] the object that is the
     //   destination of the copy-initialization.
     //
@@ -8072,7 +8087,8 @@ ExprResult InitializationSequence::Perform(Sema &S,
   case SK_ProduceObjCObject:
   case SK_StdInitializerList:
   case SK_OCLSamplerInit:
-  case SK_OCLZeroOpaqueType: {
+  case SK_OCLZeroOpaqueType:
+  case SK_HLSLBufferConversion: {
     assert(Args.size() == 1 || IsHLSLVectorOrMatrixInit);
     CurInit = Args[0];
     if (!CurInit.get()) return ExprError();
@@ -8859,6 +8875,13 @@ ExprResult InitializationSequence::Perform(Sema &S,
         CurInit = S.MaybeBindToTemporary(CurInit.get());
       break;
     }
+    case SK_HLSLBufferConversion: {
+      CurInit = ImplicitCastExpr::Create(
+          S.Context, Step->Type.getLocalUnqualifiedType(), CK_LValueToRValue,
+          CurInit.get(),
+          /*BasePath=*/nullptr, VK_PRValue, FPOptionsOverride());
+      break;
+    }
     }
   }
 
@@ -9866,9 +9889,14 @@ void InitializationSequence::dump(raw_ostream &OS) const 
{
     case SK_OCLZeroOpaqueType:
       OS << "OpenCL opaque type from zero";
       break;
+
     case SK_ParenthesizedListInit:
       OS << "initialization from a parenthesized list of values";
       break;
+
+    case SK_HLSLBufferConversion:
+      OS << "HLSL buffer conversion";
+      break;
     }
 
     OS << " [" << S->Type << ']';
diff --git a/clang/test/AST/HLSL/ConstantBuffers-AST-error.hlsl 
b/clang/test/AST/HLSL/ConstantBuffers-AST-error.hlsl
deleted file mode 100644
index 4f9d60c741f90..0000000000000
--- a/clang/test/AST/HLSL/ConstantBuffers-AST-error.hlsl
+++ /dev/null
@@ -1,24 +0,0 @@
-// RUN: not %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl 
-ast-dump -finclude-default-header -o - %s 2>&1 | FileCheck %s
-
-// Unimplemented: https://github.com/llvm/llvm-project/issues/195093
-// Once fixed, these tests should work and we should check the AST.
-
-struct S {
-  float a;
-};
-ConstantBuffer<S> cb;
-
-void takes_s(S s) {}
-
-void main() {
-  S s;
-
-  // CHECK: error: no viable constructor copying parameter of type 'const 
hlsl_constant S'
-  takes_s(cb);
-
-  // CHECK: error: no viable constructor copying variable of type 'const 
hlsl_constant S'
-  S s2 = cb;
-
-  // CHECK: error: assigning to 'S' from incompatible type 'ConstantBuffer<S>'
-  s = cb;
-}
diff --git a/clang/test/AST/HLSL/ConstantBuffers-AST.hlsl 
b/clang/test/AST/HLSL/ConstantBuffers-AST.hlsl
index 6a880c437db8f..0b51c0fc57543 100644
--- a/clang/test/AST/HLSL/ConstantBuffers-AST.hlsl
+++ b/clang/test/AST/HLSL/ConstantBuffers-AST.hlsl
@@ -116,5 +116,36 @@ float main() {
   // CHECK-NEXT: HLSLOutArgExpr {{.*}} 
'ConstantBuffer<S>':'hlsl::ConstantBuffer<S>' lvalue inout
   takes_inout_cb(cb);
 
+  // CHECK: DeclStmt
+  // CHECK-NEXT: VarDecl {{.*}} s2 'S' cinit
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'S' <LValueToRValue>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'const hlsl_constant S' lvalue 
<UserDefinedConversion>
+  // CHECK-NEXT: CXXMemberCallExpr {{.*}} 'const hlsl_constant S' lvalue
+  // CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .operator 
const hlsl_constant S & {{.*}}
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'const hlsl::ConstantBuffer<S>' 
lvalue <NoOp>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 
'ConstantBuffer<S>':'hlsl::ConstantBuffer<S>' lvalue Var {{.*}} 'cb' 
'ConstantBuffer<S>':'hlsl::ConstantBuffer<S>'
+  S s2 = cb;
+
+  // CHECK: BinaryOperator {{.*}} 'S' lvalue '='
+  // CHECK-NEXT: DeclRefExpr {{.*}} 'S' lvalue Var {{.*}} 's' 'S'
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'S' <LValueToRValue>
+  // CHECK-NEXT: CXXMemberCallExpr {{.*}} 'const hlsl_constant S' lvalue
+  // CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .operator 
const hlsl_constant S & {{.*}}
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'const hlsl::ConstantBuffer<S>' 
lvalue <NoOp>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 
'ConstantBuffer<S>':'hlsl::ConstantBuffer<S>' lvalue Var {{.*}} 'cb' 
'ConstantBuffer<S>':'hlsl::ConstantBuffer<S>'
+  S s;
+  s = cb;
+
+  // CHECK: CallExpr {{.*}} 'void'
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(S)' <FunctionToPointerDecay>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 'void (S)' lvalue Function {{.*}} 
'takes_s' 'void (S)'
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'S' <LValueToRValue>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'const hlsl_constant S' lvalue 
<UserDefinedConversion>
+  // CHECK-NEXT: CXXMemberCallExpr {{.*}} 'const hlsl_constant S' lvalue
+  // CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .operator 
const hlsl_constant S & {{.*}}
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'const hlsl::ConstantBuffer<S>' 
lvalue <NoOp>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 
'ConstantBuffer<S>':'hlsl::ConstantBuffer<S>' lvalue Var {{.*}} 'cb' 
'ConstantBuffer<S>':'hlsl::ConstantBuffer<S>'
+  takes_s(cb);
+
   return f1 + f2 + f3;
 }
diff --git a/clang/test/CodeGenHLSL/cbuffer_copy_layout.hlsl 
b/clang/test/CodeGenHLSL/cbuffer_copy_layout.hlsl
deleted file mode 100644
index 022844284f4ba..0000000000000
--- a/clang/test/CodeGenHLSL/cbuffer_copy_layout.hlsl
+++ /dev/null
@@ -1,23 +0,0 @@
-// RUN: not %clang_cc1 -finclude-default-header -triple 
dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s
-
-// Unimplemented: https://github.com/llvm/llvm-project/issues/195093
-// These cases should work. When fixed we should add proper CHECKs.
-
-struct S {
-  float3 a;
-  float2 b;
-};
-
-cbuffer CB {
-  S s_cb;
-}
-
-ConstantBuffer<S> cb;
-
-[numthreads(1,1,1)]
-void main() {
-  S l1 = s_cb;
-
-  // CHECK: error: no viable constructor copying variable of type 'const 
hlsl_constant S'
-  S l2 = cb;
-}
diff --git 
a/clang/test/CodeGenHLSL/resources/ConstantBufferT-struct-passing.hlsl 
b/clang/test/CodeGenHLSL/resources/ConstantBufferT-struct-passing.hlsl
new file mode 100644
index 0000000000000..a8107894757d2
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/ConstantBufferT-struct-passing.hlsl
@@ -0,0 +1,255 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library 
-finclude-default-header -emit-llvm -disable-llvm-passes -o - %s | \
+// RUN:     llvm-cxxfilt | FileCheck %s -DCONST_ADDR_SPACE=2 
-DPADDING_TYPE="dx.Padding" -check-prefixes=CHECK,CHECK-DXIL
+
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-library -finclude-default-header 
-emit-llvm -disable-llvm-passes -o - %s | \
+// RUN:     llvm-cxxfilt | FileCheck %s -DCONST_ADDR_SPACE=12 
-DPADDING_TYPE="spirv.Padding" -check-prefixes=CHECK,CHECK-SPIRV
+
+struct P {
+  float a;
+};
+
+struct S : P {
+  double2 b;
+};
+
+struct T {
+  P p;
+  int arr[2];
+};
+
+ConstantBuffer<S> CBS;
+ConstantBuffer<T> CBT;
+
+// CHECK-DXIL: %"class.hlsl::ConstantBuffer" = type { target("dx.CBuffer", %S) 
}
+// CHECK-SPIRV: %"class.hlsl::ConstantBuffer" = type { 
target("spirv.VulkanBuffer", %S, 2, 0) }
+// CHECK: %S = type <{ float, target("[[PADDING_TYPE]]", 12), <2 x double> }>
+
+// CHECK-DXIL: %"class.hlsl::ConstantBuffer.0" = type { target("dx.CBuffer", 
%T) }
+// CHECK-SPIRV: %"class.hlsl::ConstantBuffer.0" = type { 
target("spirv.VulkanBuffer", %T, 2, 0) }
+// CHECK: %T = type <{ %P, target("[[PADDING_TYPE]]", 12), <{ [1 x <{ i32, 
target("[[PADDING_TYPE]]", 12) }>], i32 }> }>
+// CHECK: %P = type <{ float }>
+
+// CHECK: %struct.S = type <{ %struct.P, <2 x double> }>
+// CHECK: %struct.P = type { float }
+// CHECK: %struct.T = type { %struct.P, [2 x i32] }
+
+// CHECK: @CBS = internal global %"class.hlsl::ConstantBuffer" poison, align 
{{(4|8)}}
+// CHECK: @CBT = internal global %"class.hlsl::ConstantBuffer.0" poison, align 
{{(4|8)}}
+
+void useS(S s) {}
+void useP(P p) {}
+void useT(T t) {}
+
+// CHECK-LABEL: case1
+void case1() {
+// CHECK: %s = alloca %struct.S, align 1
+// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) 
@hlsl::ConstantBuffer<S>::operator S const AS[[CONST_ADDR_SPACE]]&() const(ptr 
{{.*}} @CBS)
+
+// s.a
+// CHECK-NEXT: [[CB_A_PTR:%.*]] = getelementptr inbounds %S, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[S_A_PTR:%.*]] = getelementptr inbounds %struct.S, ptr %s, i32 
0, i32 0
+// CHECK-NEXT: [[CBUFLOAD:%.*]] = load float, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_A_PTR]], align 4
+// CHECK-NEXT: store float [[CBUFLOAD]], ptr [[S_A_PTR]], align 4
+
+// s.b
+// CHECK-NEXT: [[CB_B_PTR:%.*]] = getelementptr inbounds %S, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 2
+// CHECK-NEXT: [[S_B_PTR:%.*]] = getelementptr inbounds %struct.S, ptr %s, i32 
0, i32 1
+// CHECK-NEXT: [[CBUFLOAD2:%.*]] = load <2 x double>, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_B_PTR]], align 8
+// CHECK-NEXT: store <2 x double> [[CBUFLOAD2]], ptr [[S_B_PTR]], align 8
+  S s = CBS;
+}
+
+// CHECK-LABEL: case2
+void case2() {
+// CHECK: %s = alloca %struct.S, align 1
+// CHECK: [[TMP:%.*]] = alloca %struct.S, align 1
+// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) 
@hlsl::ConstantBuffer<S>::operator S const AS[[CONST_ADDR_SPACE]]&() const(ptr 
{{.*}} @CBS)
+
+// s.a
+// CHECK-NEXT: [[CB_A_PTR:%.*]] = getelementptr inbounds %S, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[S_A_PTR:%.*]] = getelementptr inbounds %struct.S, ptr %s, i32 
0, i32 0
+// CHECK-NEXT: [[CBUFLOAD:%.*]] = load float, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_A_PTR]], align 4
+// CHECK-NEXT: store float [[CBUFLOAD]], ptr [[S_A_PTR]], align 4
+
+// s.b
+// CHECK-NEXT: [[CB_B_PTR:%.*]] = getelementptr inbounds %S, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 2
+// CHECK-NEXT: [[S_B_PTR:%.*]] = getelementptr inbounds %struct.S, ptr %s, i32 
0, i32 1
+// CHECK-NEXT: [[CBUFLOAD2:%.*]] = load <2 x double>, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_B_PTR]], align 8
+// CHECK-NEXT: store <2 x double> [[CBUFLOAD2]], ptr [[S_B_PTR]], align 8
+
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i{{(32|64)}}(ptr align 1 [[TMP]], 
ptr align 1 %s, i{{(32|64)}} 20, i1 false)
+  S s;
+  s = CBS;
+}
+
+// CHECK-LABEL: case3
+void case3() {
+// CHECK: [[TMP:%.*]] = alloca %struct.S, align 1
+// CHECK-NEXT: [[CB_PTR:%.*]] = call {{.*}} ptr 
addrspace([[CONST_ADDR_SPACE]]) @hlsl::ConstantBuffer<S>::operator S const 
AS[[CONST_ADDR_SPACE]]&() const(ptr {{.*}} @CBS)
+
+// tmp.a
+// CHECK-NEXT: [[CB_A_PTR:%.*]] = getelementptr inbounds %S, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[TMP_A_PTR:%.*]] = getelementptr inbounds %struct.S, ptr 
[[TMP]], i32 0, i32 0
+// CHECK-NEXT: [[CBUFLOAD:%.*]] = load float, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_A_PTR]], align 4
+// CHECK-NEXT: store float [[CBUFLOAD]], ptr [[S_A_PTR]], align 4
+  
+// tmp.b
+// CHECK-NEXT: [[CB_B_PTR:%.*]] = getelementptr inbounds %S, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 2
+// CHECK-NEXT: [[TMP_B_PTR:%.*]] = getelementptr inbounds %struct.S, ptr 
[[TMP]], i32 0, i32 1
+// CHECK-NEXT: [[CBUFLOAD2:%.*]] = load <2 x double>, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_B_PTR]], align 8
+// CHECK-NEXT: store <2 x double> [[CBUFLOAD2]], ptr [[S_B_PTR]], align 8
+
+// CHECK-NEXT: call {{.*}}void @useS(S)(ptr noundef dead_on_return [[TMP]])
+  useS(CBS);
+}
+
+// CHECK-LABEL: case4
+void case4() {
+
+// CHECK: %t = alloca %struct.T, align 1
+// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) 
@hlsl::ConstantBuffer<T>::operator T const AS[[CONST_ADDR_SPACE]]&() const(ptr 
{{.*}} @CBT)
+
+// t.p
+// CHECK-NEXT: [[CB_P_PTR:%.*]] = getelementptr inbounds %T, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[T_P_PTR:%.*]] = getelementptr inbounds %struct.T, ptr %t, i32 
0, i32 0
+
+// t.p.a
+// CHECK-NEXT: [[CB_P_A_PTR:%.*]] = getelementptr inbounds %P, ptr 
addrspace([[CONST_ADDR_SPACE]]) %1, i32 0, i32 0
+// CHECK-NEXT: [[T_P_A_PTR:%.*]] = getelementptr inbounds %struct.P, ptr %2, 
i32 0, i32 0
+// CHECK-NEXT: [[CBUF_LOAD1:%.*]] = load float, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_P_A_PTR]], align 4
+// CHECK-NEXT: store float [[CBUF_LOAD1]], ptr [[T_P_A_PTR]], align 4
+
+// t.arr
+// CHECK-NEXT: [[CB_ARR_PTR:%.*]] = getelementptr inbounds %T, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 2
+// CHECK-NEXT: [[T_ARR_PTR:%.*]] = getelementptr inbounds %struct.T, ptr %t, 
i32 0, i32 1
+
+// t.arr[0]
+// CHECK-NEXT: [[CB_ARR_0_PTR:%.*]] = getelementptr inbounds <{ [1 x <{ i32, 
target("[[PADDING_TYPE]]", 12) }>], i32 }>, ptr addrspace([[CONST_ADDR_SPACE]]) 
[[CB_ARR_PTR]], i32 0, i32 0, i32 0, i32 0
+// CHECK-NEXT: [[T_ARR_0_PTR:%.*]] = getelementptr inbounds [2 x i32], ptr 
[[T_ARR_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[CBUF_LOAD2:%.*]] = load i32, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_0_PTR]], align 4
+// CHECK-NEXT: store i32 [[CBUF_LOAD2]], ptr [[T_ARR_0_PTR]], align 4
+
+// t.arr[1]
+// CHECK-NEXT: [[CB_ARR_1_PTR:%.*]] = getelementptr inbounds <{ [1 x <{ i32, 
target("[[PADDING_TYPE]]", 12) }>], i32 }>, ptr addrspace([[CONST_ADDR_SPACE]]) 
[[CB_ARR_PTR]], i32 0, i32 1
+// CHECK-NEXT: [[T_ARR_1_PTR:%.*]] = getelementptr inbounds [2 x i32], ptr 
[[T_ARR_PTR]], i32 0, i32 1
+// CHECK-NEXT: [[CBUF_LOAD3:%.*]] = load i32, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_1_PTR]], align 4
+// CHECK-NEXT: store i32 [[CBUF_LOAD3]], ptr [[T_ARR_1_PTR]], align 4
+
+  T t = CBT;
+}
+
+// CHECK-LABEL: case5
+void case5() {
+// CHECK: %t = alloca %struct.T, align 1
+// CHECK: [[TMP:%.*]] = alloca %struct.T, align 1
+// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) 
@hlsl::ConstantBuffer<T>::operator T const AS[[CONST_ADDR_SPACE]]&() const(ptr 
{{.*}} @CBT)
+
+// t.p
+// CHECK-NEXT: [[CB_P_PTR:%.*]] = getelementptr inbounds %T, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[T_P_PTR:%.*]] = getelementptr inbounds %struct.T, ptr %t, i32 
0, i32 0
+
+// t.p.a
+// CHECK-NEXT: [[CB_P_A_PTR:%.*]] = getelementptr inbounds %P, ptr 
addrspace([[CONST_ADDR_SPACE]]) %1, i32 0, i32 0
+// CHECK-NEXT: [[T_P_A_PTR:%.*]] = getelementptr inbounds %struct.P, ptr %2, 
i32 0, i32 0
+// CHECK-NEXT: [[CBUF_LOAD1:%.*]] = load float, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_P_A_PTR]], align 4
+// CHECK-NEXT: store float [[CBUF_LOAD1]], ptr [[T_P_A_PTR]], align 4
+
+// t.arr
+// CHECK-NEXT: [[CB_ARR_PTR:%.*]] = getelementptr inbounds %T, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 2
+// CHECK-NEXT: [[T_ARR_PTR:%.*]] = getelementptr inbounds %struct.T, ptr %t, 
i32 0, i32 1
+
+// t.arr[0]
+// CHECK-NEXT: [[CB_ARR_0_PTR:%.*]] = getelementptr inbounds <{ [1 x <{ i32, 
target("[[PADDING_TYPE]]", 12) }>], i32 }>, ptr addrspace([[CONST_ADDR_SPACE]]) 
[[CB_ARR_PTR]], i32 0, i32 0, i32 0, i32 0
+// CHECK-NEXT: [[T_ARR_0_PTR:%.*]] = getelementptr inbounds [2 x i32], ptr 
[[T_ARR_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[CBUF_LOAD2:%.*]] = load i32, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_0_PTR]], align 4
+// CHECK-NEXT: store i32 [[CBUF_LOAD2]], ptr [[T_ARR_0_PTR]], align 4
+
+// t.arr[1]
+// CHECK-NEXT: [[CB_ARR_1_PTR:%.*]] = getelementptr inbounds <{ [1 x <{ i32, 
target("[[PADDING_TYPE]]", 12) }>], i32 }>, ptr addrspace([[CONST_ADDR_SPACE]]) 
[[CB_ARR_PTR]], i32 0, i32 1
+// CHECK-NEXT: [[T_ARR_1_PTR:%.*]] = getelementptr inbounds [2 x i32], ptr 
[[T_ARR_PTR]], i32 0, i32 1
+// CHECK-NEXT: [[CBUF_LOAD3:%.*]] = load i32, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_1_PTR]], align 4
+// CHECK-NEXT: store i32 [[CBUF_LOAD3]], ptr [[T_ARR_1_PTR]], align 4
+
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i{{(32|64)}}(ptr align 1 [[TMP]], 
ptr align 1 %t, i{{(32|64)}} 12, i1 false)
+
+  T t;
+  t = CBT;
+}
+
+// CHECK-LABEL: case6
+void case6() {
+// CHECK: [[TMP:%.*]] = alloca %struct.T, align 1
+// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) 
@hlsl::ConstantBuffer<T>::operator T const AS[[CONST_ADDR_SPACE]]&() const(ptr 
{{.*}} @CBT)
+
+// t.p
+// CHECK-NEXT: [[CB_P_PTR:%.*]] = getelementptr inbounds %T, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[T_P_PTR:%.*]] = getelementptr inbounds %struct.T, ptr 
[[TMP]], i32 0, i32 0
+
+// t.p.a
+// CHECK-NEXT: [[CB_P_A_PTR:%.*]] = getelementptr inbounds %P, ptr 
addrspace([[CONST_ADDR_SPACE]]) %1, i32 0, i32 0
+// CHECK-NEXT: [[T_P_A_PTR:%.*]] = getelementptr inbounds %struct.P, ptr %2, 
i32 0, i32 0
+// CHECK-NEXT: [[CBUF_LOAD1:%.*]] = load float, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_P_A_PTR]], align 4
+// CHECK-NEXT: store float [[CBUF_LOAD1]], ptr [[T_P_A_PTR]], align 4
+
+// t.arr
+// CHECK-NEXT: [[CB_ARR_PTR:%.*]] = getelementptr inbounds %T, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 2
+// CHECK-NEXT: [[T_ARR_PTR:%.*]] = getelementptr inbounds %struct.T, ptr 
[[TMP]], i32 0, i32 1
+
+// t.arr[0]
+// CHECK-NEXT: [[CB_ARR_0_PTR:%.*]] = getelementptr inbounds <{ [1 x <{ i32, 
target("[[PADDING_TYPE]]", 12) }>], i32 }>, ptr addrspace([[CONST_ADDR_SPACE]]) 
[[CB_ARR_PTR]], i32 0, i32 0, i32 0, i32 0
+// CHECK-NEXT: [[T_ARR_0_PTR:%.*]] = getelementptr inbounds [2 x i32], ptr 
[[T_ARR_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[CBUF_LOAD2:%.*]] = load i32, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_0_PTR]], align 4
+// CHECK-NEXT: store i32 [[CBUF_LOAD2]], ptr [[T_ARR_0_PTR]], align 4
+
+// t.arr[1]
+// CHECK-NEXT: [[CB_ARR_1_PTR:%.*]] = getelementptr inbounds <{ [1 x <{ i32, 
target("[[PADDING_TYPE]]", 12) }>], i32 }>, ptr addrspace([[CONST_ADDR_SPACE]]) 
[[CB_ARR_PTR]], i32 0, i32 1
+// CHECK-NEXT: [[T_ARR_1_PTR:%.*]] = getelementptr inbounds [2 x i32], ptr 
[[T_ARR_PTR]], i32 0, i32 1
+// CHECK-NEXT: [[CBUF_LOAD3:%.*]] = load i32, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_1_PTR]], align 4
+// CHECK-NEXT: store i32 [[CBUF_LOAD3]], ptr [[T_ARR_1_PTR]], align 4
+  useT(CBT);
+}
+
+// CHECK-LABEL: case7
+void case7() {
+// CHECK: %p = alloca %struct.P, align 1
+// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) 
@hlsl::ConstantBuffer<T>::operator T const AS[[CONST_ADDR_SPACE]]&() const(ptr 
{{.*}} @CBT)  
+
+// CHECK-NEXT: [[CB_P_PTR:%.*]] = getelementptr inbounds nuw %T, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[CB_P_A_PTR:%.*]] = getelementptr inbounds %P, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_P_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[P_A_PTR:%.*]] = getelementptr inbounds %struct.P, ptr %p, i32 
0, i32 0
+// CHECK-NEXT: [[CBUF_LOAD:%.*]] = load float, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_P_A_PTR]], align 4
+// CHECK-NEXT: store float [[CBUF_LOAD]], ptr [[P_A_PTR]], align 4
+  P p = CBT.p;
+}
+
+// CHECK-LABEL: case8
+void case8() {
+// CHECK: %p = alloca %struct.P, align 1
+// CHECK: [[TMP:%.*]] = alloca %struct.P, align 1
+// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) 
@hlsl::ConstantBuffer<T>::operator T const AS[[CONST_ADDR_SPACE]]&() const(ptr 
{{.*}} @CBT)  
+
+// CHECK-NEXT: [[CB_P_PTR:%.*]] = getelementptr inbounds nuw %T, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[CB_P_A_PTR:%.*]] = getelementptr inbounds %P, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_P_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[P_A_PTR:%.*]] = getelementptr inbounds %struct.P, ptr %p, i32 
0, i32 0
+// CHECK-NEXT: [[CBUF_LOAD:%.*]] = load float, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_P_A_PTR]], align 4
+// CHECK-NEXT: store float [[CBUF_LOAD]], ptr [[P_A_PTR]], align 4
+
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i{{(32|64)}}(ptr align 1 [[TMP]], 
ptr align 1 %p, i{{(32|64)}} 4, i1 false)
+  P p;
+  p = CBT.p;
+}
+
+// CHECK-LABEL: case9
+void case9() {
+// CHECK: [[TMP:%.*]] = alloca %struct.P, align 1
+// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) 
@hlsl::ConstantBuffer<T>::operator T const AS[[CONST_ADDR_SPACE]]&() const(ptr 
{{.*}} @CBT)  
+
+// CHECK-NEXT: [[CB_P_PTR:%.*]] = getelementptr inbounds nuw %T, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[CB_P_A_PTR:%.*]] = getelementptr inbounds %P, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_P_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[P_A_PTR:%.*]] = getelementptr inbounds %struct.P, ptr 
[[TMP]], i32 0, i32 0
+// CHECK-NEXT: [[CBUF_LOAD:%.*]] = load float, ptr 
addrspace([[CONST_ADDR_SPACE]]) [[CB_P_A_PTR]], align 4
+// CHECK-NEXT: store float [[CBUF_LOAD]], ptr [[P_A_PTR]], align 4
+
+// CHECK-NEXT: call {{.*}}void @useP(P)(ptr noundef dead_on_return [[TMP]])
+  useP(CBT.p);
+}
diff --git a/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl 
b/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl
index 6dc566a58b5e4..540fecd6f6c42 100644
--- a/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl
+++ b/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl
@@ -63,7 +63,8 @@ void takes_inout_s(inout S s) {}
 
 void foo() {
   // This case should fail because we cannot writeback to `cb` after the call.
-  // expected-error@+1 {{no viable constructor copying parameter of type 
'const hlsl_constant S'}}
+  // expected-error@+2 {{no viable overloaded '='}}
+  // expected-note@*:* {{candidate function not viable: no known conversion 
from 'S' to 'const hlsl::ConstantBuffer<S>' for 1st argument}}
   takes_inout_s(cb);
 }
 

>From 5e3d5ca13470465ebfbd137290a59993cf13f9e7 Mon Sep 17 00:00:00 2001
From: Helena Kotas <[email protected]>
Date: Fri, 26 Jun 2026 10:16:03 -0700
Subject: [PATCH 2/2] update tests - error should point at the call site, not
 the called function

---
 clang/test/SemaHLSL/BuiltIns/asuint-errors.hlsl | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/test/SemaHLSL/BuiltIns/asuint-errors.hlsl 
b/clang/test/SemaHLSL/BuiltIns/asuint-errors.hlsl
index 214cf641f40ed..27d23ddd1688f 100644
--- a/clang/test/SemaHLSL/BuiltIns/asuint-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/asuint-errors.hlsl
@@ -36,14 +36,14 @@ void test_asuint_first_arg_const(double D) {
   const uint A = 0;
   uint B;
   asuint(D, A, B);
- // expected-error@hlsl/hlsl_intrinsics.h:* {{read-only variable is not 
assignable}}
+ // expected-error@*:* {{read-only variable is not assignable}}
 }
 
 void test_asuint_second_arg_const(double D) {
   const uint A = 0;
   uint B;
   asuint(D, B, A);
- // expected-error@hlsl/hlsl_intrinsics.h:* {{read-only variable is not 
assignable}}
+ // expected-error@*:* {{read-only variable is not assignable}}
 }
 
 void test_asuint_imidiate_value(double D) {

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

Reply via email to