Skip to content

Fix vector types w.r.t. qualified element type. - #197624

Open
michele-scandale wants to merge 2 commits into
llvm:mainfrom
michele-scandale:fix-canonical-vector-type-wrt-elem-type-qualifiers
Open

michele-scandale wants to merge 2 commits into
llvm:mainfrom
michele-scandale:fix-canonical-vector-type-wrt-elem-type-qualifiers

Conversation

@michele-scandale

@michele-scandale michele-scandale commented May 14, 2026 •

Copy link
Copy Markdown
Contributor

This commit changes the way vector types are constructed to ensure that qualifiers on the element type are moved to the vector type itself -- i.e. a canonical vector type has always an unqualified element type.

RFC: https://discourse.llvm.org/t/90783

@llvmorg-github-actions llvmorg-github-actions Bot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels May 14, 2026
@llvmorg-github-actions

llvmorg-github-actions Bot commented May 14, 2026 •

Copy link
Copy Markdown

@llvm/pr-subscribers-clang-tools-extra
@llvm/pr-subscribers-clangir
@llvm/pr-subscribers-clang-analysis
@llvm/pr-subscribers-backend-powerpc
@llvm/pr-subscribers-backend-spir-v
@llvm/pr-subscribers-hlsl
@llvm/pr-subscribers-clang-tidy
@llvm/pr-subscribers-clang-modules
@llvm/pr-subscribers-backend-risc-v
@llvm/pr-subscribers-lldb
@llvm/pr-subscribers-libcxx

@llvm/pr-subscribers-clang

Author: Michele Scandale (michele-scandale)

Changes

This commit changes the way canonical vector types are constructed to ensure that qualifiers on the element type are moved to the vector type iself -- i.e. a canonical vector type has always a unqualified element type.

RFC: https://discourse.llvm.org/t/90783


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

4 Files Affected:

  • (modified) clang/lib/AST/ASTContext.cpp (+62-60)
  • (modified) clang/lib/Sema/SemaTypeTraits.cpp (+4-2)
  • (modified) clang/test/Sema/types.c (+12)
  • (modified) clang/test/SemaCXX/vector.cpp (+14)
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index a0894318dbd53..2c9e852817b7a 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -4654,11 +4654,13 @@ QualType ASTContext::getVectorType(QualType vecType, unsigned NumElts,
   if (VectorType *VTP = VectorTypes.FindNodeOrInsertPos(ID, InsertPos))
     return QualType(VTP, 0);
 
-  // If the element type isn't canonical, this won't be a canonical type either,
-  // so fill in the canonical type field.
+  // If the element type isn't canonical or has qualifiers, this won't be a
+  // canonical type either, so fill in the canonical type field.
   QualType Canonical;
-  if (!vecType.isCanonical()) {
-    Canonical = getVectorType(getCanonicalType(vecType), NumElts, VecKind);
+  if (!vecType.isCanonical() || vecType.hasLocalQualifiers()) {
+    SplitQualType canonSplit = getCanonicalType(vecType).split();
+    Canonical = getVectorType(QualType(canonSplit.Ty, 0), NumElts, VecKind);
+    Canonical = getQualifiedType(Canonical, canonSplit.Quals);
 
     // Get the new insert position for the node we care about.
     VectorType *NewIP = VectorTypes.FindNodeOrInsertPos(ID, InsertPos);
@@ -4674,37 +4676,36 @@ QualType ASTContext::getVectorType(QualType vecType, unsigned NumElts,
 QualType ASTContext::getDependentVectorType(QualType VecType, Expr *SizeExpr,
                                             SourceLocation AttrLoc,
                                             VectorKind VecKind) const {
+  SplitQualType CanonVecType = getCanonicalType(VecType).split();
+
   llvm::FoldingSetNodeID ID;
-  DependentVectorType::Profile(ID, *this, getCanonicalType(VecType), SizeExpr,
-                               VecKind);
+  DependentVectorType::Profile(ID, *this, QualType(CanonVecType.Ty, 0),
+                               SizeExpr, VecKind);
   void *InsertPos = nullptr;
   DependentVectorType *Canon =
       DependentVectorTypes.FindNodeOrInsertPos(ID, InsertPos);
-  DependentVectorType *New;
 
-  if (Canon) {
-    New = new (*this, alignof(DependentVectorType)) DependentVectorType(
-        VecType, QualType(Canon, 0), SizeExpr, AttrLoc, VecKind);
-  } else {
-    QualType CanonVecTy = getCanonicalType(VecType);
-    if (CanonVecTy == VecType) {
-      New = new (*this, alignof(DependentVectorType))
-          DependentVectorType(VecType, QualType(), SizeExpr, AttrLoc, VecKind);
-
-      DependentVectorType *CanonCheck =
-          DependentVectorTypes.FindNodeOrInsertPos(ID, InsertPos);
-      assert(!CanonCheck &&
-             "Dependent-sized vector_size canonical type broken");
-      (void)CanonCheck;
-      DependentVectorTypes.InsertNode(New, InsertPos);
-    } else {
-      QualType CanonTy = getDependentVectorType(CanonVecTy, SizeExpr,
-                                                SourceLocation(), VecKind);
-      New = new (*this, alignof(DependentVectorType))
-          DependentVectorType(VecType, CanonTy, SizeExpr, AttrLoc, VecKind);
-    }
+  // If we don't have one, build one.
+  if (!Canon) {
+    Canon = new (*this, TypeAlignment) DependentVectorType(
+        QualType(CanonVecType.Ty, 0), QualType(), SizeExpr, AttrLoc, VecKind);
+    DependentVectorTypes.InsertNode(Canon, InsertPos);
+    Types.push_back(Canon);
   }
 
+  // Apply qualifiers from the element type to the vector.
+  QualType CanonTy = getQualifiedType(QualType(Canon, 0), CanonVecType.Quals);
+
+  // If we didn't need extra canonicalization for the element type or the size
+  // expression, then just use that as our result.
+  if (QualType(CanonVecType.Ty, 0) == VecType &&
+      Canon->getSizeExpr() == SizeExpr)
+    return CanonTy;
+
+  // Otherwise, we need to build a type which follows the spelling
+  // of the element type.
+  auto *New = new (*this, TypeAlignment)
+      DependentVectorType(VecType, CanonTy, SizeExpr, AttrLoc, VecKind);
   Types.push_back(New);
   return QualType(New, 0);
 }
@@ -4726,11 +4727,13 @@ QualType ASTContext::getExtVectorType(QualType vecType,
   if (VectorType *VTP = VectorTypes.FindNodeOrInsertPos(ID, InsertPos))
     return QualType(VTP, 0);
 
-  // If the element type isn't canonical, this won't be a canonical type either,
-  // so fill in the canonical type field.
+  // If the element type isn't canonical or has qualifiers, this won't be a
+  // canonical type either, so fill in the canonical type field.
   QualType Canonical;
-  if (!vecType.isCanonical()) {
-    Canonical = getExtVectorType(getCanonicalType(vecType), NumElts);
+  if (!vecType.isCanonical() || vecType.hasLocalQualifiers()) {
+    SplitQualType canonSplit = getCanonicalType(vecType).split();
+    Canonical = getExtVectorType(QualType(canonSplit.Ty, 0), NumElts);
+    Canonical = getQualifiedType(Canonical, canonSplit.Quals);
 
     // Get the new insert position for the node we care about.
     VectorType *NewIP = VectorTypes.FindNodeOrInsertPos(ID, InsertPos);
@@ -4744,42 +4747,41 @@ QualType ASTContext::getExtVectorType(QualType vecType,
 }
 
 QualType
-ASTContext::getDependentSizedExtVectorType(QualType vecType,
-                                           Expr *SizeExpr,
+ASTContext::getDependentSizedExtVectorType(QualType VecType, Expr *SizeExpr,
                                            SourceLocation AttrLoc) const {
+  assert(VecKind == VectorType::GenericVector ||
+         VecKind == VectorType::PackedVector);
+
+  SplitQualType CanonVecType = getCanonicalType(VecType).split();
+
   llvm::FoldingSetNodeID ID;
-  DependentSizedExtVectorType::Profile(ID, *this, getCanonicalType(vecType),
+  DependentSizedExtVectorType::Profile(ID, *this, QualType(CanonVecType.Ty, 0),
                                        SizeExpr);
-
   void *InsertPos = nullptr;
   DependentSizedExtVectorType *Canon
     = DependentSizedExtVectorTypes.FindNodeOrInsertPos(ID, InsertPos);
-  DependentSizedExtVectorType *New;
-  if (Canon) {
-    // We already have a canonical version of this array type; use it as
-    // the canonical type for a newly-built type.
-    New = new (*this, alignof(DependentSizedExtVectorType))
-        DependentSizedExtVectorType(vecType, QualType(Canon, 0), SizeExpr,
-                                    AttrLoc);
-  } else {
-    QualType CanonVecTy = getCanonicalType(vecType);
-    if (CanonVecTy == vecType) {
-      New = new (*this, alignof(DependentSizedExtVectorType))
-          DependentSizedExtVectorType(vecType, QualType(), SizeExpr, AttrLoc);
-
-      DependentSizedExtVectorType *CanonCheck
-        = DependentSizedExtVectorTypes.FindNodeOrInsertPos(ID, InsertPos);
-      assert(!CanonCheck && "Dependent-sized ext_vector canonical type broken");
-      (void)CanonCheck;
-      DependentSizedExtVectorTypes.InsertNode(New, InsertPos);
-    } else {
-      QualType CanonExtTy = getDependentSizedExtVectorType(CanonVecTy, SizeExpr,
-                                                           SourceLocation());
-      New = new (*this, alignof(DependentSizedExtVectorType))
-          DependentSizedExtVectorType(vecType, CanonExtTy, SizeExpr, AttrLoc);
-    }
+
+  // If we don't have one, build one.
+  if (!Canon) {
+    Canon = new (*this, TypeAlignment) DependentSizedExtVectorType(
+        QualType(CanonVecType.Ty, 0), QualType(), SizeExpr, AttrLoc);
+    DependentSizedExtVectorTypes.InsertNode(Canon, InsertPos);
+    Types.push_back(Canon);
   }
 
+  // Apply qualifiers from the element type to the vector.
+  QualType CanonTy = getQualifiedType(QualType(Canon, 0), CanonVecType.Quals);
+
+  // If we didn't need extra canonicalization for the element type or the size
+  // expression, then just use that as our result.
+  if (QualType(CanonVecType.Ty, 0) == VecType &&
+      Canon->getSizeExpr() == SizeExpr)
+    return CanonTy;
+
+  // Otherwise, we need to build a type which follows the spelling
+  // of the element type.
+  auto *New = new (*this, TypeAlignment)
+      DependentSizedExtVectorType(VecType, CanonTy, SizeExpr, AttrLoc);
   Types.push_back(New);
   return QualType(New, 0);
 }
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index c79b3f7045ca6..ee89f5a20a289 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -1625,8 +1625,10 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT,
   case BTT_TypeCompatible: {
     // GCC ignores cv-qualifiers on arrays for this builtin.
     Qualifiers LhsQuals, RhsQuals;
-    QualType Lhs = Self.getASTContext().getUnqualifiedArrayType(LhsT, LhsQuals);
-    QualType Rhs = Self.getASTContext().getUnqualifiedArrayType(RhsT, RhsQuals);
+    QualType Lhs = Self.getASTContext().getUnqualifiedArrayType(
+        LhsT.getCanonicalType(), LhsQuals);
+    QualType Rhs = Self.getASTContext().getUnqualifiedArrayType(
+        RhsT.getCanonicalType(), RhsQuals);
     return Self.Context.typesAreCompatible(Lhs, Rhs);
   }
   case BTT_IsConvertible:
diff --git a/clang/test/Sema/types.c b/clang/test/Sema/types.c
index 2be0e6544f3d7..21ee2feabc965 100644
--- a/clang/test/Sema/types.c
+++ b/clang/test/Sema/types.c
@@ -75,6 +75,18 @@ typedef int __attribute__((ext_vector_type(0x100000000))) e2;      // expected-e
 typedef int __attribute__((vector_size((__int128_t)1 << 100))) e3; // expected-error {{vector size too large}}
 typedef int __attribute__((ext_vector_type(0))) e4;                // expected-error {{zero vector size}}
 
+typedef const int cint;
+typedef int __attribute__((vector_size(16))) vec4_int;
+typedef cint __attribute__((vector_size(16))) vec4_cint;
+_Static_assert(__builtin_types_compatible_p(vec4_int, const vec4_int), "'vec4_int' and 'const vec4_int' are not compatible");
+_Static_assert(__builtin_types_compatible_p(vec4_int, volatile vec4_int), "'vec4_int' and 'const vec4_int' are not compatible");
+_Static_assert(__builtin_types_compatible_p(const vec4_int, vec4_cint), "'const vec4_int' and 'vec4_cint' are not compatible");
+typedef int __attribute__((ext_vector_type(4))) ext_vec4_int;
+typedef cint __attribute__((ext_vector_type(4))) ext_vec4_cint;
+_Static_assert(__builtin_types_compatible_p(ext_vec4_int, const ext_vec4_int), "'ext_vec4_int' and 'const ext_vec4_int' are not compatible");
+_Static_assert(__builtin_types_compatible_p(ext_vec4_int, volatile ext_vec4_int), "'ext_vec4_int' and 'const ext_vec4_int' are not compatible");
+_Static_assert(__builtin_types_compatible_p(const ext_vec4_int, ext_vec4_cint), "'const ext_vec4_int' and 'ext_vec4_cint' are not compatible'");
+
 // no support for vector enum type
 enum { e_2 } x3 __attribute__((vector_size(64))); // expected-error {{invalid vector element type}}
 
diff --git a/clang/test/SemaCXX/vector.cpp b/clang/test/SemaCXX/vector.cpp
index 355d93a2b8cee..ef91848c7d31a 100644
--- a/clang/test/SemaCXX/vector.cpp
+++ b/clang/test/SemaCXX/vector.cpp
@@ -817,3 +817,17 @@ namespace GH173347 {
 typedef short __attribute__((__vector_size__(8))) V;
 template <int N> V test(V x) { return (x % 5) * N; }
 }
+
+namespace test_canonical_quals {
+typedef const int cint;
+typedef int __attribute__((vector_size(16))) vec4_int;
+typedef cint __attribute__((vector_size(16))) vec4_cint;
+_Static_assert(!__is_same(vec4_int, const vec4_int), "'vec4_int' and 'const vec4_int' are not different");
+_Static_assert(!__is_same(vec4_int, volatile vec4_int), "'vec4_int' and 'const vec4_int' are not different");
+_Static_assert(__is_same(const vec4_int, vec4_cint), "'const vec4_int' and 'vec4_cint' are different");
+typedef int __attribute__((ext_vector_type(4))) ext_vec4_int;
+typedef cint __attribute__((ext_vector_type(4))) ext_vec4_cint;
+_Static_assert(!__is_same(ext_vec4_int, const ext_vec4_int), "'ext_vec4_int' and 'const ext_vec4_int' are not different");
+_Static_assert(!__is_same(ext_vec4_int, volatile ext_vec4_int), "'ext_vec4_int' and 'const ext_vec4_int' are not different");
+_Static_assert(__is_same(const ext_vec4_int, ext_vec4_cint), "'const ext_vec4_int' and 'ext_vec4_cint' are different");
+} // namespace test_canonical_quals

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changes to getDependentVectorType and getDependentSizedExtVectorType are basically an adaptation of the logic in getDependentSizedArrayType.

@michele-scandale
michele-scandale force-pushed the fix-canonical-vector-type-wrt-elem-type-qualifiers branch from ad9ab1b to 7c115c4 Compare May 14, 2026 06:28
@github-actions

github-actions Bot commented May 14, 2026 •

Copy link
Copy Markdown

🐧 Linux x64 Test Results

  • 94482 tests passed
  • 4365 tests skipped

✅ The build succeeded and all tests passed.

@github-actions

github-actions Bot commented May 14, 2026 •

Copy link
Copy Markdown

🪟 Windows x64 Test Results

  • 60191 tests passed
  • 1975 tests skipped

✅ The build succeeded and all tests passed.

@michele-scandale
michele-scandale force-pushed the fix-canonical-vector-type-wrt-elem-type-qualifiers branch from 7c115c4 to 7aabfa7 Compare May 14, 2026 06:47
@michele-scandale
michele-scandale requested a review from a team as a code owner May 14, 2026 16:40
@llvmorg-github-actions llvmorg-github-actions Bot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label May 14, 2026
@michele-scandale
michele-scandale requested a review from rjmccall May 15, 2026 23:08

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This change shouldn't be needed. getUnqualifiedArrayType/getUnqualifiedType should correctly handle the removal of qualifiers from the vector element type on the non-canonical representation.

@michele-scandale michele-scandale removed lldb backend:ARM backend:AArch64 backend:RISC-V backend:PowerPC clang-tidy clang:modules C++20 modules and Clang Header Modules clang:codegen IR generation bugs: mangling, exceptions, etc. clang:as-a-library libclang and C++ API clang:static analyzer backend:SPIR-V clang:dataflow Clang Dataflow Analysis framework - https://clang.llvm.org/docs/DataFlowAnalysisIntro.html clang:analysis clang:openmp OpenMP related changes to Clang ClangIR Anything related to the ClangIR project clang:bytecode Issues for the clang bytecode constexpr interpreter labels Jun 5, 2026
@michele-scandale

Copy link
Copy Markdown
Contributor Author

Ping

1 similar comment
@michele-scandale

Copy link
Copy Markdown
Contributor Author

Ping

QualType ElementType;

SourceLocation loc;
const ASTContext &Context;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think the type having a Context is appropriate? I thought we mentioned on the other review that this should store the element type with proper qualifiers instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

As mentioned in #198388 (comment):

If it is acceptable to add members to VectorType (and other vector related type classes) then I'd rather just have a const ASTContext & similarly to what has been done for TypeOfExprType.

Precomputing the unqualified type seems quite odd, it's an information that might be never needed. And in terms of memory increase I would still be adding an extra sizeof(void*) in both cases. Also TypeOfExprType and TypeOfType are already doing that to implement desugar. I really don't see a difference in what I'm trying to do here.

Comment thread libcxx/include/__algorithm/find.h Outdated
constexpr size_t __unroll_count = 4;
constexpr size_t __vec_size = __native_vector_size<_Tp>;
using __vec = __simd_vector<_Tp, __vec_size>;
using __vec = __simd_vector<typename remove_cv<_Tp>::type, __vec_size>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
using __vec = __simd_vector<typename remove_cv<_Tp>::type, __vec_size>;
using __vec = __simd_vector<__remove_cv_t<_Tp>, __vec_size>;

Ditto below.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Given that we seemingly have to apply it everywhere I wonder whether it wouldn't be better to move it into the alias instead.

@michele-scandale michele-scandale Jun 25, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If you have the following:

template <class _ArithmeticT, size_t _Np>
using __simd_vector __attribute__((__ext_vector_type__(_Np))) _LIBCPP_NODEBUG = __remove_cv_t<_ArithmeticT>;

then the following cannot work

template <class _Tp, size_t _Np>
inline constexpr size_t __simd_vector_size_v<__simd_vector<_Tp, _Np>> = _Np;

because the presence of __remove_cv_t breaks the template type matching. This affects also other functions.

Is it fine to have a __simd_vector_impl and implement the vector traits/helpers using __simd_vector_impl for template parameter matching purposes, and then expose __simd_vector<T, N> = __simd_vector_impl<__remove_cv_t<T>, N>?

@michele-scandale

Copy link
Copy Markdown
Contributor Author

Ping

@michele-scandale

Copy link
Copy Markdown
Contributor Author

Ping

cc @erichkeane, @frederick-vs-ja, @philnik777

@philnik777

Copy link
Copy Markdown
Contributor

I'd like to see a conclusion to the RFC before spending more time reviewing this. (CC @AaronBallman)

@AaronBallman

Copy link
Copy Markdown
Contributor

I'd like to see a conclusion to the RFC before spending more time reviewing this. (CC @AaronBallman)

It's on the Clang Area Team's schedule; I'll try to ensure we get to it at the next meeting (Thur Jul 20).

Comment thread clang/test/Sema/types.c Outdated
typedef cint __attribute__((ext_vector_type(4))) ext_vec4_cint;
_Static_assert(__builtin_types_compatible_p(ext_vec4_int, const ext_vec4_int), "'ext_vec4_int' and 'const ext_vec4_int' are not compatible");
_Static_assert(__builtin_types_compatible_p(ext_vec4_int, volatile ext_vec4_int), "'ext_vec4_int' and 'const ext_vec4_int' are not compatible");
_Static_assert(__builtin_types_compatible_p(const ext_vec4_int, ext_vec4_cint), "'const ext_vec4_int' and 'ext_vec4_cint' are not compatible'");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think this is testing what you want to test... maybe use something more like:

static_assert(__builtin_types_compatible_p(vec4_cint*, const vec4_int*), "");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Makes sense. I've updated the test and rebased the PR

This commit changes the way vector types are constructed to ensure that
qualifiers on the element type are moved to the vector type itself --
i.e. a canonical vector type has always an unqualified element type.

RFC: https://discourse.llvm.org/t/90783
@michele-scandale

Copy link
Copy Markdown
Contributor Author

@efriedma-quic @AaronBallman Any update here?

This branch has not been deployed

No deployments
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 libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. OpenCL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants