Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ part of the next non-preview release.
- Fixed undefined behavior in DXC IntelliSense caused by the uninitialized
`ExpandTokPastingArg` preprocessor option, which made token-pasting
behavior indeterminate.
- Numeric global and constant buffer variables whose type is written as a
template-id (for example `W<int>`, `vector<float, 4>`, or
`matrix<float, 2, 2>`) are now implicitly `const` like other numeric globals;
assigning to them is now a compile error instead of a compiler crash
[#8966](https://github.com/microsoft/DirectXShaderCompiler/issues/8966).

#### HLSL Language

Expand Down
19 changes: 13 additions & 6 deletions tools/clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2351,12 +2351,19 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
if (!isGroupShared) {
// check whether or not the given data is the typename or primitive types
if (DS.isTypeRep()) {
QualType type = DS.getRepAsType().get();
// canonical types of HLSL Object types are not canonical for some
// reason. other HLSL Object types of vector/matrix/array should be
// treated as const.
if (type.getCanonicalType().isCanonical() &&
IsTypeNumeric(&Actions, type)) {
// Parsed types such as template-ids are wrapped in a LocInfoType, so
// unwrap them before inspecting the type.
QualType QT = Actions.GetTypeFromParser(DS.getRepAsType());
// Instantiate user class template specializations so their fields are
// visible to IsTypeNumeric. Built-in HLSL templates are classified
// without needing instantiation.
if (const auto *Spec =
dyn_cast_or_null<ClassTemplateSpecializationDecl>(
QT.isNull() ? nullptr : QT->getAsCXXRecordDecl()))
if (!Spec->hasDefinition() &&
!Spec->getSpecializedTemplate()->isImplicit())
Actions.RequireCompleteType(DS.getTypeSpecTypeLoc(), QT, 0);
if (!QT.isNull() && IsTypeNumeric(&Actions, QT)) {
unsigned int diagID;
const char *prevSpec;
DS.SetTypeQual(DeclSpec::TQ_const, D.getDeclSpec().getLocStart(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ namespace ns_with_struct {
int i;
};
}
matrix<int, 1, 2> g_matrix_simple;
const matrix<int, 1, 2> g_matrix_simple;
int global_fn() {
return 1;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Rewrite unchanged result:
Texture2D<float4> t_float4;
matrix<bool, 1, 2> m_bool;
const matrix<bool, 1, 2> m_bool;
struct s_float2_float3 {
float2 f_float2;
float3 f_float3;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ snorm float2 const g_vector_sno;
unorm float2 const g_vector_uno;
snorm float const g_scalar_sno;
unorm float const g_scalar_uno;
matrix<snorm float, 2, 3> g_mat_sno;
matrix<unorm float, 2, 3> g_mat_uno;
row_major matrix<snorm float, 2, 3> g_row_mat_sno;
row_major matrix<unorm float, 2, 3> g_row_mat_uno;
column_major matrix<snorm float, 2, 3> g_col_mat_sno;
column_major matrix<unorm float, 2, 3> g_col_mat_uno;
const matrix<snorm float, 2, 3> g_mat_sno;
const matrix<unorm float, 2, 3> g_mat_uno;
row_major matrix<snorm float, 2, 3> const g_row_mat_sno;
row_major matrix<unorm float, 2, 3> const g_row_mat_uno;
column_major matrix<snorm float, 2, 3> const g_col_mat_sno;
column_major matrix<unorm float, 2, 3> const g_col_mat_uno;
static row_major float2x3 const sc_row_init_scalar = 1.F;
static row_major snorm float2x3 const sc_row_sno_init_scalar = 1.F;
static row_major unorm float2x3 const sc_row_uno_init_scalar = 1.F;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ struct test {
template<typename T=float>
using test2 = test<T, 1>;

// CHECK: VarDecl 0x[[t22:[0-9a-f]+]] {{.*}} used t22 'test2':'test<float, 1>'
// CHECK: VarDecl 0x[[t22:[0-9a-f]+]] {{.*}} used t22 'const test2':'const test<float, 1>'
test2 t22;

// CHECK: ClassTemplateDecl 0x{{.*}} test3
Expand All @@ -47,20 +47,20 @@ struct test3 {
T d;
};

// CHECK: VarDecl 0x[[tt:[0-9a-f]+]] {{.*}} used tt 'test3<test2>':'test3<test<float, 1> >'
// CHECK: VarDecl 0x[[tt:[0-9a-f]+]] {{.*}} used tt 'const test3<test2>':'const test3<test<float, 1> >'
test3<test2> tt;

// CHECK: FunctionDecl 0x{{.*}} foo 'float ()'
// CHECK-NEXT: CompoundStmt 0x{{.*}}
// CHECK-NEXT: ReturnStmt 0x{{.*}}
// CHECK-NEXT: BinaryOperator 0x{{.*}} 'float':'float' '+'
// CHECK-NEXT: ImplicitCastExpr 0x{{.*}} 'float':'float' <LValueToRValue>
// CHECK-NEXT: MemberExpr 0x{{.*}} 'float':'float' lvalue .d 0x[[field_d]]
// CHECK-NEXT: MemberExpr 0x{{.*}} <col:10, col:13> 'test<float, 1>':'test<float, 1>' lvalue .d 0x[[test3_d]]
// CHECK-NEXT: DeclRefExpr 0x{{.*}} <col:10> 'test3<test2>':'test3<test<float, 1> >' lvalue Var 0x[[tt]] 'tt' 'test3<test2>':'test3<test<float, 1> >'
// CHECK-NEXT: MemberExpr 0x{{.*}} 'const float':'const float' lvalue .d 0x[[field_d]]
// CHECK-NEXT: MemberExpr 0x{{.*}} <col:10, col:13> 'const test<float, 1>':'const test<float, 1>' lvalue .d 0x[[test3_d]]
// CHECK-NEXT: DeclRefExpr 0x{{.*}} <col:10> 'const test3<test2>':'const test3<test<float, 1> >' lvalue Var 0x[[tt]] 'tt' 'const test3<test2>':'const test3<test<float, 1> >'
// CHECK-NEXT: ImplicitCastExpr 0x{{.*}} 'float':'float' <LValueToRValue>
// CHECK-NEXT: MemberExpr 0x{{.*}} 'float':'float' lvalue .d 0x[[field_d]]
// CHECK-NEXT: DeclRefExpr 0x{{.*}} 'test2':'test<float, 1>' lvalue Var 0x[[t22]] 't22' 'test2':'test<float, 1>'
// CHECK-NEXT: MemberExpr 0x{{.*}} 'const float':'const float' lvalue .d 0x[[field_d]]
// CHECK-NEXT: DeclRefExpr 0x{{.*}} 'const test2':'const test<float, 1>' lvalue Var 0x[[t22]] 't22' 'const test2':'const test<float, 1>'
// CHECK-NEXT: HLSLExportAttr 0x{{.*}}
export
float foo() {
Expand Down
54 changes: 54 additions & 0 deletions tools/clang/test/SemaHLSL/const-default-templates.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// RUN: %dxc -T ps_6_0 -HV 2021 -verify %s
// RUN: %dxc -T ps_6_0 -HV 202x -verify %s

// Globals and cbuffer members are implicitly const. Verify this also applies
// when the type is spelled as a template-id (including the built-in vector and
// matrix templates), and when a class template specialization has not been
// instantiated before the declaration.

template <typename T> struct W { T v; };
template <typename T> struct R { T res; float f; };

typedef W<uint> WU; // Not instantiated until a variable is declared.

W<int> g_w; // expected-note {{variable 'g_w' declared const here}}
WU g_wu; // expected-note {{variable 'g_wu' declared const here}}
W<W<float> > g_nested; // expected-note {{variable 'g_nested' declared const here}}
W<float4> g_arr[2];
vector<float, 4> g_vec; // expected-note {{variable 'g_vec' declared const here}}
matrix<float, 2, 2> g_mat; // expected-note {{variable 'g_mat' declared const here}}

// Types containing resources are not implicitly const, but must still be
// usable.
R<Texture2D<float4> > g_res;
Texture2D<float4> g_tex;

cbuffer CB {
W<int> cb_w; // expected-note {{variable 'cb_w' declared const here}}
};

tbuffer TB {
W<int> tb_w; // expected-note {{variable 'tb_w' declared const here}}
};

static W<int> s_w;
groupshared W<int> gs_w;

float4 main() : SV_Target {
g_w.v = 1; // expected-error {{cannot assign to variable 'g_w' with const-qualified type 'const W<int>'}}
g_wu.v = 1; // expected-error {{cannot assign to variable 'g_wu' with const-qualified type 'const WU'}}
g_nested.v.v = 1; // expected-error {{cannot assign to variable 'g_nested' with const-qualified type 'const W<W<float> >'}}
g_arr[0].v = 1; // expected-error {{read-only variable is not assignable}}
g_vec = 1; // expected-error {{cannot assign to variable 'g_vec' with const-qualified type 'const vector<float, 4>'}}
g_mat = 1; // expected-error {{cannot assign to variable 'g_mat' with const-qualified type 'const matrix<float, 2, 2>'}}
cb_w.v = 1; // expected-error {{cannot assign to variable 'cb_w' with const-qualified type 'const W<int>'}}
tb_w.v = 1; // expected-error {{cannot assign to variable 'tb_w' with const-qualified type 'const W<int>'}}

// Not implicitly const.
s_w.v = 1;
gs_w.v = 1;

return g_tex.Load(0) + g_res.res.Load(0) + g_w.v + g_wu.v + g_nested.v.v +
g_arr[0].v + g_vec + g_mat[0].xyxy + cb_w.v + tb_w.v + s_w.v +
gs_w.v;
}
33 changes: 33 additions & 0 deletions tools/clang/test/SemaHLSL/hlsl/linalg/linalg-matrix-global.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// REQUIRES: dxil-1-10
// RUN: %dxc -T lib_6_10 -verify %s
// RUN: %dxc -T lib_6_10 -verify -ast-dump %s | FileCheck %s

// Global declarations of types containing LinAlg matrices must not be
// classified as numeric when determining whether a global is implicitly const.

#include <dx/linalg.h>
using namespace dx::linalg;

// expected-error@+1 {{global variable 'g_mat' containing a linear algebra matrix must be declared 'static'}}
Matrix<ComponentType::F16, 4, 4, MatrixUse::A, MatrixScope::Thread> g_mat;

struct S {
__builtin_LinAlgMatrix
[[__LinAlgMatrix_Attributes(ComponentType::F16, 4, 4, MatrixUse::A,
MatrixScope::Thread)]] handle;
};

// expected-error@+1 {{global variable 'g_s' containing a linear algebra matrix must be declared 'static'}}
S g_s;

[shader("compute")]
[numthreads(1, 1, 1)]
void main() {
Matrix<ComponentType::F16, 4, 4, MatrixUse::A, MatrixScope::Thread> local_mat;
S local_s;
}

// Invalid globals are omitted from the AST after their required diagnostics;
// check the corresponding local types here to ensure they remain unqualified.
// CHECK: VarDecl {{.*}} local_mat 'Matrix<ComponentType::F16, 4, 4, MatrixUse::A, MatrixScope::Thread>':
// CHECK: VarDecl {{.*}} local_s 'S'
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ template<typename T>
struct Inner { T val; };

// Nested template type using >> without space: no error in HLSL 202x.
// CHECK: VarDecl {{.*}} g_nested 'Outer<Inner<float> >'
// CHECK: VarDecl {{.*}} g_nested 'const Outer<Inner<float> >'
Outer<Inner<float>> g_nested;

template<typename T>
Expand All @@ -35,5 +35,5 @@ void takeVal(Outer<Inner<int>> v);
template<typename T, typename U>
struct Pair { T first; U second; };

// CHECK: VarDecl {{.*}} g_triple 'Outer<Pair<float, Inner<int> > >'
// CHECK: VarDecl {{.*}} g_triple 'const Outer<Pair<float, Inner<int> > >'
Outer<Pair<float, Inner<int>>> g_triple;
Loading