diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -447,6 +447,14 @@ return const_cast(this)->getDeclContext(); } + /// Return the non transparent context. + /// See the comment of `DeclContext::isTransparentContext()` for the + /// definition of transparent context. + DeclContext *getNonTransparentDeclContext(); + const DeclContext *getNonTransparentDeclContext() const { + return const_cast(this)->getNonTransparentDeclContext(); + } + /// Find the innermost non-closure ancestor of this declaration, /// walking up through blocks, lambdas, etc. If that ancestor is /// not a code context (!isFunctionOrMethod()), returns null. @@ -2009,7 +2017,7 @@ /// Here, E is a transparent context, so its enumerator (Val1) will /// appear (semantically) that it is in the same context of E. /// Examples of transparent contexts include: enumerations (except for - /// C++0x scoped enums), and C++ linkage specifications. + /// C++0x scoped enums), C++ linkage specifications and export declaration. bool isTransparentContext() const; /// Determines whether this context or some of its ancestors is a diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -1032,6 +1032,11 @@ return Ty->getAs(); } +DeclContext *Decl::getNonTransparentDeclContext() { + assert(getDeclContext()); + return getDeclContext()->getNonTransparentContext(); +} + /// Starting at a given context (a Decl or DeclContext), look for a /// code context that is not a closure (a lambda, block, etc.). template static Decl *getNonClosureContext(T *D) { diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -168,7 +168,7 @@ // instead of its semantic parent, unless of course the pattern we're // instantiating actually comes from the file's context! if (Function->getFriendObjectKind() && - Function->getDeclContext()->isFileContext() && + Function->getNonTransparentDeclContext()->isFileContext() && (!Pattern || !Pattern->getLexicalDeclContext()->isFileContext())) { Ctx = Function->getLexicalDeclContext(); RelativeToPrimary = false; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -6147,6 +6147,8 @@ // Move to the outer template scope. if (FunctionDecl *FD = dyn_cast(DC)) { + // FIXME: We should use `getNonTransparentDeclContext()` here instead + // of `getDeclContext()` once we find the invalid test case. if (FD->getFriendObjectKind() && FD->getDeclContext()->isFileContext()){ DC = FD->getLexicalDeclContext(); continue; diff --git a/clang/test/Modules/pr56826.cppm b/clang/test/Modules/pr56826.cppm new file mode 100644 --- /dev/null +++ b/clang/test/Modules/pr56826.cppm @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s + +export module m3; +export template struct X { + template friend void f(Self &&self) { + (Self&)self; // expected-warning {{expression result unused}} + } +}; +void g() { f(X{}); } // expected-note {{in instantiation of function template specialization}} diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp --- a/clang/unittests/AST/DeclTest.cpp +++ b/clang/unittests/AST/DeclTest.cpp @@ -260,3 +260,22 @@ EXPECT_EQ(b->getLinkageInternal(), ModuleLinkage); EXPECT_EQ(g->getLinkageInternal(), ModuleLinkage); } + +TEST(Decl, GetNonTransparentDeclContext) { + llvm::Annotations Code(R"( + export module m3; + export template struct X { + template friend void f(Self &&self) { + (Self&)self; + } + };)"); + + auto AST = + tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); + ASTContext &Ctx = AST->getASTContext(); + + auto *f = selectFirst( + "f", match(functionDecl(hasName("f")).bind("f"), Ctx)); + + EXPECT_TRUE(f->getNonTransparentDeclContext()->isFileContext()); +}