diff mbox

[C++] PR 55250

Message ID 54325957.9030708@oracle.com
State New
Headers show

Commit Message

Paolo Carlini Oct. 6, 2014, 8:56 a.m. UTC
Hi,

the second half of the bug report is about C++14 variable declarations 
in constexpr functions, an implementation request which should be 
already done by Jason's recent commit.

The first half is about us not rejecting in C++11 mode type declarations 
in constexpr functions outside, per 7.1.5/3/4:

     "typedef declarations and alias-declarations that do not define 
classes or enumerations,"

In fact, however, while working on the issue, I noticed that conversely 
we reject *all* type declarations in constructors (actually we used to 
ICE on those, which a while ago I turned to reject valid), eg:

     struct S
     {
         constexpr S() { typedef int T; }
     };

Thus I prepared the below, which in C++11 mode checks the exact 
requirements above, both for constructors and all the other functions. I 
had to add handling of BIND_EXPRs to the main conditional of 
build_constexpr_constructor_member_initializers, otherwise we ICE 
immediately on something as simple as the snippet above: I think all is 
fine because we get to the conditional having checked the BIND_EXPR_VARS 
part with the new checking function. Tested x86_64-linux.

Thanks!
Paolo.

/////////////////////
/cp
2014-10-06  Paolo Carlini  <paolo.carlini@oracle.com>

	PR c++/55250
	* semantics.c (check_constexpr_bind_expr_vars): New.
	(check_constexpr_ctor_body, massage_constexpr_body): Use it.
	(build_constexpr_constructor_member_initializers): Handle
	BIND_EXPR in the main conditional.

/testsuite
2014-10-06  Paolo Carlini  <paolo.carlini@oracle.com>

	PR c++/55250
	* g++.dg/cpp0x/constexpr-type-decl1.C: New.
	* g++.dg/cpp0x/constexpr-type-def1.C: Likewise.
	* g++.dg/cpp1y/constexpr-type-def1.C: Likewise.

Comments

Jason Merrill Oct. 6, 2014, 1:50 p.m. UTC | #1
On 10/06/2014 04:56 AM, Paolo Carlini wrote:
> +	&& ! is_typedef_decl (var)
> +	&& ! TYPE_DECL_ALIAS_P (var))

Maybe "&& DECL_IMPLICIT_TYPEDEF_P (var)" instead of the above?  OK 
either way.

Jason
diff mbox

Patch

Index: cp/semantics.c
===================================================================
--- cp/semantics.c	(revision 215914)
+++ cp/semantics.c	(working copy)
@@ -7833,6 +7833,26 @@  build_data_member_initialization (tree t, vec<cons
   return true;
 }
 
+/* Subroutine of check_constexpr_ctor_body and massage_constexpr_body.
+   In C++11 mode checks that the TYPE_DECLs in the BIND_EXPR_VARS of a 
+   BIND_EXPR conform to 7.1.5/3/4 on typedef and alias declarations.  */
+
+static bool
+check_constexpr_bind_expr_vars (tree t)
+{
+  gcc_assert (TREE_CODE (t) == BIND_EXPR);
+
+  if (cxx_dialect >= cxx14)
+    return true;
+
+  for (tree var = BIND_EXPR_VARS (t); var; var = DECL_CHAIN (var))
+    if (TREE_CODE (var) == TYPE_DECL
+	&& ! is_typedef_decl (var)
+	&& ! TYPE_DECL_ALIAS_P (var))
+      return false;
+  return true;
+}
+
 /* Make sure that there are no statements after LAST in the constructor
    body represented by LIST.  */
 
@@ -7850,7 +7870,7 @@  check_constexpr_ctor_body (tree last, tree list)
 	    break;
 	  if (TREE_CODE (t) == BIND_EXPR)
 	    {
-	      if (BIND_EXPR_VARS (t))
+	      if (!check_constexpr_bind_expr_vars (t))
 		{
 		  ok = false;
 		  break;
@@ -7860,8 +7880,6 @@  check_constexpr_ctor_body (tree last, tree list)
 	      else
 		continue;
 	    }
-	  /* We currently allow typedefs and static_assert.
-	     FIXME allow them in the standard, too.  */
 	  if (TREE_CODE (t) != STATIC_ASSERT)
 	    {
 	      ok = false;
@@ -7964,6 +7982,8 @@  build_constexpr_constructor_member_initializers (t
 	     "a function-try-block");
       return error_mark_node;
     }
+  else if (TREE_CODE (body) == BIND_EXPR)
+    ok = build_data_member_initialization (BIND_EXPR_BODY (body), &vec);
   else if (EXPR_P (body))
     ok = build_data_member_initialization (body, &vec);
   else
@@ -8055,7 +8075,8 @@  massage_constexpr_body (tree fun, tree body)
         body = EH_SPEC_STMTS (body);
       if (TREE_CODE (body) == MUST_NOT_THROW_EXPR)
 	body = TREE_OPERAND (body, 0);
-      if (TREE_CODE (body) == BIND_EXPR)
+      if (TREE_CODE (body) == BIND_EXPR
+	  && check_constexpr_bind_expr_vars (body))
 	body = BIND_EXPR_BODY (body);
       body = constexpr_fn_retval (body);
     }
Index: testsuite/g++.dg/cpp0x/constexpr-type-decl1.C
===================================================================
--- testsuite/g++.dg/cpp0x/constexpr-type-decl1.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/constexpr-type-decl1.C	(working copy)
@@ -0,0 +1,58 @@ 
+// PR c++/55250
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+struct GS { constexpr operator int() { return 1; } };
+enum GE { y = 1 };
+
+constexpr int Test1(int x) { typedef int T; return T(x) + 1; }
+constexpr int Test2(int x) { using T = int; return T(x) + 1; }
+constexpr int Test3(int x) { typedef GS T; return x + T(); }
+constexpr int Test4(int x) { using T = GS; return x + T(); }
+constexpr int Test5(int x) { typedef GE T; return x + T::y; }
+constexpr int Test6(int x) { using T = GE; return x + T::y; }
+
+SA(Test1(2) == 3);
+SA(Test2(2) == 3);
+SA(Test3(2) == 3);
+SA(Test4(2) == 3);
+SA(Test5(2) == 3);
+SA(Test6(2) == 3);
+
+struct S1
+{
+  constexpr S1() { typedef int T; SA(T(1) == 1); }
+};
+
+struct S2
+{
+  constexpr S2() { using T = int; SA(T(1) == 1); }
+};
+
+struct S3
+{
+  constexpr S3() { typedef GS T; SA(T() == 1); }
+};
+
+struct S4
+{
+  constexpr S4() { using T = GS; SA(T() == 1); }
+};
+
+struct S5
+{
+  constexpr S5() { typedef GE T; SA(T::y == 1); }
+};
+
+struct S6
+{
+  constexpr S6() { using T = GE; SA(T::y == 1); }
+};
+
+S1 s1;
+S2 s2;
+S3 s3;
+S4 s4;
+S5 s5;
+S6 s6;
Index: testsuite/g++.dg/cpp0x/constexpr-type-def1.C
===================================================================
--- testsuite/g++.dg/cpp0x/constexpr-type-def1.C	(revision 0)
+++ testsuite/g++.dg/cpp0x/constexpr-type-def1.C	(working copy)
@@ -0,0 +1,44 @@ 
+// PR c++/55250
+// { dg-do compile { target c++11 } }
+
+constexpr int Test1(int x) { enum E { y = 1 }; return x; }  // { dg-error "not a return-statement" "" { target { c++11_only } } }
+
+constexpr int Test2(int x) { struct T { }; return x; }  // { dg-error "not a return-statement" "" { target { c++11_only } } }
+
+constexpr int Test3(int x) { typedef enum E { y = 1 } EE; return x; }  // { dg-error "not a return-statement" "" { target { c++11_only } } }
+
+constexpr int Test4(int x) { typedef struct T { } TT; return x; }  // { dg-error "not a return-statement" "" { target { c++11_only } } }
+
+constexpr int Test5(int x) { using EE = enum E { y = 1 }; return x; }  // { dg-error "not a return-statement" "" { target { c++11_only } } }
+
+constexpr int Test6(int x) { using TT = struct T { }; return x; }  // { dg-error "not a return-statement" "" { target { c++11_only } } }
+
+struct S1
+{
+  constexpr S1() { enum E { y = 1 }; }  // { dg-error "does not have empty body" "" { target { c++11_only } } }
+};
+
+struct S2
+{
+  constexpr S2() { struct T { }; }  // { dg-error "does not have empty body" "" { target { c++11_only } } }
+};
+
+struct S3
+{
+  constexpr S3() { typedef enum E { y = 1 } EE; }  // { dg-error "does not have empty body" "" { target { c++11_only } } }
+};
+
+struct S4
+{
+  constexpr S4() { typedef struct T { } TT; }  // { dg-error "does not have empty body" "" { target { c++11_only } } }
+};
+
+struct S5
+{
+  constexpr S5() { using EE = enum E { y = 1 }; }  // { dg-error "does not have empty body" "" { target { c++11_only } } }
+};
+
+struct S6
+{
+  constexpr S6() { using TT = struct T { }; }  // { dg-error "does not have empty body" "" { target { c++11_only } } }
+};
Index: testsuite/g++.dg/cpp1y/constexpr-type-def1.C
===================================================================
--- testsuite/g++.dg/cpp1y/constexpr-type-def1.C	(revision 0)
+++ testsuite/g++.dg/cpp1y/constexpr-type-def1.C	(working copy)
@@ -0,0 +1,60 @@ 
+// PR c++/55250
+// { dg-do compile { target c++14 } }
+
+#define SA(X) static_assert((X),#X)
+
+constexpr int Test1(int x) { enum E { y = 1 }; return x + y; }
+
+constexpr int Test2(int x) { struct T { constexpr operator int() { return 1; } }; return x + T(); }
+
+constexpr int Test3(int x) { typedef enum E { y = 1 } EE; return x + EE::y; }
+
+constexpr int Test4(int x) { typedef struct T { constexpr operator int() { return 1; } } TT; return x + TT(); }
+
+constexpr int Test5(int x) { using EE = enum E { y = 1 }; return x + EE::y; }
+
+constexpr int Test6(int x) { using TT = struct T { constexpr operator int() { return 1; } }; return x + TT(); }
+
+SA(Test1(2) == 3);
+SA(Test2(2) == 3);
+SA(Test3(2) == 3);
+SA(Test4(2) == 3);
+SA(Test5(2) == 3);
+SA(Test6(2) == 3);
+
+struct S1
+{
+  constexpr S1() { enum E { y = 1 }; SA(y == 1); }
+};
+
+struct S2
+{
+  constexpr S2() { struct T { constexpr operator int() { return 1; } }; SA(T() == 1); }
+};
+
+struct S3
+{
+  constexpr S3() { typedef enum E { y = 1} EE; SA(EE::y == 1); }
+};
+
+struct S4
+{
+  constexpr S4() { typedef struct T { constexpr operator int() { return 1; } } TT; SA(TT() == 1); }
+};
+
+struct S5
+{
+  constexpr S5() { using EE = enum E { y = 1}; SA(EE::y == 1); }
+};
+
+struct S6
+{
+  constexpr S6() { using TT = struct T { constexpr operator int() { return 1; } }; SA(TT() == 1); }
+};
+
+S1 s1;
+S2 s2;
+S3 s3;
+S4 s4;
+S5 s5;
+S6 s6;