diff mbox

PR c++/55663 - constexpr function templ instantiation considered non-const as alias templ arg

Message ID 877gnnwzdg.fsf@redhat.com
State New
Headers show

Commit Message

Dodji Seketeli Jan. 8, 2013, 1:58 p.m. UTC
Hello,

Consider the example of the problem report

     1	template <typename>
     2	constexpr bool the_truth () { return true; }
     3
     4	template <bool>
     5	  struct Takes_bool { };
     6
     7	template<bool B>
     8	  using Alias = Takes_bool<B>;
     9
    10	template<typename T>
    11	  struct test { using type = Alias<the_truth<T>()>; };
    12
    13	int main () {
    14	  test<int> a;
    15
    16	  return 0;
    17	}

that yields the error:

    test.cc: In substitution of ‘template<bool B> using Alias = Takes_bool<B> [with bool B = the_truth<int>()]’:
    test.cc:11:51:   required from ‘struct test<int>’
    test.cc:14:13:   required from here
    test.cc:11:51: error: integral expression ‘the_truth<int>()’ is not constant
       struct test { using type = Alias<the_truth<T>()>; };

I think the issue happens in the course of instantiating test<int> at
line 14, when we look into instantiating Alias<the_truth<T>()> (at
line 11), with T = int.

There, when we check the argument 'the_truth<int>()' to see if it
actually is a constant expression, in check_instantiated_arg, we fail
to recognize its constexpr-ness b/c we just look at its TREE_CONSTANT.

Would the patch below be ok-ish in testing for the const-ness of that
argument in a general enough way that takes into account its
constexpr-ness?

Bootstapped and tested on x86_64-unknown-linux-gnu against trunk.

gcc/cp/

	PR c++/55663
	* cp-tree.h (cxx_is_constant_expression): Declare ...
	* semantics.c (cxx_is_constant_expression): ... new function.
	* pt.c (check_instantiated_arg): Use the new
	cxx_is_constant_expression in lieu of TREE_CONSTANT.

gcc/testsuite/

	PR c++/55663
	* g++.dg/cpp0x/alias-decl-31.C: New test.
---
 gcc/cp/cp-tree.h                           |  1 +
 gcc/cp/pt.c                                |  2 +-
 gcc/cp/semantics.c                         |  9 +++++++++
 gcc/testsuite/g++.dg/cpp0x/alias-decl-31.C | 20 ++++++++++++++++++++
 4 files changed, 31 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/alias-decl-31.C

Comments

Gabriel Dos Reis Jan. 8, 2013, 4:44 p.m. UTC | #1
On Tue, Jan 8, 2013 at 7:58 AM, Dodji Seketeli <dodji@redhat.com> wrote:
> Hello,
>
> Consider the example of the problem report
>
>      1  template <typename>
>      2  constexpr bool the_truth () { return true; }
>      3
>      4  template <bool>
>      5    struct Takes_bool { };
>      6
>      7  template<bool B>
>      8    using Alias = Takes_bool<B>;
>      9
>     10  template<typename T>
>     11    struct test { using type = Alias<the_truth<T>()>; };
>     12
>     13  int main () {
>     14    test<int> a;
>     15
>     16    return 0;
>     17  }
>
> that yields the error:
>
>     test.cc: In substitution of ‘template<bool B> using Alias = Takes_bool<B> [with bool B = the_truth<int>()]’:
>     test.cc:11:51:   required from ‘struct test<int>’
>     test.cc:14:13:   required from here
>     test.cc:11:51: error: integral expression ‘the_truth<int>()’ is not constant
>        struct test { using type = Alias<the_truth<T>()>; };
>
> I think the issue happens in the course of instantiating test<int> at
> line 14, when we look into instantiating Alias<the_truth<T>()> (at
> line 11), with T = int.
>
> There, when we check the argument 'the_truth<int>()' to see if it
> actually is a constant expression, in check_instantiated_arg, we fail
> to recognize its constexpr-ness b/c we just look at its TREE_CONSTANT.

Thanks for the detective work!

We already have various predicates  to test for constant
expressions so I am uneasy to add yet another one.

What we do no need -- which I already suggested -- is a
predicate to test valid non-type template arguments.

For example, we already have the predicate verify_constant
and reduced_constant_expression_p, require_potential_constant_expression.

I think reduced_constant_expression_p is what you want.

-- Gaby
Jason Merrill Jan. 8, 2013, 7:53 p.m. UTC | #2
On 01/08/2013 08:58 AM, Dodji Seketeli wrote:
> There, when we check the argument 'the_truth<int>()' to see if it
> actually is a constant expression, in check_instantiated_arg, we fail
> to recognize its constexpr-ness b/c we just look at its TREE_CONSTANT.

The problem is that by the time we get to check_instantiated_arg, we 
should have folded the expression into something TREE_CONSTANT. 
convert_template_argument should have done that; don't we ever call that 
function for this template argument?

Jason
Dodji Seketeli Jan. 9, 2013, 3:02 p.m. UTC | #3
Jason Merrill <jason@redhat.com> writes:

> On 01/08/2013 08:58 AM, Dodji Seketeli wrote:
>> There, when we check the argument 'the_truth<int>()' to see if it
>> actually is a constant expression, in check_instantiated_arg, we fail
>> to recognize its constexpr-ness b/c we just look at its TREE_CONSTANT.
>
> The problem is that by the time we get to check_instantiated_arg, we
> should have folded the expression into something
> TREE_CONSTANT. convert_template_argument should have done that; don't
> we ever call that function for this template argument?

Presumably, you mean that convert_template_argument should call
convert_nontype_argument to do that folding, right?

I guess the reason why it's not doing it is that the call to
convert_nontype_argument is conditional on

      else if (!uses_template_parms (orig_arg) && !uses_template_parms (t))
	/* We used to call digest_init here.  However, digest_init
	   will report errors, which we don't want when complain
	   is zero.  More importantly, digest_init will try too
	   hard to convert things: for example, `0' should not be
	   converted to pointer type at this point according to
	   the standard.  Accepting this is not merely an
	   extension, since deciding whether or not these
	   conversions can occur is part of determining which
	   function template to call, or whether a given explicit
	   argument specification is valid.  */
	val = convert_nontype_argument (t, orig_arg, complain);

As the argument 'the_truth<T>()' we care about is type dependant,
uses_template_parms returns true and so convert_nontype_argument is
never called.

What is your preferred way want to handle this?
Dodji Seketeli Jan. 9, 2013, 3:30 p.m. UTC | #4
Gabriel Dos Reis <gdr@integrable-solutions.net> writes:

> We already have various predicates  to test for constant
> expressions so I am uneasy to add yet another one.

I understand.  I got lost in the number of existing predicates to test
for constant expressions, to the point that I thought (wrongly) the one
I wanted wasn't present.  :-)

> I think reduced_constant_expression_p is what you want.

Thanks.  I didn't realize this would work because the comment of
initializer_constant_valid_p (that it uses) says:

    We assume that VALUE has been folded as much as possible

On a side node, as Jason said in the thread, we might not even need to
touch anything here, as check_instantiated_arg also assumes that its arg
has been fully folded.  I guess I'll propose to update the comment of
that function to reflect that assumption.

Thanks.
Gabriel Dos Reis Jan. 9, 2013, 4:14 p.m. UTC | #5
On Wed, Jan 9, 2013 at 9:30 AM, Dodji Seketeli <dodji@redhat.com> wrote:
> Gabriel Dos Reis <gdr@integrable-solutions.net> writes:
>
>> We already have various predicates  to test for constant
>> expressions so I am uneasy to add yet another one.
>
> I understand.  I got lost in the number of existing predicates to test
> for constant expressions,

so am I :-)

> to the point that I thought (wrongly) the one I wanted wasn't present.  :-)
>
>> I think reduced_constant_expression_p is what you want.
>
> Thanks.  I didn't realize this would work because the comment of
> initializer_constant_valid_p (that it uses) says:
>
>     We assume that VALUE has been folded as much as possible
>
> On a side node, as Jason said in the thread, we might not even need to
> touch anything here, as check_instantiated_arg also assumes that its arg
> has been fully folded.  I guess I'll propose to update the comment of
> that function to reflect that assumption.

I read your reply.  I am now even more puzzled
than before.  The call to uses_template_parm indicates that
we expect that code to work when are also when processing a template
(e.g. for non-dependent cases inside a template.)
That makes me wonder how it could possibly work for the
cases at hand because for non-type template arguments we need
full instantiation information to determine convertibility and "constant"ness.

-- Gaby
Jason Merrill Jan. 9, 2013, 4:17 p.m. UTC | #6
On 01/09/2013 10:02 AM, Dodji Seketeli wrote:
> As the argument 'the_truth<T>()' we care about is type dependant,
> uses_template_parms returns true and so convert_nontype_argument is
> never called.

Right, but we should call it for the instantiated argument, too.  We do 
that for class templates by calling lookup_template_class again, which 
calls coerce_template_parms.  We need to make sure we call 
coerce_template_parms when instantiating alias templates, too.

Jason
diff mbox

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 810df7d..9d52ba7 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5608,6 +5608,7 @@  extern bool potential_rvalue_constant_expression (tree);
 extern bool require_potential_constant_expression (tree);
 extern bool require_potential_rvalue_constant_expression (tree);
 extern tree cxx_constant_value (tree);
+extern bool cxx_is_constant_expression (tree);
 extern tree maybe_constant_value (tree);
 extern tree maybe_constant_init (tree);
 extern bool is_sub_constant_expr (tree);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 30bafa0..74ccfbf 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -14426,7 +14426,7 @@  check_instantiated_arg (tree tmpl, tree t, tsubst_flags_t complain)
      constant.  */
   else if (TREE_TYPE (t)
 	   && INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (t))
-	   && !TREE_CONSTANT (t))
+	   && !cxx_is_constant_expression (t))
     {
       if (complain & tf_error)
 	error ("integral expression %qE is not constant", t);
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 2e02295..e40d48f 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -8077,6 +8077,15 @@  cxx_constant_value (tree t)
   return cxx_eval_outermost_constant_expr (t, false);
 }
 
+/* Return TRUE iff E is a constant expression.  */
+
+bool
+cxx_is_constant_expression (tree e)
+{
+  tree t = cxx_constant_value (e);
+  return (t != error_mark_node && t != NULL_TREE);
+}
+
 /* If T is a constant expression, returns its reduced value.
    Otherwise, if T does not have TREE_CONSTANT set, returns T.
    Otherwise, returns a version of T without TREE_CONSTANT.  */
diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-31.C b/gcc/testsuite/g++.dg/cpp0x/alias-decl-31.C
new file mode 100644
index 0000000..83eea47
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-31.C
@@ -0,0 +1,20 @@ 
+// Origin: PR c++/55663
+// { dg-do compile { target c++11 } }
+
+template <typename>
+constexpr bool the_truth () { return true; }
+
+template <bool>
+  struct Takes_bool { };
+
+template<bool B>
+  using Alias = Takes_bool<B>;
+
+template<typename T>
+  struct test { using type = Alias<the_truth<T>()>; };
+
+int main () {
+  test<int> a;
+
+  return 0;
+}