c++: value-dep but not type-dep decltype operand [PR105756]

Message ID 20220601182013.2193209-1-ppalka@redhat.com
State New
Headers
Series c++: value-dep but not type-dep decltype operand [PR105756] |

Commit Message

Patrick Palka June 1, 2022, 6:20 p.m. UTC
  r12-7564-gec0f53a3a542e7 made us instantiate non-constant non-dependent
decltype operands by relaxing instantiate_non_dependent_expr to check
instantiation_dependent_uneval_expression_p.  But as the testcase below
demonstrates, this predicate is too permissive here because it allows
value-dependent-only expressions to go through and get instantiated
ahead of time, which causes us to crash during constexpr evaluation of
(5 % N).

This patch strengthens instantiate_non_dependent_expr to use the
non-uneval version of the predicate instead, which does consider value
dependence.  In turn, we need to make finish_decltype_type avoid calling
i_n_d_e on a value-dependent-only expression; I assume we still want to
resolve the decltype ahead of time in this case.  (Doing so seems
unintuitive to me since the expression could be ill-formed at
instantiation time as in the testcase, but it matches the behavior of
Clang and MSVC.)

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk/12?

	PR c++/105756

gcc/cp/ChangeLog:

	* pt.cc (instantiate_non_dependent_expr_internal): Adjust
	comment.
	(instantiate_non_dependent_expr_sfinae): Assert i_d_e_p instead
	of i_d_u_e_p.
	* semantics.cc (finish_decltype_type): Don't instantiate the
	expression when i_d_e_p is true.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/decltype82.C: New test.
---
 gcc/cp/pt.cc                            |  4 ++--
 gcc/cp/semantics.cc                     | 13 ++++++++++++-
 gcc/testsuite/g++.dg/cpp0x/decltype82.C | 10 ++++++++++
 3 files changed, 24 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/decltype82.C
  

Comments

Jason Merrill June 2, 2022, 8:03 p.m. UTC | #1
On 6/1/22 14:20, Patrick Palka wrote:
> r12-7564-gec0f53a3a542e7 made us instantiate non-constant non-dependent
> decltype operands by relaxing instantiate_non_dependent_expr to check
> instantiation_dependent_uneval_expression_p.  But as the testcase below
> demonstrates, this predicate is too permissive here because it allows
> value-dependent-only expressions to go through and get instantiated
> ahead of time, which causes us to crash during constexpr evaluation of
> (5 % N).

Why are we doing constexpr evaluation in unevaluated context?

> This patch strengthens instantiate_non_dependent_expr to use the
> non-uneval version of the predicate instead, which does consider value
> dependence.  In turn, we need to make finish_decltype_type avoid calling
> i_n_d_e on a value-dependent-only expression; I assume we still want to
> resolve the decltype ahead of time in this case.  (Doing so seems
> unintuitive to me since the expression could be ill-formed at
> instantiation time as in the testcase, but it matches the behavior of
> Clang and MSVC.)

I don't think there's any problem with the testcase;  decltype(1/0) is 
well-formed because the expression is not evaluated.

> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> trunk/12?
>
> 	PR c++/105756
> 
> gcc/cp/ChangeLog:
> 
> 	* pt.cc (instantiate_non_dependent_expr_internal): Adjust
> 	comment.
> 	(instantiate_non_dependent_expr_sfinae): Assert i_d_e_p instead
> 	of i_d_u_e_p.
> 	* semantics.cc (finish_decltype_type): Don't instantiate the
> 	expression when i_d_e_p is true.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp0x/decltype82.C: New test.
> ---
>   gcc/cp/pt.cc                            |  4 ++--
>   gcc/cp/semantics.cc                     | 13 ++++++++++++-
>   gcc/testsuite/g++.dg/cpp0x/decltype82.C | 10 ++++++++++
>   3 files changed, 24 insertions(+), 3 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp0x/decltype82.C
> 
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index e4a473002a0..1ea2545e115 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -6372,7 +6372,7 @@ redeclare_class_template (tree type, tree parms, tree cons)
>   
>   /* The actual substitution part of instantiate_non_dependent_expr_sfinae,
>      to be used when the caller has already checked
> -    !instantiation_dependent_uneval_expression_p (expr)
> +    !instantiation_dependent_expression_p (expr)
>      and cleared processing_template_decl.  */
>   
>   tree
> @@ -6397,7 +6397,7 @@ instantiate_non_dependent_expr_sfinae (tree expr, tsubst_flags_t complain)
>     if (processing_template_decl)
>       {
>         /* The caller should have checked this already.  */
> -      gcc_checking_assert (!instantiation_dependent_uneval_expression_p (expr));
> +      gcc_checking_assert (!instantiation_dependent_expression_p (expr));
>         processing_template_decl_sentinel s;
>         expr = instantiate_non_dependent_expr_internal (expr, complain);
>       }
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 3600d270ff8..b23848ab94c 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -11302,9 +11302,20 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
>   
>         return type;
>       }
> +  else if (processing_template_decl
> +	   && potential_constant_expression (expr)
> +	   && value_dependent_expression_p (expr))
> +    /* The above test is equivalent to instantiation_dependent_expression_p
> +       after instantiation_dependent_uneval_expression_p has been ruled out.
> +       In this case the expression is dependent but not type-dependent, so
> +       we can resolve the decltype ahead of time but we can't instantiate
> +       the expression.  */;
>     else if (processing_template_decl)
>       {
> -      expr = instantiate_non_dependent_expr_sfinae (expr, complain|tf_decltype);
> +      /* The expression isn't instantiation dependent, so we can fully
> +	 instantiate it ahead of time.  */
> +      expr = instantiate_non_dependent_expr_sfinae (expr,
> +						    complain|tf_decltype);
>         if (expr == error_mark_node)
>   	return error_mark_node;
>         /* Keep processing_template_decl cleared for the rest of the function
> diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype82.C b/gcc/testsuite/g++.dg/cpp0x/decltype82.C
> new file mode 100644
> index 00000000000..915e5e37675
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/decltype82.C
> @@ -0,0 +1,10 @@
> +// PR c++/105756
> +// { dg-do compile { target c++11 } }
> +
> +template<int N>
> +void f() {
> +  using ty1 = decltype((5 % N) == 0);
> +  using ty2 = decltype((5 / N) == 0);
> +}
> +
> +template void f<0>();
  
Patrick Palka June 2, 2022, 8:29 p.m. UTC | #2
On Thu, 2 Jun 2022, Jason Merrill wrote:

> On 6/1/22 14:20, Patrick Palka wrote:
> > r12-7564-gec0f53a3a542e7 made us instantiate non-constant non-dependent
> > decltype operands by relaxing instantiate_non_dependent_expr to check
> > instantiation_dependent_uneval_expression_p.  But as the testcase below
> > demonstrates, this predicate is too permissive here because it allows
> > value-dependent-only expressions to go through and get instantiated
> > ahead of time, which causes us to crash during constexpr evaluation of
> > (5 % N).
> 
> Why are we doing constexpr evaluation in unevaluated context?

Looks like because cp_build_binary_op attempts to fold the resulting
operator expression via cp_fully_fold (which performs speculate
constexpr evaluation):

6261  if (!processing_template_decl)
6262    {
6263      if (resultcode == SPACESHIP_EXPR)
6264        result = get_target_expr (result, complain);
6265      op0 = cp_fully_fold (op0);
6266      /* Only consider the second argument if the first isn't overflowed.  */
6267      if (!CONSTANT_CLASS_P (op0) || TREE_OVERFLOW_P (op0))
6268        return result;
6269      op1 = cp_fully_fold (op1);
6270      if (!CONSTANT_CLASS_P (op1) || TREE_OVERFLOW_P (op1))
6271        return result;
6272    }

But in an unevaluated context I suppose we don't need or want to do this
folding.  I'll work on a patch to that effect.

> 
> > This patch strengthens instantiate_non_dependent_expr to use the
> > non-uneval version of the predicate instead, which does consider value
> > dependence.  In turn, we need to make finish_decltype_type avoid calling
> > i_n_d_e on a value-dependent-only expression; I assume we still want to
> > resolve the decltype ahead of time in this case.  (Doing so seems
> > unintuitive to me since the expression could be ill-formed at
> > instantiation time as in the testcase, but it matches the behavior of
> > Clang and MSVC.)
> 
> I don't think there's any problem with the testcase;  decltype(1/0) is
> well-formed because the expression is not evaluated.

D'oh, that makes sense, not sure what I was on about :)

> 
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> > trunk/12?
> > 
> > 	PR c++/105756
> > 
> > gcc/cp/ChangeLog:
> > 
> > 	* pt.cc (instantiate_non_dependent_expr_internal): Adjust
> > 	comment.
> > 	(instantiate_non_dependent_expr_sfinae): Assert i_d_e_p instead
> > 	of i_d_u_e_p.
> > 	* semantics.cc (finish_decltype_type): Don't instantiate the
> > 	expression when i_d_e_p is true.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> > 	* g++.dg/cpp0x/decltype82.C: New test.
> > ---
> >   gcc/cp/pt.cc                            |  4 ++--
> >   gcc/cp/semantics.cc                     | 13 ++++++++++++-
> >   gcc/testsuite/g++.dg/cpp0x/decltype82.C | 10 ++++++++++
> >   3 files changed, 24 insertions(+), 3 deletions(-)
> >   create mode 100644 gcc/testsuite/g++.dg/cpp0x/decltype82.C
> > 
> > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > index e4a473002a0..1ea2545e115 100644
> > --- a/gcc/cp/pt.cc
> > +++ b/gcc/cp/pt.cc
> > @@ -6372,7 +6372,7 @@ redeclare_class_template (tree type, tree parms, tree
> > cons)
> >     /* The actual substitution part of
> > instantiate_non_dependent_expr_sfinae,
> >      to be used when the caller has already checked
> > -    !instantiation_dependent_uneval_expression_p (expr)
> > +    !instantiation_dependent_expression_p (expr)
> >      and cleared processing_template_decl.  */
> >     tree
> > @@ -6397,7 +6397,7 @@ instantiate_non_dependent_expr_sfinae (tree expr,
> > tsubst_flags_t complain)
> >     if (processing_template_decl)
> >       {
> >         /* The caller should have checked this already.  */
> > -      gcc_checking_assert (!instantiation_dependent_uneval_expression_p
> > (expr));
> > +      gcc_checking_assert (!instantiation_dependent_expression_p (expr));
> >         processing_template_decl_sentinel s;
> >         expr = instantiate_non_dependent_expr_internal (expr, complain);
> >       }
> > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> > index 3600d270ff8..b23848ab94c 100644
> > --- a/gcc/cp/semantics.cc
> > +++ b/gcc/cp/semantics.cc
> > @@ -11302,9 +11302,20 @@ finish_decltype_type (tree expr, bool
> > id_expression_or_member_access_p,
> >           return type;
> >       }
> > +  else if (processing_template_decl
> > +	   && potential_constant_expression (expr)
> > +	   && value_dependent_expression_p (expr))
> > +    /* The above test is equivalent to instantiation_dependent_expression_p
> > +       after instantiation_dependent_uneval_expression_p has been ruled
> > out.
> > +       In this case the expression is dependent but not type-dependent, so
> > +       we can resolve the decltype ahead of time but we can't instantiate
> > +       the expression.  */;
> >     else if (processing_template_decl)
> >       {
> > -      expr = instantiate_non_dependent_expr_sfinae (expr,
> > complain|tf_decltype);
> > +      /* The expression isn't instantiation dependent, so we can fully
> > +	 instantiate it ahead of time.  */
> > +      expr = instantiate_non_dependent_expr_sfinae (expr,
> > +						    complain|tf_decltype);
> >         if (expr == error_mark_node)
> >   	return error_mark_node;
> >         /* Keep processing_template_decl cleared for the rest of the
> > function
> > diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype82.C
> > b/gcc/testsuite/g++.dg/cpp0x/decltype82.C
> > new file mode 100644
> > index 00000000000..915e5e37675
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/cpp0x/decltype82.C
> > @@ -0,0 +1,10 @@
> > +// PR c++/105756
> > +// { dg-do compile { target c++11 } }
> > +
> > +template<int N>
> > +void f() {
> > +  using ty1 = decltype((5 % N) == 0);
> > +  using ty2 = decltype((5 / N) == 0);
> > +}
> > +
> > +template void f<0>();
> 
>
  
Patrick Palka June 3, 2022, 6:13 p.m. UTC | #3
On Thu, 2 Jun 2022, Patrick Palka wrote:

> On Thu, 2 Jun 2022, Jason Merrill wrote:
> 
> > On 6/1/22 14:20, Patrick Palka wrote:
> > > r12-7564-gec0f53a3a542e7 made us instantiate non-constant non-dependent
> > > decltype operands by relaxing instantiate_non_dependent_expr to check
> > > instantiation_dependent_uneval_expression_p.  But as the testcase below
> > > demonstrates, this predicate is too permissive here because it allows
> > > value-dependent-only expressions to go through and get instantiated
> > > ahead of time, which causes us to crash during constexpr evaluation of
> > > (5 % N).
> > 
> > Why are we doing constexpr evaluation in unevaluated context?
> 
> Looks like because cp_build_binary_op attempts to fold the resulting
> operator expression via cp_fully_fold (which performs speculate
> constexpr evaluation):
> 
> 6261  if (!processing_template_decl)
> 6262    {
> 6263      if (resultcode == SPACESHIP_EXPR)
> 6264        result = get_target_expr (result, complain);
> 6265      op0 = cp_fully_fold (op0);
> 6266      /* Only consider the second argument if the first isn't overflowed.  */
> 6267      if (!CONSTANT_CLASS_P (op0) || TREE_OVERFLOW_P (op0))
> 6268        return result;
> 6269      op1 = cp_fully_fold (op1);
> 6270      if (!CONSTANT_CLASS_P (op1) || TREE_OVERFLOW_P (op1))
> 6271        return result;
> 6272    }
> 
> But in an unevaluated context I suppose we don't need or want to do this
> folding.  I'll work on a patch to that effect.

Here it is:

-- >8 --
Subject: [PATCH] c++: value-dep but not type-dep decltype operand [PR105756]

Here we're crashing when instantiating ahead of time the value-dependent
but not type-dependent decltype operand (5 % N) == 0, ultimately
because cp_build_binary_op folds its operands for sake of its overflow
diagnostics, and as part of this folding it performs speculative
constexpr evaluation for the operand (5 % N) during which we crash since
it's value-dependent.

Since the operand folding performed by cp_build_binary_op appears to
be solely for sake of diagnosing overflow, and since these diagnostics
are suppressed when in an unevaluated context, this patch fixes this
crash by suppressing cp_build_binary_op's operand folding accordingly.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk/12?  The diff was generated with -w to suppress noisy whitespace
changes.

	PR c++/105756

gcc/cp/ChangeLog:

	* typeck.cc (cp_build_binary_op): Don't fold operands
	when c_inhibit_evaluation_warnings.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/decltype82.C: New test.
---
 gcc/cp/typeck.cc                        |  8 ++++++--
 gcc/testsuite/g++.dg/cpp0x/decltype82.C | 10 ++++++++++
 2 files changed, 16 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/decltype82.C

diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 190d710cd27..7c910d57b7e 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -6263,10 +6263,13 @@ cp_build_binary_op (const op_location_t &location,
     result = build2 (COMPOUND_EXPR, TREE_TYPE (result),
 		     instrument_expr, result);
 
+  if (resultcode == SPACESHIP_EXPR && !processing_template_decl)
+    result = get_target_expr_sfinae (result, complain);
+
+  if (!c_inhibit_evaluation_warnings)
+    {
       if (!processing_template_decl)
 	{
-      if (resultcode == SPACESHIP_EXPR)
-	result = get_target_expr_sfinae (result, complain);
 	  op0 = cp_fully_fold (op0);
 	  /* Only consider the second argument if the first isn't overflowed.  */
 	  if (!CONSTANT_CLASS_P (op0) || TREE_OVERFLOW_P (op0))
@@ -6282,6 +6285,7 @@ cp_build_binary_op (const op_location_t &location,
       result_ovl = fold_build2 (resultcode, build_type, op0, op1);
       if (TREE_OVERFLOW_P (result_ovl))
 	overflow_warning (location, result_ovl);
+    }
 
   return result;
 }
diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype82.C b/gcc/testsuite/g++.dg/cpp0x/decltype82.C
new file mode 100644
index 00000000000..915e5e37675
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/decltype82.C
@@ -0,0 +1,10 @@
+// PR c++/105756
+// { dg-do compile { target c++11 } }
+
+template<int N>
+void f() {
+  using ty1 = decltype((5 % N) == 0);
+  using ty2 = decltype((5 / N) == 0);
+}
+
+template void f<0>();
  
Jason Merrill June 3, 2022, 6:18 p.m. UTC | #4
On 6/3/22 14:13, Patrick Palka wrote:
> On Thu, 2 Jun 2022, Patrick Palka wrote:
> 
>> On Thu, 2 Jun 2022, Jason Merrill wrote:
>>
>>> On 6/1/22 14:20, Patrick Palka wrote:
>>>> r12-7564-gec0f53a3a542e7 made us instantiate non-constant non-dependent
>>>> decltype operands by relaxing instantiate_non_dependent_expr to check
>>>> instantiation_dependent_uneval_expression_p.  But as the testcase below
>>>> demonstrates, this predicate is too permissive here because it allows
>>>> value-dependent-only expressions to go through and get instantiated
>>>> ahead of time, which causes us to crash during constexpr evaluation of
>>>> (5 % N).
>>>
>>> Why are we doing constexpr evaluation in unevaluated context?
>>
>> Looks like because cp_build_binary_op attempts to fold the resulting
>> operator expression via cp_fully_fold (which performs speculate
>> constexpr evaluation):
>>
>> 6261  if (!processing_template_decl)
>> 6262    {
>> 6263      if (resultcode == SPACESHIP_EXPR)
>> 6264        result = get_target_expr (result, complain);
>> 6265      op0 = cp_fully_fold (op0);
>> 6266      /* Only consider the second argument if the first isn't overflowed.  */
>> 6267      if (!CONSTANT_CLASS_P (op0) || TREE_OVERFLOW_P (op0))
>> 6268        return result;
>> 6269      op1 = cp_fully_fold (op1);
>> 6270      if (!CONSTANT_CLASS_P (op1) || TREE_OVERFLOW_P (op1))
>> 6271        return result;
>> 6272    }
>>
>> But in an unevaluated context I suppose we don't need or want to do this
>> folding.  I'll work on a patch to that effect.
> 
> Here it is:
> 
> -- >8 --
> Subject: [PATCH] c++: value-dep but not type-dep decltype operand [PR105756]
> 
> Here we're crashing when instantiating ahead of time the value-dependent
> but not type-dependent decltype operand (5 % N) == 0, ultimately
> because cp_build_binary_op folds its operands for sake of its overflow
> diagnostics, and as part of this folding it performs speculative
> constexpr evaluation for the operand (5 % N) during which we crash since
> it's value-dependent.
> 
> Since the operand folding performed by cp_build_binary_op appears to
> be solely for sake of diagnosing overflow, and since these diagnostics
> are suppressed when in an unevaluated context, this patch fixes this
> crash by suppressing cp_build_binary_op's operand folding accordingly.
> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> trunk/12?  The diff was generated with -w to suppress noisy whitespace
> changes.

OK.

> 	PR c++/105756
> 
> gcc/cp/ChangeLog:
> 
> 	* typeck.cc (cp_build_binary_op): Don't fold operands
> 	when c_inhibit_evaluation_warnings.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp0x/decltype82.C: New test.
> ---
>   gcc/cp/typeck.cc                        |  8 ++++++--
>   gcc/testsuite/g++.dg/cpp0x/decltype82.C | 10 ++++++++++
>   2 files changed, 16 insertions(+), 2 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp0x/decltype82.C
> 
> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> index 190d710cd27..7c910d57b7e 100644
> --- a/gcc/cp/typeck.cc
> +++ b/gcc/cp/typeck.cc
> @@ -6263,10 +6263,13 @@ cp_build_binary_op (const op_location_t &location,
>       result = build2 (COMPOUND_EXPR, TREE_TYPE (result),
>   		     instrument_expr, result);
>   
> +  if (resultcode == SPACESHIP_EXPR && !processing_template_decl)
> +    result = get_target_expr_sfinae (result, complain);
> +
> +  if (!c_inhibit_evaluation_warnings)
> +    {
>         if (!processing_template_decl)
>   	{
> -      if (resultcode == SPACESHIP_EXPR)
> -	result = get_target_expr_sfinae (result, complain);
>   	  op0 = cp_fully_fold (op0);
>   	  /* Only consider the second argument if the first isn't overflowed.  */
>   	  if (!CONSTANT_CLASS_P (op0) || TREE_OVERFLOW_P (op0))
> @@ -6282,6 +6285,7 @@ cp_build_binary_op (const op_location_t &location,
>         result_ovl = fold_build2 (resultcode, build_type, op0, op1);
>         if (TREE_OVERFLOW_P (result_ovl))
>   	overflow_warning (location, result_ovl);
> +    }
>   
>     return result;
>   }
> diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype82.C b/gcc/testsuite/g++.dg/cpp0x/decltype82.C
> new file mode 100644
> index 00000000000..915e5e37675
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/decltype82.C
> @@ -0,0 +1,10 @@
> +// PR c++/105756
> +// { dg-do compile { target c++11 } }
> +
> +template<int N>
> +void f() {
> +  using ty1 = decltype((5 % N) == 0);
> +  using ty2 = decltype((5 / N) == 0);
> +}
> +
> +template void f<0>();
  

Patch

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index e4a473002a0..1ea2545e115 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -6372,7 +6372,7 @@  redeclare_class_template (tree type, tree parms, tree cons)
 
 /* The actual substitution part of instantiate_non_dependent_expr_sfinae,
    to be used when the caller has already checked
-    !instantiation_dependent_uneval_expression_p (expr)
+    !instantiation_dependent_expression_p (expr)
    and cleared processing_template_decl.  */
 
 tree
@@ -6397,7 +6397,7 @@  instantiate_non_dependent_expr_sfinae (tree expr, tsubst_flags_t complain)
   if (processing_template_decl)
     {
       /* The caller should have checked this already.  */
-      gcc_checking_assert (!instantiation_dependent_uneval_expression_p (expr));
+      gcc_checking_assert (!instantiation_dependent_expression_p (expr));
       processing_template_decl_sentinel s;
       expr = instantiate_non_dependent_expr_internal (expr, complain);
     }
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 3600d270ff8..b23848ab94c 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -11302,9 +11302,20 @@  finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
 
       return type;
     }
+  else if (processing_template_decl
+	   && potential_constant_expression (expr)
+	   && value_dependent_expression_p (expr))
+    /* The above test is equivalent to instantiation_dependent_expression_p
+       after instantiation_dependent_uneval_expression_p has been ruled out.
+       In this case the expression is dependent but not type-dependent, so
+       we can resolve the decltype ahead of time but we can't instantiate
+       the expression.  */;
   else if (processing_template_decl)
     {
-      expr = instantiate_non_dependent_expr_sfinae (expr, complain|tf_decltype);
+      /* The expression isn't instantiation dependent, so we can fully
+	 instantiate it ahead of time.  */
+      expr = instantiate_non_dependent_expr_sfinae (expr,
+						    complain|tf_decltype);
       if (expr == error_mark_node)
 	return error_mark_node;
       /* Keep processing_template_decl cleared for the rest of the function
diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype82.C b/gcc/testsuite/g++.dg/cpp0x/decltype82.C
new file mode 100644
index 00000000000..915e5e37675
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/decltype82.C
@@ -0,0 +1,10 @@ 
+// PR c++/105756
+// { dg-do compile { target c++11 } }
+
+template<int N>
+void f() {
+  using ty1 = decltype((5 % N) == 0);
+  using ty2 = decltype((5 / N) == 0);
+}
+
+template void f<0>();