]> granicus.if.org Git - clang/commitdiff
[Modules] Consider enable_if attrs in isSameEntity.
authorGeorge Burgess IV <george.burgess.iv@gmail.com>
Wed, 15 Feb 2017 22:43:27 +0000 (22:43 +0000)
committerGeorge Burgess IV <george.burgess.iv@gmail.com>
Wed, 15 Feb 2017 22:43:27 +0000 (22:43 +0000)
Two functions that differ only in their enable_if attributes are
considered overloads, so we should check for those when we're trying to
figure out if two functions are mergeable.

We need to do the same thing for pass_object_size, as well. Looks like
that'll be a bit less trivial, since we sometimes do these merging
checks before we have pass_object_size attributes available (see the
merge checks in ASTDeclReader::VisitFunctionDecl that happen before we
read parameters, and merge checks in calls to ReadDeclAs<>()).

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@295252 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Serialization/ASTReaderDecl.cpp
test/Modules/Inputs/overloadable-attrs/a.h [new file with mode: 0644]
test/Modules/Inputs/overloadable-attrs/module.modulemap [new file with mode: 0644]
test/Modules/overloadable-attrs.cpp [new file with mode: 0644]

index 8b14c2d8254f2a7374a136078195637c51a1e537..9be3981329a05b436763463e3f43853375d2d430 100644 (file)
@@ -2656,6 +2656,44 @@ static bool isSameTemplateParameterList(const TemplateParameterList *X,
   return true;
 }
 
+/// Determine whether the attributes we can overload on are identical for A and
+/// B. Expects A and B to (otherwise) have the same type.
+static bool hasSameOverloadableAttrs(const FunctionDecl *A,
+                                     const FunctionDecl *B) {
+  SmallVector<const EnableIfAttr *, 4> AEnableIfs;
+  // Since this is an equality check, we can ignore that enable_if attrs show up
+  // in reverse order.
+  for (const auto *EIA : A->specific_attrs<EnableIfAttr>())
+    AEnableIfs.push_back(EIA);
+
+  SmallVector<const EnableIfAttr *, 4> BEnableIfs;
+  for (const auto *EIA : B->specific_attrs<EnableIfAttr>())
+    BEnableIfs.push_back(EIA);
+
+  // Two very common cases: either we have 0 enable_if attrs, or we have an
+  // unequal number of enable_if attrs.
+  if (AEnableIfs.empty() && BEnableIfs.empty())
+    return true;
+
+  if (AEnableIfs.size() != BEnableIfs.size())
+    return false;
+
+  llvm::FoldingSetNodeID Cand1ID, Cand2ID;
+  for (unsigned I = 0, E = AEnableIfs.size(); I != E; ++I) {
+    Cand1ID.clear();
+    Cand2ID.clear();
+
+    AEnableIfs[I]->getCond()->Profile(Cand1ID, A->getASTContext(), true);
+    BEnableIfs[I]->getCond()->Profile(Cand2ID, B->getASTContext(), true);
+    if (Cand1ID != Cand2ID)
+      return false;
+  }
+
+  // FIXME: This doesn't currently consider pass_object_size attributes, since
+  // we aren't guaranteed that A and B have valid parameter lists yet.
+  return true;
+}
+
 /// \brief Determine whether the two declarations refer to the same entity.
 static bool isSameEntity(NamedDecl *X, NamedDecl *Y) {
   assert(X->getDeclName() == Y->getDeclName() && "Declaration name mismatch!");
@@ -2711,8 +2749,10 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) {
                         CtorY->getInheritedConstructor().getConstructor()))
         return false;
     }
-    return (FuncX->getLinkageInternal() == FuncY->getLinkageInternal()) &&
-      FuncX->getASTContext().hasSameType(FuncX->getType(), FuncY->getType());
+    return FuncX->getLinkageInternal() == FuncY->getLinkageInternal() &&
+           FuncX->getASTContext().hasSameType(FuncX->getType(),
+                                              FuncY->getType()) &&
+           hasSameOverloadableAttrs(FuncX, FuncY);
   }
 
   // Variables with the same type and linkage match.
diff --git a/test/Modules/Inputs/overloadable-attrs/a.h b/test/Modules/Inputs/overloadable-attrs/a.h
new file mode 100644 (file)
index 0000000..e7dabfb
--- /dev/null
@@ -0,0 +1,16 @@
+namespace enable_if_attrs {
+constexpr int fn1() __attribute__((enable_if(0, ""))) { return 0; }
+constexpr int fn1() { return 1; }
+
+constexpr int fn2() { return 1; }
+constexpr int fn2() __attribute__((enable_if(0, ""))) { return 0; }
+
+constexpr int fn3(int i) __attribute__((enable_if(!i, ""))) { return 0; }
+constexpr int fn3(int i) __attribute__((enable_if(i, ""))) { return 1; }
+
+constexpr int fn4(int i) { return 0; }
+constexpr int fn4(int i) __attribute__((enable_if(i, ""))) { return 1; }
+
+constexpr int fn5(int i) __attribute__((enable_if(i, ""))) { return 1; }
+constexpr int fn5(int i) { return 0; }
+}
diff --git a/test/Modules/Inputs/overloadable-attrs/module.modulemap b/test/Modules/Inputs/overloadable-attrs/module.modulemap
new file mode 100644 (file)
index 0000000..514d745
--- /dev/null
@@ -0,0 +1,3 @@
+module a {
+  header "a.h"
+}
diff --git a/test/Modules/overloadable-attrs.cpp b/test/Modules/overloadable-attrs.cpp
new file mode 100644 (file)
index 0000000..ae9bd73
--- /dev/null
@@ -0,0 +1,19 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -I%S/Inputs/overloadable-attrs -fmodules \
+// RUN:            -fmodule-map-file=%S/Inputs/overloadable-attrs/module.modulemap \
+// RUN:            -fmodules-cache-path=%t -verify %s -std=c++11
+//
+// Ensures that we don't merge decls with attrs that we allow overloading on.
+//
+// expected-no-diagnostics
+
+#include "a.h"
+
+static_assert(enable_if_attrs::fn1() == 1, "");
+static_assert(enable_if_attrs::fn2() == 1, "");
+static_assert(enable_if_attrs::fn3(0) == 0, "");
+static_assert(enable_if_attrs::fn3(1) == 1, "");
+static_assert(enable_if_attrs::fn4(0) == 0, "");
+static_assert(enable_if_attrs::fn4(1) == 1, "");
+static_assert(enable_if_attrs::fn5(0) == 0, "");
+static_assert(enable_if_attrs::fn5(1) == 1, "");