From bc9a985bee5d689a2082d4037b9f4608019584fe Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 21 Oct 2015 07:13:52 +0000 Subject: [PATCH] [modules] libstdc++ defines some static inline functions in its internal 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 | 3 ++ lib/Sema/SemaOverload.cpp | 36 +++++++++++++++++++ .../Inputs/libstdcxx-ambiguous-internal/a.h | 4 +++ .../Inputs/libstdcxx-ambiguous-internal/b.h | 4 +++ .../Inputs/libstdcxx-ambiguous-internal/c.h | 4 +++ .../Inputs/libstdcxx-ambiguous-internal/d.h | 3 ++ .../module.modulemap | 6 ++++ test/Modules/libstdcxx-ambiguous-internal.cpp | 7 ++++ 8 files changed, 67 insertions(+) create mode 100644 test/Modules/Inputs/libstdcxx-ambiguous-internal/a.h create mode 100644 test/Modules/Inputs/libstdcxx-ambiguous-internal/b.h create mode 100644 test/Modules/Inputs/libstdcxx-ambiguous-internal/c.h create mode 100644 test/Modules/Inputs/libstdcxx-ambiguous-internal/d.h create mode 100644 test/Modules/Inputs/libstdcxx-ambiguous-internal/module.modulemap create mode 100644 test/Modules/libstdcxx-ambiguous-internal.cpp diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 4829f2b74d..f5903b896d 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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>; def note_ovl_candidate_inherited_constructor : Note<"inherited from here">; def note_ovl_candidate_illegal_constructor : Note< diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 1ad43a3592..743ca68042 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -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 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 index 0000000000..e079bd0908 --- /dev/null +++ b/test/Modules/Inputs/libstdcxx-ambiguous-internal/a.h @@ -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 index 0000000000..c0a827870a --- /dev/null +++ b/test/Modules/Inputs/libstdcxx-ambiguous-internal/b.h @@ -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 index 0000000000..53122faf79 --- /dev/null +++ b/test/Modules/Inputs/libstdcxx-ambiguous-internal/c.h @@ -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 index 0000000000..1627bfb99e --- /dev/null +++ b/test/Modules/Inputs/libstdcxx-ambiguous-internal/d.h @@ -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 index 0000000000..12d03880c6 --- /dev/null +++ b/test/Modules/Inputs/libstdcxx-ambiguous-internal/module.modulemap @@ -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 index 0000000000..966b9c9768 --- /dev/null +++ b/test/Modules/libstdcxx-ambiguous-internal.cpp @@ -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 -- 2.40.0