whisperity created this revision.
whisperity added reviewers: aaron.ballman, vabridgers.
whisperity added a project: clang-tools-extra.
Herald added subscribers: martong, gamesh411, Szelethus, rnkovacs, xazax.hun.
whisperity requested review of this revision.
Herald added a subscriber: cfe-commits.

A downstream user identified a way to crash the check by running on code that 
involve `AttributedType`s. This patch fixes the check to first and foremost not 
crash, but also improves the logic handling qualifiers.

If the types contain any additional (not just CVR) qualifiers that are not the 
same, they will not be deemed mixable. The logic for CVR mixing, and the 
**`QualifiersMix`** check option remain unchanged.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D106361

Files:
  clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp
  
clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-len2.cpp
  
clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-qualifiermixing.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-qualifiermixing.cpp
===================================================================
--- clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-qualifiermixing.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-qualifiermixing.cpp
@@ -63,7 +63,7 @@
 // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 2 adjacent parameters of 'qualifiedThroughTypedef1' of similar type are
 // CHECK-MESSAGES: :[[@LINE-2]]:35: note: the first parameter in the range is 'I'
 // CHECK-MESSAGES: :[[@LINE-3]]:43: note: the last parameter in the range is 'CI'
-// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, 'int' and 'CInt' share a common type
+// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, 'int' and 'CInt' share a core type
 // CHECK-MESSAGES: :[[@LINE-5]]:38: note: 'int' and 'CInt' parameters accept and bind the same kind of values
 
 void qualifiedThroughTypedef2(CInt CI1, const int CI2) {}
@@ -76,7 +76,7 @@
 // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 3 adjacent parameters of 'qualifiedThroughTypedef3' of similar type are
 // CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in the range is 'CI1'
 // CHECK-MESSAGES: :[[@LINE-3]]:69: note: the last parameter in the range is 'CI3'
-// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the common type of 'CInt' and 'const MyInt1' is 'const int'
+// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the core type of 'CInt' and 'const MyInt1' is 'const int'
 // CHECK-MESSAGES: :[[@LINE-5]]:31: note: after resolving type aliases, 'CInt' and 'const int' are the same
 // CHECK-MESSAGES: :[[@LINE-6]]:41: note: after resolving type aliases, 'const MyInt1' and 'const int' are the same
 
@@ -84,21 +84,21 @@
 // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 3 adjacent parameters of 'qualifiedThroughTypedef4' of similar type are
 // CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in the range is 'CI1'
 // CHECK-MESSAGES: :[[@LINE-3]]:72: note: the last parameter in the range is 'CI3'
-// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the common type of 'CInt' and 'const MyInt1' is 'const int'
-// CHECK-MESSAGES: :[[@LINE-5]]:31: note: after resolving type aliases, the common type of 'CInt' and 'const MyInt2' is 'const int'
-// CHECK-MESSAGES: :[[@LINE-6]]:41: note: after resolving type aliases, the common type of 'const MyInt1' and 'const MyInt2' is 'const int'
+// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the core type of 'CInt' and 'const MyInt1' is 'const int'
+// CHECK-MESSAGES: :[[@LINE-5]]:31: note: after resolving type aliases, the core type of 'CInt' and 'const MyInt2' is 'const int'
+// CHECK-MESSAGES: :[[@LINE-6]]:41: note: after resolving type aliases, the core type of 'const MyInt1' and 'const MyInt2' is 'const int'
 
 void qualifiedThroughTypedef5(CMyInt1 CMI1, CMyInt2 CMI2) {}
 // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 2 adjacent parameters of 'qualifiedThroughTypedef5' of similar type are
 // CHECK-MESSAGES: :[[@LINE-2]]:39: note: the first parameter in the range is 'CMI1'
 // CHECK-MESSAGES: :[[@LINE-3]]:53: note: the last parameter in the range is 'CMI2'
-// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the common type of 'CMyInt1' and 'CMyInt2' is 'const int'
+// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the core type of 'CMyInt1' and 'CMyInt2' is 'const int'
 
 void qualifiedThroughTypedef6(CMyInt1 CMI1, int I) {}
 // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 2 adjacent parameters of 'qualifiedThroughTypedef6' of similar type are
 // CHECK-MESSAGES: :[[@LINE-2]]:39: note: the first parameter in the range is 'CMI1'
 // CHECK-MESSAGES: :[[@LINE-3]]:49: note: the last parameter in the range is 'I'
-// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, 'CMyInt1' and 'int' share a common type
+// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, 'CMyInt1' and 'int' share a core type
 // CHECK-MESSAGES: :[[@LINE-5]]:45: note: 'CMyInt1' and 'int' parameters accept and bind the same kind of values
 
 void referenceToTypedef1(CInt &CIR, int I) {}
@@ -113,3 +113,11 @@
 // CHECK-MESSAGES: :[[@LINE-2]]:20: note: the first parameter in the range is 'Dest'
 // CHECK-MESSAGES: :[[@LINE-3]]:29: note: the last parameter in the range is 'Source'
 // CHECK-MESSAGES: :[[@LINE-4]]:26: note: 'const T *' and 'T *' parameters accept and bind the same kind of values
+
+void attributedParam2(__attribute__((address_space(256))) int *One,
+                      const __attribute__((address_space(256))) MyInt1 *Two) {}
+// CHECK-MESSAGES: :[[@LINE-2]]:23: warning: 2 adjacent parameters of 'attributedParam2' of similar type are
+// CHECK-MESSAGES: :[[@LINE-3]]:64: note: the first parameter in the range is 'One'
+// CHECK-MESSAGES: :[[@LINE-3]]:73: note: the last parameter in the range is 'Two'
+// CHECK-MESSAGES: :[[@LINE-5]]:23: note: after resolving type aliases, the core type of '__attribute__((address_space(256))) int *' and 'const __attribute__((address_space(256))) MyInt1 *' is 'int'
+// CHECK-MESSAGES: :[[@LINE-5]]:23: note: '__attribute__((address_space(256))) int *' and 'const __attribute__((address_space(256))) MyInt1 *' parameters accept and bind the same kind of values
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-len2.cpp
===================================================================
--- clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-len2.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-len2.cpp
@@ -131,7 +131,7 @@
 // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 3 adjacent parameters of 'typedefMultiple' of similar type are
 // CHECK-MESSAGES: :[[@LINE-2]]:29: note: the first parameter in the range is 'I1'
 // CHECK-MESSAGES: :[[@LINE-3]]:52: note: the last parameter in the range is 'I2y'
-// CHECK-MESSAGES: :[[@LINE-4]]:22: note: after resolving type aliases, the common type of 'MyInt1' and 'MyInt2' is 'int'
+// CHECK-MESSAGES: :[[@LINE-4]]:22: note: after resolving type aliases, the core type of 'MyInt1' and 'MyInt2' is 'int'
 
 void throughTypedef1(int I, MyInt1 J) {}
 // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 2 adjacent parameters of 'throughTypedef1' of similar type are
@@ -143,7 +143,7 @@
 // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 2 adjacent parameters of 'betweenTypedef2' of similar type are
 // CHECK-MESSAGES: :[[@LINE-2]]:29: note: the first parameter in the range is 'I'
 // CHECK-MESSAGES: :[[@LINE-3]]:39: note: the last parameter in the range is 'J'
-// CHECK-MESSAGES: :[[@LINE-4]]:22: note: after resolving type aliases, the common type of 'MyInt1' and 'MyInt2' is 'int'
+// CHECK-MESSAGES: :[[@LINE-4]]:22: note: after resolving type aliases, the core type of 'MyInt1' and 'MyInt2' is 'int'
 
 void typedefChain(int I, MyInt1 MI1, MyInt2 MI2, MyInt2b MI2b) {}
 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 4 adjacent parameters of 'typedefChain' of similar type are
@@ -179,7 +179,7 @@
 // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 3 adjacent parameters of 'qualifiedThroughTypedef3' of similar type are
 // CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in the range is 'CI1'
 // CHECK-MESSAGES: :[[@LINE-3]]:69: note: the last parameter in the range is 'CI3'
-// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the common type of 'CInt' and 'const MyInt1' is 'const int'
+// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the core type of 'CInt' and 'const MyInt1' is 'const int'
 // CHECK-MESSAGES: :[[@LINE-5]]:31: note: after resolving type aliases, 'CInt' and 'const int' are the same
 // CHECK-MESSAGES: :[[@LINE-6]]:41: note: after resolving type aliases, 'const MyInt1' and 'const int' are the same
 
@@ -187,15 +187,15 @@
 // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 3 adjacent parameters of 'qualifiedThroughTypedef4' of similar type are
 // CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in the range is 'CI1'
 // CHECK-MESSAGES: :[[@LINE-3]]:72: note: the last parameter in the range is 'CI3'
-// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the common type of 'CInt' and 'const MyInt1' is 'const int'
-// CHECK-MESSAGES: :[[@LINE-5]]:31: note: after resolving type aliases, the common type of 'CInt' and 'const MyInt2' is 'const int'
-// CHECK-MESSAGES: :[[@LINE-6]]:41: note: after resolving type aliases, the common type of 'const MyInt1' and 'const MyInt2' is 'const int'
+// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the core type of 'CInt' and 'const MyInt1' is 'const int'
+// CHECK-MESSAGES: :[[@LINE-5]]:31: note: after resolving type aliases, the core type of 'CInt' and 'const MyInt2' is 'const int'
+// CHECK-MESSAGES: :[[@LINE-6]]:41: note: after resolving type aliases, the core type of 'const MyInt1' and 'const MyInt2' is 'const int'
 
 void qualifiedThroughTypedef5(CMyInt1 CMI1, CMyInt2 CMI2) {}
 // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 2 adjacent parameters of 'qualifiedThroughTypedef5' of similar type are
 // CHECK-MESSAGES: :[[@LINE-2]]:39: note: the first parameter in the range is 'CMI1'
 // CHECK-MESSAGES: :[[@LINE-3]]:53: note: the last parameter in the range is 'CMI2'
-// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the common type of 'CMyInt1' and 'CMyInt2' is 'const int'
+// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the core type of 'CMyInt1' and 'CMyInt2' is 'const int'
 
 void qualifiedThroughTypedef6(CMyInt1 CMI1, int I) {} // NO-WARN: Different qualifiers.
 
@@ -242,7 +242,7 @@
 // CHECK-MESSAGES: :[[@LINE-4]]:37: note: 'int' and 'ICRTy' parameters accept and bind the same kind of values
 // CHECK-MESSAGES: :[[@LINE-5]]:30: note: after resolving type aliases, 'int' and 'MyIntCRTy' are the same
 // CHECK-MESSAGES: :[[@LINE-6]]:52: note: 'int' and 'MyIntCRTy' parameters accept and bind the same kind of values
-// CHECK-MESSAGES: :[[@LINE-7]]:37: note: after resolving type aliases, the common type of 'ICRTy' and 'MyIntCRTy' is 'int'
+// CHECK-MESSAGES: :[[@LINE-7]]:37: note: after resolving type aliases, the core type of 'ICRTy' and 'MyIntCRTy' is 'int'
 // CHECK-MESSAGES: :[[@LINE-8]]:52: note: 'ICRTy' and 'MyIntCRTy' parameters accept and bind the same kind of values
 
 short const typedef int unsigned Eldritch;
@@ -252,13 +252,13 @@
 // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 2 adjacent parameters of 'collapse' of similar type are
 // CHECK-MESSAGES: :[[@LINE-2]]:24: note: the first parameter in the range is 'Cursed'
 // CHECK-MESSAGES: :[[@LINE-3]]:37: note: the last parameter in the range is 'Blessed'
-// CHECK-MESSAGES: :[[@LINE-4]]:15: note: after resolving type aliases, the common type of 'Eldritch' and 'Holy' is 'const unsigned short'
+// CHECK-MESSAGES: :[[@LINE-4]]:15: note: after resolving type aliases, the core type of 'Eldritch' and 'Holy' is 'const unsigned short'
 
 void collapseAndTypedef(Eldritch Cursed, const Holy &Blessed) {}
 // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'collapseAndTypedef' of similar type are
 // CHECK-MESSAGES: :[[@LINE-2]]:34: note: the first parameter in the range is 'Cursed'
 // CHECK-MESSAGES: :[[@LINE-3]]:54: note: the last parameter in the range is 'Blessed'
-// CHECK-MESSAGES: :[[@LINE-4]]:25: note: after resolving type aliases, the common type of 'Eldritch' and 'const Holy &' is 'const unsigned short'
+// CHECK-MESSAGES: :[[@LINE-4]]:25: note: after resolving type aliases, the core type of 'Eldritch' and 'const Holy &' is 'const unsigned short'
 // CHECK-MESSAGES: :[[@LINE-5]]:42: note: 'Eldritch' and 'const Holy &' parameters accept and bind the same kind of values
 
 template <typename T1, typename T2>
@@ -346,3 +346,31 @@
 
 void functionPrototypeLosesNoexcept(void (*NonThrowing)() noexcept, void (*Throwing)()) {}
 // NO-WARN: This call cannot be swapped, even if "getCanonicalType()" believes otherwise.
+
+void attributedParam1(const __attribute__((address_space(256))) int *One,
+                      const __attribute__((address_space(256))) int *Two) {}
+// CHECK-MESSAGES: :[[@LINE-2]]:23: warning: 2 adjacent parameters of 'attributedParam1' of similar type ('const __attribute__((address_space(256))) int *') are
+// CHECK-MESSAGES: :[[@LINE-3]]:70: note: the first parameter in the range is 'One'
+// CHECK-MESSAGES: :[[@LINE-3]]:70: note: the last parameter in the range is 'Two'
+
+void attributedParam1Typedef(const __attribute__((address_space(256))) int *One,
+                             const __attribute__((address_space(256))) MyInt1 *Two) {}
+// CHECK-MESSAGES: :[[@LINE-2]]:30: warning: 2 adjacent parameters of 'attributedParam1Typedef' of similar type are
+// CHECK-MESSAGES: :[[@LINE-3]]:77: note: the first parameter in the range is 'One'
+// CHECK-MESSAGES: :[[@LINE-3]]:80: note: the last parameter in the range is 'Two'
+// CHECK-MESSAGES: :[[@LINE-5]]:30: note: after resolving type aliases, the core type of 'const __attribute__((address_space(256))) int *' and 'const __attribute__((address_space(256))) MyInt1 *' is 'const __attribute__((address_space(256))) int'
+// FIXME: The last diagnostic line is a bit bad: the core type should be a
+// pointer type -- it is not clear right now, how it would be possible to
+// properly wire a logic in that fixes it.
+
+void attributedParam2(__attribute__((address_space(256))) int *One,
+                      const __attribute__((address_space(256))) MyInt1 *Two) {}
+// NO-WARN: One is CVR-qualified, the other is not.
+
+void attributedParam3(const int *One,
+                      const __attribute__((address_space(256))) MyInt1 *Two) {}
+// NO-WARN: One is attributed, the other is not.
+
+void attributedParam4(const __attribute__((address_space(512))) int *One,
+                      const __attribute__((address_space(256))) MyInt1 *Two) {}
+// NO-WARN: Different value of the attribute.
Index: clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp
+++ clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp
@@ -382,7 +382,7 @@
 
   /// A potentially calculated common underlying type after desugaring, that
   /// both sides of the mix can originate from.
-  QualType CommonType;
+  QualType CoreType;
 
   /// The steps an implicit conversion performs to get from one type to the
   /// other.
@@ -393,16 +393,15 @@
   bool CreatedFromOneWayConversion = false;
 
   MixData(MixFlags Flags) : Flags(Flags) {}
-  MixData(MixFlags Flags, QualType CommonType)
-      : Flags(Flags), CommonType(CommonType) {}
+  MixData(MixFlags Flags, QualType CoreType)
+      : Flags(Flags), CoreType(CoreType) {}
   MixData(MixFlags Flags, ConversionSequence Conv)
       : Flags(Flags), Conversion(Conv), CreatedFromOneWayConversion(true) {}
   MixData(MixFlags Flags, ConversionSequence LTR, ConversionSequence RTL)
       : Flags(Flags), Conversion(LTR), ConversionRTL(RTL) {}
-  MixData(MixFlags Flags, QualType CommonType, ConversionSequence LTR,
+  MixData(MixFlags Flags, QualType CoreType, ConversionSequence LTR,
           ConversionSequence RTL)
-      : Flags(Flags), CommonType(CommonType), Conversion(LTR),
-        ConversionRTL(RTL) {}
+      : Flags(Flags), CoreType(CoreType), Conversion(LTR), ConversionRTL(RTL) {}
 
   void sanitize() {
     assert(Flags != MixFlags::Invalid && "sanitize() called on invalid bitvec");
@@ -457,10 +456,10 @@
   MixData operator|(MixFlags EnableFlags) const {
     if (CreatedFromOneWayConversion) {
       MixData M{Flags | EnableFlags, Conversion};
-      M.CommonType = CommonType;
+      M.CoreType = CoreType;
       return M;
     }
-    return {Flags | EnableFlags, CommonType, Conversion, ConversionRTL};
+    return {Flags | EnableFlags, CoreType, Conversion, ConversionRTL};
   }
 
   /// Add the specified flag bits to the flags.
@@ -469,18 +468,22 @@
     return *this;
   }
 
-  /// Add the specified qualifiers to the common type in the Mix.
+  /// Add the specified qualifiers to the core type in the Mix.
   MixData qualify(Qualifiers Quals) const {
-    SplitQualType Split = CommonType.split();
-    Split.Quals.addQualifiers(Quals);
-    QualType CommonType{Split.Ty, Split.Quals.getAsOpaqueValue()};
+    if (CoreType.isNull())
+      return *this;
+
+    QualType NewCoreType = CoreType;
+    NewCoreType.addFastQualifiers(Quals.getFastQualifiers());
+    NewCoreType.getQualifiers().addQualifiers(Quals);
 
     if (CreatedFromOneWayConversion) {
       MixData M{Flags, Conversion};
-      M.CommonType = CommonType;
+      M.CoreType = NewCoreType;
       return M;
     }
-    return {Flags, CommonType, Conversion, ConversionRTL};
+
+    return {Flags, NewCoreType, Conversion, ConversionRTL};
   }
 };
 
@@ -497,7 +500,7 @@
   MixFlags flags() const { return Data.Flags; }
   bool flagsValid() const { return Data.isValid(); }
   bool mixable() const { return Data.indicatesMixability(); }
-  QualType commonUnderlyingType() const { return Data.CommonType; }
+  QualType coreUnderlyingType() const { return Data.CoreType; }
   const ConversionSequence &leftToRightConversionSequence() const {
     return Data.Conversion;
   }
@@ -566,7 +569,41 @@
                               ImplicitConversionModellingMode ImplicitMode);
 
 static inline bool isUselessSugar(const Type *T) {
-  return isa<DecayedType, ElaboratedType, ParenType>(T);
+  return isa<AttributedType, DecayedType, ElaboratedType, ParenType>(T);
+}
+
+/// Returns if the two types are qualified in a way that ever after equating or
+/// removing local CVR qualification, even if the unqualified types would mix,
+/// the qualified ones don't, because there are some other local qualifiers
+/// that aren't equal.
+static bool hasNonCVRMixabilityBreakingQualifiers(const ASTContext &Ctx,
+                                                  QualType LType,
+                                                  QualType RType) {
+  LLVM_DEBUG(
+      llvm::dbgs() << ">>> hasNonCVRMixabilityBreakingQualifiers for LType:\n";
+      LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n";
+      RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';);
+  Qualifiers LQual = LType.getLocalQualifiers(),
+             RQual = RType.getLocalQualifiers();
+
+  // Strip potential CVR. That is handled by the check option QualifiersMix.
+  LQual.removeCVRQualifiers();
+  RQual.removeCVRQualifiers();
+
+  Qualifiers CommonQuals = Qualifiers::removeCommonQualifiers(LQual, RQual);
+  (void)CommonQuals;
+
+  LLVM_DEBUG(llvm::dbgs() << "--- hasNonCVRMixabilityBreakingQualifiers. "
+                             "Removed common qualifiers: ";
+             CommonQuals.print(llvm::dbgs(), Ctx.getPrintingPolicy());
+             llvm::dbgs() << "\n\tremaining on LType: ";
+             LQual.print(llvm::dbgs(), Ctx.getPrintingPolicy());
+             llvm::dbgs() << "\n\tremaining on RType: ";
+             RQual.print(llvm::dbgs(), Ctx.getPrintingPolicy());
+             llvm::dbgs() << '\n';);
+
+  // If all other qualifiers are common between the two types, good to go.
+  return LQual.hasQualifiers() || RQual.hasQualifiers();
 }
 
 /// Approximate the way how LType and RType might refer to "essentially the
@@ -585,12 +622,6 @@
   LLVM_DEBUG(llvm::dbgs() << ">>> calculateMixability for LType:\n";
              LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n";
              RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';);
-
-  // Certain constructs match on the last catch-all getCanonicalType() equality,
-  // which is perhaps something not what we want. If this variable is true,
-  // the canonical type equality will be ignored.
-  bool RecursiveReturnDiscardingCanonicalType = false;
-
   if (LType == RType) {
     LLVM_DEBUG(llvm::dbgs() << "<<< calculateMixability. Trivial equality.\n");
     return {MixFlags::Trivial, LType};
@@ -628,7 +659,6 @@
            MixFlags::ReferenceBind;
   }
 
-  // Dissolve typedefs after the qualifiers outside the typedef are dealt with.
   if (LType->getAs<TypedefType>()) {
     LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS is typedef.\n");
     return calculateMixability(Check, LType.getSingleStepDesugaredType(Ctx),
@@ -645,35 +675,72 @@
 
   // A parameter of type 'cvr1 T' and another of potentially differently
   // qualified 'cvr2 T' may bind with the same power, if the user so requested.
+  //
+  // Whether to do this check for the inner unqualified types.
+  bool CompareUnqualifiedTypes = false;
+  // Should the result have a common inner type qualified for diagnosis?
+  bool RequalifyUnqualifiedMixabilityResult = false;
   if (LType.getLocalCVRQualifiers() != RType.getLocalCVRQualifiers()) {
-    LLVM_DEBUG(if (LType.getLocalCVRQualifiers()) llvm::dbgs()
-               << "--- calculateMixability. LHS is CVR.\n");
-    LLVM_DEBUG(if (RType.getLocalCVRQualifiers()) llvm::dbgs()
-               << "--- calculateMixability. RHS is CVR.\n");
+    LLVM_DEBUG(if (LType.getLocalCVRQualifiers()) {
+      llvm::dbgs() << "--- calculateMixability. LHS has CVR-Qualifiers: ";
+      Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers())
+          .print(llvm::dbgs(), Ctx.getPrintingPolicy());
+      llvm::dbgs() << '\n';
+    });
+    LLVM_DEBUG(if (RType.getLocalCVRQualifiers()) {
+      llvm::dbgs() << "--- calculateMixability. RHS has CVR-Qualifiers: ";
+      Qualifiers::fromCVRMask(RType.getLocalCVRQualifiers())
+          .print(llvm::dbgs(), Ctx.getPrintingPolicy());
+      llvm::dbgs() << '\n';
+    });
 
     if (!Check.QualifiersMix) {
       LLVM_DEBUG(llvm::dbgs()
-                 << "<<< calculateMixability. QualifiersMix turned off.\n");
+                 << "<<< calculateMixability. QualifiersMix turned off - not "
+                    "mixable.\n");
       return {MixFlags::None};
     }
 
-    return calculateMixability(Check, LType.getLocalUnqualifiedType(),
-                               RType.getLocalUnqualifiedType(), Ctx,
-                               ImplicitMode) |
-           MixFlags::Qualifiers;
+    CompareUnqualifiedTypes = true;
   }
   if (LType.getLocalCVRQualifiers() == RType.getLocalCVRQualifiers() &&
       LType.getLocalCVRQualifiers() != 0) {
-    LLVM_DEBUG(llvm::dbgs()
-               << "--- calculateMixability. LHS and RHS same CVR.\n");
-    // Apply the same qualifier back into the found common type if we found
-    // a common type between the unqualified versions.
-    return calculateMixability(Check, LType.getLocalUnqualifiedType(),
-                               RType.getLocalUnqualifiedType(), Ctx,
-                               ImplicitMode)
-        .qualify(LType.getLocalQualifiers());
+    LLVM_DEBUG(if (LType.getLocalCVRQualifiers()) {
+      llvm::dbgs()
+          << "--- calculateMixability. LHS and RHS have same CVR-Qualifiers: ";
+      Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers())
+          .print(llvm::dbgs(), Ctx.getPrintingPolicy());
+      llvm::dbgs() << '\n';
+    });
+
+    CompareUnqualifiedTypes = true;
+    RequalifyUnqualifiedMixabilityResult = true;
+  }
+
+  if (CompareUnqualifiedTypes) {
+    if (hasNonCVRMixabilityBreakingQualifiers(Ctx, LType, RType)) {
+      LLVM_DEBUG(llvm::dbgs() << "<<< calculateMixability. Additional "
+                                 "non-equal incompatible qualifiers.\n");
+      return {MixFlags::None};
+    }
+
+    MixData UnqualifiedMixability =
+        calculateMixability(Check, LType.getLocalUnqualifiedType(),
+                            RType.getLocalUnqualifiedType(), Ctx, ImplicitMode);
+
+    if (!RequalifyUnqualifiedMixabilityResult)
+      return UnqualifiedMixability | MixFlags::Qualifiers;
+
+    // Apply the same qualifier back into the found core type if we found
+    // a core type between the unqualified versions.
+    return UnqualifiedMixability.qualify(LType.getLocalQualifiers());
   }
 
+  // Certain constructs match on the last catch-all getCanonicalType() equality,
+  // which is perhaps something not what we want. If this variable is true,
+  // the canonical type equality will be ignored.
+  bool RecursiveReturnDiscardingCanonicalType = false;
+
   if (LType->isPointerType() && RType->isPointerType()) {
     // If both types are pointers, and pointed to the exact same type,
     // LType == RType took care of that. Try to see if the pointee type has
@@ -1924,16 +1991,16 @@
 };
 
 struct TypeAliasDiagnosticTuple {
-  QualType LHSType, RHSType, CommonType;
+  QualType LHSType, RHSType, CoreType;
 
   bool operator==(const TypeAliasDiagnosticTuple &Other) const {
-    return CommonType == Other.CommonType &&
+    return CoreType == Other.CoreType &&
            ((LHSType == Other.LHSType && RHSType == Other.RHSType) ||
             (LHSType == Other.RHSType && RHSType == Other.LHSType));
   }
 
   bool operator<(const TypeAliasDiagnosticTuple &Other) const {
-    return CommonType < Other.CommonType && LHSType < Other.LHSType &&
+    return CoreType < Other.CoreType && LHSType < Other.LHSType &&
            RHSType < Other.RHSType;
   }
 };
@@ -1945,19 +2012,19 @@
 
 public:
   /// Returns whether the diagnostic for LHSType and RHSType which are both
-  /// referring to CommonType being the same has not been emitted already.
-  bool operator()(QualType LHSType, QualType RHSType, QualType CommonType) {
-    if (CommonType.isNull() || CommonType == LHSType || CommonType == RHSType)
+  /// referring to CoreType being the same has not been emitted already.
+  bool operator()(QualType LHSType, QualType RHSType, QualType CoreType) {
+    if (CoreType.isNull() || CoreType == LHSType || CoreType == RHSType)
       return Base::operator()({LHSType, RHSType, {}});
 
-    TypeAliasDiagnosticTuple ThreeTuple{LHSType, RHSType, CommonType};
+    TypeAliasDiagnosticTuple ThreeTuple{LHSType, RHSType, CoreType};
     if (!Base::operator()(ThreeTuple))
       return false;
 
-    bool AlreadySaidLHSAndCommonIsSame = calledWith({LHSType, CommonType, {}});
-    bool AlreadySaidRHSAndCommonIsSame = calledWith({RHSType, CommonType, {}});
-    if (AlreadySaidLHSAndCommonIsSame && AlreadySaidRHSAndCommonIsSame) {
-      // "SomeInt == int" && "SomeOtherInt == int" => "Common(SomeInt,
+    bool AlreadySaidLHSAndCoreIsSame = calledWith({LHSType, CoreType, {}});
+    bool AlreadySaidRHSAndCoreIsSame = calledWith({RHSType, CoreType, {}});
+    if (AlreadySaidLHSAndCoreIsSame && AlreadySaidRHSAndCoreIsSame) {
+      // "SomeInt == int" && "SomeOtherInt == int" => "CoreType(SomeInt,
       // SomeOtherInt) == int", no need to diagnose it. Save the 3-tuple only
       // for shortcut if it ever appears again.
       return false;
@@ -2119,33 +2186,33 @@
       const ParmVarDecl *RVar = M.Second;
       QualType LType = LVar->getType();
       QualType RType = RVar->getType();
-      QualType CommonType = M.commonUnderlyingType();
+      QualType CoreType = M.coreUnderlyingType();
       std::string LTypeStr = LType.getAsString(PP);
       std::string RTypeStr = RType.getAsString(PP);
-      std::string CommonTypeStr = CommonType.getAsString(PP);
+      std::string CoreTypeStr = CoreType.getAsString(PP);
 
       if (hasFlag(M.flags(), MixFlags::TypeAlias) &&
-          UniqueTypeAlias(LType, RType, CommonType)) {
+          UniqueTypeAlias(LType, RType, CoreType)) {
         StringRef DiagText;
-        bool ExplicitlyPrintCommonType = false;
-        if (LTypeStr == CommonTypeStr || RTypeStr == CommonTypeStr)
+        bool ExplicitlyPrintCoreType = false;
+        if (LTypeStr == CoreTypeStr || RTypeStr == CoreTypeStr)
           if (hasFlag(M.flags(), MixFlags::Qualifiers))
             DiagText = "after resolving type aliases, '%0' and '%1' share a "
-                       "common type";
+                       "core type";
           else
             DiagText =
                 "after resolving type aliases, '%0' and '%1' are the same";
-        else if (!CommonType.isNull()) {
-          DiagText = "after resolving type aliases, the common type of '%0' "
+        else if (!CoreType.isNull()) {
+          DiagText = "after resolving type aliases, the core type of '%0' "
                      "and '%1' is '%2'";
-          ExplicitlyPrintCommonType = true;
+          ExplicitlyPrintCoreType = true;
         }
 
         auto Diag =
             diag(LVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
             << LTypeStr << RTypeStr;
-        if (ExplicitlyPrintCommonType)
-          Diag << CommonTypeStr;
+        if (ExplicitlyPrintCoreType)
+          Diag << CoreTypeStr;
       }
 
       if ((hasFlag(M.flags(), MixFlags::ReferenceBind) ||
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to