Patchwork C++ PATCH for c++/49855, c++/49896 (ICE with named constants in templates)

login
register
mail settings
Submitter Jason Merrill
Date Oct. 11, 2011, 5:50 p.m.
Message ID <4E9481F3.4030708@redhat.com>
Download mbox | patch
Permalink /patch/119035/
State New
Headers show

Comments

Jason Merrill - Oct. 11, 2011, 5:50 p.m.
The problem in both of these PRs is that G++ has assumed that we don't 
ever need to actually perform non-dependent conversions in a template; 
once we know that the conversion can be performed, we just generated a 
NOP_EXPR to change the type of the expression.  But this isn't good 
enough for initializers for variables that might be used in constant 
expressions, since we want to reduce them to constants if possible.

For conversions between scalar types, we can just go ahead and perform 
the conversion; this is a bit of a boundary violation, but should be 
harmless since such conversions are represented by a single tree code 
that we can just pass along at tsubst time.  This is also necessary to 
avoid breaking checking of non-dependent OpenMP for loops in 
c_finish_omp_for.

For C++11 constexpr conversions, it's more involved, so I've introduced 
a new tree code IMPLICIT_CONV_EXPR.

Tested x86_64-pc-linux-gnu, applying to trunk.
Jason Merrill - Oct. 11, 2011, 6:19 p.m.
On 10/11/2011 02:19 PM, Jason Merrill wrote:
> For the 4.6 branch I'm only making the change for scalars.
>
> Tested x86_64-pc-linux-gnu, applying to trunk.

Er, to 4.6.

Patch

commit 6800169668342f9009a1001bddff8b1400245836
Author: Jason Merrill <jason@redhat.com>
Date:   Sun Oct 9 23:27:52 2011 +0100

    	PR c++/49855
    	PR c++/49896
    	* cp-tree.def (IMPLICIT_CONV_EXPR): New.
    	* call.c (perform_implicit_conversion_flags): Build it
    	instead of NOP_EXPR.
    	* cp-objcp-common.c (cp_common_init_ts): It's typed.
    	* cxx-pretty-print.c (pp_cxx_cast_expression): Handle it.
    	(pp_cxx_expression): Likewise.
    	* error.c (dump_expr): Likewise.
    	* semantics.c (potential_constant_expression_1): Likewise.
    	* tree.c (cp_tree_equal): Likewise.
    	(cp_walk_subtrees): Likewise.
    	* pt.c (iterative_hash_template_arg): Likewise.
    	(for_each_template_parm_r): Likewise.
    	(type_dependent_expression_p): Likewise.
    	(tsubst_copy, tsubst_copy_and_build): Handle IMPLICIT_CONV_EXPR
    	and CONVERT_EXPR.
    	* cp-tree.h (IMPLICIT_CONV_EXPR_DIRECT_INIT): New.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 4c03e76..7219afe 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8397,13 +8397,19 @@  perform_implicit_conversion_flags (tree type, tree expr, tsubst_flags_t complain
 	}
       expr = error_mark_node;
     }
-  else if (processing_template_decl)
+  else if (processing_template_decl
+	   /* As a kludge, we always perform conversions between scalar
+	      types, as IMPLICIT_CONV_EXPR confuses c_finish_omp_for.  */
+	   && !(SCALAR_TYPE_P (type) && SCALAR_TYPE_P (TREE_TYPE (expr))))
     {
       /* In a template, we are only concerned about determining the
 	 type of non-dependent expressions, so we do not have to
-	 perform the actual conversion.  */
-      if (TREE_TYPE (expr) != type)
-	expr = build_nop (type, expr);
+	 perform the actual conversion.  But for initializers, we
+	 need to be able to perform it at instantiation
+	 (or fold_non_dependent_expr) time.  */
+      expr = build1 (IMPLICIT_CONV_EXPR, type, expr);
+      if (!(flags & LOOKUP_ONLYCONVERTING))
+	IMPLICIT_CONV_EXPR_DIRECT_INIT (expr) = true;
     }
   else
     expr = convert_like (conv, expr, complain);
diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c
index 1866b81..035fdcd 100644
--- a/gcc/cp/cp-objcp-common.c
+++ b/gcc/cp/cp-objcp-common.c
@@ -267,6 +267,7 @@  cp_common_init_ts (void)
   MARK_TS_TYPED (CONST_CAST_EXPR);
   MARK_TS_TYPED (STATIC_CAST_EXPR);
   MARK_TS_TYPED (DYNAMIC_CAST_EXPR);
+  MARK_TS_TYPED (IMPLICIT_CONV_EXPR);
   MARK_TS_TYPED (TEMPLATE_ID_EXPR);
   MARK_TS_TYPED (ARROW_EXPR);
   MARK_TS_TYPED (SIZEOF_EXPR);
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index bb1b753..be29870 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -250,6 +250,7 @@  DEFTREECODE (REINTERPRET_CAST_EXPR, "reinterpret_cast_expr", tcc_unary, 1)
 DEFTREECODE (CONST_CAST_EXPR, "const_cast_expr", tcc_unary, 1)
 DEFTREECODE (STATIC_CAST_EXPR, "static_cast_expr", tcc_unary, 1)
 DEFTREECODE (DYNAMIC_CAST_EXPR, "dynamic_cast_expr", tcc_unary, 1)
+DEFTREECODE (IMPLICIT_CONV_EXPR, "implicit_conv_expr", tcc_unary, 1)
 DEFTREECODE (DOTSTAR_EXPR, "dotstar_expr", tcc_expression, 2)
 DEFTREECODE (TYPEID_EXPR, "typeid_expr", tcc_expression, 1)
 DEFTREECODE (NOEXCEPT_EXPR, "noexcept_expr", tcc_unary, 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index f824f38..b53accf 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -72,6 +72,7 @@  c-common.h, not after.
       DECLTYPE_FOR_LAMBDA_CAPTURE (in DECLTYPE_TYPE)
       VEC_INIT_EXPR_IS_CONSTEXPR (in VEC_INIT_EXPR)
       DECL_OVERRIDE_P (in FUNCTION_DECL)
+      IMPLICIT_CONV_EXPR_DIRECT_INIT (in IMPLICIT_CONV_EXPR)
    1: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
       TI_PENDING_TEMPLATE_FLAG.
       TEMPLATE_PARMS_FOR_INLINE.
@@ -3233,6 +3234,11 @@  more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
    B b{1,2}, not B b({1,2}) or B b = {1,2}.  */
 #define CONSTRUCTOR_IS_DIRECT_INIT(NODE) (TREE_LANG_FLAG_0 (CONSTRUCTOR_CHECK (NODE)))
 
+/* True if NODE represents a conversion for direct-initialization in a
+   template.  Set by perform_implicit_conversion_flags.  */
+#define IMPLICIT_CONV_EXPR_DIRECT_INIT(NODE) \
+  (TREE_LANG_FLAG_0 (IMPLICIT_CONV_EXPR_CHECK (NODE)))
+
 /* Nonzero means that an object of this type can not be initialized using
    an initializer list.  */
 #define CLASSTYPE_NON_AGGREGATE(NODE) \
diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c
index c5f1ac0..708afc8 100644
--- a/gcc/cp/cxx-pretty-print.c
+++ b/gcc/cp/cxx-pretty-print.c
@@ -830,6 +830,7 @@  pp_cxx_cast_expression (cxx_pretty_printer *pp, tree t)
   switch (TREE_CODE (t))
     {
     case CAST_EXPR:
+    case IMPLICIT_CONV_EXPR:
       pp_cxx_type_id (pp, TREE_TYPE (t));
       pp_cxx_call_argument_list (pp, TREE_OPERAND (t, 0));
       break;
@@ -1084,6 +1085,7 @@  pp_cxx_expression (cxx_pretty_printer *pp, tree t)
       break;
 
     case CAST_EXPR:
+    case IMPLICIT_CONV_EXPR:
       pp_cxx_cast_expression (pp, t);
       break;
 
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 4d12a0d..7d345c9 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -2052,6 +2052,7 @@  dump_expr (tree t, int flags)
       break;
 
     CASE_CONVERT:
+    case IMPLICIT_CONV_EXPR:
     case VIEW_CONVERT_EXPR:
       {
 	tree op = TREE_OPERAND (t, 0);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index c95f1cb..43c4185 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -1591,6 +1591,7 @@  iterative_hash_template_arg (tree arg, hashval_t val)
       return val;
 
     case CAST_EXPR:
+    case IMPLICIT_CONV_EXPR:
     case STATIC_CAST_EXPR:
     case REINTERPRET_CAST_EXPR:
     case CONST_CAST_EXPR:
@@ -7702,6 +7703,7 @@  for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d)
 
     case MODOP_EXPR:
     case CAST_EXPR:
+    case IMPLICIT_CONV_EXPR:
     case REINTERPRET_CAST_EXPR:
     case CONST_CAST_EXPR:
     case STATIC_CAST_EXPR:
@@ -11714,7 +11716,7 @@  tsubst_qualified_id (tree qualified_id, tree args,
 
 /* Like tsubst, but deals with expressions.  This function just replaces
    template parms; to finish processing the resultant expression, use
-   tsubst_expr.  */
+   tsubst_copy_and_build or tsubst_expr.  */
 
 static tree
 tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
@@ -11879,6 +11881,8 @@  tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
     case CONST_CAST_EXPR:
     case STATIC_CAST_EXPR:
     case DYNAMIC_CAST_EXPR:
+    case IMPLICIT_CONV_EXPR:
+    case CONVERT_EXPR:
     case NOP_EXPR:
       return build1
 	(code, tsubst (TREE_TYPE (t), args, complain, in_decl),
@@ -13077,6 +13081,23 @@  tsubst_copy_and_build (tree t,
 	(tsubst (TREE_TYPE (t), args, complain, in_decl),
 	 RECUR (TREE_OPERAND (t, 0)));
 
+    case IMPLICIT_CONV_EXPR:
+      {
+	tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
+	tree expr = RECUR (TREE_OPERAND (t, 0));
+	int flags = LOOKUP_IMPLICIT;
+	if (IMPLICIT_CONV_EXPR_DIRECT_INIT (t))
+	  flags = LOOKUP_NORMAL;
+	return perform_implicit_conversion_flags (type, expr, complain,
+						  flags);
+      }
+
+    case CONVERT_EXPR:
+      return build1
+	(CONVERT_EXPR,
+	 tsubst (TREE_TYPE (t), args, complain, in_decl),
+	 RECUR (TREE_OPERAND (t, 0)));
+
     case CAST_EXPR:
     case REINTERPRET_CAST_EXPR:
     case CONST_CAST_EXPR:
@@ -19172,6 +19193,7 @@  type_dependent_expression_p (tree expression)
       || TREE_CODE (expression) == STATIC_CAST_EXPR
       || TREE_CODE (expression) == CONST_CAST_EXPR
       || TREE_CODE (expression) == REINTERPRET_CAST_EXPR
+      || TREE_CODE (expression) == IMPLICIT_CONV_EXPR
       || TREE_CODE (expression) == CAST_EXPR)
     return dependent_type_p (TREE_TYPE (expression));
 
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 7ad1e8d..189c13a 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -7481,8 +7481,6 @@  cxx_eval_constant_expression (const constexpr_call *call, tree t,
       return t;
 
     case LAMBDA_EXPR:
-    case DYNAMIC_CAST_EXPR:
-    case PSEUDO_DTOR_EXPR:
     case PREINCREMENT_EXPR:
     case POSTINCREMENT_EXPR:
     case PREDECREMENT_EXPR:
@@ -8023,6 +8021,7 @@  potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
     case CONST_CAST_EXPR:
     case STATIC_CAST_EXPR:
     case REINTERPRET_CAST_EXPR:
+    case IMPLICIT_CONV_EXPR:
       return (potential_constant_expression_1
 	      (TREE_OPERAND (t, 0),
 	       TREE_CODE (TREE_TYPE (t)) != REFERENCE_TYPE, flags));
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index f23b888..75aa265 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2384,6 +2384,7 @@  cp_tree_equal (tree t1, tree t2)
     case REINTERPRET_CAST_EXPR:
     case CONST_CAST_EXPR:
     case DYNAMIC_CAST_EXPR:
+    case IMPLICIT_CONV_EXPR:
     case NEW_EXPR:
       if (!same_type_p (TREE_TYPE (t1), TREE_TYPE (t2)))
 	return false;
@@ -2994,6 +2995,7 @@  cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func,
     case STATIC_CAST_EXPR:
     case CONST_CAST_EXPR:
     case DYNAMIC_CAST_EXPR:
+    case IMPLICIT_CONV_EXPR:
       if (TREE_TYPE (*tp))
 	WALK_SUBTREE (TREE_TYPE (*tp));
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-template3.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-template3.C
new file mode 100644
index 0000000..75b2fc4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-template3.C
@@ -0,0 +1,9 @@ 
+// { dg-options -std=c++0x }
+
+struct A { constexpr operator int() { return 42; } };
+
+template<class T>
+struct B {
+  static const int versionConst = A();
+  enum { versionEnum = versionConst };
+};
diff --git a/gcc/testsuite/g++.dg/template/constant1.C b/gcc/testsuite/g++.dg/template/constant1.C
new file mode 100644
index 0000000..a2c5a08
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/constant1.C
@@ -0,0 +1,13 @@ 
+// PR c++/49855
+
+extern void foo(int);
+
+template <class Key, class Value> void Basic() {
+  const int kT = 1.5e6;        // <--- causes ICE
+  int size = kT*2/3;
+  do {
+    foo(size);
+    size = size * 0.5 - 1;
+  } while (size >= 0 );
+
+}
diff --git a/gcc/testsuite/g++.dg/template/constant2.C b/gcc/testsuite/g++.dg/template/constant2.C
new file mode 100644
index 0000000..f71e4f5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/constant2.C
@@ -0,0 +1,22 @@ 
+// PR c++/49896
+
+template<class C>
+class test {
+ protected:
+  static const int versionConst = 0x80000000;
+  enum { versionEnum = versionConst };
+ public:
+  int getVersion();
+};
+
+template<class C>
+int test<C>::getVersion() {
+  return versionEnum;
+}
+
+class dummy_class {};
+
+int main() {
+  test<dummy_class> t;
+  return t.getVersion();
+}