From ab214d2744212e1dae2cf7f786585a7d9fdd14b7 Mon Sep 17 00:00:00 2001 From: Chris B Date: Tue, 19 May 2026 09:54:16 -0500 Subject: [PATCH 1/8] [202x] Support >> without spaces (#8453) This allows HLSL templates to have double closing angle brackets without a space between them matching C++11 template parsing. --- tools/clang/lib/Parse/ParseExpr.cpp | 22 +++++++---- tools/clang/lib/Parse/ParseTemplate.cpp | 4 +- .../template-right-angle-brackets.hlsl | 31 +++++++++++++++ .../template-right-angle-brackets.hlsl | 39 +++++++++++++++++++ 4 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 tools/clang/test/SemaHLSL/v2021/templates/template-right-angle-brackets.hlsl create mode 100644 tools/clang/test/SemaHLSL/v202x/templates/template-right-angle-brackets.hlsl diff --git a/tools/clang/lib/Parse/ParseExpr.cpp b/tools/clang/lib/Parse/ParseExpr.cpp index 8f51dd4b6c..9af3dbe610 100644 --- a/tools/clang/lib/Parse/ParseExpr.cpp +++ b/tools/clang/lib/Parse/ParseExpr.cpp @@ -254,9 +254,11 @@ static bool isFoldOperator(tok::TokenKind Kind) { /// precedence of at least \p MinPrec. ExprResult Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { - prec::Level NextTokPrec = getBinOpPrecedence(Tok.getKind(), - GreaterThanIsOperator, - getLangOpts().CPlusPlus11); + prec::Level NextTokPrec = + getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator, + getLangOpts().CPlusPlus11 || + (getLangOpts().HLSL && getLangOpts().HLSLVersion >= + hlsl::LangStd::v202x)); SourceLocation ColonLoc; while (1) { @@ -390,8 +392,11 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { // Remember the precedence of this operator and get the precedence of the // operator immediately to the right of the RHS. prec::Level ThisPrec = NextTokPrec; - NextTokPrec = getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator, - getLangOpts().CPlusPlus11); + NextTokPrec = getBinOpPrecedence( + Tok.getKind(), GreaterThanIsOperator, + getLangOpts().CPlusPlus11 || + (getLangOpts().HLSL && + getLangOpts().HLSLVersion >= hlsl::LangStd::v202x)); // Assignment and conditional expressions are right-associative. bool isRightAssoc = ThisPrec == prec::Conditional || @@ -424,8 +429,11 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { LHS = ExprError(); } - NextTokPrec = getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator, - getLangOpts().CPlusPlus11); + NextTokPrec = getBinOpPrecedence( + Tok.getKind(), GreaterThanIsOperator, + getLangOpts().CPlusPlus11 || + (getLangOpts().HLSL && + getLangOpts().HLSLVersion >= hlsl::LangStd::v202x)); } if (!RHS.isInvalid() && RHSIsInitList) { diff --git a/tools/clang/lib/Parse/ParseTemplate.cpp b/tools/clang/lib/Parse/ParseTemplate.cpp index dc334cf7d6..fbedf41caa 100644 --- a/tools/clang/lib/Parse/ParseTemplate.cpp +++ b/tools/clang/lib/Parse/ParseTemplate.cpp @@ -840,7 +840,9 @@ bool Parser::ParseGreaterThanInTemplateList(SourceLocation &RAngleLoc, Hint2 = FixItHint::CreateInsertion(Next.getLocation(), " "); unsigned DiagId = diag::err_two_right_angle_brackets_need_space; - if (getLangOpts().CPlusPlus11 && + if ((getLangOpts().CPlusPlus11 || + (getLangOpts().HLSL && + getLangOpts().HLSLVersion >= hlsl::LangStd::v202x)) && (Tok.is(tok::greatergreater) || Tok.is(tok::greatergreatergreater))) DiagId = diag::warn_cxx98_compat_two_right_angle_brackets; else if (Tok.is(tok::greaterequal)) diff --git a/tools/clang/test/SemaHLSL/v2021/templates/template-right-angle-brackets.hlsl b/tools/clang/test/SemaHLSL/v2021/templates/template-right-angle-brackets.hlsl new file mode 100644 index 0000000000..4db25fabc0 --- /dev/null +++ b/tools/clang/test/SemaHLSL/v2021/templates/template-right-angle-brackets.hlsl @@ -0,0 +1,31 @@ +// RUN: %dxc -T lib_6_3 -HV 2021 -verify %s + +// In HLSL 2021, two consecutive right angle brackets (>>) in a template +// argument list must be separated by a space. Using >> without spaces should +// produce an error. + +template +struct Outer { T val; }; + +template +struct Inner { T val; }; + +// Nested template type using >> without space: should be an error in HLSL 2021. +Outer> g_nested; // expected-error {{a space is required between consecutive right angle brackets (use '> >')}} + +template +struct Wrapper { + Outer> field; // expected-error {{a space is required between consecutive right angle brackets (use '> >')}} +}; + +// Function return type with nested template and >>. +Outer> getVal(); // expected-error {{a space is required between consecutive right angle brackets (use '> >')}} + +// Function parameter with nested template and >>. +void takeVal(Outer> v); // expected-error {{a space is required between consecutive right angle brackets (use '> >')}} + +// Three right angle brackets (>>>) in a template should also error. +template +struct Pair { T first; U second; }; + +Outer>> g_triple; // expected-error {{a space is required between consecutive right angle brackets (use '> >')}} diff --git a/tools/clang/test/SemaHLSL/v202x/templates/template-right-angle-brackets.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/template-right-angle-brackets.hlsl new file mode 100644 index 0000000000..10790e9433 --- /dev/null +++ b/tools/clang/test/SemaHLSL/v202x/templates/template-right-angle-brackets.hlsl @@ -0,0 +1,39 @@ +// 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 + +// In HLSL 202x, two consecutive right angle brackets (>>) are allowed in +// template argument lists without spaces, matching C++11 behavior. No +// diagnostic should be emitted for >> or >>> in this mode. + +// expected-no-diagnostics + +template +struct Outer { T val; }; + +template +struct Inner { T val; }; + +// Nested template type using >> without space: no error in HLSL 202x. +// CHECK: VarDecl {{.*}} g_nested 'Outer >' +Outer> g_nested; + +template +struct Wrapper { + // CHECK: FieldDecl {{.*}} field 'Outer >' + Outer> field; +}; + +// Function return type with nested template using >>. +// CHECK: FunctionDecl {{.*}} getVal 'Outer > ()' +Outer> getVal(); + +// Function parameter using >>. +// CHECK: FunctionDecl {{.*}} takeVal 'void (Outer >)' +void takeVal(Outer> v); + +// Three right angle brackets (>>>) in a template: no error in HLSL 202x. +template +struct Pair { T first; U second; }; + +// CHECK: VarDecl {{.*}} g_triple 'Outer > >' +Outer>> g_triple; From c4676d3c302dac6c9e9869f13db2f0892b9d3daf Mon Sep 17 00:00:00 2001 From: Ashley Coleman Date: Wed, 20 May 2026 11:37:31 -0600 Subject: [PATCH 2/8] [SM6.10] Update InterlockedAccumulate Param Order (#8459) Update the LinAlg header file in response to https://github.com/microsoft/hlsl-specs/pull/875 Swaps the InterlockedAccumulate parameters back to their original location --- tools/clang/lib/Headers/hlsl/dx/linalg.h | 2 +- tools/clang/test/CodeGenDXIL/hlsl/linalg/api/vectors.hlsl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/clang/lib/Headers/hlsl/dx/linalg.h b/tools/clang/lib/Headers/hlsl/dx/linalg.h index e223f2286e..3e7eee1aab 100644 --- a/tools/clang/lib/Headers/hlsl/dx/linalg.h +++ b/tools/clang/lib/Headers/hlsl/dx/linalg.h @@ -652,7 +652,7 @@ OuterProduct(vector VecA, vector VecB) { template typename hlsl::enable_if::value, void>::type -InterlockedAccumulate(RWByteAddressBuffer Res, vector Vec, +InterlockedAccumulate(vector Vec, RWByteAddressBuffer Res, uint StartOffset, uint Align = 64) { __builtin_LinAlg_VectorAccumulateToDescriptor(Vec, Res, StartOffset, Align); } diff --git a/tools/clang/test/CodeGenDXIL/hlsl/linalg/api/vectors.hlsl b/tools/clang/test/CodeGenDXIL/hlsl/linalg/api/vectors.hlsl index 3d1735aa9c..b449a29635 100644 --- a/tools/clang/test/CodeGenDXIL/hlsl/linalg/api/vectors.hlsl +++ b/tools/clang/test/CodeGenDXIL/hlsl/linalg/api/vectors.hlsl @@ -193,9 +193,9 @@ void main(uint ID : SV_GroupID) { // CHECK: call void @dx.op.linAlgVectorAccumulateToDescriptor.v4f16(i32 -2147483617, <4 x half> // CHECK-SAME: , %dx.types.Handle %{{[0-9]+}}, i32 0, i32 64) // CHECK-SAME: ; LinAlgVectorAccumulateToDescriptor(vector,handle,offset,align) - InterlockedAccumulate(RWBAB, vec1, 0); + InterlockedAccumulate(vec1, RWBAB, 0); // CHECK: call void @dx.op.linAlgVectorAccumulateToDescriptor.v8f16(i32 -2147483617, <8 x half> %{{[0-9]+}}, // CHECK-SAME: %dx.types.Handle %{{[0-9]+}}, i32 8, i32 64) ; LinAlgVectorAccumulateToDescriptor(vector,handle,offset,align) - InterlockedAccumulate(RWBAB, vec2, 8); + InterlockedAccumulate(vec2, RWBAB, 8); } From d9c0a26b704e7e5956305e58e6e9727c3fe1d101 Mon Sep 17 00:00:00 2001 From: Chris B Date: Wed, 20 May 2026 16:09:43 -0500 Subject: [PATCH 3/8] Implement `auto` for C++11-style type deduction (#8452) Partial implementation of C++11-stlye type deduction added to HLSL under the 202x language mode. This is still missing special handling for non-deducible special HLSL types. The main one that comes to mind is the type of the `ResourceDescriptorHeap` and `SamplerDescriptorHeap`. I think this PR can stand on its own because using it to infer those special types won't technically break anything it just might expose some bits and pieces of the implementation that we don't want. --- .../clang/Basic/DiagnosticParseKinds.td | 3 +- tools/clang/include/clang/Lex/Token.h | 2 +- tools/clang/lib/Parse/ParseDecl.cpp | 8 ++- tools/clang/lib/Sema/DeclSpec.cpp | 10 +++- tools/clang/lib/Sema/SemaDecl.cpp | 1 - .../test/CodeGenSPIRV/var.auto.deduction.hlsl | 51 +++++++++++++++++ .../test/CodeGenSPIRV/var.auto.template.hlsl | 53 ++++++++++++++++++ tools/clang/test/HLSL/cpp-errors.hlsl | 5 +- .../hlsl/auto/auto-no-pointer.hlsl | 10 ++++ .../hlsl/auto/auto-no-reference.hlsl | 11 ++++ .../hlsl/auto/auto-template.hlsl | 41 ++++++++++++++ .../hlsl/auto/auto-variable-deduction.hlsl | 55 +++++++++++++++++++ 12 files changed, 241 insertions(+), 9 deletions(-) create mode 100644 tools/clang/test/CodeGenSPIRV/var.auto.deduction.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/var.auto.template.hlsl create mode 100644 tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-no-pointer.hlsl create mode 100644 tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-no-reference.hlsl create mode 100644 tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-template.hlsl create mode 100644 tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-variable-deduction.hlsl diff --git a/tools/clang/include/clang/Basic/DiagnosticParseKinds.td b/tools/clang/include/clang/Basic/DiagnosticParseKinds.td index e328393726..ac4bad2cc2 100644 --- a/tools/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/tools/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -285,7 +285,8 @@ def warn_cxx98_compat_trailing_return_type : Warning< "trailing return types are incompatible with C++98">, InGroup, DefaultIgnore; def ext_auto_type_specifier : ExtWarn< - "'auto' type specifier is a C++11 extension">, InGroup; + "'auto' type specifier is a %select{C++11|HLSL 202x}0 extension">, + InGroup; def warn_auto_storage_class : Warning< "'auto' storage class specifier is redundant and incompatible with C++11">, InGroup, DefaultIgnore; diff --git a/tools/clang/include/clang/Lex/Token.h b/tools/clang/include/clang/Lex/Token.h index 7cb0118596..42e45bc035 100644 --- a/tools/clang/include/clang/Lex/Token.h +++ b/tools/clang/include/clang/Lex/Token.h @@ -295,7 +295,7 @@ class Token { // HLSL Change Starts bool isHLSLReserved() const { return is(tok::kw___is_signed) || is(tok::kw___declspec) || - is(tok::kw___forceinline) || is(tok::kw_auto) || is(tok::kw_catch) || + is(tok::kw___forceinline) || is(tok::kw_catch) || is(tok::kw_const_cast) || is(tok::kw_delete) || is(tok::kw_dynamic_cast) || is(tok::kw_enum) || is(tok::kw_explicit) || is(tok::kw_friend) || is(tok::kw_goto) || diff --git a/tools/clang/lib/Parse/ParseDecl.cpp b/tools/clang/lib/Parse/ParseDecl.cpp index c91c8940a4..85404a1673 100644 --- a/tools/clang/lib/Parse/ParseDecl.cpp +++ b/tools/clang/lib/Parse/ParseDecl.cpp @@ -3918,8 +3918,12 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, break; // HLSL Change Ends case tok::kw_auto: - if (getLangOpts().HLSL) { goto HLSLReservedKeyword; } // HLSL Change - auto is reserved for HLSL - if (getLangOpts().CPlusPlus11) { + // HLSL Change Begin - auto is reserved for HLSL 2015 and earlier. + if (getLangOpts().HLSL && + getLangOpts().HLSLVersion <= hlsl::LangStd::v2015) + goto HLSLReservedKeyword; + // HLSL Change End + if (getLangOpts().CPlusPlus11 || getLangOpts().HLSL) { // HLSL Change if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) { isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc, PrevSpec, DiagID, Policy); diff --git a/tools/clang/lib/Sema/DeclSpec.cpp b/tools/clang/lib/Sema/DeclSpec.cpp index 37c1554092..455f755242 100644 --- a/tools/clang/lib/Sema/DeclSpec.cpp +++ b/tools/clang/lib/Sema/DeclSpec.cpp @@ -1158,8 +1158,14 @@ void DeclSpec::Finish(DiagnosticsEngine &D, Preprocessor &PP, const PrintingPoli } // Diagnose if we've recovered from an ill-formed 'auto' storage class // specifier in a pre-C++11 dialect of C++. - if (!PP.getLangOpts().CPlusPlus11 && TypeSpecType == TST_auto) - Diag(D, TSTLoc, diag::ext_auto_type_specifier); + // HLSL Change Begin - HLSL supports 'auto' as a type specifier in 202x+. + if (!(PP.getLangOpts().CPlusPlus11 || + (PP.getLangOpts().HLSL && + PP.getLangOpts().HLSLVersion >= hlsl::LangStd::v202x)) && + TypeSpecType == TST_auto) + Diag(D, TSTLoc, diag::ext_auto_type_specifier) + << /* HLSL */ PP.getLangOpts().HLSL; + // HLSL Change End if (PP.getLangOpts().CPlusPlus && !PP.getLangOpts().CPlusPlus11 && StorageClassSpec == SCS_auto) Diag(D, StorageClassSpecLoc, diag::warn_auto_storage_class) diff --git a/tools/clang/lib/Sema/SemaDecl.cpp b/tools/clang/lib/Sema/SemaDecl.cpp index a772054960..b6fb971aac 100644 --- a/tools/clang/lib/Sema/SemaDecl.cpp +++ b/tools/clang/lib/Sema/SemaDecl.cpp @@ -9047,7 +9047,6 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, // If this is a redeclaration, check that the type we just deduced matches // the previously declared type. - assert(!getLangOpts().HLSL && "auto types are not supported - merge type below is inconsequential"); // HLSL Change if (VarDecl *Old = VDecl->getPreviousDecl()) { // We never need to merge the type, because we cannot form an incomplete // array of auto, nor deduce such a type. diff --git a/tools/clang/test/CodeGenSPIRV/var.auto.deduction.hlsl b/tools/clang/test/CodeGenSPIRV/var.auto.deduction.hlsl new file mode 100644 index 0000000000..553bb6642a --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/var.auto.deduction.hlsl @@ -0,0 +1,51 @@ +// RUN: %dxc -T cs_6_0 -E main -HV 202x -fcgl %s -spirv | FileCheck %s + +// Test that the 'auto' keyword can be used to declare variables with inferred +// types from initialization expressions when targeting SPIR-V. + +// CHECK: [[INT:%[a-zA-Z0-9_]+]] = OpTypeInt 32 1 +// CHECK: [[INT_1:%[a-zA-Z0-9_]+]] = OpConstant [[INT]] 1 +// CHECK: [[FLOAT:%[a-zA-Z0-9_]+]] = OpTypeFloat 32 +// CHECK: [[FLOAT_2:%[a-zA-Z0-9_]+]] = OpConstant [[FLOAT]] 2 +// CHECK: [[BOOL:%[a-zA-Z0-9_]+]] = OpTypeBool +// CHECK: [[TRUE:%[a-zA-Z0-9_]+]] = OpConstantTrue [[BOOL]] +// CHECK: [[V4FLOAT:%[a-zA-Z0-9_]+]] = OpTypeVector [[FLOAT]] 4 +// CHECK: [[VEC_CONST:%[a-zA-Z0-9_]+]] = OpConstantComposite [[V4FLOAT]] {{%[a-zA-Z0-9_]+}} [[FLOAT_2]] {{%[a-zA-Z0-9_]+}} {{%[a-zA-Z0-9_]+}} + +// CHECK: [[PTR_INT:%_ptr_Function_int]] = OpTypePointer Function [[INT]] +// CHECK: [[PTR_FLOAT:%_ptr_Function_float]] = OpTypePointer Function [[FLOAT]] +// CHECK: [[PTR_BOOL:%_ptr_Function_bool]] = OpTypePointer Function [[BOOL]] +// CHECK: [[PTR_V4FLOAT:%_ptr_Function_v4float]] = OpTypePointer Function [[V4FLOAT]] + +// CHECK: %a = OpVariable [[PTR_INT]] Function +// CHECK: %b = OpVariable [[PTR_FLOAT]] Function +// CHECK: %c = OpVariable [[PTR_BOOL]] Function +// CHECK: %d = OpVariable [[PTR_V4FLOAT]] Function +// CHECK: %sum = OpVariable [[PTR_INT]] Function +// CHECK: %product = OpVariable [[PTR_FLOAT]] Function + +// CHECK: OpStore %a [[INT_1]] +// CHECK: OpStore %b [[FLOAT_2]] +// CHECK: OpStore %c [[TRUE]] +// CHECK: OpStore %d [[VEC_CONST]] + +RWBuffer output : register(u0); + +[numthreads(1,1,1)] +void main() { + // Auto deduces int from integer literal + auto a = 1; + // Auto deduces float from float literal + auto b = 2.0f; + // Auto deduces bool from bool literal + auto c = true; + // Auto deduces float4 from vector type + auto d = float4(1.0f, 2.0f, 3.0f, 4.0f); + + // Auto from arithmetic expressions + auto sum = a + a; + auto product = b * b; + + // Use the values to prevent dead-code elimination + output[0] = (float)sum + product + d.x + (float)c; +} diff --git a/tools/clang/test/CodeGenSPIRV/var.auto.template.hlsl b/tools/clang/test/CodeGenSPIRV/var.auto.template.hlsl new file mode 100644 index 0000000000..7a2b5acf4d --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/var.auto.template.hlsl @@ -0,0 +1,53 @@ +// RUN: %dxc -T cs_6_0 -E main -HV 202x -fcgl %s -spirv | FileCheck %s + +// Test that the 'auto' keyword works correctly in template contexts when +// targeting SPIR-V: +// - auto variables inside template function bodies +// - auto variables assigned from results of template function instantiations + +RWBuffer output : register(u0); + +// Template with explicit return type; auto is used inside the body. +template +T square(T val) { + auto result = val * val; + return result; +} + +// Template with explicit return type using a conditional expression. +template +T clampPositive(T val) { + auto zero = (T)0; + auto clamped = val < zero ? zero : val; + return clamped; +} + +// CHECK: [[INT:%[a-zA-Z0-9_]+]] = OpTypeInt 32 1 +// CHECK: [[FLOAT:%[a-zA-Z0-9_]+]] = OpTypeFloat 32 +// CHECK: [[PTR_INT:%_ptr_Function_int]] = OpTypePointer Function [[INT]] +// CHECK: [[PTR_FLOAT:%_ptr_Function_float]] = OpTypePointer Function [[FLOAT]] + +// Auto-deduced locals in main() instantiated for int and float. +// CHECK: %a = OpVariable [[PTR_INT]] Function +// CHECK: %b = OpVariable [[PTR_FLOAT]] Function +// CHECK: %c = OpVariable [[PTR_INT]] Function +// CHECK: %d = OpVariable [[PTR_FLOAT]] Function + +// Auto inside template bodies retains correct types after instantiation. +// CHECK: %result = OpVariable [[PTR_INT]] Function +// CHECK: %result_0 = OpVariable [[PTR_FLOAT]] Function +// CHECK: %zero = OpVariable [[PTR_INT]] Function +// CHECK: %clamped = OpVariable [[PTR_INT]] Function +// CHECK: %zero_0 = OpVariable [[PTR_FLOAT]] Function +// CHECK: %clamped_0 = OpVariable [[PTR_FLOAT]] Function + +[numthreads(1,1,1)] +void main() { + // auto variables assigned from template instantiation results. + auto a = square(5); // instantiated as int + auto b = square(2.5f); // instantiated as float + auto c = clampPositive(-3); // instantiated as int + auto d = clampPositive(1.5f); // instantiated as float + + output[0] = (float)a + b + (float)c + d; +} diff --git a/tools/clang/test/HLSL/cpp-errors.hlsl b/tools/clang/test/HLSL/cpp-errors.hlsl index bee34d249b..1ecf4a57e1 100644 --- a/tools/clang/test/HLSL/cpp-errors.hlsl +++ b/tools/clang/test/HLSL/cpp-errors.hlsl @@ -12,7 +12,7 @@ s_arr_i_f arr_struct_two[] = { 1, 2, 3, 4 }; int g_int; typeof(g_int) g_typeof_int; // expected-error {{HLSL requires a type specifier for all declarations}} expected-error {{expected ';' after top level declarator}} expected-error {{unknown type name 'typeof'; did you mean 'typedef'?}} typedef int (*fn_int)(int); // expected-error {{pointers are unsupported in HLSL}} -auto g_auto = 3; // expected-error {{'auto' is a reserved keyword in HLSL}} expected-error {{HLSL requires a type specifier for all declarations}} +auto g_auto = 3; // auto is now supported in HLSL via type deduction; no error expected __is_signed g_is_signed; // expected-error {{'__is_signed' is a reserved keyword in HLSL}} expected-error {{HLSL requires a type specifier for all declarations}} register int g_register; // expected-error {{'register' is a reserved keyword in HLSL}} __thread int g_thread; // expected-error {{'__thread' is a reserved keyword in HLSL}} @@ -65,7 +65,8 @@ void fn_throw() throw() { } // expected-error {{exception specification is unsup void fn_noexcept() noexcept { }; // expected-error {{expected function body after function declarator}} // This would be a failure because of unsupported trailer return types, but we mis-parse it differently. -auto fn_trailing() -> int { return 1; } ; // expected-error {{'auto' is a reserved keyword in HLSL}} expected-error {{expected function body after function declarator}} +// auto is now type-deduced so no reserved keyword error; only the trailing return type syntax causes an error. +auto fn_trailing() -> int { return 1; } ; // expected-error {{expected function body after function declarator}} void fn_param_with_default(int val = 1) { } void fn_with_variadic(int a, ...) { } // expected-error {{variadic arguments is unsupported in HLSL}} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-no-pointer.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-no-pointer.hlsl new file mode 100644 index 0000000000..425a69cd8c --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-no-pointer.hlsl @@ -0,0 +1,10 @@ +// RUN: %dxc -T cs_6_0 -HV 202x -verify %s + +// Test that 'auto' cannot be used to declare pointer types in HLSL. +// Pointers are unsupported in HLSL. + +[numthreads(1,1,1)] +void main() { + int x = 1; + auto* ptr = &x; // expected-error {{pointers are unsupported in HLSL}} +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-no-reference.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-no-reference.hlsl new file mode 100644 index 0000000000..499c26aec7 --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-no-reference.hlsl @@ -0,0 +1,11 @@ +// RUN: %dxc -T cs_6_0 -HV 202x -verify %s + +// Test that 'auto' cannot be used to declare reference types in HLSL. +// References are unsupported in HLSL. + +int gVal = 42; + +[numthreads(1,1,1)] +void main() { + auto& ref = gVal; // expected-error {{references are unsupported in HLSL}} +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-template.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-template.hlsl new file mode 100644 index 0000000000..885d2226ae --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-template.hlsl @@ -0,0 +1,41 @@ +// RUN: %dxc -T cs_6_0 -HV 202x -fcgl %s | FileCheck %s + +// Test that the 'auto' keyword works correctly in template contexts in HLSL: +// - auto variables inside template function bodies +// - auto variables assigned from results of template function instantiations + + + +RWBuffer output : register(u0); + +// Template with explicit return type; auto is used inside the body. +template +T square(T val) { + auto result = val * val; + return result; +} + +// Template with explicit return type using a conditional expression. +template +T clampPositive(T val) { + auto zero = (T)0; + auto clamped = val < zero ? zero : val; + return clamped; +} + +// CHECK-LABEL: define void @main() +// CHECK: {{.*}} = alloca i32 +// CHECK: {{.*}} = alloca float +// CHECK: {{.*}} = alloca i32 +// CHECK: {{.*}} = alloca float + +[numthreads(1,1,1)] +void main() { + // auto variables assigned from template instantiation results. + auto a = square(5); // instantiated as int + auto b = square(2.5f); // instantiated as float + auto c = clampPositive(-3); // instantiated as int, result = 0 + auto d = clampPositive(1.5f); // instantiated as float + + output[0] = (float)a + b + (float)c + d; +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-variable-deduction.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-variable-deduction.hlsl new file mode 100644 index 0000000000..8cc375a02e --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-variable-deduction.hlsl @@ -0,0 +1,55 @@ +// RUN: %dxc -T cs_6_0 -E main - %s -verify +// RUN: %dxc -T cs_6_0 -E main -HV 202x -fcgl %s | FileCheck %s + +// Test that the 'auto' keyword can be used to declare variables with inferred +// types from initialization expressions in HLSL. + +// CHECK-LABEL: define void @main() +// CHECK: [[A:%[a-zA-Z0-9_]+]] = alloca i32 +// CHECK: [[B:%[a-zA-Z0-9_]+]] = alloca float +// CHECK: [[C:%[a-zA-Z0-9_]+]] = alloca i32 +// CHECK: [[D:%[a-zA-Z0-9_]+]] = alloca <4 x float> +// CHECK: {{.*}} = alloca i32 +// CHECK: {{.*}} = alloca float +// CHECK: {{.*}} = alloca <4 x float> +// CHECK: {{.*}} = alloca i32 +// CHECK: {{.*}} = alloca float +// CHECK: store i32 1, i32* [[A]] +// CHECK: store float 2.000000e+00, float* [[B]] +// CHECK: store i32 1, i32* [[C]] + +RWBuffer output : register(u0); + +[numthreads(1,1,1)] +void main() { + // Auto deduces int from integer literal + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto a = 1; + // Auto deduces float from float literal + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto b = 2.0f; + // Auto deduces bool (stored as i32) from bool literal + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto c = true; + // Auto deduces float4 from vector type + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto d = float4(1.0f, 2.0f, 3.0f, 4.0f); + + // Auto from arithmetic expressions + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto sum = a + a; + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto product = b * b; + + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto mulAdd = mad(d, d, d); + + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto m = min(sum, c); + + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto dP = dot(d, d); + + // Use the values to prevent dead-code elimination + output[0] = (float)sum + product + d.x + (float)c; +} From 8c0d03247b3587d8c377b77a86e50f69bb112802 Mon Sep 17 00:00:00 2001 From: Ashley Coleman Date: Wed, 20 May 2026 17:14:36 -0600 Subject: [PATCH 4/8] [SM6.10] Add Smoke Test For VectorAccumulate (#8458) Adds a smoke test for the VectorAccumulate operation added for the preview followup --------- Co-authored-by: github-actions[bot] --- .../clang/unittests/HLSLExec/LinAlgTests.cpp | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp index 7b8aeeeba7..b3c4b761ea 100644 --- a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp +++ b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp @@ -359,6 +359,9 @@ class DxilConf_SM610_LinAlg { // Convert TEST_METHOD(Convert); + // Vector Accumulate + TEST_METHOD(VectorAccumulateDescriptor_Thread_F16); + private: CComPtr D3DDevice; dxc::SpecificDllLoader DxcSupport; @@ -1693,7 +1696,7 @@ static const char ConvertShader[] = R"( static void runConvert(ID3D12Device *Device, dxc::SpecificDllLoader &DxcSupport, bool Verbose) { - std::string Args = "-HV 202x"; + std::string Args = "-HV 202x -enable-16bit-types"; MatrixDim NumElements = 4; size_t BufferSize = elementSize(ComponentType::F32) * NumElements; @@ -1718,4 +1721,44 @@ void DxilConf_SM610_LinAlg::Convert() { runConvert(D3DDevice, DxcSupport, VerboseLogging); } +static const char VectorAccumulateDescriptorShader[] = R"( + RWByteAddressBuffer Output : register(u0); + + [numthreads(1, 1, 1)] + void main() { + vector InVec = {1.0, 2.0, 3.0, 4.0}; + __builtin_LinAlg_VectorAccumulateToDescriptor(InVec, Output, 0, 64); + } +)"; + +static void runVectorAccumulateDescriptor(ID3D12Device *Device, + dxc::SpecificDllLoader &DxcSupport, + bool Verbose) { + std::string Args = "-HV 202x -enable-16bit-types"; + MatrixDim NumElements = 4; + size_t BufferSize = elementSize(ComponentType::F16) * NumElements; + + compileShader(DxcSupport, VectorAccumulateDescriptorShader, "cs_6_10", Args, + Verbose); + + auto Expected = makeExpectedVec(ComponentType::F16, NumElements, 1.0); + + auto Op = createComputeOp(VectorAccumulateDescriptorShader, "cs_6_10", + "UAV(u0)", Args.c_str()); + addUAVBuffer(Op.get(), "Output", BufferSize, true); + addRootView(Op.get(), 0, "Output"); + + auto Result = runShaderOp(Device, DxcSupport, std::move(Op)); + + MappedData OutData; + Result->Test->GetReadBackData("Output", &OutData); + + VERIFY_IS_TRUE(verifyComponentBuffer(ComponentType::F16, OutData.data(), + Expected, NumElements, Verbose)); +} + +void DxilConf_SM610_LinAlg::VectorAccumulateDescriptor_Thread_F16() { + runVectorAccumulateDescriptor(D3DDevice, DxcSupport, VerboseLogging); +} + } // namespace LinAlg From 14696d1cc10dd5fd802a28a751733747b11022ad Mon Sep 17 00:00:00 2001 From: Ashley Coleman Date: Thu, 21 May 2026 13:40:49 -0600 Subject: [PATCH 5/8] [SM6.10] Fix numeric types allowed in LinAlg intrins (#8465) Fixes https://github.com/microsoft/DirectXShaderCompiler/issues/8271 Use `LinAlg` as the intrinsic type instead of `numeric` for LinAlg intrinsics and update the `LinAlg` to match current allowed types --- tools/clang/lib/Sema/SemaHLSL.cpp | 12 ++++++----- .../matrixvectormultiplyadd/nominal.hlsl | 14 +++++++++++++ utils/hct/gen_intrin_main.txt | 20 +++++++++---------- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index 127733e71e..2067ebaf26 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -1170,12 +1170,14 @@ static const ArBasicKind g_RayQueryCT[] = {AR_OBJECT_RAY_QUERY, AR_BASIC_UNKNOWN}; static const ArBasicKind g_LinAlgCT[] = { + AR_BASIC_LITERAL_FLOAT, AR_BASIC_FLOAT16, AR_BASIC_FLOAT32, AR_BASIC_FLOAT32_PARTIAL_PRECISION, - AR_BASIC_FLOAT16, AR_BASIC_INT32, - AR_BASIC_INT16, AR_BASIC_UINT32, - AR_BASIC_UINT16, AR_BASIC_INT8_4PACKED, - AR_BASIC_UINT8_4PACKED, AR_BASIC_NOCAST, - AR_BASIC_UNKNOWN}; + AR_BASIC_FLOAT64, AR_BASIC_LITERAL_INT, + AR_BASIC_UINT16, AR_BASIC_UINT32, + AR_BASIC_UINT64, AR_BASIC_INT16, + AR_BASIC_INT32, AR_BASIC_INT64, + AR_BASIC_UINT8_4PACKED, AR_BASIC_INT8_4PACKED, + AR_BASIC_NOCAST, AR_BASIC_UNKNOWN}; static const ArBasicKind g_AccelerationStructCT[] = { AR_OBJECT_ACCELERATION_STRUCT, AR_BASIC_UNKNOWN}; diff --git a/tools/clang/test/CodeGenDXIL/hlsl/linalg/builtins/matrixvectormultiplyadd/nominal.hlsl b/tools/clang/test/CodeGenDXIL/hlsl/linalg/builtins/matrixvectormultiplyadd/nominal.hlsl index d4f0037460..edfa306148 100644 --- a/tools/clang/test/CodeGenDXIL/hlsl/linalg/builtins/matrixvectormultiplyadd/nominal.hlsl +++ b/tools/clang/test/CodeGenDXIL/hlsl/linalg/builtins/matrixvectormultiplyadd/nominal.hlsl @@ -49,4 +49,18 @@ void main() { // CHECK2-SAME: i1 true, <4 x i64> %{{[0-9]+}}, i32 1, <4 x i64> %{{[0-9]+}}, i32 0) __builtin_LinAlg_MatrixVectorMultiplyAdd(result3, mat3, true, vec3, 1, result3, 0); + + // CHECK: call <8 x i32> @dx.op.linAlgMatVecMulAdd.v8i32.mC17M8N8U0S0.v8i32.v8i32(i32 -2147483622, + // CHECK-SAME: %dx.types.LinAlgMatrixC17M8N8U0S0 {{.*}}, i1 true, <8 x i32> zeroinitializer, + // CHECK-SAME: i32 1, <8 x i32> zeroinitializer, i32 0) + // CHECK-SAME: ; LinAlgMatVecMulAdd(matrix,isOutputSigned,inputVector,inputInterpretation,biasVector,biasInterpretation) + + // CHECK2: call void @"dx.hl.op..void (i32, <8 x i32>*, %dx.types.LinAlgMatrixC17M8N8U0S0, i1, <8 x i32>, + // CHECK2-SAME: i32, <8 x i32>, i32)"(i32 419, <8 x i32>* %result4, %dx.types.LinAlgMatrixC17M8N8U0S0 %{{[0-9]+}}, + // CHECK2-SAME: i1 true, <8 x i32> %{{[0-9]+}}, i32 1, <8 x i32> %{{[0-9]+}}, i32 0) + + __builtin_LinAlgMatrix [[__LinAlgMatrix_Attributes(17, 8, 8, 0, 0)]] mat4; + vector vec4 = 0; + vector result4 = 0; + __builtin_LinAlg_MatrixVectorMultiplyAdd(result4, mat4, true, vec4, 1, result4, 0); } diff --git a/utils/hct/gen_intrin_main.txt b/utils/hct/gen_intrin_main.txt index c0d1d3dcfc..37a4ee406f 100644 --- a/utils/hct/gen_intrin_main.txt +++ b/utils/hct/gen_intrin_main.txt @@ -393,24 +393,24 @@ void [[min_sm=6.10]] __builtin_LinAlg_FillMatrix(out LinAlgMatrix ret, in numeri void [[min_sm=6.10]] __builtin_LinAlg_CopyConvertMatrix(out LinAlgMatrix ret, in LinAlgMatrix source, in bool transpose); void [[min_sm=6.10]] __builtin_LinAlg_MatrixLoadFromDescriptor(out LinAlgMatrix ret, in ByteAddressBuffer buf, in uint offset, in uint stride, in uint layout, in uint align); void [[min_sm=6.10]] __builtin_LinAlg_MatrixLoadFromDescriptor(out LinAlgMatrix ret, in RWByteAddressBuffer buf, in uint offset, in uint stride, in uint layout, in uint align); -void [[min_sm=6.10]] __builtin_LinAlg_MatrixLoadFromMemory(out LinAlgMatrix ret, groupshared numeric[] memory, in uint offset, in uint stride, in uint layout); +void [[min_sm=6.10]] __builtin_LinAlg_MatrixLoadFromMemory(out LinAlgMatrix ret, groupshared LinAlg[] memory, in uint offset, in uint stride, in uint layout); uint [[min_sm=6.10]] __builtin_LinAlg_MatrixLength(in LinAlgMatrix matrix); uint<2> [[min_sm=6.10]] __builtin_LinAlg_MatrixGetCoordinate(in LinAlgMatrix matrix, in uint threadLocalIndex); -void [[min_sm=6.10]] __builtin_LinAlg_MatrixGetElement(out numeric ret, in LinAlgMatrix matrix, in uint threadLocalIndex); -void [[min_sm=6.10]] __builtin_LinAlg_MatrixSetElement(ref LinAlgMatrix ret, in LinAlgMatrix matrix, in uint threadLocalIndex, in numeric value); +void [[min_sm=6.10]] __builtin_LinAlg_MatrixGetElement(out LinAlg ret, in LinAlgMatrix matrix, in uint threadLocalIndex); +void [[min_sm=6.10]] __builtin_LinAlg_MatrixSetElement(ref LinAlgMatrix ret, in LinAlgMatrix matrix, in uint threadLocalIndex, in LinAlg value); void [[min_sm=6.10]] __builtin_LinAlg_MatrixStoreToDescriptor(in LinAlgMatrix matrix, in RWByteAddressBuffer buf, in uint offset, in uint stride, in uint layout, in uint align); -void [[min_sm=6.10]] __builtin_LinAlg_MatrixStoreToMemory(in LinAlgMatrix matrix, groupshared numeric[] memory, in uint offset, in uint stride, in uint layout); +void [[min_sm=6.10]] __builtin_LinAlg_MatrixStoreToMemory(in LinAlgMatrix matrix, groupshared LinAlg[] memory, in uint offset, in uint stride, in uint layout); uint [[min_sm=6.10]] __builtin_LinAlg_MatrixQueryAccumulatorLayout(); void [[min_sm=6.10]] __builtin_LinAlg_MatrixMatrixMultiply(out LinAlgMatrix matrixC, in LinAlgMatrix matrixA, in LinAlgMatrix matrixB); void [[min_sm=6.10]] __builtin_LinAlg_MatrixMatrixMultiplyAccumulate(ref LinAlgMatrix matrixR, in LinAlgMatrix matrixA, in LinAlgMatrix matrixB, in LinAlgMatrix matrixC); void [[min_sm=6.10]] __builtin_LinAlg_MatrixAccumulate(ref LinAlgMatrix matrixC, in LinAlgMatrix matrixLHS, in LinAlgMatrix matrixRHS); -void [[min_sm=6.10]] __builtin_LinAlg_MatrixVectorMultiply(out numeric ret, in LinAlgMatrix mat, in bool isOutputSigned, in numeric input, in uint inputInterp); -void [[min_sm=6.10]] __builtin_LinAlg_MatrixVectorMultiplyAdd(out numeric ret, in LinAlgMatrix mat, in bool isOutputSigned, in numeric input, in uint inputInterp, in numeric bias, in uint biasInterp); +void [[min_sm=6.10]] __builtin_LinAlg_MatrixVectorMultiply(out LinAlg ret, in LinAlgMatrix mat, in bool isOutputSigned, in LinAlg input, in uint inputInterp); +void [[min_sm=6.10]] __builtin_LinAlg_MatrixVectorMultiplyAdd(out LinAlg ret, in LinAlgMatrix mat, in bool isOutputSigned, in LinAlg input, in uint inputInterp, in LinAlg bias, in uint biasInterp); void [[min_sm=6.10]] __builtin_LinAlg_MatrixAccumulateToDescriptor(in LinAlgMatrix matrix, in RWByteAddressBuffer buf, in uint offset, in uint stride, in uint layout, in uint align); -void [[min_sm=6.10]] __builtin_LinAlg_MatrixAccumulateToMemory(in LinAlgMatrix matrix, groupshared numeric[] memory, in uint offset, in uint stride, in uint layout); -void [[min_sm=6.10]] __builtin_LinAlg_MatrixOuterProduct(out LinAlgMatrix ret, in numeric vecA, in numeric vecB); -void [[min_sm=6.10]] __builtin_LinAlg_Convert(out numeric ret, in numeric vec, in uint input_interp, in uint output_interp); -void [[min_sm=6.10]] __builtin_LinAlg_VectorAccumulateToDescriptor(in numeric<> vec, in RWByteAddressBuffer buf, in uint offset, in uint align); +void [[min_sm=6.10]] __builtin_LinAlg_MatrixAccumulateToMemory(in LinAlgMatrix matrix, groupshared LinAlg[] memory, in uint offset, in uint stride, in uint layout); +void [[min_sm=6.10]] __builtin_LinAlg_MatrixOuterProduct(out LinAlgMatrix ret, in LinAlg vecA, in LinAlg vecB); +void [[min_sm=6.10]] __builtin_LinAlg_Convert(out LinAlg ret, in LinAlg vec, in uint input_interp, in uint output_interp); +void [[min_sm=6.10]] __builtin_LinAlg_VectorAccumulateToDescriptor(in LinAlg<> vec, in RWByteAddressBuffer buf, in uint offset, in uint align); } namespace From 8634ef1a026b037fd74c9a82868979be8abe710d Mon Sep 17 00:00:00 2001 From: Chris B Date: Thu, 21 May 2026 16:11:08 -0500 Subject: [PATCH 6/8] Add [[nodiscard]] attribute (#8462) This PR adds support for the `[[nodiscard]]` attribute so that diagnostics can be easily issued for cases where return types of functions should not be discarded. This came up in feedback about the `Matrix::Fill` function in the linear algebra specification. --- tools/clang/include/clang/AST/Decl.h | 4 ++++ tools/clang/include/clang/Basic/Attr.td | 4 +++- .../include/clang/Basic/DiagnosticSemaKinds.td | 8 +++++--- tools/clang/lib/AST/Decl.cpp | 14 ++++++++++++++ tools/clang/lib/Headers/hlsl/dx/linalg.h | 5 +++-- tools/clang/lib/Sema/SemaStmt.cpp | 11 +++++++++-- .../test/SemaHLSL/attributes/nodiscard.hlsl | 18 ++++++++++++++++++ 7 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 tools/clang/test/SemaHLSL/attributes/nodiscard.hlsl diff --git a/tools/clang/include/clang/AST/Decl.h b/tools/clang/include/clang/AST/Decl.h index 0d7ffe3808..4e9722533c 100644 --- a/tools/clang/include/clang/AST/Decl.h +++ b/tools/clang/include/clang/AST/Decl.h @@ -2079,6 +2079,10 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext, /// operators. bool hasUnusedResultAttr() const; + // HLSL Change Begin - Add support for nodiscard attribute. + Attr *getNoDiscardAttr() const; + // HLSL Change Ends + /// \brief Returns the storage class as written in the source. For the /// computed linkage of symbol, see getLinkage. StorageClass getStorageClass() const { return StorageClass(SClass); } diff --git a/tools/clang/include/clang/Basic/Attr.td b/tools/clang/include/clang/Basic/Attr.td index f220157d52..c3199013ee 100644 --- a/tools/clang/include/clang/Basic/Attr.td +++ b/tools/clang/include/clang/Basic/Attr.td @@ -2470,13 +2470,15 @@ def WarnUnused : InheritableAttr { let Documentation = [Undocumented]; } +// HLSL Change Begin - add C++11 spelling. def WarnUnusedResult : InheritableAttr { - let Spellings = [GCC<"warn_unused_result">, + let Spellings = [CXX11<"", "nodiscard", 2017>, GCC<"warn_unused_result">, CXX11<"clang", "warn_unused_result">]; let Subjects = SubjectList<[ObjCMethod, CXXRecord, FunctionLike], WarnDiag, "ExpectedFunctionMethodOrClass">; let Documentation = [Undocumented]; } +// HLSL Change End - add C++11 spelling. def Weak : InheritableAttr { let Spellings = [GCC<"weak">]; diff --git a/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td b/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td index c35d14f7b2..1449bae57d 100644 --- a/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6368,9 +6368,11 @@ def warn_side_effects_unevaluated_context : Warning< def warn_side_effects_typeid : Warning< "expression with side effects will be evaluated despite being used as an " "operand to 'typeid'">, InGroup; -def warn_unused_result : Warning< - "ignoring return value of function declared with warn_unused_result " - "attribute">, InGroup>; +// HLSL Change Begin - allow attribute spelling to come in. +def warn_unused_result + : Warning<"ignoring return value of function declared with %0 attribute">, + InGroup>; +// HLSL Change End def warn_unused_volatile : Warning< "expression result unused; assign into a variable to force a volatile load">, InGroup>; diff --git a/tools/clang/lib/AST/Decl.cpp b/tools/clang/lib/AST/Decl.cpp index 39b3ac768d..6b584685e1 100644 --- a/tools/clang/lib/AST/Decl.cpp +++ b/tools/clang/lib/AST/Decl.cpp @@ -2884,6 +2884,20 @@ bool FunctionDecl::hasUnusedResultAttr() const { return hasAttr(); } +// HLSL Change Begin - support nodiscard attr. +Attr *FunctionDecl::getNoDiscardAttr() const { + QualType RetType = getReturnType(); + if (RetType->isRecordType()) { + const CXXRecordDecl *Ret = RetType->getAsCXXRecordDecl(); + const CXXMethodDecl *MD = dyn_cast(this); + if (Ret && Ret->hasAttr() && + !(MD && MD->getCorrespondingMethodInClass(Ret, true))) + return Ret->getAttr(); + } + return getAttr(); +} +// HLSL Change End + /// \brief For an inline function definition in C, or for a gnu_inline function /// in C++, determine whether the definition will be externally visible. /// diff --git a/tools/clang/lib/Headers/hlsl/dx/linalg.h b/tools/clang/lib/Headers/hlsl/dx/linalg.h index 3e7eee1aab..44c1e96c37 100644 --- a/tools/clang/lib/Headers/hlsl/dx/linalg.h +++ b/tools/clang/lib/Headers/hlsl/dx/linalg.h @@ -289,8 +289,9 @@ class Matrix { } template - static typename hlsl::enable_if::value, Matrix>::type - Splat(T Val) { + [[nodiscard]] static + typename hlsl::enable_if::value, Matrix>::type + Splat(T Val) { Matrix Result; __builtin_LinAlg_FillMatrix(Result.__handle, Val); return Result; diff --git a/tools/clang/lib/Sema/SemaStmt.cpp b/tools/clang/lib/Sema/SemaStmt.cpp index 4e47a68888..baa9559dbb 100644 --- a/tools/clang/lib/Sema/SemaStmt.cpp +++ b/tools/clang/lib/Sema/SemaStmt.cpp @@ -245,7 +245,11 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) { const FunctionDecl *Func = dyn_cast(FD); if (Func ? Func->hasUnusedResultAttr() : FD->hasAttr()) { - Diag(Loc, diag::warn_unused_result) << R1 << R2; + // HLSL Change Begin - allow attribute spelling to come in. + Attr *NoDiscardAttr = Func ? Func->getNoDiscardAttr() + : FD->getAttr(); + Diag(Loc, diag::warn_unused_result) << NoDiscardAttr << R1 << R2; + // HLSL Change End return; } if (ShouldSuppress) @@ -270,7 +274,10 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) { const ObjCMethodDecl *MD = ME->getMethodDecl(); if (MD) { if (MD->hasAttr()) { - Diag(Loc, diag::warn_unused_result) << R1 << R2; + // HLSL Change Begin - allow attribute spelling to come in. + Diag(Loc, diag::warn_unused_result) + << MD->getAttr() << R1 << R2; + // HLSL Change End return; } } diff --git a/tools/clang/test/SemaHLSL/attributes/nodiscard.hlsl b/tools/clang/test/SemaHLSL/attributes/nodiscard.hlsl new file mode 100644 index 0000000000..44b8d9edf4 --- /dev/null +++ b/tools/clang/test/SemaHLSL/attributes/nodiscard.hlsl @@ -0,0 +1,18 @@ +// RUN: %dxc -I %hlsl_headers -T cs_6_10 -verify %s + +#include +using namespace dx::linalg; + +using MatrixATy = Matrix; + +[nodiscard] int fn() { return 42; } +[[nodiscard]] int fn2() { return 42; } + +[numthreads(4, 4, 4)] +void main(uint ID : SV_GroupID) +{ + MatrixATy MatA1; + MatA1.Splat(1.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + fn(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + fn2(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +} From ac0a538752cb38f86077ff237d30fdef12292b4e Mon Sep 17 00:00:00 2001 From: Chris B Date: Thu, 21 May 2026 18:56:55 -0500 Subject: [PATCH 7/8] Update nodiscard test (#8471) This adds coverage of two uses missed in the original test. The first covers structs with nodiscard on the type declaration, the second covers selecting the method spelling of the attribute over the type attribute if both are present. --- tools/clang/lib/AST/Decl.cpp | 10 ++++++---- .../test/SemaHLSL/attributes/nodiscard.hlsl | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/tools/clang/lib/AST/Decl.cpp b/tools/clang/lib/AST/Decl.cpp index 6b584685e1..4c8c0ae268 100644 --- a/tools/clang/lib/AST/Decl.cpp +++ b/tools/clang/lib/AST/Decl.cpp @@ -2876,10 +2876,11 @@ bool FunctionDecl::hasUnusedResultAttr() const { QualType RetType = getReturnType(); if (RetType->isRecordType()) { const CXXRecordDecl *Ret = RetType->getAsCXXRecordDecl(); - const CXXMethodDecl *MD = dyn_cast(this); - if (Ret && Ret->hasAttr() && - !(MD && MD->getCorrespondingMethodInClass(Ret, true))) + // HLSL Change Begin - nodiscard on a type should apply to all methods, even + // if they are not marked nodiscard themselves. This is the C++17 behavior. + if (Ret && Ret->hasAttr()) return true; + // HLSL Change End } return hasAttr(); } @@ -2891,7 +2892,8 @@ Attr *FunctionDecl::getNoDiscardAttr() const { const CXXRecordDecl *Ret = RetType->getAsCXXRecordDecl(); const CXXMethodDecl *MD = dyn_cast(this); if (Ret && Ret->hasAttr() && - !(MD && MD->getCorrespondingMethodInClass(Ret, true))) + (!hasAttr() || + !(MD && (MD->getCorrespondingMethodInClass(Ret, true))))) return Ret->getAttr(); } return getAttr(); diff --git a/tools/clang/test/SemaHLSL/attributes/nodiscard.hlsl b/tools/clang/test/SemaHLSL/attributes/nodiscard.hlsl index 44b8d9edf4..c925b5ba42 100644 --- a/tools/clang/test/SemaHLSL/attributes/nodiscard.hlsl +++ b/tools/clang/test/SemaHLSL/attributes/nodiscard.hlsl @@ -8,6 +8,18 @@ using MatrixATy = Matrix Date: Thu, 21 May 2026 17:59:51 -0600 Subject: [PATCH 8/8] Update Release Notes --- docs/ReleaseNotes.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 23b0cb51ba..d86d61f386 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -26,6 +26,12 @@ The included licenses apply to the following files: - Fixed validator rejecting LinAlgMatrix type in DXIL [#8441](https://github.com/microsoft/DirectXShaderCompiler/pull/8441) - Fixed float trunc and coord calc errors in ExecTests [#8420](https://github.com/microsoft/DirectXShaderCompiler/pull/8420) - Convert Bias Vector to match the output type [#8394](https://github.com/microsoft/DirectXShaderCompiler/pull/8394) +- Fixed certain types being disallowed in LinAlg intrinsics [#8465](https://github.com/microsoft/DirectXShaderCompiler/pull/8465) + +#### Misc Features +- Support templates with >> instead of > > in 202x [#8453](https://github.com/microsoft/DirectXShaderCompiler/pull/8453) +- Implement `auto` for type deduction [#8452](https://github.com/microsoft/DirectXShaderCompiler/pull/8452) +- Implement `[[nodiscard]]` attribute to warn on unused returns [#8462](https://github.com/microsoft/DirectXShaderCompiler/pull/8462) #### Bug Fixes - Fixed build break on certain platforms [#8307](https://github.com/microsoft/DirectXShaderCompiler/pull/8307)