diff --git a/tools/clang/include/clang/Basic/DiagnosticParseKinds.td b/tools/clang/include/clang/Basic/DiagnosticParseKinds.td index e328393726..ac4bad2cc2 100644 --- a/tools/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/tools/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -285,7 +285,8 @@ def warn_cxx98_compat_trailing_return_type : Warning< "trailing return types are incompatible with C++98">, InGroup, DefaultIgnore; def ext_auto_type_specifier : ExtWarn< - "'auto' type specifier is a C++11 extension">, InGroup; + "'auto' type specifier is a %select{C++11|HLSL 202x}0 extension">, + InGroup; def warn_auto_storage_class : Warning< "'auto' storage class specifier is redundant and incompatible with C++11">, InGroup, DefaultIgnore; diff --git a/tools/clang/include/clang/Lex/Token.h b/tools/clang/include/clang/Lex/Token.h index 7cb0118596..42e45bc035 100644 --- a/tools/clang/include/clang/Lex/Token.h +++ b/tools/clang/include/clang/Lex/Token.h @@ -295,7 +295,7 @@ class Token { // HLSL Change Starts bool isHLSLReserved() const { return is(tok::kw___is_signed) || is(tok::kw___declspec) || - is(tok::kw___forceinline) || is(tok::kw_auto) || is(tok::kw_catch) || + is(tok::kw___forceinline) || is(tok::kw_catch) || is(tok::kw_const_cast) || is(tok::kw_delete) || is(tok::kw_dynamic_cast) || is(tok::kw_enum) || is(tok::kw_explicit) || is(tok::kw_friend) || is(tok::kw_goto) || diff --git a/tools/clang/lib/Parse/ParseDecl.cpp b/tools/clang/lib/Parse/ParseDecl.cpp index c91c8940a4..85404a1673 100644 --- a/tools/clang/lib/Parse/ParseDecl.cpp +++ b/tools/clang/lib/Parse/ParseDecl.cpp @@ -3918,8 +3918,12 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, break; // HLSL Change Ends case tok::kw_auto: - if (getLangOpts().HLSL) { goto HLSLReservedKeyword; } // HLSL Change - auto is reserved for HLSL - if (getLangOpts().CPlusPlus11) { + // HLSL Change Begin - auto is reserved for HLSL 2015 and earlier. + if (getLangOpts().HLSL && + getLangOpts().HLSLVersion <= hlsl::LangStd::v2015) + goto HLSLReservedKeyword; + // HLSL Change End + if (getLangOpts().CPlusPlus11 || getLangOpts().HLSL) { // HLSL Change if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) { isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc, PrevSpec, DiagID, Policy); diff --git a/tools/clang/lib/Sema/DeclSpec.cpp b/tools/clang/lib/Sema/DeclSpec.cpp index 37c1554092..455f755242 100644 --- a/tools/clang/lib/Sema/DeclSpec.cpp +++ b/tools/clang/lib/Sema/DeclSpec.cpp @@ -1158,8 +1158,14 @@ void DeclSpec::Finish(DiagnosticsEngine &D, Preprocessor &PP, const PrintingPoli } // Diagnose if we've recovered from an ill-formed 'auto' storage class // specifier in a pre-C++11 dialect of C++. - if (!PP.getLangOpts().CPlusPlus11 && TypeSpecType == TST_auto) - Diag(D, TSTLoc, diag::ext_auto_type_specifier); + // HLSL Change Begin - HLSL supports 'auto' as a type specifier in 202x+. + if (!(PP.getLangOpts().CPlusPlus11 || + (PP.getLangOpts().HLSL && + PP.getLangOpts().HLSLVersion >= hlsl::LangStd::v202x)) && + TypeSpecType == TST_auto) + Diag(D, TSTLoc, diag::ext_auto_type_specifier) + << /* HLSL */ PP.getLangOpts().HLSL; + // HLSL Change End if (PP.getLangOpts().CPlusPlus && !PP.getLangOpts().CPlusPlus11 && StorageClassSpec == SCS_auto) Diag(D, StorageClassSpecLoc, diag::warn_auto_storage_class) diff --git a/tools/clang/lib/Sema/SemaDecl.cpp b/tools/clang/lib/Sema/SemaDecl.cpp index a772054960..b6fb971aac 100644 --- a/tools/clang/lib/Sema/SemaDecl.cpp +++ b/tools/clang/lib/Sema/SemaDecl.cpp @@ -9047,7 +9047,6 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, // If this is a redeclaration, check that the type we just deduced matches // the previously declared type. - assert(!getLangOpts().HLSL && "auto types are not supported - merge type below is inconsequential"); // HLSL Change if (VarDecl *Old = VDecl->getPreviousDecl()) { // We never need to merge the type, because we cannot form an incomplete // array of auto, nor deduce such a type. diff --git a/tools/clang/test/CodeGenSPIRV/var.auto.deduction.hlsl b/tools/clang/test/CodeGenSPIRV/var.auto.deduction.hlsl new file mode 100644 index 0000000000..553bb6642a --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/var.auto.deduction.hlsl @@ -0,0 +1,51 @@ +// RUN: %dxc -T cs_6_0 -E main -HV 202x -fcgl %s -spirv | FileCheck %s + +// Test that the 'auto' keyword can be used to declare variables with inferred +// types from initialization expressions when targeting SPIR-V. + +// CHECK: [[INT:%[a-zA-Z0-9_]+]] = OpTypeInt 32 1 +// CHECK: [[INT_1:%[a-zA-Z0-9_]+]] = OpConstant [[INT]] 1 +// CHECK: [[FLOAT:%[a-zA-Z0-9_]+]] = OpTypeFloat 32 +// CHECK: [[FLOAT_2:%[a-zA-Z0-9_]+]] = OpConstant [[FLOAT]] 2 +// CHECK: [[BOOL:%[a-zA-Z0-9_]+]] = OpTypeBool +// CHECK: [[TRUE:%[a-zA-Z0-9_]+]] = OpConstantTrue [[BOOL]] +// CHECK: [[V4FLOAT:%[a-zA-Z0-9_]+]] = OpTypeVector [[FLOAT]] 4 +// CHECK: [[VEC_CONST:%[a-zA-Z0-9_]+]] = OpConstantComposite [[V4FLOAT]] {{%[a-zA-Z0-9_]+}} [[FLOAT_2]] {{%[a-zA-Z0-9_]+}} {{%[a-zA-Z0-9_]+}} + +// CHECK: [[PTR_INT:%_ptr_Function_int]] = OpTypePointer Function [[INT]] +// CHECK: [[PTR_FLOAT:%_ptr_Function_float]] = OpTypePointer Function [[FLOAT]] +// CHECK: [[PTR_BOOL:%_ptr_Function_bool]] = OpTypePointer Function [[BOOL]] +// CHECK: [[PTR_V4FLOAT:%_ptr_Function_v4float]] = OpTypePointer Function [[V4FLOAT]] + +// CHECK: %a = OpVariable [[PTR_INT]] Function +// CHECK: %b = OpVariable [[PTR_FLOAT]] Function +// CHECK: %c = OpVariable [[PTR_BOOL]] Function +// CHECK: %d = OpVariable [[PTR_V4FLOAT]] Function +// CHECK: %sum = OpVariable [[PTR_INT]] Function +// CHECK: %product = OpVariable [[PTR_FLOAT]] Function + +// CHECK: OpStore %a [[INT_1]] +// CHECK: OpStore %b [[FLOAT_2]] +// CHECK: OpStore %c [[TRUE]] +// CHECK: OpStore %d [[VEC_CONST]] + +RWBuffer output : register(u0); + +[numthreads(1,1,1)] +void main() { + // Auto deduces int from integer literal + auto a = 1; + // Auto deduces float from float literal + auto b = 2.0f; + // Auto deduces bool from bool literal + auto c = true; + // Auto deduces float4 from vector type + auto d = float4(1.0f, 2.0f, 3.0f, 4.0f); + + // Auto from arithmetic expressions + auto sum = a + a; + auto product = b * b; + + // Use the values to prevent dead-code elimination + output[0] = (float)sum + product + d.x + (float)c; +} diff --git a/tools/clang/test/CodeGenSPIRV/var.auto.template.hlsl b/tools/clang/test/CodeGenSPIRV/var.auto.template.hlsl new file mode 100644 index 0000000000..7a2b5acf4d --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/var.auto.template.hlsl @@ -0,0 +1,53 @@ +// RUN: %dxc -T cs_6_0 -E main -HV 202x -fcgl %s -spirv | FileCheck %s + +// Test that the 'auto' keyword works correctly in template contexts when +// targeting SPIR-V: +// - auto variables inside template function bodies +// - auto variables assigned from results of template function instantiations + +RWBuffer output : register(u0); + +// Template with explicit return type; auto is used inside the body. +template +T square(T val) { + auto result = val * val; + return result; +} + +// Template with explicit return type using a conditional expression. +template +T clampPositive(T val) { + auto zero = (T)0; + auto clamped = val < zero ? zero : val; + return clamped; +} + +// CHECK: [[INT:%[a-zA-Z0-9_]+]] = OpTypeInt 32 1 +// CHECK: [[FLOAT:%[a-zA-Z0-9_]+]] = OpTypeFloat 32 +// CHECK: [[PTR_INT:%_ptr_Function_int]] = OpTypePointer Function [[INT]] +// CHECK: [[PTR_FLOAT:%_ptr_Function_float]] = OpTypePointer Function [[FLOAT]] + +// Auto-deduced locals in main() instantiated for int and float. +// CHECK: %a = OpVariable [[PTR_INT]] Function +// CHECK: %b = OpVariable [[PTR_FLOAT]] Function +// CHECK: %c = OpVariable [[PTR_INT]] Function +// CHECK: %d = OpVariable [[PTR_FLOAT]] Function + +// Auto inside template bodies retains correct types after instantiation. +// CHECK: %result = OpVariable [[PTR_INT]] Function +// CHECK: %result_0 = OpVariable [[PTR_FLOAT]] Function +// CHECK: %zero = OpVariable [[PTR_INT]] Function +// CHECK: %clamped = OpVariable [[PTR_INT]] Function +// CHECK: %zero_0 = OpVariable [[PTR_FLOAT]] Function +// CHECK: %clamped_0 = OpVariable [[PTR_FLOAT]] Function + +[numthreads(1,1,1)] +void main() { + // auto variables assigned from template instantiation results. + auto a = square(5); // instantiated as int + auto b = square(2.5f); // instantiated as float + auto c = clampPositive(-3); // instantiated as int + auto d = clampPositive(1.5f); // instantiated as float + + output[0] = (float)a + b + (float)c + d; +} diff --git a/tools/clang/test/HLSL/cpp-errors.hlsl b/tools/clang/test/HLSL/cpp-errors.hlsl index bee34d249b..1ecf4a57e1 100644 --- a/tools/clang/test/HLSL/cpp-errors.hlsl +++ b/tools/clang/test/HLSL/cpp-errors.hlsl @@ -12,7 +12,7 @@ s_arr_i_f arr_struct_two[] = { 1, 2, 3, 4 }; int g_int; 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'?}} typedef int (*fn_int)(int); // expected-error {{pointers are unsupported in HLSL}} -auto g_auto = 3; // expected-error {{'auto' is a reserved keyword in HLSL}} expected-error {{HLSL requires a type specifier for all declarations}} +auto g_auto = 3; // auto is now supported in HLSL via type deduction; no error expected __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}} register int g_register; // expected-error {{'register' is a reserved keyword in HLSL}} __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 void fn_noexcept() noexcept { }; // expected-error {{expected function body after function declarator}} // This would be a failure because of unsupported trailer return types, but we mis-parse it differently. -auto fn_trailing() -> int { return 1; } ; // expected-error {{'auto' is a reserved keyword in HLSL}} expected-error {{expected function body after function declarator}} +// auto is now type-deduced so no reserved keyword error; only the trailing return type syntax causes an error. +auto fn_trailing() -> int { return 1; } ; // expected-error {{expected function body after function declarator}} void fn_param_with_default(int val = 1) { } void fn_with_variadic(int a, ...) { } // expected-error {{variadic arguments is unsupported in HLSL}} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-no-pointer.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-no-pointer.hlsl new file mode 100644 index 0000000000..425a69cd8c --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-no-pointer.hlsl @@ -0,0 +1,10 @@ +// RUN: %dxc -T cs_6_0 -HV 202x -verify %s + +// Test that 'auto' cannot be used to declare pointer types in HLSL. +// Pointers are unsupported in HLSL. + +[numthreads(1,1,1)] +void main() { + int x = 1; + auto* ptr = &x; // expected-error {{pointers are unsupported in HLSL}} +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-no-reference.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-no-reference.hlsl new file mode 100644 index 0000000000..499c26aec7 --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-no-reference.hlsl @@ -0,0 +1,11 @@ +// RUN: %dxc -T cs_6_0 -HV 202x -verify %s + +// Test that 'auto' cannot be used to declare reference types in HLSL. +// References are unsupported in HLSL. + +int gVal = 42; + +[numthreads(1,1,1)] +void main() { + auto& ref = gVal; // expected-error {{references are unsupported in HLSL}} +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-template.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-template.hlsl new file mode 100644 index 0000000000..885d2226ae --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-template.hlsl @@ -0,0 +1,41 @@ +// RUN: %dxc -T cs_6_0 -HV 202x -fcgl %s | FileCheck %s + +// Test that the 'auto' keyword works correctly in template contexts in HLSL: +// - auto variables inside template function bodies +// - auto variables assigned from results of template function instantiations + + + +RWBuffer output : register(u0); + +// Template with explicit return type; auto is used inside the body. +template +T square(T val) { + auto result = val * val; + return result; +} + +// Template with explicit return type using a conditional expression. +template +T clampPositive(T val) { + auto zero = (T)0; + auto clamped = val < zero ? zero : val; + return clamped; +} + +// CHECK-LABEL: define void @main() +// CHECK: {{.*}} = alloca i32 +// CHECK: {{.*}} = alloca float +// CHECK: {{.*}} = alloca i32 +// CHECK: {{.*}} = alloca float + +[numthreads(1,1,1)] +void main() { + // auto variables assigned from template instantiation results. + auto a = square(5); // instantiated as int + auto b = square(2.5f); // instantiated as float + auto c = clampPositive(-3); // instantiated as int, result = 0 + auto d = clampPositive(1.5f); // instantiated as float + + output[0] = (float)a + b + (float)c + d; +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-variable-deduction.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-variable-deduction.hlsl new file mode 100644 index 0000000000..8cc375a02e --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-variable-deduction.hlsl @@ -0,0 +1,55 @@ +// RUN: %dxc -T cs_6_0 -E main - %s -verify +// RUN: %dxc -T cs_6_0 -E main -HV 202x -fcgl %s | FileCheck %s + +// Test that the 'auto' keyword can be used to declare variables with inferred +// types from initialization expressions in HLSL. + +// CHECK-LABEL: define void @main() +// CHECK: [[A:%[a-zA-Z0-9_]+]] = alloca i32 +// CHECK: [[B:%[a-zA-Z0-9_]+]] = alloca float +// CHECK: [[C:%[a-zA-Z0-9_]+]] = alloca i32 +// CHECK: [[D:%[a-zA-Z0-9_]+]] = alloca <4 x float> +// CHECK: {{.*}} = alloca i32 +// CHECK: {{.*}} = alloca float +// CHECK: {{.*}} = alloca <4 x float> +// CHECK: {{.*}} = alloca i32 +// CHECK: {{.*}} = alloca float +// CHECK: store i32 1, i32* [[A]] +// CHECK: store float 2.000000e+00, float* [[B]] +// CHECK: store i32 1, i32* [[C]] + +RWBuffer output : register(u0); + +[numthreads(1,1,1)] +void main() { + // Auto deduces int from integer literal + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto a = 1; + // Auto deduces float from float literal + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto b = 2.0f; + // Auto deduces bool (stored as i32) from bool literal + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto c = true; + // Auto deduces float4 from vector type + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto d = float4(1.0f, 2.0f, 3.0f, 4.0f); + + // Auto from arithmetic expressions + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto sum = a + a; + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto product = b * b; + + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto mulAdd = mad(d, d, d); + + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto m = min(sum, c); + + // expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} + auto dP = dot(d, d); + + // Use the values to prevent dead-code elimination + output[0] = (float)sum + product + d.x + (float)c; +}