From: Martin Storsjo Date: Fri, 27 Sep 2019 12:25:19 +0000 (+0000) Subject: [clang] [AST] Treat "inline gnu_inline" the same way as "extern inline gnu_inline... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=822f64eae96c73035a0890db1614d4ce2b792fa4;p=clang [clang] [AST] Treat "inline gnu_inline" the same way as "extern inline gnu_inline" in C++ mode This matches how GCC handles it, see e.g. https://gcc.godbolt.org/z/HPplnl. GCC documents the gnu_inline attribute with "In C++, this attribute does not depend on extern in any way, but it still requires the inline keyword to enable its special behavior." The previous behaviour of gnu_inline in C++, without the extern keyword, can be traced back to the original commit that added support for gnu_inline, SVN r69045. Differential Revision: https://reviews.llvm.org/D67414 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@373078 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index 5dbc4a0415..91ae3071ff 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -128,7 +128,10 @@ C11 Feature Support C++ Language Changes in Clang ----------------------------- -- ... +- The behaviour of the `gnu_inline` attribute now matches GCC, for cases + where used without the `extern` keyword. As this is a change compared to + how it behaved in previous Clang versions, a warning is emitted for this + combination. C++1z Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index c2cef944b6..adc658bb29 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -3008,6 +3008,10 @@ def warn_gnu_inline_attribute_requires_inline : Warning< "'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< diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index e95338138f..19a012b332 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3261,6 +3261,9 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const { 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. @@ -3289,9 +3292,6 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const { 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, @@ -3371,6 +3371,8 @@ bool FunctionDecl::isInlineDefinitionExternallyVisible() const { // 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; diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index b69cb5d843..d31b48c122 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -4255,6 +4255,9 @@ static void handleGNUInlineAttr(Sema &S, Decl *D, const ParsedAttr &AL) { 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(S.Context, AL)); } diff --git a/test/CodeGen/inline.c b/test/CodeGen/inline.c index ea6e9d7649..b511ece281 100644 --- a/test/CodeGen/inline.c +++ b/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 @@ __inline void unreferenced1() {} 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() {} diff --git a/test/SemaCUDA/gnu-inline.cu b/test/SemaCUDA/gnu-inline.cu index 0ed543326b..e383082f2a 100644 --- a/test/SemaCUDA/gnu-inline.cu +++ b/test/SemaCUDA/gnu-inline.cu @@ -7,4 +7,4 @@ // Check that we can handle gnu_inline functions when compiling in CUDA mode. void foo(); -inline __attribute__((gnu_inline)) void bar() { foo(); } +extern inline __attribute__((gnu_inline)) void bar() { foo(); } diff --git a/test/SemaCXX/gnu_inline.cpp b/test/SemaCXX/gnu_inline.cpp new file mode 100644 index 0000000000..43a4bae48e --- /dev/null +++ b/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() {} diff --git a/test/SemaCXX/undefined-inline.cpp b/test/SemaCXX/undefined-inline.cpp index feb12f4552..21c6b5de79 100644 --- a/test/SemaCXX/undefined-inline.cpp +++ b/test/SemaCXX/undefined-inline.cpp @@ -40,20 +40,20 @@ namespace test7 { } 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 {