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