diff --git a/clang/include/clang/AST/DeclContextInternals.h b/clang/include/clang/AST/DeclContextInternals.h index c4734ab5789538..42cc677f82135e 100644 --- a/clang/include/clang/AST/DeclContextInternals.h +++ b/clang/include/clang/AST/DeclContextInternals.h @@ -160,12 +160,16 @@ class StoredDeclsList { void replaceExternalDecls(ArrayRef Decls) { // Remove all declarations that are either external or are replaced with - // external declarations. + // external declarations with higher visibilities. erase_if([Decls](NamedDecl *ND) { if (ND->isFromASTFile()) return true; + // FIXME: Can we get rid of this loop completely? for (NamedDecl *D : Decls) - if (D->declarationReplaces(ND, /*IsKnownNewer=*/false)) + // Only replace the local declaration if the external declaration has + // higher visibilities. + if (D->getModuleOwnershipKind() <= ND->getModuleOwnershipKind() && + D->declarationReplaces(ND, /*IsKnownNewer=*/false)) return true; return false; }); diff --git a/clang/test/Modules/pr88400.cppm b/clang/test/Modules/pr88400.cppm new file mode 100644 index 00000000000000..ff69137a0b9040 --- /dev/null +++ b/clang/test/Modules/pr88400.cppm @@ -0,0 +1,61 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/bar.cppm -emit-module-interface -o %t/bar.pcm +// RUN: %clang_cc1 -std=c++20 %t/foo.cc -fmodule-file=bar=%t/bar.pcm -fsyntax-only -verify +// RUN: %clang_cc1 -std=c++20 %t/bar.cc -fmodule-file=bar=%t/bar.pcm -fsyntax-only -verify +// +// RUN: %clang_cc1 -std=c++20 %t/bar.cppm -emit-reduced-module-interface -o %t/bar.pcm +// RUN: %clang_cc1 -std=c++20 %t/foo.cc -fmodule-file=bar=%t/bar.pcm -fsyntax-only -verify +// RUN: %clang_cc1 -std=c++20 %t/bar.cc -fmodule-file=bar=%t/bar.pcm -fsyntax-only -verify + +//--- header.h +#pragma once + +namespace N { + template + concept X = true; + + template + class Y { + public: + template + friend class Y; + }; + + inline Y x; +} + +//--- bar.cppm +module; + +#include "header.h" + +export module bar; + +namespace N { + // To make sure N::Y won't get elided. + using N::x; +} + +//--- foo.cc +// expected-no-diagnostics +#include "header.h" + +import bar; + +void y() { + N::Y y{}; +}; + +//--- bar.cc +// expected-no-diagnostics +import bar; + +#include "header.h" + +void y() { + N::Y y{}; +}; +