2011-06-29 Jason Merrill <jason@redhat.com>
+ PR c++/45923
+ * class.c (explain_non_literal_class): New.
+ (finalize_literal_type_property): Call it.
+ * cp-tree.h: Declare it.
+ * semantics.c (ensure_literal_type_for_constexpr_object): Call it.
+ (is_valid_constexpr_fn): Likewise.
+ (massage_constexpr_body): Split out from...
+ (register_constexpr_fundef): ...here.
+ (is_instantiation_of_constexpr): New.
+ (expand_or_defer_fn_1): Leave DECL_SAVED_TREE alone in that case.
+ (explain_invalid_constexpr_fn): New.
+ (cxx_eval_call_expression): Call it.
+ (potential_constant_expression_1): Likewise. Avoid redundant errors.
+ * method.c (process_subob_fn): Diagnose non-constexpr.
+ (walk_field_subobs): Likewise.
+ (synthesized_method_walk): Don't shortcut if we want diagnostics.
+ (explain_implicit_non_constexpr): New.
+ (defaulted_late_check): Use it.
+ * call.c (build_cxx_call): Remember location.
+
* method.c (maybe_explain_implicit_delete): Use pointer_set
instead of htab.
{
tree fndecl;
+ /* Remember roughly where this call is. */
+ location_t loc = EXPR_LOC_OR_HERE (fn);
fn = build_call_a (fn, nargs, argarray);
+ SET_EXPR_LOCATION (fn, loc);
/* If this call might throw an exception, note that fact. */
fndecl = get_callee_fndecl (fn);
#include "cgraph.h"
#include "tree-dump.h"
#include "splay-tree.h"
+#include "pointer-set.h"
/* The number of nested classes being processed. If we are not in the
scope of any class, this is zero. */
{
DECL_DECLARED_CONSTEXPR_P (fn) = false;
if (!DECL_TEMPLATE_INFO (fn))
- error ("enclosing class of %q+#D is not a literal type", fn);
+ {
+ error ("enclosing class of constexpr non-static member "
+ "function %q+#D is not a literal type", fn);
+ explain_non_literal_class (t);
+ }
}
}
+/* T is a non-literal type used in a context which requires a constant
+ expression. Explain why it isn't literal. */
+
+void
+explain_non_literal_class (tree t)
+{
+ static struct pointer_set_t *diagnosed;
+
+ if (!CLASS_TYPE_P (t))
+ return;
+ t = TYPE_MAIN_VARIANT (t);
+
+ if (diagnosed == NULL)
+ diagnosed = pointer_set_create ();
+ if (pointer_set_insert (diagnosed, t) != 0)
+ /* Already explained. */
+ return;
+
+ inform (0, "%q+T is not literal because:", t);
+ if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
+ inform (0, " %q+T has a non-trivial destructor", 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);
+ else
+ {
+ tree binfo, base_binfo, field; int i;
+ for (binfo = TYPE_BINFO (t), i = 0;
+ BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+ {
+ tree basetype = TREE_TYPE (base_binfo);
+ if (!CLASSTYPE_LITERAL_P (basetype))
+ {
+ inform (0, " base class %qT of %q+T is non-literal",
+ basetype, t);
+ explain_non_literal_class (basetype);
+ return;
+ }
+ }
+ for (field = TYPE_FIELDS (t); field; field = TREE_CHAIN (field))
+ {
+ tree ftype;
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
+ ftype = TREE_TYPE (field);
+ if (!literal_type_p (ftype))
+ {
+ inform (0, " non-static data member %q+D has "
+ "non-literal type", field);
+ if (CLASS_TYPE_P (ftype))
+ explain_non_literal_class (ftype);
+ }
+ }
+ }
+}
+
/* Check the validity of the bases and members declared in T. Add any
implicitly-generated functions (like copy-constructors and
assignment operators). Compute various flag bits (like
extern bool type_has_move_constructor (tree);
extern bool type_has_move_assign (tree);
extern bool type_build_ctor_call (tree);
+extern void explain_non_literal_class (tree);
extern void defaulted_late_check (tree);
extern bool defaultable_fn_check (tree);
extern void fixup_type_variants (tree);
extern void use_thunk (tree, bool);
extern bool trivial_fn_p (tree);
extern bool maybe_explain_implicit_delete (tree);
+extern void explain_implicit_non_constexpr (tree);
extern void synthesize_method (tree);
extern tree lazily_declare_fn (special_function_kind,
tree);
extern tree maybe_constant_init (tree);
extern bool is_sub_constant_expr (tree);
extern bool reduced_constant_expression_p (tree);
+extern void explain_invalid_constexpr_fn (tree);
extern VEC(tree,heap)* cx_error_context (void);
enum {
&& !DECL_TEMPLATE_INSTANTIATED (fn))
instantiate_decl (fn, /*defer_ok*/false, /*expl_class*/false);
if (!DECL_DECLARED_CONSTEXPR_P (fn))
- *constexpr_p = false;
+ {
+ *constexpr_p = false;
+ if (msg)
+ {
+ inform (0, "defaulted constructor calls non-constexpr "
+ "%q+D", fn);
+ explain_invalid_constexpr_fn (fn);
+ }
+ }
}
return;
/* FIXME will need adjustment for non-static data member
initializers. */
if (constexpr_p && !CLASS_TYPE_P (mem_type))
- *constexpr_p = false;
+ {
+ *constexpr_p = false;
+ if (msg)
+ inform (0, "defaulted default constructor does not "
+ "initialize %q+#D", field);
+ }
}
if (!CLASS_TYPE_P (mem_type))
/* The caller wants to generate an implicit declaration of SFK for CTYPE
which is const if relevant and CONST_P is set. If spec_p, trivial_p and
deleted_p are non-null, set their referent appropriately. If diag is
- true, we're being called from maybe_explain_implicit_delete to give
- errors. */
+ true, we're either being called from maybe_explain_implicit_delete to
+ give errors, or if constexpr_p is non-null, from
+ explain_invalid_constexpr_fn. */
static void
synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
resolution, so a constructor can be trivial even if it would otherwise
call a non-trivial constructor. */
if (expected_trivial
+ && !diag
&& (!copy_arg_p || cxx_dialect < cxx0x))
{
if (constexpr_p && sfk == sfk_constructor)
return false;
}
+/* DECL is a defaulted function which was declared constexpr. Explain why
+ it can't be constexpr. */
+
+void
+explain_implicit_non_constexpr (tree decl)
+{
+ tree parm_type = TREE_VALUE (FUNCTION_FIRST_USER_PARMTYPE (decl));
+ bool const_p = CP_TYPE_CONST_P (non_reference (parm_type));
+ bool dummy;
+ synthesized_method_walk (DECL_CLASS_CONTEXT (decl),
+ special_function_p (decl), const_p,
+ NULL, NULL, NULL, &dummy, true);
+}
+
/* Implicitly declare the special function indicated by KIND, as a
member of TYPE. For copy constructors and assignment operators,
CONST_P indicates whether these functions should take a const
&& DECL_DECLARED_CONSTEXPR_P (fn))
{
if (!CLASSTYPE_TEMPLATE_INSTANTIATION (ctx))
- error ("%qD cannot be declared as constexpr", fn);
+ {
+ error ("explicitly defaulted function %q+D cannot be declared "
+ "as constexpr because the implicit declaration is not "
+ "constexpr:", fn);
+ explain_implicit_non_constexpr (fn);
+ }
DECL_DECLARED_CONSTEXPR_P (fn) = false;
}
}
}
+/* Returns true iff FUN is an instantiation of a constexpr function
+ template. */
+
+static inline bool
+is_instantiation_of_constexpr (tree fun)
+{
+ return (DECL_TEMPLATE_INFO (fun)
+ && DECL_DECLARED_CONSTEXPR_P (DECL_TEMPLATE_RESULT
+ (DECL_TI_TEMPLATE (fun))));
+}
+
/* Generate RTL for FN. */
bool
/* We don't want to process FN again, so pretend we've written
it out, even though we haven't. */
TREE_ASM_WRITTEN (fn) = 1;
- DECL_SAVED_TREE (fn) = NULL_TREE;
+ /* If this is an instantiation of a constexpr function, keep
+ DECL_SAVED_TREE for explain_invalid_constexpr_fn. */
+ if (!is_instantiation_of_constexpr (fn))
+ DECL_SAVED_TREE (fn) = NULL_TREE;
return false;
}
{
error ("the type %qT of constexpr variable %qD is not literal",
type, decl);
+ explain_non_literal_class (type);
return NULL;
}
}
{
ret = false;
if (complain)
- error ("invalid type for parameter %d of constexpr "
- "function %q+#D", DECL_PARM_INDEX (parm), fun);
+ {
+ error ("invalid type for parameter %d of constexpr "
+ "function %q+#D", DECL_PARM_INDEX (parm), fun);
+ explain_non_literal_class (TREE_TYPE (parm));
+ }
}
if (!DECL_CONSTRUCTOR_P (fun))
{
ret = false;
if (complain)
- error ("invalid return type %qT of constexpr function %q+D",
- rettype, fun);
+ {
+ error ("invalid return type %qT of constexpr function %q+D",
+ rettype, fun);
+ explain_non_literal_class (rettype);
+ }
}
/* Check this again here for cxx_eval_call_expression. */
{
ret = false;
if (complain)
- error ("enclosing class of %q+#D is not a literal type", fun);
+ {
+ error ("enclosing class of constexpr non-static member "
+ "function %q+#D is not a literal type", fun);
+ explain_non_literal_class (DECL_CONTEXT (fun));
+ }
}
}
}
}
-/* 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.
- For constructor BODY is actually the TREE_LIST of the
- member-initializer list. */
+/* Subroutine of register_constexpr_fundef. BODY is the DECL_SAVED_TREE of
+ FUN; do the necessary transformations to turn it into a single expression
+ that we can store in the hash table. */
-tree
-register_constexpr_fundef (tree fun, tree body)
+static tree
+massage_constexpr_body (tree fun, tree body)
{
- constexpr_fundef entry;
- constexpr_fundef **slot;
-
if (DECL_CONSTRUCTOR_P (fun))
body = build_constexpr_constructor_member_initializers
(DECL_CONTEXT (fun), body);
if (TREE_CODE (body) == CLEANUP_POINT_EXPR)
body = TREE_OPERAND (body, 0);
body = constexpr_fn_retval (body);
- if (body == NULL_TREE || body == error_mark_node)
- {
- error ("body of constexpr function %qD not a return-statement", fun);
- DECL_DECLARED_CONSTEXPR_P (fun) = false;
- return NULL;
- }
+ }
+ return body;
+}
+
+/* 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.
+ For constructor BODY is actually the TREE_LIST of the
+ member-initializer list. */
+
+tree
+register_constexpr_fundef (tree fun, tree body)
+{
+ constexpr_fundef entry;
+ constexpr_fundef **slot;
+
+ body = massage_constexpr_body (fun, body);
+ if (body == NULL_TREE || body == error_mark_node)
+ {
+ error ("body of constexpr function %qD not a return-statement", fun);
+ DECL_DECLARED_CONSTEXPR_P (fun) = false;
+ return NULL;
}
if (!potential_rvalue_constant_expression (body))
return fun;
}
+/* FUN is a non-constexpr function called in a context that requires a
+ constant expression. If it comes from a constexpr template, explain why
+ the instantiation isn't constexpr. */
+
+void
+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))
+ return;
+ if (diagnosed == NULL)
+ diagnosed = pointer_set_create ();
+ if (pointer_set_insert (diagnosed, fun) != 0)
+ /* Already explained. */
+ return;
+
+ 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);
+ /* First check the declaration. */
+ if (is_valid_constexpr_fn (fun, true))
+ {
+ /* Then if it's OK, the body. */
+ if (DECL_DEFAULTED_FN (fun))
+ explain_implicit_non_constexpr (fun);
+ else
+ {
+ body = massage_constexpr_body (fun, DECL_SAVED_TREE (fun));
+ require_potential_rvalue_constant_expression (body);
+ }
+ }
+ input_location = save_loc;
+}
+
/* Objects of this type represent calls to constexpr functions
along with the bindings of parameters to their arguments, for
the purpose of compile time evaluation. */
}
if (TREE_CODE (fun) != FUNCTION_DECL)
{
- if (!allow_non_constant)
+ if (!allow_non_constant && !*non_constant_p)
error_at (loc, "expression %qE does not designate a constexpr "
"function", fun);
*non_constant_p = true;
{
if (!allow_non_constant)
{
- error_at (loc, "%qD is not a constexpr function", fun);
- if (DECL_TEMPLATE_INFO (fun)
- && DECL_DECLARED_CONSTEXPR_P (DECL_TEMPLATE_RESULT
- (DECL_TI_TEMPLATE (fun))))
- is_valid_constexpr_fn (fun, true);
+ error_at (loc, "call to non-constexpr function %qD", fun);
+ explain_invalid_constexpr_fn (fun);
}
*non_constant_p = true;
return t;
if (!literal_type_p (TREE_TYPE (t)))
{
if (!allow_non_constant)
- error ("temporary of non-literal type %qT in a "
- "constant expression", TREE_TYPE (t));
+ {
+ error ("temporary of non-literal type %qT in a "
+ "constant expression", TREE_TYPE (t));
+ explain_non_literal_class (TREE_TYPE (t));
+ }
*non_constant_p = true;
break;
}
&& !morally_constexpr_builtin_function_p (fun))
{
if (flags & tf_error)
- error ("%qD is not %<constexpr%>", fun);
+ {
+ error_at (EXPR_LOC_OR_HERE (t),
+ "call to non-constexpr function %qD", fun);
+ explain_invalid_constexpr_fn (fun);
+ }
return false;
}
/* A call to a non-static member function takes the address
if (is_this_parameter (x))
/* OK. */;
else if (!potential_constant_expression_1 (x, rval, flags))
- {
- if (flags & tf_error)
- error ("object argument is not a potential "
- "constant expression");
- return false;
- }
+ return false;
i = 1;
}
}
if (potential_constant_expression_1 (fun, rval, flags))
/* Might end up being a constant function pointer. */;
else
- {
- if (flags & tf_error)
- error ("%qE is not a function name", fun);
- return false;
- }
+ return false;
}
for (; i < nargs; ++i)
{
tree x = get_nth_callarg (t, i);
if (!potential_constant_expression_1 (x, rval, flags))
- {
- if (flags & tf_error)
- error ("argument in position %qP is not a "
- "potential constant expression", i);
- return false;
- }
+ return false;
}
return true;
}
if (!literal_type_p (TREE_TYPE (t)))
{
if (flags & tf_error)
- error ("temporary of non-literal type %qT in a "
- "constant expression", TREE_TYPE (t));
+ {
+ error ("temporary of non-literal type %qT in a "
+ "constant expression", TREE_TYPE (t));
+ explain_non_literal_class (TREE_TYPE (t));
+ }
return false;
}
case INIT_EXPR:
+2011-06-29 Jason Merrill <jason@redhat.com>
+
+ PR c++/45923
+ * g++.dg/cpp0x/constexpr-diag3.C: New.
+ * g++.dg/cpp0x/constexpr-diag1.C: Adjust error message.
+ * g++.dg/cpp0x/constexpr-ex1.C: Adjust error message.
+ * g++.dg/cpp0x/constexpr-friend.C: Adjust error message.
+ * g++.dg/cpp0x/constexpr-incomplete2.C: Adjust error message.
+
2011-06-29 Jason Merrill <jason@redhat.com>
* g++.dg/cpp0x/constexpr-is_literal.C: Adjust.
constexpr A<int> ai = { 42 };
constexpr int i = ai.f();
-constexpr int b = A<B>().f(); // { dg-error "not a constexpr function" }
+constexpr int b = A<B>().f(); // { dg-error "non-constexpr function" }
template <class T>
constexpr int f (T t) { return 42; } // { dg-error "parameter" }
--- /dev/null
+// PR c++/45923
+// { dg-options -std=c++0x }
+
+int f(int);
+
+template <class T>
+constexpr T g(T t) { return f(t); } // { dg-error "f.int" }
+
+int main()
+{
+ constexpr int i = g(1); // { dg-error "g.T" }
+}
+
+// --------------------
+
+struct complex // { dg-message "no constexpr constructor" }
+{
+ complex(double r, double i) : re(r), im(i) { }
+ constexpr double real() { return re; } // { dg-error "not a literal type" }
+ double imag() const { return im; }
+
+private:
+ double re;
+ double im;
+};
+
+constexpr complex co1(0, 1); // { dg-error "not literal" }
+constexpr double dd2 = co1.real(); // { dg-error "non-constexpr function" }
+
+// --------------------
+
+struct base // { dg-message "no constexpr constructor" }
+{
+ int _M_i;
+ base() : _M_i(5) { }
+};
+
+struct derived : public base // { dg-message "base class" }
+{
+ constexpr derived(): base() { } // { dg-error "non-constexpr function" }
+};
+
+constexpr derived obj; // { dg-error "not literal" }
+
+// --------------------
+
+struct Def
+{
+ int _M_i; // { dg-message "does not initialize" }
+
+ constexpr Def() = default; // { dg-error "implicit declaration is not constexpr" }
+};
+
+constexpr Def defobj; // { dg-error "uninitialized" }
}
};
constexpr resource f(resource d)
-{ return d; } // { dg-error "not .constexpr" }
+{ return d; } // { dg-error "non-constexpr" }
constexpr resource d = f(9); // { dg-error "resource" }
// 4.4 floating-point constant expressions
constexpr int i = f(C<int>());
constexpr int j = C<int>().m(C<int>());
-constexpr int k = C<double>().m(A()); // { dg-error "not a constexpr function" }
-constexpr int l = g(C<double>(),A()); // { dg-error "not a constexpr function" }
+constexpr int k = C<double>().m(A()); // { dg-error "non-constexpr function" }
+constexpr int l = g(C<double>(),A()); // { dg-error "non-constexpr function" }
C<D> c;
};
-constexpr D d {}; // { dg-error "not a constexpr function" }
+constexpr D d {}; // { dg-error "non-constexpr function" }