Skip to content

Conversation

@michele-scandale
Copy link
Contributor

This is a follow-up of 13aac46. This commit adjusts the implementation of hasBooleanRepresentation to have a similar behavior as the one of hasIntegerRepresentation. In particular vector of booleans should be handled in hasBooleanRepresentation, while _Atomic(bool) should not.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen IR generation bugs: mangling, exceptions, etc. labels Apr 16, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 16, 2025

@llvm/pr-subscribers-clang-codegen

Author: Michele Scandale (michele-scandale)

Changes

This is a follow-up of 13aac46. This commit adjusts the implementation of hasBooleanRepresentation to have a similar behavior as the one of hasIntegerRepresentation. In particular vector of booleans should be handled in hasBooleanRepresentation, while _Atomic(bool) should not.


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

5 Files Affected:

  • (modified) clang/include/clang/AST/Decl.h (+8)
  • (modified) clang/include/clang/AST/Type.h (+9-1)
  • (modified) clang/lib/AST/Type.cpp (+3-10)
  • (modified) clang/lib/CodeGen/CGExpr.cpp (+13-7)
  • (modified) clang/lib/Sema/SemaType.cpp (+1-1)
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 3faf63e395a08..07ada202075a2 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -5238,6 +5238,14 @@ inline bool IsEnumDeclScoped(EnumDecl *ED) {
   return ED->isScoped();
 }
 
+/// Return the integer type corresponding to the given decl.
+///
+/// We use this function to break a cycle between the inline definitions in
+/// Type.h and Decl.h.
+inline QualType GetEnumDeclIntegerType(EnumDecl *ED) {
+  return ED->getIntegerType();
+}
+
 /// OpenMP variants are mangled early based on their OpenMP context selector.
 /// The new name looks likes this:
 ///  <name> + OpenMPVariantManglingSeparatorStr + <mangled OpenMP context>
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 5bf036e3347eb..ce6904abcc71a 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2774,7 +2774,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   bool hasFloatingRepresentation() const;
 
   /// Determine whether this type has a boolean representation
-  /// of some sort.
+  /// of some sort, e.g., it is a boolean type or a vector thereof.
   bool hasBooleanRepresentation() const;
 
   // Type Checking Functions: Check to see if this type is structurally the
@@ -8531,6 +8531,7 @@ inline bool Type::isNullPtrType() const {
 
 bool IsEnumDeclComplete(EnumDecl *);
 bool IsEnumDeclScoped(EnumDecl *);
+QualType GetEnumDeclIntegerType(EnumDecl *);
 
 inline bool Type::isIntegerType() const {
   if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
@@ -8623,6 +8624,13 @@ inline bool Type::isIntegralOrEnumerationType() const {
 inline bool Type::isBooleanType() const {
   if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
     return BT->getKind() == BuiltinType::Bool;
+  if (const EnumType *ET = dyn_cast<EnumType>(CanonicalType)) {
+    // Incomplete enum types are not treated as integer types.
+    // FIXME: In C++, enum types are never integer types.
+    return IsEnumDeclComplete(ET->getDecl()) &&
+           !IsEnumDeclScoped(ET->getDecl()) &&
+           GetEnumDeclIntegerType(ET->getDecl())->isBooleanType();
+  }
   return false;
 }
 
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 53620003c9655..b456f43d39224 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2336,16 +2336,9 @@ bool Type::isArithmeticType() const {
 }
 
 bool Type::hasBooleanRepresentation() const {
-  if (isBooleanType())
-    return true;
-
-  if (const EnumType *ET = getAs<EnumType>())
-    return ET->getDecl()->getIntegerType()->isBooleanType();
-
-  if (const AtomicType *AT = getAs<AtomicType>())
-    return AT->getValueType()->hasBooleanRepresentation();
-
-  return false;
+  if (const auto *VT = dyn_cast<VectorType>(CanonicalType))
+    return VT->getElementType()->isBooleanType();
+  return isBooleanType();
 }
 
 Type::ScalarTypeKind Type::getScalarTypeKind() const {
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index abb88477062fc..786a56eed7ed5 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1920,7 +1920,7 @@ static bool getRangeForType(CodeGenFunction &CGF, QualType Ty,
 llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) {
   llvm::APInt Min, End;
   if (!getRangeForType(*this, Ty, Min, End, CGM.getCodeGenOpts().StrictEnums,
-                       Ty->hasBooleanRepresentation()))
+                       Ty->hasBooleanRepresentation() && !Ty->isVectorType()))
     return nullptr;
 
   llvm::MDBuilder MDHelper(getLLVMContext());
@@ -1948,7 +1948,7 @@ bool CodeGenFunction::EmitScalarRangeCheck(llvm::Value *Value, QualType Ty,
   if (!HasBoolCheck && !HasEnumCheck)
     return false;
 
-  bool IsBool = Ty->hasBooleanRepresentation() ||
+  bool IsBool = (Ty->hasBooleanRepresentation() && !Ty->isVectorType()) ||
                 NSAPI(CGM.getContext()).isObjCBOOLType(Ty);
   bool NeedsBoolCheck = HasBoolCheck && IsBool;
   bool NeedsEnumCheck = HasEnumCheck && Ty->getAs<EnumType>();
@@ -2068,11 +2068,8 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(Address Addr, bool Volatile,
 /// by ConvertType) to its load/store type (as returned by
 /// convertTypeForLoadStore).
 llvm::Value *CodeGenFunction::EmitToMemory(llvm::Value *Value, QualType Ty) {
-  if (Ty->hasBooleanRepresentation() || Ty->isBitIntType()) {
-    llvm::Type *StoreTy = convertTypeForLoadStore(Ty, Value->getType());
-    bool Signed = Ty->isSignedIntegerOrEnumerationType();
-    return Builder.CreateIntCast(Value, StoreTy, Signed, "storedv");
-  }
+  if (auto *AtomicTy = Ty->getAs<AtomicType>())
+    Ty = AtomicTy->getValueType();
 
   if (Ty->isExtVectorBoolType()) {
     llvm::Type *StoreTy = convertTypeForLoadStore(Ty, Value->getType());
@@ -2088,6 +2085,12 @@ llvm::Value *CodeGenFunction::EmitToMemory(llvm::Value *Value, QualType Ty) {
     Value = Builder.CreateBitCast(Value, StoreTy);
   }
 
+  if (Ty->hasBooleanRepresentation() || Ty->isBitIntType()) {
+    llvm::Type *StoreTy = convertTypeForLoadStore(Ty, Value->getType());
+    bool Signed = Ty->isSignedIntegerOrEnumerationType();
+    return Builder.CreateIntCast(Value, StoreTy, Signed, "storedv");
+  }
+
   return Value;
 }
 
@@ -2095,6 +2098,9 @@ llvm::Value *CodeGenFunction::EmitToMemory(llvm::Value *Value, QualType Ty) {
 /// by convertTypeForLoadStore) to its primary IR type (as returned
 /// by ConvertType).
 llvm::Value *CodeGenFunction::EmitFromMemory(llvm::Value *Value, QualType Ty) {
+  if (auto *AtomicTy = Ty->getAs<AtomicType>())
+    Ty = AtomicTy->getValueType();
+
   if (Ty->isPackedVectorBoolType(getContext())) {
     const auto *RawIntTy = Value->getType();
 
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index eba7267904fb2..427905a4ddafe 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -9955,7 +9955,7 @@ QualType Sema::BuiltinChangeSignedness(QualType BaseType, UTTKind UKind,
                                        SourceLocation Loc) {
   bool IsMakeSigned = UKind == UnaryTransformType::MakeSigned;
   if ((!BaseType->isIntegerType() && !BaseType->isEnumeralType()) ||
-      BaseType->isBooleanType() ||
+      (BaseType->isBooleanType() && !BaseType->isEnumeralType()) ||
       (BaseType->isBitIntType() &&
        BaseType->getAs<BitIntType>()->getNumBits() < 2)) {
     Diag(Loc, diag::err_make_signed_integral_only)

@llvmbot
Copy link
Member

llvmbot commented Apr 16, 2025

@llvm/pr-subscribers-clang

Author: Michele Scandale (michele-scandale)

Changes

This is a follow-up of 13aac46. This commit adjusts the implementation of hasBooleanRepresentation to have a similar behavior as the one of hasIntegerRepresentation. In particular vector of booleans should be handled in hasBooleanRepresentation, while _Atomic(bool) should not.


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

5 Files Affected:

  • (modified) clang/include/clang/AST/Decl.h (+8)
  • (modified) clang/include/clang/AST/Type.h (+9-1)
  • (modified) clang/lib/AST/Type.cpp (+3-10)
  • (modified) clang/lib/CodeGen/CGExpr.cpp (+13-7)
  • (modified) clang/lib/Sema/SemaType.cpp (+1-1)
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 3faf63e395a08..07ada202075a2 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -5238,6 +5238,14 @@ inline bool IsEnumDeclScoped(EnumDecl *ED) {
   return ED->isScoped();
 }
 
+/// Return the integer type corresponding to the given decl.
+///
+/// We use this function to break a cycle between the inline definitions in
+/// Type.h and Decl.h.
+inline QualType GetEnumDeclIntegerType(EnumDecl *ED) {
+  return ED->getIntegerType();
+}
+
 /// OpenMP variants are mangled early based on their OpenMP context selector.
 /// The new name looks likes this:
 ///  <name> + OpenMPVariantManglingSeparatorStr + <mangled OpenMP context>
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 5bf036e3347eb..ce6904abcc71a 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2774,7 +2774,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   bool hasFloatingRepresentation() const;
 
   /// Determine whether this type has a boolean representation
-  /// of some sort.
+  /// of some sort, e.g., it is a boolean type or a vector thereof.
   bool hasBooleanRepresentation() const;
 
   // Type Checking Functions: Check to see if this type is structurally the
@@ -8531,6 +8531,7 @@ inline bool Type::isNullPtrType() const {
 
 bool IsEnumDeclComplete(EnumDecl *);
 bool IsEnumDeclScoped(EnumDecl *);
+QualType GetEnumDeclIntegerType(EnumDecl *);
 
 inline bool Type::isIntegerType() const {
   if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
@@ -8623,6 +8624,13 @@ inline bool Type::isIntegralOrEnumerationType() const {
 inline bool Type::isBooleanType() const {
   if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
     return BT->getKind() == BuiltinType::Bool;
+  if (const EnumType *ET = dyn_cast<EnumType>(CanonicalType)) {
+    // Incomplete enum types are not treated as integer types.
+    // FIXME: In C++, enum types are never integer types.
+    return IsEnumDeclComplete(ET->getDecl()) &&
+           !IsEnumDeclScoped(ET->getDecl()) &&
+           GetEnumDeclIntegerType(ET->getDecl())->isBooleanType();
+  }
   return false;
 }
 
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 53620003c9655..b456f43d39224 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2336,16 +2336,9 @@ bool Type::isArithmeticType() const {
 }
 
 bool Type::hasBooleanRepresentation() const {
-  if (isBooleanType())
-    return true;
-
-  if (const EnumType *ET = getAs<EnumType>())
-    return ET->getDecl()->getIntegerType()->isBooleanType();
-
-  if (const AtomicType *AT = getAs<AtomicType>())
-    return AT->getValueType()->hasBooleanRepresentation();
-
-  return false;
+  if (const auto *VT = dyn_cast<VectorType>(CanonicalType))
+    return VT->getElementType()->isBooleanType();
+  return isBooleanType();
 }
 
 Type::ScalarTypeKind Type::getScalarTypeKind() const {
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index abb88477062fc..786a56eed7ed5 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1920,7 +1920,7 @@ static bool getRangeForType(CodeGenFunction &CGF, QualType Ty,
 llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) {
   llvm::APInt Min, End;
   if (!getRangeForType(*this, Ty, Min, End, CGM.getCodeGenOpts().StrictEnums,
-                       Ty->hasBooleanRepresentation()))
+                       Ty->hasBooleanRepresentation() && !Ty->isVectorType()))
     return nullptr;
 
   llvm::MDBuilder MDHelper(getLLVMContext());
@@ -1948,7 +1948,7 @@ bool CodeGenFunction::EmitScalarRangeCheck(llvm::Value *Value, QualType Ty,
   if (!HasBoolCheck && !HasEnumCheck)
     return false;
 
-  bool IsBool = Ty->hasBooleanRepresentation() ||
+  bool IsBool = (Ty->hasBooleanRepresentation() && !Ty->isVectorType()) ||
                 NSAPI(CGM.getContext()).isObjCBOOLType(Ty);
   bool NeedsBoolCheck = HasBoolCheck && IsBool;
   bool NeedsEnumCheck = HasEnumCheck && Ty->getAs<EnumType>();
@@ -2068,11 +2068,8 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(Address Addr, bool Volatile,
 /// by ConvertType) to its load/store type (as returned by
 /// convertTypeForLoadStore).
 llvm::Value *CodeGenFunction::EmitToMemory(llvm::Value *Value, QualType Ty) {
-  if (Ty->hasBooleanRepresentation() || Ty->isBitIntType()) {
-    llvm::Type *StoreTy = convertTypeForLoadStore(Ty, Value->getType());
-    bool Signed = Ty->isSignedIntegerOrEnumerationType();
-    return Builder.CreateIntCast(Value, StoreTy, Signed, "storedv");
-  }
+  if (auto *AtomicTy = Ty->getAs<AtomicType>())
+    Ty = AtomicTy->getValueType();
 
   if (Ty->isExtVectorBoolType()) {
     llvm::Type *StoreTy = convertTypeForLoadStore(Ty, Value->getType());
@@ -2088,6 +2085,12 @@ llvm::Value *CodeGenFunction::EmitToMemory(llvm::Value *Value, QualType Ty) {
     Value = Builder.CreateBitCast(Value, StoreTy);
   }
 
+  if (Ty->hasBooleanRepresentation() || Ty->isBitIntType()) {
+    llvm::Type *StoreTy = convertTypeForLoadStore(Ty, Value->getType());
+    bool Signed = Ty->isSignedIntegerOrEnumerationType();
+    return Builder.CreateIntCast(Value, StoreTy, Signed, "storedv");
+  }
+
   return Value;
 }
 
@@ -2095,6 +2098,9 @@ llvm::Value *CodeGenFunction::EmitToMemory(llvm::Value *Value, QualType Ty) {
 /// by convertTypeForLoadStore) to its primary IR type (as returned
 /// by ConvertType).
 llvm::Value *CodeGenFunction::EmitFromMemory(llvm::Value *Value, QualType Ty) {
+  if (auto *AtomicTy = Ty->getAs<AtomicType>())
+    Ty = AtomicTy->getValueType();
+
   if (Ty->isPackedVectorBoolType(getContext())) {
     const auto *RawIntTy = Value->getType();
 
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index eba7267904fb2..427905a4ddafe 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -9955,7 +9955,7 @@ QualType Sema::BuiltinChangeSignedness(QualType BaseType, UTTKind UKind,
                                        SourceLocation Loc) {
   bool IsMakeSigned = UKind == UnaryTransformType::MakeSigned;
   if ((!BaseType->isIntegerType() && !BaseType->isEnumeralType()) ||
-      BaseType->isBooleanType() ||
+      (BaseType->isBooleanType() && !BaseType->isEnumeralType()) ||
       (BaseType->isBitIntType() &&
        BaseType->getAs<BitIntType>()->getNumBits() < 2)) {
     Diag(Loc, diag::err_make_signed_integral_only)

inline bool Type::isBooleanType() const {
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
return BT->getKind() == BuiltinType::Bool;
if (const EnumType *ET = dyn_cast<EnumType>(CanonicalType)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is to match isIntegerType that is used by hasIntegerRepresentation.
I'm not entirely sure it makes sense to handle enums here (given also the FIXME comment).
I'll try to see what breaks by removing this.

Copy link
Contributor Author

@michele-scandale michele-scandale Apr 16, 2025

Choose a reason for hiding this comment

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

From a local run of check-clang there are no tests failing with this part of the code. However I can see on small examples how EmitFromMemory and EmitToMemory are not doing anything to converts between i1 and i8 -- which seems wrong.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not entirely sure it makes sense to handle enums here

I don't believe it makes sense to report true for an enum type. An enumeration type is never a boolean type in C or in C++. An enumeration can have a boolean underlying type, but it's still an enumeration type rather than a boolean type.

Copy link
Contributor

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

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

This looks reasonable to me.

Value = Builder.CreateBitCast(Value, StoreTy);
}

if (Ty->hasBooleanRepresentation() || Ty->isBitIntType()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is isExtVectorBoolType() guaranteed to filter out all the cases where a boolean vector type might get us here? It doesn't seem like the previous code had any sort of handling for vectors of bool other than the isExtVectorBoolType handling, but this seems like a potential change in behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll double check. I know there have been changes recently to support a different ABI in the HLSL case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Only ExtVectorType of booleans are supported, the regular VectorType disallow boolean element types (see Sema::BuildVectorType and Sema::BuildExtVectorType).
In the if (Ty->isExtVectorBoolType()) above both kind of vector bool ABIs are handled, so there is no issue there.

/// Converts a scalar value from its load/store type (as returned
/// by convertTypeForLoadStore) to its primary IR type (as returned
/// by ConvertType).
llvm::Value *CodeGenFunction::EmitFromMemory(llvm::Value *Value, QualType Ty) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

In here it is fine to handle the vector of bool types whenTy->hasBooleanRepresentation() as only the non packed ABI will reach that code, and it is fine to generate a Trunc (just like in EmitToMemory a ZExt is generated)

inline bool Type::isBooleanType() const {
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
return BT->getKind() == BuiltinType::Bool;
if (const EnumType *ET = dyn_cast<EnumType>(CanonicalType)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (const EnumType *ET = dyn_cast<EnumType>(CanonicalType)) {
if (const auto *ET = dyn_cast<EnumType>(CanonicalType)) {

///
/// We use this function to break a cycle between the inline definitions in
/// Type.h and Decl.h.
inline QualType GetEnumDeclIntegerType(EnumDecl *ED) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make the parameter const?

Copy link
Contributor

Choose a reason for hiding this comment

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

We've generally been naming new functions with lower-case letters.

inline bool Type::isBooleanType() const {
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
return BT->getKind() == BuiltinType::Bool;
if (const EnumType *ET = dyn_cast<EnumType>(CanonicalType)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not entirely sure it makes sense to handle enums here

I don't believe it makes sense to report true for an enum type. An enumeration type is never a boolean type in C or in C++. An enumeration can have a boolean underlying type, but it's still an enumeration type rather than a boolean type.

// Incomplete enum types are not treated as integer types.
// FIXME: In C++, enum types are never integer types.
return IsEnumDeclComplete(ET->getDecl()) &&
!IsEnumDeclScoped(ET->getDecl()) &&
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why does the the enum being scoped matter?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is just the same code that exist in isIntegerType. I am puzzled by this code as well, but I suppose there is a good reason for it.

Copy link
Contributor

Choose a reason for hiding this comment

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

C specifies that "enumerated types" are integer types, and C++ does not, and isIntegerType is trying to honor that as best it can. It's one of those situations where we need to ask what callers actually want, because in most cases it's probably not this language-specific formal property. If hasIntegerRepresentation is really a question about the representation of the type, it should be ignoring scoped-ness of enums. But it seems to be used in Sema in ways that should definitely exclude scoped enums, like the semantic analysis of __builtin_shuffle_vector and vector math.

isBooleanType should probably continue to check only for bool. It seems reasonable for hasBooleanRepresentation to look through enums, though, and it should probably ignore the scoped-ness of the enum.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

C specifies that "enumerated types" are integer types, and C++ does not, and isIntegerType is trying to honor that as best it can. It's one of those situations where we need to ask what callers actually want, because in most cases it's probably not this language-specific formal property. If hasIntegerRepresentation is really a question about the representation of the type, it should be ignoring scoped-ness of enums. But it seems to be used in Sema in ways that should definitely exclude scoped enums, like the semantic analysis of __builtin_shuffle_vector and vector math.

The check on scoped enum comes from:

commit 21673c4e7ec08457b53798b9879b7cc9a5909eb8
Author: Douglas Gregor <[email protected]>
Date:   Thu May 5 16:13:52 2011 +0000

    Scoped enumerations should not be treated as integer types (in the C
    sense). Fixes <rdar://problem/9366066> by eliminating an inconsistency
    between C++ overloading (which handled scoped enumerations correctly)
    and C binary operator type-checking (which didn't).
    
    llvm-svn: 130924

isBooleanType should probably continue to check only for bool.

Ok.

It seems reasonable for hasBooleanRepresentation to look through enums, though, and it should probably ignore the scoped-ness of the enum.

I can do that, but I'm not super happy about the inconsistency.

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay. My understanding is that supporting CodeGen and its dual-representation handling of boolean types is the immediate purpose of this method. "Representation" is a good name for such a method because this really is about lower-level representation.

At a high level, scoped enums with an underlying type of bool either need to use i8 as both their memory and their scalar lowered type or they need to be doing toMemory/fromMemory operations when moving to/from memory. I think the latter is probably the better situation overall. So I think we really want this method to ignored scoped-ness of enums.

The immediate problem this creates is that we have a bunch of existing methods that are arguably misnamed: they're named something involving "representation", but they're not really being used to ask about low-level representation. Instead, they're vector-inclusive variants of other predicates. I agree this is a bad place to have ended up, because people will naturally expect all the similarly-named methods to behave consistently.

Since the existing methods are arguably misnamed, I wonder if we could reasonably just change them. Since the difference is (AFAIK) purely to make them vector-inclusive, perhaps we should just remove them and have a getNonVectorType() method that clients are expected to call before using the existing predicates.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since the existing methods are arguably misnamed, I wonder if we could reasonably just change them. Since the difference is (AFAIK) purely to make them vector-inclusive, perhaps we should just remove them and have a getNonVectorType() method that clients are expected to call before using the existing predicates.

Just to make sure I'm not misunderstanding things: are you suggesting to just drop the "vector of" portion of the has*Representation and move that onto clients of these APIs, right?

I'm not sure if that can work in general. E.g.

bool Type::hasIntegerRepresentation() const {
  if (const auto *VT = dyn_cast<VectorType>(CanonicalType))
    return VT->getElementType()->isIntegerType();
  if (CanonicalType->isSveVLSBuiltinType()) {
    const auto *VT = cast<BuiltinType>(CanonicalType);
    return VT->getKind() == BuiltinType::SveBool ||
           (VT->getKind() >= BuiltinType::SveInt8 &&
            VT->getKind() <= BuiltinType::SveUint64);
  }
  if (CanonicalType->isRVVVLSBuiltinType()) {
    const auto *VT = cast<BuiltinType>(CanonicalType);
    return (VT->getKind() >= BuiltinType::RvvInt8mf8 &&
            VT->getKind() <= BuiltinType::RvvUint64m8);
  }

  return isIntegerType();
}

This is more than just types where isIntegerType is true, or vector of such types.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. I'm not sure I understand why someone added a bunch of new vector types as individual builtin types (from the names, they sure seem to have element types and otherwise fit the general vector model!), but if that's what we have to deal with, then I suppose I'm just arguing that the methods should be renamed to not say "representation".

///
/// We use this function to break a cycle between the inline definitions in
/// Type.h and Decl.h.
inline QualType GetEnumDeclIntegerType(EnumDecl *ED) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We've generally been naming new functions with lower-case letters.


/// Determine whether this type has a boolean representation
/// of some sort.
/// of some sort, e.g., it is a boolean type or a vector thereof.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// of some sort, e.g., it is a boolean type or a vector thereof.
/// of some sort, i.e., it is a boolean type or a vector thereof.

// Incomplete enum types are not treated as integer types.
// FIXME: In C++, enum types are never integer types.
return IsEnumDeclComplete(ET->getDecl()) &&
!IsEnumDeclScoped(ET->getDecl()) &&
Copy link
Contributor

Choose a reason for hiding this comment

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

C specifies that "enumerated types" are integer types, and C++ does not, and isIntegerType is trying to honor that as best it can. It's one of those situations where we need to ask what callers actually want, because in most cases it's probably not this language-specific formal property. If hasIntegerRepresentation is really a question about the representation of the type, it should be ignoring scoped-ness of enums. But it seems to be used in Sema in ways that should definitely exclude scoped enums, like the semantic analysis of __builtin_shuffle_vector and vector math.

isBooleanType should probably continue to check only for bool. It seems reasonable for hasBooleanRepresentation to look through enums, though, and it should probably ignore the scoped-ness of the enum.

@michele-scandale michele-scandale force-pushed the hasBooleanRepresentation-rework branch from 31777c1 to 17e7377 Compare April 17, 2025 20:39
@michele-scandale
Copy link
Contributor Author

Putting aside for now the naming issue (#136038 (comment)), is there anything left for the changes proposed in this PR?

@AaronBallman
Copy link
Collaborator

Putting aside for now the naming issue (#136038 (comment)), is there anything left for the changes proposed in this PR?

Based on:

... but I think "will this end up as an i1" is a fair summary.

I think this probably should include 1-bit bit-precise integer types in hasBooleanRepresentation(), but it may not be critical.

This is a follow-up of 13aac46.
This commit adjusts the implementation of `hasBooleanRepresentation` to
somewhat aligned to `hasIntegerRepresentation`.
In particular vector of booleans should be handled in
`hasBooleanRepresentation`, while `_Atomic(bool)` should not.
@michele-scandale michele-scandale force-pushed the hasBooleanRepresentation-rework branch from 17e7377 to 359e31e Compare April 21, 2025 19:47
Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

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

LGTM!

@michele-scandale
Copy link
Contributor Author

@rjmccall Are you ok landing this change, and defer the renaming to a separate change?

@rjmccall
Copy link
Contributor

That's fine with me, yeah.

@michele-scandale michele-scandale merged commit 34a4c58 into llvm:main Apr 23, 2025
11 checks passed
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
This is a follow-up of 13aac46.
This commit adjusts the implementation of `hasBooleanRepresentation` to
be somewhat aligned to `hasIntegerRepresentation`.
In particular vector of booleans should be handled in
`hasBooleanRepresentation`, while `_Atomic(bool)` should not.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants