From bebee4fdfb244e46a768e3e61cdc5d7f4079ee72 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Fri, 11 Sep 2026 11:30:03 -0500 Subject: [PATCH 01/10] [202x] Enable variadic templates This enables C++11 variadic templates in HLSL and exposes `...` function parameter packs. This feature is being refined by TC57 for inclusion in the first draft of the HLSL specification, and while including it here is getting a little ahead of the committee, it's a frequently requested high-value feature that is minimally invasive to support in DXC and is already supported in Clang. Resolves #8905 Assisted-by: GitHub Copilot --- docs/ReleaseNotes.md | 3 + tools/clang/include/clang/Basic/LangOptions.h | 7 + tools/clang/lib/Parse/ParseDecl.cpp | 2 +- tools/clang/lib/Parse/ParseExpr.cpp | 5 +- tools/clang/lib/Parse/ParseInit.cpp | 2 +- tools/clang/lib/Parse/ParseTemplate.cpp | 9 +- tools/clang/lib/Sema/SemaTemplateVariadic.cpp | 2 +- tools/clang/lib/Sema/SemaType.cpp | 4 +- .../variadic.templates.basic.hlsl | 41 ++++++ .../variadic.templates.builtin.hlsl | 42 ++++++ ...variadic.templates.initlist-scalarize.hlsl | 88 ++++++++++++ .../hlsl/templates/variadic-202x.hlsl | 24 ++++ .../templates/variadic-builtin-templates.hlsl | 36 +++++ .../variadic-initlist-scalarize.hlsl | 69 +++++++++ .../variadic-base-class-unsupported.hlsl | 23 +++ ...templates-initlist-scalarize-negative.hlsl | 58 ++++++++ ...variadic-templates-initlist-scalarize.hlsl | 106 ++++++++++++++ .../variadic-templates-negative.hlsl | 54 ++++++++ .../templates/variadic-templates-pre202x.hlsl | 37 +++++ .../v202x/templates/variadic-templates.hlsl | 131 ++++++++++++++++++ 20 files changed, 732 insertions(+), 11 deletions(-) create mode 100644 tools/clang/test/CodeGenSPIRV/variadic.templates.basic.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/variadic.templates.builtin.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/variadic.templates.initlist-scalarize.hlsl create mode 100644 tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-202x.hlsl create mode 100644 tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-builtin-templates.hlsl create mode 100644 tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-initlist-scalarize.hlsl create mode 100644 tools/clang/test/SemaHLSL/v202x/templates/variadic-base-class-unsupported.hlsl create mode 100644 tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-initlist-scalarize-negative.hlsl create mode 100644 tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-initlist-scalarize.hlsl create mode 100644 tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-negative.hlsl create mode 100644 tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-pre202x.hlsl create mode 100644 tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index aacc4162b1..87f8ae79c4 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -49,6 +49,9 @@ line upon naming the release. Refer to previous for appropriate section names. with language changes introduced in HLSL 2026. - The legacy effects syntax support is removed in HLSL 202x [#8480](https://github.com/microsoft/DirectXShaderCompiler/issues/8480). +- HLSL 202x now supports template and function parameter packs, pack + expansions, and `sizeof...()` + [#8905](https://github.com/microsoft/DirectXShaderCompiler/issues/8905). #### SPIR-V diff --git a/tools/clang/include/clang/Basic/LangOptions.h b/tools/clang/include/clang/Basic/LangOptions.h index 433b767c8d..e2495048a4 100644 --- a/tools/clang/include/clang/Basic/LangOptions.h +++ b/tools/clang/include/clang/Basic/LangOptions.h @@ -188,6 +188,13 @@ class LangOptions : public LangOptionsBase { return MSCompatibilityVersion >= MajorVersion * 10000000U; } + // HLSL Change Starts + /// Whether the active HLSL version rejects variadic templates. + bool HLSLDisallowsVariadicTemplates() const { + return HLSL && HLSLVersion < hlsl::LangStd::v202x; + } + // HLSL Change Ends + /// \brief Reset all of the options that are not considered when building a /// module. void resetNonModularOptions(); diff --git a/tools/clang/lib/Parse/ParseDecl.cpp b/tools/clang/lib/Parse/ParseDecl.cpp index 81d16ffe80..f382880070 100644 --- a/tools/clang/lib/Parse/ParseDecl.cpp +++ b/tools/clang/lib/Parse/ParseDecl.cpp @@ -6107,7 +6107,7 @@ void Parser::ParseDirectDeclarator(Declarator &D) { // been expanded or contains auto; otherwise, it is parsed as part of the // parameter-declaration-clause. if (Tok.is(tok::ellipsis) && D.getCXXScopeSpec().isEmpty() && - !getLangOpts().HLSL && // HLSL Change: do not support ellipsis + !getLangOpts().HLSLDisallowsVariadicTemplates() && // HLSL Change !((D.getContext() == Declarator::PrototypeContext || D.getContext() == Declarator::LambdaExprParameterContext || D.getContext() == Declarator::BlockLiteralContext) && diff --git a/tools/clang/lib/Parse/ParseExpr.cpp b/tools/clang/lib/Parse/ParseExpr.cpp index 9af3dbe610..da9f6b5eeb 100644 --- a/tools/clang/lib/Parse/ParseExpr.cpp +++ b/tools/clang/lib/Parse/ParseExpr.cpp @@ -1946,7 +1946,8 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() { ConsumeToken(); // [C++11] 'sizeof' '...' '(' identifier ')' - if (Tok.is(tok::ellipsis) && OpTok.is(tok::kw_sizeof) && !getLangOpts().HLSL) { // HLSL Change + if (Tok.is(tok::ellipsis) && OpTok.is(tok::kw_sizeof) && + !getLangOpts().HLSLDisallowsVariadicTemplates()) { // HLSL Change SourceLocation EllipsisLoc = ConsumeToken(); SourceLocation LParenLoc, RParenLoc; IdentifierInfo *Name = nullptr; @@ -2801,7 +2802,7 @@ bool Parser::ParseExpressionList(SmallVectorImpl &Exprs, if (Tok.is(tok::ellipsis)) { // HLSL Change Starts - if (getLangOpts().HLSL) { + if (getLangOpts().HLSLDisallowsVariadicTemplates()) { Diag(Tok, diag::err_hlsl_variadic_templates); SkipUntil(tok::r_paren, StopBeforeMatch); Actions.CorrectDelayedTyposInExpr(Expr); diff --git a/tools/clang/lib/Parse/ParseInit.cpp b/tools/clang/lib/Parse/ParseInit.cpp index 1dcb20645d..1ef318f52e 100644 --- a/tools/clang/lib/Parse/ParseInit.cpp +++ b/tools/clang/lib/Parse/ParseInit.cpp @@ -438,7 +438,7 @@ ExprResult Parser::ParseBraceInitializer() { if (Tok.is(tok::ellipsis)) { // HLSL Change Starts - if (getLangOpts().HLSL) { + if (getLangOpts().HLSLDisallowsVariadicTemplates()) { Diag(Tok, diag::err_hlsl_unsupported_construct) << "expansion"; InitExprsOk = false; SkipUntil(tok::r_brace, StopBeforeMatch); diff --git a/tools/clang/lib/Parse/ParseTemplate.cpp b/tools/clang/lib/Parse/ParseTemplate.cpp index fbedf41caa..72772426b3 100644 --- a/tools/clang/lib/Parse/ParseTemplate.cpp +++ b/tools/clang/lib/Parse/ParseTemplate.cpp @@ -516,11 +516,12 @@ Decl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { SourceLocation EllipsisLoc; if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) { // HLSL Change Starts - if (getLangOpts().HLSL) { + if (getLangOpts().HLSLDisallowsVariadicTemplates()) { Diag(EllipsisLoc, diag::err_hlsl_variadic_templates); return nullptr; } // HLSL Change Ends + if (!getLangOpts().HLSL) // HLSL Change: HLSL has no C++98-compat warnings Diag(EllipsisLoc, getLangOpts().CPlusPlus11 ? diag::warn_cxx98_compat_variadic_templates @@ -620,9 +621,9 @@ Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) { SourceLocation EllipsisLoc; if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) { // HLSL Change Starts - if (getLangOpts().HLSL) + if (getLangOpts().HLSLDisallowsVariadicTemplates()) Diag(EllipsisLoc, diag::err_hlsl_variadic_templates); - else + else if (!getLangOpts().HLSL) // HLSL has no C++98-compat warnings // HLSL Change Ends Diag(EllipsisLoc, getLangOpts().CPlusPlus11 ? diag::warn_cxx98_compat_variadic_templates @@ -1296,7 +1297,7 @@ Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs) { SourceLocation EllipsisLoc; if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) { // HLSL Change Starts - if (getLangOpts().HLSL) { + if (getLangOpts().HLSLDisallowsVariadicTemplates()) { Diag(EllipsisLoc, diag::err_hlsl_unsupported_construct) << "ellipsis"; SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch); return true; diff --git a/tools/clang/lib/Sema/SemaTemplateVariadic.cpp b/tools/clang/lib/Sema/SemaTemplateVariadic.cpp index b575bfaf4d..e1dd3dce56 100644 --- a/tools/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/tools/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -545,7 +545,7 @@ bool Sema::CheckParameterPacksForExpansion( std::pair FirstPack; bool HaveFirstPack = false; - if (getLangOpts().HLSL) { + if (getLangOpts().HLSLDisallowsVariadicTemplates()) { Diag(EllipsisLoc, diag::err_hlsl_variadic_templates); return true; } diff --git a/tools/clang/lib/Sema/SemaType.cpp b/tools/clang/lib/Sema/SemaType.cpp index 7465cc2cec..223c633ec3 100644 --- a/tools/clang/lib/Sema/SemaType.cpp +++ b/tools/clang/lib/Sema/SemaType.cpp @@ -4280,14 +4280,14 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // parameter packs in the type of the non-type template parameter, then // it expands those parameter packs. // HLSL Change Starts - if (LangOpts.HLSL) { + if (LangOpts.HLSLDisallowsVariadicTemplates()) { S.Diag(D.getEllipsisLoc(), diag::err_hlsl_variadic_templates); break; } // HLSL Change Ends if (T->containsUnexpandedParameterPack()) T = Context.getPackExpansionType(T, None); - else + else if (!LangOpts.HLSL) // HLSL Change: HLSL has no C++98-compat warnings S.Diag(D.getEllipsisLoc(), LangOpts.CPlusPlus11 ? diag::warn_cxx98_compat_variadic_templates diff --git a/tools/clang/test/CodeGenSPIRV/variadic.templates.basic.hlsl b/tools/clang/test/CodeGenSPIRV/variadic.templates.basic.hlsl new file mode 100644 index 0000000000..764c50b94b --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/variadic.templates.basic.hlsl @@ -0,0 +1,41 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x -fcgl %s -spirv | FileCheck %s + +// Verify SPIR-V code generation for function parameter packs, pack expansions, +// and sizeof...(). + +template +T Sum(T First) { + return First; +} + +template +T Sum(T First, U Second, Rest... Others) { + return First + Sum(Second, Others...); +} + +template +uint CountArgs(Args... args) { + return sizeof...(Args); +} + +// CHECK-LABEL: %src_main = OpFunction %float None +// CHECK: OpFunctionCall %float %Sum +// CHECK: OpFunctionCall %uint %CountArgs +// CHECK-LABEL: %Sum = OpFunction %float None +// CHECK: OpFunctionCall %float %Sum_0 +// CHECK-LABEL: %CountArgs = OpFunction %uint None +// CHECK-NEXT: %args = OpFunctionParameter +// CHECK-NEXT: %args_0 = OpFunctionParameter +// CHECK-NEXT: %args_1 = OpFunctionParameter +// CHECK: OpReturnValue %uint_3 +// CHECK-LABEL: %Sum_0 = OpFunction %float None +// CHECK: OpFunctionCall %float %Sum_1 +// CHECK-LABEL: %Sum_1 = OpFunction %float None +// CHECK: OpFunctionCall %float %Sum_2 +// CHECK-LABEL: %Sum_2 = OpFunction %float None +float main() : SV_Target { + float total = Sum(1.0, 2.0, 3.0, 4.0); + // Keep CountArgs in the unoptimized SPIR-V without changing the result. + total += 0 * (float)CountArgs(1, 2, 3); + return total; +} diff --git a/tools/clang/test/CodeGenSPIRV/variadic.templates.builtin.hlsl b/tools/clang/test/CodeGenSPIRV/variadic.templates.builtin.hlsl new file mode 100644 index 0000000000..9ab283e49e --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/variadic.templates.builtin.hlsl @@ -0,0 +1,42 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x -fcgl %s -spirv | FileCheck %s + +// Verify pack expansion into HLSL vector, matrix, and resource templates. + +template +struct Holder { + StructuredBuffer Buf; +}; + +template +vector MakeVector(T First, Rest... Others) { + return vector(First, Others...); +} + +template +struct MatrixWrapper { + matrix M; +}; + +// CHECK: OpName %type_StructuredBuffer_float "type.StructuredBuffer.float" +// CHECK: OpName %g_Holder "g_Holder" +// CHECK: OpName %MatrixWrapper "MatrixWrapper" +// CHECK: OpMemberName %MatrixWrapper 0 "M" +// CHECK: OpName %MakeVector "MakeVector" +Holder g_Holder : register(t0); + +// CHECK-LABEL: %src_main = OpFunction %v4float +float4 main(float4 a : A) : SV_Target { + // CHECK: OpFunctionCall %v4float %MakeVector + vector v = MakeVector(a.x, a.y, a.z, a.w); + + MatrixWrapper mw; + // CHECK: OpCompositeConstruct %mat2v2float + mw.M = matrix(v.x, v.y, v.z, v.w); + + // CHECK: OpAccessChain %_ptr_Uniform_float {{%[0-9]+}} %int_0 %int_0 + float bufVal = g_Holder.Buf.Load(0); + + return float4(mw.M._11, mw.M._22, bufVal, v.w); +} + +// CHECK-LABEL: %MakeVector = OpFunction %v4float diff --git a/tools/clang/test/CodeGenSPIRV/variadic.templates.initlist-scalarize.hlsl b/tools/clang/test/CodeGenSPIRV/variadic.templates.initlist-scalarize.hlsl new file mode 100644 index 0000000000..d65bec15a7 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/variadic.templates.initlist-scalarize.hlsl @@ -0,0 +1,88 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x -fcgl %s -spirv | FileCheck %s + +// Verify that pack expansions and equivalent explicit initializers produce +// the expected SPIR-V composite structure. + +struct PairOf2 { + float2 lo; + float2 hi; +}; + +template +float4 PackVectorMixed(Ts... vals) { + float4 v = { vals... }; + return v; +} +float4 ManualVectorMixed(float2 a, float2 b) { + float4 v = { a, b }; + return v; +} + +template +float2 PackArrayOverflow(Ts... vals) { + float2 arr[2] = { vals... }; + return arr[0] + arr[1]; +} +float2 ManualArrayOverflow(float a, float b, float c, float d) { + float2 arr[2] = { a, b, c, d }; + return arr[0] + arr[1]; +} + +template +PairOf2 PackStruct(Ts... vals) { + PairOf2 s = { vals... }; + return s; +} +PairOf2 ManualStruct(float2 a, float2 b) { + PairOf2 s = { a, b }; + return s; +} + +// CHECK-LABEL: %PackVectorMixed = OpFunction %v4float +// CHECK: [[PACK_V0:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 0 +// CHECK: [[PACK_V1:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 1 +// CHECK: [[PACK_V2:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 0 +// CHECK: [[PACK_V3:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 1 +// CHECK: OpCompositeConstruct %v4float [[PACK_V0]] [[PACK_V1]] +// CHECK-SAME: [[PACK_V2]] [[PACK_V3]] +// CHECK-LABEL: %ManualVectorMixed = OpFunction %v4float +// CHECK: [[MANUAL_V0:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 0 +// CHECK: [[MANUAL_V1:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 1 +// CHECK: [[MANUAL_V2:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 0 +// CHECK: [[MANUAL_V3:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 1 +// CHECK: OpCompositeConstruct %v4float [[MANUAL_V0]] [[MANUAL_V1]] +// CHECK-SAME: [[MANUAL_V2]] [[MANUAL_V3]] + +// CHECK-LABEL: %PackArrayOverflow = OpFunction %v2float +// CHECK: [[PACK_A0:%[0-9]+]] = OpCompositeConstruct %v2float +// CHECK: [[PACK_A1:%[0-9]+]] = OpCompositeConstruct %v2float +// CHECK: OpCompositeConstruct %_arr_v2float_uint_2 [[PACK_A0]] [[PACK_A1]] +// CHECK-LABEL: %ManualArrayOverflow = OpFunction %v2float +// CHECK: [[MANUAL_A0:%[0-9]+]] = OpCompositeConstruct %v2float +// CHECK: [[MANUAL_A1:%[0-9]+]] = OpCompositeConstruct %v2float +// CHECK: OpCompositeConstruct %_arr_v2float_uint_2 [[MANUAL_A0]] [[MANUAL_A1]] + +// CHECK-LABEL: %PackStruct = OpFunction %PairOf2 +// CHECK: [[PACK_S0:%[0-9]+]] = OpLoad %v2float +// CHECK: [[PACK_S1:%[0-9]+]] = OpLoad %v2float +// CHECK: OpCompositeConstruct %PairOf2 [[PACK_S0]] [[PACK_S1]] +// CHECK-LABEL: %ManualStruct = OpFunction %PairOf2 +// CHECK: [[MANUAL_S0:%[0-9]+]] = OpLoad %v2float +// CHECK: [[MANUAL_S1:%[0-9]+]] = OpLoad %v2float +// CHECK: OpCompositeConstruct %PairOf2 [[MANUAL_S0]] [[MANUAL_S1]] +float4 main(float4 inp : A) : SV_Target { + float2 lo = inp.xy; + float2 hi = inp.zw; + + float4 vp = PackVectorMixed(lo, hi); + float4 vm = ManualVectorMixed(lo, hi); + + float2 op = PackArrayOverflow(inp.x, inp.y, inp.z, inp.w); + float2 om = ManualArrayOverflow(inp.x, inp.y, inp.z, inp.w); + + PairOf2 sp = PackStruct(lo, hi); + PairOf2 sm = ManualStruct(lo, hi); + + return vp + vm + float4(op + om, 0, 0) + + float4(sp.lo + sm.lo, sp.hi + sm.hi); +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-202x.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-202x.hlsl new file mode 100644 index 0000000000..acfbba4749 --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-202x.hlsl @@ -0,0 +1,24 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x %s | FileCheck %s +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 0, float 1.300000e+01) + +// Verify template and function parameter packs, pack expansions, and +// sizeof...() in DXIL. + +template +T Sum(T First) { + return First; +} + +template +T Sum(T First, U Second, Rest... Others) { + return First + Sum(Second, Others...); +} + +template +uint CountArgs(Args... args) { + return sizeof...(Args); +} + +float main() : SV_Target { + return Sum(1.0, 2.0, 3.0, 4.0) + CountArgs(1, 2, 3); +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-builtin-templates.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-builtin-templates.hlsl new file mode 100644 index 0000000000..866a7e44d1 --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-builtin-templates.hlsl @@ -0,0 +1,36 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x %s | FileCheck %s + +// Verify pack expansion into HLSL vector, matrix, and resource templates. + +template +struct Holder { + StructuredBuffer Buf; +}; +Holder g_Holder : register(t0); + +template +vector MakeVector(T First, Rest... Others) { + return vector(First, Others...); +} + +template +struct MatrixWrapper { + matrix M; +}; + +// CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57 +// CHECK: call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 0 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 1 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 2 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 3 +float4 main(float4 a : A) : SV_Target { + vector v = MakeVector(a.x, a.y, a.z, a.w); + + MatrixWrapper mw; + mw.M = matrix(v.x, v.y, v.z, v.w); + + float bufVal = g_Holder.Buf.Load(0); + + return float4(mw.M._11, mw.M._22, bufVal, v.w); +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-initlist-scalarize.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-initlist-scalarize.hlsl new file mode 100644 index 0000000000..79c6abdd35 --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-initlist-scalarize.hlsl @@ -0,0 +1,69 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x %s | FileCheck %s + +// Compare pack-expanded and explicit initializer-list scalarization using +// shader inputs so the results remain observable in DXIL. + +struct PairOf2 { + float2 lo; + float2 hi; +}; + +// Vector target filled from mixed vector+vector pack elements. +template +float4 PackVectorMixed(Ts... vals) { + float4 v = { vals... }; + return v; +} +float4 ManualVectorMixed(float2 a, float2 b) { + float4 v = { a, b }; + return v; +} + +// A flat initializer list overflowing across the boundary of an array of +// vectors. +template +float2 PackArrayOverflow(Ts... vals) { + float2 arr[2] = { vals... }; + return arr[0] + arr[1]; +} +float2 ManualArrayOverflow(float a, float b, float c, float d) { + float2 arr[2] = { a, b, c, d }; + return arr[0] + arr[1]; +} + +// Struct-member scalarization, including a vector-typed member. +template +PairOf2 PackStruct(Ts... vals) { + PairOf2 s = { vals... }; + return s; +} +PairOf2 ManualStruct(float2 a, float2 b) { + PairOf2 s = { a, b }; + return s; +} + +// CHECK: define void @main() +// CHECK-DAG: fmul fast float %{{.*}}, 2.000000e+00 +// CHECK-DAG: fmul fast float %{{.*}}, 6.000000e+00 +// CHECK-DAG: fmul fast float %{{.*}}, 4.000000e+00 +// CHECK-DAG: fmul fast float %{{.*}}, 4.000000e+00 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 0 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 1 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 2 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 3 +float4 main(float4 inp : A) : SV_Target { + float2 lo = inp.xy; + float2 hi = inp.zw; + + float4 vp = PackVectorMixed(lo, hi); + float4 vm = ManualVectorMixed(lo, hi); + + float2 op = PackArrayOverflow(inp.x, inp.y, inp.z, inp.w); + float2 om = ManualArrayOverflow(inp.x, inp.y, inp.z, inp.w); + + PairOf2 sp = PackStruct(lo, hi); + PairOf2 sm = ManualStruct(lo, hi); + + return vp + vm + float4(op + om, 0, 0) + + float4(sp.lo + sm.lo, sp.hi + sm.hi); +} diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-base-class-unsupported.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-base-class-unsupported.hlsl new file mode 100644 index 0000000000..a73c6bf518 --- /dev/null +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-base-class-unsupported.hlsl @@ -0,0 +1,23 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x %s -verify + +// HLSL supports only one fixed base type, not base-class packs. + +struct Base1 { + float x; +}; +struct Base2 { + float y; +}; + +// expected-error@+3 {{base type ellipsis is unsupported in HLSL}} +// expected-error@+2 {{multiple concrete base types specified}} +template +struct Derived : Bases... { + float z; +}; + +float main() : SV_Target { + Derived d; // expected-note {{in instantiation of template class 'Derived' requested here}} + d.z = 1.0; + return d.z; +} diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-initlist-scalarize-negative.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-initlist-scalarize-negative.hlsl new file mode 100644 index 0000000000..d6e5a69b84 --- /dev/null +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-initlist-scalarize-negative.hlsl @@ -0,0 +1,58 @@ +// RUN: %dxc -T lib_6_3 -HV 202x -verify %s + +// Compare diagnostics for pack-expanded and explicit initializer lists. + +float4 ManualTooFew(float a, float b) { + float4 v = { a, b }; // expected-error {{too few elements in vector initialization (expected 4 elements, have 2)}} + return v; +} + +template +float4 PackTooFew(Ts... vals) { + float4 v = { vals... }; // expected-error {{too few elements in vector initialization (expected 4 elements, have 2)}} + return v; +} + +float3 ManualTooMany(float a, float b, float c, float d) { + float3 v = { a, b, c, d }; // expected-error {{too many elements in vector initialization (expected 3 elements, have 4)}} + return v; +} + +template +float3 PackTooMany(Ts... vals) { + float3 v = { vals... }; // expected-error {{too many elements in vector initialization (expected 3 elements, have 4)}} + return v; +} + +struct S { float a; float2 b; float c; }; + +S ManualStructTooFew(float a, float b) { + S s = { a, b }; // expected-error {{too few elements in vector initialization (expected 4 elements, have 2)}} + return s; +} + +template +S PackStructTooFew(Ts... vals) { + S s = { vals... }; // expected-error {{too few elements in vector initialization (expected 4 elements, have 2)}} + return s; +} + +float2x2 ManualMatrixTooFew(float a, float b, float c) { + float2x2 m = { a, b, c }; // expected-error {{too few elements in vector initialization (expected 4 elements, have 3)}} + return m; +} + +template +float2x2 PackMatrixTooFew(Ts... vals) { + float2x2 m = { vals... }; // expected-error {{too few elements in vector initialization (expected 4 elements, have 3)}} + return m; +} + +export +float UseAll() { + float4 v1 = PackTooFew(1.0, 2.0); // expected-note {{in instantiation of function template specialization 'PackTooFew' requested here}} + float3 v2 = PackTooMany(1.0, 2.0, 3.0, 4.0); // expected-note {{in instantiation of function template specialization 'PackTooMany' requested here}} + S s = PackStructTooFew(1.0, 2.0); // expected-note {{in instantiation of function template specialization 'PackStructTooFew' requested here}} + float2x2 m = PackMatrixTooFew(1.0, 2.0, 3.0); // expected-note {{in instantiation of function template specialization 'PackMatrixTooFew' requested here}} + return v1.x + v2.x + s.a + m._11 + ManualTooFew(1.0, 2.0).x + ManualTooMany(1.0, 2.0, 3.0, 4.0).x + ManualStructTooFew(1.0, 2.0).a + ManualMatrixTooFew(1.0, 2.0, 3.0)._11; +} diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-initlist-scalarize.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-initlist-scalarize.hlsl new file mode 100644 index 0000000000..80b7413ddb --- /dev/null +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-initlist-scalarize.hlsl @@ -0,0 +1,106 @@ +// RUN: %dxc -T lib_6_3 -HV 202x -verify %s +// RUN: %dxc -T lib_6_3 -HV 202x -ast-dump %s 2>&1 | FileCheck %s + +// Compare pack-expanded and explicit initializer-list scalarization. + +// expected-no-diagnostics + +struct ScalarizeStruct { + float a; + float2 b; + float c; +}; + +// Exact-count vector scalarization. + +// CHECK: FunctionDecl {{.*}} used ManualVectorExact 'float4 (float, float, float, float)' +// CHECK: InitListExpr {{.*}} 'float4':'vector' +float4 ManualVectorExact(float a, float b, float c, float d) { + float4 v = { a, b, c, d }; + return v; +} + +// CHECK: FunctionDecl {{.*}} used PackVectorExact 'float4 (float, float, float, float)' +// CHECK: InitListExpr {{.*}} 'float4':'vector' +template +float4 PackVectorExact(Ts... vals) { + float4 v = { vals... }; + return v; +} + +// Mixed scalar and vector elements. + +// CHECK: FunctionDecl {{.*}} used ManualVectorMixed 'float3 (float2, float)' +// CHECK: InitListExpr {{.*}} 'float3':'vector' +float3 ManualVectorMixed(float2 a, float b) { + float3 v = { a, b }; + return v; +} + +// CHECK: FunctionDecl {{.*}} used PackVectorMixed 'float3 (vector, float)' +// CHECK: InitListExpr {{.*}} 'float3':'vector' +template +float3 PackVectorMixed(Ts... vals) { + float3 v = { vals... }; + return v; +} + +// Scalar overflow across array elements. + +float2 ManualArrayOverflow(float a, float b, float c, float d) { + float2 arr[2] = { a, b, c, d }; + return arr[0] + arr[1]; +} + +template +float2 PackArrayOverflow(Ts... vals) { + float2 arr[2] = { vals... }; + return arr[0] + arr[1]; +} + +// Struct-member scalarization. + +ScalarizeStruct ManualStruct(float a, float2 b, float c) { + ScalarizeStruct s = { a, b, c }; + return s; +} + +template +ScalarizeStruct PackStruct(Ts... vals) { + ScalarizeStruct s = { vals... }; + return s; +} + +// Matrix scalarization. + +float2x2 ManualMatrix(float a, float b, float c, float d) { + float2x2 m = { a, b, c, d }; + return m; +} + +template +float2x2 PackMatrix(Ts... vals) { + float2x2 m = { vals... }; + return m; +} + +export +float TestInitListScalarization() { + float4 v1 = ManualVectorExact(1.0, 2.0, 3.0, 4.0); + float4 v2 = PackVectorExact(1.0, 2.0, 3.0, 4.0); + + float2 b2 = float2(1.0, 2.0); + float3 v3 = ManualVectorMixed(b2, 3.0); + float3 v4 = PackVectorMixed(b2, 3.0); + + float o1 = ManualArrayOverflow(1.0, 2.0, 3.0, 4.0).x; + float o2 = PackArrayOverflow(1.0, 2.0, 3.0, 4.0).x; + + ScalarizeStruct s1 = ManualStruct(1.0, b2, 2.0); + ScalarizeStruct s2 = PackStruct(1.0, b2, 2.0); + + float2x2 m1 = ManualMatrix(1.0, 2.0, 3.0, 4.0); + float2x2 m2 = PackMatrix(1.0, 2.0, 3.0, 4.0); + + return v1.x + v2.x + v3.x + v4.x + o1 + o2 + s1.a + s2.a + m1._11 + m2._11; +} diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-negative.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-negative.hlsl new file mode 100644 index 0000000000..43a7f036dd --- /dev/null +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-negative.hlsl @@ -0,0 +1,54 @@ +// RUN: %dxc -T lib_6_3 -HV 202x -verify %s + +// Verify unsupported constructs and standard variadic-template diagnostics. + +// HLSL does not support multiple or variadic base classes. +struct Base1 { + float x; +}; +struct Base2 { + float y; +}; + +template +struct Derived : Bases... { + // expected-error@-1{{base type ellipsis is unsupported in HLSL}} + // expected-error@-2{{multiple concrete base types specified}} + float z; +}; + +void UseDerived() { + Derived d; + // expected-note@-1{{in instantiation of template class 'Derived' requested here}} + d.z = 0; +} + +// Class template parameter packs must remain last. +template +// expected-error@-1{{template parameter pack must be the last template parameter}} +struct PackNotLast {}; + +// sizeof...() only applies to the name of an actual parameter pack. +uint NotAPack() { + return sizeof...(NotAPack); + // expected-error@-1{{'NotAPack' does not refer to the name of a parameter pack}} +} + +// C-style variadic functions remain unsupported. +void CStyleVarArgs(int a, ...); +// expected-error@-1{{variadic arguments is unsupported in HLSL}} + +// Mismatched pack arguments use ordinary overload-resolution diagnostics. +template +struct Zipper { + template + static uint Count(Ts... ts, Us... us) { + // expected-note@-1{{candidate function not viable: requires 3 arguments, but 2 were provided}} + return sizeof...(Ts) + sizeof...(Us); + } +}; + +uint TestMismatchedPackArgs() { + return Zipper::Count(1, 2.0); + // expected-error@-1{{no matching function for call to 'Count'}} +} diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-pre202x.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-pre202x.hlsl new file mode 100644 index 0000000000..a41bb46317 --- /dev/null +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-pre202x.hlsl @@ -0,0 +1,37 @@ +// RUN: %dxc -T lib_6_3 -HV 2021 -verify %s + +// Variadic templates are a HLSL 202x feature. HLSL 2021 (the version prior +// to 202x that supports templates at all) must continue to reject the C++ +// variadic template syntax. + +template +// expected-error@-1{{variadic templates are not supported in HLSL}} +T Sum(T First, Rest... Others) { + // expected-error@-1{{unknown type name 'Rest'}} + // expected-error@-2{{variadic arguments is unsupported in HLSL}} + // expected-error@-3{{expected ')'}} + // expected-note@-4{{to match this '('}} + return First; +} + +template +// expected-error@-1{{'...' must be innermost component of anonymous pack declaration}} +// expected-error@-2{{variadic templates are not supported in HLSL}} +// expected-error@-3{{expected ',' or '>' in template-parameter-list}} +struct IntPack {}; + +uint CallSizeofPack() { + // sizeof...() is only meaningful with variadic templates, and remains + // unsupported before HLSL 202x. + return sizeof...(Values); + // expected-error@-1{{expected expression}} +} + +int CallInitListExpansion() { + int a = 1, b = 2, c = 3; + // Pack expansion inside a braced-init-list also remains unsupported + // before HLSL 202x. + int values[3] = {a, b, c...}; + // expected-error@-1{{expansion is unsupported in HLSL}} + return values[0]; +} diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl new file mode 100644 index 0000000000..051ac96e9f --- /dev/null +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl @@ -0,0 +1,131 @@ +// RUN: %dxc -T lib_6_3 -HV 202x -verify %s +// RUN: %dxc -T lib_6_3 -HV 202x -ast-dump %s 2>&1 | FileCheck %s + +// Verify HLSL 202x variadic templates in semantic analysis and the AST. + +// expected-no-diagnostics + +// Template type parameter pack. +// CHECK: FunctionTemplateDecl {{.*}} Sum +// CHECK: TemplateTypeParmDecl {{.*}} typename ... Rest +template +T Sum(T First, Rest... Others) { + return First; +} + +template +T Sum(T First) { + return First; +} + +// Non-type template parameter pack. +// CHECK: ClassTemplateDecl {{.*}} IntPack +// CHECK: NonTypeTemplateParmDecl {{.*}} 'int' ... Values +template +struct IntPack { + static const int Count = sizeof...(Values); +}; + +// A pack of types forwarded as a template argument list to another +// variadic template. +template +struct Tuple { + static const uint Size = sizeof...(Args); +}; + +template +uint CountArgs(Args... args) { + return sizeof...(Args); +} + +// Pack expansion forwarding a parameter pack as a template argument list. +template +uint Forward(Args... args) { + return Tuple::Size; +} + +// Recursive class-template partial specialization peeling one type off a +// pack at a time is the canonical variadic-template pattern for +// compile-time recursion and depends on partial ordering between the +// primary template and the partial specialization -- Sema machinery +// that HLSL previously never exercised at all. +// CHECK: ClassTemplatePartialSpecializationDecl {{.*}} PackLength +// CHECK: TemplateTypeParmDecl {{.*}} typename ... Rest +template +struct PackLength { + static const uint Value = 0; +}; +template +struct PackLength { + static const uint Value = 1 + PackLength::Value; +}; +// The fully-empty-pack explicit specialization is also exercised, since it +// is the recursion's base case. +template <> +struct PackLength<> { + static const uint Value = 0; +}; + +template +vector MakeVector(T First, Rest... Others) { + return vector(First, Others...); +} + +// Pack expansion inside a braced-init-list (a construct HLSL parses via +// its own, more restrictive initializer-list parsing, distinct from the +// call-argument and template-argument-list pack-expansion contexts). +template +T SumArray(T First, Rest... Others) { + T values[1 + sizeof...(Rest)] = {First, Others...}; + T total = (T)0; + for (int i = 0; i < 1 + sizeof...(Rest); ++i) + total += values[i]; + return total; +} + +// CHECK: ClassTemplateDecl {{.*}} BufferHolder +// CHECK: TemplateTypeParmDecl {{.*}} typename ... Ts +template +struct BufferHolder { + StructuredBuffer Buf; +}; +BufferHolder g_FloatHolder; +BufferHolder g_IntHolder; + +// CHECK: ClassTemplateDecl {{.*}} MatrixWrapper +// CHECK: NonTypeTemplateParmDecl {{.*}} 'int' ... Dims +template +struct MatrixWrapper { + matrix M; +}; + +// A member (nested) template with its own, independent parameter pack, +// combined with the pack of its enclosing class template. +template +struct Zipper { + template + static uint Count(Ts... ts, Us... us) { + return sizeof...(Ts) + sizeof...(Us); + } +}; + +// Zero-argument (empty pack) instantiation. +uint TestEmptyPack() { return CountArgs(); } + +export +float TestVariadic() { + float a = Sum(1.0, 2.0, 3.0); + uint b = CountArgs(1, 2, 3, 4); + uint c = Forward(1, 2); + const int d = IntPack<1, 2, 3>::Count; + const uint e = PackLength::Value; + const uint eEmpty = PackLength<>::Value; + vector v = MakeVector(1.0, 2.0, 3.0, 4.0); + float f = SumArray(1.0, 2.0, 3.0, 4.0, 5.0); + MatrixWrapper mw; + mw.M = matrix(1, 2, 3, 4); + uint z = Zipper::Count(1, 2.0, 3.0); + uint empty = TestEmptyPack(); + return a + b + c + d + e + eEmpty + v.x + f + mw.M._11 + z + empty + + (float)g_FloatHolder.Buf.Load(0) + (float)g_IntHolder.Buf.Load(0); +} From dcdcf52b607607c06c04adec2e8969d5dbf04696 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Fri, 11 Sep 2026 12:14:29 -0500 Subject: [PATCH 02/10] clang-format --- tools/clang/lib/Parse/ParseDecl.cpp | 3 +-- tools/clang/lib/Parse/ParseTemplate.cpp | 7 +++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/tools/clang/lib/Parse/ParseDecl.cpp b/tools/clang/lib/Parse/ParseDecl.cpp index f382880070..ef4746fe39 100644 --- a/tools/clang/lib/Parse/ParseDecl.cpp +++ b/tools/clang/lib/Parse/ParseDecl.cpp @@ -6111,8 +6111,7 @@ void Parser::ParseDirectDeclarator(Declarator &D) { !((D.getContext() == Declarator::PrototypeContext || D.getContext() == Declarator::LambdaExprParameterContext || D.getContext() == Declarator::BlockLiteralContext) && - NextToken().is(tok::r_paren) && - !D.hasGroupingParens() && + NextToken().is(tok::r_paren) && !D.hasGroupingParens() && !Actions.containsUnexpandedParameterPacks(D) && D.getDeclSpec().getTypeSpecType() != TST_auto)) { SourceLocation EllipsisLoc = ConsumeToken(); diff --git a/tools/clang/lib/Parse/ParseTemplate.cpp b/tools/clang/lib/Parse/ParseTemplate.cpp index 72772426b3..960dc6fa30 100644 --- a/tools/clang/lib/Parse/ParseTemplate.cpp +++ b/tools/clang/lib/Parse/ParseTemplate.cpp @@ -522,10 +522,9 @@ Decl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { } // HLSL Change Ends if (!getLangOpts().HLSL) // HLSL Change: HLSL has no C++98-compat warnings - Diag(EllipsisLoc, - getLangOpts().CPlusPlus11 - ? diag::warn_cxx98_compat_variadic_templates - : diag::ext_variadic_templates); + Diag(EllipsisLoc, getLangOpts().CPlusPlus11 + ? diag::warn_cxx98_compat_variadic_templates + : diag::ext_variadic_templates); } // Grab the template parameter name (if given) From e87a9e5758a00818081ded395b10a9fdac5ff35f Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Fri, 11 Sep 2026 13:46:44 -0500 Subject: [PATCH 03/10] Fix broken SPIRV test Not sure how I missed this before pushing initially... --- .../CodeGenSPIRV/variadic.templates.builtin.hlsl | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/tools/clang/test/CodeGenSPIRV/variadic.templates.builtin.hlsl b/tools/clang/test/CodeGenSPIRV/variadic.templates.builtin.hlsl index 9ab283e49e..b6aa079025 100644 --- a/tools/clang/test/CodeGenSPIRV/variadic.templates.builtin.hlsl +++ b/tools/clang/test/CodeGenSPIRV/variadic.templates.builtin.hlsl @@ -1,11 +1,6 @@ // RUN: %dxc -E main -T ps_6_0 -HV 202x -fcgl %s -spirv | FileCheck %s -// Verify pack expansion into HLSL vector, matrix, and resource templates. - -template -struct Holder { - StructuredBuffer Buf; -}; +// Verify pack expansion into HLSL vector and matrix templates. template vector MakeVector(T First, Rest... Others) { @@ -17,12 +12,9 @@ struct MatrixWrapper { matrix M; }; -// CHECK: OpName %type_StructuredBuffer_float "type.StructuredBuffer.float" -// CHECK: OpName %g_Holder "g_Holder" // CHECK: OpName %MatrixWrapper "MatrixWrapper" // CHECK: OpMemberName %MatrixWrapper 0 "M" // CHECK: OpName %MakeVector "MakeVector" -Holder g_Holder : register(t0); // CHECK-LABEL: %src_main = OpFunction %v4float float4 main(float4 a : A) : SV_Target { @@ -33,10 +25,7 @@ float4 main(float4 a : A) : SV_Target { // CHECK: OpCompositeConstruct %mat2v2float mw.M = matrix(v.x, v.y, v.z, v.w); - // CHECK: OpAccessChain %_ptr_Uniform_float {{%[0-9]+}} %int_0 %int_0 - float bufVal = g_Holder.Buf.Load(0); - - return float4(mw.M._11, mw.M._22, bufVal, v.w); + return float4(mw.M._11, mw.M._22, v.z, v.w); } // CHECK-LABEL: %MakeVector = OpFunction %v4float From ca705fb577a3227c04f90649244b37d76053f261 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Fri, 11 Sep 2026 15:15:57 -0500 Subject: [PATCH 04/10] Fix handling of parameter modifiers In tree transform we need to generate an expanded parameter modifier set for the parameter pack. We also need ot handle value-dependent expressions for constant evaluation. --- tools/clang/lib/Sema/SemaHLSL.cpp | 2 +- tools/clang/lib/Sema/TreeTransform.h | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index da6f7f8f90..33005c0b58 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -6099,7 +6099,7 @@ class HLSLExternalSource : public ExternalSemaSource { if (isMatrix || isVector) { Expr *expr = arg.getAsExpr(); llvm::APSInt constantResult; - if (expr != nullptr && + if (expr != nullptr && !expr->isValueDependent() && expr->isIntegerConstantExpr(constantResult, *m_context)) { if (CheckRangedTemplateArgument(argSrcLoc, constantResult, isVector)) diff --git a/tools/clang/lib/Sema/TreeTransform.h b/tools/clang/lib/Sema/TreeTransform.h index ef3a83c988..f48cc3d0c1 100644 --- a/tools/clang/lib/Sema/TreeTransform.h +++ b/tools/clang/lib/Sema/TreeTransform.h @@ -4753,8 +4753,17 @@ QualType TreeTransform::TransformFunctionProtoType( !std::equal(T->param_type_begin(), T->param_type_end(), ParamTypes.begin()) || EPIChanged) { // HLSL Change - FIX - We should move param mods to parameter QualTypes + SmallVector ExpandedParamMods; + ArrayRef ParamMods = T->getParamMods(); + if (ParamMods.size() != ParamTypes.size()) { + ExpandedParamMods.reserve(ParamDecls.size()); + for (ParmVarDecl *Param : ParamDecls) + ExpandedParamMods.push_back(Param ? Param->getParamModifiers() + : hlsl::ParameterModifier()); + ParamMods = ExpandedParamMods; + } Result = getDerived().RebuildFunctionProtoType(ResultType, ParamTypes, - T->getParamMods(), EPI); + ParamMods, EPI); // HLSL Change - End if (Result.isNull()) return QualType(); From 2472bd04831588ae9e28aadba4db92b03b196a35 Mon Sep 17 00:00:00 2001 From: Chris B Date: Mon, 21 Sep 2026 12:17:42 -0500 Subject: [PATCH 05/10] Address PR feedback * Makes fold expressions emit a warning * Fixes variadic template feature checks --- .../include/clang/Basic/DiagnosticGroups.td | 4 +++- .../clang/Basic/DiagnosticParseKinds.td | 3 +++ tools/clang/lib/Frontend/InitPreprocessor.cpp | 4 ++-- tools/clang/lib/Lex/PPMacroExpansion.cpp | 8 ++++++-- tools/clang/lib/Parse/ParseExpr.cpp | 9 ++++++--- .../templates/variadic-templates-pre202x.hlsl | 10 ++++++++++ .../v202x/templates/variadic-templates.hlsl | 19 +++++++++++++++++-- 7 files changed, 47 insertions(+), 10 deletions(-) diff --git a/tools/clang/include/clang/Basic/DiagnosticGroups.td b/tools/clang/include/clang/Basic/DiagnosticGroups.td index 758916199e..1f2e40c0bc 100644 --- a/tools/clang/include/clang/Basic/DiagnosticGroups.td +++ b/tools/clang/include/clang/Basic/DiagnosticGroups.td @@ -811,5 +811,7 @@ def HLSL2026Compat : DiagGroup<"hlsl-2026-compat", [HLSLLegacyLiterals, HLSLEffectsSyntax]>; def HLSLGroupshared202x : DiagGroup<"hlsl-groupshared-202x">; -def HLSL202xExtensions : DiagGroup<"hlsl-202x-extensions", [HLSLGroupshared202x]>; +def HLSLFoldExpressions : DiagGroup<"hlsl-fold-expressions">; +def HLSL202xExtensions : DiagGroup<"hlsl-202x-extensions", + [HLSLFoldExpressions, HLSLGroupshared202x]>; // HLSL Change Ends diff --git a/tools/clang/include/clang/Basic/DiagnosticParseKinds.td b/tools/clang/include/clang/Basic/DiagnosticParseKinds.td index 4fb2493f91..efd7c44654 100644 --- a/tools/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/tools/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -279,6 +279,9 @@ def warn_cxx98_compat_generalized_initializer_lists : Warning< InGroup, DefaultIgnore; def err_hlsl_compat_generalized_initializer_lists : Error< "generalized initializer lists are incompatible with HLSL">; +def warn_hlsl_fold_expression : Warning< + "fold expressions are a C++17 extension and are not part of standard HLSL">, + InGroup; def err_init_list_bin_op : Error<"initializer list cannot be used on the " "%select{left|right}0 hand side of operator '%1'">; def warn_cxx98_compat_trailing_return_type : Warning< diff --git a/tools/clang/lib/Frontend/InitPreprocessor.cpp b/tools/clang/lib/Frontend/InitPreprocessor.cpp index b0a53650b5..47c43e46aa 100644 --- a/tools/clang/lib/Frontend/InitPreprocessor.cpp +++ b/tools/clang/lib/Frontend/InitPreprocessor.cpp @@ -376,6 +376,8 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI, // HLSL Version Builder.defineMacro("__HLSL_VERSION", Twine((unsigned int)LangOpts.HLSLVersion)); + if (!LangOpts.HLSLDisallowsVariadicTemplates()) + Builder.defineMacro("__cpp_variadic_templates", "200704"); // This define is enabled in Clang and allows conditionally compiling code // based on whether or not native 16-bit types are supported. if (!LangOpts.UseMinPrecision) @@ -487,7 +489,6 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, Builder.defineMacro("__cpp_decltype", "200707"); Builder.defineMacro("__cpp_attributes", "200809"); Builder.defineMacro("__cpp_rvalue_references", "200610"); - Builder.defineMacro("__cpp_variadic_templates", "200704"); Builder.defineMacro("__cpp_initializer_lists", "200806"); Builder.defineMacro("__cpp_delegating_constructors", "200604"); Builder.defineMacro("__cpp_nsdmi", "200809"); @@ -495,7 +496,6 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, Builder.defineMacro("__cpp_ref_qualifiers", "200710"); Builder.defineMacro("__cpp_alias_templates", "200704"); } - // C++14 features. if (LangOpts.CPlusPlus14) { Builder.defineMacro("__cpp_binary_literals", "201304"); diff --git a/tools/clang/lib/Lex/PPMacroExpansion.cpp b/tools/clang/lib/Lex/PPMacroExpansion.cpp index 16040d69c7..55859a9831 100644 --- a/tools/clang/lib/Lex/PPMacroExpansion.cpp +++ b/tools/clang/lib/Lex/PPMacroExpansion.cpp @@ -1156,7 +1156,9 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("cxx_unicode_literals", LangOpts.CPlusPlus11) .Case("cxx_unrestricted_unions", LangOpts.CPlusPlus11) .Case("cxx_user_literals", LangOpts.CPlusPlus11) - .Case("cxx_variadic_templates", LangOpts.CPlusPlus11) + .Case("cxx_variadic_templates", + LangOpts.CPlusPlus11 || + (LangOpts.HLSL && !LangOpts.HLSLDisallowsVariadicTemplates())) // C++1y features .Case("cxx_aggregate_nsdmi", LangOpts.CPlusPlus14) .Case("cxx_binary_literals", LangOpts.CPlusPlus14) @@ -1249,7 +1251,9 @@ static bool HasExtension(const Preprocessor &PP, const IdentifierInfo *II) { .Case("cxx_range_for", LangOpts.CPlusPlus) .Case("cxx_reference_qualified_functions", LangOpts.CPlusPlus) .Case("cxx_rvalue_references", LangOpts.CPlusPlus) - .Case("cxx_variadic_templates", LangOpts.CPlusPlus) + .Case("cxx_variadic_templates", + LangOpts.CPlusPlus && + !LangOpts.HLSLDisallowsVariadicTemplates()) // C++1y features supported by other languages as extensions. .Case("cxx_binary_literals", true) .Case("cxx_init_captures", LangOpts.CPlusPlus11) diff --git a/tools/clang/lib/Parse/ParseExpr.cpp b/tools/clang/lib/Parse/ParseExpr.cpp index da9f6b5eeb..76117ceb9b 100644 --- a/tools/clang/lib/Parse/ParseExpr.cpp +++ b/tools/clang/lib/Parse/ParseExpr.cpp @@ -2748,9 +2748,12 @@ ExprResult Parser::ParseFoldExpression(ExprResult LHS, } } - Diag(EllipsisLoc, getLangOpts().CPlusPlus1z - ? diag::warn_cxx14_compat_fold_expression - : diag::ext_fold_expression); + if (getLangOpts().HLSL && !getLangOpts().HLSLDisallowsVariadicTemplates()) + Diag(EllipsisLoc, diag::warn_hlsl_fold_expression); + else + Diag(EllipsisLoc, getLangOpts().CPlusPlus1z + ? diag::warn_cxx14_compat_fold_expression + : diag::ext_fold_expression); T.consumeClose(); return Actions.ActOnCXXFoldExpr(T.getOpenLocation(), LHS.get(), Kind, diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-pre202x.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-pre202x.hlsl index a41bb46317..2c3b2a8313 100644 --- a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-pre202x.hlsl +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-pre202x.hlsl @@ -4,6 +4,16 @@ // to 202x that supports templates at all) must continue to reject the C++ // variadic template syntax. +#if __has_feature(cxx_variadic_templates) +#error HLSL 2021 should not report variadic templates as a feature +#endif +#if __has_extension(cxx_variadic_templates) +#error HLSL 2021 should not report variadic templates as an extension +#endif +#ifdef __cpp_variadic_templates +#error HLSL 2021 should not define __cpp_variadic_templates +#endif + template // expected-error@-1{{variadic templates are not supported in HLSL}} T Sum(T First, Rest... Others) { diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl index 051ac96e9f..48b2a70830 100644 --- a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl @@ -3,7 +3,15 @@ // Verify HLSL 202x variadic templates in semantic analysis and the AST. -// expected-no-diagnostics +#if !__has_feature(cxx_variadic_templates) +#error HLSL 202x should report variadic templates as a feature +#endif +#if !__has_extension(cxx_variadic_templates) +#error HLSL 202x should report variadic templates as an extension +#endif +#if __cpp_variadic_templates != 200704 +#error HLSL 202x should define __cpp_variadic_templates +#endif // Template type parameter pack. // CHECK: FunctionTemplateDecl {{.*}} Sum @@ -38,6 +46,11 @@ uint CountArgs(Args... args) { return sizeof...(Args); } +template +uint FoldSum(Args... args) { + return (args + ...); // expected-warning {{fold expressions are a C++17 extension and are not part of standard HLSL}} +} + // Pack expansion forwarding a parameter pack as a template argument list. template uint Forward(Args... args) { @@ -126,6 +139,8 @@ float TestVariadic() { mw.M = matrix(1, 2, 3, 4); uint z = Zipper::Count(1, 2.0, 3.0); uint empty = TestEmptyPack(); + uint folded = FoldSum(1, 2, 3, 4); return a + b + c + d + e + eEmpty + v.x + f + mw.M._11 + z + empty + - (float)g_FloatHolder.Buf.Load(0) + (float)g_IntHolder.Buf.Load(0); + folded + (float)g_FloatHolder.Buf.Load(0) + + (float)g_IntHolder.Buf.Load(0); } From b859c867165c6e5848d1fb1dd5e705e2cd6302b5 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Mon, 21 Sep 2026 12:18:50 -0500 Subject: [PATCH 06/10] clang-format ../tools/clang/include/clang/Basic/DiagnosticGroups.td ../tools/clang/include/clang/Basic/DiagnosticParseKinds.td --- .../include/clang/Basic/DiagnosticGroups.td | 5 +- .../clang/Basic/DiagnosticParseKinds.td | 7 ++- tools/clang/lib/Lex/PPMacroExpansion.cpp | 55 +++++++++---------- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/tools/clang/include/clang/Basic/DiagnosticGroups.td b/tools/clang/include/clang/Basic/DiagnosticGroups.td index 1f2e40c0bc..6de83dad18 100644 --- a/tools/clang/include/clang/Basic/DiagnosticGroups.td +++ b/tools/clang/include/clang/Basic/DiagnosticGroups.td @@ -812,6 +812,7 @@ def HLSL2026Compat def HLSLGroupshared202x : DiagGroup<"hlsl-groupshared-202x">; def HLSLFoldExpressions : DiagGroup<"hlsl-fold-expressions">; -def HLSL202xExtensions : DiagGroup<"hlsl-202x-extensions", - [HLSLFoldExpressions, HLSLGroupshared202x]>; +def HLSL202xExtensions + : DiagGroup<"hlsl-202x-extensions", [HLSLFoldExpressions, + HLSLGroupshared202x]>; // HLSL Change Ends diff --git a/tools/clang/include/clang/Basic/DiagnosticParseKinds.td b/tools/clang/include/clang/Basic/DiagnosticParseKinds.td index efd7c44654..0e5e5aeca8 100644 --- a/tools/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/tools/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -279,9 +279,10 @@ def warn_cxx98_compat_generalized_initializer_lists : Warning< InGroup, DefaultIgnore; def err_hlsl_compat_generalized_initializer_lists : Error< "generalized initializer lists are incompatible with HLSL">; -def warn_hlsl_fold_expression : Warning< - "fold expressions are a C++17 extension and are not part of standard HLSL">, - InGroup; +def warn_hlsl_fold_expression + : Warning<"fold expressions are a C++17 extension and are not part of " + "standard HLSL">, + InGroup; def err_init_list_bin_op : Error<"initializer list cannot be used on the " "%select{left|right}0 hand side of operator '%1'">; def warn_cxx98_compat_trailing_return_type : Warning< diff --git a/tools/clang/lib/Lex/PPMacroExpansion.cpp b/tools/clang/lib/Lex/PPMacroExpansion.cpp index 55859a9831..9dedf6ac2c 100644 --- a/tools/clang/lib/Lex/PPMacroExpansion.cpp +++ b/tools/clang/lib/Lex/PPMacroExpansion.cpp @@ -1157,8 +1157,8 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("cxx_unrestricted_unions", LangOpts.CPlusPlus11) .Case("cxx_user_literals", LangOpts.CPlusPlus11) .Case("cxx_variadic_templates", - LangOpts.CPlusPlus11 || - (LangOpts.HLSL && !LangOpts.HLSLDisallowsVariadicTemplates())) + LangOpts.CPlusPlus11 || + (LangOpts.HLSL && !LangOpts.HLSLDisallowsVariadicTemplates())) // C++1y features .Case("cxx_aggregate_nsdmi", LangOpts.CPlusPlus14) .Case("cxx_binary_literals", LangOpts.CPlusPlus14) @@ -1233,32 +1233,31 @@ static bool HasExtension(const Preprocessor &PP, const IdentifierInfo *II) { // Because we inherit the feature list from HasFeature, this string switch // must be less restrictive than HasFeature's. return llvm::StringSwitch(Extension) - // C11 features supported by other languages as extensions. - .Case("c_alignas", true) - .Case("c_alignof", true) - .Case("c_atomic", true) - .Case("c_generic_selections", true) - .Case("c_static_assert", true) - .Case("c_thread_local", PP.getTargetInfo().isTLSSupported()) - // C++11 features supported by other languages as extensions. - .Case("cxx_atomic", LangOpts.CPlusPlus) - .Case("cxx_deleted_functions", LangOpts.CPlusPlus) - .Case("cxx_explicit_conversions", LangOpts.CPlusPlus) - .Case("cxx_inline_namespaces", LangOpts.CPlusPlus) - .Case("cxx_local_type_template_args", LangOpts.CPlusPlus) - .Case("cxx_nonstatic_member_init", LangOpts.CPlusPlus) - .Case("cxx_override_control", LangOpts.CPlusPlus) - .Case("cxx_range_for", LangOpts.CPlusPlus) - .Case("cxx_reference_qualified_functions", LangOpts.CPlusPlus) - .Case("cxx_rvalue_references", LangOpts.CPlusPlus) - .Case("cxx_variadic_templates", - LangOpts.CPlusPlus && - !LangOpts.HLSLDisallowsVariadicTemplates()) - // C++1y features supported by other languages as extensions. - .Case("cxx_binary_literals", true) - .Case("cxx_init_captures", LangOpts.CPlusPlus11) - .Case("cxx_variable_templates", LangOpts.CPlusPlus) - .Default(false); + // C11 features supported by other languages as extensions. + .Case("c_alignas", true) + .Case("c_alignof", true) + .Case("c_atomic", true) + .Case("c_generic_selections", true) + .Case("c_static_assert", true) + .Case("c_thread_local", PP.getTargetInfo().isTLSSupported()) + // C++11 features supported by other languages as extensions. + .Case("cxx_atomic", LangOpts.CPlusPlus) + .Case("cxx_deleted_functions", LangOpts.CPlusPlus) + .Case("cxx_explicit_conversions", LangOpts.CPlusPlus) + .Case("cxx_inline_namespaces", LangOpts.CPlusPlus) + .Case("cxx_local_type_template_args", LangOpts.CPlusPlus) + .Case("cxx_nonstatic_member_init", LangOpts.CPlusPlus) + .Case("cxx_override_control", LangOpts.CPlusPlus) + .Case("cxx_range_for", LangOpts.CPlusPlus) + .Case("cxx_reference_qualified_functions", LangOpts.CPlusPlus) + .Case("cxx_rvalue_references", LangOpts.CPlusPlus) + .Case("cxx_variadic_templates", + LangOpts.CPlusPlus && !LangOpts.HLSLDisallowsVariadicTemplates()) + // C++1y features supported by other languages as extensions. + .Case("cxx_binary_literals", true) + .Case("cxx_init_captures", LangOpts.CPlusPlus11) + .Case("cxx_variable_templates", LangOpts.CPlusPlus) + .Default(false); } /// EvaluateHasIncludeCommon - Process a '__has_include("path")' From e0b2653d04cf5537e1006186dbd3ae6487039481 Mon Sep 17 00:00:00 2001 From: Chris B Date: Mon, 21 Sep 2026 18:40:28 -0500 Subject: [PATCH 07/10] Respond to review feedback --- tools/clang/lib/AST/ASTContext.cpp | 2 ++ tools/clang/lib/Frontend/InitPreprocessor.cpp | 1 + .../clang/lib/Sema/SemaTemplateDeduction.cpp | 21 ++++++++++++------- .../v202x/templates/variadic-templates.hlsl | 8 ++++++- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/tools/clang/lib/AST/ASTContext.cpp b/tools/clang/lib/AST/ASTContext.cpp index dd6ec2784d..19511b0f41 100644 --- a/tools/clang/lib/AST/ASTContext.cpp +++ b/tools/clang/lib/AST/ASTContext.cpp @@ -3078,6 +3078,8 @@ ASTContext::getFunctionType(QualType ResultTy, ArrayRef ArgArray, const FunctionProtoType::ExtProtoInfo &EPI, ArrayRef ParamMods) const { // HLSL Change - param mods size_t NumArgs = ArgArray.size(); + assert((ParamMods.empty() || ParamMods.size() == NumArgs) && + "parameter modifier count does not match parameter count"); // Unique functions, to guarantee there is only one function of a particular // structure. diff --git a/tools/clang/lib/Frontend/InitPreprocessor.cpp b/tools/clang/lib/Frontend/InitPreprocessor.cpp index 47c43e46aa..81f2abb2cc 100644 --- a/tools/clang/lib/Frontend/InitPreprocessor.cpp +++ b/tools/clang/lib/Frontend/InitPreprocessor.cpp @@ -489,6 +489,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, Builder.defineMacro("__cpp_decltype", "200707"); Builder.defineMacro("__cpp_attributes", "200809"); Builder.defineMacro("__cpp_rvalue_references", "200610"); + Builder.defineMacro("__cpp_variadic_templates", "200704"); Builder.defineMacro("__cpp_initializer_lists", "200806"); Builder.defineMacro("__cpp_delegating_constructors", "200604"); Builder.defineMacro("__cpp_nsdmi", "200809"); diff --git a/tools/clang/lib/Sema/SemaTemplateDeduction.cpp b/tools/clang/lib/Sema/SemaTemplateDeduction.cpp index e2e510d0c1..62fb28c195 100644 --- a/tools/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/tools/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2555,16 +2555,17 @@ Sema::SubstituteExplicitTemplateArguments( // Isolate our substituted parameters from our caller. LocalInstantiationScope InstScope(*this, /*MergeWithOuterScope*/true); + SmallVector ParamDecls; // Instantiate the types of each of the function parameters given the // explicitly-specified template arguments. If the function has a trailing // return type, substitute it after the arguments to ensure we substitute // in lexical order. if (Proto->hasTrailingReturn()) { - if (SubstParmTypes(Function->getLocation(), - Function->param_begin(), Function->getNumParams(), + if (SubstParmTypes(Function->getLocation(), Function->param_begin(), + Function->getNumParams(), MultiLevelTemplateArgumentList(*ExplicitArgumentList), - ParamTypes)) + ParamTypes, &ParamDecls)) return TDK_SubstitutionFailure; } @@ -2598,18 +2599,22 @@ Sema::SubstituteExplicitTemplateArguments( // Instantiate the types of each of the function parameters given the // explicitly-specified template arguments if we didn't do so earlier. if (!Proto->hasTrailingReturn() && - SubstParmTypes(Function->getLocation(), - Function->param_begin(), Function->getNumParams(), + SubstParmTypes(Function->getLocation(), Function->param_begin(), + Function->getNumParams(), MultiLevelTemplateArgumentList(*ExplicitArgumentList), - ParamTypes)) + ParamTypes, &ParamDecls)) return TDK_SubstitutionFailure; if (FunctionType) { // HLSL Change - FIX - We should move param mods to parameter QualTypes + SmallVector ParamMods; + ParamMods.reserve(ParamDecls.size()); + for (ParmVarDecl *Param : ParamDecls) + ParamMods.push_back(Param ? Param->getParamModifiers() + : hlsl::ParameterModifier()); *FunctionType = BuildFunctionType( ResultType, ParamTypes, Function->getLocation(), - Function->getDeclName(), Proto->getExtProtoInfo(), - cast(Function->getType())->getParamMods()); + Function->getDeclName(), Proto->getExtProtoInfo(), ParamMods); // HLSL Change - End if (FunctionType->isNull() || Trap.hasErrorOccurred()) return TDK_SubstitutionFailure; diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl index 48b2a70830..f90df92039 100644 --- a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl @@ -125,6 +125,9 @@ struct Zipper { // Zero-argument (empty pack) instantiation. uint TestEmptyPack() { return CountArgs(); } +template +void ExplicitArgs(Ts... values) {} + export float TestVariadic() { float a = Sum(1.0, 2.0, 3.0); @@ -139,7 +142,10 @@ float TestVariadic() { mw.M = matrix(1, 2, 3, 4); uint z = Zipper::Count(1, 2.0, 3.0); uint empty = TestEmptyPack(); - uint folded = FoldSum(1, 2, 3, 4); + int explicitInt; + float explicitFloat; + ExplicitArgs(explicitInt, explicitFloat); + uint folded = FoldSum(1, 2, 3, 4); return a + b + c + d + e + eEmpty + v.x + f + mw.M._11 + z + empty + folded + (float)g_FloatHolder.Buf.Load(0) + (float)g_IntHolder.Buf.Load(0); From aa56ead8ef3dd366d9a671ffce415a0704745486 Mon Sep 17 00:00:00 2001 From: Chris B Date: Tue, 22 Sep 2026 08:14:22 -0500 Subject: [PATCH 08/10] Fix parameter packs with parameter modifiers --- tools/clang/lib/AST/Decl.cpp | 20 +++++++++++++------ tools/clang/lib/Sema/TreeTransform.h | 13 ++++++++++-- .../variadic-templates-negative.hlsl | 11 ++++++++++ .../v202x/templates/variadic-templates.hlsl | 14 +++++++++++-- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/tools/clang/lib/AST/Decl.cpp b/tools/clang/lib/AST/Decl.cpp index 4c8c0ae268..3919809ff1 100644 --- a/tools/clang/lib/AST/Decl.cpp +++ b/tools/clang/lib/AST/Decl.cpp @@ -2370,15 +2370,23 @@ unsigned ParmVarDecl::getParameterIndexLarge() const { // HLSL Change Begins void ParmVarDecl::updateOutParamToRefType(ASTContext &C) { + QualType ParamType = getType(); + const PackExpansionType *Expansion = dyn_cast(ParamType); + if (Expansion) + ParamType = Expansion->getPattern(); + // Aggregate type will be indirect param convert to pointer type. // So don't update to ReferenceType. - if ((!getType()->isArrayType() && !getType()->isRecordType()) || - hlsl::IsHLSLVecMatType(getType())) - setType(C.getLValueReferenceType(getType(), false)); + if ((!ParamType->isArrayType() && !ParamType->isRecordType()) || + hlsl::IsHLSLVecMatType(ParamType)) + ParamType = C.getLValueReferenceType(ParamType, false); // Add restrict to out param. - QualType QT = getType(); - QT.addRestrict(); - setType(QT); + ParamType.addRestrict(); + + if (Expansion) + ParamType = C.getPackExpansionType(ParamType, + Expansion->getNumExpansions()); + setType(ParamType); } // HLSL Change Ends diff --git a/tools/clang/lib/Sema/TreeTransform.h b/tools/clang/lib/Sema/TreeTransform.h index f48cc3d0c1..e2d7419de3 100644 --- a/tools/clang/lib/Sema/TreeTransform.h +++ b/tools/clang/lib/Sema/TreeTransform.h @@ -10731,9 +10731,18 @@ TreeTransform::TransformBlockExpr(BlockExpr *E) { getDerived().TransformType(exprFunctionType->getReturnType()); // HLSL Change - FIX - We should move param mods to parameter QualTypes + SmallVector ExpandedParamMods; + ArrayRef ParamMods = + exprFunctionType->getParamMods(); + if (ParamMods.size() != paramTypes.size()) { + ExpandedParamMods.reserve(params.size()); + for (ParmVarDecl *Param : params) + ExpandedParamMods.push_back(Param ? Param->getParamModifiers() + : hlsl::ParameterModifier()); + ParamMods = ExpandedParamMods; + } QualType functionType = getDerived().RebuildFunctionProtoType( - exprResultType, paramTypes, exprFunctionType->getParamMods(), - exprFunctionType->getExtProtoInfo()); + exprResultType, paramTypes, ParamMods, exprFunctionType->getExtProtoInfo()); // HLSL Change - End blockScope->FunctionType = functionType; diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-negative.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-negative.hlsl index 43a7f036dd..8d5059ee8b 100644 --- a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-negative.hlsl +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-negative.hlsl @@ -52,3 +52,14 @@ uint TestMismatchedPackArgs() { return Zipper::Count(1, 2.0); // expected-error@-1{{no matching function for call to 'Count'}} } + +template +void ExplicitOut(out Ts... values) { + // expected-note@-1{{for 2nd argument}} +} + +void TestExplicitOutRValue() { + int first; + ExplicitOut(first, 1.0); + // expected-error@-1{{no matching function for call to 'ExplicitOut'}} +} diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl index f90df92039..12a9636d16 100644 --- a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl @@ -125,8 +125,17 @@ struct Zipper { // Zero-argument (empty pack) instantiation. uint TestEmptyPack() { return CountArgs(); } +// CHECK: FunctionTemplateDecl {{.*}} ExplicitOut +// CHECK: FunctionDecl {{.*}} ExplicitOut 'void (Ts &__restrict...)' +// CHECK: ParmVarDecl {{.*}} values 'Ts &__restrict...' +// CHECK-NEXT: HLSLOutAttr +// CHECK: FunctionDecl {{.*}} ExplicitOut 'void (int &__restrict, float &__restrict)' +// CHECK: ParmVarDecl {{.*}} values 'int &__restrict' +// CHECK-NEXT: HLSLOutAttr +// CHECK: ParmVarDecl {{.*}} values 'float &__restrict' +// CHECK-NEXT: HLSLOutAttr template -void ExplicitArgs(Ts... values) {} +void ExplicitOut(out Ts... values) {} export float TestVariadic() { @@ -144,7 +153,8 @@ float TestVariadic() { uint empty = TestEmptyPack(); int explicitInt; float explicitFloat; - ExplicitArgs(explicitInt, explicitFloat); + ExplicitOut(explicitInt, explicitFloat); + ExplicitOut(explicitInt, explicitInt); uint folded = FoldSum(1, 2, 3, 4); return a + b + c + d + e + eEmpty + v.x + f + mw.M._11 + z + empty + folded + (float)g_FloatHolder.Buf.Load(0) + From ef1a0550bc803c69dce2fcdc150469ffcd9ce1c4 Mon Sep 17 00:00:00 2001 From: Chris B Date: Tue, 22 Sep 2026 08:29:43 -0500 Subject: [PATCH 09/10] Self-review --- tools/clang/lib/Frontend/InitPreprocessor.cpp | 1 + tools/clang/lib/Sema/SemaTemplateDeduction.cpp | 11 +++++++++-- tools/clang/lib/Sema/TreeTransform.h | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/tools/clang/lib/Frontend/InitPreprocessor.cpp b/tools/clang/lib/Frontend/InitPreprocessor.cpp index 81f2abb2cc..220821128a 100644 --- a/tools/clang/lib/Frontend/InitPreprocessor.cpp +++ b/tools/clang/lib/Frontend/InitPreprocessor.cpp @@ -497,6 +497,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, Builder.defineMacro("__cpp_ref_qualifiers", "200710"); Builder.defineMacro("__cpp_alias_templates", "200704"); } + // C++14 features. if (LangOpts.CPlusPlus14) { Builder.defineMacro("__cpp_binary_literals", "201304"); diff --git a/tools/clang/lib/Sema/SemaTemplateDeduction.cpp b/tools/clang/lib/Sema/SemaTemplateDeduction.cpp index 62fb28c195..2a30d8753b 100644 --- a/tools/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/tools/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2555,6 +2555,8 @@ Sema::SubstituteExplicitTemplateArguments( // Isolate our substituted parameters from our caller. LocalInstantiationScope InstScope(*this, /*MergeWithOuterScope*/true); + // HLSL Change Begin - HLSL needs the parameter decls to instantiate parameter + // modifiers correctly. SmallVector ParamDecls; // Instantiate the types of each of the function parameters given the @@ -2568,6 +2570,7 @@ Sema::SubstituteExplicitTemplateArguments( ParamTypes, &ParamDecls)) return TDK_SubstitutionFailure; } + // HLSL Change End // Instantiate the return type. QualType ResultType; @@ -2595,18 +2598,22 @@ Sema::SubstituteExplicitTemplateArguments( if (ResultType.isNull() || Trap.hasErrorOccurred()) return TDK_SubstitutionFailure; } - + // Instantiate the types of each of the function parameters given the // explicitly-specified template arguments if we didn't do so earlier. + // HLSL Change Begin - Pass ParamDecls to SubstParmTypes to correctly + // instantiate parameter modifiers. if (!Proto->hasTrailingReturn() && SubstParmTypes(Function->getLocation(), Function->param_begin(), Function->getNumParams(), MultiLevelTemplateArgumentList(*ExplicitArgumentList), ParamTypes, &ParamDecls)) return TDK_SubstitutionFailure; + // HLSL Change - End if (FunctionType) { - // HLSL Change - FIX - We should move param mods to parameter QualTypes + // HLSL Change Begin - Pass ParamDecls to SubstParmTypes to correctly + // instantiate parameter modifiers. SmallVector ParamMods; ParamMods.reserve(ParamDecls.size()); for (ParmVarDecl *Param : ParamDecls) diff --git a/tools/clang/lib/Sema/TreeTransform.h b/tools/clang/lib/Sema/TreeTransform.h index e2d7419de3..3070abf36f 100644 --- a/tools/clang/lib/Sema/TreeTransform.h +++ b/tools/clang/lib/Sema/TreeTransform.h @@ -10730,7 +10730,7 @@ TreeTransform::TransformBlockExpr(BlockExpr *E) { QualType exprResultType = getDerived().TransformType(exprFunctionType->getReturnType()); - // HLSL Change - FIX - We should move param mods to parameter QualTypes + // HLSL Change - Fix up the parameter modifiers for the block's parameters. SmallVector ExpandedParamMods; ArrayRef ParamMods = exprFunctionType->getParamMods(); @@ -10983,7 +10983,7 @@ TreeTransform::RebuildDependentSizedExtVectorType(QualType ElementType, return SemaRef.BuildExtVectorType(ElementType, SizeExpr, AttributeLoc); } -// HLSL Change - FIX - We should move param mods to parameter QualTypes +// HLSL Change - Fix up the parameter modifiers for the function prototype. template QualType TreeTransform::RebuildFunctionProtoType( QualType T, From 28131435d2de40858184d006b79ff482c3297b56 Mon Sep 17 00:00:00 2001 From: Chris B Date: Tue, 22 Sep 2026 08:30:13 -0500 Subject: [PATCH 10/10] clang-format --- tools/clang/lib/AST/Decl.cpp | 4 ++-- tools/clang/lib/Frontend/InitPreprocessor.cpp | 2 +- tools/clang/lib/Sema/SemaTemplateDeduction.cpp | 2 +- tools/clang/lib/Sema/TreeTransform.h | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/clang/lib/AST/Decl.cpp b/tools/clang/lib/AST/Decl.cpp index 3919809ff1..f99acecf75 100644 --- a/tools/clang/lib/AST/Decl.cpp +++ b/tools/clang/lib/AST/Decl.cpp @@ -2384,8 +2384,8 @@ void ParmVarDecl::updateOutParamToRefType(ASTContext &C) { ParamType.addRestrict(); if (Expansion) - ParamType = C.getPackExpansionType(ParamType, - Expansion->getNumExpansions()); + ParamType = + C.getPackExpansionType(ParamType, Expansion->getNumExpansions()); setType(ParamType); } // HLSL Change Ends diff --git a/tools/clang/lib/Frontend/InitPreprocessor.cpp b/tools/clang/lib/Frontend/InitPreprocessor.cpp index 220821128a..621f5da94b 100644 --- a/tools/clang/lib/Frontend/InitPreprocessor.cpp +++ b/tools/clang/lib/Frontend/InitPreprocessor.cpp @@ -497,7 +497,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, Builder.defineMacro("__cpp_ref_qualifiers", "200710"); Builder.defineMacro("__cpp_alias_templates", "200704"); } - + // C++14 features. if (LangOpts.CPlusPlus14) { Builder.defineMacro("__cpp_binary_literals", "201304"); diff --git a/tools/clang/lib/Sema/SemaTemplateDeduction.cpp b/tools/clang/lib/Sema/SemaTemplateDeduction.cpp index 2a30d8753b..819de72767 100644 --- a/tools/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/tools/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2571,7 +2571,7 @@ Sema::SubstituteExplicitTemplateArguments( return TDK_SubstitutionFailure; } // HLSL Change End - + // Instantiate the return type. QualType ResultType; { diff --git a/tools/clang/lib/Sema/TreeTransform.h b/tools/clang/lib/Sema/TreeTransform.h index 3070abf36f..0d1c4fb2e0 100644 --- a/tools/clang/lib/Sema/TreeTransform.h +++ b/tools/clang/lib/Sema/TreeTransform.h @@ -10742,7 +10742,8 @@ TreeTransform::TransformBlockExpr(BlockExpr *E) { ParamMods = ExpandedParamMods; } QualType functionType = getDerived().RebuildFunctionProtoType( - exprResultType, paramTypes, ParamMods, exprFunctionType->getExtProtoInfo()); + exprResultType, paramTypes, ParamMods, + exprFunctionType->getExtProtoInfo()); // HLSL Change - End blockScope->FunctionType = functionType;