C++ PATCH for c++/90825 - endless recursion when evaluating sizeof

Message ID 20190611015946.GJ5989@redhat.com
State New
Headers show
Series
  • C++ PATCH for c++/90825 - endless recursion when evaluating sizeof
Related show

Commit Message

Marek Polacek June 11, 2019, 1:59 a.m.
This test segvs since r269078, this hunk in particular:

@@ -4581,8 +4713,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       break;

     case SIZEOF_EXPR:
-      r = fold_sizeof_expr (t);
-      VERIFY_CONSTANT (r);
+      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,
+                   non_constant_p, overflow_p,
+                   jump_target);
       break;

In a template, fold_sizeof_expr will just create a new SIZEOF_EXPR, that is the
same, but not identical; see cxx_sizeof_expr.  Then cxx_eval_constant_expression
calls itself on this new expr, then fold_sizeof_expr creates another SIZEOF_EXPR.
Then we call cxx_eval_constant_expression...

The call to cxx_eval_constant_expression is there to keep constexpr-vla1.C
working.  But we can avoid calling it in a template and it'll still work, and
we avoid this nasty recursion.

Bootstrapped/regtested on x86_64-linux, ok for trunk and 9?

2019-06-10  Marek Polacek  <polacek@redhat.com>

	PR c++/90825 - endless recursion when evaluating sizeof.
	* constexpr.c (cxx_eval_constant_expression): Don't recurse on the
	result of fold_sizeof_expr in a template.

	* g++.dg/cpp0x/constexpr-sizeof2.C: New test.

Comments

Jakub Jelinek June 11, 2019, 11:47 a.m. | #1
On Mon, Jun 10, 2019 at 09:59:46PM -0400, Marek Polacek wrote:
> This test segvs since r269078, this hunk in particular:

> 

> @@ -4581,8 +4713,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,

>        break;

> 

>      case SIZEOF_EXPR:

> -      r = fold_sizeof_expr (t);

> -      VERIFY_CONSTANT (r);

> +      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,

> +                   non_constant_p, overflow_p,

> +                   jump_target);

>        break;

> 

> In a template, fold_sizeof_expr will just create a new SIZEOF_EXPR, that is the

> same, but not identical; see cxx_sizeof_expr.  Then cxx_eval_constant_expression


Not always, if it calls cxx_sizeof_expr, it will, but if it calls
cxx_sizeof_or_alignof_type it will only if the type is dependent or VLA.

So, I'd think you should call cxx_eval_constant_expression if TREE_CODE (r)
!= SIZEOF_EXPR, otherwise probably *non_constant_p = true; is in order,
maybe together with gcc_assert (ctx->quiet); ?  I'd hope that if we really
require a constant expression we evaluate it in !processing_template_decl
contexts.

>      case SIZEOF_EXPR:

> -      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,

> -					non_constant_p, overflow_p,

> -					jump_target);

> +      r = fold_sizeof_expr (t);

> +      /* Don't recurse in a template, because then fold_sizeof_expr merely

> +	 creates a new SIZEOF_EXPR, leading to an infinite recursion.  */

> +      if (!processing_template_decl)

> +	r = cxx_eval_constant_expression (ctx, r, lval,

> +					  non_constant_p, overflow_p,

> +					  jump_target);

>        break;


	Jakub
Jason Merrill June 11, 2019, 12:37 p.m. | #2
On 6/11/19 7:47 AM, Jakub Jelinek wrote:
> On Mon, Jun 10, 2019 at 09:59:46PM -0400, Marek Polacek wrote:

>> This test segvs since r269078, this hunk in particular:

>>

>> @@ -4581,8 +4713,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,

>>         break;

>>

>>       case SIZEOF_EXPR:

>> -      r = fold_sizeof_expr (t);

>> -      VERIFY_CONSTANT (r);

>> +      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,

>> +                   non_constant_p, overflow_p,

>> +                   jump_target);

>>         break;

>>

>> In a template, fold_sizeof_expr will just create a new SIZEOF_EXPR, that is the

>> same, but not identical; see cxx_sizeof_expr.  Then cxx_eval_constant_expression

> 

> Not always, if it calls cxx_sizeof_expr, it will, but if it calls

> cxx_sizeof_or_alignof_type it will only if the type is dependent or VLA.

> 

> So, I'd think you should call cxx_eval_constant_expression if TREE_CODE (r)

> != SIZEOF_EXPR, otherwise probably *non_constant_p = true; is in order,

> maybe together with gcc_assert (ctx->quiet); ?  I'd hope that if we really

> require a constant expression we evaluate it in !processing_template_decl

> contexts.


Makes sense.  Also, cxx_sizeof_expr should probably only return a 
SIZEOF_EXPR if the operand is instantiation-dependent.

Jason
Marek Polacek June 11, 2019, 6:28 p.m. | #3
On Tue, Jun 11, 2019 at 08:37:27AM -0400, Jason Merrill wrote:
> On 6/11/19 7:47 AM, Jakub Jelinek wrote:

> > On Mon, Jun 10, 2019 at 09:59:46PM -0400, Marek Polacek wrote:

> > > This test segvs since r269078, this hunk in particular:

> > > 

> > > @@ -4581,8 +4713,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,

> > >         break;

> > > 

> > >       case SIZEOF_EXPR:

> > > -      r = fold_sizeof_expr (t);

> > > -      VERIFY_CONSTANT (r);

> > > +      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,

> > > +                   non_constant_p, overflow_p,

> > > +                   jump_target);

> > >         break;

> > > 

> > > In a template, fold_sizeof_expr will just create a new SIZEOF_EXPR, that is the

> > > same, but not identical; see cxx_sizeof_expr.  Then cxx_eval_constant_expression

> > 

> > Not always, if it calls cxx_sizeof_expr, it will, but if it calls

> > cxx_sizeof_or_alignof_type it will only if the type is dependent or VLA.

> > 

> > So, I'd think you should call cxx_eval_constant_expression if TREE_CODE (r)

> > != SIZEOF_EXPR, otherwise probably *non_constant_p = true; is in order,

> > maybe together with gcc_assert (ctx->quiet); ?  I'd hope that if we really

> > require a constant expression we evaluate it in !processing_template_decl

> > contexts.


Ok, I had been meaning to add the *non_constant_p bit but never did.  :(

> Makes sense.  Also, cxx_sizeof_expr should probably only return a

> SIZEOF_EXPR if the operand is instantiation-dependent.


That results in

  FAIL: g++.dg/template/incomplete6.C  -std=c++98 (internal compiler error)
  FAIL: g++.dg/template/incomplete6.C  -std=c++98 (test for excess errors)
  FAIL: g++.dg/template/overload13.C  -std=c++98 (internal compiler error)
  FAIL: g++.dg/template/overload13.C  -std=c++98 (test for excess errors)

because we trigger an assert in value_dependent_expression_p:

            /* If there are no operands, it must be an expression such
               as "int()". This should not happen for aggregate types
               because it would form non-constant expressions.  */
            gcc_assert (cxx_dialect >= cxx11
                        || INTEGRAL_OR_ENUMERATION_TYPE_P (type));

            return false;

and in this case we have T() where T is a class, and it's in C++98.

It's not needed to fix this PR so perhaps the following could go in,
but is there anything I should do about that?

Bootstrapped/regtested on x86_64-linux, ok for trunk and 9?

2019-06-11  Marek Polacek  <polacek@redhat.com>

	PR c++/90825 - endless recursion when evaluating sizeof.
	* constexpr.c (cxx_eval_constant_expression): Don't recurse on the
	result of fold_sizeof_expr if is returns a SIZEOF_EXPR.

	* g++.dg/cpp0x/constexpr-sizeof2.C: New test.

diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index a2f29694462..443e1c7899f 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -4808,9 +4808,16 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       break;
 
     case SIZEOF_EXPR:
-      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,
-					non_constant_p, overflow_p,
-					jump_target);
+      r = fold_sizeof_expr (t);
+      /* In a template, fold_sizeof_expr may merely create a new SIZEOF_EXPR,
+	 which could lead to an infinite recursion.  */
+      if (TREE_CODE (r) != SIZEOF_EXPR)
+	r = cxx_eval_constant_expression (ctx, r, lval,
+					  non_constant_p, overflow_p,
+					  jump_target);
+      else
+	*non_constant_p = true;
+
       break;
 
     case COMPOUND_EXPR:
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-sizeof2.C gcc/testsuite/g++.dg/cpp0x/constexpr-sizeof2.C
new file mode 100644
index 00000000000..8676ae40b61
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-sizeof2.C
@@ -0,0 +1,14 @@
+// PR c++/90825 - endless recursion when evaluating sizeof.
+// { dg-do compile { target c++11 } }
+
+class address {
+  char host_[63];
+public:
+  static constexpr unsigned buffer_size() noexcept { return sizeof(host_); }
+};
+
+template <class Archive>
+void load()
+{
+  char host[address::buffer_size()];
+}
Jason Merrill June 11, 2019, 7:05 p.m. | #4
On 6/11/19 2:28 PM, Marek Polacek wrote:
> On Tue, Jun 11, 2019 at 08:37:27AM -0400, Jason Merrill wrote:

>> On 6/11/19 7:47 AM, Jakub Jelinek wrote:

>>> On Mon, Jun 10, 2019 at 09:59:46PM -0400, Marek Polacek wrote:

>>>> This test segvs since r269078, this hunk in particular:

>>>>

>>>> @@ -4581,8 +4713,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,

>>>>          break;

>>>>

>>>>        case SIZEOF_EXPR:

>>>> -      r = fold_sizeof_expr (t);

>>>> -      VERIFY_CONSTANT (r);

>>>> +      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,

>>>> +                   non_constant_p, overflow_p,

>>>> +                   jump_target);

>>>>          break;

>>>>

>>>> In a template, fold_sizeof_expr will just create a new SIZEOF_EXPR, that is the

>>>> same, but not identical; see cxx_sizeof_expr.  Then cxx_eval_constant_expression

>>>

>>> Not always, if it calls cxx_sizeof_expr, it will, but if it calls

>>> cxx_sizeof_or_alignof_type it will only if the type is dependent or VLA.

>>>

>>> So, I'd think you should call cxx_eval_constant_expression if TREE_CODE (r)

>>> != SIZEOF_EXPR, otherwise probably *non_constant_p = true; is in order,

>>> maybe together with gcc_assert (ctx->quiet); ?  I'd hope that if we really

>>> require a constant expression we evaluate it in !processing_template_decl

>>> contexts.

> 

> Ok, I had been meaning to add the *non_constant_p bit but never did.  :(

> 

>> Makes sense.  Also, cxx_sizeof_expr should probably only return a

>> SIZEOF_EXPR if the operand is instantiation-dependent.

> 

> That results in

> 

>    FAIL: g++.dg/template/incomplete6.C  -std=c++98 (internal compiler error)

>    FAIL: g++.dg/template/incomplete6.C  -std=c++98 (test for excess errors)

>    FAIL: g++.dg/template/overload13.C  -std=c++98 (internal compiler error)

>    FAIL: g++.dg/template/overload13.C  -std=c++98 (test for excess errors)

> 

> because we trigger an assert in value_dependent_expression_p:

> 

>              /* If there are no operands, it must be an expression such

>                 as "int()". This should not happen for aggregate types

>                 because it would form non-constant expressions.  */

>              gcc_assert (cxx_dialect >= cxx11

>                          || INTEGRAL_OR_ENUMERATION_TYPE_P (type));

> 

>              return false;

> 

> and in this case we have T() where T is a class, and it's in C++98.

> 

> It's not needed to fix this PR so perhaps the following could go in,

> but is there anything I should do about that?


instantiation_dependent_uneval_expression_p shouldn't have that problem.

> Bootstrapped/regtested on x86_64-linux, ok for trunk and 9?

> 

> 2019-06-11  Marek Polacek  <polacek@redhat.com>

> 

> 	PR c++/90825 - endless recursion when evaluating sizeof.

> 	* constexpr.c (cxx_eval_constant_expression): Don't recurse on the

> 	result of fold_sizeof_expr if is returns a SIZEOF_EXPR.

> 

> 	* g++.dg/cpp0x/constexpr-sizeof2.C: New test.

> 

> diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c

> index a2f29694462..443e1c7899f 100644

> --- gcc/cp/constexpr.c

> +++ gcc/cp/constexpr.c

> @@ -4808,9 +4808,16 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,

>         break;

>   

>       case SIZEOF_EXPR:

> -      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,

> -					non_constant_p, overflow_p,

> -					jump_target);

> +      r = fold_sizeof_expr (t);

> +      /* In a template, fold_sizeof_expr may merely create a new SIZEOF_EXPR,

> +	 which could lead to an infinite recursion.  */

> +      if (TREE_CODE (r) != SIZEOF_EXPR)

> +	r = cxx_eval_constant_expression (ctx, r, lval,

> +					  non_constant_p, overflow_p,

> +					  jump_target);

> +      else

> +	*non_constant_p = true;


Let's also add the assert Jakub suggested.

Jason
Marek Polacek June 11, 2019, 7:59 p.m. | #5
On Tue, Jun 11, 2019 at 03:05:26PM -0400, Jason Merrill wrote:
> On 6/11/19 2:28 PM, Marek Polacek wrote:

> > On Tue, Jun 11, 2019 at 08:37:27AM -0400, Jason Merrill wrote:

> > > On 6/11/19 7:47 AM, Jakub Jelinek wrote:

> > > > On Mon, Jun 10, 2019 at 09:59:46PM -0400, Marek Polacek wrote:

> > > > > This test segvs since r269078, this hunk in particular:

> > > > > 

> > > > > @@ -4581,8 +4713,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,

> > > > >          break;

> > > > > 

> > > > >        case SIZEOF_EXPR:

> > > > > -      r = fold_sizeof_expr (t);

> > > > > -      VERIFY_CONSTANT (r);

> > > > > +      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,

> > > > > +                   non_constant_p, overflow_p,

> > > > > +                   jump_target);

> > > > >          break;

> > > > > 

> > > > > In a template, fold_sizeof_expr will just create a new SIZEOF_EXPR, that is the

> > > > > same, but not identical; see cxx_sizeof_expr.  Then cxx_eval_constant_expression

> > > > 

> > > > Not always, if it calls cxx_sizeof_expr, it will, but if it calls

> > > > cxx_sizeof_or_alignof_type it will only if the type is dependent or VLA.

> > > > 

> > > > So, I'd think you should call cxx_eval_constant_expression if TREE_CODE (r)

> > > > != SIZEOF_EXPR, otherwise probably *non_constant_p = true; is in order,

> > > > maybe together with gcc_assert (ctx->quiet); ?  I'd hope that if we really

> > > > require a constant expression we evaluate it in !processing_template_decl

> > > > contexts.

> > 

> > Ok, I had been meaning to add the *non_constant_p bit but never did.  :(

> > 

> > > Makes sense.  Also, cxx_sizeof_expr should probably only return a

> > > SIZEOF_EXPR if the operand is instantiation-dependent.

> > 

> > That results in

> > 

> >    FAIL: g++.dg/template/incomplete6.C  -std=c++98 (internal compiler error)

> >    FAIL: g++.dg/template/incomplete6.C  -std=c++98 (test for excess errors)

> >    FAIL: g++.dg/template/overload13.C  -std=c++98 (internal compiler error)

> >    FAIL: g++.dg/template/overload13.C  -std=c++98 (test for excess errors)

> > 

> > because we trigger an assert in value_dependent_expression_p:

> > 

> >              /* If there are no operands, it must be an expression such

> >                 as "int()". This should not happen for aggregate types

> >                 because it would form non-constant expressions.  */

> >              gcc_assert (cxx_dialect >= cxx11

> >                          || INTEGRAL_OR_ENUMERATION_TYPE_P (type));

> > 

> >              return false;

> > 

> > and in this case we have T() where T is a class, and it's in C++98.

> > 

> > It's not needed to fix this PR so perhaps the following could go in,

> > but is there anything I should do about that?

> 

> instantiation_dependent_uneval_expression_p shouldn't have that problem.


Ah, nice.

> > diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c

> > index a2f29694462..443e1c7899f 100644

> > --- gcc/cp/constexpr.c

> > +++ gcc/cp/constexpr.c

> > @@ -4808,9 +4808,16 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,

> >         break;

> >       case SIZEOF_EXPR:

> > -      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,

> > -					non_constant_p, overflow_p,

> > -					jump_target);

> > +      r = fold_sizeof_expr (t);

> > +      /* In a template, fold_sizeof_expr may merely create a new SIZEOF_EXPR,

> > +	 which could lead to an infinite recursion.  */

> > +      if (TREE_CODE (r) != SIZEOF_EXPR)

> > +	r = cxx_eval_constant_expression (ctx, r, lval,

> > +					  non_constant_p, overflow_p,

> > +					  jump_target);

> > +      else

> > +	*non_constant_p = true;

> 

> Let's also add the assert Jakub suggested.


Done.

Luckily, this also fixed c++/90832 so I'm adding a test for that, too.

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

2019-06-11  Marek Polacek  <polacek@redhat.com>

	PR c++/90825 - endless recursion when evaluating sizeof.
	PR c++/90832 - endless recursion when evaluating sizeof.
	* constexpr.c (cxx_eval_constant_expression): Don't recurse on the
	result of fold_sizeof_expr if is returns a SIZEOF_EXPR.
	* typeck.c (cxx_sizeof_expr): Only return a SIZEOF_EXPR if the operand
	is instantiation-dependent.

	* g++.dg/cpp0x/constexpr-sizeof2.C: New test.
	* g++.dg/cpp0x/constexpr-sizeof3.C: New test.

diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index a2f29694462..80eaffdb33f 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -4808,9 +4808,19 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       break;
 
     case SIZEOF_EXPR:
-      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,
-					non_constant_p, overflow_p,
-					jump_target);
+      r = fold_sizeof_expr (t);
+      /* In a template, fold_sizeof_expr may merely create a new SIZEOF_EXPR,
+	 which could lead to an infinite recursion.  */
+      if (TREE_CODE (r) != SIZEOF_EXPR)
+	r = cxx_eval_constant_expression (ctx, r, lval,
+					  non_constant_p, overflow_p,
+					  jump_target);
+      else
+	{
+	  *non_constant_p = true;
+	  gcc_assert (ctx->quiet);
+	}
+
       break;
 
     case COMPOUND_EXPR:
diff --git gcc/cp/typeck.c gcc/cp/typeck.c
index 154da59627b..a8fb1624b48 100644
--- gcc/cp/typeck.c
+++ gcc/cp/typeck.c
@@ -1690,7 +1690,7 @@ cxx_sizeof_expr (tree e, tsubst_flags_t complain)
   if (e == error_mark_node)
     return error_mark_node;
 
-  if (processing_template_decl)
+  if (instantiation_dependent_uneval_expression_p (e))
     {
       e = build_min (SIZEOF_EXPR, size_type_node, e);
       TREE_SIDE_EFFECTS (e) = 0;
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-sizeof2.C gcc/testsuite/g++.dg/cpp0x/constexpr-sizeof2.C
new file mode 100644
index 00000000000..8676ae40b61
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-sizeof2.C
@@ -0,0 +1,14 @@
+// PR c++/90825 - endless recursion when evaluating sizeof.
+// { dg-do compile { target c++11 } }
+
+class address {
+  char host_[63];
+public:
+  static constexpr unsigned buffer_size() noexcept { return sizeof(host_); }
+};
+
+template <class Archive>
+void load()
+{
+  char host[address::buffer_size()];
+}
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-sizeof3.C gcc/testsuite/g++.dg/cpp0x/constexpr-sizeof3.C
new file mode 100644
index 00000000000..05f07c38796
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-sizeof3.C
@@ -0,0 +1,22 @@
+// PR c++/90832 - endless recursion when evaluating sizeof.
+// { dg-do compile { target c++11 } }
+
+class B
+{
+ template <typename T> friend struct A;
+ B() {}
+};
+
+template <typename T>
+struct A
+{
+ A() noexcept(sizeof(B{})) { }
+};
+
+struct C
+{
+ C()
+ {
+ static_assert( sizeof(A<int>{}), "" );
+ }
+};
Jason Merrill June 12, 2019, 9:35 p.m. | #6
On 6/11/19 3:59 PM, Marek Polacek wrote:
> On Tue, Jun 11, 2019 at 03:05:26PM -0400, Jason Merrill wrote:

>> On 6/11/19 2:28 PM, Marek Polacek wrote:

>>> On Tue, Jun 11, 2019 at 08:37:27AM -0400, Jason Merrill wrote:

>>>> On 6/11/19 7:47 AM, Jakub Jelinek wrote:

>>>>> On Mon, Jun 10, 2019 at 09:59:46PM -0400, Marek Polacek wrote:

>>>>>> This test segvs since r269078, this hunk in particular:

>>>>>>

>>>>>> @@ -4581,8 +4713,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,

>>>>>>           break;

>>>>>>

>>>>>>         case SIZEOF_EXPR:

>>>>>> -      r = fold_sizeof_expr (t);

>>>>>> -      VERIFY_CONSTANT (r);

>>>>>> +      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,

>>>>>> +                   non_constant_p, overflow_p,

>>>>>> +                   jump_target);

>>>>>>           break;

>>>>>>

>>>>>> In a template, fold_sizeof_expr will just create a new SIZEOF_EXPR, that is the

>>>>>> same, but not identical; see cxx_sizeof_expr.  Then cxx_eval_constant_expression

>>>>>

>>>>> Not always, if it calls cxx_sizeof_expr, it will, but if it calls

>>>>> cxx_sizeof_or_alignof_type it will only if the type is dependent or VLA.

>>>>>

>>>>> So, I'd think you should call cxx_eval_constant_expression if TREE_CODE (r)

>>>>> != SIZEOF_EXPR, otherwise probably *non_constant_p = true; is in order,

>>>>> maybe together with gcc_assert (ctx->quiet); ?  I'd hope that if we really

>>>>> require a constant expression we evaluate it in !processing_template_decl

>>>>> contexts.

>>>

>>> Ok, I had been meaning to add the *non_constant_p bit but never did.  :(

>>>

>>>> Makes sense.  Also, cxx_sizeof_expr should probably only return a

>>>> SIZEOF_EXPR if the operand is instantiation-dependent.

>>>

>>> That results in

>>>

>>>     FAIL: g++.dg/template/incomplete6.C  -std=c++98 (internal compiler error)

>>>     FAIL: g++.dg/template/incomplete6.C  -std=c++98 (test for excess errors)

>>>     FAIL: g++.dg/template/overload13.C  -std=c++98 (internal compiler error)

>>>     FAIL: g++.dg/template/overload13.C  -std=c++98 (test for excess errors)

>>>

>>> because we trigger an assert in value_dependent_expression_p:

>>>

>>>               /* If there are no operands, it must be an expression such

>>>                  as "int()". This should not happen for aggregate types

>>>                  because it would form non-constant expressions.  */

>>>               gcc_assert (cxx_dialect >= cxx11

>>>                           || INTEGRAL_OR_ENUMERATION_TYPE_P (type));

>>>

>>>               return false;

>>>

>>> and in this case we have T() where T is a class, and it's in C++98.

>>>

>>> It's not needed to fix this PR so perhaps the following could go in,

>>> but is there anything I should do about that?

>>

>> instantiation_dependent_uneval_expression_p shouldn't have that problem.

> 

> Ah, nice.

> 

>>> diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c

>>> index a2f29694462..443e1c7899f 100644

>>> --- gcc/cp/constexpr.c

>>> +++ gcc/cp/constexpr.c

>>> @@ -4808,9 +4808,16 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,

>>>          break;

>>>        case SIZEOF_EXPR:

>>> -      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,

>>> -					non_constant_p, overflow_p,

>>> -					jump_target);

>>> +      r = fold_sizeof_expr (t);

>>> +      /* In a template, fold_sizeof_expr may merely create a new SIZEOF_EXPR,

>>> +	 which could lead to an infinite recursion.  */

>>> +      if (TREE_CODE (r) != SIZEOF_EXPR)

>>> +	r = cxx_eval_constant_expression (ctx, r, lval,

>>> +					  non_constant_p, overflow_p,

>>> +					  jump_target);

>>> +      else

>>> +	*non_constant_p = true;

>>

>> Let's also add the assert Jakub suggested.

> 

> Done.

> 

> Luckily, this also fixed c++/90832 so I'm adding a test for that, too.

> 

> Bootstrapped/regtested on x86_64-linux, ok for trunk?


OK.

Jason

Patch

diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index a2f29694462..a68aac3b531 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -4808,9 +4808,13 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       break;
 
     case SIZEOF_EXPR:
-      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,
-					non_constant_p, overflow_p,
-					jump_target);
+      r = fold_sizeof_expr (t);
+      /* Don't recurse in a template, because then fold_sizeof_expr merely
+	 creates a new SIZEOF_EXPR, leading to an infinite recursion.  */
+      if (!processing_template_decl)
+	r = cxx_eval_constant_expression (ctx, r, lval,
+					  non_constant_p, overflow_p,
+					  jump_target);
       break;
 
     case COMPOUND_EXPR:
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-sizeof2.C gcc/testsuite/g++.dg/cpp0x/constexpr-sizeof2.C
new file mode 100644
index 00000000000..2d3a169d28c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-sizeof2.C
@@ -0,0 +1,14 @@ 
+// PR c++/90825 - endless recursion when evaluating sizeof.
+// { dg-do compile { target c++11 } }
+
+class address {
+  char host_[63];
+public:
+  static constexpr unsigned buffer_size() noexcept { return sizeof(host_); }
+};
+
+template <class Archive>
+void load()
+{
+  char host[address::buffer_size()];
+}