Missing bits from N4268, constant evaluation for all non-type args.
authorJason Merrill <jason@redhat.com>
Sat, 10 Jun 2017 00:40:50 +0000 (20:40 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Sat, 10 Jun 2017 00:40:50 +0000 (20:40 -0400)
* call.c (build_converted_constant_expr): Rename from
build_integral_nontype_arg_conv, handle all types.
* pt.c (convert_nontype_argument): In C++17 call it for all types.
Move NOP stripping inside pointer case, don't strip ADDR_EXPR.
* cvt.c (strip_fnptr_conv): Also strip conversions to the same type.

From-SVN: r249089

16 files changed:
gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/cp-tree.h
gcc/cp/cvt.c
gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp0x/constexpr-targ.C
gcc/testsuite/g++.dg/template/crash106.C
gcc/testsuite/g++.dg/template/crash84.C
gcc/testsuite/g++.dg/template/crash87.C
gcc/testsuite/g++.dg/template/dependent-args1.C
gcc/testsuite/g++.dg/template/nontype-array1.C
gcc/testsuite/g++.dg/template/nontype13.C
gcc/testsuite/g++.dg/template/nontype21.C
gcc/testsuite/g++.dg/template/nontype26.C
gcc/testsuite/g++.dg/template/ptrmem20.C
gcc/testsuite/g++.dg/template/ptrmem8.C

index 4e966a2e26fb8952377625694312eb7f94590622..14eaa98332741c93d26c7fc50d64cd6d90381e04 100644 (file)
@@ -1,5 +1,12 @@
 2017-06-09  Jason Merrill  <jason@redhat.com>
 
+       Missing bits from N4268, constant evaluation for all non-type args.
+       * call.c (build_converted_constant_expr): Rename from
+       build_integral_nontype_arg_conv, handle all types.
+       * pt.c (convert_nontype_argument): In C++17 call it for all types.
+       Move NOP stripping inside pointer case, don't strip ADDR_EXPR.
+       * cvt.c (strip_fnptr_conv): Also strip conversions to the same type.
+
        Overhaul pointer-to-member conversion and template argument handling.
        * call.c (standard_conversion): Avoid creating ck_pmem when the
        class type is the same.
index a4b6a95f13e776a758e4061fbe850814ca41f9c2..ef9968340c6ea42e4d901f46c96c49dfbc6ecc05 100644 (file)
@@ -4005,15 +4005,16 @@ build_user_type_conversion (tree totype, tree expr, int flags,
 
 /* Subroutine of convert_nontype_argument.
 
-   EXPR is an argument for a template non-type parameter of integral or
-   enumeration type.  Do any necessary conversions (that are permitted for
-   non-type arguments) to convert it to the parameter type.
+   EXPR is an expression used in a context that requires a converted
+   constant-expression, such as a template non-type parameter.  Do any
+   necessary conversions (that are permitted for converted
+   constant-expressions) to convert it to the desired type.
 
    If conversion is successful, returns the converted expression;
    otherwise, returns error_mark_node.  */
 
 tree
-build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain)
+build_converted_constant_expr (tree type, tree expr, tsubst_flags_t complain)
 {
   conversion *conv;
   void *p;
@@ -4023,8 +4024,6 @@ build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain)
   if (error_operand_p (expr))
     return error_mark_node;
 
-  gcc_assert (INTEGRAL_OR_ENUMERATION_TYPE_P (type));
-
   /* Get the high-water mark for the CONVERSION_OBSTACK.  */
   p = conversion_obstack_alloc (0);
 
@@ -4032,36 +4031,86 @@ build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain)
                              /*c_cast_p=*/false,
                              LOOKUP_IMPLICIT, complain);
 
-  /* for a non-type template-parameter of integral or
-     enumeration type, integral promotions (4.5) and integral
-     conversions (4.7) are applied.  */
-  /* It should be sufficient to check the outermost conversion step, since
-     there are no qualification conversions to integer type.  */
-  if (conv)
-    switch (conv->kind)
-      {
-       /* A conversion function is OK.  If it isn't constexpr, we'll
-          complain later that the argument isn't constant.  */
-      case ck_user:
-       /* The lvalue-to-rvalue conversion is OK.  */
-      case ck_rvalue:
-      case ck_identity:
-       break;
+  /* A converted constant expression of type T is an expression, implicitly
+     converted to type T, where the converted expression is a constant
+     expression and the implicit conversion sequence contains only
+
+       * user-defined conversions,
+       * lvalue-to-rvalue conversions (7.1),
+       * array-to-pointer conversions (7.2),
+       * function-to-pointer conversions (7.3),
+       * qualification conversions (7.5),
+       * integral promotions (7.6),
+       * integral conversions (7.8) other than narrowing conversions (11.6.4),
+       * null pointer conversions (7.11) from std::nullptr_t,
+       * null member pointer conversions (7.12) from std::nullptr_t, and
+       * function pointer conversions (7.13),
+
+     and where the reference binding (if any) binds directly.  */
+
+  for (conversion *c = conv;
+       conv && c->kind != ck_identity;
+       c = next_conversion (c))
+    {
+      switch (c->kind)
+       {
+         /* A conversion function is OK.  If it isn't constexpr, we'll
+            complain later that the argument isn't constant.  */
+       case ck_user:
+         /* The lvalue-to-rvalue conversion is OK.  */
+       case ck_rvalue:
+         /* Array-to-pointer and function-to-pointer.  */
+       case ck_lvalue:
+         /* Function pointer conversions.  */
+       case ck_fnptr:
+         /* Qualification conversions.  */
+       case ck_qual:
+         break;
 
-      case ck_std:
-       t = next_conversion (conv)->type;
-       if (INTEGRAL_OR_ENUMERATION_TYPE_P (t))
+       case ck_ref_bind:
+         if (c->need_temporary_p)
+           {
+             if (complain & tf_error)
+               error_at (loc, "initializing %qH with %qI in converted "
+                         "constant expression does not bind directly",
+                         type, next_conversion (c)->type);
+             conv = NULL;
+           }
          break;
 
-       if (complain & tf_error)
-         error_at (loc, "conversion from %qH to %qI not considered for "
-                   "non-type template argument", t, type);
-       /* fall through.  */
+       case ck_base:
+       case ck_pmem:
+       case ck_ptr:
+       case ck_std:
+         t = next_conversion (c)->type;
+         if (INTEGRAL_OR_ENUMERATION_TYPE_P (t)
+             && INTEGRAL_OR_ENUMERATION_TYPE_P (type))
+           /* Integral promotion or conversion.  */
+           break;
+         if (NULLPTR_TYPE_P (t))
+           /* Conversion from nullptr to pointer or pointer-to-member.  */
+           break;
 
-      default:
-       conv = NULL;
-       break;
-      }
+         if (complain & tf_error)
+           error_at (loc, "conversion from %qH to %qI in a "
+                     "converted constant expression", t, type);
+         /* fall through.  */
+
+       default:
+         conv = NULL;
+         break;
+       }
+    }
+
+  /* Avoid confusing convert_nontype_argument by introducing
+     a redundant conversion to the same reference type.  */
+  if (conv && conv->kind == ck_ref_bind
+      && REFERENCE_REF_P (expr))
+    {
+      tree ref = TREE_OPERAND (expr, 0);
+      if (same_type_p (type, TREE_TYPE (ref)))
+       return ref;
+    }
 
   if (conv)
     expr = convert_like (conv, expr, complain);
index 07da0cda5e741bf031149a1de9328fd640c80cca..6d4d93702f8d5db203fdaf13716d97e0aaaca65e 100644 (file)
@@ -5907,7 +5907,7 @@ extern bool reference_related_p                   (tree, tree);
 extern int remaining_arguments                 (tree);
 extern tree perform_implicit_conversion                (tree, tree, tsubst_flags_t);
 extern tree perform_implicit_conversion_flags  (tree, tree, tsubst_flags_t, int);
-extern tree build_integral_nontype_arg_conv    (tree, tree, tsubst_flags_t);
+extern tree build_converted_constant_expr      (tree, tree, tsubst_flags_t);
 extern tree perform_direct_initialization_if_possible (tree, tree, bool,
                                                        tsubst_flags_t);
 extern tree in_charge_arg_for_name             (tree);
index 3460e1334294407238366a336c9d618a2ffa9bc1..631ff49673f0d659c704343ec6f1285641f3d338 100644 (file)
@@ -2020,8 +2020,8 @@ fnptr_conv_p (tree to, tree from)
          || can_convert_tx_safety (t, f));
 }
 
-/* Return FN with any NOP_EXPRs that represent function pointer
-   conversions stripped.  */
+/* Return FN with any NOP_EXPRs stripped that represent function pointer
+   conversions or conversions to the same type.  */
 
 tree
 strip_fnptr_conv (tree fn)
@@ -2029,7 +2029,10 @@ strip_fnptr_conv (tree fn)
   while (TREE_CODE (fn) == NOP_EXPR)
     {
       tree op = TREE_OPERAND (fn, 0);
-      if (fnptr_conv_p (TREE_TYPE (fn), TREE_TYPE (op)))
+      tree ft = TREE_TYPE (fn);
+      tree ot = TREE_TYPE (op);
+      if (same_type_p (ft, ot)
+         || fnptr_conv_p (ft, ot))
        fn = op;
       else
        break;
index b537cb8a85d7d9111a1f513fdd1e3ea4afbd36b6..4d4484f919bb363c32f197c39739939e21e7e1d7 100644 (file)
@@ -6430,6 +6430,8 @@ static tree
 convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 {
   tree expr_type;
+  location_t loc = EXPR_LOC_OR_LOC (expr, input_location);
+  tree orig_expr = expr;
 
   /* Detect immediately string literals as invalid non-type argument.
      This special-case is not needed for correctness (we would easily
@@ -6503,18 +6505,17 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
           argument for a parameter of pointer to member type, we just want
           to leave it in that form rather than lower it to a
           CONSTRUCTOR.  */;
-      else if (INTEGRAL_OR_ENUMERATION_TYPE_P (type))
-       /* Constant value checking is done later with type conversion.  */;
-      else if (cxx_dialect >= cxx1z)
+      else if (INTEGRAL_OR_ENUMERATION_TYPE_P (type)
+              || 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);
-           }
+         /* C++17: A template-argument for a non-type template-parameter shall
+            be a converted constant expression (8.20) of the type of the
+            template-parameter.  */
+         expr = build_converted_constant_expr (type, expr, complain);
+         if (expr == error_mark_node)
+           return error_mark_node;
+         expr = maybe_constant_value (expr);
+         expr = convert_from_reference (expr);
        }
       else if (TYPE_PTR_OR_PTRMEM_P (type))
        {
@@ -6558,26 +6559,6 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
        }
     }
 
-  /* We could also generate a NOP_EXPR(ADDR_EXPR()) when the
-     parameter is a pointer to object, through decay and
-     qualification conversion. Let's strip everything.  */
-  else if (TREE_CODE (expr) == NOP_EXPR && TYPE_PTROBV_P (type))
-    {
-      tree probe = expr;
-      STRIP_NOPS (probe);
-      if (TREE_CODE (probe) == ADDR_EXPR
-         && TYPE_PTR_P (TREE_TYPE (probe)))
-       {
-         /* Skip the ADDR_EXPR only if it is part of the decay for
-            an array. Otherwise, it is part of the original argument
-            in the source code.  */
-         if (TREE_CODE (TREE_TYPE (TREE_OPERAND (probe, 0))) == ARRAY_TYPE)
-           probe = TREE_OPERAND (probe, 0);
-         expr = probe;
-         expr_type = TREE_TYPE (expr);
-       }
-    }
-
   /* [temp.arg.nontype]/5, bullet 1
 
      For a non-type template-parameter of integral or enumeration type,
@@ -6585,10 +6566,13 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
      (_conv.integral_) are applied.  */
   if (INTEGRAL_OR_ENUMERATION_TYPE_P (type))
     {
-      tree t = build_integral_nontype_arg_conv (type, expr, complain);
-      t = maybe_constant_value (t);
-      if (t != error_mark_node)
-       expr = t;
+      if (cxx_dialect < cxx11)
+       {
+         tree t = build_converted_constant_expr (type, expr, complain);
+         t = maybe_constant_value (t);
+         if (t != error_mark_node)
+           expr = t;
+       }
 
       if (!same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (expr)))
        return error_mark_node;
@@ -6606,8 +6590,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
                return NULL_TREE;
              expr = cxx_constant_value (expr);
              if (errorcount > errs || warningcount + werrorcount > warns)
-               inform (EXPR_LOC_OR_LOC (expr, input_location),
-                       "in template argument for type %qT ", type);
+               inform (loc, "in template argument for type %qT ", type);
              if (expr == error_mark_node)
                return NULL_TREE;
              /* else cxx_constant_value complained but gave us
@@ -6629,6 +6612,23 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
      conversion (_conv.array_) are applied.  */
   else if (TYPE_PTROBV_P (type))
     {
+      tree decayed = expr;
+
+      /* Look through any NOP_EXPRs around an ADDR_EXPR, whether they come from
+        decay_conversion or an explicit cast.  If it's a problematic cast,
+        we'll complain about it below.  */
+      if (TREE_CODE (expr) == NOP_EXPR)
+       {
+         tree probe = expr;
+         STRIP_NOPS (probe);
+         if (TREE_CODE (probe) == ADDR_EXPR
+             && TYPE_PTR_P (TREE_TYPE (probe)))
+           {
+             expr = probe;
+             expr_type = TREE_TYPE (expr);
+           }
+       }
+
       /* [temp.arg.nontype]/1  (TC1 version, DR 49):
 
         A template-argument for a non-type, non-template template-parameter
@@ -6648,15 +6648,14 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
        ;
       else if (cxx_dialect >= cxx11 && integer_zerop (expr))
        /* Null pointer values are OK in C++11.  */;
-      else if (TREE_CODE (expr) != ADDR_EXPR
-              && TREE_CODE (expr_type) != ARRAY_TYPE)
+      else if (TREE_CODE (expr) != ADDR_EXPR)
        {
          if (VAR_P (expr))
            {
              if (complain & tf_error)
                error ("%qD is not a valid template argument "
                       "because %qD is a variable, not the address of "
-                      "a variable", expr, expr);
+                      "a variable", orig_expr, expr);
              return NULL_TREE;
            }
          if (POINTER_TYPE_P (expr_type))
@@ -6664,7 +6663,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
              if (complain & tf_error)
                error ("%qE is not a valid template argument for %qT "
                       "because it is not the address of a variable",
-                      expr, type);
+                      orig_expr, type);
              return NULL_TREE;
            }
          /* Other values, like integer constants, might be valid
@@ -6673,15 +6672,13 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
        }
       else
        {
-         tree decl;
+         tree decl = TREE_OPERAND (expr, 0);
 
-         decl = ((TREE_CODE (expr) == ADDR_EXPR)
-                 ? TREE_OPERAND (expr, 0) : expr);
          if (!VAR_P (decl))
            {
              if (complain & tf_error)
                error ("%qE is not a valid template argument of type %qT "
-                      "because %qE is not a variable", expr, type, decl);
+                      "because %qE is not a variable", orig_expr, type, decl);
              return NULL_TREE;
            }
          else if (cxx_dialect < cxx11 && !DECL_EXTERNAL_LINKAGE_P (decl))
@@ -6689,7 +6686,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
              if (complain & tf_error)
                error ("%qE is not a valid template argument of type %qT "
                       "because %qD does not have external linkage",
-                      expr, type, decl);
+                      orig_expr, type, decl);
              return NULL_TREE;
            }
          else if ((cxx_dialect >= cxx11 && cxx_dialect < cxx1z)
@@ -6697,7 +6694,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
            {
              if (complain & tf_error)
                error ("%qE is not a valid template argument of type %qT "
-                      "because %qD has no linkage", expr, type, decl);
+                      "because %qD has no linkage", orig_expr, type, decl);
              return NULL_TREE;
            }
          /* C++17: For a non-type template-parameter of reference or pointer
@@ -6734,9 +6731,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
            }
        }
 
-      expr = decay_conversion (expr, complain);
-      if (expr == error_mark_node)
-       return error_mark_node;
+      expr = decayed;
 
       expr = perform_qualification_conversions (type, expr);
       if (expr == error_mark_node)
@@ -23858,7 +23853,7 @@ value_dependent_expression_p (tree expression)
              {
                tree t = TREE_OPERAND (expression, i);
 
-               /* In some cases, some of the operands may be missing.l
+               /* In some cases, some of the operands may be missing.
                   (For example, in the case of PREDECREMENT_EXPR, the
                   amount to increment by may be missing.)  That doesn't
                   make the expression dependent.  */
index b5171141f94f0964c23ccc3c3eb4535a287db8a8..98bb502249af49d97f879d5749e27d11110899e0 100644 (file)
@@ -10,4 +10,4 @@ struct B
 { };
 
 constexpr A a { };
-B<a> b;                                // { dg-error "template argument|invalid type" }
+B<a> b;                         // { dg-error "template argument|converted constant" }
index 5bef101e59806f5801ead46282d44160be02b84e..8d9726999003514faed477005c6eba87447674fc 100644 (file)
@@ -11,4 +11,4 @@ template<T N = 0, void (A::*)() = &A::foo<N> > struct B {}; // { dg-error "type|
 
 B<> b; // { dg-message "non-type" }
 
-// { dg-prune-output "could not convert" }
+// { dg-prune-output "(could not convert|no matches)" }
index 103e90a7076cd43271cef57630952bf4b66727d6..b3099d931c2ede5bb525e92719622ede565b7207 100644 (file)
@@ -14,7 +14,7 @@ void
 foo ()
 {
   a<int> a1; // OK
-  a<int>::b<a,int> b1; // { dg-error "template argument" }
+  a<int>::b<a,int> b1; // { dg-error "template argument|converted constant" }
 }
 
 // { dg-prune-output "invalid type in declaration" }
index 7b8bf4ada0d0834a2bf494f5f89100d3f930f63d..af81edbfd80a09da42a76679065830c6c8ec9c55 100644 (file)
@@ -17,7 +17,7 @@ template <bool name>
 class BUG2 : BUG
 {
 public:
- typedef BUG1_5<name> ptr; // { dg-error "could not convert template argument" }
+ typedef BUG1_5<name> ptr; // { dg-error "convert" }
 };
 
 int main()
index a540e55f4e52da420cf4e80d9c9162b1086457c0..8fffbf85317ad8bbf6c9d97895745136a3d938d3 100644 (file)
@@ -10,4 +10,4 @@ template<int N, void (A::*)() = &A::foo<N> > struct B {};
 
 B<int> b; // { dg-error "type/value mismatch|expected a constant|invalid type" }
 
-// { dg-prune-output "could not convert" }
+// { dg-prune-output "(could not convert|no match)" }
index cf21908481b67885a787fa9e79964b08f9548568..f22551b0f1adfb7c037a33bce181dead8b93a8e9 100644 (file)
@@ -10,17 +10,31 @@ constexpr char const s3[] = "hi";  // OK since C++11
 
 constexpr char const * f() { return s3; }
 
+using fn_p = char const * (*)();
+template <fn_p> struct A { };
+constexpr fn_p f2() { return f; }
+
+struct B
+{
+  constexpr B() { }
+  constexpr operator const char *() { return s3; }
+};
+
 int main()
 {
   Message<s1> m1;  // OK (all versions)
   Message<s2> m2;  // OK for clang since C++14, for gcc since C++17
   Message<s3> m3;  // OK for clang/gcc since C++11
 
+  A<f2()> a1; // { dg-error "" "" { target c++14_down } }
+
   static char const s4[] = "hi";
   static constexpr char const s5[] = "hi";  // OK since C++11
   Message<s4> m4;  // { dg-error "no linkage" "" { target c++14_down } }
   Message<s5> m5;  // { dg-error "no linkage" "" { target c++14_down } }
   Message<f()> m6; // { dg-error "" "" { target c++14_down } }
+  Message<B{}> m7; // { dg-error "" "" { target c++14_down } }
+
 
   char const s8[] = "hi";
   Message<s8> m8;  // { dg-error "" }
index d604da9445416b6d87e71a7e3e0b7c04b3657694..3250109aa4a66387153b80a3c1549c6e523f9774 100644 (file)
@@ -11,7 +11,7 @@ struct Dummy
   template<bool B>
   void tester()
   {
-    bar<evil>()(); // { dg-error "constant|template" }
+    bar<evil>()(); // { dg-error "constant|template|convert" }
   }
   template<bool B>
   struct bar
index 69cab54f412b48703b4ca159c78b88495f0ead44..508f9090240269d362c27d853d3cfaf4f5aefd9e 100644 (file)
@@ -4,4 +4,4 @@ template<char const * const x> class Something { };
 
 extern char const xyz;
 
-class SomethingElse:public Something<xyz> { }; // { dg-error "xyz. is a variable" }
+class SomethingElse:public Something<xyz> { }; // { dg-error "" }
index 763d987dd399d7970fb4e91fa5b7882bb29f1c96..588ce1cc23a79c025a4a006776de92546c3fe2ad 100644 (file)
@@ -7,7 +7,7 @@ template<int& i> void doit() {
 template<const int& i> class X {
 public:
     void foo() {
-      doit<i>();  // { dg-error "cv-qualification|no matching" }
+      doit<i>();  // { dg-error "qualifi|template arg|no matching" }
     }
 };
 
index 2d2453d73db1918baa079df42a89a37fade99ac1..e17ed84c739fa103b657c9aece3b562f58def084 100644 (file)
@@ -12,5 +12,5 @@ template<void (A::*)()> void bar(); // { dg-message "note" }
 
 void baz()
 {
-  bar<&B::foo>();  // { dg-error "not a valid template argument|no match" }
+  bar<&B::foo>();  // { dg-error "template argument|no match" }
 }
index 85ffa4a6e8d3081fecc478bf71b3b1fb6e93fddf..b759b7077d53a61170c781182d07409f7a7b59e2 100644 (file)
@@ -15,10 +15,8 @@ template <int (D::*fun)() const> int Get(); // { dg-message "note" }
 
 int main () 
 {
-  Get<&B::I>();   // { dg-error "not a valid template argument" "not valid" }
+  Get<&B::I>();   // { dg-error "template argument|converted constant" "not valid" }
   // { dg-error "no match" "no match" { target *-*-* } .-1 }
-  // { dg-message "note" "note" { target *-*-* } .-2 }
-  Get<&D::I>();   // { dg-error "not a valid template argument" "not valid" }
+  Get<&D::I>();   // { dg-error "template argument|converted constant" "not valid" }
   // { dg-error "no match" "no match" { target *-*-* } .-1 }
-  // { dg-message "note" "note" { target *-*-* } .-2 }
 }