Skip to content

Commit d9c0a26

Browse files
authored
Implement auto for C++11-style type deduction (#8452)
Partial implementation of C++11-stlye type deduction added to HLSL under the 202x language mode. This is still missing special handling for non-deducible special HLSL types. The main one that comes to mind is the type of the `ResourceDescriptorHeap` and `SamplerDescriptorHeap`. I think this PR can stand on its own because using it to infer those special types won't technically break anything it just might expose some bits and pieces of the implementation that we don't want.
1 parent c4676d3 commit d9c0a26

12 files changed

Lines changed: 241 additions & 9 deletions

File tree

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,8 @@ def warn_cxx98_compat_trailing_return_type : Warning<
285285
"trailing return types are incompatible with C++98">,
286286
InGroup<CXX98Compat>, DefaultIgnore;
287287
def ext_auto_type_specifier : ExtWarn<
288-
"'auto' type specifier is a C++11 extension">, InGroup<CXX11>;
288+
"'auto' type specifier is a %select{C++11|HLSL 202x}0 extension">,
289+
InGroup<CXX11>;
289290
def warn_auto_storage_class : Warning<
290291
"'auto' storage class specifier is redundant and incompatible with C++11">,
291292
InGroup<CXX11Compat>, DefaultIgnore;

tools/clang/include/clang/Lex/Token.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ class Token {
295295
// HLSL Change Starts
296296
bool isHLSLReserved() const {
297297
return is(tok::kw___is_signed) || is(tok::kw___declspec) ||
298-
is(tok::kw___forceinline) || is(tok::kw_auto) || is(tok::kw_catch) ||
298+
is(tok::kw___forceinline) || is(tok::kw_catch) ||
299299
is(tok::kw_const_cast) || is(tok::kw_delete) ||
300300
is(tok::kw_dynamic_cast) || is(tok::kw_enum) ||
301301
is(tok::kw_explicit) || is(tok::kw_friend) || is(tok::kw_goto) ||

tools/clang/lib/Parse/ParseDecl.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3918,8 +3918,12 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
39183918
break;
39193919
// HLSL Change Ends
39203920
case tok::kw_auto:
3921-
if (getLangOpts().HLSL) { goto HLSLReservedKeyword; } // HLSL Change - auto is reserved for HLSL
3922-
if (getLangOpts().CPlusPlus11) {
3921+
// HLSL Change Begin - auto is reserved for HLSL 2015 and earlier.
3922+
if (getLangOpts().HLSL &&
3923+
getLangOpts().HLSLVersion <= hlsl::LangStd::v2015)
3924+
goto HLSLReservedKeyword;
3925+
// HLSL Change End
3926+
if (getLangOpts().CPlusPlus11 || getLangOpts().HLSL) { // HLSL Change
39233927
if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) {
39243928
isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc,
39253929
PrevSpec, DiagID, Policy);

tools/clang/lib/Sema/DeclSpec.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,8 +1158,14 @@ void DeclSpec::Finish(DiagnosticsEngine &D, Preprocessor &PP, const PrintingPoli
11581158
}
11591159
// Diagnose if we've recovered from an ill-formed 'auto' storage class
11601160
// specifier in a pre-C++11 dialect of C++.
1161-
if (!PP.getLangOpts().CPlusPlus11 && TypeSpecType == TST_auto)
1162-
Diag(D, TSTLoc, diag::ext_auto_type_specifier);
1161+
// HLSL Change Begin - HLSL supports 'auto' as a type specifier in 202x+.
1162+
if (!(PP.getLangOpts().CPlusPlus11 ||
1163+
(PP.getLangOpts().HLSL &&
1164+
PP.getLangOpts().HLSLVersion >= hlsl::LangStd::v202x)) &&
1165+
TypeSpecType == TST_auto)
1166+
Diag(D, TSTLoc, diag::ext_auto_type_specifier)
1167+
<< /* HLSL */ PP.getLangOpts().HLSL;
1168+
// HLSL Change End
11631169
if (PP.getLangOpts().CPlusPlus && !PP.getLangOpts().CPlusPlus11 &&
11641170
StorageClassSpec == SCS_auto)
11651171
Diag(D, StorageClassSpecLoc, diag::warn_auto_storage_class)

tools/clang/lib/Sema/SemaDecl.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9047,7 +9047,6 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
90479047

90489048
// If this is a redeclaration, check that the type we just deduced matches
90499049
// the previously declared type.
9050-
assert(!getLangOpts().HLSL && "auto types are not supported - merge type below is inconsequential"); // HLSL Change
90519050
if (VarDecl *Old = VDecl->getPreviousDecl()) {
90529051
// We never need to merge the type, because we cannot form an incomplete
90539052
// array of auto, nor deduce such a type.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: %dxc -T cs_6_0 -E main -HV 202x -fcgl %s -spirv | FileCheck %s
2+
3+
// Test that the 'auto' keyword can be used to declare variables with inferred
4+
// types from initialization expressions when targeting SPIR-V.
5+
6+
// CHECK: [[INT:%[a-zA-Z0-9_]+]] = OpTypeInt 32 1
7+
// CHECK: [[INT_1:%[a-zA-Z0-9_]+]] = OpConstant [[INT]] 1
8+
// CHECK: [[FLOAT:%[a-zA-Z0-9_]+]] = OpTypeFloat 32
9+
// CHECK: [[FLOAT_2:%[a-zA-Z0-9_]+]] = OpConstant [[FLOAT]] 2
10+
// CHECK: [[BOOL:%[a-zA-Z0-9_]+]] = OpTypeBool
11+
// CHECK: [[TRUE:%[a-zA-Z0-9_]+]] = OpConstantTrue [[BOOL]]
12+
// CHECK: [[V4FLOAT:%[a-zA-Z0-9_]+]] = OpTypeVector [[FLOAT]] 4
13+
// CHECK: [[VEC_CONST:%[a-zA-Z0-9_]+]] = OpConstantComposite [[V4FLOAT]] {{%[a-zA-Z0-9_]+}} [[FLOAT_2]] {{%[a-zA-Z0-9_]+}} {{%[a-zA-Z0-9_]+}}
14+
15+
// CHECK: [[PTR_INT:%_ptr_Function_int]] = OpTypePointer Function [[INT]]
16+
// CHECK: [[PTR_FLOAT:%_ptr_Function_float]] = OpTypePointer Function [[FLOAT]]
17+
// CHECK: [[PTR_BOOL:%_ptr_Function_bool]] = OpTypePointer Function [[BOOL]]
18+
// CHECK: [[PTR_V4FLOAT:%_ptr_Function_v4float]] = OpTypePointer Function [[V4FLOAT]]
19+
20+
// CHECK: %a = OpVariable [[PTR_INT]] Function
21+
// CHECK: %b = OpVariable [[PTR_FLOAT]] Function
22+
// CHECK: %c = OpVariable [[PTR_BOOL]] Function
23+
// CHECK: %d = OpVariable [[PTR_V4FLOAT]] Function
24+
// CHECK: %sum = OpVariable [[PTR_INT]] Function
25+
// CHECK: %product = OpVariable [[PTR_FLOAT]] Function
26+
27+
// CHECK: OpStore %a [[INT_1]]
28+
// CHECK: OpStore %b [[FLOAT_2]]
29+
// CHECK: OpStore %c [[TRUE]]
30+
// CHECK: OpStore %d [[VEC_CONST]]
31+
32+
RWBuffer<float> output : register(u0);
33+
34+
[numthreads(1,1,1)]
35+
void main() {
36+
// Auto deduces int from integer literal
37+
auto a = 1;
38+
// Auto deduces float from float literal
39+
auto b = 2.0f;
40+
// Auto deduces bool from bool literal
41+
auto c = true;
42+
// Auto deduces float4 from vector type
43+
auto d = float4(1.0f, 2.0f, 3.0f, 4.0f);
44+
45+
// Auto from arithmetic expressions
46+
auto sum = a + a;
47+
auto product = b * b;
48+
49+
// Use the values to prevent dead-code elimination
50+
output[0] = (float)sum + product + d.x + (float)c;
51+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: %dxc -T cs_6_0 -E main -HV 202x -fcgl %s -spirv | FileCheck %s
2+
3+
// Test that the 'auto' keyword works correctly in template contexts when
4+
// targeting SPIR-V:
5+
// - auto variables inside template function bodies
6+
// - auto variables assigned from results of template function instantiations
7+
8+
RWBuffer<float> output : register(u0);
9+
10+
// Template with explicit return type; auto is used inside the body.
11+
template<typename T>
12+
T square(T val) {
13+
auto result = val * val;
14+
return result;
15+
}
16+
17+
// Template with explicit return type using a conditional expression.
18+
template<typename T>
19+
T clampPositive(T val) {
20+
auto zero = (T)0;
21+
auto clamped = val < zero ? zero : val;
22+
return clamped;
23+
}
24+
25+
// CHECK: [[INT:%[a-zA-Z0-9_]+]] = OpTypeInt 32 1
26+
// CHECK: [[FLOAT:%[a-zA-Z0-9_]+]] = OpTypeFloat 32
27+
// CHECK: [[PTR_INT:%_ptr_Function_int]] = OpTypePointer Function [[INT]]
28+
// CHECK: [[PTR_FLOAT:%_ptr_Function_float]] = OpTypePointer Function [[FLOAT]]
29+
30+
// Auto-deduced locals in main() instantiated for int and float.
31+
// CHECK: %a = OpVariable [[PTR_INT]] Function
32+
// CHECK: %b = OpVariable [[PTR_FLOAT]] Function
33+
// CHECK: %c = OpVariable [[PTR_INT]] Function
34+
// CHECK: %d = OpVariable [[PTR_FLOAT]] Function
35+
36+
// Auto inside template bodies retains correct types after instantiation.
37+
// CHECK: %result = OpVariable [[PTR_INT]] Function
38+
// CHECK: %result_0 = OpVariable [[PTR_FLOAT]] Function
39+
// CHECK: %zero = OpVariable [[PTR_INT]] Function
40+
// CHECK: %clamped = OpVariable [[PTR_INT]] Function
41+
// CHECK: %zero_0 = OpVariable [[PTR_FLOAT]] Function
42+
// CHECK: %clamped_0 = OpVariable [[PTR_FLOAT]] Function
43+
44+
[numthreads(1,1,1)]
45+
void main() {
46+
// auto variables assigned from template instantiation results.
47+
auto a = square(5); // instantiated as int
48+
auto b = square(2.5f); // instantiated as float
49+
auto c = clampPositive(-3); // instantiated as int
50+
auto d = clampPositive(1.5f); // instantiated as float
51+
52+
output[0] = (float)a + b + (float)c + d;
53+
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ s_arr_i_f arr_struct_two[] = { 1, 2, 3, 4 };
1212
int g_int;
1313
typeof(g_int) g_typeof_int; // expected-error {{HLSL requires a type specifier for all declarations}} expected-error {{expected ';' after top level declarator}} expected-error {{unknown type name 'typeof'; did you mean 'typedef'?}}
1414
typedef int (*fn_int)(int); // expected-error {{pointers are unsupported in HLSL}}
15-
auto g_auto = 3; // expected-error {{'auto' is a reserved keyword in HLSL}} expected-error {{HLSL requires a type specifier for all declarations}}
15+
auto g_auto = 3; // auto is now supported in HLSL via type deduction; no error expected
1616
__is_signed g_is_signed; // expected-error {{'__is_signed' is a reserved keyword in HLSL}} expected-error {{HLSL requires a type specifier for all declarations}}
1717
register int g_register; // expected-error {{'register' is a reserved keyword in HLSL}}
1818
__thread int g_thread; // expected-error {{'__thread' is a reserved keyword in HLSL}}
@@ -65,7 +65,8 @@ void fn_throw() throw() { } // expected-error {{exception specification is unsup
6565
void fn_noexcept() noexcept { }; // expected-error {{expected function body after function declarator}}
6666

6767
// This would be a failure because of unsupported trailer return types, but we mis-parse it differently.
68-
auto fn_trailing() -> int { return 1; } ; // expected-error {{'auto' is a reserved keyword in HLSL}} expected-error {{expected function body after function declarator}}
68+
// auto is now type-deduced so no reserved keyword error; only the trailing return type syntax causes an error.
69+
auto fn_trailing() -> int { return 1; } ; // expected-error {{expected function body after function declarator}}
6970

7071
void fn_param_with_default(int val = 1) { }
7172
void fn_with_variadic(int a, ...) { } // expected-error {{variadic arguments is unsupported in HLSL}}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %dxc -T cs_6_0 -HV 202x -verify %s
2+
3+
// Test that 'auto' cannot be used to declare pointer types in HLSL.
4+
// Pointers are unsupported in HLSL.
5+
6+
[numthreads(1,1,1)]
7+
void main() {
8+
int x = 1;
9+
auto* ptr = &x; // expected-error {{pointers are unsupported in HLSL}}
10+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %dxc -T cs_6_0 -HV 202x -verify %s
2+
3+
// Test that 'auto' cannot be used to declare reference types in HLSL.
4+
// References are unsupported in HLSL.
5+
6+
int gVal = 42;
7+
8+
[numthreads(1,1,1)]
9+
void main() {
10+
auto& ref = gVal; // expected-error {{references are unsupported in HLSL}}
11+
}

0 commit comments

Comments
 (0)