diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index f10cae41cb71..7abb74cd0b74 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3581,6 +3581,9 @@ def err_static_non_static : Error< "static declaration of %0 follows non-static declaration">; def err_different_language_linkage : Error< "declaration of %0 has a different language linkage">; +def ext_retained_language_linkage : Extension< + "friend function %0 retaining previous language linkage is an extension">, + InGroup>; def err_extern_c_global_conflict : Error< "declaration of %1 %select{with C language linkage|in global scope}0 " "conflicts with declaration %select{in global scope|with C language linkage}0">; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 81ade1f3ce09..80f95e686900 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2607,9 +2607,22 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S, } if (haveIncompatibleLanguageLinkages(Old, New)) { - Diag(New->getLocation(), diag::err_different_language_linkage) << New; - Diag(Old->getLocation(), PrevDiag); - return true; + // As a special case, retain the language linkage from previous + // declarations of a friend function as an extension. + // + // This liberal interpretation of C++ [class.friend]p3 matches GCC/MSVC + // and is useful because there's otherwise no way to specify language + // linkage within class scope. + // + // Check cautiously as the friend object kind isn't yet complete. + if (New->getFriendObjectKind() != Decl::FOK_None) { + Diag(New->getLocation(), diag::ext_retained_language_linkage) << New; + Diag(Old->getLocation(), PrevDiag); + } else { + Diag(New->getLocation(), diag::err_different_language_linkage) << New; + Diag(Old->getLocation(), PrevDiag); + return true; + } } if (OldQTypeForComparison == NewQType) diff --git a/clang/test/SemaCXX/linkage-spec.cpp b/clang/test/SemaCXX/linkage-spec.cpp index bdc217d9c8cf..1598d0e35a05 100644 --- a/clang/test/SemaCXX/linkage-spec.cpp +++ b/clang/test/SemaCXX/linkage-spec.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify -Wretained-language-linkage -DW_RETAINED_LANGUAGE_LINKAGE %s extern "C" { extern "C" void f(int); } @@ -154,3 +155,21 @@ void bar_pr7927() { ::f_pr7927(E_7927); ::f_pr7927(0); } + +namespace PR17337 { + extern "C++" { + class Foo; + extern "C" int bar3(Foo *y); + class Foo { + int x; + friend int bar3(Foo *y); +#ifdef W_RETAINED_LANGUAGE_LINKAGE +// expected-note@-5 {{previous declaration is here}} +// expected-warning@-3 {{retaining previous language linkage}} +#endif + }; + extern "C" int bar3(Foo *y) { + return y->x; + } + } +}