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