Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -2803,6 +2803,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
return getUnqualifiedArrayType(T, Quals);
}

// Determine whether an array is a valid return type
// Array is a valid return type for HLSL
bool isReturnableArrayType() const { return getLangOpts().HLSL; }

/// Determine whether the given types are equivalent after
/// cvr-qualifiers have been removed.
bool hasSameUnqualifiedType(QualType T1, QualType T2) const {
Expand Down
6 changes: 4 additions & 2 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20681,7 +20681,8 @@ ExprResult RebuildUnknownAnyExpr::VisitCallExpr(CallExpr *E) {
const FunctionType *FnType = CalleeType->castAs<FunctionType>();

// Verify that this is a legal result type of a function.
if (DestType->isArrayType() || DestType->isFunctionType()) {
if ((DestType->isArrayType() && !S.Context.isReturnableArrayType()) ||
DestType->isFunctionType()) {
unsigned diagID = diag::err_func_returning_array_function;
if (Kind == FK_BlockPointer)
diagID = diag::err_block_returning_array_function;
Expand Down Expand Up @@ -20760,7 +20761,8 @@ ExprResult RebuildUnknownAnyExpr::VisitCallExpr(CallExpr *E) {

ExprResult RebuildUnknownAnyExpr::VisitObjCMessageExpr(ObjCMessageExpr *E) {
// Verify that this is a legal result type of a call.
if (DestType->isArrayType() || DestType->isFunctionType()) {
if ((DestType->isArrayType() && !S.Context.isReturnableArrayType()) ||
DestType->isFunctionType()) {
S.Diag(E->getExprLoc(), diag::err_func_returning_array_function)
<< DestType->isFunctionType() << DestType;
return ExprError();
Expand Down
7 changes: 5 additions & 2 deletions clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2530,7 +2530,8 @@ QualType Sema::BuildMatrixType(QualType ElementTy, Expr *NumRows, Expr *NumCols,
}

bool Sema::CheckFunctionReturnType(QualType T, SourceLocation Loc) {
if (T->isArrayType() || T->isFunctionType()) {
if ((T->isArrayType() && !Context.isReturnableArrayType()) ||
T->isFunctionType()) {
Diag(Loc, diag::err_func_returning_array_function)
<< T->isFunctionType() << T;
return true;
Expand Down Expand Up @@ -4934,7 +4935,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,

// C99 6.7.5.3p1: The return type may not be a function or array type.
// For conversion functions, we'll diagnose this particular error later.
if (!D.isInvalidType() && (T->isArrayType() || T->isFunctionType()) &&
if (!D.isInvalidType() &&
((T->isArrayType() && !S.Context.isReturnableArrayType()) ||
T->isFunctionType()) &&
(D.getName().getKind() !=
UnqualifiedIdKind::IK_ConversionFunctionId)) {
unsigned diagID = diag::err_func_returning_array_function;
Expand Down
33 changes: 33 additions & 0 deletions clang/test/CodeGenHLSL/BasicFeatures/ArrayReturn.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -disable-llvm-passes -emit-llvm -finclude-default-header -o - %s | FileCheck %s

typedef int Foo[2];

// CHECK-LABEL: define void {{.*}}boop{{.*}}(ptr dead_on_unwind noalias writable sret([2 x i32]) align 4 %agg.result)
// CHECK: [[G:%.*]] = alloca [2 x i32], align 4
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[G]], ptr align 4 {{.*}}, i32 8, i1 false)
// CHECK-NEXT: [[AIB:%.*]] = getelementptr inbounds [2 x i32], ptr %agg.result, i32 0, i32 0
// CHECK-NEXT: br label %arrayinit.body
// CHECK: arrayinit.body:
// CHECK-NEXT: [[AII:%.*]] = phi i32 [ 0, %entry ], [ %arrayinit.next, %arrayinit.body ]
// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds i32, ptr [[AIB]], i32 [[AII]]
// CHECK-NEXT: [[AI:%.*]] = getelementptr inbounds nuw [2 x i32], ptr [[G]], i32 0, i32 [[AII]]
// CHECK-NEXT: [[Y:%.*]] = load i32, ptr [[AI]], align 4
// CHECK-NEXT: store i32 [[Y]], ptr [[X]], align 4
// CHECK-NEXT: [[AIN:%.*]] = add nuw i32 [[AII]], 1
// CHECK-NEXT: [[AID:%.*]] = icmp eq i32 [[AIN]], 2
// CHECK-NEXT: br i1 [[AID]], label %arrayinit.end, label %arrayinit.body
// CHECK: arrayinit.end:
// CHECK-NEXT: ret void
export Foo boop() {
Foo G = {1,2};
return G;
}

// CHECK-LABEL: define void {{.*}}foo{{.*}}(ptr dead_on_unwind noalias writable sret([2 x i32]) align 4 %agg.result)
// CHECK: store i32 1, ptr %agg.result, align 4
// CHECK-NEXT: [[E:%.*]] = getelementptr inbounds i32, ptr %agg.result, i32 1
// CHECK-NEXT: store i32 2, ptr [[E]], align 4
// CHECK-NEXT: ret void
export int foo()[2] {
return {1,2};
}