From 4554330ae5589cb87e1f0adb4f112167fc8fabd6 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Fri, 8 May 2026 12:34:23 -0700 Subject: [PATCH 1/2] Move pointer/reference rejection to Sema This is a refactoring to move the rejection of pointer and reference types into Sema rather than rejecting it during parsing. This has a few consequences and benefits. The consequence as seen in the changes to the cpp-errors tests are that we don't see pointer use errors in cases where a parser error prevents sema code from executing (as seen in operator cases). The benefit is that this also intercepts pointer and reference types that are deduced (via templates, auto or decltype). --- .../clang/Basic/DiagnosticParseKinds.td | 2 - .../clang/Basic/DiagnosticSemaKinds.td | 3 + tools/clang/lib/Parse/ParseDecl.cpp | 15 ----- tools/clang/lib/Sema/SemaHLSL.cpp | 18 +++++ tools/clang/lib/Sema/SemaTemplate.cpp | 8 ++- tools/clang/lib/Sema/SemaType.cpp | 21 ++++++ tools/clang/lib/Sema/TreeTransform.h | 7 +- tools/clang/test/HLSL/cpp-errors-hv2015.hlsl | 8 +-- tools/clang/test/HLSL/cpp-errors.hlsl | 8 +-- .../decltype-ptr-ref-instance-vars.hlsl | 67 +++++++++++++++++++ tools/clang/test/SemaHLSL/more-operators.hlsl | 64 +++++++++--------- .../overloading-new-delete-errors.hlsl | 8 +-- .../template-implicit-this-sfinae.hlsl | 65 ++++++++++++++++++ 13 files changed, 231 insertions(+), 63 deletions(-) create mode 100644 tools/clang/test/SemaHLSL/decltype-ptr-ref-instance-vars.hlsl create mode 100644 tools/clang/test/SemaHLSL/template-implicit-this-sfinae.hlsl diff --git a/tools/clang/include/clang/Basic/DiagnosticParseKinds.td b/tools/clang/include/clang/Basic/DiagnosticParseKinds.td index 0fe0d22bbf..e328393726 100644 --- a/tools/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/tools/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -994,8 +994,6 @@ def err_hlsl_unsupported_member_default : Error< "struct/class members cannot have default values">; def err_hlsl_unsupported_packoffset_component : Error< "packoffset component should indicate offset with one of x, y, z, w, r, g, b, or a">; -def err_hlsl_unsupported_pointer : Error< - "pointers are unsupported in HLSL">; def err_hlsl_unsupported_register_number : Error< "register number should be an integral numeric string">; def err_hlsl_unsupported_register_noninteger : Error< diff --git a/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td b/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td index eaffd4ee79..c35d14f7b2 100644 --- a/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8061,6 +8061,9 @@ def err_hlsl_linalg_attributed_matrix_required def err_hlsl_linalg_unsupported_stage : Error< "builtin unavailable in shader stage '%0' (requires 'compute', 'mesh' or 'amplification')">; +def err_hlsl_pointers_unsupported : Error< + "%select{pointers|references}0 are unsupported in HLSL">; + // HLSL Change Ends // SPIRV Change Starts diff --git a/tools/clang/lib/Parse/ParseDecl.cpp b/tools/clang/lib/Parse/ParseDecl.cpp index 6cfd498226..c91c8940a4 100644 --- a/tools/clang/lib/Parse/ParseDecl.cpp +++ b/tools/clang/lib/Parse/ParseDecl.cpp @@ -5887,15 +5887,6 @@ void Parser::ParseDeclaratorInternal(Declarator &D, return; } - // HLSL Change Starts - No pointer support in HLSL. - if (getLangOpts().HLSL) { - Diag(Tok, diag::err_hlsl_unsupported_pointer); - D.SetIdentifier(0, Tok.getLocation()); - D.setInvalidType(); - return; - } - // HLSL Change Ends - SourceLocation Loc = ConsumeToken(); D.SetRangeEnd(Loc); DeclSpec DS(AttrFactory); @@ -5917,12 +5908,6 @@ void Parser::ParseDeclaratorInternal(Declarator &D, tok::TokenKind Kind = Tok.getKind(); - // HLSL Change Starts - HLSL doesn't support pointers, references or blocks - if (getLangOpts().HLSL && isPtrOperatorToken(Kind, getLangOpts(), D.getContext())) { - Diag(Tok, diag::err_hlsl_unsupported_pointer); - } - // HLSL Change Ends - // Not a pointer, C++ reference, or block. if (!isPtrOperatorToken(Kind, getLangOpts(), D.getContext())) { if (DirectDeclParser) diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index 7df68f5a11..127733e71e 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -15474,6 +15474,24 @@ bool Sema::DiagnoseHLSLDecl(Declarator &D, DeclContext *DC, Expr *BitWidth, if (!isFunction) hlslSource->WarnMinPrecision(qt, D.getLocStart()); + // HLSL Change Starts - disallow pointers through __decltype. + if (!D.isInvalidType() && pType && !qt->isDependentType()) { + if (const auto *DTT = dyn_cast(pType)) { + QualType Underlying = DTT->getUnderlyingType(); + if (Underlying->isPointerType()) { + Diag(D.getLocStart(), diag::err_hlsl_pointers_unsupported) << 0; + D.setInvalidType(); + return false; + } + if (Underlying->isReferenceType()) { + Diag(D.getLocStart(), diag::err_hlsl_pointers_unsupported) << 1; + D.setInvalidType(); + return false; + } + } + } + // HLSL Change Ends + // Early checks - these are not simple attribution errors, but constructs that // are fundamentally unsupported, // and so we avoid errors that might indicate they can be repaired. diff --git a/tools/clang/lib/Sema/SemaTemplate.cpp b/tools/clang/lib/Sema/SemaTemplate.cpp index 37b296aefd..b5945017da 100644 --- a/tools/clang/lib/Sema/SemaTemplate.cpp +++ b/tools/clang/lib/Sema/SemaTemplate.cpp @@ -421,8 +421,14 @@ Sema::ActOnDependentIdExpression(const CXXScopeSpec &SS, NamedDecl *FirstQualifierInScope = nullptr; // HLSL Change begin - This is a reference. + // In HLSL, 'this' is an lvalue reference (not a pointer), so implicit + // member accesses use '.' (IsArrow=false). The base type must be the + // class type T, not the pointer type T*. + QualType MemberBaseType = + getLangOpts().HLSL ? ThisType->getPointeeType() : ThisType; return CXXDependentScopeMemberExpr::Create( - Context, /*This*/ nullptr, ThisType, /*IsArrow*/ !getLangOpts().HLSL, + Context, /*This*/ nullptr, MemberBaseType, + /*IsArrow*/ !getLangOpts().HLSL, /*Op*/ SourceLocation(), SS.getWithLocInContext(Context), TemplateKWLoc, FirstQualifierInScope, NameInfo, TemplateArgs); // HLSL Change end - This is a reference. diff --git a/tools/clang/lib/Sema/SemaType.cpp b/tools/clang/lib/Sema/SemaType.cpp index 52c55df6ae..7465cc2cec 100644 --- a/tools/clang/lib/Sema/SemaType.cpp +++ b/tools/clang/lib/Sema/SemaType.cpp @@ -1849,6 +1849,13 @@ QualType Sema::BuildPointerType(QualType T, return QualType(); } + // HLSL Change Begin - Disallow pointers. + if (getLangOpts().HLSL && Loc.isValid()) { + Diag(Loc, diag::err_hlsl_pointers_unsupported) << 0; + return QualType(); + } + // HLSL Change End. + if (checkQualifiedFunction(*this, T, Loc, QFK_Pointer)) return QualType(); @@ -1911,6 +1918,13 @@ QualType Sema::BuildReferenceType(QualType T, bool SpelledAsLValue, return QualType(); } + // HLSL Change Begin - Disallow references. + if (getLangOpts().HLSL && Loc.isValid()) { + Diag(Loc, diag::err_hlsl_pointers_unsupported) << 1; + return QualType(); + } + // HLSL Change End. + if (checkQualifiedFunction(*this, T, Loc, QFK_Reference)) return QualType(); @@ -2313,6 +2327,13 @@ QualType Sema::BuildMemberPointerType(QualType T, QualType Class, return QualType(); } + // HLSL Change Begin - Disallow pointers. + if (getLangOpts().HLSL && Loc.isValid()) { + Diag(Loc, diag::err_hlsl_pointers_unsupported) << 0; + return QualType(); + } + // HLSL Change End. + // Adjust the default free function calling convention to the default method // calling convention. if (T->isFunctionType()) diff --git a/tools/clang/lib/Sema/TreeTransform.h b/tools/clang/lib/Sema/TreeTransform.h index 2d2e692cd4..ef3a83c988 100644 --- a/tools/clang/lib/Sema/TreeTransform.h +++ b/tools/clang/lib/Sema/TreeTransform.h @@ -9852,7 +9852,12 @@ TreeTransform::TransformCXXDependentScopeMemberExpr( } else { OldBase = nullptr; BaseType = getDerived().TransformType(E->getBaseType()); - ObjectType = BaseType->getPointeeType(); + // HLSL Change - Begin + // In HLSL, 'this' is an lvalue reference so implicit member accesses use + // '.' (IsArrow=false) and the stored base type IS the object type, not a + // pointer to it. + ObjectType = E->isArrow() ? BaseType->getPointeeType() : BaseType; + // HLSL Change - End } // Transform the first part of the nested-name-specifier that qualifies diff --git a/tools/clang/test/HLSL/cpp-errors-hv2015.hlsl b/tools/clang/test/HLSL/cpp-errors-hv2015.hlsl index e792a702c2..57c512741c 100644 --- a/tools/clang/test/HLSL/cpp-errors-hv2015.hlsl +++ b/tools/clang/test/HLSL/cpp-errors-hv2015.hlsl @@ -94,10 +94,10 @@ typename typedef float4 TFloat4; // expected-error {{'typename' is a reserved ke class C { int fn_eq_default() = default; // expected-error {{function deletion and defaulting is unsupported in HLSL}} - // Errors are a bit misleading here, but ultimate we don't support these. - void* operator new(); // expected-error {{'operator' is a reserved keyword in HLSL}} expected-error {{pointers are unsupported in HLSL}} - void* operator new(int); // expected-error {{'operator' is a reserved keyword in HLSL}} expected-error {{pointers are unsupported in HLSL}} - void* operator new(size_t); // expected-error {{'operator' is a reserved keyword in HLSL}} expected-error {{pointers are unsupported in HLSL}} + // Pointer return type is no longer caught at parse time; operator keyword error is still reported. + void* operator new(); // expected-error {{'operator' is a reserved keyword in HLSL}} + void* operator new(int); // expected-error {{'operator' is a reserved keyword in HLSL}} + void* operator new(size_t); // expected-error {{'operator' is a reserved keyword in HLSL}} C() = delete; // expected-error {{HLSL requires a type specifier for all declarations}} expected-error {{constructor cannot have a return type}} }; diff --git a/tools/clang/test/HLSL/cpp-errors.hlsl b/tools/clang/test/HLSL/cpp-errors.hlsl index ac9630d9d7..bee34d249b 100644 --- a/tools/clang/test/HLSL/cpp-errors.hlsl +++ b/tools/clang/test/HLSL/cpp-errors.hlsl @@ -91,10 +91,10 @@ typename typedef float4 TFloat4; // expected-error {{'typename' is a reserved ke class C { int fn_eq_default() = default; // expected-error {{function deletion and defaulting is unsupported in HLSL}} - // Errors are a bit misleading here, but ultimate we don't support these. - void* operator new(); // expected-error {{'operator' is a reserved keyword in HLSL}} expected-error {{pointers are unsupported in HLSL}} - void* operator new(int); // expected-error {{'operator' is a reserved keyword in HLSL}} expected-error {{pointers are unsupported in HLSL}} - void* operator new(size_t); // expected-error {{'operator' is a reserved keyword in HLSL}} expected-error {{pointers are unsupported in HLSL}} + // Pointer return type is no longer caught at parse time; operator keyword error is still reported. + void* operator new(); // expected-error {{'operator' is a reserved keyword in HLSL}} + void* operator new(int); // expected-error {{'operator' is a reserved keyword in HLSL}} + void* operator new(size_t); // expected-error {{'operator' is a reserved keyword in HLSL}} C() = delete; // expected-error {{HLSL requires a type specifier for all declarations}} expected-error {{constructor cannot have a return type}} }; diff --git a/tools/clang/test/SemaHLSL/decltype-ptr-ref-instance-vars.hlsl b/tools/clang/test/SemaHLSL/decltype-ptr-ref-instance-vars.hlsl new file mode 100644 index 0000000000..2b12371598 --- /dev/null +++ b/tools/clang/test/SemaHLSL/decltype-ptr-ref-instance-vars.hlsl @@ -0,0 +1,67 @@ +// RUN: %dxc -Tlib_6_3 -Wno-unused-value -verify %s +// RUN: %dxc -Tcs_6_0 -Wno-unused-value -verify %s + +// Verify that pointer and reference types produced by __decltype cannot be +// used as struct instance variable types or local variable types in HLSL. +// Also verifies that template instantiation with pointer/reference type +// arguments is rejected, and that valid __decltype uses are not affected. + +// __decltype is the GCC way of saying 'decltype', but doesn't require C++11. + +// groupshared variables are mutable (unlike plain global variables in HLSL), +// which allows their lvalue expressions to produce reference types via +// __decltype. +groupshared int g; + +// --- Struct field: reference type from __decltype --- +struct RefField { + __decltype(++g) ref_field; // expected-error {{references are unsupported in HLSL}} +}; + +// --- Struct field: pointer type via explicit cast in __decltype --- +struct PtrField { + __decltype((int *)0) ptr_field; // expected-error {{pointers are unsupported in HLSL}} +}; + +// --- Struct field: plain (non-reference) type from __decltype is valid --- +struct PlainField { + __decltype(0 + 1) plain_field; // no error: int rvalue, not a reference +}; + +// --- Local variable: reference type from __decltype --- +void test_local_ref() { + int x = 0; + __decltype(++x) local_ref; // expected-error {{references are unsupported in HLSL}} +} + +// --- Local variable: pointer type via __decltype --- +void test_local_ptr() { + __decltype((int *)0) local_ptr; // expected-error {{pointers are unsupported in HLSL}} +} + +// --- Template: field with explicit reference type is rejected at definition --- +template +struct RefContainer { + T &ref_field; // expected-error {{references are unsupported in HLSL}} +}; + +// --- Template instantiation: type argument is an explicit pointer type --- +// This verifies that pointer instance variables cannot be created through +// template instantiation with pointer type arguments. +template +struct Container { + T field; +}; + +void test_template_ptr_explicit() { + Container c; // expected-error {{pointers are unsupported in HLSL}} +} + +// --- Regression: __decltype in is_same template argument still works --- +// HLSL has a non-standard is_same extension that treats T and T& as the same +// type. This is used in scalar-operators tests and must not be broken. +void test_is_same_regression() { + int x = 0; + _Static_assert(std::is_same::value, + "regression: is_same with lvalue __decltype must still work"); +} diff --git a/tools/clang/test/SemaHLSL/more-operators.hlsl b/tools/clang/test/SemaHLSL/more-operators.hlsl index cbd0250fb9..ed449051b6 100644 --- a/tools/clang/test/SemaHLSL/more-operators.hlsl +++ b/tools/clang/test/SemaHLSL/more-operators.hlsl @@ -276,25 +276,25 @@ float4 plain(float4 param4 /* : FOO */) /*: FOO */{ _Static_assert(std::is_same::value, ""); (--bool_l); // expected-error {{operator cannot be used with a bool lvalue}} fxc-error {{X3020: operator cannot be used with a bool lvalue}} (bool_l--); // expected-error {{operator cannot be used with a bool lvalue}} fxc-error {{X3020: operator cannot be used with a bool lvalue}} - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); // fxc-pass {{}} - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); // fxc-pass {{}} - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); (--SamplerState_l); // expected-error {{scalar, vector, or matrix expected}} fxc-pass {{}} (SamplerState_l--); // expected-error {{scalar, vector, or matrix expected}} fxc-pass {{}} @@ -306,39 +306,39 @@ float4 plain(float4 param4 /* : FOO */) /*: FOO */{ (bool1_l--); // expected-error {{operator cannot be used with a bool lvalue}} fxc-error {{X3020: operator cannot be used with a bool lvalue}} (--bool2_l); // expected-error {{operator cannot be used with a bool lvalue}} fxc-error {{X3020: operator cannot be used with a bool lvalue}} (bool2_l--); // expected-error {{operator cannot be used with a bool lvalue}} fxc-error {{X3020: operator cannot be used with a bool lvalue}} - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); /* fxc-pass {{}} */ - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); (++bool_l); // expected-error {{operator cannot be used with a bool lvalue}} fxc-error {{X3020: operator cannot be used with a bool lvalue}} (bool_l++); // expected-error {{operator cannot be used with a bool lvalue}} fxc-error {{X3020: operator cannot be used with a bool lvalue}} - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); // fxc-pass {{}} - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); // fxc-pass {{}} - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); (++SamplerState_l); // expected-error {{scalar, vector, or matrix expected}} fxc-pass {{}} (SamplerState_l++); // expected-error {{scalar, vector, or matrix expected}} fxc-pass {{}} @@ -350,17 +350,17 @@ float4 plain(float4 param4 /* : FOO */) /*: FOO */{ (bool1_l++); // expected-error {{operator cannot be used with a bool lvalue}} fxc-error {{X3020: operator cannot be used with a bool lvalue}} (++bool2_l); // expected-error {{operator cannot be used with a bool lvalue}} fxc-error {{X3020: operator cannot be used with a bool lvalue}} (bool2_l++); // expected-error {{operator cannot be used with a bool lvalue}} fxc-error {{X3020: operator cannot be used with a bool lvalue}} - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); /* fxc-pass {{}} */ - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); - _Static_assert(std::is_same::value, ""); // expected-error {{pointers are unsupported in HLSL}} fxc-pass {{}} + _Static_assert(std::is_same::value, ""); // expected-error {{references are unsupported in HLSL}} fxc-pass {{}} _Static_assert(std::is_same::value, ""); // Tests with multidimensional arrays. diff --git a/tools/clang/test/SemaHLSL/overloading-new-delete-errors.hlsl b/tools/clang/test/SemaHLSL/overloading-new-delete-errors.hlsl index ed68ecc4d9..c22e97479c 100644 --- a/tools/clang/test/SemaHLSL/overloading-new-delete-errors.hlsl +++ b/tools/clang/test/SemaHLSL/overloading-new-delete-errors.hlsl @@ -7,16 +7,16 @@ struct S { float foo; - void * operator new(int size) { // expected-error {{overloading 'operator new' is not allowed}} expected-error {{pointers are unsupported in HLSL}} - return (void *)0; // expected-error {{pointers are unsupported in HLSL}} expected-error {{cannot convert from 'literal int' to 'void *'}} expected-warning {{'operator new' should not return a null pointer unless it is declared 'throw()'}} + S operator new(int size) { // expected-error {{overloading 'operator new' is not allowed}} + return (S)0; } - void operator delete(void *ptr) { // expected-error {{overloading 'operator delete' is not allowed}} expected-error {{pointers are unsupported in HLSL}} + void operator delete(int ptr) { // expected-error {{overloading 'operator delete' is not allowed}} (void) ptr; } }; [shader("vertex")] void main() { - S *a = new S(); // expected-error {{'new' is a reserved keyword in HLSL}} expected-error {{pointers are unsupported in HLSL}} + S a = new S(); // expected-error {{'new' is a reserved keyword in HLSL}} delete a; // expected-error {{'delete' is a reserved keyword in HLSL}} } diff --git a/tools/clang/test/SemaHLSL/template-implicit-this-sfinae.hlsl b/tools/clang/test/SemaHLSL/template-implicit-this-sfinae.hlsl new file mode 100644 index 0000000000..9c9d43798e --- /dev/null +++ b/tools/clang/test/SemaHLSL/template-implicit-this-sfinae.hlsl @@ -0,0 +1,65 @@ +// RUN: %dxc -T cs_6_0 %s 2>&1 | FileCheck %s --check-prefix=COMPILE +// RUN: %dxc -T cs_6_0 -ast-dump %s 2>&1 | FileCheck %s + +// Test that template instantiation of member functions with SFINAE patterns +// involving static const members (accessed via implicit 'this') does not +// assert or crash. In HLSL, 'this' is an lvalue reference (not a pointer), +// so CXXDependentScopeMemberExpr must store the class type T (not T*) as the +// base type to avoid HLSL's ban on pointer types during template instantiation. + +namespace hlsl { +template struct enable_if {}; +template struct enable_if { + using type = T; +}; +template struct is_arithmetic { + static const bool value = false; +}; +template <> struct is_arithmetic { + static const bool value = true; +}; +template <> struct is_arithmetic { + static const bool value = true; +}; +} // namespace hlsl + +template +struct Wrapper { + static const bool IsArithmetic = hlsl::is_arithmetic::value; + + // SFINAE on a static const bool member accessed via implicit 'this'. + // This pattern previously triggered an assert when built with + // LLVM_ENABLE_ASSERTIONS=ON because template instantiation tried to rebuild + // the implicit 'this' pointer type, which is illegal in HLSL. + template + typename hlsl::enable_if::type Get() { + return val; + } + + T val; +}; + +// Verify that in the uninstantiated template the implicit 'this' is an lvalue +// of the class type (not a pointer), so HLSL's pointer ban is not violated. +// CHECK: CXXThisExpr {{.*}} 'Wrapper' lvalue this + +// Verify the same holds in the Wrapper instantiation's Get method. +// CHECK: CXXThisExpr {{.*}} 'Wrapper' lvalue this + +// Verify the same holds in the Wrapper instantiation's Get method. +// CHECK: CXXThisExpr {{.*}} 'Wrapper' lvalue this + +// Verify no CXXThisExpr carries a pointer type for 'this'. +// CHECK-NOT: CXXThisExpr {{.*}} '{{[^']*}} \*' lvalue this + +// COMPILE: define void @main +[numthreads(1, 1, 1)] +void main() { + Wrapper wf; + wf.val = 1.0f; + float f = wf.Get(); + + Wrapper wi; + wi.val = 2; + int i = wi.Get(); +} From d7510a07318f09e8f47249652b9319e8d1a7a5da Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Wed, 13 May 2026 18:31:35 -0500 Subject: [PATCH 2/2] Update check prefix ../tools/clang/test/SemaHLSL/template-implicit-this-sfinae.hlsl --- tools/clang/test/SemaHLSL/template-implicit-this-sfinae.hlsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/clang/test/SemaHLSL/template-implicit-this-sfinae.hlsl b/tools/clang/test/SemaHLSL/template-implicit-this-sfinae.hlsl index 9c9d43798e..e14ee1dbb1 100644 --- a/tools/clang/test/SemaHLSL/template-implicit-this-sfinae.hlsl +++ b/tools/clang/test/SemaHLSL/template-implicit-this-sfinae.hlsl @@ -1,4 +1,4 @@ -// RUN: %dxc -T cs_6_0 %s 2>&1 | FileCheck %s --check-prefix=COMPILE +// RUN: %dxc -T cs_6_0 %s 2>&1 | FileCheck %s --check-prefix=CHECK_COMPILE // RUN: %dxc -T cs_6_0 -ast-dump %s 2>&1 | FileCheck %s // Test that template instantiation of member functions with SFINAE patterns @@ -52,7 +52,7 @@ struct Wrapper { // Verify no CXXThisExpr carries a pointer type for 'this'. // CHECK-NOT: CXXThisExpr {{.*}} '{{[^']*}} \*' lvalue this -// COMPILE: define void @main +// CHECK_COMPILE: define void @main [numthreads(1, 1, 1)] void main() { Wrapper wf;