c_parse_final_cleanups checks DECL_IN_AGGR_P to avoid trying to emit a
static data member that has not been defined.  The inline variable
patch changed that to exempt inline variables.  But in this case we
haven't instantiated the variable yet, so we really don't have a
definition.

I've taken different approaches to fixing this in trunk and GCC 8: for
GCC 8 I just fix this place.  For the trunk I replace various places
needing to check DECL_IN_AGGR_P && !DECL_INLINE_VAR_P with setting
DECL_IN_AGGR_P appropriately for inline variables.

Tested x86_64-pc-linux-gnu, applying to trunk; I'll apply the GCC 8
patch after the branch thaws.
commit a93c757d3c29aace53f0937971de3d45003df6f9
Author: Jason Merrill <ja...@redhat.com>
Date:   Tue Feb 19 16:47:22 2019 -1000

            PR c++/87921 - wrong error with inline static data member.
    
    c_parse_final_cleanups checks DECL_IN_AGGR_P to avoid trying to emit a
    static data member that has not been defined.  The inline variable patch
    changed that to exempt inline variables.  But in this case we haven't
    instantiated the variable yet, so we really don't have a definition.  This
    patch changes inline variable handling such that DECL_IN_AGGR_P is not set
    for a defined inline variable, so we can remove all the checks of
    DECL_INLINE_VAR_P after DECL_IN_AGGR_P.
    
    With that change we were failing on a static data member that had been
    instantiated due to a use before we got around to processing it in
    instantiate_class_template; we should detect that and avoid all the
    finish_static_data_member_decl processing, which assumes that it is the
    first time we're seeing the variable.
    
            * decl2.c (finish_static_data_member_decl): Don't set DECL_IN_AGGR_P
            for a non-template inline variable.  Do nothing for an
            already-instantiated variable.
            (c_parse_final_cleanups): Check DECL_IN_AGGR_P without
            DECL_INLINE_VAR_P.
            * decl.c (check_initializer): Likewise.
            (make_rtl_for_nonlocal_decl): Likewise.
            * pt.c (instantiate_decl): Likewise.
            * typeck2.c (store_init_value): Likewise.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index ac3654467ac..879712f6dbc 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2956,8 +2956,8 @@ struct GTY(()) lang_decl {
 /* Nonzero for _DECL means that this decl appears in (or will appear
    in) as a member in a RECORD_TYPE or UNION_TYPE node.  It is also for
    detecting circularity in case members are multiply defined.  In the
-   case of a VAR_DECL, it is also used to determine how program storage
-   should be allocated.  */
+   case of a VAR_DECL, it means that no definition has been seen, even
+   if an initializer has been.  */
 #define DECL_IN_AGGR_P(NODE) (DECL_LANG_FLAG_3 (NODE))
 
 /* Nonzero for a VAR_DECL means that the variable's initialization (if
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index c164975318b..bb52b926e8c 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -6562,9 +6562,8 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
     }
 
   if (init_code
-      && (DECL_IN_AGGR_P (decl)
-	  && DECL_INITIALIZED_IN_CLASS_P (decl)
-	  && !DECL_VAR_DECLARED_INLINE_P (decl)))
+      && DECL_IN_AGGR_P (decl)
+      && DECL_INITIALIZED_IN_CLASS_P (decl))
     {
       static int explained = 0;
 
@@ -6632,8 +6631,7 @@ make_rtl_for_nonlocal_decl (tree decl, tree init, const char* asmspec)
 	 external; it is only a declaration, and not a definition.  */
       if (init == NULL_TREE)
 	gcc_assert (DECL_EXTERNAL (decl)
-		    || !TREE_PUBLIC (decl)
-		    || DECL_INLINE_VAR_P (decl));
+		    || !TREE_PUBLIC (decl));
     }
 
   /* We don't create any RTL for local variables.  */
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 72c52e39e00..a5e09d006c1 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -744,6 +744,11 @@ finish_static_data_member_decl (tree decl,
 				tree asmspec_tree,
 				int flags)
 {
+  if (DECL_TEMPLATE_INSTANTIATED (decl))
+    /* We already needed to instantiate this, so the processing in this
+       function is unnecessary/wrong.  */
+    return;
+
   DECL_CONTEXT (decl) = current_class_type;
 
   /* We cannot call pushdecl here, because that would fill in the
@@ -772,7 +777,12 @@ finish_static_data_member_decl (tree decl,
 	  break;
 	}
 
-  DECL_IN_AGGR_P (decl) = 1;
+  if (DECL_INLINE_VAR_P (decl) && !DECL_TEMPLATE_INSTANTIATION (decl))
+    /* An inline variable is immediately defined, so don't set DECL_IN_AGGR_P.
+       Except that if decl is a template instantiation, it isn't defined until
+       instantiate_decl.  */;
+  else
+    DECL_IN_AGGR_P (decl) = 1;
 
   if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
       && TYPE_DOMAIN (TREE_TYPE (decl)) == NULL_TREE)
@@ -4982,7 +4992,7 @@ c_parse_final_cleanups (void)
 	{
 	  if (var_finalized_p (decl) || DECL_REALLY_EXTERN (decl)
 	      /* Don't write it out if we haven't seen a definition.  */
-	      || (DECL_IN_AGGR_P (decl) && !DECL_INLINE_VAR_P (decl)))
+	      || DECL_IN_AGGR_P (decl))
 	    continue;
 	  import_export_decl (decl);
 	  /* If this static data member is needed, provide it to the
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 8c5a1b312fc..a212be8c747 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -24395,8 +24395,7 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p)
     {
       deleted_p = false;
       if (DECL_CLASS_SCOPE_P (code_pattern))
-	pattern_defined = (! DECL_IN_AGGR_P (code_pattern)
-			   || DECL_INLINE_VAR_P (code_pattern));
+	pattern_defined = ! DECL_IN_AGGR_P (code_pattern);
       else
 	pattern_defined = ! DECL_EXTERNAL (code_pattern);
     }
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index af56632c0fc..b265ea05741 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -843,8 +843,7 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
       value = fold_non_dependent_expr (value);
       if (DECL_DECLARED_CONSTEXPR_P (decl)
 	  || (DECL_IN_AGGR_P (decl)
-	      && DECL_INITIALIZED_IN_CLASS_P (decl)
-	      && !DECL_VAR_DECLARED_INLINE_P (decl)))
+	      && DECL_INITIALIZED_IN_CLASS_P (decl)))
 	{
 	  /* Diagnose a non-constant initializer for constexpr variable or
 	     non-inline in-class-initialized static data member.  */
diff --git a/gcc/testsuite/g++.dg/cpp1z/inline-var5.C b/gcc/testsuite/g++.dg/cpp1z/inline-var5.C
new file mode 100644
index 00000000000..27730106f27
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/inline-var5.C
@@ -0,0 +1,16 @@
+// PR c++/87921
+// { dg-do compile { target c++17 } }
+
+template <class H>
+struct X
+{
+  static inline long x[] = { 1L };
+  long foo () { return x[0]; }
+};
+
+void
+bar ()
+{
+  class L {};
+  X<L> v {};
+}
commit 74c41e559636a78b8415467ce2ca7ea0eb641956
Author: Jason Merrill <ja...@redhat.com>
Date:   Tue Feb 19 16:47:22 2019 -1000

            PR c++/87921 - wrong error with inline static data member.
    
    An instantiation of an inline variable isn't defined until it's
    instantiated, so don't treat it as defined just because it's inline.
    
            * decl2.c (c_parse_final_cleanups): Don't force out uninstantiated
            inline static data members.

diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 6a67c4e5b33..1043af49b91 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -4905,7 +4905,10 @@ c_parse_final_cleanups (void)
 	{
 	  if (var_finalized_p (decl) || DECL_REALLY_EXTERN (decl)
 	      /* Don't write it out if we haven't seen a definition.  */
-	      || (DECL_IN_AGGR_P (decl) && !DECL_INLINE_VAR_P (decl)))
+	      || (DECL_IN_AGGR_P (decl) && !DECL_INLINE_VAR_P (decl))
+	      /* Or haven't instantiated it.  */
+	      || (DECL_TEMPLATE_INSTANTIATION (decl)
+		  && !DECL_TEMPLATE_INSTANTIATED (decl)))
 	    continue;
 	  import_export_decl (decl);
 	  /* If this static data member is needed, provide it to the
diff --git a/gcc/testsuite/g++.dg/cpp1z/inline-var5.C b/gcc/testsuite/g++.dg/cpp1z/inline-var5.C
new file mode 100644
index 00000000000..27730106f27
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/inline-var5.C
@@ -0,0 +1,16 @@
+// PR c++/87921
+// { dg-do compile { target c++17 } }
+
+template <class H>
+struct X
+{
+  static inline long x[] = { 1L };
+  long foo () { return x[0]; }
+};
+
+void
+bar ()
+{
+  class L {};
+  X<L> v {};
+}

Reply via email to