re PR c++/45923 (constexpr diagnostics, more more)

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.

From-SVN: r175646
This commit is contained in:
Jason Merrill 2011-06-29 10:34:58 -04:00 committed by Jason Merrill
parent 874d29e5be
commit f732fa7b6c
12 changed files with 315 additions and 61 deletions

View file

@ -1,5 +1,25 @@
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.

View file

@ -6721,7 +6721,10 @@ build_cxx_call (tree fn, int nargs, tree *argarray)
{
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);

View file

@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see
#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. */
@ -4582,10 +4583,73 @@ finalize_literal_type_property (tree t)
{
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

View file

@ -4816,6 +4816,7 @@ extern bool type_has_virtual_destructor (tree);
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);
@ -5094,6 +5095,7 @@ extern void finish_thunk (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);
@ -5364,6 +5366,7 @@ extern tree maybe_constant_value (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 {

View file

@ -958,7 +958,15 @@ process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p,
&& !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;
@ -1037,7 +1045,12 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk,
/* 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))
@ -1071,8 +1084,9 @@ walk_field_subobs (tree fields, tree fnname, special_function_kind sfk,
/* 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,
@ -1175,6 +1189,7 @@ 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)
@ -1366,6 +1381,20 @@ maybe_explain_implicit_delete (tree decl)
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
@ -1581,7 +1610,12 @@ defaulted_late_check (tree fn)
&& 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;
}

View file

@ -3538,6 +3538,17 @@ emit_associated_thunks (tree fn)
}
}
/* 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
@ -3567,7 +3578,10 @@ expand_or_defer_fn_1 (tree fn)
/* 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;
}
@ -5299,6 +5313,7 @@ ensure_literal_type_for_constexpr_object (tree decl)
{
error ("the type %qT of constexpr variable %qD is not literal",
type, decl);
explain_non_literal_class (type);
return NULL;
}
}
@ -5365,8 +5380,11 @@ is_valid_constexpr_fn (tree fun, bool complain)
{
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))
@ -5376,8 +5394,11 @@ is_valid_constexpr_fn (tree fun, bool complain)
{
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. */
@ -5386,7 +5407,11 @@ is_valid_constexpr_fn (tree fun, bool complain)
{
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));
}
}
}
@ -5640,18 +5665,13 @@ constexpr_fn_retval (tree 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. */
/* 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);
@ -5666,12 +5686,28 @@ register_constexpr_fundef (tree fun, tree 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))
@ -5700,6 +5736,44 @@ register_constexpr_fundef (tree fun, tree 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. */
@ -6005,7 +6079,7 @@ cxx_eval_call_expression (const constexpr_call *old_call, tree t,
}
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;
@ -6020,11 +6094,8 @@ cxx_eval_call_expression (const constexpr_call *old_call, tree t,
{
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;
@ -7023,8 +7094,11 @@ cxx_eval_constant_expression (const constexpr_call *call, tree 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;
}
@ -7574,7 +7648,11 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
&& !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
@ -7588,12 +7666,7 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
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;
}
}
@ -7609,22 +7682,13 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
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;
}
@ -7853,8 +7917,11 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
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:

View file

@ -1,3 +1,12 @@
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.

View file

@ -13,7 +13,7 @@ struct B { B(); operator int(); };
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" }

View file

@ -0,0 +1,54 @@
// 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" }

View file

@ -88,7 +88,7 @@ struct resource {
}
};
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

View file

@ -19,5 +19,5 @@ struct C
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" }

View file

@ -28,4 +28,4 @@ struct D
C<D> c;
};
constexpr D d {}; // { dg-error "not a constexpr function" }
constexpr D d {}; // { dg-error "non-constexpr function" }