diff mbox series

c++: Fix ICE with inline variable in template [PR97975]

Message ID 20201201173820.1908090-1-polacek@redhat.com
State New
Headers show
Series c++: Fix ICE with inline variable in template [PR97975] | expand

Commit Message

Marek Polacek Dec. 1, 2020, 5:38 p.m. UTC
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.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/10?

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                       |  7 ++++---
 gcc/cp/cp-tree.h                         |  2 +-
 gcc/cp/typeck2.c                         |  7 +++++--
 gcc/testsuite/g++.dg/cpp1z/inline-var8.C | 17 +++++++++++++++++
 4 files changed, 27 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1z/inline-var8.C


base-commit: 855bb43f6d0bee5a74b5d3739456ca34b4609a50

Comments

Jason Merrill Dec. 2, 2020, 7:21 p.m. UTC | #1
On 12/1/20 12:38 PM, Marek Polacek wrote:
> 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.
> 
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/10?

OK.

> 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                       |  7 ++++---
>   gcc/cp/cp-tree.h                         |  2 +-
>   gcc/cp/typeck2.c                         |  7 +++++--
>   gcc/testsuite/g++.dg/cpp1z/inline-var8.C | 17 +++++++++++++++++
>   4 files changed, 27 insertions(+), 6 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp1z/inline-var8.C
> 
> diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
> index 054ee524c7a..9a1a1db1267 100644
> --- a/gcc/cp/constexpr.c
> +++ b/gcc/cp/constexpr.c
> @@ -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
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 021de76e142..41516deeafa 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7953,7 +7953,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);
> diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
> index 721987e8502..575c609a365 100644
> --- a/gcc/cp/typeck2.c
> +++ b/gcc/cp/typeck2.c
> @@ -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
> index 00000000000..8db3c19374d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1z/inline-var8.C
> @@ -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;
> +};
> 
> base-commit: 855bb43f6d0bee5a74b5d3739456ca34b4609a50
>
diff mbox series

Patch

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 054ee524c7a..9a1a1db1267 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -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
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 021de76e142..41516deeafa 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7953,7 +7953,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);
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 721987e8502..575c609a365 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -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
index 00000000000..8db3c19374d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/inline-var8.C
@@ -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;
+};