erichkeane updated this revision to Diff 475836.
erichkeane added a comment.

Updated to work via option-3 above, I believe this is the appropriate solution 
based on the itanium proposal and the C++ standard.  @rjmccall, what concerns 
do we still have about Itanium being willing to add this?  Do we think any 
progress is EVER going to be made?


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D126818/new/

https://reviews.llvm.org/D126818

Files:
  clang/lib/AST/ItaniumMangle.cpp
  clang/test/CodeGenCXX/friend-concepts-mangling.cpp

Index: clang/test/CodeGenCXX/friend-concepts-mangling.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/friend-concepts-mangling.cpp
@@ -0,0 +1,71 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -emit-llvm -o - %s | FileCheck %s
+
+// See US115 from P2103R0 and 
+// https://github.com/itanium-cxx-abi/cxx-abi/issues/24.
+// Friends that have concept constraints that refer to a containing template 
+// (which makes them required to be a definition) must mangle their containing
+// type.
+struct Base {};
+
+template <int N>
+struct S : public Base {
+  friend int func(Base &)
+    requires(N == 1)
+    { return 1; }
+  friend int func(Base &)
+    requires(N == 2)
+    { return 3; }
+  // Without the 'F' mangling addition, this is in conflict with the above.
+  int func(Base &) { return 5; }
+
+  template<typename T>
+    friend int templ_func(Base &)
+    requires(N == 1 || N == 2)
+    { return 10; }
+
+  template<typename T>
+    int templ_func(Base &)
+    { return 15; }
+};
+
+void useS() {
+  S<1> s1{};
+  S<2> s2{};
+  auto const func1 = func(s1);
+  // CHECK: call noundef i32 @_ZN1SILi1EEF4funcER4Base
+  auto const func2 = func(s2);
+  // CHECK: call noundef i32 @_ZN1SILi2EEF4funcER4Base
+
+  // These have the same signature, so we need to make sure they have a
+  // different mangling (or more correctly, that the friend functions do not
+  // conflict). Note that these are the same as above, with the exception of the
+  // missing 'F' before the unqualified name.
+  auto const func3 = s1.func(s1);
+  // CHECK: call noundef i32 @_ZN1SILi1EE4funcER4Base
+  auto const func4 = s2.func(s2);
+  // CHECK: call noundef i32 @_ZN1SILi2EE4funcER4Base
+
+  auto const templ1 = templ_func<int>(s1);
+  // CHECK: call noundef i32 @_ZN1SILi1EEF10templ_funcIiEEiR4Base
+  auto const templ2 = templ_func<int>(s2);
+  // CHECK: call noundef i32 @_ZN1SILi2EEF10templ_funcIiEEiR4Base
+  auto const templ3 = s1.templ_func<int>(s1);
+  // CHECK: call noundef i32 @_ZN1SILi1EE10templ_funcIiEEiR4Base
+  auto const templ4 = s2.templ_func<int>(s2);
+  // CHECK: call noundef i32 @_ZN1SILi2EE10templ_funcIiEEiR4Base
+}
+
+
+template<int N>
+struct U {
+  friend void foo(int) requires (N == 1) {}
+  friend void foo(int) requires (N == 2) {}
+  operator int() const;
+};
+
+void UseU() {
+  foo(U<1>{});
+  // CHECK: call void @_ZN1UILi1EEF3fooEi
+  foo(U<2>{});
+  // CHECK: call void @_ZN1UILi2EEF3fooEi
+}
Index: clang/lib/AST/ItaniumMangle.cpp
===================================================================
--- clang/lib/AST/ItaniumMangle.cpp
+++ clang/lib/AST/ItaniumMangle.cpp
@@ -610,6 +610,26 @@
   return StdNamespace;
 }
 
+// Determine if this is a constrained friend that meets the requirements to be a
+// different scope, according to [temp.friend]p9.
+static bool IsDifferentlyMangledConstrainedFriend(const Decl *D) {
+  if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
+    return IsDifferentlyMangledConstrainedFriend(FTD->getTemplatedDecl());
+
+  if (D->getFriendObjectKind() == Decl::FOK_None)
+    return false;
+
+  const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
+
+  if (!FD)
+    return false;
+
+  if (FD->getPrimaryTemplate())
+    return FD->FriendConstraintRefersToEnclosingTemplate();
+
+  return FD->getTrailingRequiresClause();
+}
+
 /// Retrieve the declaration context that should be used when mangling the given
 /// declaration.
 const DeclContext *
@@ -659,6 +679,11 @@
     if (FD->isExternC())
       return getASTContext().getTranslationUnitDecl();
 
+  // If these are mangled differently because they are constrained friends,
+  // mangle it in its lexical context.
+  if (IsDifferentlyMangledConstrainedFriend(D))
+    return D->getLexicalDeclContext();
+
   return DC->getRedeclContext();
 }
 
@@ -1694,13 +1719,22 @@
   Out << II->getLength() << II->getName();
 }
 
+// See if the 'F' between the prefix of a nested name or nested template is
+// necessary.  That is, is this a friend with a constraint that refers to an
+// enclosing template.
+static void mangleConstrainedFriendness(llvm::raw_ostream &Out, GlobalDecl GD) {
+  if (IsDifferentlyMangledConstrainedFriend(GD.getDecl()))
+      Out << 'F';
+}
+
 void CXXNameMangler::mangleNestedName(GlobalDecl GD,
                                       const DeclContext *DC,
                                       const AbiTagList *AdditionalAbiTags,
                                       bool NoFunction) {
   const NamedDecl *ND = cast<NamedDecl>(GD.getDecl());
   // <nested-name>
-  //   ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E
+  //   ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> [F] 
+  //       <unqualified-name> E
   //   ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix>
   //       <template-args> E
 
@@ -1721,6 +1755,7 @@
     mangleTemplateArgs(asTemplateName(TD), *TemplateArgs);
   } else {
     manglePrefix(DC, NoFunction);
+    mangleConstrainedFriendness(Out, GD);
     mangleUnqualifiedName(GD, DC, AdditionalAbiTags);
   }
 
@@ -2139,7 +2174,7 @@
 void CXXNameMangler::mangleTemplatePrefix(GlobalDecl GD,
                                           bool NoFunction) {
   const TemplateDecl *ND = cast<TemplateDecl>(GD.getDecl());
-  // <template-prefix> ::= <prefix> <template unqualified-name>
+  // <template-prefix> ::= <prefix> [F] <template unqualified-name>
   //                   ::= <template-param>
   //                   ::= <substitution>
   // <template-template-param> ::= <template-param>
@@ -2154,6 +2189,7 @@
   } else {
     const DeclContext *DC = Context.getEffectiveDeclContext(ND);
     manglePrefix(DC, NoFunction);
+    mangleConstrainedFriendness(Out, GD);
     if (isa<BuiltinTemplateDecl>(ND) || isa<ConceptDecl>(ND))
       mangleUnqualifiedName(GD, DC, nullptr);
     else
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to