diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index f10cae41cb711206784ed20828070bd7104a671c..7abb74cd0b74098e27fe02ae8c2b0744db7769fb 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/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<DiagGroup<"retained-language-linkage">>;
 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/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 81ade1f3ce0911147c79ef96de073a37d225d036..80f95e68690098b8d693d7e2fa89c0d0e1b4f10e 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/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/test/SemaCXX/linkage-spec.cpp b/test/SemaCXX/linkage-spec.cpp
index bdc217d9c8cf04a0cf783ee1917b8692a00128cc..1598d0e35a050b1663099e12cd8350dd2b7887b5 100644
--- a/test/SemaCXX/linkage-spec.cpp
+++ b/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;
+    }
+  }
+}