c++: Fix ICE with inline variable in template [PR97975]
authorMarek Polacek <polacek@redhat.com>
Tue, 1 Dec 2020 15:39:08 +0000 (10:39 -0500)
committerMarek Polacek <polacek@redhat.com>
Wed, 2 Dec 2020 19:36:26 +0000 (14:36 -0500)
In this test, we have

  static inline const int c = b;

in a class template, and we call store_init_value as usual.  There, the
value is

  IMPLICIT_CONV_EXPR<const float>(b)

which is is_nondependent_static_init_expression but isn't
is_nondependent_constant_expression (they only differ in STRICT).
We call fold_non_dependent_expr, but that just returns the expression
because it only instantiates is_nondependent_constant_expression
expressions.  Since we're not checking the initializer of a constexpr
variable, we go on to call maybe_constant_init, whereupon we crash
because it tries to evaluate all is_nondependent_static_init_expression
expressions, which our value is, but it still contains a template code.

I think the fix is to call fold_non_dependent_init instead of
maybe_constant_init, and only call fold_non_dependent_expr on the
"this is a constexpr variable" path so as to avoid instantiating twice
in a row.  Outside a template this should also avoid evaluating the
value twice.

gcc/cp/ChangeLog:

PR c++/97975
* constexpr.c (fold_non_dependent_init): Add a tree parameter.
Use it.
* cp-tree.h (fold_non_dependent_init): Add a tree parameter with
a default value.
* typeck2.c (store_init_value): Call fold_non_dependent_expr
only when checking the initializer for constexpr variables.
Call fold_non_dependent_init instead of maybe_constant_init.

gcc/testsuite/ChangeLog:

PR c++/97975
* g++.dg/cpp1z/inline-var8.C: New test.

gcc/cp/constexpr.c
gcc/cp/cp-tree.h
gcc/cp/typeck2.c
gcc/testsuite/g++.dg/cpp1z/inline-var8.C [new file with mode: 0644]

index 054ee524c7abe05c59786faa29d9a99293e08f28..9a1a1db1267e2c355cdb64ebb1906f12647ada97 100644 (file)
@@ -7271,7 +7271,8 @@ maybe_fold_non_dependent_expr (tree expr,
 tree
 fold_non_dependent_init (tree t,
                         tsubst_flags_t complain /*=tf_warning_or_error*/,
-                        bool manifestly_const_eval /*=false*/)
+                        bool manifestly_const_eval /*=false*/,
+                        tree object /* = NULL_TREE */)
 {
   if (t == NULL_TREE)
     return NULL_TREE;
@@ -7279,7 +7280,7 @@ fold_non_dependent_init (tree t,
   if (processing_template_decl)
     {
       t = fold_non_dependent_expr_template (t, complain,
-                                           manifestly_const_eval, NULL_TREE);
+                                           manifestly_const_eval, object);
       /* maybe_constant_init does this stripping, so do it here too.  */
       if (TREE_CODE (t) == TARGET_EXPR)
        {
@@ -7290,7 +7291,7 @@ fold_non_dependent_init (tree t,
       return t;
     }
 
-  return maybe_constant_init (t, NULL_TREE, manifestly_const_eval);
+  return maybe_constant_init (t, object, manifestly_const_eval);
 }
 
 /* Like maybe_constant_value, but returns a CONSTRUCTOR directly, rather
index 3b67e7ae76aada57cc56d8935f8ffbb50fc3275f..41ae13b3dbdca712fb9067c2a24b8dd6d8ebfb6a 100644 (file)
@@ -8014,7 +8014,7 @@ extern tree maybe_fold_non_dependent_expr (tree,
                                                 tsubst_flags_t = tf_warning_or_error);
 extern tree fold_non_dependent_init            (tree,
                                                 tsubst_flags_t = tf_warning_or_error,
-                                                bool = false);
+                                                bool = false, tree = NULL_TREE);
 extern tree fold_simple                                (tree);
 extern bool reduced_constant_expression_p       (tree);
 extern bool is_instantiation_of_constexpr       (tree);
index 721987e850273a2752bfba3206068efa69bd126c..575c609a365865a1d666464e394c0de82bc8329a 100644 (file)
@@ -744,11 +744,13 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
     {
       bool const_init;
       tree oldval = value;
-      value = fold_non_dependent_expr (value, tf_warning_or_error, true, decl);
       if (DECL_DECLARED_CONSTEXPR_P (decl)
          || (DECL_IN_AGGR_P (decl)
              && DECL_INITIALIZED_IN_CLASS_P (decl)))
        {
+         value = fold_non_dependent_expr (value, tf_warning_or_error,
+                                          /*manifestly_const_eval=*/true,
+                                          decl);
          /* Diagnose a non-constant initializer for constexpr variable or
             non-inline in-class-initialized static data member.  */
          if (!require_constant_expression (value))
@@ -762,7 +764,8 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
            value = cxx_constant_init (value, decl);
        }
       else
-       value = maybe_constant_init (value, decl, true);
+       value = fold_non_dependent_init (value, tf_warning_or_error,
+                                        /*manifestly_const_eval=*/true, decl);
       if (TREE_CODE (value) == CONSTRUCTOR && cp_has_mutable_p (type))
        /* Poison this CONSTRUCTOR so it can't be copied to another
           constexpr variable.  */
diff --git a/gcc/testsuite/g++.dg/cpp1z/inline-var8.C b/gcc/testsuite/g++.dg/cpp1z/inline-var8.C
new file mode 100644 (file)
index 0000000..8db3c19
--- /dev/null
@@ -0,0 +1,17 @@
+// PR c++/97975
+// { dg-do compile { target c++17 } }
+
+template <class>
+class A
+{
+  static const float b;
+  static inline const int c = b;
+};
+
+A<int> a;
+
+struct B
+{
+  static const float b;
+  static inline const int c = b;
+};