From 8685348075d91945066dea9b564bd42cbc1d22bd Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Tue, 6 Apr 2021 01:21:05 -0400 Subject: [PATCH] c++: C++17 constexpr static data member linkage [PR99901] C++17 makes constexpr static data members implicitly inline variables. In C++14, a subsequent out-of-class declaration is the definition. We want to continue emitting a symbol for such a declaration in C++17 mode, for ABI compatibility with C++14 code that wants to refer to it. Normally I'd distinguish in- and out-of-class declarations by looking at DECL_IN_AGGR_P, but we never set DECL_IN_AGGR_P on inline variables. I think that's wrong, but don't want to mess with it so close to release. Conveniently, we already have a test for in-class declaration earlier in the function. gcc/cp/ChangeLog: PR c++/99901 * decl.c (cp_finish_decl): mark_needed an implicitly inline static data member with an out-of-class redeclaration. gcc/testsuite/ChangeLog: PR c++/99901 * g++.dg/cpp1z/inline-var9.C: New test. --- gcc/cp/decl.c | 18 ++++++++++---- gcc/testsuite/g++.dg/cpp1z/inline-var9.C | 40 ++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1z/inline-var9.C diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 6789aa8..edab147 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -7693,10 +7693,13 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, if (asmspec_tree && asmspec_tree != error_mark_node) asmspec = TREE_STRING_POINTER (asmspec_tree); - if (current_class_type - && CP_DECL_CONTEXT (decl) == current_class_type - && TYPE_BEING_DEFINED (current_class_type) - && !CLASSTYPE_TEMPLATE_INSTANTIATION (current_class_type) + bool in_class_decl + = (current_class_type + && CP_DECL_CONTEXT (decl) == current_class_type + && TYPE_BEING_DEFINED (current_class_type) + && !CLASSTYPE_TEMPLATE_INSTANTIATION (current_class_type)); + + if (in_class_decl && (DECL_INITIAL (decl) || init)) DECL_INITIALIZED_IN_CLASS_P (decl) = 1; @@ -8069,6 +8072,13 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, if (!flag_weak) /* Check again now that we have an initializer. */ maybe_commonize_var (decl); + /* A class-scope constexpr variable with an out-of-class declaration. + C++17 makes them implicitly inline, but still force it out. */ + if (DECL_INLINE_VAR_P (decl) + && !DECL_VAR_DECLARED_INLINE_P (decl) + && !DECL_TEMPLATE_INSTANTIATION (decl) + && !in_class_decl) + mark_needed (decl); } if (var_definition_p diff --git a/gcc/testsuite/g++.dg/cpp1z/inline-var9.C b/gcc/testsuite/g++.dg/cpp1z/inline-var9.C new file mode 100644 index 0000000..43c9748 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/inline-var9.C @@ -0,0 +1,40 @@ +// PR c++/99901 +// { dg-do compile { target c++11 } } +// { dg-final { scan-assembler-not "_ZN1A1aE" } } +// { dg-final { scan-assembler-not "_ZN2A21aE" } } +// { dg-final { scan-assembler-not "_ZN1CIiE1cE" } } +// { dg-final { scan-assembler "_ZN1B1bE" } } +// { dg-final { scan-assembler "_ZN2B21bE" } } +// { dg-final { scan-assembler "_ZN2B31bE" } } + +struct A { + static const int a = 5; +}; + +struct A2 { + static constexpr int a = 5; +}; + +struct B { + static const int b; +}; +constexpr int B::b = 5; + +struct B2 { + static const int b = 5; +}; +constexpr int B2::b; + +struct B3 { + static constexpr int b = 5; +}; +const int B3::b; + +template +struct C { + static constexpr int c = 5; +}; +template +constexpr int C::c; + +int i = C::c; -- 2.7.4