Index: include/clang/AST/ASTConsumer.h =================================================================== --- include/clang/AST/ASTConsumer.h +++ include/clang/AST/ASTConsumer.h @@ -59,6 +59,10 @@ /// completed. virtual void HandleInlineMethodDefinition(CXXMethodDecl *D) {} + /// \brief This callback is invoked each time an inline friend function + /// definition is completed. + virtual void HandleInlineFriendFunctionDefinition(FunctionDecl *D) {} + /// HandleInterestingDecl - Handle the specified interesting declaration. This /// is called by the AST reader when deserializing things that might interest /// the consumer. The default implementation forwards to HandleTopLevelDecl. Index: include/clang/Frontend/MultiplexConsumer.h =================================================================== --- include/clang/Frontend/MultiplexConsumer.h +++ include/clang/Frontend/MultiplexConsumer.h @@ -37,6 +37,7 @@ void HandleCXXStaticMemberVarInstantiation(VarDecl *VD) override; bool HandleTopLevelDecl(DeclGroupRef D) override; void HandleInlineMethodDefinition(CXXMethodDecl *D) override; + void HandleInlineFriendFunctionDefinition(FunctionDecl *D) override; void HandleInterestingDecl(DeclGroupRef D) override; void HandleTranslationUnit(ASTContext &Ctx) override; void HandleTagDeclDefinition(TagDecl *D) override; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -1769,6 +1769,7 @@ Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body, bool IsInstantiation); Decl *ActOnSkippedFunctionBody(Decl *Decl); void ActOnFinishInlineMethodDef(CXXMethodDecl *D); + void ActOnFinishInlineFriendFunctionDef(FunctionDecl *D); /// ActOnFinishDelayedAttribute - Invoked when we have finished parsing an /// attribute for which parsing is delayed. Index: lib/CodeGen/CodeGenAction.cpp =================================================================== --- lib/CodeGen/CodeGenAction.cpp +++ lib/CodeGen/CodeGenAction.cpp @@ -132,6 +132,19 @@ LLVMIRGeneration.stopTimer(); } + void HandleInlineFriendFunctionDefinition(FunctionDecl *D) override { + PrettyStackTraceDecl CrashInfo(D, SourceLocation(), + Context->getSourceManager(), + "LLVM IR generation of inline friend function"); + if (llvm::TimePassesIsEnabled) + LLVMIRGeneration.startTimer(); + + Gen->HandleInlineFriendFunctionDefinition(D); + + if (llvm::TimePassesIsEnabled) + LLVMIRGeneration.stopTimer(); + } + void HandleTranslationUnit(ASTContext &C) override { { PrettyStackTraceString CrashInfo("Per-file LLVM IR generation"); Index: lib/CodeGen/ModuleBuilder.cpp =================================================================== --- lib/CodeGen/ModuleBuilder.cpp +++ lib/CodeGen/ModuleBuilder.cpp @@ -163,6 +163,18 @@ Builder->AddDeferredUnusedCoverageMapping(D); } + void HandleInlineFriendFunctionDefinition(FunctionDecl *D) override { + if (Diags.hasErrorOccurred()) + return; + + assert(D->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend) && + "Must be a friend decl"); + assert(D->doesThisDeclarationHaveABody()); + + if (Ctx->getTargetInfo().getCXXABI().isMicrosoft()) + Builder->EmitTopLevelDecl(D); + } + /// HandleTagDeclDefinition - This callback is invoked each time a TagDecl /// to (e.g. struct, union, enum, class) is completed. This allows the /// client hack on the type, which can occur at any point in the file Index: lib/Frontend/MultiplexConsumer.cpp =================================================================== --- lib/Frontend/MultiplexConsumer.cpp +++ lib/Frontend/MultiplexConsumer.cpp @@ -271,6 +271,11 @@ Consumer->HandleInlineMethodDefinition(D); } +void MultiplexConsumer::HandleInlineFriendFunctionDefinition(FunctionDecl *D) { + for (auto &Consumer : Consumers) + Consumer->HandleInlineFriendFunctionDefinition(D); +} + void MultiplexConsumer::HandleCXXStaticMemberVarInstantiation(VarDecl *VD) { for (auto &Consumer : Consumers) Consumer->HandleCXXStaticMemberVarInstantiation(VD); Index: lib/Parse/ParseCXXInlineMethods.cpp =================================================================== --- lib/Parse/ParseCXXInlineMethods.cpp +++ lib/Parse/ParseCXXInlineMethods.cpp @@ -565,6 +565,8 @@ if (CXXMethodDecl *MD = dyn_cast_or_null(LM.D)) Actions.ActOnFinishInlineMethodDef(MD); + else if (auto *FD = dyn_cast_or_null(LM.D)) + Actions.ActOnFinishInlineFriendFunctionDef(FD); } /// ParseLexedMemberInitializers - We finished parsing the member specification Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -10655,6 +10655,10 @@ Consumer.HandleInlineMethodDefinition(D); } +void Sema::ActOnFinishInlineFriendFunctionDef(FunctionDecl *D) { + Consumer.HandleInlineFriendFunctionDefinition(D); +} + static bool ShouldWarnAboutMissingPrototype(const FunctionDecl *FD, const FunctionDecl*& PossibleZeroParamPrototype) { // Don't warn about invalid declarations. Index: test/CodeGenCXX/dllexport.cpp =================================================================== --- test/CodeGenCXX/dllexport.cpp +++ test/CodeGenCXX/dllexport.cpp @@ -255,9 +255,11 @@ // GNU-DAG: define dllexport void @_Z7friend1v() // MSC-DAG: define dllexport void @"\01?friend2@@YAXXZ"() // GNU-DAG: define dllexport void @_Z7friend2v() +// MSC-DAG: define weak_odr dllexport void @"\01?friend3@@YAXXZ"() struct FuncFriend { friend __declspec(dllexport) void friend1(); friend __declspec(dllexport) void friend2(); + friend __declspec(dllexport) void friend3() {} }; __declspec(dllexport) void friend1() {} void friend2() {}