diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 8b862ae47af89..eef9414668809 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13223,6 +13223,8 @@ def note_hlsl_resource_range_here: Note<"overlapping resource range here">; def err_hlsl_incomplete_resource_array_in_function_param: Error< "incomplete resource array in a function parameter">; +def err_hlsl_assign_to_global_resource: Error< + "assignment to global resource variable %0 is not allowed">; // Layout randomization diagnostics. def err_non_designated_init_used : Error< diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 4df330ed87120..46b088c0174b0 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -133,6 +133,8 @@ class SemaHLSL : public SemaBase { bool isSemanticValid(FunctionDecl *FD, DeclaratorDecl *D); void CheckSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param, const HLSLAnnotationAttr *AnnotationAttr); + bool CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr, + SourceLocation Loc); void DiagnoseAttrStageMismatch( const Attr *A, llvm::Triple::EnvironmentType Stage, std::initializer_list AllowedStages); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 03def26fe53bd..3b267c1b1693d 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -15711,6 +15711,12 @@ ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, RHSExpr = resolvedRHS.get(); } + if (getLangOpts().HLSL && (LHSExpr->getType()->isHLSLResourceRecord() || + LHSExpr->getType()->isHLSLResourceRecordArray())) { + if (!HLSL().CheckResourceBinOp(Opc, LHSExpr, RHSExpr, OpLoc)) + return ExprError(); + } + if (getLangOpts().CPlusPlus) { // Otherwise, build an overloaded op if either expression is type-dependent // or has an overloadable type. diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 55be036207eec..305691dcf4f6c 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -3956,6 +3956,34 @@ bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) { return false; } +// Return true if everything is ok; returns false if there was an error. +bool SemaHLSL::CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, + Expr *RHSExpr, SourceLocation Loc) { + assert((LHSExpr->getType()->isHLSLResourceRecord() || + LHSExpr->getType()->isHLSLResourceRecordArray()) && + "expected LHS to be a resource record or array of resource records"); + if (Opc != BO_Assign) + return true; + + // If LHS is an array subscript, get the underlying declaration. + Expr *E = LHSExpr; + while (auto *ASE = dyn_cast(E)) + E = ASE->getBase()->IgnoreParenImpCasts(); + + // Report error if LHS is a resource declared at a global scope. + if (DeclRefExpr *DRE = dyn_cast(E->IgnoreParens())) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) { + if (VD->hasGlobalStorage()) { + // assignment to global resource is not allowed + SemaRef.Diag(Loc, diag::err_hlsl_assign_to_global_resource) << VD; + SemaRef.Diag(VD->getLocation(), diag::note_var_declared_here) << VD; + return false; + } + } + } + return true; +} + // Walks though the global variable declaration, collects all resource binding // requirements and adds them to Bindings void SemaHLSL::collectResourceBindingsOnVarDecl(VarDecl *VD) { diff --git a/clang/test/CodeGenHLSL/static-local-ctor.hlsl b/clang/test/CodeGenHLSL/static-local-ctor.hlsl index bb575d23216b0..fd92e413bb3fa 100644 --- a/clang/test/CodeGenHLSL/static-local-ctor.hlsl +++ b/clang/test/CodeGenHLSL/static-local-ctor.hlsl @@ -8,6 +8,9 @@ RWBuffer buf[10]; +// CHECK: @[[main_mybuf:.*]] = internal global %"class.hlsl::RWBuffer" zeroinitializer, align 4 +// CHECK: @[[main_mybuf_guard:.*]] = internal global i8 0, align 1 + void InitBuf(RWBuffer buf) { for (unsigned int i = 0; i < 100; i++) buf[i] = 0; @@ -24,15 +27,14 @@ void InitBuf(RWBuffer buf) { // CHECK-NOT: _Init_thread_header // CHECK: init.check: // CHECK-NEXT: call void @hlsl::RWBuffer::__createFromImplicitBinding +// CHECK-NEXT: call void @hlsl::RWBuffer::RWBuffer(hlsl::RWBuffer const&)(ptr {{.*}} @main()::mybuf, ptr {{.*}}) # // CHECK-NEXT: store i8 1, ptr @guard variable for main()::mybuf // CHECK-NOT: _Init_thread_footer - [shader("compute")] [numthreads(1,1,1)] void main() { // A non-trivially constructed static local will get checks to verify that it is generated just once - static RWBuffer mybuf; - mybuf = buf[0]; + static RWBuffer mybuf = buf[0]; InitBuf(mybuf); } diff --git a/clang/test/SemaHLSL/prohibit_resource_edits.hlsl b/clang/test/SemaHLSL/prohibit_resource_edits.hlsl new file mode 100644 index 0000000000000..caffa32fd8085 --- /dev/null +++ b/clang/test/SemaHLSL/prohibit_resource_edits.hlsl @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -o - -fsyntax-only %s -verify + +RWBuffer A[10]; // expected-note {{variable 'A' is declared here}} expected-note {{variable 'A' is declared here}} // expected-note {{variable 'A' is declared here}} +RWBuffer B; // expected-note {{variable 'B' is declared here}} +RWBuffer C[10]; + +void test() { + // expected-error@+1{{assignment to global resource variable 'A' is not allowed}} + A = C; + + // expected-error@+1{{assignment to global resource variable 'B' is not allowed}} + B = C[0]; + + // expected-error@+1{{assignment to global resource variable 'A' is not allowed}} + A[1] = B; + + // expected-error@+1{{assignment to global resource variable 'A' is not allowed}} + A[1] = C[2]; + + // local resources are assignable + RWBuffer LocalA[10] = A; // ok + RWBuffer LocalB = B; // ok + + // read-write resources can be written to + A[0][0] = 1.0f; // ok + B[0] = 2.0f; // ok +}