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

Conversation

llvm-beanz
Copy link
Collaborator

This change allows array variables to copy-initialize from other arrays. It also corrects a small error in HLSL C-Style casting that did not error on casting to arrays if elementwise and splat conversions fail.

Fixes #127551

This change allows array variables to copy-initialize from other
arrays. It also corrects a small error in HLSL C-Style casting that did
not error on casting to arrays if elementwise and splat conversions
fail.

Fixes llvm#127551
@llvm-beanz llvm-beanz requested a review from spall February 18, 2025 02:46
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" HLSL HLSL Language Support labels Feb 18, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 18, 2025

@llvm/pr-subscribers-hlsl

@llvm/pr-subscribers-clang

Author: Chris B (llvm-beanz)

Changes

This change allows array variables to copy-initialize from other arrays. It also corrects a small error in HLSL C-Style casting that did not error on casting to arrays if elementwise and splat conversions fail.

Fixes #127551


Full diff: https://github.com/llvm/llvm-project/pull/127557.diff

4 Files Affected:

  • (modified) clang/lib/Sema/SemaCast.cpp (+39-31)
  • (modified) clang/lib/Sema/SemaInit.cpp (+12)
  • (added) clang/test/SemaHLSL/Language/AssignArray.hlsl (+34)
  • (modified) clang/test/SemaHLSL/Language/ElementwiseCast-errors.hlsl (+1-1)
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index 8972957ded9f5..3ab8598e513c6 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -2778,37 +2778,45 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle,
                                   : 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;
+  // HLSL has several unique forms of C-style casts which support aggregate to
+  // aggregate casting.
+  if (Self.getLangOpts().HLSL) {
+    // 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;
+    }
+
+    // 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;
+    }
+    if (DestType->isArrayType()) {
+      Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
+          << 4 << SrcTy << DestType;
+      SrcExpr = ExprError();
+      return;
+    }
   }
 
   if (ValueKind == VK_PRValue && !DestType->isRecordType() &&
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 6a76e6d74a4b0..a34005bf376aa 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -6585,6 +6585,18 @@ void InitializationSequence::InitializeFrom(Sema &S,
       }
     }
 
+    if (S.getLangOpts().HLSL && Initializer && isa<ConstantArrayType>(DestAT)) {
+      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) &&
diff --git a/clang/test/SemaHLSL/Language/AssignArray.hlsl b/clang/test/SemaHLSL/Language/AssignArray.hlsl
new file mode 100644
index 0000000000000..1f813e7a350b1
--- /dev/null
+++ b/clang/test/SemaHLSL/Language/AssignArray.hlsl
@@ -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;
+}
+
+
+
+
diff --git a/clang/test/SemaHLSL/Language/ElementwiseCast-errors.hlsl b/clang/test/SemaHLSL/Language/ElementwiseCast-errors.hlsl
index 9417249383469..30591507b3260 100644
--- a/clang/test/SemaHLSL/Language/ElementwiseCast-errors.hlsl
+++ b/clang/test/SemaHLSL/Language/ElementwiseCast-errors.hlsl
@@ -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 {

Copy link
Contributor

@spall spall left a comment

Choose a reason for hiding this comment

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

LGTM

@@ -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.

Comment on lines 2938 to 2939
}
if (DestType->isArrayType()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

A blank line here would be aesthetically pleasing

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Could also use a comment... Added both.

@llvm-beanz llvm-beanz merged commit 715edd7 into llvm:main Feb 19, 2025
8 checks passed
@damyanp damyanp moved this to Closed in HLSL Support Apr 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category HLSL HLSL Language Support
Projects
Status: Closed
Development

Successfully merging this pull request may close these issues.

[HLSL] Allow array to be initialized from an array of the same type
4 participants