At the Bloomington C++ meeting we discussed some issues with the
constexpr specification that the clang team encountered while trying to
implement it. Among the issues was a problem that also came up recently
for us as BZ 50248: if the constexpr-ness of a template instantiation
depends on its body, we need to instantiate it in order to decide
whether or not an implicitly-declared function that uses it is
constexpr. The resolution of DR 1358 is that an instantiation of a
constexpr template is constexpr even if it can never produce a constant
expression.
The second patch is related to DR 1360, where the clang team was
complaining that deciding whether or not a class is literal requires the
implicit declaration of the default constructor, which they would like
to do lazily. We seem to have agreed that it can be avoided in the
cases where doing such is useful, but while looking at this I noticed a
bug in our handling of this stuff: the function
synthesized_default_constructor_is_constexpr was only right for trivial
constructors. So now I've renamed it accordingly, and force the
implicit declaration for the non-trivial case.
Tested x86_64-pc-linux-gnu, applying to trunk.
commit 7efc1b17c6bf70cc2c4d12ce912c194f09f2ea17
Author: Jason Merrill <ja...@redhat.com>
Date: Wed Aug 31 10:23:57 2011 -0400
PR c++/50248
Core 1358
* init.c (perform_member_init): Don't diagnose missing inits here.
(emit_mem_initializers): Or here.
* method.c (process_subob_fn): Don't instantiate constexpr ctors.
* semantics.c (cx_check_missing_mem_inits): New.
(explain_invalid_constexpr_fn): Call it.
(register_constexpr_fundef): Likewise. Leave
DECL_DECLARED_CONSTEXPR_P set when the body is unsuitable.
(cxx_eval_call_expression): Adjust diagnostics.
(cxx_eval_constant_expression): Catch use of 'this' in a constructor.
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 847f519..ff1884b 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -606,15 +606,6 @@ perform_member_init (tree member, tree init)
core_type = strip_array_types (type);
- if (DECL_DECLARED_CONSTEXPR_P (current_function_decl)
- && !type_has_constexpr_default_constructor (core_type))
- {
- if (!DECL_TEMPLATE_INSTANTIATION (current_function_decl))
- error ("uninitialized member %qD in %<constexpr%> constructor",
- member);
- DECL_DECLARED_CONSTEXPR_P (current_function_decl) = false;
- }
-
if (CLASS_TYPE_P (core_type)
&& (CLASSTYPE_READONLY_FIELDS_NEED_INIT (core_type)
|| CLASSTYPE_REF_FIELDS_NEED_INIT (core_type)))
@@ -962,16 +953,6 @@ emit_mem_initializers (tree mem_inits)
OPT_Wextra, "base class %q#T should be explicitly "
"initialized in the copy constructor",
BINFO_TYPE (subobject));
-
- if (DECL_DECLARED_CONSTEXPR_P (current_function_decl)
- && !(type_has_constexpr_default_constructor
- (BINFO_TYPE (subobject))))
- {
- if (!DECL_TEMPLATE_INSTANTIATION (current_function_decl))
- error ("uninitialized base %qT in %<constexpr%> constructor",
- BINFO_TYPE (subobject));
- DECL_DECLARED_CONSTEXPR_P (current_function_decl) = false;
- }
}
/* Initialize the base. */
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 3d272a3..74a3bdb 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -952,23 +952,14 @@ process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p,
goto bad;
}
- if (constexpr_p)
+ if (constexpr_p && !DECL_DECLARED_CONSTEXPR_P (fn))
{
- /* If this is a specialization of a constexpr template, we need to
- force the instantiation now so that we know whether or not it's
- really constexpr. */
- if (DECL_DECLARED_CONSTEXPR_P (fn) && DECL_TEMPLATE_INSTANTIATION (fn)
- && !DECL_TEMPLATE_INSTANTIATED (fn))
- instantiate_decl (fn, /*defer_ok*/false, /*expl_class*/false);
- if (!DECL_DECLARED_CONSTEXPR_P (fn))
+ *constexpr_p = false;
+ if (msg)
{
- *constexpr_p = false;
- if (msg)
- {
- inform (0, "defaulted constructor calls non-constexpr "
- "%q+D", fn);
- explain_invalid_constexpr_fn (fn);
- }
+ inform (0, "defaulted constructor calls non-constexpr "
+ "%q+D", fn);
+ explain_invalid_constexpr_fn (fn);
}
}
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index ce84062..fd96d70 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5775,6 +5775,53 @@ massage_constexpr_body (tree fun, tree body)
return body;
}
+/* FUN is a constexpr constructor with massaged body BODY. Return true
+ if some bases/fields are uninitialized, and complain if COMPLAIN. */
+
+static bool
+cx_check_missing_mem_inits (tree fun, tree body, bool complain)
+{
+ bool bad;
+ tree field;
+ unsigned i, nelts;
+
+ if (TREE_CODE (body) != CONSTRUCTOR)
+ return false;
+
+ bad = false;
+ nelts = CONSTRUCTOR_NELTS (body);
+ field = TYPE_FIELDS (DECL_CONTEXT (fun));
+ for (i = 0; i <= nelts; ++i)
+ {
+ tree index;
+ if (i == nelts)
+ index = NULL_TREE;
+ else
+ {
+ index = CONSTRUCTOR_ELT (body, i)->index;
+ /* Skip base vtable inits. */
+ if (TREE_CODE (index) == COMPONENT_REF)
+ continue;
+ }
+ for (; field != index; field = DECL_CHAIN (field))
+ {
+ if (TREE_CODE (field) != FIELD_DECL
+ || (DECL_C_BIT_FIELD (field) && !DECL_NAME (field)))
+ continue;
+ if (!complain)
+ return true;
+ error ("uninitialized member %qD in %<constexpr%> constructor",
+ field);
+ bad = true;
+ }
+ if (field == NULL_TREE)
+ break;
+ field = DECL_CHAIN (field);
+ }
+
+ return bad;
+}
+
/* We are processing the definition of the constexpr function FUN.
Check that its BODY fulfills the propriate requirements and
enter it in the constexpr function definition table.
@@ -5797,12 +5844,15 @@ register_constexpr_fundef (tree fun, tree body)
if (!potential_rvalue_constant_expression (body))
{
- DECL_DECLARED_CONSTEXPR_P (fun) = false;
if (!DECL_TEMPLATE_INFO (fun))
require_potential_rvalue_constant_expression (body);
return NULL;
}
+ if (DECL_CONSTRUCTOR_P (fun)
+ && cx_check_missing_mem_inits (fun, body, !DECL_TEMPLATE_INFO (fun)))
+ return NULL;
+
/* Create the constexpr function table if necessary. */
if (constexpr_fundef_table == NULL)
constexpr_fundef_table = htab_create_ggc (101,
@@ -5842,8 +5892,7 @@ explain_invalid_constexpr_fn (tree fun)
save_loc = input_location;
input_location = DECL_SOURCE_LOCATION (fun);
- inform (0, "%q+D is not constexpr because it does not satisfy the "
- "requirements:", fun);
+ inform (0, "%q+D is not usable as a constexpr function because:", fun);
/* First check the declaration. */
if (is_valid_constexpr_fn (fun, true))
{
@@ -5854,6 +5903,8 @@ explain_invalid_constexpr_fn (tree fun)
{
body = massage_constexpr_body (fun, DECL_SAVED_TREE (fun));
require_potential_rvalue_constant_expression (body);
+ if (DECL_CONSTRUCTOR_P (fun))
+ cx_check_missing_mem_inits (fun, body, true);
}
}
input_location = save_loc;
@@ -6203,7 +6254,16 @@ cxx_eval_call_expression (const constexpr_call *old_call, tree t,
if (new_call.fundef == NULL || new_call.fundef->body == NULL)
{
if (!allow_non_constant)
- error_at (loc, "%qD used before its definition", fun);
+ {
+ if (DECL_SAVED_TREE (fun))
+ {
+ /* The definition of fun was somehow unsuitable. */
+ error_at (loc, "%qD called in a constant expression", fun);
+ explain_invalid_constexpr_fn (fun);
+ }
+ else
+ error_at (loc, "%qD used before its definition", fun);
+ }
*non_constant_p = true;
return t;
}
@@ -7176,7 +7236,17 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
case PARM_DECL:
if (call && DECL_CONTEXT (t) == call->fundef->decl)
- r = lookup_parameter_binding (call, t);
+ {
+ if (DECL_ARTIFICIAL (t) && DECL_CONSTRUCTOR_P (DECL_CONTEXT (t)))
+ {
+ if (!allow_non_constant)
+ sorry ("use of the value of the object being constructed "
+ "in a constant expression");
+ *non_constant_p = true;
+ }
+ else
+ r = lookup_parameter_binding (call, t);
+ }
else if (addr)
/* Defer in case this is only used for its type. */;
else
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C
index 5124f7c..a6cf408 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C
@@ -14,7 +14,7 @@ struct s {
int v;
};
-constexpr s bang; // { dg-error "" }
+constexpr s bang; // { dg-message "" }
struct R {
int i,j;
@@ -33,7 +33,7 @@ struct T {
constexpr T t1;
// Ill-formed (diagnostic required)
-constexpr T t2(t1); // { dg-error "" }
+constexpr T t2(t1); // { dg-message "" }
// Well-formed
struct U {
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
index 584a5a0..3df7956 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
@@ -89,6 +89,6 @@ struct resource {
};
constexpr resource f(resource d)
{ return d; } // { dg-error "non-constexpr" }
-constexpr resource d = f(9); // { dg-error "resource" }
+constexpr resource d = f(9); // { dg-message "constexpr" }
// 4.4 floating-point constant expressions
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-template1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-template1.C
new file mode 100644
index 0000000..8807723
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-template1.C
@@ -0,0 +1,27 @@
+// PR c++/50248, DR 1358
+// { dg-options -std=c++0x }
+
+template<class Elt, unsigned max>
+struct earray
+{
+ Elt elts[max];
+ earray() = default;
+ template<typename... Elt2>
+ constexpr earray(Elt2&& ... e): elts(0) { }
+};
+
+struct SessionData
+{
+ SessionData(SessionData&) = delete;
+ SessionData() = default;
+};
+
+struct MapSessionData : SessionData
+{
+ earray<short, 11> equip_index;
+};
+
+void test()
+{
+ MapSessionData *sd = new MapSessionData;
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
new file mode 100644
index 0000000..6786d16
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C
@@ -0,0 +1,12 @@
+// { dg-options -std=c++0x }
+
+template <class T> struct A
+{
+ T t;
+ constexpr A() { } // { dg-error "uninitialized" }
+};
+
+int main()
+{
+ constexpr A<int> a; // { dg-error "A()" }
+}
commit 2f4a14c2ce37bbba87e45977c1314ab7984baf72
Author: Jason Merrill <ja...@redhat.com>
Date: Fri Aug 19 09:21:45 2011 -0400
* class.c (trivial_default_constructor_is_constexpr): Rename from
synthesized_default_constructor_is_constexpr.
(type_has_constexpr_default_constructor): Adjust.
(add_implicitly_declared_members): Call it instead.
(explain_non_literal_class): Explain about non-constexpr default ctor.
* cp-tree.h: Adjust.
* method.c (synthesized_method_walk): Adjust.
* semantics.c (explain_invalid_constexpr_fn): Handle defaulted
functions, too.
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 2a4bc77..a4a7468 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -2726,7 +2726,8 @@ add_implicitly_declared_members (tree t,
CLASSTYPE_LAZY_DEFAULT_CTOR (t) = 1;
if (cxx_dialect >= cxx0x)
TYPE_HAS_CONSTEXPR_CTOR (t)
- = synthesized_default_constructor_is_constexpr (t);
+ /* This might force the declaration. */
+ = type_has_constexpr_default_constructor (t);
}
/* [class.ctor]
@@ -4355,15 +4356,15 @@ type_has_user_provided_default_constructor (tree t)
return false;
}
-/* Returns true iff for class T, a synthesized default constructor
+/* Returns true iff for class T, a trivial synthesized default constructor
would be constexpr. */
bool
-synthesized_default_constructor_is_constexpr (tree t)
+trivial_default_constructor_is_constexpr (tree t)
{
- /* A defaulted default constructor is constexpr
+ /* A defaulted trivial default constructor is constexpr
if there is nothing to initialize. */
- /* FIXME adjust for non-static data member initializers. */
+ gcc_assert (!TYPE_HAS_COMPLEX_DFLT (t));
return is_really_empty_class (t);
}
@@ -4381,7 +4382,12 @@ type_has_constexpr_default_constructor (tree t)
return false;
}
if (CLASSTYPE_LAZY_DEFAULT_CTOR (t))
- return synthesized_default_constructor_is_constexpr (t);
+ {
+ if (!TYPE_HAS_COMPLEX_DFLT (t))
+ return trivial_default_constructor_is_constexpr (t);
+ /* Non-trivial, we need to check subobject constructors. */
+ lazily_declare_fn (sfk_constructor, t);
+ }
fns = locate_ctor (t);
return (fns && DECL_DECLARED_CONSTEXPR_P (fns));
}
@@ -4608,9 +4614,14 @@ explain_non_literal_class (tree t)
else if (CLASSTYPE_NON_AGGREGATE (t)
&& !TYPE_HAS_TRIVIAL_DFLT (t)
&& !TYPE_HAS_CONSTEXPR_CTOR (t))
- inform (0, " %q+T is not an aggregate, does not have a trivial "
- "default constructor, and has no constexpr constructor that "
- "is not a copy or move constructor", t);
+ {
+ inform (0, " %q+T is not an aggregate, does not have a trivial "
+ "default constructor, and has no constexpr constructor that "
+ "is not a copy or move constructor", t);
+ if (TYPE_HAS_DEFAULT_CONSTRUCTOR (t)
+ && !type_has_user_provided_default_constructor (t))
+ explain_invalid_constexpr_fn (locate_ctor (t));
+ }
else
{
tree binfo, base_binfo, field; int i;
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d18599b..cf6c056 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4823,7 +4823,7 @@ extern tree in_class_defaulted_default_constructor (tree);
extern bool user_provided_p (tree);
extern bool type_has_user_provided_constructor (tree);
extern bool type_has_user_provided_default_constructor (tree);
-extern bool synthesized_default_constructor_is_constexpr (tree);
+extern bool trivial_default_constructor_is_constexpr (tree);
extern bool type_has_constexpr_default_constructor (tree);
extern bool type_has_virtual_destructor (tree);
extern bool type_has_move_constructor (tree);
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 74a3bdb..5b24f8f 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -1187,7 +1187,7 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
&& (!copy_arg_p || cxx_dialect < cxx0x))
{
if (constexpr_p && sfk == sfk_constructor)
- *constexpr_p = synthesized_default_constructor_is_constexpr (ctype);
+ *constexpr_p = trivial_default_constructor_is_constexpr (ctype);
return;
}
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index fd96d70..bdc4cf2 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5881,8 +5881,9 @@ explain_invalid_constexpr_fn (tree fun)
static struct pointer_set_t *diagnosed;
tree body;
location_t save_loc;
- /* Only diagnose instantiations of constexpr templates. */
- if (!is_instantiation_of_constexpr (fun))
+ /* Only diagnose defaulted functions or instantiations. */
+ if (!DECL_DEFAULTED_FN (fun)
+ && !is_instantiation_of_constexpr (fun))
return;
if (diagnosed == NULL)
diagnosed = pointer_set_create ();
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-default-ctor.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-default-ctor.C
new file mode 100644
index 0000000..d3868b5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-default-ctor.C
@@ -0,0 +1,12 @@
+// { dg-options -std=c++0x }
+
+struct A {
+ int i;
+ constexpr A():i(42) { };
+};
+struct B: A { };
+constexpr int f(B b) { return b.i; }
+
+struct C { C(); }; // { dg-message "calls non-constexpr" }
+struct D: C { }; // { dg-message "no constexpr constructor" }
+constexpr int g(D d) { return 42; } // { dg-error "invalid type" }