From 34f3adea24185674aefa8f3a57ea6bc6a57ce41c Mon Sep 17 00:00:00 2001 From: Chris B Date: Mon, 9 Nov 2026 16:22:34 -0600 Subject: [PATCH 1/6] Make strings intangible types This updates the implementation of the built-in `string` type to be an intangible type. Aligning on the intangible semantics allows strings to be used in contexts that were allowed in FXC and some contexts supported by SPIRV. This change does not expose any new things users can do with strings, but it is a neccessary basis for extending string operations. ../tools/clang/test/CodeGenSPIRV/type.string.initializer.hlsl ../tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-ini tializers.hlsl ../tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-empty-aggreg ates.hlsl ../tools/clang/test/SemaHLSL/subobjects-ast-dump.hlsl --- docs/ReleaseNotes.md | 3 + lib/DXIL/DxilTypeSystem.cpp | 26 +++- lib/HLSL/HLLegalizeParameter.cpp | 9 ++ tools/clang/include/clang/AST/ASTContext.h | 2 +- tools/clang/lib/AST/ASTContext.cpp | 4 +- tools/clang/lib/CodeGen/CGDecl.cpp | 10 +- tools/clang/lib/CodeGen/CGHLSLMS.cpp | 36 ++++- tools/clang/lib/SPIRV/DeclResultIdMapper.cpp | 6 + tools/clang/lib/Sema/SemaDecl.cpp | 5 - tools/clang/lib/Sema/SemaHLSL.cpp | 29 ++-- .../clang/test/CodeGenSPIRV/type.string.hlsl | 3 +- .../CodeGenSPIRV/type.string.initializer.hlsl | 10 ++ .../string-constant-initializers.hlsl | 113 ++++++++++++++++ .../intrinsics/string-empty-aggregates.hlsl | 47 +++++++ tools/clang/test/SemaHLSL/string-storage.hlsl | 127 ++++++++++++++++++ tools/clang/test/SemaHLSL/string.hlsl | 34 ++--- .../test/SemaHLSL/subobjects-ast-dump.hlsl | 18 +-- .../test/SemaHLSL/subobjects-syntax.hlsl | 8 +- 18 files changed, 431 insertions(+), 59 deletions(-) create mode 100644 tools/clang/test/CodeGenSPIRV/type.string.initializer.hlsl create mode 100644 tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl create mode 100644 tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-empty-aggregates.hlsl create mode 100644 tools/clang/test/SemaHLSL/string-storage.hlsl diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 082a606687b..2aa76fb22b7 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -52,6 +52,9 @@ line upon naming the release. Refer to previous for appropriate section names. #### HLSL Language +- Updated implementation of `string` types to conform to restrictions for + intangible objects aligning with TC57's direction (see: + [hlsl-tc57/tc57#200](https://github.com/hlsl-tc57/tc57/issues/200)). - The `shared` and `uniform` keywords are removed in HLSL 202x, with compatibility warnings available for earlier language versions [#8482](https://github.com/microsoft/DirectXShaderCompiler/issues/8482). diff --git a/lib/DXIL/DxilTypeSystem.cpp b/lib/DXIL/DxilTypeSystem.cpp index da7d85c7548..3d654ee2c8d 100644 --- a/lib/DXIL/DxilTypeSystem.cpp +++ b/lib/DXIL/DxilTypeSystem.cpp @@ -13,6 +13,7 @@ #include "dxc/Support/Global.h" #include "dxc/Support/WinFunctions.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/Support/raw_ostream.h" @@ -393,6 +394,21 @@ DxilTypeSystem::AddStructAnnotation(const StructType *pStructType, return pA; } +static bool ContainsString(const Type *Ty, + SmallPtrSetImpl &CheckedTypes) { + if (!CheckedTypes.insert(Ty).second) + return false; + if (Ty->isPointerTy()) + return Ty->getPointerElementType()->isIntegerTy(8); + if (auto *AT = dyn_cast(Ty)) + return ContainsString(AT->getElementType(), CheckedTypes); + if (auto *ST = dyn_cast(Ty)) + for (Type *Element : ST->elements()) + if (ContainsString(Element, CheckedTypes)) + return true; + return false; +} + void DxilTypeSystem::FinishStructAnnotation(DxilStructAnnotation &SA) { const llvm::StructType *ST = SA.GetStructType(); DXASSERT(SA.GetNumFields() == ST->getNumElements(), @@ -411,9 +427,13 @@ void DxilTypeSystem::FinishStructAnnotation(DxilStructAnnotation &SA) { SA.SetContainsTargetTypes(); } - // Mark if empty - if (SA.GetCBufferSize() == 0) - SA.MarkEmptyStruct(); + // Zero cbuffer-layout size does not imply an empty struct: + // string-only aggregates still contain values that must be copied. + if (SA.GetCBufferSize() == 0) { + SmallPtrSet CheckedTypes; + if (!ContainsString(ST, CheckedTypes)) + SA.MarkEmptyStruct(); + } } DxilStructAnnotation * diff --git a/lib/HLSL/HLLegalizeParameter.cpp b/lib/HLSL/HLLegalizeParameter.cpp index 51653070eaf..ed799dfc5d2 100644 --- a/lib/HLSL/HLLegalizeParameter.cpp +++ b/lib/HLSL/HLLegalizeParameter.cpp @@ -295,6 +295,15 @@ bool HLLegalizeParameter::runOnModule(Module &M) { void HLLegalizeParameter::patchWriteOnInParam(Function &F, Argument &Arg, const DataLayout &DL) { + // Skip i8 pointer arguments, these should only occur for strings. + // Note: this is an utter disaster if we ever want to support i8 data types in + // HLSL. So that will be fun... + if (Arg.getType()->isPointerTy()) { + Type *EltTy = Arg.getType()->getPointerElementType(); + if (EltTy->isIntegerTy() && EltTy->getIntegerBitWidth() == 8) + return; + } + // TODO: Adding lifetime intrinsics isn't easy here, have to analyze uses. Type *Ty = Arg.getType()->getPointerElementType(); AllocaInst *temp = createAllocaForPatch(F, Ty); diff --git a/tools/clang/include/clang/AST/ASTContext.h b/tools/clang/include/clang/AST/ASTContext.h index 169efa26ba9..e230e7729e7 100644 --- a/tools/clang/include/clang/AST/ASTContext.h +++ b/tools/clang/include/clang/AST/ASTContext.h @@ -849,8 +849,8 @@ class ASTContext : public RefCountedBase { CanQualType Min12IntTy, Min10FloatTy; CanQualType LitIntTy, LitFloatTy; CanQualType HalfFloatTy, Min16FloatTy, Min16IntTy, Min16UIntTy; - CanQualType HLSLStringTy; CanQualType Int8_4PackedTy, UInt8_4PackedTy; + QualType HLSLStringTy; CanQualType LinAlgMatrixTy; // HLSL Changes end diff --git a/tools/clang/lib/AST/ASTContext.cpp b/tools/clang/lib/AST/ASTContext.cpp index 19511b0f41f..566c411f372 100644 --- a/tools/clang/lib/AST/ASTContext.cpp +++ b/tools/clang/lib/AST/ASTContext.cpp @@ -1110,7 +1110,9 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target) { InitBuiltinType(UInt8_4PackedTy, BuiltinType::UInt8_4Packed); InitBuiltinType(LinAlgMatrixTy, BuiltinType::LinAlgMatrix); - HLSLStringTy = this->getPointerType(CharTy); + // This shouldn't actually be a canonical type, since it effectively must be + // const. + HLSLStringTy = this->getPointerType(CharTy.withConst()); hlsl::InitializeASTContextForHLSL(*this); // Previously in constructor, guarded by !DelayInitialization } diff --git a/tools/clang/lib/CodeGen/CGDecl.cpp b/tools/clang/lib/CodeGen/CGDecl.cpp index e61750c654b..9345bdff3aa 100644 --- a/tools/clang/lib/CodeGen/CGDecl.cpp +++ b/tools/clang/lib/CodeGen/CGDecl.cpp @@ -11,15 +11,18 @@ // //===----------------------------------------------------------------------===// -#include "CodeGenFunction.h" #include "CGCleanup.h" #include "CGDebugInfo.h" +#include "CGHLSLRuntime.h" // HLSL Change #include "CGOpenCLRuntime.h" +#include "CodeGenFunction.h" #include "CodeGenModule.h" +#include "dxc/DXIL/DxilMetadataHelper.h" // HLSL Change #include "clang/AST/ASTContext.h" #include "clang/AST/CharUnits.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/HlslTypes.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" @@ -28,8 +31,6 @@ #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Type.h" -#include "CGHLSLRuntime.h" // HLSL Change -#include "dxc/DXIL/DxilMetadataHelper.h" // HLSL Change using namespace clang; using namespace CodeGen; @@ -1773,7 +1774,8 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, llvm::Value *Arg, } } else { // HLSL Change Starts - if (getLangOpts().HLSL && Arg->getType()->isPointerTy()) { + if (getLangOpts().HLSL && Arg->getType()->isPointerTy() && + !hlsl::IsStringType(Ty)) { // HLSL doesn't have pointer. // Only case pointer type will generated is this pointer for methods. // Don't store this pointer, just use the parameter directly. diff --git a/tools/clang/lib/CodeGen/CGHLSLMS.cpp b/tools/clang/lib/CodeGen/CGHLSLMS.cpp index 1b806933e6d..acfcb98eef9 100644 --- a/tools/clang/lib/CodeGen/CGHLSLMS.cpp +++ b/tools/clang/lib/CodeGen/CGHLSLMS.cpp @@ -4157,6 +4157,12 @@ void CGMSHLSLRuntime::FlattenValToInitList(CodeGenFunction &CGF, CGBuilderTy &Builder = CGF.Builder; llvm::Type *valTy = val->getType(); + if (hlsl::IsStringType(Ty) && valTy == CGF.ConvertType(Ty)) { + elts.emplace_back(val); + eltTys.emplace_back(Ty); + return; + } + if (valTy->isPointerTy()) { llvm::Type *valEltTy = valTy->getPointerElementType(); if (valEltTy->isVectorTy() || valEltTy->isSingleValueType()) { @@ -4347,6 +4353,9 @@ static void AddMissingCastOpsInInitList(SmallVector &elts, for (FieldDecl *field : RD->fields()) AddMissingCastOpsInInitList(elts, eltTys, idx, field->getType(), CGF); } + } else if (IsStringType(Ty)) { + DXASSERT_NOMSG(IsStringType(eltTys[idx])); + idx++; } else { // Basic type. elts[idx] = ConvertScalarOrVector(CGF, elts[idx], eltTys[idx], Ty); @@ -4743,8 +4752,8 @@ static void FlatConstToList(CodeGenTypes &Types, bool bDefaultRowMajor, EltVals, EltQualTys); } } else { - // At this point, we should have scalars in their memory representation - DXASSERT_NOMSG(QualTy->isBuiltinType()); + // Scalar leaves, including string pointers, are in memory representation. + DXASSERT_NOMSG(QualTy->isBuiltinType() || hlsl::IsStringType(QualTy)); EltVals.emplace_back(C); EltQualTys.emplace_back(QualTy); } @@ -4922,11 +4931,16 @@ static Constant *BuildConstInitializer(CodeGenTypes &Types, return BuildConstStruct(Types, bDefaultRowMajor, QualTy, EltVals, EltQualTys, EltIdx); } else { - DXASSERT_NOMSG(QualTy->isBuiltinType()); + DXASSERT_NOMSG(QualTy->isBuiltinType() || hlsl::IsStringType(QualTy)); Constant *EltVal = EltVals[EltIdx]; QualType EltQualTy = EltQualTys[EltIdx]; EltIdx++; + if (hlsl::IsStringType(QualTy)) { + DXASSERT_NOMSG(hlsl::IsStringType(EltQualTy)); + return EltVal; + } + // Initializer constants are in their memory representation. if (EltQualTy == QualTy && MemRepr) return EltVal; @@ -5549,7 +5563,10 @@ void CGMSHLSLRuntime::FlattenAggregatePtrToGepList( CodeGenFunction &CGF, Value *Ptr, SmallVector &idxList, clang::QualType Type, llvm::Type *Ty, SmallVector &GepList, SmallVector &EltTyList) { - if (llvm::PointerType *PT = dyn_cast(Ty)) { + if (hlsl::IsStringType(Type) && Ty == CGF.ConvertType(Type)) { + GepList.push_back(CGF.Builder.CreateInBoundsGEP(Ptr, idxList)); + EltTyList.push_back(Type); + } else if (llvm::PointerType *PT = dyn_cast(Ty)) { Constant *idx = Constant::getIntegerValue( IntegerType::get(Ty->getContext(), 32), APInt(32, 0)); idxList.emplace_back(idx); @@ -5673,8 +5690,10 @@ void CGMSHLSLRuntime::LoadElements(CodeGenFunction &CGF, ArrayRef Ptrs, for (size_t i = 0, e = Ptrs.size(); i < e; i++) { Value *Ptr = Ptrs[i]; llvm::Type *Ty = Ptr->getType()->getPointerElementType(); - DXASSERT_LOCALVAR(Ty, Ty->isIntegerTy() || Ty->isFloatingPointTy(), - "Expected only element types."); + DXASSERT_LOCALVAR(Ty, + Ty->isIntegerTy() || Ty->isFloatingPointTy() || + hlsl::IsStringType(QualTys[i]), + "Expected only element types or strings."); Value *Val = CGF.Builder.CreateLoad(Ptr); Val = CGF.EmitFromMemory(Val, QualTys[i]); Vals.push_back(Val); @@ -5691,6 +5710,11 @@ void CGMSHLSLRuntime::ConvertAndStoreElements(CodeGenFunction &CGF, QualType DstQualTy = DstQualTys[i]; Value *SrcVal = SrcVals[i]; QualType SrcQualTy = SrcQualTys[i]; + if (hlsl::IsStringType(DstQualTy)) { + DXASSERT_NOMSG(hlsl::IsStringType(SrcQualTy)); + CGF.Builder.CreateStore(SrcVal, DstPtr); + continue; + } DXASSERT(SrcVal->getType()->isIntegerTy() || SrcVal->getType()->isFloatingPointTy(), "Expected only element types."); diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp index 46967298c70..525defe7f93 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp @@ -1437,6 +1437,12 @@ DeclResultIdMapper::createOrUpdateStringVar(const VarDecl *var) { const StringLiteral *stringLiteral = dyn_cast(var->getInit()->IgnoreParenCasts()); + if (!stringLiteral) { + emitError( + "string variable initializer must be a string literal with -spirv", + var->getInit()->getExprLoc()); + return nullptr; + } SpirvString *init = spvBuilder.getString(stringLiteral->getString()); registerVariableForDecl(var, createDeclSpirvInfo(init)); return init; diff --git a/tools/clang/lib/Sema/SemaDecl.cpp b/tools/clang/lib/Sema/SemaDecl.cpp index 1b1b6e1305d..cf106d6706c 100644 --- a/tools/clang/lib/Sema/SemaDecl.cpp +++ b/tools/clang/lib/Sema/SemaDecl.cpp @@ -10381,11 +10381,6 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D) { // HLSL Change Starts if (getLangOpts().HLSL) { const bool IsParm = true; - if (hlsl::IsStringType(parmDeclType)) { - static const unsigned selectParamIdx = 1; - Diag(D.getLocStart(), diag::err_hlsl_unsupported_string_decl) << selectParamIdx; - D.setInvalidType(); - } if (!DiagnoseHLSLDecl(D, Context.getTranslationUnitDecl(), /*BitWidth*/ nullptr, TInfo, IsParm)) { assert(D.isInvalidType() && "otherwise DiagnoseHLSLDecl failed but " "didn't invalidate declaration"); diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index 1d6b56b2693..cd2c0a5992c 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -5925,6 +5925,10 @@ class HLSLExternalSource : public ExternalSemaSource { if (Arg.getKind() == TemplateArgument::ArgKind::Type) { QualType ArgType = Arg.getAsType(); SourceLocation ArgSrcLoc = ArgLoc.getLocation(); + if (!ArgType->isDependentType() && + m_sema->RequireCompleteType( + ArgSrcLoc, ArgType, diag::err_typecheck_decl_incomplete_type)) + return true; if (DiagnoseTypeElements( *m_sema, ArgSrcLoc, ArgType, TypeDiagContext::StructuredBuffers /*ObjDiagContext*/, @@ -10704,7 +10708,7 @@ void HLSLExternalSource::CheckBinOpForHLSL(SourceLocation OpLoc, SuppressWarningsFalse, SuppressErrorsFalse, &standard)) { return; } - if (RHS.get()->isLValue()) { + if (RHS.get()->isLValue() && standard.First != ICK_Array_To_Pointer) { standard.First = ICK_Lvalue_To_Rvalue; } RHS = m_sema->PerformImplicitConversion(RHS.get(), ResultTy, standard, @@ -11525,6 +11529,10 @@ HLSLExternalSource::DeduceTemplateArgumentsForHLSL( } } } else if (IsBABStore) { + if (Is2018 && DiagnoseTypeElements( + *getSema(), Args[1]->getExprLoc(), argTypes[2], + TypeDiagContext::TypeParameter, TypeDiagContext::Valid)) + return Sema::TemplateDeductionResult::TDK_Invalid; // Prior to HLSL 2018, Store operation only stored scalar uint. if (!Is2018) { if (GetNumElements(argTypes[2]) != 1) { @@ -12830,6 +12838,8 @@ void Sema::DiagnoseReachableHLSLCall(CallExpr *CE, const hlsl::ShaderModel *SM, ///////////////////////////////////////////////////////////////////////////// static bool AllowObjectInContext(QualType Ty, TypeDiagContext DiagContext) { + if (IsStringType(Ty)) + return DiagContext == TypeDiagContext::GlobalVariables; // Disallow all object in template type parameters (former // err_hlsl_objectintemplateargument) if (DiagContext == TypeDiagContext::TypeParameter) @@ -12870,6 +12880,8 @@ DiagnoseElementTypes(Sema &S, SourceLocation Loc, QualType Ty, bool &Empty, if (Ty.isNull() || Ty->isDependentType()) return false; + Ty = Ty.getNonReferenceType(); + const bool CheckLongVec = LongVecDiagContext != TypeDiagContext::Valid; const bool CheckObjects = ObjDiagContext != TypeDiagContext::Valid; @@ -12914,6 +12926,7 @@ DiagnoseElementTypes(Sema &S, SourceLocation Loc, QualType Ty, bool &Empty, case AR_TOBJ_MATRIX: Empty = false; return false; + case AR_TOBJ_STRING: case AR_TOBJ_OBJECT: Empty = false; if (!CheckObjects || AllowObjectInContext(Ty, ObjDiagContext)) @@ -15735,11 +15748,11 @@ bool Sema::DiagnoseHLSLDecl(Declarator &D, DeclContext *DC, Expr *BitWidth, } } - // String and subobject declarations are supported only as top level global - // variables. Const and static modifiers are implied - add them if missing. - if ((hlsl::IsStringType(qt) || hlslSource->IsSubobjectType(qt)) && + // Global strings and subobjects have implicit const and static modifiers. + // Strings may also be fields or local variables without these modifiers. + if (((hlsl::IsStringType(qt) && !isField && !isLocalVar) || + hlslSource->IsSubobjectType(qt)) && !D.isInvalidType()) { - // string are supported only as top level global variables if (!DC->isTranslationUnit()) { Diag(D.getLocStart(), diag::err_hlsl_object_not_global) << (int)hlsl::IsStringType(qt); @@ -15752,7 +15765,7 @@ bool Sema::DiagnoseHLSLDecl(Declarator &D, DeclContext *DC, Expr *BitWidth, } const char *PrevSpec = nullptr; unsigned DiagID = 0; - if (!isStatic) { + if (!isStatic && !isParameter) { D.getMutableDeclSpec().SetStorageClassSpec( *this, DeclSpec::SCS_static, D.getLocStart(), PrevSpec, DiagID, Context.getPrintingPolicy()); @@ -16195,8 +16208,8 @@ bool Sema::DiagnoseHLSLDecl(Declarator &D, DeclContext *DC, Expr *BitWidth, result = false; } - // Disallow intangible HLSL objects in the global scope. - if (isGlobal) { + // Disallow intangible HLSL objects in externally visible storage. + if (isGlobal || (isParameter && isGroupShared)) { // Suppress actual emitting of errors for incompletable types here // They are redundant to those produced in ActOnUninitializedDecl. struct SilentDiagnoser : public TypeDiagnoser { diff --git a/tools/clang/test/CodeGenSPIRV/type.string.hlsl b/tools/clang/test/CodeGenSPIRV/type.string.hlsl index 0edb63a9dca..cb6c842f56c 100644 --- a/tools/clang/test/CodeGenSPIRV/type.string.hlsl +++ b/tools/clang/test/CodeGenSPIRV/type.string.hlsl @@ -11,5 +11,6 @@ const string a = "first string"; [numthreads(1,1,1)] void main() { + // CHECK: {{%[0-9]+}} = OpString "local string" + string local = ("local string"); } - diff --git a/tools/clang/test/CodeGenSPIRV/type.string.initializer.hlsl b/tools/clang/test/CodeGenSPIRV/type.string.initializer.hlsl new file mode 100644 index 00000000000..b7d3bd2a441 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/type.string.initializer.hlsl @@ -0,0 +1,10 @@ +// RUN: not %dxc -T cs_6_0 -E main -fcgl %s -spirv 2>&1 | FileCheck %s + +string globalText = "text"; + +[numthreads(1, 1, 1)] +void main() { + // CHECK: error: string variable initializer must be a string literal with -spirv + // CHECK-NEXT: string local = globalText; + string local = globalText; +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl new file mode 100644 index 00000000000..91d416bf09f --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl @@ -0,0 +1,113 @@ +// RUN: %dxc -T cs_6_0 -E main -HV 202x %s | FileCheck %s --check-prefixes=CHECK,OPT +// RUN: %dxc -T cs_6_0 -E main -HV 202x -Od %s | FileCheck %s --check-prefixes=CHECK,NOOPT +// RUN: %dxc -T cs_6_0 -E main -HV 202x -Zi -Qembed_debug %s | FileCheck %s --check-prefixes=CHECK,OPT +// RUN: %dxc -T cs_6_0 -E main -HV 2021 %s | FileCheck %s --check-prefixes=CHECK,OPT +// RUN: %dxc -T cs_6_0 -E main -HV 202x -fcgl %s | FileCheck %s --check-prefix=FCGL +// RUN: %dxc -T cs_6_0 -E main -HV 2021 -fcgl %s | FileCheck %s --check-prefix=FCGL + +// CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 0, i32 undef, i32 3, +// CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 4, i32 undef, i32 4, +// CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 8, i32 undef, i32 5, +// CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 12, i32 undef, i32 3, +// CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 16, i32 undef, i32 6, +// CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 20, i32 undef, i32 3, +// CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 24, i32 undef, i32 7, +// CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 28, i32 undef, i32 6, +// OPT: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 32, i32 undef, i32 5, +// NOOPT: [[FLOAT:%[^ ]+]] = uitofp i32 5 to float +// NOOPT: [[INT:%[^ ]+]] = fptoui float [[FLOAT]] to i32 +// NOOPT: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 32, i32 undef, i32 [[INT]], + +// FCGL-DAG: %struct.StringState = type { i8*, i32 } +// FCGL-DAG: %struct.StringOnly = type { i8* } +// FCGL-DAG: %struct.NestedState = type { [2 x %struct.StringState] } +// FCGL-DAG: %struct.DerivedState = type { %struct.StringState, i8* } +// FCGL-DAG: @GlobalText = internal constant i8* getelementptr inbounds ([6 x i8], [6 x i8]* [[HELLO:@"[^"]+"]], i32 0, i32 0) +// FCGL-DAG: @OnlyText = internal constant %struct.StringOnly { i8* getelementptr inbounds ([6 x i8], [6 x i8]* [[HELLO]], i32 0, i32 0) } +// FCGL-DAG: @GlobalLiteral = internal global %struct.StringState { i8* getelementptr inbounds ([6 x i8], [6 x i8]* [[HELLO]], i32 0, i32 0), i32 3 } +// FCGL-DAG: [[HELLO]] = linkonce_odr unnamed_addr constant [6 x i8] c"hello\00" +// FCGL-LABEL: define internal void {{.*CopyText.*}}(i8* %Text, i8** noalias dereferenceable(4) %Result) +// FCGL: [[SLOT:%[^ ]+]] = alloca i8* +// FCGL: store i8* %Text, i8** [[SLOT]] +// FCGL: [[VALUE:%[^ ]+]] = load i8*, i8** [[SLOT]] +// FCGL: store i8* [[VALUE]], i8** %Result +// FCGL-LABEL: define internal void {{.*ConvertState.*}} +// FCGL: [[TEXT_PTR:%[^ ]+]] = getelementptr inbounds %struct.StringState, %struct.StringState* %State, i32 0, i32 0 +// FCGL: [[TEXT:%[^ ]+]] = load i8*, i8** [[TEXT_PTR]] +// FCGL: store i8* [[TEXT]], i8** +// FCGL: uitofp i32 + +struct StringOnly { + string Text; +}; + +struct StringState { + string Text; + uint Value; +}; + +struct FloatStringState { + string Text; + float Value; +}; + +typedef StringState StateType; + +struct NestedState { + StringState Values[2]; +}; + +struct FloatNestedState { + FloatStringState Values[2]; +}; + +struct DerivedState : StringState { + string Other; +}; + +static const string GlobalText = "hello"; +static StringState GlobalLiteral = {"hello", 3}; +static const StateType GlobalReference = {GlobalText, 4}; +static NestedState Nested = {{{"hello", 5}, GlobalLiteral}}; +static DerivedState Derived = {GlobalLiteral, GlobalText}; +static const StringOnly OnlyText = {"hello"}; + +RWByteAddressBuffer Output : register(u0); + +StringState Copy(StringState State) { + return State; +} + +void Set(out StringState State) { + State.Text = "hello"; + State.Value = 6; +} + +void CopyText(string Text, out string Result) { + Result = Text; +} + +FloatStringState ConvertState(StringState State) { + return (FloatStringState)State; +} + +[numthreads(1, 1, 1)] +void main() { + StringState Local = Copy(GlobalLiteral); + Output.Store(0, Local.Value); + Output.Store(4, GlobalReference.Value); + Output.Store(8, Nested.Values[0].Value); + Output.Store(12, Derived.Value); + StringOnly Text = OnlyText; + CopyText(Derived.Other, Text.Text); + Local.Text = Text.Text; + Set(Local); + Output.Store(16, Local.Value); + Output.Store(20, Nested.Values[1].Value); + static const StringState StaticLocal = {"hello", 7}; + Output.Store(24, StaticLocal.Value); + FloatStringState Converted = ConvertState(Local); + Output.Store(28, (uint)Converted.Value); + FloatNestedState ConvertedNested = (FloatNestedState)Nested; + Output.Store(32, (uint)ConvertedNested.Values[0].Value); +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-empty-aggregates.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-empty-aggregates.hlsl new file mode 100644 index 00000000000..968293221c6 --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-empty-aggregates.hlsl @@ -0,0 +1,47 @@ +// RUN: %dxc -T cs_6_0 -E main %s | FileCheck %s +// RUN: %dxc -T cs_6_0 -E main -fcgl %s | FileCheck %s + +// String containment must visit distinct types, not every path through this +// shared type graph, even when no strings are present. +// CHECK: define void @main() + +struct Empty {}; +struct S1 { Empty A, B; }; +struct S2 { S1 A, B; }; +struct S3 { S2 A, B; }; +struct S4 { S3 A, B; }; +struct S5 { S4 A, B; }; +struct S6 { S5 A, B; }; +struct S7 { S6 A, B; }; +struct S8 { S7 A, B; }; +struct S9 { S8 A, B; }; +struct S10 { S9 A, B; }; +struct S11 { S10 A, B; }; +struct S12 { S11 A, B; }; +struct S13 { S12 A, B; }; +struct S14 { S13 A, B; }; +struct S15 { S14 A, B; }; +struct S16 { S15 A, B; }; +struct S17 { S16 A, B; }; +struct S18 { S17 A, B; }; +struct S19 { S18 A, B; }; +struct S20 { S19 A, B; }; +struct S21 { S20 A, B; }; +struct S22 { S21 A, B; }; +struct S23 { S22 A, B; }; +struct S24 { S23 A, B; }; +struct S25 { S24 A, B; }; +struct S26 { S25 A, B; }; +struct S27 { S26 A, B; }; +struct S28 { S27 A, B; }; +struct S29 { S28 A, B; }; +struct S30 { S29 A, B; }; +struct S31 { S30 A, B; }; +struct S32 { S31 A, B; }; + +static S32 State; + +[numthreads(1, 1, 1)] +void main() { + S32 Local = State; +} diff --git a/tools/clang/test/SemaHLSL/string-storage.hlsl b/tools/clang/test/SemaHLSL/string-storage.hlsl new file mode 100644 index 00000000000..1c247e47832 --- /dev/null +++ b/tools/clang/test/SemaHLSL/string-storage.hlsl @@ -0,0 +1,127 @@ +// RUN: %dxc -T lib_6_8 -HV 2021 -verify %s +// RUN: %dxc -T lib_6_8 -HV 202x -verify %s + +struct StringState { + string Text; // expected-note 19 {{field declared here}} +}; + +struct NestedState { + StringState Values[2]; +}; + +struct DerivedState : StringState { + uint Value; +}; + +struct Numeric { + uint Value; +}; + +template +struct Wrapper { + T Value; +}; + +// expected-error@+1 {{object 'const string' is not allowed in ConstantBuffers or TextureBuffers}} +ConstantBuffer CB; +// expected-error@+1 {{object 'const string' is not allowed in ConstantBuffers or TextureBuffers}} +TextureBuffer TB; +// expected-error@+1 {{object 'const string' is not allowed in structured buffers}} +StructuredBuffer SB; +// expected-error@+1 {{object 'const string' is not allowed in structured buffers}} +RWStructuredBuffer RWSB; +// expected-error@+1 {{object 'const string' is not allowed in structured buffers}} +AppendStructuredBuffer ASB; +// expected-error@+1 {{object 'const string' is not allowed in structured buffers}} +ConsumeStructuredBuffer CSB; +// expected-error@+1 {{object 'const string' is not allowed in structured buffers}} +RasterizerOrderedStructuredBuffer ROSB; +// expected-error@+1 {{object 'const string' is not allowed in structured buffers}} +StructuredBuffer DirectSB; +// expected-error@+1 {{object 'const string' is not allowed in structured buffers}} +RWStructuredBuffer > Wrapped; + +// expected-error@+1 {{'const string' cannot be used as a type parameter}} +Buffer DirectBuffer; +// expected-error@+2 {{'const string' cannot be used as a type parameter}} +// expected-note@+1 {{usage of 'const string' found in field 'Text'}} +RWTexture2D Texture; + +// expected-error@+1 {{object 'const string' is not allowed in cbuffers or tbuffers}} +NestedState Global; +cbuffer Constants { + // expected-error@+1 {{object 'const string' is not allowed in cbuffers or tbuffers}} + StringState State; +}; +tbuffer TextureConstants { + // expected-error@+1 {{object 'const string' is not allowed in cbuffers or tbuffers}} + DerivedState Derived; +}; + +// expected-error@+1 {{object 'const string' is not allowed in groupshared variables}} +groupshared string SharedString; +// expected-error@+1 {{object 'const string' is not allowed in groupshared variables}} +groupshared StringState Shared; +// expected-error@+1 {{object 'const string' is not allowed in groupshared variables}} +groupshared NestedState SharedArray[2]; +// expected-error@+1 {{object 'const string' is not allowed in groupshared variables}} +groupshared DerivedState SharedDerived; + +// expected-error@+1 {{object 'const string' is not allowed in groupshared variables}} +void SharedParameter(groupshared StringState State); +#if __HLSL_VERSION <= 2021 +// expected-warning@-2 {{Support for groupshared parameter annotation not added until HLSL 202x}} +#endif + +ByteAddressBuffer Bytes; +RWByteAddressBuffer RWBytes; + +void ByteAddressAccess(StringState A, NestedState B, DerivedState C) { + // expected-error@+2 {{Explicit template arguments on intrinsic Load must be a single numeric type}} + // expected-error@+1 {{object 'const string' is not allowed in builtin template parameters}} + A = Bytes.Load(0); + // expected-error@+2 {{Explicit template arguments on intrinsic Load must be a single numeric type}} + // expected-error@+1 {{object 'const string' is not allowed in builtin template parameters}} + B = RWBytes.Load(0); + // expected-error@+2 {{Explicit template arguments on intrinsic Store must be a single numeric type}} + // expected-error@+1 {{object 'const string' is not allowed in builtin template parameters}} + RWBytes.Store(0, C); + // expected-error@+2 {{object 'const string' is not allowed in builtin template parameters}} + // expected-error@+1 {{no matching member function for call to 'Store'}} + RWBytes.Store(0, C); + // expected-note@? {{candidate template ignored: couldn't infer template argument 'TResult'}} +} + +ConstantBuffer NumericCB; +RWStructuredBuffer NumericBuffer; +groupshared Numeric SharedNumeric[2]; +static StringState PrivateState; + +void PrivateStrings() { + string Text = "hello"; + StringState Local = {Text}; + NestedState Nested = {{Local, Local}}; + Text = "goodbye"; + Local.Text = Text; + PrivateState = Local; +} + +[shader("pixel")] +float4 StringInput(string Text : TEXCOORD0) : SV_Target { + // expected-error@-1 {{object 'const string' is not allowed in entry function parameters}} + return 0; +} + +[shader("pixel")] +float4 StringOutput(out string Text : SV_Target1) : SV_Target0 { + // expected-error@-1 {{object 'const string' is not allowed in entry function parameters}} + Text = "hello"; + return 0; +} + +[shader("pixel")] +float4 StringInOut(inout string Text : TEXCOORD0) : SV_Target { + // expected-error@-1 {{object 'const string' is not allowed in entry function parameters}} + Text = "hello"; + return 0; +} diff --git a/tools/clang/test/SemaHLSL/string.hlsl b/tools/clang/test/SemaHLSL/string.hlsl index dc4ca5b3321..36224de1ada 100644 --- a/tools/clang/test/SemaHLSL/string.hlsl +++ b/tools/clang/test/SemaHLSL/string.hlsl @@ -26,30 +26,30 @@ string s_global_concat = "my string " "with " "broken up" " parts"; -static const bool b1 = s_global1; /* expected-error {{cannot initialize a variable of type 'const bool' with an lvalue of type 'string'}} fxc-error {{X3017: cannot implicitly convert from 'const string' to 'const bool'}} */ +static const bool b1 = s_global1; /* expected-error {{cannot initialize a variable of type 'const bool' with an lvalue of type 'const string'}} fxc-error {{X3017: cannot implicitly convert from 'const string' to 'const bool'}} */ static const bool b2 = false; -static const string s_global4 = true; /* expected-error {{cannot initialize a variable of type 'string' with an rvalue of type 'bool'}} fxc-error {{X3017: cannot implicitly convert from 'bool' to 'const string'}} */ -static const string s_global5 = b2; /* expected-error {{cannot initialize a variable of type 'string' with an lvalue of type 'const bool'}} fxc-error {{X3017: cannot implicitly convert from 'const bool' to 'const string'}} */ +static const string s_global4 = true; /* expected-error {{cannot initialize a variable of type 'const string' with an rvalue of type 'bool'}} fxc-error {{X3017: cannot implicitly convert from 'bool' to 'const string'}} */ +static const string s_global5 = b2; /* expected-error {{cannot initialize a variable of type 'const string' with an lvalue of type 'const bool'}} fxc-error {{X3017: cannot implicitly convert from 'const bool' to 'const string'}} */ -static const int i1 = s_global1; /* expected-error {{cannot initialize a variable of type 'const int' with an lvalue of type 'string'}} fxc-error {{X3017: cannot implicitly convert from 'const string' to 'const int'}} */ +static const int i1 = s_global1; /* expected-error {{cannot initialize a variable of type 'const int' with an lvalue of type 'const string'}} fxc-error {{X3017: cannot implicitly convert from 'const string' to 'const int'}} */ static const int i2 = 10; -static const string s_global6 = 10; /* expected-error {{cannot initialize a variable of type 'string' with an rvalue of type 'literal int'}} fxc-error {{X3017: cannot implicitly convert from 'int' to 'const string'}} */ -static const string s_global7 = i2; /* expected-error {{cannot initialize a variable of type 'string' with an lvalue of type 'const int'}} fxc-error {{X3017: cannot implicitly convert from 'const int' to 'const string'}} */ +static const string s_global6 = 10; /* expected-error {{cannot initialize a variable of type 'const string' with an rvalue of type 'literal int'}} fxc-error {{X3017: cannot implicitly convert from 'int' to 'const string'}} */ +static const string s_global7 = i2; /* expected-error {{cannot initialize a variable of type 'const string' with an lvalue of type 'const int'}} fxc-error {{X3017: cannot implicitly convert from 'const int' to 'const string'}} */ -static const float f1 = s_global1; /* expected-error {{cannot initialize a variable of type 'const float' with an lvalue of type 'string'}} fxc-error {{X3017: cannot implicitly convert from 'const string' to 'const float'}} */ +static const float f1 = s_global1; /* expected-error {{cannot initialize a variable of type 'const float' with an lvalue of type 'const string'}} fxc-error {{X3017: cannot implicitly convert from 'const string' to 'const float'}} */ static const float f2 = 3.15; -static const string s_global8= 3.14; /* expected-error {{cannot initialize a variable of type 'string' with an rvalue of type 'literal float'}} fxc-error {{X3017: cannot implicitly convert from 'float' to 'const string'}} */ -static const string s_global9= f2; /* expected-error {{cannot initialize a variable of type 'string' with an lvalue of type 'const float'}} fxc-error {{X3017: cannot implicitly convert from 'const float' to 'const string'}} */ +static const string s_global8= 3.14; /* expected-error {{cannot initialize a variable of type 'const string' with an rvalue of type 'literal float'}} fxc-error {{X3017: cannot implicitly convert from 'float' to 'const string'}} */ +static const string s_global9= f2; /* expected-error {{cannot initialize a variable of type 'const string' with an lvalue of type 'const float'}} fxc-error {{X3017: cannot implicitly convert from 'const float' to 'const string'}} */ static const string s_global10 = { 'A', 'B', 'C' }; /* expected-error {{too many elements in vector initialization (expected 1 element, have 3)}} fxc-error {{X3017: 's_global10': initializer does not match type}} */ static const string s_global11 = { 1, 2, 3 }; /* expected-error {{too many elements in vector initialization (expected 1 element, have 3)}} fxc-error {{X3017: 's_global11': initializer does not match type}} */ static const string s_global12 = { "ABC" }; string g_strArray[5]; /* expected-error {{array of type string is not supported}} fxc-pass {{}} */ -vector g_strVector1; /* expected-error {{'string' cannot be used as a type parameter where a scalar is required}} fxc-error {{X3122: vector element type must be a scalar type}} */ -matrix g_strMatrix1; /* expected-error {{'string' cannot be used as a type parameter where a scalar is required}} fxc-error {{X3123: matrix element type must be a scalar type}} */ +vector g_strVector1; /* expected-error {{'const string' cannot be used as a type parameter where a scalar is required}} fxc-error {{X3122: vector element type must be a scalar type}} */ +matrix g_strMatrix1; /* expected-error {{'const string' cannot be used as a type parameter where a scalar is required}} fxc-error {{X3123: matrix element type must be a scalar type}} */ -void hello_here(string message, string s, float f) { /* expected-error {{parameter of type string is not supported}} expected-error {{parameter of type string is not supported}} fxc-pass {{}} */ +void hello_here(string message, string s, float f) { /* fxc-pass {{}} */ printf(s); printf(message); printf("%f", f); @@ -60,17 +60,17 @@ string get_message() { /* expected-error {{ } struct test { - string field; /* expected-error {{string declaration may only appear in global scope}} fxc-pass {{}} */ + string field; /* fxc-pass {{}} */ }; [shader("pixel")] float4 main() : SV_Target0 { /* */ - string str; /* expected-error {{string declaration may only appear in global scope}} fxc-pass {{}} */ - str = s_global2; /* expected-error {{use of undeclared identifier 'str'}} fxc-pass {{}} */ + string str; /* fxc-pass {{}} */ + str = s_global2; /* fxc-pass {{}} */ string strArray[5]; /* expected-error {{array of type string is not supported}} fxc-pass {{}} */ - vector strVector1; /* expected-error {{'string' cannot be used as a type parameter where a scalar is required}} fxc-error {{X3122: vector element type must be a scalar type}} */ - matrix strMatrix1; /* expected-error {{'string' cannot be used as a type parameter where a scalar is required}} fxc-error {{X3123: matrix element type must be a scalar type}} */ + vector strVector1; /* expected-error {{'const string' cannot be used as a type parameter where a scalar is required}} fxc-error {{X3122: vector element type must be a scalar type}} */ + matrix strMatrix1; /* expected-error {{'const string' cannot be used as a type parameter where a scalar is required}} fxc-error {{X3123: matrix element type must be a scalar type}} */ float4 cp4_local; printf("hi mom", 1, 2, 3); diff --git a/tools/clang/test/SemaHLSL/subobjects-ast-dump.hlsl b/tools/clang/test/SemaHLSL/subobjects-ast-dump.hlsl index ecd155ed41c..9413814b6e1 100644 --- a/tools/clang/test/SemaHLSL/subobjects-ast-dump.hlsl +++ b/tools/clang/test/SemaHLSL/subobjects-ast-dump.hlsl @@ -15,18 +15,18 @@ // ASTIMPL-NEXT: HLSLSubObjectAttr 0x{{.+}} <> Implicit 1 2 // ASTIMPL-NEXT: HLSLNonAutoDeducibleAttr 0x{{.+}} <> Implicit // ASTIMPL-NEXT: FinalAttr 0x{{.+}} <> Implicit final -// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit Data 'string' +// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit Data 'const string' // ASTIMPL-NEXT: CXXRecordDecl 0x{{.+}} <> implicit referenced struct LocalRootSignature definition // ASTIMPL-NEXT: HLSLSubObjectAttr 0x{{.+}} <> Implicit 2 2 // ASTIMPL-NEXT: HLSLNonAutoDeducibleAttr 0x{{.+}} <> Implicit // ASTIMPL-NEXT: FinalAttr 0x{{.+}} <> Implicit final -// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit Data 'string' +// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit Data 'const string' // ASTIMPL-NEXT: CXXRecordDecl 0x{{.+}} <> implicit referenced struct SubobjectToExportsAssociation definition // ASTIMPL-NEXT: HLSLSubObjectAttr 0x{{.+}} <> Implicit 8 2 // ASTIMPL-NEXT: HLSLNonAutoDeducibleAttr 0x{{.+}} <> Implicit // ASTIMPL-NEXT: FinalAttr 0x{{.+}} <> Implicit final -// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit Subobject 'string' -// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit Exports 'string' +// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit Subobject 'const string' +// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit Exports 'const string' // ASTIMPL-NEXT: CXXRecordDecl 0x{{.+}} <> implicit referenced struct RaytracingShaderConfig definition // ASTIMPL-NEXT: HLSLSubObjectAttr 0x{{.+}} <> Implicit 9 2 // ASTIMPL-NEXT: HLSLNonAutoDeducibleAttr 0x{{.+}} <> Implicit @@ -42,15 +42,15 @@ // ASTIMPL-NEXT: HLSLSubObjectAttr 0x{{.+}} <> Implicit 11 0 // ASTIMPL-NEXT: HLSLNonAutoDeducibleAttr 0x{{.+}} <> Implicit // ASTIMPL-NEXT: FinalAttr 0x{{.+}} <> Implicit final -// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit AnyHit 'string' -// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit ClosestHit 'string' +// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit AnyHit 'const string' +// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit ClosestHit 'const string' // ASTIMPL-NEXT: CXXRecordDecl 0x{{.+}} <> implicit referenced struct ProceduralPrimitiveHitGroup definition // ASTIMPL-NEXT: HLSLSubObjectAttr 0x{{.+}} <> Implicit 11 1 // ASTIMPL-NEXT: HLSLNonAutoDeducibleAttr 0x{{.+}} <> Implicit // ASTIMPL-NEXT: FinalAttr 0x{{.+}} <> Implicit final -// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit AnyHit 'string' -// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit ClosestHit 'string' -// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit Intersection 'string' +// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit AnyHit 'const string' +// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit ClosestHit 'const string' +// ASTIMPL-NEXT: FieldDecl 0x{{.+}} <> implicit Intersection 'const string' // ASTIMPL-NEXT: CXXRecordDecl 0x{{.+}} <> implicit referenced struct RaytracingPipelineConfig1 definition // ASTIMPL-NEXT: HLSLSubObjectAttr 0x{{.+}} <> Implicit 12 2 // ASTIMPL-NEXT: HLSLNonAutoDeducibleAttr 0x{{.+}} <> Implicit diff --git a/tools/clang/test/SemaHLSL/subobjects-syntax.hlsl b/tools/clang/test/SemaHLSL/subobjects-syntax.hlsl index 5ab083a064e..f9e1fa5e2d2 100644 --- a/tools/clang/test/SemaHLSL/subobjects-syntax.hlsl +++ b/tools/clang/test/SemaHLSL/subobjects-syntax.hlsl @@ -7,7 +7,7 @@ string localRs = "UAV(u0, visibility = SHADER_VISIBILITY_GEOMETRY)"; GlobalRootSignature grs1_1 = {"CBV(b0)"}; GlobalRootSignature grs1_2 = { globalRs }; -GlobalRootSignature grs2_2 = globalRs; /* expected-error {{cannot initialize a variable of type 'GlobalRootSignature' with an lvalue of type 'string'}} */ +GlobalRootSignature grs2_2 = globalRs; /* expected-error {{cannot initialize a variable of type 'GlobalRootSignature' with an lvalue of type 'const string'}} */ GlobalRootSignature grs2_3 = "CBV(b0)"; /* expected-error {{cannot initialize a variable of type 'GlobalRootSignature' with an lvalue of type 'literal string'}} */ GlobalRootSignature grs2_4 = 10; /* expected-error {{cannot initialize a variable of type 'GlobalRootSignature' with an rvalue of type 'literal int'}} */ GlobalRootSignature grs2_5 = {"CBV(b0)", 78};; /* expected-error {{too many elements in subobject initialization (expected 1 element, have 2)}} */ @@ -29,7 +29,7 @@ StateObjectConfig soc2_7 = 1.5f; /* expected-error {{ LocalRootSignature lrs1_1 = {"UAV(u0, visibility = SHADER_VISIBILITY_GEOMETRY)"}; LocalRootSignature lrs1_2 = { localRs }; -LocalRootSignature lrs2_2 = localRs; /* expected-error {{cannot initialize a variable of type 'LocalRootSignature' with an lvalue of type 'string'}} */ +LocalRootSignature lrs2_2 = localRs; /* expected-error {{cannot initialize a variable of type 'LocalRootSignature' with an lvalue of type 'const string'}} */ LocalRootSignature lrs2_3 = "UAV(u0, visibility = SHADER_VISIBILITY_GEOMETRY)"; /* expected-error {{cannot initialize a variable of type 'LocalRootSignature' with an lvalue of type 'literal string'}} */ LocalRootSignature lrs2_4 = 10; /* expected-error {{cannot initialize a variable of type 'LocalRootSignature' with an rvalue of type 'literal int'}} */ LocalRootSignature lrs2_5 = { 1.7f, "UAV(u0, visibility = SHADER_VISIBILITY_GEOMETRY)" };; /* expected-error {{too many elements in subobject initialization (expected 1 element, have 2)}} */ @@ -83,7 +83,7 @@ TriangleHitGroup trHitGt2_3 = { "a", 10 }; /* expected-error TriangleHitGroup trHitGt2_4 = { 1, 2 }; /* expected-error {{type mismatch}} */ TriangleHitGroup trHitGt2_5 = "foo"; /* expected-error {{cannot initialize a variable of type 'TriangleHitGroup' with an lvalue of type 'literal string'}} */ TriangleHitGroup trHitGt2_6 = 115; /* expected-error {{cannot initialize a variable of type 'TriangleHitGroup' with an rvalue of type 'literal int'}} */ -TriangleHitGroup trHitGt2_7 = s2; /* expected-error {{cannot initialize a variable of type 'TriangleHitGroup' with an lvalue of type 'string'}} */ +TriangleHitGroup trHitGt2_7 = s2; /* expected-error {{cannot initialize a variable of type 'TriangleHitGroup' with an lvalue of type 'const string'}} */ ProceduralPrimitiveHitGroup ppHitGt1_1 = { "a", "b", "c"}; ProceduralPrimitiveHitGroup ppHitGt1_2 = { s1, s2, s3 }; @@ -94,7 +94,7 @@ ProceduralPrimitiveHitGroup ppHitGt2_3 = { "a", "b", 10 }; /* expected-error ProceduralPrimitiveHitGroup ppHitGt2_4 = { 1, 2, 3 }; /* expected-error {{type mismatch}} */ ProceduralPrimitiveHitGroup ppHitGt2_5 = "foo"; /* expected-error {{cannot initialize a variable of type 'ProceduralPrimitiveHitGroup' with an lvalue of type 'literal string'}} */ ProceduralPrimitiveHitGroup ppHitGt2_6 = 115; /* expected-error {{cannot initialize a variable of type 'ProceduralPrimitiveHitGroup' with an rvalue of type 'literal int'}} */ -ProceduralPrimitiveHitGroup ppHitGt2_7 = s2; /* expected-error {{cannot initialize a variable of type 'ProceduralPrimitiveHitGroup' with an lvalue of type 'string'}} */ +ProceduralPrimitiveHitGroup ppHitGt2_7 = s2; /* expected-error {{cannot initialize a variable of type 'ProceduralPrimitiveHitGroup' with an lvalue of type 'const string'}} */ TriangleHitGroup trHitGt2_8 = { s1, s4 }; ProceduralPrimitiveHitGroup ppHitGt2_8 = { s1, "", s4 }; From 13a00750521dc98dfaea47bdc0a77a271f92f9c0 Mon Sep 17 00:00:00 2001 From: Chris B Date: Tue, 10 Nov 2026 14:23:37 -0600 Subject: [PATCH 2/6] Apply batched suggestions from code review Co-authored-by: Chris B --- tools/clang/lib/CodeGen/CGDecl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/clang/lib/CodeGen/CGDecl.cpp b/tools/clang/lib/CodeGen/CGDecl.cpp index 9345bdff3aa..4d59646da82 100644 --- a/tools/clang/lib/CodeGen/CGDecl.cpp +++ b/tools/clang/lib/CodeGen/CGDecl.cpp @@ -15,14 +15,14 @@ #include "CGDebugInfo.h" #include "CGHLSLRuntime.h" // HLSL Change #include "CGOpenCLRuntime.h" -#include "CodeGenFunction.h" +#include "CodeGenFunction.h" // HLSL Change (clang-format) #include "CodeGenModule.h" #include "dxc/DXIL/DxilMetadataHelper.h" // HLSL Change #include "clang/AST/ASTContext.h" #include "clang/AST/CharUnits.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" -#include "clang/AST/HlslTypes.h" +#include "clang/AST/HlslTypes.h" // HLSL Change #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" From 5824d3931ace342cbae96ee49f34aa279dd57d49 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Wed, 11 Nov 2026 12:15:33 -0600 Subject: [PATCH 3/6] Address PR feedback ../tools/clang/test/CodeGenSPIRV/type.string.initializer.hlsl ../tools/clang/test/CodeGenSPIRV/type.string.parameter.hlsl ../tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-ini tializers.hlsl --- tools/clang/lib/CodeGen/CGHLSLMS.cpp | 6 +++++- tools/clang/lib/SPIRV/DeclResultIdMapper.cpp | 1 + tools/clang/lib/Sema/SemaDecl.cpp | 6 ++++++ .../test/CodeGenSPIRV/type.string.initializer.hlsl | 3 ++- .../test/CodeGenSPIRV/type.string.parameter.hlsl | 12 ++++++++++++ .../intrinsics/string-constant-initializers.hlsl | 9 +++++++++ 6 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 tools/clang/test/CodeGenSPIRV/type.string.parameter.hlsl diff --git a/tools/clang/lib/CodeGen/CGHLSLMS.cpp b/tools/clang/lib/CodeGen/CGHLSLMS.cpp index acfcb98eef9..bb72dbd6c49 100644 --- a/tools/clang/lib/CodeGen/CGHLSLMS.cpp +++ b/tools/clang/lib/CodeGen/CGHLSLMS.cpp @@ -6054,7 +6054,11 @@ void CGMSHLSLRuntime::EmitHLSLSplat(CodeGenFunction &CGF, Value *SrcVal, SmallVector &idxList, QualType Type, QualType SrcType, llvm::Type *Ty) { - if (llvm::PointerType *PT = dyn_cast(Ty)) { + if (hlsl::IsStringType(Type) && Ty == CGF.ConvertType(Type)) { + DXASSERT_NOMSG(hlsl::IsStringType(SrcType)); + Value *DestGEP = CGF.Builder.CreateInBoundsGEP(DestPtr, idxList); + CGF.Builder.CreateStore(SrcVal, DestGEP); + } else if (llvm::PointerType *PT = dyn_cast(Ty)) { idxList.emplace_back(CGF.Builder.getInt32(0)); EmitHLSLSplat(CGF, SrcVal, DestPtr, idxList, Type, SrcType, diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp index 525defe7f93..2279d84a1f6 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp @@ -1441,6 +1441,7 @@ DeclResultIdMapper::createOrUpdateStringVar(const VarDecl *var) { emitError( "string variable initializer must be a string literal with -spirv", var->getInit()->getExprLoc()); + registerVariableForDecl(var, createDeclSpirvInfo(spvBuilder.getString(""))); return nullptr; } SpirvString *init = spvBuilder.getString(stringLiteral->getString()); diff --git a/tools/clang/lib/Sema/SemaDecl.cpp b/tools/clang/lib/Sema/SemaDecl.cpp index cf106d6706c..36540ca8ee9 100644 --- a/tools/clang/lib/Sema/SemaDecl.cpp +++ b/tools/clang/lib/Sema/SemaDecl.cpp @@ -10381,6 +10381,12 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D) { // HLSL Change Starts if (getLangOpts().HLSL) { const bool IsParm = true; + if (getLangOpts().SPIRV && hlsl::IsStringType(parmDeclType)) { + static const unsigned selectParamIdx = 1; + Diag(D.getLocStart(), diag::err_hlsl_unsupported_string_decl) + << selectParamIdx; + D.setInvalidType(); + } if (!DiagnoseHLSLDecl(D, Context.getTranslationUnitDecl(), /*BitWidth*/ nullptr, TInfo, IsParm)) { assert(D.isInvalidType() && "otherwise DiagnoseHLSLDecl failed but " "didn't invalidate declaration"); diff --git a/tools/clang/test/CodeGenSPIRV/type.string.initializer.hlsl b/tools/clang/test/CodeGenSPIRV/type.string.initializer.hlsl index b7d3bd2a441..7a826831ef1 100644 --- a/tools/clang/test/CodeGenSPIRV/type.string.initializer.hlsl +++ b/tools/clang/test/CodeGenSPIRV/type.string.initializer.hlsl @@ -1,4 +1,4 @@ -// RUN: not %dxc -T cs_6_0 -E main -fcgl %s -spirv 2>&1 | FileCheck %s +// RUN: not %dxc -T cs_6_0 -E main -fcgl %s -spirv 2>&1 | FileCheck %s --implicit-check-not="fatal error:" string globalText = "text"; @@ -7,4 +7,5 @@ void main() { // CHECK: error: string variable initializer must be a string literal with -spirv // CHECK-NEXT: string local = globalText; string local = globalText; + printf(local); } diff --git a/tools/clang/test/CodeGenSPIRV/type.string.parameter.hlsl b/tools/clang/test/CodeGenSPIRV/type.string.parameter.hlsl new file mode 100644 index 00000000000..7a223addb8c --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/type.string.parameter.hlsl @@ -0,0 +1,12 @@ +// RUN: not %dxc -T cs_6_0 -E main -fcgl %s -spirv 2>&1 | FileCheck %s + +// CHECK: error: parameter of type string is not supported +// CHECK-NEXT: void log(string text) { +void log(string text) { + printf(text); +} + +[numthreads(1, 1, 1)] +void main() { + log("hello"); +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl index 91d416bf09f..d27f9d08683 100644 --- a/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl @@ -36,6 +36,10 @@ // FCGL: [[TEXT:%[^ ]+]] = load i8*, i8** [[TEXT_PTR]] // FCGL: store i8* [[TEXT]], i8** // FCGL: uitofp i32 +// FCGL-LABEL: define internal void {{.*SplatText.*}}(%struct.StringOnly* noalias sret %agg.result, i8* %Text) +// FCGL: [[SPLAT_TEXT:%[^ ]+]] = load i8*, i8** +// FCGL: [[SPLAT_FIELD:%[^ ]+]] = getelementptr inbounds %struct.StringOnly, %struct.StringOnly* {{.*}}, i32 0, i32 0 +// FCGL: store i8* [[SPLAT_TEXT]], i8** [[SPLAT_FIELD]] struct StringOnly { string Text; @@ -91,6 +95,10 @@ FloatStringState ConvertState(StringState State) { return (FloatStringState)State; } +StringOnly SplatText(string Text) { + return (StringOnly)Text; +} + [numthreads(1, 1, 1)] void main() { StringState Local = Copy(GlobalLiteral); @@ -110,4 +118,5 @@ void main() { Output.Store(28, (uint)Converted.Value); FloatNestedState ConvertedNested = (FloatNestedState)Nested; Output.Store(32, (uint)ConvertedNested.Values[0].Value); + Text = SplatText(GlobalText); } From 2119c76a74e9898a526a865c72736c59628b2e97 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Wed, 11 Nov 2026 12:46:58 -0600 Subject: [PATCH 4/6] Fix a few minor diagnostic issues to not produce fatal errors ../tools/clang/test/CodeGenSPIRV/type.string.uninitialized.hlsl ../tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-ini tializers.hlsl --- tools/clang/lib/CodeGen/CGHLSLMS.cpp | 6 ++++++ tools/clang/lib/SPIRV/DeclResultIdMapper.cpp | 1 + .../CodeGenSPIRV/type.string.uninitialized.hlsl | 14 +++++++++++--- .../intrinsics/string-constant-initializers.hlsl | 13 +++++++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/tools/clang/lib/CodeGen/CGHLSLMS.cpp b/tools/clang/lib/CodeGen/CGHLSLMS.cpp index bb72dbd6c49..b0cfeacd335 100644 --- a/tools/clang/lib/CodeGen/CGHLSLMS.cpp +++ b/tools/clang/lib/CodeGen/CGHLSLMS.cpp @@ -6026,6 +6026,12 @@ static void SimpleFlatValCopy(CodeGenFunction &CGF, Value *SrcVal, DXASSERT(DstTy == CGF.ConvertTypeForMem(DstQualTy), "QualType/Type mismatch!"); + if (hlsl::IsStringType(DstQualTy)) { + DXASSERT_NOMSG(hlsl::IsStringType(SrcQualTy)); + CGF.Builder.CreateStore(SrcVal, DstPtr); + return; + } + llvm::VectorType *DstVecTy = dyn_cast(DstTy); QualType DstScalarQualTy = DstQualTy; if (DstVecTy) { diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp index 2279d84a1f6..3b7b7df1627 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp @@ -1432,6 +1432,7 @@ DeclResultIdMapper::createOrUpdateStringVar(const VarDecl *var) { // generate an OpString for it. if (!var->hasInit()) { emitError("Found uninitialized string variable.", var->getLocation()); + registerVariableForDecl(var, createDeclSpirvInfo(spvBuilder.getString(""))); return nullptr; } diff --git a/tools/clang/test/CodeGenSPIRV/type.string.uninitialized.hlsl b/tools/clang/test/CodeGenSPIRV/type.string.uninitialized.hlsl index 0f36693d955..8df09e22b68 100644 --- a/tools/clang/test/CodeGenSPIRV/type.string.uninitialized.hlsl +++ b/tools/clang/test/CodeGenSPIRV/type.string.uninitialized.hlsl @@ -1,8 +1,16 @@ -// RUN: not %dxc -T cs_6_0 -E main -fcgl %s -spirv 2>&1 | FileCheck %s +// RUN: not %dxc -T cs_6_0 -E main -fcgl %s -spirv 2>&1 | FileCheck %s --implicit-check-not="fatal error:" +// RUN: not %dxc -T cs_6_0 -E main -fcgl -DLOCAL_STRING %s -spirv 2>&1 | FileCheck %s --implicit-check-not="fatal error:" -// CHECK: 4:8: error: Found uninitialized string variable. +// CHECK: error: Found uninitialized string variable. +#ifndef LOCAL_STRING string first; +#endif [numthreads(1,1,1)] -void main() {} +void main() { +#ifdef LOCAL_STRING + string local; + printf(local); +#endif +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl index d27f9d08683..211f2920496 100644 --- a/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl @@ -26,6 +26,12 @@ // FCGL-DAG: @OnlyText = internal constant %struct.StringOnly { i8* getelementptr inbounds ([6 x i8], [6 x i8]* [[HELLO]], i32 0, i32 0) } // FCGL-DAG: @GlobalLiteral = internal global %struct.StringState { i8* getelementptr inbounds ([6 x i8], [6 x i8]* [[HELLO]], i32 0, i32 0), i32 3 } // FCGL-DAG: [[HELLO]] = linkonce_odr unnamed_addr constant [6 x i8] c"hello\00" +// FCGL-LABEL: define void @main() +// FCGL: [[COPY_IN:%[^ ]+]] = load i8*, i8** %AliasedText +// FCGL-NEXT: store i8* [[COPY_IN]], i8** [[COPY_TMP:%[^, ]+]] +// FCGL-NEXT: call void {{.*CopyAliasedText.*}}(i8** dereferenceable(4) %AliasedText, i8** dereferenceable(4) [[COPY_TMP]]) +// FCGL-NEXT: [[COPY_OUT:%[^ ]+]] = load i8*, i8** [[COPY_TMP]] +// FCGL-NEXT: store i8* [[COPY_OUT]], i8** %AliasedText // FCGL-LABEL: define internal void {{.*CopyText.*}}(i8* %Text, i8** noalias dereferenceable(4) %Result) // FCGL: [[SLOT:%[^ ]+]] = alloca i8* // FCGL: store i8* %Text, i8** [[SLOT]] @@ -99,8 +105,15 @@ StringOnly SplatText(string Text) { return (StringOnly)Text; } +void CopyAliasedText(inout string First, inout string Second) { + First = "changed"; + Second = First; +} + [numthreads(1, 1, 1)] void main() { + string AliasedText = GlobalText; + CopyAliasedText(AliasedText, AliasedText); StringState Local = Copy(GlobalLiteral); Output.Store(0, Local.Value); Output.Store(4, GlobalReference.Value); From 2b37f582bda87ff61604696b389de4c43278e122 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Fri, 13 Nov 2026 11:18:08 -0600 Subject: [PATCH 5/6] Handle aggregate casts of strings ../tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-ini tializers.hlsl --- tools/clang/lib/Sema/SemaHLSL.cpp | 5 ++++- .../hlsl/intrinsics/string-constant-initializers.hlsl | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index cd2c0a5992c..ed9aa6b51d4 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -10481,7 +10481,10 @@ bool HLSLExternalSource::CanConvert(SourceLocation loc, Expr *sourceExpr, lSuccess: if (standard) { if (sourceExpr->isLValue()) { - if (needsLValueToRValue) { + if (Second == ICK_Flat_Conversion && hlsl::IsStringLiteralType(source)) { + // Aggregate casts must flatten the string pointer, not its characters. + standard->First = ICK_Array_To_Pointer; + } else if (needsLValueToRValue) { // We don't need LValueToRValue cast before casting a derived object // to its base. if (Second == ICK_HLSL_Derived_To_Base) { diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl index 211f2920496..02e0451ba27 100644 --- a/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/string-constant-initializers.hlsl @@ -32,6 +32,12 @@ // FCGL-NEXT: call void {{.*CopyAliasedText.*}}(i8** dereferenceable(4) %AliasedText, i8** dereferenceable(4) [[COPY_TMP]]) // FCGL-NEXT: [[COPY_OUT:%[^ ]+]] = load i8*, i8** [[COPY_TMP]] // FCGL-NEXT: store i8* [[COPY_OUT]], i8** %AliasedText +// FCGL: [[LITERAL_FIELD:%[^ ]+]] = getelementptr inbounds %struct.StringOnly, %struct.StringOnly* %LiteralText, i32 0, i32 0 +// FCGL-NEXT: store i8* getelementptr inbounds ([6 x i8], [6 x i8]* [[HELLO]], i32 0, i32 0), i8** [[LITERAL_FIELD]] +// FCGL: [[EMPTY_FIELD:%[^ ]+]] = getelementptr inbounds %struct.StringOnly, %struct.StringOnly* %EmptyLiteralText, i32 0, i32 0 +// FCGL-NEXT: store i8* getelementptr inbounds ([1 x i8], [1 x i8]* {{@"[^"]+"}}, i32 0, i32 0), i8** [[EMPTY_FIELD]] +// FCGL: [[PAREN_FIELD:%[^ ]+]] = getelementptr inbounds %struct.StringOnly, %struct.StringOnly* %ParenLiteralText, i32 0, i32 0 +// FCGL-NEXT: store i8* getelementptr inbounds ([6 x i8], [6 x i8]* [[HELLO]], i32 0, i32 0), i8** [[PAREN_FIELD]] // FCGL-LABEL: define internal void {{.*CopyText.*}}(i8* %Text, i8** noalias dereferenceable(4) %Result) // FCGL: [[SLOT:%[^ ]+]] = alloca i8* // FCGL: store i8* %Text, i8** [[SLOT]] @@ -132,4 +138,7 @@ void main() { FloatNestedState ConvertedNested = (FloatNestedState)Nested; Output.Store(32, (uint)ConvertedNested.Values[0].Value); Text = SplatText(GlobalText); + StringOnly LiteralText = (StringOnly)"hello"; + StringOnly EmptyLiteralText = (StringOnly)""; + StringOnly ParenLiteralText = (StringOnly)("he" "llo"); } From 6acc6579813595acf90f7cd660f592b62f53283c Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Fri, 13 Nov 2026 13:26:04 -0600 Subject: [PATCH 6/6] Fixes based on review feedback Disallow strings as fields when targeting spirv Fix up handling of typedefs ../tools/clang/include/clang/Basic/DiagnosticSemaKinds.td ../tools/clang/test/CodeGenSPIRV/type.string.field.hlsl ../tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/structured-buffer-f orward-declaration.hlsl --- .../clang/Basic/DiagnosticSemaKinds.td | 2 +- tools/clang/lib/Sema/SemaDecl.cpp | 10 +++++++++ tools/clang/lib/Sema/SemaHLSL.cpp | 21 +++++++++++++++---- .../test/CodeGenSPIRV/type.string.field.hlsl | 17 +++++++++++++++ ...structured-buffer-forward-declaration.hlsl | 20 ++++++++++++++++++ tools/clang/test/SemaHLSL/string-storage.hlsl | 13 ++++++++++++ 6 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 tools/clang/test/CodeGenSPIRV/type.string.field.hlsl create mode 100644 tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/structured-buffer-forward-declaration.hlsl diff --git a/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td b/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td index 90323226fe7..5fd7adc46fd 100644 --- a/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7908,7 +7908,7 @@ def err_hlsl_missing_attr: Error< def err_hlsl_missing_inout_attr: Error< "stream-output object must be an inout parameter">; def err_hlsl_unsupported_string_decl: Error< - "%select{array|parameter|return value}0 of type string is not supported">; + "%select{array|parameter|return value|field}0 of type string is not supported">; def err_hlsl_missing_out_attr: Error< "%0 object must be an out parameter">; def err_hlsl_missing_in_attr: Error< diff --git a/tools/clang/lib/Sema/SemaDecl.cpp b/tools/clang/lib/Sema/SemaDecl.cpp index 36540ca8ee9..8f06c3aa608 100644 --- a/tools/clang/lib/Sema/SemaDecl.cpp +++ b/tools/clang/lib/Sema/SemaDecl.cpp @@ -12920,6 +12920,16 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, T = Context.IntTy; } + // HLSL Change Starts + if (!InvalidDecl && getLangOpts().HLSL && getLangOpts().SPIRV && + hlsl::IsStringType(T)) { + static const unsigned selectFieldIdx = 3; + Diag(Loc, diag::err_hlsl_unsupported_string_decl) << selectFieldIdx; + Record->setInvalidDecl(); + InvalidDecl = true; + } + // HLSL Change Ends + QualType EltTy = Context.getBaseElementType(T); if (!EltTy->isDependentType()) { if (RequireCompleteType(Loc, EltTy, diag::err_field_incomplete)) { diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index ed9aa6b51d4..c14c9f47c2c 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -5925,10 +5925,10 @@ class HLSLExternalSource : public ExternalSemaSource { if (Arg.getKind() == TemplateArgument::ArgKind::Type) { QualType ArgType = Arg.getAsType(); SourceLocation ArgSrcLoc = ArgLoc.getLocation(); - if (!ArgType->isDependentType() && - m_sema->RequireCompleteType( - ArgSrcLoc, ArgType, diag::err_typecheck_decl_incomplete_type)) - return true; + // Instantiate available definitions without rejecting forward + // declarations used in resource type aliases. + if (!ArgType->isDependentType()) + m_sema->RequireCompleteType(ArgSrcLoc, ArgType, 0); if (DiagnoseTypeElements( *m_sema, ArgSrcLoc, ArgType, TypeDiagContext::StructuredBuffers /*ObjDiagContext*/, @@ -16211,6 +16211,19 @@ bool Sema::DiagnoseHLSLDecl(Declarator &D, DeclContext *DC, Expr *BitWidth, result = false; } + // Recheck element types when a resource alias is used after its forward- + // declared element type has been defined. + if (!isTypedef && hlsl::IsHLSLStructuredBufferType(qt)) { + QualType ElementTy = hlsl::GetHLSLResourceResultType(qt); + if (!ElementTy->isDependentType() && + !RequireCompleteType(D.getLocStart(), ElementTy, 0) && + DiagnoseTypeElements(*this, D.getLocStart(), ElementTy, + TypeDiagContext::StructuredBuffers, + TypeDiagContext::Valid, nullptr, + hlsl::GetHLSLResourceClass(qt))) + result = false; + } + // Disallow intangible HLSL objects in externally visible storage. if (isGlobal || (isParameter && isGroupShared)) { // Suppress actual emitting of errors for incompletable types here diff --git a/tools/clang/test/CodeGenSPIRV/type.string.field.hlsl b/tools/clang/test/CodeGenSPIRV/type.string.field.hlsl new file mode 100644 index 00000000000..d9aa4132e84 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/type.string.field.hlsl @@ -0,0 +1,17 @@ +// RUN: %dxc -T cs_6_0 -E main -HV 2021 -spirv -verify %s +// RUN: %dxc -T cs_6_0 -E main -HV 202x -spirv -verify %s + +struct StringState { + string Text; // expected-error {{field of type string is not supported}} +}; + +template +struct Wrapper { + T Text; // expected-error {{field of type string is not supported}} +}; + +[numthreads(1, 1, 1)] +void main() { + // expected-note@+1 {{in instantiation of template class 'Wrapper' requested here}} + Wrapper Wrapped; +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/structured-buffer-forward-declaration.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/structured-buffer-forward-declaration.hlsl new file mode 100644 index 00000000000..70f2d858bc9 --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/structured-buffer-forward-declaration.hlsl @@ -0,0 +1,20 @@ +// RUN: %dxc -T cs_6_0 -E main -HV 2021 %s | FileCheck %s +// RUN: %dxc -T cs_6_0 -E main -HV 202x %s | FileCheck %s + +struct Element; +typedef StructuredBuffer InputBuffer; +typedef RWStructuredBuffer OutputBuffer; + +struct Element { + uint Value; +}; + +InputBuffer Input; +OutputBuffer Output; + +// CHECK: call %dx.types.ResRet.i32 @dx.op.bufferLoad.i32 +// CHECK: call void @dx.op.bufferStore.i32 +[numthreads(1, 1, 1)] +void main() { + Output[0] = Input[0]; +} diff --git a/tools/clang/test/SemaHLSL/string-storage.hlsl b/tools/clang/test/SemaHLSL/string-storage.hlsl index 1c247e47832..6b7d07e98b3 100644 --- a/tools/clang/test/SemaHLSL/string-storage.hlsl +++ b/tools/clang/test/SemaHLSL/string-storage.hlsl @@ -41,6 +41,19 @@ StructuredBuffer DirectSB; // expected-error@+1 {{object 'const string' is not allowed in structured buffers}} RWStructuredBuffer > Wrapped; +struct ForwardStringState; +typedef StructuredBuffer ForwardStringBuffer; +typedef RWStructuredBuffer ForwardRWStringBuffer; + +struct ForwardStringState { + string Text; // expected-note 2 {{field declared here}} +}; + +// expected-error@+1 {{object 'const string' is not allowed in structured buffers}} +ForwardStringBuffer ForwardSB; +// expected-error@+1 {{object 'const string' is not allowed in structured buffers}} +ForwardRWStringBuffer ForwardRWSB; + // expected-error@+1 {{'const string' cannot be used as a type parameter}} Buffer DirectBuffer; // expected-error@+2 {{'const string' cannot be used as a type parameter}}