Skip to content
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

Add bit-precise overloads for builtin operators #84755

Merged

Conversation

AaronBallman
Copy link
Collaborator

We previously were not adding them to the candidate set and so use of a bit-precise integer as a class member could lead to ambiguous overload sets.

Fixes #82998

We previously were not adding them to the candidate set and so use of a
bit-precise integer as a class member could lead to ambiguous overload
sets.

Fixes llvm#82998
@AaronBallman AaronBallman added c++ clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Mar 11, 2024
@llvmbot llvmbot added the clang Clang issues not falling into any other category label Mar 11, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Mar 11, 2024

@llvm/pr-subscribers-clang

Author: Aaron Ballman (AaronBallman)

Changes

We previously were not adding them to the candidate set and so use of a bit-precise integer as a class member could lead to ambiguous overload sets.

Fixes #82998


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

3 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+3)
  • (modified) clang/lib/Sema/SemaOverload.cpp (+23)
  • (added) clang/test/SemaCXX/overload-bitint.cpp (+42)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index bce27dc8c4a996..5b3a5b6c9bf23e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -262,6 +262,9 @@ Bug Fixes in This Version
   operator.
   Fixes (#GH83267).
 
+- Clang now correctly generates overloads for bit-precise integer types for
+  builtin operators in C++. Fixes #GH82998.
+
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index b0c693f078efe2..675f6f9ebcda5f 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -8516,6 +8516,9 @@ class BuiltinCandidateTypeSet  {
   /// candidates.
   TypeSet MatrixTypes;
 
+  /// The set of _BitInt types that will be used in the built-in candidates.
+  TypeSet BitIntTypes;
+
   /// A flag indicating non-record types are viable candidates
   bool HasNonRecordTypes;
 
@@ -8564,6 +8567,7 @@ class BuiltinCandidateTypeSet  {
   }
   llvm::iterator_range<iterator> vector_types() { return VectorTypes; }
   llvm::iterator_range<iterator> matrix_types() { return MatrixTypes; }
+  llvm::iterator_range<iterator> bitint_types() { return BitIntTypes; }
 
   bool containsMatrixType(QualType Ty) const { return MatrixTypes.count(Ty); }
   bool hasNonRecordTypes() { return HasNonRecordTypes; }
@@ -8735,6 +8739,9 @@ BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty,
   } else if (Ty->isEnumeralType()) {
     HasArithmeticOrEnumeralTypes = true;
     EnumerationTypes.insert(Ty);
+  } else if (Ty->isBitIntType()) {
+    HasArithmeticOrEnumeralTypes = true;
+    BitIntTypes.insert(Ty);
   } else if (Ty->isVectorType()) {
     // We treat vector types as arithmetic types in many contexts as an
     // extension.
@@ -8955,6 +8962,22 @@ class BuiltinOperatorOverloadBuilder {
         (S.Context.getAuxTargetInfo() &&
          S.Context.getAuxTargetInfo()->hasInt128Type()))
       ArithmeticTypes.push_back(S.Context.UnsignedInt128Ty);
+
+    /// _BitInt overloads are a bit special. We don't want to add candidates
+    /// for the entire family of _BitInt types, so instead we only add
+    /// candidates for the unique, unqualified _BitInt types present in the
+    /// candidate type set. The candidate set already handled ensuring the type
+    /// is unqualified and canonical, but because we're adding from N different
+    /// sets, we need to do some extra work to unique things. Copy the
+    /// candidates into a unique set, then copy from that set into the list of
+    /// arithmetic types.
+    llvm::SmallSetVector<CanQualType, 2> BitIntCandidates;
+    llvm::for_each(CandidateTypes, [&BitIntCandidates](
+                                       BuiltinCandidateTypeSet &Candidate) {
+      for (QualType BitTy : Candidate.bitint_types())
+        BitIntCandidates.insert(CanQualType::CreateUnsafe(BitTy));
+    });
+    llvm::copy(BitIntCandidates, std::back_inserter(ArithmeticTypes));
     LastPromotedIntegralType = ArithmeticTypes.size();
     LastPromotedArithmeticType = ArithmeticTypes.size();
     // End of promoted types.
diff --git a/clang/test/SemaCXX/overload-bitint.cpp b/clang/test/SemaCXX/overload-bitint.cpp
new file mode 100644
index 00000000000000..b834a3b01fede6
--- /dev/null
+++ b/clang/test/SemaCXX/overload-bitint.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -std=c++20 %s -verify
+// expected-no-diagnostics
+
+#include "Inputs/std-compare.h"
+
+struct S {
+  _BitInt(12) a;
+
+  constexpr operator _BitInt(12)() const { return a; }
+};
+
+// None of these used to compile because we weren't adding _BitInt types to the
+// overload set for builtin operators. See GH82998.
+static_assert(S{10} < 11);
+static_assert(S{10} <= 11);
+static_assert(S{12} > 11);
+static_assert(S{12} >= 11);
+static_assert(S{10} == 10);
+static_assert((S{10} <=> 10) == 0);
+static_assert(S{10} != 11);
+static_assert(S{10} + 0 == 10);
+static_assert(S{10} - 0 == 10);
+static_assert(S{10} * 1 == 10);
+static_assert(S{10} / 1 == 10);
+static_assert(S{10} % 1 == 0);
+static_assert(S{10} << 0 == 10);
+static_assert(S{10} >> 0 == 10);
+static_assert((S{10} | 0) == 10);
+static_assert((S{10} & 10) == 10);
+static_assert((S{10} ^ 0) == 10);
+static_assert(-S{10} == -10);
+static_assert(+S{10} == +10);
+static_assert(~S{10} == ~10);
+
+struct A {
+  _BitInt(12) a;
+
+  bool operator==(const A&) const = default;
+  bool operator!=(const A&) const = default;
+  std::strong_ordering operator<=>(const A&) const = default;
+};
+

for (QualType BitTy : Candidate.bitint_types())
BitIntCandidates.insert(CanQualType::CreateUnsafe(BitTy));
});
llvm::copy(BitIntCandidates, std::back_inserter(ArithmeticTypes));
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
llvm::copy(BitIntCandidates, std::back_inserter(ArithmeticTypes));
llvm::move(BitIntCandidates, std::back_inserter(ArithmeticTypes));

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

/// _BitInt overloads are a bit special. We don't want to add candidates
/// for the entire family of _BitInt types, so instead we only add
/// candidates for the unique, unqualified _BitInt types present in the
/// candidate type set. The candidate set already handled ensuring the type
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment is a bit weird imo. "We don't want to add candidates for the entire family of _BitInt types" seems a bit obvious (and if we wanted to do that, wouldn't we need a lot more special handling any way?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sure, I can cut the comment down/remove it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

for (QualType BitTy : Candidate.bitint_types())
BitIntCandidates.insert(CanQualType::CreateUnsafe(BitTy));
});
llvm::copy(BitIntCandidates, std::back_inserter(ArithmeticTypes));
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 concerned about the assert on 9001. It seems to me that this changes us from being a reasonably 'fixed' list to having a perhaps-unlimited list.

I think that the intent of that assert is nice (ensuring we can small-vector-opt this list), but this section here makes that no longer guaranteed, right?

The ArithmeticTypesCap is 24, and we do a push_back of 23 other types (by my count!). So adding TWO bit-int types + having all the rest of the types enabled causes that assert to fire.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch! I'll correct this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

We're subtracting out the bit-precise types when determining whether
we're under the cap because we don't know for sure how many bit-precise
types will wind up in the overload set. This assertion exists to let us
know when we've hit a point where we need to allocate rather than use
the small vector inline storage, so to avoid needing those allocations,
I've bumped the cap up by two elements on the assumption that builtin
overload set will often not have more than two candidate types.
Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

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

LGTM if Corentin is happy with the comment.

Copy link
Contributor

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

@AaronBallman AaronBallman merged commit eb31970 into llvm:main Mar 12, 2024
5 checks passed
@AaronBallman AaronBallman deleted the aballman-bitint-builtin-operators branch March 12, 2024 11:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ 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.

[clang] _BitInt member of struct/class implicitly deletes operator==
4 participants