Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2988,6 +2988,10 @@ "'gnu_inline' attribute requires function to be marked 'inline'," " attribute ignored">, InGroup; +def warn_gnu_inline_cplusplus_without_extern : Warning< + "'gnu_inline' attribute without 'extern' in C++ treated as externally" + " available, this changed in Clang 10">, + InGroup>; def err_attribute_vecreturn_only_vector_member : Error< "the vecreturn attribute can only be used on a class or structure with one member, which must be a vector">; def err_attribute_vecreturn_only_pod_record : Error< Index: clang/lib/AST/Decl.cpp =================================================================== --- clang/lib/AST/Decl.cpp +++ clang/lib/AST/Decl.cpp @@ -3251,6 +3251,9 @@ return true; } + if (Context.getLangOpts().CPlusPlus) + return false; + if (Context.getLangOpts().GNUInline || hasAttr()) { // With GNU inlining, a declaration with 'inline' but not 'extern', forces // an externally visible definition. @@ -3279,9 +3282,6 @@ return FoundBody; } - if (Context.getLangOpts().CPlusPlus) - return false; - // C99 6.7.4p6: // [...] If all of the file scope declarations for a function in a // translation unit include the inline function specifier without extern, @@ -3360,6 +3360,8 @@ // If it's not the case that both 'inline' and 'extern' are // specified on the definition, then this inline definition is // externally visible. + if (Context.getLangOpts().CPlusPlus) + return false; if (!(isInlineSpecified() && getStorageClass() == SC_Extern)) return true; Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -4423,6 +4423,9 @@ return; } + if (S.LangOpts.CPlusPlus && Fn->getStorageClass() != SC_Extern) + S.Diag(AL.getLoc(), diag::warn_gnu_inline_cplusplus_without_extern); + D->addAttr(::new (S.Context) GNUInlineAttr(AL.getRange(), S.Context, AL.getAttributeSpellingListIndex())); Index: clang/test/CodeGen/inline.c =================================================================== --- clang/test/CodeGen/inline.c +++ clang/test/CodeGen/inline.c @@ -52,7 +52,7 @@ // CHECK3-LABEL: define i32 @_Z3barv() // CHECK3-LABEL: define linkonce_odr i32 @_Z3foov() // CHECK3-NOT: unreferenced -// CHECK3-LABEL: define void @_Z10gnu_inlinev() +// CHECK3-LABEL: define available_externally void @_Z10gnu_inlinev() // CHECK3-LABEL: define available_externally void @_Z13gnu_ei_inlinev() // CHECK3-NOT: @_Z5testCv // CHECK3-LABEL: define linkonce_odr i32 @_Z2eiv() @@ -85,6 +85,7 @@ extern __inline void unreferenced2() {} __inline __attribute((__gnu_inline__)) void gnu_inline() {} +void (*P1)() = gnu_inline; // PR3988 extern __inline __attribute__((gnu_inline)) void gnu_ei_inline() {} Index: clang/test/SemaCUDA/gnu-inline.cu =================================================================== --- clang/test/SemaCUDA/gnu-inline.cu +++ clang/test/SemaCUDA/gnu-inline.cu @@ -2,9 +2,7 @@ #include "Inputs/cuda.h" -// expected-no-diagnostics - // Check that we can handle gnu_inline functions when compiling in CUDA mode. void foo(); -inline __attribute__((gnu_inline)) void bar() { foo(); } +inline __attribute__((gnu_inline)) void bar() { foo(); } // expected-warning {{'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10}} Index: clang/test/SemaCXX/gnu_inline.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/gnu_inline.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +extern inline +__attribute__((__gnu_inline__)) +void gnu_inline1() {} + +inline +__attribute__((__gnu_inline__)) // expected-warning {{'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10}} +void gnu_inline2() {} Index: clang/test/SemaCXX/undefined-inline.cpp =================================================================== --- clang/test/SemaCXX/undefined-inline.cpp +++ clang/test/SemaCXX/undefined-inline.cpp @@ -40,20 +40,20 @@ } namespace test8 { - inline void foo() __attribute__((gnu_inline)); + inline void foo() __attribute__((gnu_inline)); // expected-warning {{'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10}} void test() { foo(); } } namespace test9 { void foo(); void test() { foo(); } - inline void foo() __attribute__((gnu_inline)); + inline void foo() __attribute__((gnu_inline)); // expected-warning {{'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10}} } namespace test10 { inline void foo(); void test() { foo(); } - inline void foo() __attribute__((gnu_inline)); + inline void foo() __attribute__((gnu_inline)); // expected-warning {{'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10}} } namespace test11 {