Skip to content

[HLSL] Allow arrays to copy-initialize #127557

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
87 changes: 54 additions & 33 deletions clang/lib/Sema/SemaCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ namespace {
void CheckStaticCast();
void CheckDynamicCast();
void CheckCXXCStyleCast(bool FunctionalCast, bool ListInitialization);
bool CheckHLSLCStyleCast(CheckedConversionKind CCK);
void CheckCStyleCast();
void CheckBuiltinBitCast();
void CheckAddrspaceCast();
Expand Down Expand Up @@ -2776,39 +2777,9 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle,
CheckedConversionKind CCK = FunctionalStyle
? CheckedConversionKind::FunctionalCast
: CheckedConversionKind::CStyleCast;

QualType SrcTy = SrcExpr.get()->getType();
// This case should not trigger on regular vector cast, vector truncation
if (Self.getLangOpts().HLSL &&
Self.HLSL().CanPerformElementwiseCast(SrcExpr.get(), DestType)) {
if (SrcTy->isConstantArrayType())
SrcExpr = Self.ImpCastExprToType(
SrcExpr.get(), Self.Context.getArrayParameterType(SrcTy),
CK_HLSLArrayRValue, VK_PRValue, nullptr, CCK);
Kind = CK_HLSLElementwiseCast;
return;
}

// This case should not trigger on regular vector splat
// If the relative order of this and the HLSLElementWise cast checks
// are changed, it might change which cast handles what in a few cases
if (Self.getLangOpts().HLSL &&
Self.HLSL().CanPerformAggregateSplatCast(SrcExpr.get(), DestType)) {
const VectorType *VT = SrcTy->getAs<VectorType>();
// change splat from vec1 case to splat from scalar
if (VT && VT->getNumElements() == 1)
SrcExpr = Self.ImpCastExprToType(
SrcExpr.get(), VT->getElementType(), CK_HLSLVectorTruncation,
SrcExpr.get()->getValueKind(), nullptr, CCK);
// Inserting a scalar cast here allows for a simplified codegen in
// the case the destTy is a vector
if (const VectorType *DVT = DestType->getAs<VectorType>())
SrcExpr = Self.ImpCastExprToType(
SrcExpr.get(), DVT->getElementType(),
Self.PrepareScalarCast(SrcExpr, DVT->getElementType()),
SrcExpr.get()->getValueKind(), nullptr, CCK);
Kind = CK_HLSLAggregateSplatCast;
return;
if (Self.getLangOpts().HLSL) {
if (CheckHLSLCStyleCast(CCK))
return;
}

if (ValueKind == VK_PRValue && !DestType->isRecordType() &&
Expand Down Expand Up @@ -2927,6 +2898,56 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle,
}
}

// CheckHLSLCStyleCast - Returns `true` ihe cast is handled or errored as an
// HLSL-specific cast. Returns false if the cast should be checked as a CXX
// C-Style cast.
bool CastOperation::CheckHLSLCStyleCast(CheckedConversionKind CCK) {
assert(Self.getLangOpts().HLSL && "Must be HLSL!");
QualType SrcTy = SrcExpr.get()->getType();
// HLSL has several unique forms of C-style casts which support aggregate to
// aggregate casting.
// This case should not trigger on regular vector cast, vector truncation
if (Self.HLSL().CanPerformElementwiseCast(SrcExpr.get(), DestType)) {
if (SrcTy->isConstantArrayType())
SrcExpr = Self.ImpCastExprToType(
SrcExpr.get(), Self.Context.getArrayParameterType(SrcTy),
CK_HLSLArrayRValue, VK_PRValue, nullptr, CCK);
Kind = CK_HLSLElementwiseCast;
return true;
}

// This case should not trigger on regular vector splat
// If the relative order of this and the HLSLElementWise cast checks
// are changed, it might change which cast handles what in a few cases
if (Self.HLSL().CanPerformAggregateSplatCast(SrcExpr.get(), DestType)) {
const VectorType *VT = SrcTy->getAs<VectorType>();
// change splat from vec1 case to splat from scalar
if (VT && VT->getNumElements() == 1)
SrcExpr = Self.ImpCastExprToType(
SrcExpr.get(), VT->getElementType(), CK_HLSLVectorTruncation,
SrcExpr.get()->getValueKind(), nullptr, CCK);
// Inserting a scalar cast here allows for a simplified codegen in
// the case the destTy is a vector
if (const VectorType *DVT = DestType->getAs<VectorType>())
SrcExpr = Self.ImpCastExprToType(
SrcExpr.get(), DVT->getElementType(),
Self.PrepareScalarCast(SrcExpr, DVT->getElementType()),
SrcExpr.get()->getValueKind(), nullptr, CCK);
Kind = CK_HLSLAggregateSplatCast;
return true;
}

// If the destination is an array, we've exhausted the valid HLSL casts, so we
// should emit a dignostic and stop processing.
if (DestType->isArrayType()) {
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
<< 4 << SrcTy << DestType;
SrcExpr = ExprError();
return true;
}
return false;
}

/// DiagnoseBadFunctionCast - Warn whenever a function call is cast to a
/// non-matching type. Such as enum function call to int, int call to
/// pointer; etc. Cast to 'void' is an exception.
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6585,6 +6585,18 @@ void InitializationSequence::InitializeFrom(Sema &S,
}
}

if (S.getLangOpts().HLSL && Initializer && isa<ConstantArrayType>(DestAT)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be nice to consolidate this if statement with the one below, unless HLSL Arrays can always be copy initialized, then it might be nicer left separate.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Looking at this a bit. canPerformArrayCopy fails in cases that would otherwise be valid in HLSL. At least for HLSL today we can always copy an array if the source and destination type are the same, so we don't need to check that.

QualType SrcType = Entity.getType();
if (SrcType->isArrayParameterType())
SrcType =
cast<ArrayParameterType>(SrcType)->getConstantArrayType(Context);
if (S.Context.hasSameUnqualifiedType(DestType, SrcType)) {
TryArrayCopy(S, Kind, Entity, Initializer, DestType, *this,
TreatUnavailableAsInvalid);
return;
}
}

// Some kinds of initialization permit an array to be initialized from
// another array of the same type, and perform elementwise initialization.
if (Initializer && isa<ConstantArrayType>(DestAT) &&
Expand Down
34 changes: 34 additions & 0 deletions clang/test/SemaHLSL/Language/AssignArray.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library %s -ast-dump | FileCheck %s

typedef vector<int,4> int8[2];

export void fn(int8 A) {
int8 a = {A};
// CHECK-LABEL: VarDecl {{.*}} b 'int8':'vector<int, 4>[2]' cinit
// CHECK-NEXT: ArrayInitLoopExpr {{.*}} 'int8':'vector<int, 4>[2]'
// CHECK-NEXT: OpaqueValueExpr {{.*}} 'int8':'vector<int, 4>[2]' lvalue
// CHECK-NEXT: DeclRefExpr {{.*}} 'int8':'vector<int, 4>[2]' lvalue Var {{.*}} 'a' 'int8':'vector<int, 4>[2]'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<int, 4>' <LValueToRValue>
// CHECK-NEXT: ArraySubscriptExpr {{.*}} 'vector<int, 4>' lvalue
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<int, 4> *' <ArrayToPointerDecay>
// CHECK-NEXT: OpaqueValueExpr {{.*}} 'int8':'vector<int, 4>[2]' lvalue
// CHECK-NEXT: DeclRefExpr {{.*}} 'int8':'vector<int, 4>[2]' lvalue Var {{.*}} 'a' 'int8':'vector<int, 4>[2]'
// CHECK-NEXT: ArrayInitIndexExpr {{.*}} 'unsigned long'
int8 b = a;

// CHECK-LABEL: VarDecl {{.*}} c 'int8':'vector<int, 4>[2]' cinit
// CHECK-NEXT: ArrayInitLoopExpr {{.*}} 'int8':'vector<int, 4>[2]'
// CHECK-NEXT: OpaqueValueExpr {{.*}} 'vector<int, 4>[2]' lvalue
// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 4>[2]' lvalue ParmVar {{.*}} 'A' 'vector<int, 4>[2]'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<int, 4>' <LValueToRValue>
// CHECK-NEXT: ArraySubscriptExpr {{.*}} 'vector<int, 4>' lvalue
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<int, 4> *' <ArrayToPointerDecay>
// CHECK-NEXT: OpaqueValueExpr {{.*}} 'vector<int, 4>[2]' lvalue
// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 4>[2]' lvalue ParmVar {{.*}} 'A' 'vector<int, 4>[2]'
// CHECK-NEXT: ArrayInitIndexExpr {{.*}} 'unsigned long'
int8 c = A;
}




2 changes: 1 addition & 1 deletion clang/test/SemaHLSL/Language/ElementwiseCast-errors.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export void cantCast() {
int A[3] = {1,2,3};
int B[4] = {1,2,3,4};
B = (int[4])A;
// expected-error@-1 {{C-style cast from 'int *' to 'int[4]' is not allowed}}
// expected-error@-1 {{C-style cast from 'int[3]' to 'int[4]' is not allowed}}
}

struct S {
Expand Down