This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
C++ PATCH for constexpr
- From: Gabriel Dos Reis <gdr at cs dot tamu dot edu>
- To: jason at redhat dot com
- Cc: bkoz at redhat dot com, gcc-patches at gcc dot gnu dot org
- Date: Thu, 26 Nov 2009 19:39:11 -0600
- Subject: C++ PATCH for constexpr
Hi Jason,
Below is a revision of the constexpr patch. Please have a look
the the codes that involves AGGR_INIT_EXPR. I incorporated your earlier
comments, but you might want to double check in case I forgot a point.
The modification to the library will be submitted in a different patch,
once this is in and Benjamin clarifies how he wants to handle the
'constexpr' specifier bit (it does not change the ABI).
Summary:
This version implements the core of compile-time evaluation, incorporating
DR resolutions as discussed on the reflectors, and other
clarifications.
Now, the general plan:
(1) I maintain a table that maps constexpr functions to their bodies.
(2) since constexpr functions are pure, I maintain a compile-time
memoization table, and in the process we can detect circular
recursions.
This patch handles, I believe, almost all examples and all uses
that affect the standard library. Exceptions are templates, and the
request to accept reference types. I would prefer to handle those in a
separate patch once the basic evaluation machinery in accepted into
mainline.
The general idea is that compile time evaluation of expressions usually
produces values, e.g. _CST nodes or CONSTRUCTOR nodes (for arrays and
object of class types.). However, occasionally, we also have symbolic
values, e.g. (ADDR_EXPR var) where var has a static storage class.
We don't attempt to fold those. And we don't attempt to
fold further (POINTER_PLUS (ADDR_EXPR var) 1). Yet, we still maintain
that those expressions are `constant'. Consequently, we cannot just use
TREE_CONSTANT. So I introduced a flag taken from one of the spare bits
in tree_common to mark non-_CST that should not be folded further.
I thought the flag solution is less disturbing to existing fold
machinery than introducing a separate tree code. Also, the C front-end
c_fully_fold is inadequate. The other thing I needed to maintain is
that of type preservation. If we have an expression of type T, its
compile-time evaluation should also yield a value of type T.
In particular I consider CONST_DECL to be fully evaluated; I don't look
into their DECL_INITIAL (which might have different type). On the other
hand I do look into constexpr VAR_DECL's DECL_INITIAL.
Finally, as for the evaluation proper, I use very small environment that
consists in bindings function parameters to the values they receive in a
call. I do not maintain a call stack, as I'm not sure we really need
one for diagnostic purposes.
I do not know exactly what to do with __builtin_xxx functions when
testing whether an expression is a potential constant expressions or
not. When and how GCC fold those expressions depend on commandlines...
Best,
-- Gaby
gcc/ChangeLog
2009-11-26 Gabriel Dos Reis <gdr@cse.tamu.edu>
* tree.h (VAR_DECL_P): New predicate macro.
(FUNCTION_DECL_P): Likewise.
(tree_base::lang_flag_7): New language flag.
(tree_base::spare): Decrease precision by one.
(TREE_LANG_FLAG_7): New.
gcc/cp/ChangeLog
2009-11-26 Gabriel Dos Reis <gdr@cse.tamu.edu>
* parser.c (cp_parser_ctor_initializer_opt_and_function_body):
Check body of constexpr constructors.
* cp-tree.h (COMPILE_TIME_CONSTANT_P): New.
(VALID_FOR_STATIC_INITIALIZATION_P): Likewise.
(TYPE_ARRAY_P): Likewise.
(hash_constexpr_args): Declare.
(register_constexpr_fundef): Likewise.
(cxx_constant_value): Likewise.
(generalized_constant_expression_allowed): New.
* decl.c (validate_constexpr_redeclaration): New.
(duplicate_decls): Use it.
(cp_finish_decl): Validate constexpr bit.
(grokdeclarator): Check uses of constexpr specifier.
(maybe_save_function_definition): New.
(finish_function): Use it.
* class.c (check_bases): Accumulate literal type property from
base classes.
(check_field_decls): Same for non-static data members.
(finalize_literal_type_property): New.
(check_bases_and_members): Use it.
(finish_struct_1): Assume the class being processed is literal.
* typeck2.c (store_init_value): Fold initializers of
constexpr variables.
* pt.c (hash_constexpr_args): Define.
* semantics.c (ensure_literal_type_for_constexpr_object): Tidy.
(constexpr_fundef): New datatype.
(constexpr_fundef_table): New global table.
(constexpr_fundef_equal): New.
(constexpr_fundef_hash): Likewise.
(retrieve_constexpr_fundef): Likewise.
(validate_constexpr_fundecl): Tidy. Allow constexpr function
declarations that are not definitions.
(build_constexpr_constructor_member_initializers): New.
(register_constexpr_fundef): Define.
(constexpr_call): New datatype.
(constexpr_call_table): New global table.
(constexpr_call_hash): New.
(constexpr_call_equal): Likewise.
(maybe_initialize_constexpr_call_table): Likewise.
(is_this_parameter): Likewise.
(get_function_named_in_call): Likewise.
(get_nth_callarg): Likewise.
(lookup_parameter_binding): Likewise.
(cxx_eval_builtin_function_call): Likewise.
(cxx_bind_parameters_in_call): Likewise.
(cxx_eval_call_expression): Likewise.
(cxx_eval_unary_expression): Likewise.
(cxx_eval_binary_expression): Likewise.
(cxx_eval_conditional_expression): Likewise.
(cxx_eval_array_reference): Likewise.
(cxx_eval_component_reference): Likewise.
(cxx_eval_logical_expression): Likewise.
(cxx_eval_object_construction): Likewise.
(cxx_eval_constant_expression): Likewise.
(cxx_constant_value): Define.
(has_automatic_or_tls): New.
(potential_constant_expression): Likewise.
=== gcc/tree.h
==================================================================
--- gcc/tree.h (revision 154697)
+++ gcc/tree.h (patch const level 1)
@@ -105,6 +105,16 @@
#define DECL_P(CODE)\
(TREE_CODE_CLASS (TREE_CODE (CODE)) == tcc_declaration)
+/* Nonzero if NODE represents a VAR_DECL. */
+
+#define VAR_DECL_P(NODE) \
+ (TREE_CODE (NODE) == VAR_DECL)
+
+/* Nonzero if NODE represents a FUNCTION_DECL. */
+
+#define FUNCTION_DECL_P(NODE) \
+ (TREE_CODE (NODE) == FUNCTION_DECL)
+
/* Nonzero if DECL represents a VAR_DECL or FUNCTION_DECL. */
#define VAR_OR_FUNCTION_DECL_P(DECL)\
@@ -386,12 +396,13 @@
unsigned lang_flag_4 : 1;
unsigned lang_flag_5 : 1;
unsigned lang_flag_6 : 1;
+ unsigned lang_flag_7 : 1;
unsigned visited : 1;
unsigned packed_flag : 1;
unsigned user_align : 1;
- unsigned spare : 13;
+ unsigned spare : 12;
/* This field is only used with type nodes; the only reason it is present
in tree_base instead of tree_type is to save space. The size of the
@@ -1358,6 +1369,7 @@
#define TREE_LANG_FLAG_4(NODE) ((NODE)->base.lang_flag_4)
#define TREE_LANG_FLAG_5(NODE) ((NODE)->base.lang_flag_5)
#define TREE_LANG_FLAG_6(NODE) ((NODE)->base.lang_flag_6)
+#define TREE_LANG_FLAG_7(NODE) ((NODE)->base.lang_flag_7)
/* Define additional fields and accessors for nodes representing constants. */
=== gcc/cp/class.c
==================================================================
--- gcc/cp/class.c (revision 154697)
+++ gcc/cp/class.c (patch const level 1)
@@ -1264,6 +1264,11 @@
gcc_assert (COMPLETE_TYPE_P (basetype));
+ /* If any of the base class is non-literal, the whole class
+ becomes non-literal. */
+ if (!CLASSTYPE_LITERAL_P (basetype))
+ CLASSTYPE_LITERAL_P (t) = false;
+
/* Effective C++ rule 14. We only need to check TYPE_POLYMORPHIC_P
here because the case of virtual functions but non-virtual
dtor is handled in finish_struct_1. */
@@ -3004,6 +3009,11 @@
if (TREE_PRIVATE (x) || TREE_PROTECTED (x))
CLASSTYPE_NON_AGGREGATE (t) = 1;
+ /* If at least one non-static data member is non-literal, the whole
+ class becomes non-literal. */
+ if (!literal_type_p (type))
+ CLASSTYPE_LITERAL_P (t) = false;
+
/* A standard-layout class is a class that:
...
has the same access control (Clause 11) for all non-static data members,
@@ -4317,6 +4327,45 @@
return has_two_argument_delete_p;
}
+
+/* Finish computing the `literal type' property of class type T.
+
+ At this point, we have already processed base classes and
+ non-static data members. We need to check whether the copy
+ constructor is trivial, the destructor is trivial, and there
+ is a trivial default constructor or at least one constexpr
+ constructor other than the copy constructor. */
+
+static void
+finalize_literal_type_property (tree t)
+{
+ if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
+ CLASSTYPE_LITERAL_P (t) = false;
+ if (!TYPE_HAS_TRIVIAL_INIT_REF (t))
+ CLASSTYPE_LITERAL_P (t) = false;
+ if (CLASSTYPE_LITERAL_P (t) && !TYPE_HAS_TRIVIAL_DFLT (t)
+ && CLASSTYPE_METHOD_VEC (t) != NULL)
+ {
+ tree ctors = CLASSTYPE_CONSTRUCTORS (t);
+ bool found_one = false;
+ for (; !found_one && ctors != NULL; ctors = OVL_NEXT (ctors))
+ {
+ tree ctor = OVL_CURRENT (ctors);
+ /* If this class a constexpr constructor template, then the class
+ is literal if at least one instantiation is 'constexpr'.
+ If no such instantiation exists, there is no way to use
+ the literalness of the class. Consequently, we can accept
+ constexpr constructor template. */
+ if (DECL_COPY_CONSTRUCTOR_P (ctor)
+ || DECL_CLONED_FUNCTION_P (ctor))
+ continue;
+ if (DECL_DECLARED_CONSTEXPR_P (STRIP_TEMPLATE (ctor)))
+ found_one = true;
+ }
+ CLASSTYPE_LITERAL_P (t) = found_one;
+ }
+}
+
/* 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
@@ -4472,6 +4521,10 @@
CLASSTYPE_NON_AGGREGATE (t) = 1;
}
+ /* Compute the `literal type' property before we get to
+ do anything with non-static member functions. */
+ finalize_literal_type_property (t);
+
/* Create the in-charge and not-in-charge variants of constructors
and destructors. */
clone_constructors_and_destructors (t);
@@ -5291,6 +5344,7 @@
CLASSTYPE_EMPTY_P (t) = 1;
CLASSTYPE_NEARLY_EMPTY_P (t) = 1;
CLASSTYPE_CONTAINS_EMPTY_CLASS_P (t) = 0;
+ CLASSTYPE_LITERAL_P (t) = true;
/* Do end-of-class semantic processing: checking the validity of the
bases and members and add implicitly generated methods. */
=== gcc/cp/decl.c
==================================================================
--- gcc/cp/decl.c (revision 154697)
+++ gcc/cp/decl.c (patch const level 1)
@@ -1099,6 +1099,25 @@
}
}
+/* Return true if both OLD_DECL and NEW_DECL agrees on constexprnes.
+ Otherwise issue diagnostics. */
+
+static bool
+validate_constexpr_redeclaration (tree old_decl, tree new_decl)
+{
+ old_decl = STRIP_TEMPLATE (old_decl);
+ new_decl = STRIP_TEMPLATE (new_decl);
+ if (!VAR_OR_FUNCTION_DECL_P (old_decl)
+ || !VAR_OR_FUNCTION_DECL_P (new_decl))
+ return true;
+ if (DECL_DECLARED_CONSTEXPR_P (old_decl)
+ == DECL_DECLARED_CONSTEXPR_P (new_decl))
+ return true;
+ error ("redeclaration %qD differs in %<constexpr%>", new_decl);
+ error ("from previous declaration %q+D", old_decl);
+ return false;
+}
+
#define GNU_INLINE_P(fn) (DECL_DECLARED_INLINE_P (fn) \
&& lookup_attribute ("gnu_inline", \
DECL_ATTRIBUTES (fn)))
@@ -1576,6 +1595,9 @@
warn about it. */
warn_extern_redeclared_static (newdecl, olddecl);
+ if (!validate_constexpr_redeclaration (olddecl, newdecl))
+ return error_mark_node;
+
/* We have committed to returning 1 at this point. */
if (TREE_CODE (newdecl) == FUNCTION_DECL)
{
@@ -5606,6 +5628,12 @@
}
}
+ if (!processing_template_decl
+ && FUNCTION_DECL_P (decl)
+ && !DECL_CLONED_FUNCTION_P (decl)
+ && DECL_DECLARED_CONSTEXPR_P (decl))
+ validate_constexpr_fundecl (decl);
+
if (init && TREE_CODE (decl) == FUNCTION_DECL)
{
tree clone;
@@ -5648,7 +5676,9 @@
DECL_INITIAL (decl) = NULL_TREE;
}
- if (init && init_const_expr_p && TREE_CODE (decl) == VAR_DECL)
+ if (init
+ && (init_const_expr_p || DECL_DECLARED_CONSTEXPR_P (decl))
+ && VAR_DECL_P (decl))
{
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = 1;
if (DECL_INTEGRAL_CONSTANT_VAR_P (decl))
@@ -5770,7 +5800,7 @@
if (init)
{
DECL_NONTRIVIALLY_INITIALIZED_P (decl) = 1;
- if (init_const_expr_p)
+ if (init_const_expr_p || DECL_DECLARED_CONSTEXPR_P (decl))
{
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = 1;
if (DECL_INTEGRAL_CONSTANT_VAR_P (decl))
@@ -7906,6 +7936,12 @@
if (name == NULL)
name = decl_context == PARM ? "parameter" : "type name";
+ if (constexpr_p && declspecs->specs[(int)ds_typedef])
+ {
+ error ("%<constexpr%> cannot appear in a typedef declaration");
+ return error_mark_node;
+ }
+
/* If there were multiple types specified in the decl-specifier-seq,
issue an error message. */
if (declspecs->multiple_types_p)
@@ -9216,6 +9252,10 @@
int publicp = 0;
tree function_context;
+ if (constexpr_p && !staticp && !friendp
+ && sfk != sfk_constructor && sfk != sfk_destructor)
+ memfn_quals |= TYPE_QUAL_CONST;
+
if (friendp == 0)
{
if (ctype == NULL_TREE)
@@ -9271,7 +9311,10 @@
return error_mark_node;
}
if (constexpr_p)
- error ("a destructor cannot be %<constexpr%>");
+ {
+ error ("a destructor cannot be %<constexpr%>");
+ return error_mark_node;
+ }
}
else if (sfk == sfk_constructor && friendp)
{
@@ -11594,10 +11637,6 @@
/* In a function definition, arg types must be complete. */
require_complete_types_for_parms (current_function_parms);
- /* constexpr functions must have literal argument types and
- literal return type. */
- validate_constexpr_fundecl (decl);
-
if (dependent_type_p (return_type))
return;
if (!COMPLETE_OR_VOID_TYPE_P (return_type)
@@ -12365,6 +12404,30 @@
return block;
}
+/* Subroutine of finish_function.
+ Save the body of constexpr functions for possible
+ future compile time evaluation. */
+
+static void
+maybe_save_function_definition (tree fun)
+{
+ if (!processing_template_decl
+ && DECL_DECLARED_CONSTEXPR_P (fun)
+ && !DECL_CLONED_FUNCTION_P (fun))
+ {
+ if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fun)
+ && !DECL_CONSTRUCTOR_P (fun)
+ && !literal_type_p (DECL_CONTEXT (fun)))
+ {
+ error ("containing class of %qD is not a literal type",
+ DECL_CONTEXT (fun));
+ DECL_DECLARED_CONSTEXPR_P (fun) = false;
+ return;
+ }
+ register_constexpr_fundef (fun, DECL_SAVED_TREE (fun));
+ }
+}
+
/* Finish up a function declaration and compile that function
all the way to assembler language output. The free the storage
for the function definition.
@@ -12485,6 +12548,10 @@
of curly braces for a function. */
gcc_assert (stmts_are_full_exprs_p ());
+ /* Save constexpr function body before it gets munged by
+ the NRV transformation. */
+ maybe_save_function_definition (fndecl);
+
/* Set up the named return value optimization, if we can. Candidate
variables are selected in check_return_expr. */
if (current_function_return_value)
=== gcc/cp/cp-tree.h
==================================================================
--- gcc/cp/cp-tree.h (revision 154697)
+++ gcc/cp/cp-tree.h (patch const level 1)
@@ -113,6 +113,7 @@
6: IDENTIFIER_REPO_CHOSEN (in IDENTIFIER_NODE)
DECL_CONSTRUCTION_VTABLE_P (in VAR_DECL)
TYPE_MARKED_P (in _TYPE)
+ 7: COMPILE_TIME_CONSTANT_P (in _EXPR or _REF)
Usage of TYPE_LANG_FLAG_?:
0: TYPE_DEPENDENT_P
@@ -2141,6 +2142,21 @@
&& INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (NODE)) \
&& DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (NODE))
+/* True if the expression tree NODE represents an object that can
+ be taken apart at compile time. This is not to be confused with
+ link-time constants or load-time constants. A compiler constant
+ may still be an expression that the middle end may be able to
+ reduce further. */
+#define COMPILE_TIME_CONSTANT_P(NODE) \
+ (TREE_LANG_FLAG_7 (NODE))
+
+/* Same as COMPILE_TIME_CONSTANT_P, except that it includes literal
+ values too, such as INTEGER_CST, PTRMEM_CST, or address of
+ variables with static storage. */
+#define VALID_FOR_STATIC_INITIALIZATION_P(NODE) \
+ (CONSTANT_CLASS_P (NODE) || COMPILE_TIME_CONSTANT_P (NODE))
+
+
/* Nonzero if the DECL was initialized in the class definition itself,
rather than outside the class. This is used for both static member
VAR_DECLS, and FUNCTION_DECLS that are defined in the class. */
@@ -3123,6 +3139,10 @@
#define TYPE_PTR_P(NODE) \
(TREE_CODE (NODE) == POINTER_TYPE)
+/* Return true if NODE is an array type. */
+#define TYPE_ARRAY_P(NODE) \
+ (TREE_CODE (NODE) == ARRAY_TYPE)
+
/* Returns true if NODE is an object type:
[basic.types]
@@ -4929,6 +4949,8 @@
extern tree get_function_template_decl (const_tree);
extern tree resolve_nondeduced_context (tree);
+extern hashval_t hash_constexpr_args (tree, hashval_t);
+
/* in repo.c */
extern void init_repo (void);
extern int repo_emit_p (tree);
@@ -5054,8 +5076,14 @@
extern void finish_cleanup (tree, tree);
extern bool literal_type_p (tree);
extern tree validate_constexpr_fundecl (tree);
+extern tree register_constexpr_fundef (tree, tree);
extern tree ensure_literal_type_for_constexpr_object (tree);
+extern tree cxx_constant_value (tree);
+/* True if C++0x-style conctant expresions are allowed. */
+#define generalized_constant_expression_allowed() \
+ ((cxx_dialect != cxx98) || in_system_header)
+
enum {
BCS_NO_SCOPE = 1,
BCS_TRY_BLOCK = 2,
=== gcc/cp/typeck2.c
==================================================================
--- gcc/cp/typeck2.c (revision 154697)
+++ gcc/cp/typeck2.c (patch const level 1)
@@ -632,6 +632,11 @@
/* Digest the specified initializer into an expression. */
value = digest_init_flags (type, init, flags);
+
+ /* constexpr variables need to have their initializers reduced. */
+ if (DECL_DECLARED_CONSTEXPR_P (decl) && value != error_mark_node)
+ value = cxx_constant_value (value);
+
/* If the initializer is not a constant, fill in DECL_INITIAL with
the bits that are constant, and then return an expression that
will perform the dynamic initialization. */
=== gcc/cp/pt.c
==================================================================
--- gcc/cp/pt.c (revision 154697)
+++ gcc/cp/pt.c (patch const level 1)
@@ -1576,6 +1576,15 @@
return 0;
}
+/* Return a hash value for arguments ARGS in a call to a constexpr
+ function. INIT is an initial hash value to combine with. */
+
+hashval_t
+hash_constexpr_args (tree arg, hashval_t init)
+{
+ return iterative_hash_template_arg (arg, init);
+}
+
/* Unregister the specialization SPEC as a specialization of TMPL.
Replace it with NEW_SPEC, if NEW_SPEC is non-NULL. Returns true
if the SPEC was listed as a specialization of TMPL.
=== gcc/cp/semantics.c
==================================================================
--- gcc/cp/semantics.c (revision 154697)
+++ gcc/cp/semantics.c (patch const level 1)
@@ -5248,6 +5248,7 @@
return 0;
}
+
/* Return true if T is a literal type. */
bool
@@ -5270,7 +5271,7 @@
ensure_literal_type_for_constexpr_object (tree decl)
{
tree type = TREE_TYPE (decl);
- if (TREE_CODE (decl) == VAR_DECL && DECL_DECLARED_CONSTEXPR_P (decl)
+ if (VAR_DECL_P (decl) && DECL_DECLARED_CONSTEXPR_P (decl)
&& !processing_template_decl && !literal_type_p (type))
{
error ("the type %qT of constexpr variable %qD is not literal",
@@ -5280,6 +5281,57 @@
return decl;
}
+/* Representationof entries in the constexpr function definition table. */
+
+typedef struct GTY(()) constexpr_fundef {
+ tree decl;
+ tree parms;
+ tree body;
+} constexpr_fundef;
+
+/* This table holds all constexpr function definitions seen in
+ the current translation unit. */
+
+static GTY ((param_is (constexpr_fundef))) htab_t constexpr_fundef_table;
+
+static bool potential_constant_expression (tree, tsubst_flags_t);
+
+/* Utility function used for managing the constexpr function table.
+ Return true if the entries pointed to by P and Q are for the
+ same constexpr function. */
+
+static inline int
+constexpr_fundef_equal (const void *p, const void *q)
+{
+ const constexpr_fundef *lhs = (const constexpr_fundef *) p;
+ const constexpr_fundef *rhs = (const constexpr_fundef *) q;
+ return lhs->decl == rhs->decl;
+}
+
+
+/* Utility function used for managing the constexpr function table.
+ Return a hash value for the entry pointed to by Q. */
+
+static inline hashval_t
+constexpr_fundef_hash (const void *p)
+{
+ const constexpr_fundef *fundef = (const constexpr_fundef *) p;
+ return DECL_UID (fundef->decl);
+}
+
+/* Return a previously saved definition of function FUN. */
+
+static constexpr_fundef *
+retrieve_constexpr_fundef (tree fun)
+{
+ constexpr_fundef fundef = { NULL, NULL, NULL };
+ if (constexpr_fundef_table == NULL)
+ return NULL;
+
+ fundef.decl = fun;
+ return (constexpr_fundef *) htab_find (constexpr_fundef_table, &fundef);
+}
+
/* Return non-null if FUN certainly designates a valid constexpr function
declaration. Otherwise return NULL. Issue appropriate diagnostics
if necessary. Note that we only check the declaration, not the body
@@ -5290,45 +5342,1049 @@
{
tree rettype = NULL;
tree parm = NULL;
+ constexpr_fundef entry;
+ constexpr_fundef **slot;
- /* Don't bother if FUN is not marked constexpr. */
- if (!DECL_DECLARED_CONSTEXPR_P (fun))
- return NULL;
-
- /* For a function template, we have absolutely no guarantee that all
- instantiations will be constexpr. */
- if (TREE_CODE (fun) == TEMPLATE_DECL)
- return NULL;
-
parm = FUNCTION_FIRST_USER_PARM (fun);
for (; parm != NULL; parm = TREE_CHAIN (parm))
+ if (!literal_type_p (TREE_TYPE (parm)))
+ {
+ error ("parameter %q#D is not of literal type", parm);
+ DECL_DECLARED_CONSTEXPR_P (fun) = false;
+ return NULL;
+ }
+
+ if (!DECL_CONSTRUCTOR_P (fun))
{
- tree type = TREE_TYPE (parm);
- if (dependent_type_p (type))
- return NULL;
- if (!literal_type_p (type))
+ rettype = TREE_TYPE (TREE_TYPE (fun));
+ if (!literal_type_p (rettype))
{
- error ("parameter %q#D is not of literal type", parm);
+ error ("return type %qT of function %qD is not a literal type",
+ rettype, fun);
+ DECL_DECLARED_CONSTEXPR_P (fun) = false;
return NULL;
}
}
+ /* Create the constexpr function table if necessary. */
+ if (constexpr_fundef_table == NULL)
+ constexpr_fundef_table = htab_create_ggc (101,
+ constexpr_fundef_hash,
+ constexpr_fundef_equal,
+ ggc_free);
+ entry.decl = fun;
+ /* We don't store parameters at this point. The parameters that
+ matter are the ones we see in the definition.
+ register_constexpr_fundef will store them. */
+ entry.parms = NULL;
+ entry.body = NULL;
+ slot = (constexpr_fundef **)
+ htab_find_slot (constexpr_fundef_table, &entry, INSERT);
+ if (*slot == NULL)
+ {
+ *slot = GGC_NEW (constexpr_fundef);
+ **slot = entry;
+ }
+ return fun;
+}
+
+/* Build compile-time evalable representations of member-initializer list
+ for a constexpr constructor. */
+
+static tree
+build_constexpr_constructor_member_initializers (tree type, tree body)
+{
+ tree_stmt_iterator i;
+ tree inits = NULL;
+ if (TREE_CODE (body) == BIND_EXPR)
+ body = BIND_EXPR_BODY (body);
+ gcc_assert (TREE_CODE (body) == STATEMENT_LIST);
+ for (i = tsi_start (body); !tsi_end_p (i); tsi_next (&i))
+ {
+ tree x = tsi_stmt (i);
+ tree member;
+ gcc_assert (TREE_CODE (x) == CLEANUP_POINT_EXPR);
+ x = TREE_OPERAND (x, 0);
+ gcc_assert (TREE_CODE (x) == EXPR_STMT);
+ x = TREE_OPERAND (x, 0);
+ gcc_assert (TREE_CODE (x) == CONVERT_EXPR);
+ x = TREE_OPERAND (x, 0);
+ gcc_assert (TREE_CODE (x) == INIT_EXPR);
+ member = TREE_OPERAND (x, 0);
+ if (TREE_CODE (member) == COMPONENT_REF)
+ member = TREE_OPERAND (member, 1);
+ inits = tree_cons (member, unshare_expr (TREE_OPERAND (x, 1)), inits);
+ }
+ return build1 (CTOR_INITIALIZER, type, nreverse (inits));
+}
+
+/* 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 *fundef = retrieve_constexpr_fundef (fun);
+ gcc_assert (fundef != NULL && fundef->body == NULL);
+
if (DECL_CONSTRUCTOR_P (fun))
- return fun;
+ body = build_constexpr_constructor_member_initializers
+ (DECL_CONTEXT (fun), body);
+ else
+ {
+ if (TREE_CODE (body) == EH_SPEC_BLOCK)
+ body = EH_SPEC_STMTS (body);
+ if (TREE_CODE (body) == CLEANUP_POINT_EXPR)
+ body = TREE_OPERAND (body, 0);
+ if (TREE_CODE (body) != RETURN_EXPR)
+ {
+ error ("body of constexpr function %qD not a return-statement", fun);
+ DECL_DECLARED_CONSTEXPR_P (fun) = false;
+ return NULL;
+ }
+ body = unshare_expr (TREE_OPERAND (body, 0));
+ }
- rettype = TREE_TYPE (TREE_TYPE (fun));
- if (dependent_type_p (rettype))
- return NULL;
- if (!literal_type_p (rettype))
+ if (!potential_constant_expression (body, tf_error))
{
- error ("return type %qT of function %qD is not a literal type",
- TREE_TYPE (TREE_TYPE (fun)), fun);
+ DECL_DECLARED_CONSTEXPR_P (fun) = false;
return NULL;
}
+ fundef->parms = DECL_ARGUMENTS (fun);
+ fundef->body = body;
return fun;
}
+/* 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. */
+typedef struct GTY(()) constexpr_call {
+ /* Description of the constexpr function definition. */
+ constexpr_fundef *fundef;
+ /* Parameter bindings enironment. A TREE_LIST where each TREE_PURPOSE
+ is a parameter _DECL and the TREE_VALUE is the value of the parameter.
+ Note: This arrangement is made to accomodate the use of
+ iterative_hash_template_args (see pt.c). If you change this
+ representation, also change the implementation of the function
+ hash_constexpr_args. */
+ tree bindings;
+ /* Result of the call.
+ NULL means the call is being evaluated.
+ error_mark_node means that the evaluation was erroneous
+ otherwise, the actuall value of the call. */
+ tree result;
+} constexpr_call;
+
+/* A table of all constexpr calls that have been evaluated by the
+ compiler in this translation unit. */
+
+static GTY ((param_is (constexpr_call))) htab_t constexpr_call_table;
+
+static tree cxx_eval_constant_expression (const constexpr_call *, tree);
+
+/* Compute a hash value for a constexpr call representation. */
+
+static hashval_t
+constexpr_call_hash (const void *p)
+{
+ const constexpr_call *info = (const constexpr_call *) p;
+ return hash_constexpr_args (info->bindings,
+ constexpr_fundef_hash (info->fundef));
+}
+
+/* Return 1 if the objects pointed to by P and Q represent the same
+ call to a constexpr function with same set of argument list.
+ Otherwise, return 0. */
+
+static int
+constexpr_call_equal(const void *p, const void *q)
+{
+ const constexpr_call *lhs = (const constexpr_call *) p;
+ const constexpr_call *rhs = (const constexpr_call *) q;
+ tree lhs_bindings;
+ tree rhs_bindings;
+ if (lhs == rhs)
+ return 1;
+ if (!constexpr_fundef_equal (lhs->fundef, rhs->fundef))
+ return 0;
+ lhs_bindings = lhs->bindings;
+ rhs_bindings = rhs->bindings;
+ while (lhs_bindings != NULL && rhs_bindings != NULL)
+ {
+ tree lhs_arg = TREE_VALUE (lhs_bindings);
+ tree rhs_arg = TREE_VALUE (rhs_bindings);
+ if (!same_type_p (TREE_TYPE (lhs_arg), TREE_TYPE (rhs_arg)))
+ return 0;
+ if (!cp_tree_equal (lhs_arg, rhs_arg))
+ return 0;
+ lhs_bindings = TREE_CHAIN (lhs_bindings);
+ rhs_bindings = TREE_CHAIN (rhs_bindings);
+ }
+ return lhs_bindings == rhs_bindings;
+}
+
+/* Initialized the constexpr call table, if needed. */
+
+static void
+maybe_initialize_constexpr_call_table (void)
+{
+ if (constexpr_call_table == NULL)
+ constexpr_call_table = htab_create_ggc (101,
+ constexpr_call_hash,
+ constexpr_call_equal,
+ ggc_free);
+}
+
+/* Return true if T designate the implied `this' parameter. */
+
+static inline bool
+is_this_parameter (tree t)
+{
+ return DECL_P (t) && DECL_NAME (t) == this_identifier;
+}
+
+/* We have an expression tree T that represents a call, either CALL_EXPR
+ or AGGR_INIT_EXPR. If the call is lexically to a named function,
+ retrun the _DECL for that function. */
+
+static tree
+get_function_named_in_call (tree t)
+{
+ tree fun = NULL;
+ switch (TREE_CODE (t))
+ {
+ case CALL_EXPR:
+ fun = CALL_EXPR_FN (t);
+ break;
+
+ case AGGR_INIT_EXPR:
+ fun = AGGR_INIT_EXPR_FN (t);
+ break;
+
+ default:
+ gcc_unreachable();
+ break;
+ }
+ if (TREE_CODE (fun) == ADDR_EXPR
+ && FUNCTION_DECL_P (TREE_OPERAND (fun, 0)))
+ fun = TREE_OPERAND (fun, 0);
+ return fun;
+}
+
+/* We have an expression tree T that represents a call, either CALL_EXPR
+ or AGGR_INIT_EXPR. Return the Nth argument. */
+
+static inline tree
+get_nth_callarg (tree t, int n)
+{
+ switch (TREE_CODE (t))
+ {
+ case CALL_EXPR:
+ return CALL_EXPR_ARG (t, n);
+
+ case AGGR_INIT_EXPR:
+ return AGGR_INIT_EXPR_ARG (t, n);
+
+ default:
+ gcc_unreachable();
+ return NULL;
+ }
+}
+
+
+/* Look up the binding of the function parameter T in a constexpr
+ function call context CALL. */
+
+static tree
+lookup_parameter_binding (const constexpr_call *call, tree t)
+{
+ tree b;
+ for (b = call->bindings; b != NULL; b = TREE_CHAIN (b))
+ if (TREE_PURPOSE (b) == t)
+ return TREE_VALUE (b);
+
+ gcc_unreachable();
+ return error_mark_node;
+}
+
+/* Attempt to evaluate T which represents a call to a builtin function.
+ We assume here that all builtin functions evaluate to scalar types
+ represented by _CST nodes. */
+
+static tree
+cxx_eval_builtin_function_call (const constexpr_call *call, tree t)
+{
+ const int nargs = call_expr_nargs (t);
+ tree *args = (tree *) alloca (nargs * sizeof (tree));
+ tree new_call;
+ tree v;
+ int i;
+ for (i = 0; i < nargs; ++i)
+ {
+ args[i] = cxx_eval_constant_expression (call, CALL_EXPR_ARG (t, i));
+ if (args[i] == error_mark_node)
+ return args[i];
+ }
+ new_call = build_call_array_loc (EXPR_LOCATION (t), TREE_TYPE (t),
+ CALL_EXPR_FN (t), nargs, args);
+ v = fold (new_call);
+ if (new_call == v || !CONSTANT_CLASS_P (v))
+ return error_mark_node;
+ return v;
+}
+
+/* Subroutine of cxx_eval_call_expression.
+ We are processing a call expression (either CALL_EXPR or
+ AGGR_INIT_EXPR) in the call context of OLD_CALL. Evaluate
+ all arguments and bind their values to correspondings
+ parameters, making up the NEW_CALL context. */
+
+static bool
+cxx_bind_parameters_in_call (const constexpr_call *old_call, tree t,
+ constexpr_call *new_call)
+{
+ const int nargs = call_expr_nargs (t);
+ tree parms = new_call->fundef->parms;
+ tree fun = new_call->fundef->decl;
+ int i;
+ for (i = 0; i < nargs; ++i, parms = TREE_CHAIN (parms))
+ {
+ tree arg;
+ /* For member function, the first argument is a pointer to the implied
+ object. And for an object contruction, don't bind `this' before
+ it is fully constructed. */
+ if (i == 0 && TREE_CODE (t) == AGGR_INIT_EXPR)
+ continue;
+ else if (i == 0 && DECL_NONSTATIC_MEMBER_P (fun))
+ {
+ tree x = get_nth_callarg (t, i);
+ if (is_this_parameter (x))
+ arg = lookup_parameter_binding (old_call, x);
+ else
+ {
+ if (TREE_CODE (x) == ADDR_EXPR)
+ x = TREE_OPERAND (x, 0);
+ arg = cxx_eval_constant_expression (old_call, x);
+ }
+ }
+ else
+ arg = cxx_eval_constant_expression (old_call, get_nth_callarg (t, i));
+ if (arg == error_mark_node)
+ return false;
+ new_call->bindings = tree_cons (parms, arg, new_call->bindings);
+ }
+ return true;
+}
+
+/* Subroutine of cxx_eval_constant_expression.
+ Evaluate the call expression tree T in the context of OLD_CALL expression
+ evaluation. */
+
+static tree
+cxx_eval_call_expression (const constexpr_call *old_call, tree t)
+{
+ tree fun = get_function_named_in_call (t);
+ constexpr_call new_call = { NULL, NULL, NULL };
+ constexpr_call **slot;
+ /* The call must be to a constexpr function.
+ FIXME: check the standard. */
+ if (!DECL_P (fun))
+ {
+ error ("expression %qE does not designate a constexpr function", fun);
+ return error_mark_node;
+ }
+ if (DECL_CLONED_FUNCTION_P (fun))
+ fun = DECL_CLONED_FUNCTION (fun);
+ if (is_builtin_fn (fun))
+ return cxx_eval_builtin_function_call (old_call, t);
+ if (!DECL_DECLARED_CONSTEXPR_P (fun))
+ {
+ error ("%qD is not a constexpr function", fun);
+ return error_mark_node;
+ }
+
+ /* If in direct recursive call, optimize definition search. */
+ if (old_call != NULL && old_call->fundef->decl == fun)
+ new_call.fundef = old_call->fundef;
+ else
+ {
+ new_call.fundef = retrieve_constexpr_fundef (fun);
+ if (new_call.fundef == NULL || new_call.fundef->body == NULL)
+ {
+ error ("constexpr %qD used before its definition", fun);
+ return error_mark_node;
+ }
+ }
+ if (!cxx_bind_parameters_in_call (old_call, t, &new_call))
+ return error_mark_node;
+
+
+ /* If we have seen this call before, we are done. */
+ maybe_initialize_constexpr_call_table ();
+ slot = (constexpr_call **)
+ htab_find_slot (constexpr_call_table, &new_call, INSERT);
+ if (*slot != NULL)
+ {
+ /* Calls which are in progress have their result set to NULL
+ so that we can detect circular dependencies. */
+ if ((*slot)->result == NULL)
+ {
+ error ("call to %qE has circular dependency", fun);
+ (*slot)->result = error_mark_node;
+ }
+ return (*slot)->result;
+ }
+ new_call.result =
+ cxx_eval_constant_expression(&new_call, new_call.fundef->body);
+ *slot = GGC_NEW (constexpr_call);
+ **slot = new_call;
+ return new_call.result;
+}
+
+/* Subroutine of cxx_eval_constant_expression.
+ Attempt to reduce the unary expression tree T to a compile time value.
+ If successful, return the value. Otherwise issue a diagnostic
+ and return error_mark_node. */
+
+static tree
+cxx_eval_unary_expression (const constexpr_call *call, tree t)
+{
+ tree arg = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0));
+ if (arg == error_mark_node)
+ return arg;
+ arg = fold_if_not_in_template (build1 (TREE_CODE (t), TREE_TYPE (t), arg));
+ if (VALID_FOR_STATIC_INITIALIZATION_P (arg))
+ return arg;
+ sorry ("could not evaluate %qE to a value", arg);
+ return error_mark_node;
+}
+
+/* Subroutine of cxx_eval_constant_expression.
+ Like cxx_eval_unary_expression, except for binary expressions. */
+
+static tree
+cxx_eval_binary_expression (const constexpr_call *call, tree t)
+{
+ tree lhs = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0));
+ tree rhs = cxx_eval_constant_expression (call, TREE_OPERAND (t, 1));
+ if (lhs == error_mark_node || rhs == error_mark_node)
+ return error_mark_node;
+ t = fold_if_not_in_template
+ (build2 (TREE_CODE (t), TREE_TYPE (t), lhs, rhs));
+ if (VALID_FOR_STATIC_INITIALIZATION_P (t))
+ return t;
+ sorry ("could not evaluate %qE to a value", t);
+ return error_mark_node;
+}
+
+/* Subroutine of cxx_eval_constant_expression.
+ Attempt to evaluate condition expressions. Dead branches are not
+ looked into. */
+
+static tree
+cxx_eval_conditional_expression (const constexpr_call *call, tree t)
+{
+ tree val = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0));
+ if (val == error_mark_node)
+ return val;
+ if (val == boolean_true_node)
+ return cxx_eval_constant_expression (call, TREE_OPERAND (t, 1));
+ gcc_assert (val == boolean_false_node);
+ return cxx_eval_constant_expression (call, TREE_OPERAND (t, 2));
+}
+
+/* Subroutine of cxx_eval_constant_expression.
+ Attempt to reduce a reference to an array slot. */
+
+static tree
+cxx_eval_array_reference (const constexpr_call *call, tree t)
+{
+ tree ary = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0));
+ tree index;
+ if (ary == error_mark_node)
+ return ary;
+ gcc_assert (TREE_CODE (ary) == CONSTRUCTOR);
+ index = cxx_eval_constant_expression (call, TREE_OPERAND (t, 1));
+ if (index == error_mark_node)
+ return index;
+ /* FIXME: For the time being, refuse to index into a too big arrary. */
+ if (!host_integerp (index, 0))
+ {
+ error ("array subscript too big");
+ return error_mark_node;
+ }
+ if (tree_low_cst (index, 0) >= CONSTRUCTOR_NELTS (ary))
+ {
+ error ("array subscript out of bound");
+ return error_mark_node;
+ }
+ return VEC_index(constructor_elt, CONSTRUCTOR_ELTS (ary),
+ tree_low_cst (index, 0))->value;
+}
+
+/* Subroutine of cxx_eval_constant_expression.
+ Attempt to reduce a field access of a value of class type. */
+
+static tree
+cxx_eval_component_reference (const constexpr_call *call, tree t)
+{
+ tree whole = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0));
+ tree part = TREE_OPERAND (t, 1);
+ unsigned HOST_WIDE_INT i;
+ tree field;
+ tree value;
+ if (whole == error_mark_node)
+ return whole;
+ gcc_assert (TREE_CODE (whole) == CONSTRUCTOR);
+ FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (whole), i, field, value)
+ {
+ if (field == part)
+ return value;
+ }
+ gcc_unreachable();
+ return error_mark_node;
+}
+
+/* Subroutine of cxx_eval_constant_expression.
+ Evaluate a short-circuited logical expression T in the context
+ of a given constexpr CALL. BAILOUT_VALUE is the value for
+ early return. CONTINUE_VALUE is used here purely for
+ sanity check purposes. */
+
+static tree
+cxx_eval_logical_expression (const constexpr_call *call, tree t,
+ tree bailout_value, tree continue_value)
+{
+ tree lhs = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0));
+ if (lhs == error_mark_node || lhs == bailout_value)
+ return lhs;
+ gcc_assert (lhs == continue_value);
+ return cxx_eval_constant_expression (call, TREE_OPERAND (t, 1));
+}
+
+/* Subroutine of cxx_eval_constant_expression.
+ Evaluate an object construction (CTOR_INITIALIZER) denoted by T,
+ in the context of contexpr CALL. */
+
+static tree
+cxx_eval_object_construction (const constexpr_call *call, tree t)
+{
+ tree subobjects = NULL;
+ tree inits = TREE_OPERAND (t, 0);
+ for (; inits != NULL; inits = TREE_CHAIN (inits))
+ {
+ tree v = cxx_eval_constant_expression (call, TREE_VALUE (inits));
+ if (v == error_mark_node)
+ return v;
+ subobjects = tree_cons (TREE_PURPOSE (inits), v, subobjects);
+ }
+ t = build_constructor_from_list (TREE_TYPE (t), nreverse (subobjects));
+ COMPILE_TIME_CONSTANT_P (t) = true;
+ return t;
+}
+
+
+/* Attempt to reduced the expression tree T to a compile time value.
+ On failure, issue diagnostic and return error_mark_node. */
+
+static tree
+cxx_eval_constant_expression (const constexpr_call *call, tree t)
+{
+ if (t == error_mark_node || VALID_FOR_STATIC_INITIALIZATION_P (t))
+ return t;
+
+ STRIP_NOPS (t);
+ switch (TREE_CODE (t))
+ {
+ case VAR_DECL:
+ return cxx_eval_constant_expression (call, DECL_INITIAL (t));
+
+ case PARM_DECL:
+ return lookup_parameter_binding (call, t);
+
+ case CALL_EXPR:
+ case AGGR_INIT_EXPR:
+ return cxx_eval_call_expression (call, t);
+
+ case INIT_EXPR:
+ case TARGET_EXPR:
+ return cxx_eval_constant_expression (call, TREE_OPERAND (t, 1));
+
+ case RETURN_EXPR:
+ case NON_LVALUE_EXPR:
+ return cxx_eval_constant_expression (call, TREE_OPERAND (t, 0));
+
+ case REALPART_EXPR:
+ case IMAGPART_EXPR:
+ case CONJ_EXPR:
+ case SAVE_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FLOAT_EXPR:
+ case NEGATE_EXPR:
+ case ABS_EXPR:
+ case BIT_NOT_EXPR:
+ case TRUTH_NOT_EXPR:
+ case PAREN_EXPR:
+ case FIXED_CONVERT_EXPR:
+ return cxx_eval_unary_expression (call, t);
+
+ case COMPOUND_EXPR:
+ case PLUS_EXPR:
+ case MULT_EXPR:
+ case TRUNC_DIV_EXPR:
+ case CEIL_DIV_EXPR:
+ case FLOOR_DIV_EXPR:
+ case ROUND_DIV_EXPR:
+ case TRUNC_MOD_EXPR:
+ case CEIL_MOD_EXPR:
+ case ROUND_MOD_EXPR:
+ case RDIV_EXPR:
+ case EXACT_DIV_EXPR:
+ case MIN_EXPR:
+ case MAX_EXPR:
+ case LSHIFT_EXPR:
+ case RSHIFT_EXPR:
+ case LROTATE_EXPR:
+ case RROTATE_EXPR:
+ case BIT_IOR_EXPR:
+ case BIT_XOR_EXPR:
+ case BIT_AND_EXPR:
+ case TRUTH_XOR_EXPR:
+ case LT_EXPR:
+ case LE_EXPR:
+ case GT_EXPR:
+ case GE_EXPR:
+ case EQ_EXPR:
+ case NE_EXPR:
+ case UNLT_EXPR:
+ case UNLE_EXPR:
+ case UNGT_EXPR:
+ case UNGE_EXPR:
+ case UNEQ_EXPR:
+ case RANGE_EXPR:
+ case COMPLEX_EXPR:
+ return cxx_eval_binary_expression (call, t);
+
+ case TRUTH_ANDIF_EXPR:
+ return cxx_eval_logical_expression (call, t, boolean_false_node,
+ boolean_true_node);
+
+ case TRUTH_ORIF_EXPR:
+ return cxx_eval_logical_expression (call, t, boolean_true_node,
+ boolean_false_node);
+
+ case ARRAY_REF:
+ return cxx_eval_array_reference (call, t);
+
+ case COMPONENT_REF:
+ return cxx_eval_component_reference (call, t);
+
+ case COND_EXPR:
+ case VEC_COND_EXPR:
+ return cxx_eval_conditional_expression (call, t);
+
+ case INDIRECT_REF:
+ {
+ tree x = TREE_OPERAND (t, 0);
+ gcc_assert (TREE_CODE (x) == NOP_EXPR);
+ STRIP_NOPS (x);
+ if (is_this_parameter (x))
+ return lookup_parameter_binding (call, x);
+ return cxx_eval_constant_expression (call, x);
+ }
+
+ case CTOR_INITIALIZER:
+ return cxx_eval_object_construction (call, t);
+
+ default:
+ internal_error ("unexpected expression %qE of kind %s", t,
+ tree_code_name[TREE_CODE (t)]);
+ return error_mark_node;
+ }
+}
+
+
+/* If T represents a constant expression returns its reduced value.
+ Otherwise return error_mark_node. If T is dependent, then
+ return NULL. */
+
+tree
+cxx_constant_value (tree t)
+{
+ return potential_constant_expression (t, tf_error)
+ ? cxx_eval_constant_expression (NULL, t)
+ : error_mark_node;
+}
+
+/* Return true if DECL has automatic or thread local storage. */
+
+static bool
+has_automatic_or_tls (tree decl)
+{
+ switch (TREE_CODE (decl))
+ {
+ case PARM_DECL:
+ return true;
+
+ case VAR_DECL:
+ return DECL_THREAD_LOCAL_P (decl)
+ || (DECL_FUNCTION_SCOPE_P (decl) && !TREE_STATIC (decl));
+
+ default:
+ return false;
+ }
+}
+
+
+/* Return true if T denotes a potential constant expressions.
+ Issue diagnostic as appropriate under control of flags. Variables
+ with static storage duration initialized by constant expressions
+ are guaranteed to be statically initialized.
+
+ C++0x [expr.const]
+
+ 6 An expression is a potential constant expression if it is
+ a constant expression where all occurences of function
+ parameters are replaced by arbitrary constant expressions
+ of the appropriate type.
+
+ 2 A conditional expression is a constant expression unless it
+ involves one of the following as a potentially evaluated
+ subexpression (3.2), but subexpressions of logical AND (5.14),
+ logical OR (5.15), and conditional (5.16) operations that are
+ not evaluated are not considered. */
+
+static bool
+potential_constant_expression (tree t, tsubst_flags_t flags)
+{
+ if (t == error_mark_node)
+ return false;
+ if (TREE_THIS_VOLATILE (t))
+ {
+ if (flags & tf_error)
+ error ("expression %qE has side-effects", t);
+ return false;
+ }
+ if (VALID_FOR_STATIC_INITIALIZATION_P (t))
+ return true;
+
+ switch (TREE_CODE (t))
+ {
+ case PARM_DECL:
+ /* -- this (5.1) unless it appears as the postfix-expression in a
+ class member access expression, including the result of the
+ implicit transformation in the body of the non-static
+ member function (9.3.1); */
+ if (is_this_parameter (t))
+ {
+ if (flags & tf_error)
+ error ("%qE is not a potential constant expression", t);
+ return false;
+ }
+ return true;
+
+ case AGGR_INIT_EXPR:
+ if (AGGR_INIT_VIA_CTOR_P (t) && !CLASSTYPE_LITERAL_P (TREE_TYPE (t)))
+ {
+ error ("object of non-literal type is not potential constant");
+ return false;
+ }
+ /* fall through. */
+ case CALL_EXPR:
+ /* -- an invocation of a function other than a constexpr function
+ or a constexpr constructor. */
+ {
+ tree fun = get_function_named_in_call (t);
+ const int nargs = call_expr_nargs (t);
+ int i;
+ if (!DECL_P (fun))
+ {
+ if (flags & tf_error)
+ error ("%qE is not a function name", fun);
+ return false;
+ }
+ if (DECL_CLONED_FUNCTION_P (fun))
+ fun = DECL_CLONED_FUNCTION (fun);
+ if (builtin_valid_in_constant_expr_p (fun))
+ return true;
+ if (!DECL_DECLARED_CONSTEXPR_P (fun))
+ {
+ if (flags & tf_error)
+ error ("%qD is not %<constexpr%>", fun);
+ return false;
+ }
+ for (i = 0; i < nargs; ++i)
+ {
+ tree x = get_nth_callarg (t, i);
+ /* A call to a non-static member function takes the
+ address of the implied object as first argument.
+ If this is an rvalue object, don't look into its storage. */
+ if (i == 0 && DECL_NONSTATIC_MEMBER_P (fun)
+ && (TREE_CODE (t) != AGGR_INIT_EXPR
+ || !AGGR_INIT_VIA_CTOR_P (t)))
+ {
+ gcc_assert (TREE_CODE (x) == ADDR_EXPR);
+ if (!potential_constant_expression (x, flags))
+ return false;
+ }
+ else if (!potential_constant_expression (x, flags))
+ {
+ if (flags & tf_error)
+ error ("argument in position %qd is not a "
+ "potential constant expression", i);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ case NOP_EXPR:
+ case NON_LVALUE_EXPR:
+ /* -- an lvalue-to-rvalue conversion (4.1) unless it is applied to
+ -- an lvalue of integral type that refers to a non-volatile
+ const variable or static data member initialized with
+ constant expressions, or
+
+ -- an lvalue of literal type that refers to non-volatile
+ object defined with constexpr, or that refers to a
+ sub-object of such an object; */
+ return potential_constant_expression (TREE_OPERAND (t, 0), flags);
+
+ case VAR_DECL:
+ if (!DECL_INTEGRAL_CONSTANT_VAR_P (t)
+ && !DECL_DECLARED_CONSTEXPR_P (t))
+ {
+ if (flags & tf_error)
+ error ("variable %qD is not declared constexpr", t);
+ return false;
+ }
+ return true;
+
+ case CONVERT_EXPR:
+ case VIEW_CONVERT_EXPR:
+ /* -- an array-to-pointer conversion that is applied to an lvalue
+ that designates an object with thread or automatic storage
+ duration;
+ -- a type conversion from a pointer or pointer-to-member type
+ to a literal type. */
+ {
+ tree from = TREE_OPERAND (t, 0);
+ tree source = TREE_TYPE (from);
+ tree target = TREE_TYPE (t);
+ if (TYPE_ARRAY_P (source) && TYPE_PTR_P (target)
+ && has_automatic_or_tls (from))
+ {
+ if (flags & tf_error)
+ error ("array-to-pointer conversion of %qE with automatic "
+ "or thread local storage cannot yield a constant "
+ "expression", from);
+ return false;
+ }
+ if (TYPE_PTR_P (source) || TYPE_PTRMEM_P (source))
+ {
+ if (flags & tf_error)
+ error ("conversion of expression %qE of pointer or "
+ "pointer-to-member type cannot yield a constant "
+ "expression", from);
+ return false;
+ }
+ return potential_constant_expression (from, flags);
+ }
+
+ case ADDR_EXPR:
+ /* -- a unary operator & that is applied to an lvalue that
+ designates an object with thread or automatic storage
+ duration; */
+ t = TREE_OPERAND (t, 0);
+ if (VAR_DECL_P (t) && TREE_STATIC (t))
+ return true;
+ if (has_automatic_or_tls (t))
+ {
+ if (flags & tf_error)
+ error ("address-of a object %qE with thread local or "
+ "automatic storage is not a constant expression", t);
+ return false;
+ }
+ return potential_constant_expression (t, flags);
+
+ case COMPONENT_REF:
+ case BIT_FIELD_REF:
+ /* -- a class member access unless its postfix-expression is
+ of literal type or of pointer to literal type. */
+ return potential_constant_expression (TREE_OPERAND (t, 0), flags);
+
+ case INDIRECT_REF:
+ {
+ tree x = TREE_OPERAND (t, 0);
+ STRIP_NOPS (x);
+ if (!is_this_parameter (x))
+ return potential_constant_expression (x, flags);
+ return true;
+ }
+
+ case LAMBDA_EXPR:
+ case DYNAMIC_CAST_EXPR:
+ case PSEUDO_DTOR_EXPR:
+ case PREINCREMENT_EXPR:
+ case POSTINCREMENT_EXPR:
+ case PREDECREMENT_EXPR:
+ case POSTDECREMENT_EXPR:
+ case NEW_EXPR:
+ case VEC_NEW_EXPR:
+ case DELETE_EXPR:
+ case VEC_DELETE_EXPR:
+ case THROW_EXPR:
+ case MODIFY_EXPR:
+ case MODOP_EXPR:
+ /* GCC internal stuff. */
+ case VA_ARG_EXPR:
+ case OBJ_TYPE_REF:
+ case WITH_CLEANUP_EXPR:
+ case CLEANUP_POINT_EXPR:
+ if (flags & tf_error)
+ error ("expression %qE is not a constant-expression", t);
+ return false;
+
+ case TYPEID_EXPR:
+ /* -- a typeid expression whose operand is of polymorphic
+ class type; */
+ {
+ tree e = TREE_OPERAND (t, 0);
+ if (!TYPE_P (e) && TYPE_POLYMORPHIC_P (TREE_TYPE (e)))
+ {
+ if (flags & tf_error)
+ error ("typeid-expression is not a constant expression "
+ "because %qE is of polymorphic type", e);
+ return false;
+ }
+ return true;
+ }
+
+ case MINUS_EXPR:
+ /* -- a subtraction where both operands are pointers. */
+ if (TYPE_PTR_P (TREE_OPERAND (t, 0))
+ && TYPE_PTR_P (TREE_OPERAND (t, 1)))
+ {
+ if (flags & tf_error)
+ error ("difference of two pointer expressions is not "
+ "a constant expression");
+ return false;
+ }
+ return potential_constant_expression (TREE_OPERAND (t, 0), flags)
+ && potential_constant_expression (TREE_OPERAND (t, 1), flags);
+
+ case LT_EXPR:
+ case LE_EXPR:
+ case GT_EXPR:
+ case GE_EXPR:
+ case EQ_EXPR:
+ case NE_EXPR:
+ /* -- a relational or equality operator where at least
+ one of the operands is a pointer. */
+ if (TYPE_PTR_P (TREE_OPERAND (t, 0))
+ || TYPE_PTR_P (TREE_OPERAND (t, 1)))
+ {
+ if (flags & tf_error)
+ error ("pointer comparison expression is not a "
+ "constant expression");
+ return false;
+ }
+ return potential_constant_expression (TREE_OPERAND (t, 0), flags)
+ && potential_constant_expression (TREE_OPERAND (t, 1), flags);
+
+ case REALPART_EXPR:
+ case IMAGPART_EXPR:
+ case CONJ_EXPR:
+ case SAVE_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FLOAT_EXPR:
+ case NEGATE_EXPR:
+ case ABS_EXPR:
+ case BIT_NOT_EXPR:
+ case TRUTH_NOT_EXPR:
+ case PAREN_EXPR:
+ case FIXED_CONVERT_EXPR:
+ case CONST_CAST_EXPR:
+ /* For convenience. */
+ case RETURN_EXPR:
+ return potential_constant_expression (TREE_OPERAND (t, 0), flags);
+
+ case INIT_EXPR:
+ case TARGET_EXPR:
+ return potential_constant_expression (TREE_OPERAND (t, 1), flags);
+
+ case CONSTRUCTOR:
+ {
+ VEC(constructor_elt, gc) *v = CONSTRUCTOR_ELTS (t);
+ constructor_elt *ce;
+ HOST_WIDE_INT i;
+ for (i = 0; VEC_iterate(constructor_elt, v, i, ce); ++i)
+ if (!potential_constant_expression (ce->value, flags))
+ return false;
+ return true;
+ }
+
+ case ARRAY_REF:
+ case ARRAY_RANGE_REF:
+ case COMPOUND_EXPR:
+ case PLUS_EXPR:
+ case MULT_EXPR:
+ case POINTER_PLUS_EXPR:
+ case TRUNC_DIV_EXPR:
+ case CEIL_DIV_EXPR:
+ case FLOOR_DIV_EXPR:
+ case ROUND_DIV_EXPR:
+ case TRUNC_MOD_EXPR:
+ case CEIL_MOD_EXPR:
+ case ROUND_MOD_EXPR:
+ case RDIV_EXPR:
+ case EXACT_DIV_EXPR:
+ case MIN_EXPR:
+ case MAX_EXPR:
+ case LSHIFT_EXPR:
+ case RSHIFT_EXPR:
+ case LROTATE_EXPR:
+ case RROTATE_EXPR:
+ case BIT_IOR_EXPR:
+ case BIT_XOR_EXPR:
+ case BIT_AND_EXPR:
+ case TRUTH_ANDIF_EXPR:
+ case TRUTH_ORIF_EXPR:
+ case TRUTH_XOR_EXPR:
+ case UNLT_EXPR:
+ case UNLE_EXPR:
+ case UNGT_EXPR:
+ case UNGE_EXPR:
+ case UNEQ_EXPR:
+ case RANGE_EXPR:
+ case COMPLEX_EXPR:
+ return potential_constant_expression (TREE_OPERAND (t, 0), flags)
+ && potential_constant_expression (TREE_OPERAND (t, 1), flags);
+
+ case COND_EXPR:
+ case VEC_COND_EXPR:
+ return potential_constant_expression (TREE_OPERAND (t, 0), flags)
+ && potential_constant_expression (TREE_OPERAND (t, 1), flags)
+ && potential_constant_expression (TREE_OPERAND (t, 2), flags);
+
+ case CTOR_INITIALIZER:
+ for (t = TREE_OPERAND (t, 0); t != NULL; t = TREE_CHAIN (t))
+ if (!potential_constant_expression (TREE_VALUE (t), flags))
+ return false;
+ return true;
+
+ default:
+ sorry ("unexpected ast of kind %s", tree_code_name[TREE_CODE (t)]);
+ gcc_unreachable();
+ return false;
+ }
+}
+
+
/* Constructor for a lambda expression. */
tree
=== gcc/cp/parser.c
==================================================================
--- gcc/cp/parser.c (revision 154697)
+++ gcc/cp/parser.c (patch const level 1)
@@ -15388,12 +15388,32 @@
{
tree body;
bool ctor_initializer_p;
+ const bool check_body_p =
+ DECL_CONSTRUCTOR_P (current_function_decl)
+ && DECL_DECLARED_CONSTEXPR_P (current_function_decl);
+ tree last = NULL;
/* Begin the function body. */
body = begin_function_body ();
/* Parse the optional ctor-initializer. */
ctor_initializer_p = cp_parser_ctor_initializer_opt (parser);
+
+ /* If we're parsing a constexpr constructor definition, we need
+ to check that the constructor body is indeed empty. However,
+ before we get to cp_parser_function_body lot of junk has been
+ generated, so we can't just check that we have an empty block.
+ Rather we take a snapshot of the outermost block, and check whether
+ cp_parser_function_body changed its state. */
+ if (check_body_p && TREE_CODE (body) == STATEMENT_LIST)
+ last = STATEMENT_LIST_TAIL (body)->stmt;
/* Parse the function-body. */
cp_parser_function_body (parser);
+ if (check_body_p
+ && (TREE_CODE (body) != STATEMENT_LIST
+ || last != STATEMENT_LIST_TAIL (body)->stmt))
+ {
+ error ("constexpr constructor does not have empty body");
+ DECL_DECLARED_CONSTEXPR_P (current_function_decl) = false;
+ }
/* Finish the function body. */
finish_function_body (body);