]> granicus.if.org Git - clang/commitdiff
Retain previous language linkage of friend function declarations
authorAlp Toker <alp@nuanti.com>
Tue, 22 Oct 2013 22:53:01 +0000 (22:53 +0000)
committerAlp Toker <alp@nuanti.com>
Tue, 22 Oct 2013 22:53:01 +0000 (22:53 +0000)
With this extension, friend function declarations will retain the language
linkage specified for previous declarations instead of emitting an error
diagnostic.

The feature is known to be compatible with GCC and MSVC and permits a
language to be specified indirectly where it cannot otherwise be written
directly in class scope.

Work is ongoing to improve linkage spec diagnostics.

Fixes PR17337.

Reviewed by Richard Smith.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@193206 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/SemaDecl.cpp
test/SemaCXX/linkage-spec.cpp

index f10cae41cb711206784ed20828070bd7104a671c..7abb74cd0b74098e27fe02ae8c2b0744db7769fb 100644 (file)
@@ -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">;
index 81ade1f3ce0911147c79ef96de073a37d225d036..80f95e68690098b8d693d7e2fa89c0d0e1b4f10e 100644 (file)
@@ -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)
index bdc217d9c8cf04a0cf783ee1917b8692a00128cc..1598d0e35a050b1663099e12cd8350dd2b7887b5 100644 (file)
@@ -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;
+    }
+  }
+}