diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index a4dc8518b1..39ae4d044e 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -44,6 +44,8 @@ line upon naming the release. Refer to previous for appropriate section names. - The `shared` and `uniform` keywords are removed in HLSL 202x, with compatibility warnings available for earlier language versions [#8482](https://github.com/microsoft/DirectXShaderCompiler/issues/8482). +- Added support for `auto` return types for normal functions aligning with C++14 + [#8903](https://github.com/microsoft/DirectXShaderCompiler/issues/8903). #### Bug Fixes diff --git a/tools/clang/include/clang/Basic/Attr.td b/tools/clang/include/clang/Basic/Attr.td index 21b57c8934..2d298fb056 100644 --- a/tools/clang/include/clang/Basic/Attr.td +++ b/tools/clang/include/clang/Basic/Attr.td @@ -1189,8 +1189,7 @@ def HLSLSubObject : InheritableAttr { } // Marks builtin record types that have no deducible value form, so 'auto' must -// not infer them: the inner indexer objects behind .mips / .sample, and the -// subobject types used to configure DXR state objects. +// not infer them. def HLSLNonAutoDeducible : InheritableAttr { let Spellings = []; // No spellings! let Subjects = SubjectList<[CXXRecord]>; diff --git a/tools/clang/lib/AST/ASTContextHLSL.cpp b/tools/clang/lib/AST/ASTContextHLSL.cpp index 6077177939..c63e494fc4 100644 --- a/tools/clang/lib/AST/ASTContextHLSL.cpp +++ b/tools/clang/lib/AST/ASTContextHLSL.cpp @@ -1334,6 +1334,7 @@ CXXRecordDecl *hlsl::DeclareResourceType(ASTContext &context, bool bSampler) { CXXRecordDecl *recordDecl = typeDeclBuilder.getRecordDecl(); recordDecl->addAttr( HLSLDynamicResourceAttr::CreateImplicit(context, bSampler)); + recordDecl->addAttr(HLSLNonAutoDeducibleAttr::CreateImplicit(context)); QualType indexType = context.UnsignedIntTy; QualType resultType = context.getRecordType(recordDecl); diff --git a/tools/clang/lib/Sema/SemaDecl.cpp b/tools/clang/lib/Sema/SemaDecl.cpp index c4c6c6e787..fe6369ffd5 100644 --- a/tools/clang/lib/Sema/SemaDecl.cpp +++ b/tools/clang/lib/Sema/SemaDecl.cpp @@ -7485,7 +7485,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, Diag(D.getDeclSpec().getVirtualSpecLoc(), diag::err_auto_fn_virtual); } - if (getLangOpts().CPlusPlus14 && + if ((getLangOpts().CPlusPlus14 || getLangOpts().HLSL) && // HLSL Change (NewFD->isDependentContext() || (isFriend && CurContext->isDependentContext())) && NewFD->getReturnType()->isUndeducedType()) { @@ -10908,8 +10908,11 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, if (FD) { FD->setBody(Body); - if (getLangOpts().CPlusPlus14 && !FD->isInvalidDecl() && Body && - !FD->isDependentContext() && FD->getReturnType()->isUndeducedType()) { + // HLSL Change Begin - HLSL supports C++14-style deduced return types. + if ((getLangOpts().CPlusPlus14 || getLangOpts().HLSL) && + !FD->isInvalidDecl() && Body && !FD->isDependentContext() && + FD->getReturnType()->isUndeducedType()) { + // HLSL Change End // If the function has a deduced result type but contains no 'return' // statements, the result type as written must be exactly 'auto', and // the deduced result type is 'void'. diff --git a/tools/clang/lib/Sema/SemaExpr.cpp b/tools/clang/lib/Sema/SemaExpr.cpp index 1c6c699bde..220c31ba26 100644 --- a/tools/clang/lib/Sema/SemaExpr.cpp +++ b/tools/clang/lib/Sema/SemaExpr.cpp @@ -63,7 +63,9 @@ bool Sema::CanUseDecl(NamedDecl *D) { // If the function has a deduced return type, and we can't deduce it, // then we can't use it either. - if (getLangOpts().CPlusPlus14 && FD->getReturnType()->isUndeducedType() && + // HLSL Change - HLSL supports C++14-style deduced return types. + if ((getLangOpts().CPlusPlus14 || getLangOpts().HLSL) && + FD->getReturnType()->isUndeducedType() && DeduceReturnType(FD, SourceLocation(), /*Diagnose*/ false)) return false; } @@ -366,8 +368,9 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, // If the function has a deduced return type, and we can't deduce it, // then we can't use it either. - if (getLangOpts().CPlusPlus14 && FD->getReturnType()->isUndeducedType() && - DeduceReturnType(FD, Loc)) + // HLSL Change - HLSL supports C++14-style deduced return types. + if ((getLangOpts().CPlusPlus14 || getLangOpts().HLSL) && + FD->getReturnType()->isUndeducedType() && DeduceReturnType(FD, Loc)) return true; } DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass, diff --git a/tools/clang/lib/Sema/SemaStmt.cpp b/tools/clang/lib/Sema/SemaStmt.cpp index baa9559dbb..856748e207 100644 --- a/tools/clang/lib/Sema/SemaStmt.cpp +++ b/tools/clang/lib/Sema/SemaStmt.cpp @@ -3071,6 +3071,17 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, if (DAR != DAR_Succeeded) return true; + // HLSL Change Begin - Diagnose deduced return types that 'auto' cannot + // represent. A dependent deduced type cannot be classified yet; defer the + // check to instantiation, when 'auto' is re-deduced to a concrete type. + if (getLangOpts().HLSL && !Deduced->isDependentType() && + !hlsl::IsTypeDeducibleWithAuto(Deduced)) { + Diag(RetExpr->getExprLoc(), diag::err_hlsl_auto_undeducible_type) + << Deduced; + return true; + } + // HLSL Change End + // If a local type is part of the returned type, mark its fields as // referenced. LocalTypedefNameReferencer Referencer(*this); @@ -3177,7 +3188,9 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // FIXME: Add a flag to the ScopeInfo to indicate whether we're performing // deduction. - if (getLangOpts().CPlusPlus14) { + // HLSL Change Begin - HLSL supports C++14-style deduced return types. + if (getLangOpts().CPlusPlus14 || getLangOpts().HLSL) { + // HLSL Change End if (AutoType *AT = FnRetType->getContainedAutoType()) { FunctionDecl *FD = cast(CurContext); if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) { diff --git a/tools/clang/lib/Sema/SemaType.cpp b/tools/clang/lib/Sema/SemaType.cpp index 7465cc2cec..9f7d1f5fca 100644 --- a/tools/clang/lib/Sema/SemaType.cpp +++ b/tools/clang/lib/Sema/SemaType.cpp @@ -2658,8 +2658,12 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, // type (this is checked later) and we can skip this. In other languages // using auto, we need to check regardless. // C++14 In generic lambdas allow 'auto' in their parameters. + // HLSL Change Begin - HLSL supports 'auto' as a function declarator return + // type with C++14-style deduction; skip this check for functions. if (ContainsPlaceholderType && - (!SemaRef.getLangOpts().CPlusPlus11 || !D.isFunctionDeclarator())) { + (!(SemaRef.getLangOpts().CPlusPlus11 || SemaRef.getLangOpts().HLSL) || + !D.isFunctionDeclarator())) { + // HLSL Change End int Error = -1; switch (D.getContext()) { @@ -3748,9 +3752,11 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, if (!D.isInvalidType()) { // trailing-return-type is only required if we're declaring a function, // and not, for instance, a pointer to a function. + // HLSL Change Begin - HLSL supports C++14-style deduced return types. if (D.getDeclSpec().containsPlaceholderType() && !FTI.hasTrailingReturnType() && chunkIndex == 0 && - !S.getLangOpts().CPlusPlus14) { + !(S.getLangOpts().CPlusPlus14 || S.getLangOpts().HLSL)) { + // HLSL Change End S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto ? diag::err_auto_missing_trailing_return diff --git a/tools/clang/test/CodeGenSPIRV/fn.auto.return.hlsl b/tools/clang/test/CodeGenSPIRV/fn.auto.return.hlsl new file mode 100644 index 0000000000..b66b267d61 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/fn.auto.return.hlsl @@ -0,0 +1,45 @@ +// RUN: %dxc -T cs_6_0 -E main -HV 202x -fcgl %s -spirv | FileCheck %s + +// Test that the 'auto' keyword can be used as a function return type and +// that the deduced type is used correctly when targeting SPIR-V. + +// CHECK-DAG: [[INT:%[a-zA-Z0-9_]+]] = OpTypeInt 32 1 +// CHECK-DAG: [[FLOAT:%[a-zA-Z0-9_]+]] = OpTypeFloat 32 +// CHECK-DAG: [[V4FLOAT:%[a-zA-Z0-9_]+]] = OpTypeVector [[FLOAT]] 4 + +// Function 'SquareInt' must return int. +// CHECK-DAG: %SquareInt = OpFunction [[INT]] None +// CHECK-DAG: %SquareFloat = OpFunction [[FLOAT]] None +// CHECK-DAG: %Scale = OpFunction [[V4FLOAT]] None +// CHECK-DAG: %WriteOutput = OpFunction %void None +// CHECK-DAG: %Sum = OpFunction [[FLOAT]] None + +RWBuffer output : register(u0); + +auto SquareInt(int x) { + return x * x; +} + +auto SquareFloat(float x) { + return x * x; +} + +auto Scale(float4 v, float s) { + return v * s; +} + +template +auto Sum(T L, T R) { + return L + R; +} + +auto WriteOutput(uint i, float v) { + output[i] = v; +} + +[numthreads(1,1,1)] +void main() { + float4 v = float4(1, 2, 3, 4); + float4 s = Scale(v, 0.5f); + WriteOutput(0, (float)SquareInt(3) + Sum(SquareFloat(2.5f), s.x)); +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-return-errors.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-return-errors.hlsl new file mode 100644 index 0000000000..d8cb621d7b --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-return-errors.hlsl @@ -0,0 +1,31 @@ +// RUN: %dxc -T cs_6_0 -HV 202x -verify %s + +// Test diagnostics for incorrect uses of 'auto' as a function return type. + +// Inconsistent deduced types between return statements is an error. +auto BadDeduction(int x) { + if (x > 0) + return 1; // deduced as int + return 2.0f; // expected-error {{'auto' in return type deduced as 'float' here but deduced as 'int' in earlier return statement}} +} + +// A function declared with 'auto' must be defined before it is used; a +// forward declaration alone is not sufficient for the call site. +auto ForwardOnly(int x); +void useForward() { + ForwardOnly(1); // expected-error {{function 'ForwardOnly' with deduced return type cannot be used before it is defined}} +} +// expected-note@-4 {{'ForwardOnly' declared here}} + +// A recursive call to a function with deduced return type must occur after +// a return statement that allows the type to be deduced. +auto BadRecurse(int x) { + return BadRecurse(x - 1) + 1; // expected-error {{cannot be used before it is defined}} +} +// expected-note@-3 {{'BadRecurse' declared here}} + +[numthreads(1,1,1)] +void main() { + BadDeduction(1); + BadRecurse(2); +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-return-extension-warning.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-return-extension-warning.hlsl new file mode 100644 index 0000000000..886a84bd27 --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-return-extension-warning.hlsl @@ -0,0 +1,19 @@ +// RUN: %dxc -T cs_6_0 -E main -HV 2016 -verify %s +// RUN: %dxc -T cs_6_0 -E main -HV 2017 -verify %s +// RUN: %dxc -T cs_6_0 -E main -HV 2018 -verify %s +// RUN: %dxc -T cs_6_0 -E main -HV 2021 -verify %s + +// 'auto' is allowed as a function return type from HLSL 2016 onward, but +// using it before language mode 202x produces an extension warning. + +RWBuffer output : register(u0); + +// expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} +auto Square(int x) { + return x * x; +} + +[numthreads(1,1,1)] +void main() { + output[0] = (float)Square(3); +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-return-type.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-return-type.hlsl new file mode 100644 index 0000000000..7e1648e259 --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-return-type.hlsl @@ -0,0 +1,74 @@ +// 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 as a function return type and +// that the deduced type matches C++14 [dcl.spec.auto] rules. + +// CHECK-LABEL: define void @main() + +// CHECK-LABEL: define internal <4 x float> @"\01?Scale +// CHECK: ret <4 x float> + +// CHECK-LABEL: define internal void @"\01?WriteOutput +// CHECK: ret void + +// CHECK-LABEL: define internal i32 @"\01?SquareInt +// CHECK: ret i32 + +// CHECK-LABEL: define internal float @"\01?SquareFloat +// CHECK: ret float + +// CHECK-LABEL: define internal float @"\01??$Sum +// CHECK: ret float + +// CHECK-LABEL: define internal float @"\01?Clamp01 +// CHECK: ret float + +RWBuffer output : register(u0); + +// Deduces int from a single return statement. +// expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} +auto SquareInt(int x) { + return x * x; +} + +// Deduces float from a single return statement. +// expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} +auto SquareFloat(float x) { + return x * x; +} + +// Deduces float4 from a single return statement. +// expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} +auto Scale(float4 v, float s) { + return v * s; +} + +// Deduces void when no return statement is present. +// expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} +auto WriteOutput(uint i, float v) { + output[i] = v; +} + +// Multiple return statements with the same deduced type are allowed. +// expected-warning@+1 {{'auto' type specifier is a HLSL 202x extension}} +auto Clamp01(float v) { + if (v < 0.0f) + return 0.0f; + if (v > 1.0f) + return 1.0f; + return v; +} + +// expected-warning@+2 {{'auto' type specifier is a HLSL 202x extension}} +template +auto Sum(T L, T R) { + return L + R; +} + +[numthreads(1,1,1)] +void main() { + float4 v = float4(1, 2, 3, 4); + float4 s = Scale(v, 0.5f); + WriteOutput(0, (float)SquareInt(3) + SquareFloat(2.5f) + Sum(s.x, Clamp01(1.5f))); +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-return-undeducible-types.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-return-undeducible-types.hlsl new file mode 100644 index 0000000000..17c74b7f0c --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-return-undeducible-types.hlsl @@ -0,0 +1,74 @@ +// RUN: %dxc -T lib_6_6 -HV 202x -verify %s + +// Test that deducing a return type that 'auto' cannot represent produces a +// diagnostic, mirroring the checks already performed for 'auto' variables +// (see auto-undeducible-types.hlsl and auto-no-subobject.hlsl). + +Texture2D tex : register(t0); +Texture2DMS texMS : register(t1); + +GlobalRootSignature grs = {"CBV(b0)"}; + +// String literals cannot be deduced by 'auto'. +auto GetString() { + // expected-error@+1 {{'auto' cannot deduce type 'literal string'}} + return "abc"; +} + +// The proxy types used for '.mips'/'.sample' subscript operators cannot be +// deduced by 'auto'. +auto GetMips() { + // expected-error@+1 {{'auto' cannot deduce type}} + return tex.mips; +} + +auto GetMipsElement() { + // expected-error@+1 {{'auto' cannot deduce type}} + return tex.mips[0]; +} + +auto GetSample() { + // expected-error@+1 {{'auto' cannot deduce type}} + return texMS.sample; +} + +auto GetSampleElement() { + // expected-error@+1 {{'auto' cannot deduce type}} + return texMS.sample[0]; +} + +// Subobjects cannot be deduced by 'auto'. +auto GetSubobject() { + // expected-error@+1 {{'auto' cannot deduce type 'GlobalRootSignature'}} + return grs; +} + +// Fully subscripted mips/sample accesses deduce a normal, deducible type. +auto GetMipsValue() { + return tex.mips[0][int2(1, 2)]; +} + +auto GetSampleValue() { + return texMS.sample[0][int2(1, 2)]; +} + +auto GetDynamicResource() { + // expected-error@+1 {{'auto' cannot deduce type '.Resource'}} + return ResourceDescriptorHeap[0]; +} + +auto GetDynamicSampler() { + // expected-error@+1 {{'auto' cannot deduce type '.Sampler'}} + return SamplerDescriptorHeap[0]; +} + +auto GetDynamicResource2() { + return ((RWBuffer)ResourceDescriptorHeap[0]); +} + +auto GetDynamicResource(bool IsRW) { + if (IsRW) + return ((RWBuffer)ResourceDescriptorHeap[0]); + // expected-error@+1 {{'auto' in return type deduced as 'Buffer' here but deduced as 'RWBuffer' in earlier return statement}} + return ((Buffer)ResourceDescriptorHeap[0]); +}