Skip to content

Commit 7284bb1

Browse files
authored
Move pointer/reference rejection to Sema (#8436)
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).
1 parent 1e4181c commit 7284bb1

13 files changed

Lines changed: 231 additions & 63 deletions

tools/clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -994,8 +994,6 @@ def err_hlsl_unsupported_member_default : Error<
994994
"struct/class members cannot have default values">;
995995
def err_hlsl_unsupported_packoffset_component : Error<
996996
"packoffset component should indicate offset with one of x, y, z, w, r, g, b, or a">;
997-
def err_hlsl_unsupported_pointer : Error<
998-
"pointers are unsupported in HLSL">;
999997
def err_hlsl_unsupported_register_number : Error<
1000998
"register number should be an integral numeric string">;
1001999
def err_hlsl_unsupported_register_noninteger : Error<

tools/clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8061,6 +8061,9 @@ def err_hlsl_linalg_attributed_matrix_required
80618061
def err_hlsl_linalg_unsupported_stage : Error<
80628062
"builtin unavailable in shader stage '%0' (requires 'compute', 'mesh' or 'amplification')">;
80638063

8064+
def err_hlsl_pointers_unsupported : Error<
8065+
"%select{pointers|references}0 are unsupported in HLSL">;
8066+
80648067
// HLSL Change Ends
80658068

80668069
// SPIRV Change Starts

tools/clang/lib/Parse/ParseDecl.cpp

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5887,15 +5887,6 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
58875887
return;
58885888
}
58895889

5890-
// HLSL Change Starts - No pointer support in HLSL.
5891-
if (getLangOpts().HLSL) {
5892-
Diag(Tok, diag::err_hlsl_unsupported_pointer);
5893-
D.SetIdentifier(0, Tok.getLocation());
5894-
D.setInvalidType();
5895-
return;
5896-
}
5897-
// HLSL Change Ends
5898-
58995890
SourceLocation Loc = ConsumeToken();
59005891
D.SetRangeEnd(Loc);
59015892
DeclSpec DS(AttrFactory);
@@ -5917,12 +5908,6 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
59175908

59185909
tok::TokenKind Kind = Tok.getKind();
59195910

5920-
// HLSL Change Starts - HLSL doesn't support pointers, references or blocks
5921-
if (getLangOpts().HLSL && isPtrOperatorToken(Kind, getLangOpts(), D.getContext())) {
5922-
Diag(Tok, diag::err_hlsl_unsupported_pointer);
5923-
}
5924-
// HLSL Change Ends
5925-
59265911
// Not a pointer, C++ reference, or block.
59275912
if (!isPtrOperatorToken(Kind, getLangOpts(), D.getContext())) {
59285913
if (DirectDeclParser)

tools/clang/lib/Sema/SemaHLSL.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15474,6 +15474,24 @@ bool Sema::DiagnoseHLSLDecl(Declarator &D, DeclContext *DC, Expr *BitWidth,
1547415474
if (!isFunction)
1547515475
hlslSource->WarnMinPrecision(qt, D.getLocStart());
1547615476

15477+
// HLSL Change Starts - disallow pointers through __decltype.
15478+
if (!D.isInvalidType() && pType && !qt->isDependentType()) {
15479+
if (const auto *DTT = dyn_cast<DecltypeType>(pType)) {
15480+
QualType Underlying = DTT->getUnderlyingType();
15481+
if (Underlying->isPointerType()) {
15482+
Diag(D.getLocStart(), diag::err_hlsl_pointers_unsupported) << 0;
15483+
D.setInvalidType();
15484+
return false;
15485+
}
15486+
if (Underlying->isReferenceType()) {
15487+
Diag(D.getLocStart(), diag::err_hlsl_pointers_unsupported) << 1;
15488+
D.setInvalidType();
15489+
return false;
15490+
}
15491+
}
15492+
}
15493+
// HLSL Change Ends
15494+
1547715495
// Early checks - these are not simple attribution errors, but constructs that
1547815496
// are fundamentally unsupported,
1547915497
// and so we avoid errors that might indicate they can be repaired.

tools/clang/lib/Sema/SemaTemplate.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,8 +421,14 @@ Sema::ActOnDependentIdExpression(const CXXScopeSpec &SS,
421421
NamedDecl *FirstQualifierInScope = nullptr;
422422

423423
// HLSL Change begin - This is a reference.
424+
// In HLSL, 'this' is an lvalue reference (not a pointer), so implicit
425+
// member accesses use '.' (IsArrow=false). The base type must be the
426+
// class type T, not the pointer type T*.
427+
QualType MemberBaseType =
428+
getLangOpts().HLSL ? ThisType->getPointeeType() : ThisType;
424429
return CXXDependentScopeMemberExpr::Create(
425-
Context, /*This*/ nullptr, ThisType, /*IsArrow*/ !getLangOpts().HLSL,
430+
Context, /*This*/ nullptr, MemberBaseType,
431+
/*IsArrow*/ !getLangOpts().HLSL,
426432
/*Op*/ SourceLocation(), SS.getWithLocInContext(Context), TemplateKWLoc,
427433
FirstQualifierInScope, NameInfo, TemplateArgs);
428434
// HLSL Change end - This is a reference.

tools/clang/lib/Sema/SemaType.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1849,6 +1849,13 @@ QualType Sema::BuildPointerType(QualType T,
18491849
return QualType();
18501850
}
18511851

1852+
// HLSL Change Begin - Disallow pointers.
1853+
if (getLangOpts().HLSL && Loc.isValid()) {
1854+
Diag(Loc, diag::err_hlsl_pointers_unsupported) << 0;
1855+
return QualType();
1856+
}
1857+
// HLSL Change End.
1858+
18521859
if (checkQualifiedFunction(*this, T, Loc, QFK_Pointer))
18531860
return QualType();
18541861

@@ -1911,6 +1918,13 @@ QualType Sema::BuildReferenceType(QualType T, bool SpelledAsLValue,
19111918
return QualType();
19121919
}
19131920

1921+
// HLSL Change Begin - Disallow references.
1922+
if (getLangOpts().HLSL && Loc.isValid()) {
1923+
Diag(Loc, diag::err_hlsl_pointers_unsupported) << 1;
1924+
return QualType();
1925+
}
1926+
// HLSL Change End.
1927+
19141928
if (checkQualifiedFunction(*this, T, Loc, QFK_Reference))
19151929
return QualType();
19161930

@@ -2313,6 +2327,13 @@ QualType Sema::BuildMemberPointerType(QualType T, QualType Class,
23132327
return QualType();
23142328
}
23152329

2330+
// HLSL Change Begin - Disallow pointers.
2331+
if (getLangOpts().HLSL && Loc.isValid()) {
2332+
Diag(Loc, diag::err_hlsl_pointers_unsupported) << 0;
2333+
return QualType();
2334+
}
2335+
// HLSL Change End.
2336+
23162337
// Adjust the default free function calling convention to the default method
23172338
// calling convention.
23182339
if (T->isFunctionType())

tools/clang/lib/Sema/TreeTransform.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9852,7 +9852,12 @@ TreeTransform<Derived>::TransformCXXDependentScopeMemberExpr(
98529852
} else {
98539853
OldBase = nullptr;
98549854
BaseType = getDerived().TransformType(E->getBaseType());
9855-
ObjectType = BaseType->getPointeeType();
9855+
// HLSL Change - Begin
9856+
// In HLSL, 'this' is an lvalue reference so implicit member accesses use
9857+
// '.' (IsArrow=false) and the stored base type IS the object type, not a
9858+
// pointer to it.
9859+
ObjectType = E->isArrow() ? BaseType->getPointeeType() : BaseType;
9860+
// HLSL Change - End
98569861
}
98579862

98589863
// Transform the first part of the nested-name-specifier that qualifies

tools/clang/test/HLSL/cpp-errors-hv2015.hlsl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,10 @@ typename typedef float4 TFloat4; // expected-error {{'typename' is a reserved ke
9494
class C {
9595
int fn_eq_default() = default; // expected-error {{function deletion and defaulting is unsupported in HLSL}}
9696

97-
// Errors are a bit misleading here, but ultimate we don't support these.
98-
void* operator new(); // expected-error {{'operator' is a reserved keyword in HLSL}} expected-error {{pointers are unsupported in HLSL}}
99-
void* operator new(int); // expected-error {{'operator' is a reserved keyword in HLSL}} expected-error {{pointers are unsupported in HLSL}}
100-
void* operator new(size_t); // expected-error {{'operator' is a reserved keyword in HLSL}} expected-error {{pointers are unsupported in HLSL}}
97+
// Pointer return type is no longer caught at parse time; operator keyword error is still reported.
98+
void* operator new(); // expected-error {{'operator' is a reserved keyword in HLSL}}
99+
void* operator new(int); // expected-error {{'operator' is a reserved keyword in HLSL}}
100+
void* operator new(size_t); // expected-error {{'operator' is a reserved keyword in HLSL}}
101101

102102
C() = delete; // expected-error {{HLSL requires a type specifier for all declarations}} expected-error {{constructor cannot have a return type}}
103103
};

tools/clang/test/HLSL/cpp-errors.hlsl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,10 @@ typename typedef float4 TFloat4; // expected-error {{'typename' is a reserved ke
9191
class C {
9292
int fn_eq_default() = default; // expected-error {{function deletion and defaulting is unsupported in HLSL}}
9393

94-
// Errors are a bit misleading here, but ultimate we don't support these.
95-
void* operator new(); // expected-error {{'operator' is a reserved keyword in HLSL}} expected-error {{pointers are unsupported in HLSL}}
96-
void* operator new(int); // expected-error {{'operator' is a reserved keyword in HLSL}} expected-error {{pointers are unsupported in HLSL}}
97-
void* operator new(size_t); // expected-error {{'operator' is a reserved keyword in HLSL}} expected-error {{pointers are unsupported in HLSL}}
94+
// Pointer return type is no longer caught at parse time; operator keyword error is still reported.
95+
void* operator new(); // expected-error {{'operator' is a reserved keyword in HLSL}}
96+
void* operator new(int); // expected-error {{'operator' is a reserved keyword in HLSL}}
97+
void* operator new(size_t); // expected-error {{'operator' is a reserved keyword in HLSL}}
9898

9999
C() = delete; // expected-error {{HLSL requires a type specifier for all declarations}} expected-error {{constructor cannot have a return type}}
100100
};
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// RUN: %dxc -Tlib_6_3 -Wno-unused-value -verify %s
2+
// RUN: %dxc -Tcs_6_0 -Wno-unused-value -verify %s
3+
4+
// Verify that pointer and reference types produced by __decltype cannot be
5+
// used as struct instance variable types or local variable types in HLSL.
6+
// Also verifies that template instantiation with pointer/reference type
7+
// arguments is rejected, and that valid __decltype uses are not affected.
8+
9+
// __decltype is the GCC way of saying 'decltype', but doesn't require C++11.
10+
11+
// groupshared variables are mutable (unlike plain global variables in HLSL),
12+
// which allows their lvalue expressions to produce reference types via
13+
// __decltype.
14+
groupshared int g;
15+
16+
// --- Struct field: reference type from __decltype ---
17+
struct RefField {
18+
__decltype(++g) ref_field; // expected-error {{references are unsupported in HLSL}}
19+
};
20+
21+
// --- Struct field: pointer type via explicit cast in __decltype ---
22+
struct PtrField {
23+
__decltype((int *)0) ptr_field; // expected-error {{pointers are unsupported in HLSL}}
24+
};
25+
26+
// --- Struct field: plain (non-reference) type from __decltype is valid ---
27+
struct PlainField {
28+
__decltype(0 + 1) plain_field; // no error: int rvalue, not a reference
29+
};
30+
31+
// --- Local variable: reference type from __decltype ---
32+
void test_local_ref() {
33+
int x = 0;
34+
__decltype(++x) local_ref; // expected-error {{references are unsupported in HLSL}}
35+
}
36+
37+
// --- Local variable: pointer type via __decltype ---
38+
void test_local_ptr() {
39+
__decltype((int *)0) local_ptr; // expected-error {{pointers are unsupported in HLSL}}
40+
}
41+
42+
// --- Template: field with explicit reference type is rejected at definition ---
43+
template <typename T>
44+
struct RefContainer {
45+
T &ref_field; // expected-error {{references are unsupported in HLSL}}
46+
};
47+
48+
// --- Template instantiation: type argument is an explicit pointer type ---
49+
// This verifies that pointer instance variables cannot be created through
50+
// template instantiation with pointer type arguments.
51+
template <typename T>
52+
struct Container {
53+
T field;
54+
};
55+
56+
void test_template_ptr_explicit() {
57+
Container<int *> c; // expected-error {{pointers are unsupported in HLSL}}
58+
}
59+
60+
// --- Regression: __decltype in is_same template argument still works ---
61+
// HLSL has a non-standard is_same extension that treats T and T& as the same
62+
// type. This is used in scalar-operators tests and must not be broken.
63+
void test_is_same_regression() {
64+
int x = 0;
65+
_Static_assert(std::is_same<int, __decltype(x += 1)>::value,
66+
"regression: is_same with lvalue __decltype must still work");
67+
}

0 commit comments

Comments
 (0)