]> granicus.if.org Git - clang/commitdiff
[modules] libstdc++ defines some static inline functions in its internal
authorRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 21 Oct 2015 07:13:52 +0000 (07:13 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 21 Oct 2015 07:13:52 +0000 (07:13 +0000)
headers. If those headers end up being textually included twice into the same
module, we get ambiguity errors.

Work around this by downgrading the ambiguity error to a warning if multiple
identical internal-linkage functions appear in an overload set, and just pick
one of those functions as the lookup result.

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

include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/SemaOverload.cpp
test/Modules/Inputs/libstdcxx-ambiguous-internal/a.h [new file with mode: 0644]
test/Modules/Inputs/libstdcxx-ambiguous-internal/b.h [new file with mode: 0644]
test/Modules/Inputs/libstdcxx-ambiguous-internal/c.h [new file with mode: 0644]
test/Modules/Inputs/libstdcxx-ambiguous-internal/d.h [new file with mode: 0644]
test/Modules/Inputs/libstdcxx-ambiguous-internal/module.modulemap [new file with mode: 0644]
test/Modules/libstdcxx-ambiguous-internal.cpp [new file with mode: 0644]

index 4829f2b74d0ea5632464b7e876cc1a79c6a04e8c..f5903b896d22684515b2bcb86f2b2786b2972589 100644 (file)
@@ -2974,6 +2974,9 @@ def note_ovl_candidate : Note<"candidate "
     "%select{none|const|restrict|const and restrict|volatile|const and volatile"
     "|volatile and restrict|const, volatile, and restrict}4)"
     "| made ineligible by enable_if}2">;
+def ext_ovl_equivalent_internal_linkage_functions_in_modules : ExtWarn<
+  "ambiguous use of internal linkage function %0 defined in multiple modules">,
+  InGroup<DiagGroup<"modules-ambiguous-internal-linkage-function">>;
 
 def note_ovl_candidate_inherited_constructor : Note<"inherited from here">;
 def note_ovl_candidate_illegal_constructor : Note<
index 1ad43a35925b483d09363cd37fbc696ea95d3e3c..743ca680427c1282beff5da59b1307921ded40fd 100644 (file)
@@ -8565,6 +8565,27 @@ bool clang::isBetterOverloadCandidate(Sema &S, const OverloadCandidate &Cand1,
   return false;
 }
 
+/// Determine whether two function declarations are "equivalent" for overload
+/// resolution purposes. This applies when the same internal linkage function
+/// is defined by two modules (textually including the same header). In such
+/// a case, we don't consider the declarations to declare the same entity, but
+/// we also don't want lookups with both declarations visible to be ambiguous
+/// in some cases (this happens when using a modularized libstdc++).
+static bool isEquivalentCompatibleOverload(Sema &S,
+                                           const OverloadCandidate &Best,
+                                           const OverloadCandidate &Cand) {
+  return Best.Function && Cand.Function &&
+         Best.Function->getDeclContext()->getRedeclContext()->Equals(
+             Cand.Function->getDeclContext()->getRedeclContext()) &&
+         S.getOwningModule(Best.Function) != S.getOwningModule(Cand.Function) &&
+         !Best.Function->isExternallyVisible() &&
+         !Cand.Function->isExternallyVisible() &&
+         !S.IsOverload(Best.Function, Cand.Function, /*UsingDecl*/false);
+}
+
+static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
+                                  unsigned NumArgs);
+
 /// \brief Computes the best viable function (C++ 13.3.3)
 /// within an overload candidate set.
 ///
@@ -8592,6 +8613,8 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
   if (Best == end())
     return OR_No_Viable_Function;
 
+  llvm::SmallVector<const OverloadCandidate *, 4> EquivalentCands;
+
   // Make sure that this function is better than every other viable
   // function. If not, we have an ambiguity.
   for (iterator Cand = begin(); Cand != end(); ++Cand) {
@@ -8599,6 +8622,11 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
         Cand != Best &&
         !isBetterOverloadCandidate(S, *Best, *Cand, Loc,
                                    UserDefinedConversion)) {
+      if (isEquivalentCompatibleOverload(S, *Best, *Cand)) {
+        EquivalentCands.push_back(Cand);
+        continue;
+      }
+
       Best = end();
       return OR_Ambiguous;
     }
@@ -8610,6 +8638,14 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
        S.isFunctionConsideredUnavailable(Best->Function)))
     return OR_Deleted;
 
+  if (!EquivalentCands.empty()) {
+    S.Diag(Loc, diag::ext_ovl_equivalent_internal_linkage_functions_in_modules)
+      << Best->Function;
+    S.NoteOverloadCandidate(Best->Function);
+    for (auto *Cand : EquivalentCands)
+      S.NoteOverloadCandidate(Cand->Function);
+  }
+
   return OR_Success;
 }
 
diff --git a/test/Modules/Inputs/libstdcxx-ambiguous-internal/a.h b/test/Modules/Inputs/libstdcxx-ambiguous-internal/a.h
new file mode 100644 (file)
index 0000000..e079bd0
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef A_H
+#define A_H
+static inline void f() {}
+#endif
diff --git a/test/Modules/Inputs/libstdcxx-ambiguous-internal/b.h b/test/Modules/Inputs/libstdcxx-ambiguous-internal/b.h
new file mode 100644 (file)
index 0000000..c0a8278
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef B_H
+#define B_H
+#include "a.h"
+#endif
diff --git a/test/Modules/Inputs/libstdcxx-ambiguous-internal/c.h b/test/Modules/Inputs/libstdcxx-ambiguous-internal/c.h
new file mode 100644 (file)
index 0000000..53122fa
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef C_H
+#define C_H
+#include "a.h"
+#endif
diff --git a/test/Modules/Inputs/libstdcxx-ambiguous-internal/d.h b/test/Modules/Inputs/libstdcxx-ambiguous-internal/d.h
new file mode 100644 (file)
index 0000000..1627bfb
--- /dev/null
@@ -0,0 +1,3 @@
+#include "b.h"
+#include "c.h"
+inline void g() { f(); }
diff --git a/test/Modules/Inputs/libstdcxx-ambiguous-internal/module.modulemap b/test/Modules/Inputs/libstdcxx-ambiguous-internal/module.modulemap
new file mode 100644 (file)
index 0000000..12d0388
--- /dev/null
@@ -0,0 +1,6 @@
+module std {
+  module A { textual header "a.h" }
+  module B { header "b.h" }
+  module C { header "c.h" }
+  module D { header "d.h" export * }
+}
diff --git a/test/Modules/libstdcxx-ambiguous-internal.cpp b/test/Modules/libstdcxx-ambiguous-internal.cpp
new file mode 100644 (file)
index 0000000..966b9c9
--- /dev/null
@@ -0,0 +1,7 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -x c++ -std=c++11 -fmodules -emit-module -fmodule-name=std -fmodules-cache-path=%t %S/Inputs/libstdcxx-ambiguous-internal/module.modulemap -Werror
+// RUN: %clang_cc1 -x c++ -std=c++11 -fmodules -emit-module -fmodule-name=std -fmodules-cache-path=%t %S/Inputs/libstdcxx-ambiguous-internal/module.modulemap -fmodules-local-submodule-visibility -DAMBIGUOUS 2>&1| FileCheck %s
+
+// CHECK-NOT: error
+// CHECK: warning: ambiguous use of internal linkage function 'f' defined in multiple modules
+// CHECK-NOT: error