summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2021-04-06 01:21:05 -0400
committerJason Merrill <jason@redhat.com>2022-05-13 13:39:30 -0400
commit623842bead8452c03f2b1c6817f2a86a1d2d4d12 (patch)
tree1183f18470fbe365e2a09707580a3ece08e7fe85
parent7ce3d2ab2a1a144f94a3a9be716fb831cb76603b (diff)
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.
-rw-r--r--gcc/cp/decl.c18
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/inline-var9.C40
2 files changed, 54 insertions, 4 deletions
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 565f5711fd5..7fb669fd48f 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -7010,10 +7010,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;
@@ -7345,6 +7348,13 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
{
layout_var_decl (decl);
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);
}
/* This needs to happen after the linkage is set. */
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 00000000000..43c9748877b
--- /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 <class T>
+struct C {
+ static constexpr int c = 5;
+};
+template <class T>
+constexpr int C<T>::c;
+
+int i = C<int>::c;