diff mbox

C++ PATCH to constexpr array decay handling

Message ID CADzB+2ncQH2ybwXVV7KexHSa7w2ACxJ3C_f2jAZ=R4NrLKpQWg@mail.gmail.com
State New
Headers show

Commit Message

Jason Merrill June 9, 2017, 8:05 p.m. UTC
Recent discussion on the C++ committee core language list pointed out
that we were wrongly rejecting use of an array variable as a template
argument for a pointer parameter, because we were asking whether the
array is constant.  This patch corrects that check to consider the
address of the array rather than its value.

The second part of the patch implements the C++17 change that no
longer requires a pointer non-template argument to point to a variable
with linkage.

Tested x86_64-pc-linux-gnu, applying to trunk and 7.
commit 4f4f61e45d63194212b01e52efd78df410352cff
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Jun 6 18:26:17 2017 -0700

            Fix array decay handling in constant expressions.
    
            * parser.c (cp_parser_constant_expression): Check
            potential_rvalue_constant_expression after decay_conversion.
            * pt.c (convert_nontype_argument): Don't require linkage in C++17.
diff mbox

Patch

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index bc77ee9..d02ad36 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -9462,10 +9462,14 @@  cp_parser_constant_expression (cp_parser* parser,
       /* Require an rvalue constant expression here; that's what our
 	 callers expect.  Reference constant expressions are handled
 	 separately in e.g. cp_parser_template_argument.  */
-      bool is_const = potential_rvalue_constant_expression (expression);
+      tree decay = expression;
+      if (TREE_TYPE (expression)
+	  && TREE_CODE (TREE_TYPE (expression)) == ARRAY_TYPE)
+	decay = build_address (expression);
+      bool is_const = potential_rvalue_constant_expression (decay);
       parser->non_integral_constant_expression_p = !is_const;
       if (!is_const && !allow_non_constant_p)
-	require_potential_rvalue_constant_expression (expression);
+	require_potential_rvalue_constant_expression (decay);
     }
   if (allow_non_constant_p)
     *non_constant_p = parser->non_integral_constant_expression_p;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index a72c314..188338e 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -6683,13 +6683,46 @@  convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 		       expr, type, decl);
 	      return NULL_TREE;
 	    }
-	  else if (cxx_dialect >= cxx11 && decl_linkage (decl) == lk_none)
+	  else if ((cxx_dialect >= cxx11 && cxx_dialect < cxx1z)
+		   && decl_linkage (decl) == lk_none)
 	    {
 	      if (complain & tf_error)
 		error ("%qE is not a valid template argument of type %qT "
 		       "because %qD has no linkage", expr, type, decl);
 	      return NULL_TREE;
 	    }
+	  /* C++17: For a non-type template-parameter of reference or pointer
+	     type, the value of the constant expression shall not refer to (or
+	     for a pointer type, shall not be the address of):
+	       * a subobject (4.5),
+	       * a temporary object (15.2),
+	       * a string literal (5.13.5),
+	       * the result of a typeid expression (8.2.8), or
+	       * a predefined __func__ variable (11.4.1).  */
+	  else if (DECL_ARTIFICIAL (decl))
+	    {
+	      if (complain & tf_error)
+		error ("the address of %qD is not a valid template argument",
+		       decl);
+	      return NULL_TREE;
+	    }
+	  else if (!same_type_ignoring_top_level_qualifiers_p
+		   (strip_array_types (TREE_TYPE (type)),
+		    strip_array_types (TREE_TYPE (decl))))
+	    {
+	      if (complain & tf_error)
+		error ("the address of the %qT subobject of %qD is not a "
+		       "valid template argument", TREE_TYPE (type), decl);
+	      return NULL_TREE;
+	    }
+	  else if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl))
+	    {
+	      if (complain & tf_error)
+		error ("the address of %qD is not a valid template argument "
+		       "because it does not have static storage duration",
+		       decl);
+	      return NULL_TREE;
+	    }
 	}
 
       expr = decay_conversion (expr, complain);
diff --git a/gcc/testsuite/g++.dg/template/function1.C b/gcc/testsuite/g++.dg/template/function1.C
index f234585..4bab2e4 100644
--- a/gcc/testsuite/g++.dg/template/function1.C
+++ b/gcc/testsuite/g++.dg/template/function1.C
@@ -1,10 +1,8 @@ 
 // PR c++/38647
-// { dg-do compile { target { ! c++1z } } }
-// { dg-prune-output "note" }
 
 template<const char *, int> struct A {};
 const char func[] = "abc";
-template<int N> struct A<func, N> {};	// { dg-error "cannot appear|is invalid|not a valid|constant expression" }
+template<int N> struct A<func, N> {};	// { dg-error "cannot appear|is invalid|not a valid|constant expression" "" { target c++98_only } }
 
 char a1[1];
 A<a1, 0> a;
diff --git a/gcc/testsuite/g++.dg/template/nontype-array1.C b/gcc/testsuite/g++.dg/template/nontype-array1.C
new file mode 100644
index 0000000..cf21908
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/nontype-array1.C
@@ -0,0 +1,27 @@ 
+// { dg-do compile { target c++11 } }
+
+template <char const* STR>
+class Message {
+};
+
+extern char const s1[] = "hi";
+char const s2[] = "hi";
+constexpr char const s3[] = "hi";  // OK since C++11
+
+constexpr char const * f() { 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
+
+  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 } }
+
+  char const s8[] = "hi";
+  Message<s8> m8;  // { dg-error "" }
+}