[clang] [C++20][Modules] Load function body from the module that gives canonical decl (PR #111992)

Dmitry Polukhin via cfe-commits cfe-commits at lists.llvm.org
Mon Oct 14 09:21:00 PDT 2024


================
@@ -10057,15 +10057,18 @@ void ASTReader::finishPendingActions() {
       // For a function defined inline within a class template, force the
       // canonical definition to be the one inside the canonical definition of
       // the template. This ensures that we instantiate from a correct view
-      // of the template.
+      // of the template. This behaviour seems to be important only for inline
+      // friend functions. For normal member functions, it might results in
+      // selecting canonical decl from module A but body from module B.
       //
       // Sadly we can't do this more generally: we can't be sure that all
       // copies of an arbitrary class definition will have the same members
       // defined (eg, some member functions may not be instantiated, and some
       // special members may or may not have been implicitly defined).
-      if (auto *RD = dyn_cast<CXXRecordDecl>(FD->getLexicalParent()))
-        if (RD->isDependentContext() && !RD->isThisDeclarationADefinition())
-          continue;
+      if (FD->getFriendObjectKind())
----------------
dmpolukhin wrote:

@ChuanqiXu9 and @ilya-biryukov I'm not sure that I fully understand the example [test case](https://github.com/llvm/llvm-project/blob/main/clang/test/Modules/friend-definition-2.cpp). Do you understand what does the example really do?

Things unclear to me marked with `// >>>`.
```
// RUN: %clang_cc1 -std=c++14 -fmodules %s -verify
// RUN: %clang_cc1 -std=c++14 -fmodules %s -verify -triple i686-windows
// expected-no-diagnostics

// >>> it looks like undocumented pragma?
#pragma clang module build A 

// >>> Is it modulemap file syntax?
module A {}
#pragma clang module contents
#pragma clang module begin A

// >>> What is the context of this code, is it GMF or part of module A, is it implicitly exported?
template<typename T> struct ct { friend auto operator-(ct, ct) { struct X {}; return X(); } void x(); };
#pragma clang module end
#pragma clang module endbuild

#pragma clang module build B
module B {}
#pragma clang module contents
#pragma clang module begin B

// >>> Seems like it was part of GMF otherwise it is duplicating declaration?
template<typename T> struct ct { friend auto operator-(ct, ct) { struct X{}; return X(); } void x(); };
inline auto f() { return ct<float>() - ct<float>(); }
#pragma clang module end
#pragma clang module endbuild

// >>> Seems like here we are in different translation unit

// Force the definition of ct in module A to be the primary definition.
#pragma clang module import A
// >>> Why does it define something from module A !?
// >>> Should we see here error like: `declaration of 'x' in the global module follows declaration in module A`
template<typename T> void ct<T>::x() {}

// Attempt to cause the definition of operator- in the ct primary template in
// module B to be the primary definition of that function. If that happens,
// we'll be left with a class template ct that appears to not contain a
// definition of the inline friend function.
#pragma clang module import B
auto v = f();

ct<int> make();
void h() {
  make() - make();
}
```

https://github.com/llvm/llvm-project/pull/111992


More information about the cfe-commits mailing list