This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
C++ PATCH for N4268, constant evaluation of non-type template arguments
- From: Jason Merrill <jason at redhat dot com>
- To: gcc-patches List <gcc-patches at gcc dot gnu dot org>
- Date: Mon, 19 Oct 2015 16:08:53 -1000
- Subject: C++ PATCH for N4268, constant evaluation of non-type template arguments
- Authentication-results: sourceware.org; auth=none
N4268 is the last bullet point remaining for C++1z features until
Saturday, when presumably more stuff will be added. The basic
functionality is quite straightforward: first generalize parsing, then
pass all arguments through maybe_constant_value. But to get correct
handling of pointers to members, we need to prevent constexpr evaluation
from lowering PTRMEM_CST until it's really needed, namely if we're
trying to use one in a .* expression or we're handing off to the
language-independent code.
Tested x86_64-pc-linux-gnu, applying to trunk.
commit 24ba0af22de3694ee1271a053cea04a35b0c71f1
Author: Jason Merrill <jason@redhat.com>
Date: Sun Oct 18 22:57:24 2015 -1000
Implement N4268, Do constant evaluation of all non-type template args.
gcc/c-family/
* c-cppbuiltin.c (c_cpp_builtins): Define
__cpp_nontype_template_args.
gcc/cp/
* parser.c (cp_parser_template_argument): For C++1z just parse a
constant-expression.
* pt.c (convert_nontype_argument): For C++1z always call
maybe_constant_value.
diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
index cd6fd51..c6c3d6b 100644
--- a/gcc/c-family/c-cppbuiltin.c
+++ b/gcc/c-family/c-cppbuiltin.c
@@ -876,6 +876,7 @@ c_cpp_builtins (cpp_reader *pfile)
cpp_define (pfile, "__cpp_enumerator_attributes=201411");
cpp_define (pfile, "__cpp_nested_namespace_definitions=201411");
cpp_define (pfile, "__cpp_fold_expressions=201411");
+ cpp_define (pfile, "__cpp_nontype_template_args=201411");
}
if (flag_concepts)
/* Use a value smaller than the 201507 specified in
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 435757d..f07a5e4 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -14977,8 +14977,12 @@ cp_parser_template_argument (cp_parser* parser)
warn_deprecated_use (argument, NULL_TREE);
return argument;
}
- /* It must be a non-type argument. There permitted cases are given
- in [temp.arg.nontype]:
+ /* It must be a non-type argument. In C++17 any constant-expression is
+ allowed. */
+ if (cxx_dialect > cxx14)
+ goto general_expr;
+
+ /* Otherwise, the permitted cases are given in [temp.arg.nontype]:
-- an integral constant-expression of integral or enumeration
type; or
@@ -15090,6 +15094,7 @@ cp_parser_template_argument (cp_parser* parser)
return error_mark_node;
}
+ general_expr:
/* If the argument wasn't successfully parsed as a type-id followed
by '>>', the argument can only be a constant expression now.
Otherwise, we try parsing the constant-expression tentatively,
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 14a5ddd..142245a 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -6233,6 +6233,17 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
CONSTRUCTOR. */;
else if (INTEGRAL_OR_ENUMERATION_TYPE_P (type))
expr = maybe_constant_value (expr);
+ else if (cxx_dialect >= cxx1z)
+ {
+ if (TREE_CODE (type) != REFERENCE_TYPE)
+ expr = maybe_constant_value (expr);
+ else if (REFERENCE_REF_P (expr))
+ {
+ expr = TREE_OPERAND (expr, 0);
+ expr = maybe_constant_value (expr);
+ expr = convert_from_reference (expr);
+ }
+ }
else if (TYPE_PTR_OR_PTRMEM_P (type))
{
tree folded = maybe_constant_value (expr);
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype1.C b/gcc/testsuite/g++.dg/cpp1z/nontype1.C
new file mode 100644
index 0000000..bb46a99
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype1.C
@@ -0,0 +1,25 @@
+// { dg-options -std=c++1z }
+
+struct S { int m; static int s; } s;
+
+struct T: S { };
+
+template<int* p> class X { };
+template<const int S::*> class M {};
+template<const S&> class SP {};
+
+constexpr int *p = &S::s;
+constexpr int S::*sp = &T::m;
+constexpr S& sr = s;
+constexpr S& sf() { return s; }
+
+// { dg-final { scan-assembler "_Z1g1XIXadL_ZN1S1sEEEE" } }
+void g (X<&s.s>) { }
+// { dg-final { scan-assembler "_Z1f1XIXadL_ZN1S1sEEEE" } }
+void f (X<p>) { }
+// { dg-final { scan-assembler "_Z1f1MIXadL_ZN1S1mEEEE" } }
+void f (M<sp>) {}
+// { dg-final { scan-assembler "_Z1f2SPIL_Z1sEE" } }
+void f (SP<sr>) {}
+// { dg-final { scan-assembler "_Z1g2SPIL_Z1sEE" } }
+void g (SP<sf()>) {}
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype2.C b/gcc/testsuite/g++.dg/cpp1z/nontype2.C
new file mode 100644
index 0000000..e489476
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype2.C
@@ -0,0 +1,18 @@
+// { dg-options -std=c++1z }
+
+#include <typeinfo>
+
+struct S { int m; static int s; } s;
+
+template<int* p> class X { };
+template<const char *s> class Y {};
+template<const std::type_info &> class Z {};
+
+X<&s.m> x7; // { dg-error "" }
+Y<"foo"> y1; // { dg-error "string literal" }
+Z<typeid(p)> z1; // { dg-error "" }
+
+void f()
+{
+ Y<__func__> y; // { dg-error "" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype3.C b/gcc/testsuite/g++.dg/cpp1z/nontype3.C
new file mode 100644
index 0000000..886d7a5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype3.C
@@ -0,0 +1,9 @@
+// { dg-options "-std=c++1z" }
+
+#ifndef __cpp_nontype_template_args
+#error __cpp_nontype_template_args not defined
+#endif
+
+#if __cpp_nontype_template_args != 201411
+#error Wrong value for __cpp_nontype_template_args
+#endif
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype3a.C b/gcc/testsuite/g++.dg/cpp1z/nontype3a.C
new file mode 100644
index 0000000..a253d6b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype3a.C
@@ -0,0 +1,5 @@
+// This macro should not be defined without -std=c++1z.
+
+#ifdef __cpp_nontype_template_args
+#error __cpp_nontype_template_args defined
+#endif
commit fea6597952f6cb45b5751f1b2097a2092cb18b8d
Author: Jason Merrill <jason@redhat.com>
Date: Sun Oct 18 22:57:08 2015 -1000
Expand PTRMEM_CST only when necessary.
* constexpr.c (cxx_eval_constant_expression): Expand PTRMEM_CST
only when necessary.
(cxx_eval_component_reference): Like here.
* decl2.c (lower_var_init): And here.
(c_parse_final_cleanups): Call it.
* decl.c (complete_vars): Not here.
* typeck2.c (digest_init_r): Or here.
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 6619c42..dd27b99 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1873,6 +1873,8 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
tree whole = cxx_eval_constant_expression (ctx, orig_whole,
lval,
non_constant_p, overflow_p);
+ if (TREE_CODE (whole) == PTRMEM_CST)
+ whole = cplus_expand_constant (whole);
if (whole == orig_whole)
return t;
if (lval)
@@ -3139,9 +3141,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
}
if (CONSTANT_CLASS_P (t))
{
- if (TREE_CODE (t) == PTRMEM_CST)
- t = cplus_expand_constant (t);
- else if (TREE_OVERFLOW (t) && (!flag_permissive || ctx->quiet))
+ if (TREE_OVERFLOW (t) && (!flag_permissive || ctx->quiet))
*overflow_p = true;
return t;
}
@@ -3555,7 +3555,11 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
non_constant_p, overflow_p);
if (*non_constant_p)
return t;
- if (POINTER_TYPE_P (TREE_TYPE (t))
+ tree type = TREE_TYPE (t);
+ if (TREE_CODE (op) == PTRMEM_CST
+ && !TYPE_PTRMEM_P (type))
+ op = cplus_expand_constant (op);
+ if (POINTER_TYPE_P (type)
&& TREE_CODE (op) == INTEGER_CST
&& !integer_zerop (op))
{
@@ -3569,7 +3573,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
/* We didn't fold at the top so we could check for ptr-int
conversion. */
return fold (t);
- r = fold_build1 (TREE_CODE (t), TREE_TYPE (t), op);
+ r = fold_build1 (TREE_CODE (t), type, op);
/* Conversion of an out-of-range value has implementation-defined
behavior; the language considers it different from arithmetic
overflow, which is undefined. */
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 5716a13..50bb39c 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -6714,6 +6714,9 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
to put statics on the list so we can deal with the label
address extension. FIXME. */
add_local_decl (cfun, decl);
+ /* And make sure it's in the symbol table for
+ c_parse_final_cleanups to find. */
+ varpool_node::get_create (decl);
}
/* Convert the initializer to the type of DECL, if we have not
@@ -14887,10 +14890,6 @@ complete_vars (tree type)
cp_apply_type_quals_to_decl (cp_type_quals (type), var);
}
- if (DECL_INITIAL (var)
- && decl_constant_var_p (var))
- DECL_INITIAL (var) = cplus_expand_constant (DECL_INITIAL (var));
-
/* Remove this entry from the list. */
incomplete_vars->unordered_remove (ix);
}
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index a5b44e0..3b3817e 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -4484,6 +4484,22 @@ maybe_warn_sized_delete ()
maybe_warn_sized_delete (VEC_DELETE_EXPR);
}
+/* Earlier we left PTRMEM_CST in variable initializers alone so that we could
+ look them up when evaluating non-type template parameters. Now we need to
+ lower them to something the back end can understand. */
+
+static void
+lower_var_init ()
+{
+ varpool_node *node;
+ FOR_EACH_VARIABLE (node)
+ {
+ tree d = node->decl;
+ if (tree init = DECL_INITIAL (d))
+ DECL_INITIAL (d) = cplus_expand_constant (init);
+ }
+}
+
/* This routine is called at the end of compilation.
Its job is to create all the code needed to initialize and
destroy the global aggregates. We do the destruction
@@ -4793,6 +4809,8 @@ c_parse_final_cleanups (void)
}
while (reconsider);
+ lower_var_init ();
+
generate_mangling_aliases ();
/* All used inline functions must have a definition at this point. */
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 2c9143e..e61a57f 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1059,22 +1059,11 @@ digest_init_r (tree type, tree init, bool nested, int flags,
|| BRACE_ENCLOSED_INITIALIZER_P (init))
&& (SCALAR_TYPE_P (type) || code == REFERENCE_TYPE))
{
- tree *exp;
-
if (nested)
flags |= LOOKUP_NO_NARROWING;
init = convert_for_initialization (0, type, init, flags,
ICR_INIT, NULL_TREE, 0,
complain);
- exp = &init;
-
- /* Skip any conversions since we'll be outputting the underlying
- constant. */
- while (CONVERT_EXPR_P (*exp)
- || TREE_CODE (*exp) == NON_LVALUE_EXPR)
- exp = &TREE_OPERAND (*exp, 0);
-
- *exp = cplus_expand_constant (*exp);
return init;
}