erichkeane created this revision.
erichkeane added reviewers: rjmccall, clang-language-wg.
Herald added a project: All.
erichkeane requested review of this revision.
Thanks to concepts, C++ can now have friends that differ by their
containing function.  Because of this, we need to differentiate ones
from different instantiations.  This patch implements itanium-cxx-abi
issue #24 (https://github.com/itanium-cxx-abi/cxx-abi/issues/24).

In addition to mangling the containing scope, this ALSO adds the 'F'
differentiator as proposed.


https://reviews.llvm.org/D126818

Files:
  clang/include/clang/AST/Decl.h
  clang/lib/AST/Decl.cpp
  clang/lib/AST/ItaniumMangle.cpp
  clang/test/CodeGenCXX/friend-concepts.cpp

Index: clang/test/CodeGenCXX/friend-concepts.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/friend-concepts.cpp
@@ -0,0 +1,55 @@
+// 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 (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; }
+};
+
+int main() {
+  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
+}
Index: clang/lib/AST/ItaniumMangle.cpp
===================================================================
--- clang/lib/AST/ItaniumMangle.cpp
+++ clang/lib/AST/ItaniumMangle.cpp
@@ -658,10 +658,25 @@
     if (VD->isExternC())
       return getASTContext().getTranslationUnitDecl();
 
-  if (const auto *FD = dyn_cast<FunctionDecl>(D))
+  if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
     if (FD->isExternC())
       return getASTContext().getTranslationUnitDecl();
 
+    // If this is a friend, and has constraints, mangle it in the decl context
+    // of its lexical context, since in different scopes, they are considered
+    // different functions.  See [temp.friend]p9.
+    if (FD->isConstrainedFriend())
+        return FD->getLexicalDeclContext();
+  }
+
+  // If this is a friend, and has constraints, mangle it in the decl context
+  // of its lexical context, since in different scopes, they are considered
+  // different functions.  See [temp.friend]p9.
+  // For template friends, we just use the templated decl version here.
+  if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
+    if (FTD->getTemplatedDecl()->isConstrainedFriend())
+      return FTD->getLexicalDeclContext();
+
   return DC->getRedeclContext();
 }
 
@@ -1697,13 +1712,26 @@
   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.
+static void mangleConstrainedFriendness(llvm::raw_ostream &Out, GlobalDecl GD) {
+  if (const auto *FD = dyn_cast<FunctionDecl>(GD.getDecl()))
+    if (FD->isConstrainedFriend())
+      Out << 'F';
+
+  if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(GD.getDecl()))
+    if (FTD->getTemplatedDecl()->isConstrainedFriend())
+      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
 
@@ -1724,6 +1752,7 @@
     mangleTemplateArgs(asTemplateName(TD), *TemplateArgs);
   } else {
     manglePrefix(DC, NoFunction);
+    mangleConstrainedFriendness(Out, GD);
     mangleUnqualifiedName(GD, DC, AdditionalAbiTags);
   }
 
@@ -2143,7 +2172,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>
@@ -2158,6 +2187,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
Index: clang/lib/AST/Decl.cpp
===================================================================
--- clang/lib/AST/Decl.cpp
+++ clang/lib/AST/Decl.cpp
@@ -3349,6 +3349,13 @@
   return isMultiVersion() && hasAttr<TargetClonesAttr>();
 }
 
+bool FunctionDecl::isConstrainedFriend() const {
+  return getFriendObjectKind() &&
+         (getTrailingRequiresClause() ||
+          (getDescribedFunctionTemplate() &&
+           getDescribedFunctionTemplate()->hasAssociatedConstraints()));
+}
+
 void
 FunctionDecl::setPreviousDeclaration(FunctionDecl *PrevDecl) {
   redeclarable_base::setPreviousDecl(PrevDecl);
Index: clang/include/clang/AST/Decl.h
===================================================================
--- clang/include/clang/AST/Decl.h
+++ clang/include/clang/AST/Decl.h
@@ -2490,6 +2490,11 @@
       AC.push_back(TRC);
   }
 
+  // For the purposes of mangling, determine if this is a friend function with
+  // constraints on it, so that we can decide to mangle this with its containing
+  // scope and the 'F' for itanium.
+  bool isConstrainedFriend() const;
+
   void setPreviousDeclaration(FunctionDecl * PrevDecl);
 
   FunctionDecl *getCanonicalDecl() override;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to