diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2311,11 +2311,11 @@ bool hasVisibleMemberSpecialization( const NamedDecl *D, llvm::SmallVectorImpl *Modules = nullptr); - /// Determine if \p A and \p B are equivalent internal linkage declarations + /// Determine if \p A and \p B are equivalent no/internal linkage declarations /// from different modules, and thus an ambiguity error can be downgraded to - /// an extension warning. - bool isEquivalentInternalLinkageDeclaration(const NamedDecl *A, - const NamedDecl *B); + /// an extension warning or omitted. + bool isEquivalentNonExternalLinkageDeclaration(const NamedDecl *A, + const NamedDecl *B); void diagnoseEquivalentInternalLinkageDeclarations( SourceLocation Loc, const NamedDecl *D, ArrayRef Equiv); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -11908,7 +11908,7 @@ if (UsingShadowDecl *Shadow = dyn_cast(*I)) PrevShadow = Shadow; FoundEquivalentDecl = true; - } else if (isEquivalentInternalLinkageDeclaration(D, Target)) { + } else if (isEquivalentNonExternalLinkageDeclaration(D, Target)) { // We don't conflict with an existing using shadow decl of an equivalent // declaration, but we're not a redeclaration of it. FoundEquivalentDecl = true; diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -509,6 +509,7 @@ NamedDecl *HasNonFunction = nullptr; llvm::SmallVector EquivalentNonFunctions; + bool ComplainOnEquivalent = false; unsigned UniqueTagIndex = 0; @@ -576,11 +577,15 @@ } else { if (HasNonFunction) { // If we're about to create an ambiguity between two declarations that - // are equivalent, but one is an internal linkage declaration from one - // module and the other is an internal linkage declaration from another - // module, just skip it. - if (getSema().isEquivalentInternalLinkageDeclaration(HasNonFunction, - D)) { + // are equivalent, but one is an internal/no linkage declaration from + // one module and the other is an internal/no linkage declaration from + // another module, just skip it. + if (getSema().isEquivalentNonExternalLinkageDeclaration(HasNonFunction, + D)) { + // Don't complain about declarations that are externally visible. + ComplainOnEquivalent |= !HasNonFunction->isExternallyVisible(); + ComplainOnEquivalent |= !D->isExternallyVisible(); + EquivalentNonFunctions.push_back(D); Decls[I] = Decls[--N]; continue; @@ -616,7 +621,7 @@ // FIXME: This diagnostic should really be delayed until we're done with // the lookup result, in case the ambiguity is resolved by the caller. - if (!EquivalentNonFunctions.empty() && !Ambiguous) + if (!EquivalentNonFunctions.empty() && !Ambiguous && ComplainOnEquivalent) getSema().diagnoseEquivalentInternalLinkageDeclarations( getNameLoc(), HasNonFunction, EquivalentNonFunctions); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -9955,19 +9955,19 @@ /// 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++). -bool Sema::isEquivalentInternalLinkageDeclaration(const NamedDecl *A, - const NamedDecl *B) { +bool Sema::isEquivalentNonExternalLinkageDeclaration(const NamedDecl *A, + const NamedDecl *B) { auto *VA = dyn_cast_or_null(A); auto *VB = dyn_cast_or_null(B); if (!VA || !VB) return false; - // The declarations must be declaring the same name as an internal linkage + // The declarations must be declaring the same name as an internal/no linkage // entity in different modules. if (!VA->getDeclContext()->getRedeclContext()->Equals( VB->getDeclContext()->getRedeclContext()) || getOwningModule(VA) == getOwningModule(VB) || - VA->isExternallyVisible() || VB->isExternallyVisible()) + VA->hasExternalFormalLinkage() || VB->hasExternalFormalLinkage()) return false; // Check that the declarations appear to be equivalent. @@ -10093,8 +10093,8 @@ PendingBest.push_back(Cand); Cand->Best = true; - if (S.isEquivalentInternalLinkageDeclaration(Cand->Function, - Curr->Function)) + if (S.isEquivalentNonExternalLinkageDeclaration(Cand->Function, + Curr->Function)) EquivalentCands.push_back(Cand->Function); else Best = end(); diff --git a/clang/test/Modules/ambiguous-anonymous-enum-lookup.m.cpp b/clang/test/Modules/ambiguous-anonymous-enum-lookup.m.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/ambiguous-anonymous-enum-lookup.m.cpp @@ -0,0 +1,40 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: %clang_cc1 -fsyntax-only -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache -I %t/include %t/test.cpp -verify +// RUN: %clang_cc1 -fsyntax-only -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache -I %t/include %t/test.m -verify + +//--- include/textual.h +#ifndef TEXTUAL_H +#define TEXTUAL_H +enum { kAnonymousEnumValue = 0 }; +#endif + +//--- include/empty.h +//--- include/initially_hidden.h +#include "textual.h" + +//--- include/module.modulemap +module Piecewise { + module Empty { header "empty.h" } + module InitiallyHidden { header "initially_hidden.h" } +} + +//--- test.cpp +#include "empty.h" +#include "textual.h" +#include "initially_hidden.h" + +int testReferencingAnonymousEnumConstant() { + return kAnonymousEnumValue; // expected-warning {{ambiguous use of internal linkage declaration 'kAnonymousEnumValue' defined in multiple modules}} + // expected-note@textual.h:3 {{declared here}} + // expected-note@textual.h:3 {{declared here in module 'Piecewise.InitiallyHidden'}} +} + +//--- test.m +#include "empty.h" +#include "textual.h" +#include "initially_hidden.h" + +int testReferencingAnonymousEnumConstant() { + return kAnonymousEnumValue; // expected-no-diagnostics +}