Skip to content
Open
2 changes: 2 additions & 0 deletions docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,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).

#### SPIR-V

Expand Down
3 changes: 1 addition & 2 deletions tools/clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -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]>;
Expand Down
1 change: 1 addition & 0 deletions tools/clang/lib/AST/ASTContextHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Comment thread
llvm-beanz marked this conversation as resolved.

QualType indexType = context.UnsignedIntTy;
QualType resultType = context.getRecordType(recordDecl);
Expand Down
11 changes: 7 additions & 4 deletions tools/clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7480,12 +7480,12 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
NewFD->setVirtualAsWritten(true);
}

if (getLangOpts().CPlusPlus14 &&
if ((getLangOpts().CPlusPlus14) &&

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary change? or is this meant to check if its hlsl too? I've made suggestions for both options below.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ((getLangOpts().CPlusPlus14) &&
if (getLangOpts().CPlusPlus14 &&

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ((getLangOpts().CPlusPlus14) &&
if ((getLangOpts().CPlusPlus14 || getLangOpts().HLSL) && // HLSL Change

NewFD->getReturnType()->isUndeducedType())
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()) {
Comment thread
llvm-beanz marked this conversation as resolved.
Expand Down Expand Up @@ -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'.
Expand Down
9 changes: 6 additions & 3 deletions tools/clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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,
Expand Down
15 changes: 14 additions & 1 deletion tools/clang/lib/Sema/SemaStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Comment thread
Copilot marked this conversation as resolved.
// HLSL Change End
if (AutoType *AT = FnRetType->getContainedAutoType()) {
FunctionDecl *FD = cast<FunctionDecl>(CurContext);
if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) {
Expand Down
10 changes: 8 additions & 2 deletions tools/clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())) {
Comment thread
llvm-beanz marked this conversation as resolved.
// HLSL Change End
int Error = -1;

switch (D.getContext()) {
Expand Down Expand Up @@ -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)) {
Comment thread
llvm-beanz marked this conversation as resolved.
// HLSL Change End
S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(),
D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto
? diag::err_auto_missing_trailing_return
Expand Down
45 changes: 45 additions & 0 deletions tools/clang/test/CodeGenSPIRV/fn.auto.return.hlsl
Original file line number Diff line number Diff line change
@@ -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<float> 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 <typename T>
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));
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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<float> 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);
}
74 changes: 74 additions & 0 deletions tools/clang/test/HLSLFileCheckLit/hlsl/auto/auto-return-type.hlsl
Original file line number Diff line number Diff line change
@@ -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<float> 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 <typename T>
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)));
}
Original file line number Diff line number Diff line change
@@ -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<float4> tex : register(t0);
Texture2DMS<float4> 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<float>)ResourceDescriptorHeap[0]);
}

auto GetDynamicResource(bool IsRW) {
if (IsRW)
return ((RWBuffer<float>)ResourceDescriptorHeap[0]);
// expected-error@+1 {{'auto' in return type deduced as 'Buffer<float>' here but deduced as 'RWBuffer<float>' in earlier return statement}}
return ((Buffer<float>)ResourceDescriptorHeap[0]);
}
Loading