Implement C++17 constexpr lambda.
gcc/c-family/ * c-cppbuiltin.c (c_cpp_builtins): Update __cpp_constexpr for C++17 constexpr lambdas. gcc/cp/ * class.c (finalize_literal_type_property): Handle lambdas. * constexpr.c (is_valid_constexpr_fn): Likewise. No longer static. (explain_invalid_constexpr_fn, cxx_eval_call_expression): Handle lambdas. (cxx_eval_constant_expression): Handle capture proxy. (var_in_constexpr_fn): Don't check for C++14. (var_in_maybe_constexpr_fn): New. (potential_constant_expression_1): Use it. Check DECL_EXPR for declarations not allowed in constexpr function. * decl.c (make_rtl_for_nonlocal_decl): Use var_in_maybe_constexpr_fn. (finish_function): Set DECL_DECLARED_CONSTEXPR_P on lambda members. * lambda.c (begin_lambda_type): Set CLASSTYPE_LITERAL_P. (maybe_add_lambda_conv_op): Clear thunk CALL_EXPR location. (lambda_static_thunk_p): New. * parser.c (cp_keyword_starts_decl_specifier_p): Add RID_CONSTEXPR. (CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR): New enumerator. (cp_parser_decl_specifier_seq): Handle it. (cp_parser_lambda_declarator_opt): Use cp_parser_decl_specifier_seq. * pt.c (instantiate_class_template_1): Set CLASSTYPE_LITERAL_P. (tsubst_copy_and_build) [CALL_EXPR]: Propagate CALL_FROM_THUNK_P. * error.c (dump_function_decl): Check TFF_NO_TEMPLATE_BINDINGS. (dump_expr) [FUNCTION_DECL]: Pass it. From-SVN: r239268
This commit is contained in:
parent
7dc2b4a235
commit
98e5a19af5
|
@ -1,3 +1,8 @@
|
||||||
|
2016-08-09 Jason Merrill <jason@redhat.com>
|
||||||
|
|
||||||
|
* c-cppbuiltin.c (c_cpp_builtins): Update __cpp_constexpr for
|
||||||
|
C++17 constexpr lambdas.
|
||||||
|
|
||||||
2016-08-08 David Malcolm <dmalcolm@redhat.com>
|
2016-08-08 David Malcolm <dmalcolm@redhat.com>
|
||||||
|
|
||||||
PR c/64955
|
PR c/64955
|
||||||
|
|
|
@ -863,7 +863,8 @@ c_cpp_builtins (cpp_reader *pfile)
|
||||||
cpp_define (pfile, "__cpp_return_type_deduction=201304");
|
cpp_define (pfile, "__cpp_return_type_deduction=201304");
|
||||||
cpp_define (pfile, "__cpp_init_captures=201304");
|
cpp_define (pfile, "__cpp_init_captures=201304");
|
||||||
cpp_define (pfile, "__cpp_generic_lambdas=201304");
|
cpp_define (pfile, "__cpp_generic_lambdas=201304");
|
||||||
cpp_define (pfile, "__cpp_constexpr=201304");
|
if (cxx_dialect <= cxx14)
|
||||||
|
cpp_define (pfile, "__cpp_constexpr=201304");
|
||||||
cpp_define (pfile, "__cpp_decltype_auto=201304");
|
cpp_define (pfile, "__cpp_decltype_auto=201304");
|
||||||
cpp_define (pfile, "__cpp_aggregate_nsdmi=201304");
|
cpp_define (pfile, "__cpp_aggregate_nsdmi=201304");
|
||||||
cpp_define (pfile, "__cpp_variable_templates=201304");
|
cpp_define (pfile, "__cpp_variable_templates=201304");
|
||||||
|
@ -880,6 +881,7 @@ c_cpp_builtins (cpp_reader *pfile)
|
||||||
cpp_define (pfile, "__cpp_fold_expressions=201603");
|
cpp_define (pfile, "__cpp_fold_expressions=201603");
|
||||||
cpp_define (pfile, "__cpp_nontype_template_args=201411");
|
cpp_define (pfile, "__cpp_nontype_template_args=201411");
|
||||||
cpp_define (pfile, "__cpp_range_based_for=201603");
|
cpp_define (pfile, "__cpp_range_based_for=201603");
|
||||||
|
cpp_define (pfile, "__cpp_constexpr=201603");
|
||||||
}
|
}
|
||||||
if (flag_concepts)
|
if (flag_concepts)
|
||||||
/* Use a value smaller than the 201507 specified in
|
/* Use a value smaller than the 201507 specified in
|
||||||
|
|
|
@ -1,3 +1,30 @@
|
||||||
|
2016-08-08 Jason Merrill <jason@redhat.com>
|
||||||
|
|
||||||
|
Implement C++17 constexpr lambda.
|
||||||
|
* class.c (finalize_literal_type_property): Handle lambdas.
|
||||||
|
* constexpr.c (is_valid_constexpr_fn): Likewise. No longer static.
|
||||||
|
(explain_invalid_constexpr_fn, cxx_eval_call_expression): Handle
|
||||||
|
lambdas.
|
||||||
|
(cxx_eval_constant_expression): Handle capture proxy.
|
||||||
|
(var_in_constexpr_fn): Don't check for C++14.
|
||||||
|
(var_in_maybe_constexpr_fn): New.
|
||||||
|
(potential_constant_expression_1): Use it. Check DECL_EXPR for
|
||||||
|
declarations not allowed in constexpr function. Handle
|
||||||
|
STATIC_ASSERT, RANGE_FOR_STMT.
|
||||||
|
* decl.c (make_rtl_for_nonlocal_decl): Use var_in_maybe_constexpr_fn.
|
||||||
|
(finish_function): Set DECL_DECLARED_CONSTEXPR_P on lambda members.
|
||||||
|
* lambda.c (begin_lambda_type): Set CLASSTYPE_LITERAL_P.
|
||||||
|
(maybe_add_lambda_conv_op): Clear thunk CALL_EXPR location.
|
||||||
|
(lambda_static_thunk_p): New.
|
||||||
|
* parser.c (cp_keyword_starts_decl_specifier_p): Add RID_CONSTEXPR.
|
||||||
|
(CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR): New enumerator.
|
||||||
|
(cp_parser_decl_specifier_seq): Handle it.
|
||||||
|
(cp_parser_lambda_declarator_opt): Use cp_parser_decl_specifier_seq.
|
||||||
|
* pt.c (instantiate_class_template_1): Set CLASSTYPE_LITERAL_P.
|
||||||
|
(tsubst_copy_and_build) [CALL_EXPR]: Propagate CALL_FROM_THUNK_P.
|
||||||
|
* error.c (dump_function_decl): Check TFF_NO_TEMPLATE_BINDINGS.
|
||||||
|
(dump_expr) [FUNCTION_DECL]: Pass it.
|
||||||
|
|
||||||
2016-08-08 Jason Merrill <jason@redhat.com>
|
2016-08-08 Jason Merrill <jason@redhat.com>
|
||||||
|
|
||||||
PR c++/67131
|
PR c++/67131
|
||||||
|
|
|
@ -5659,7 +5659,7 @@ finalize_literal_type_property (tree t)
|
||||||
&& !DECL_CONSTRUCTOR_P (fn))
|
&& !DECL_CONSTRUCTOR_P (fn))
|
||||||
{
|
{
|
||||||
DECL_DECLARED_CONSTEXPR_P (fn) = false;
|
DECL_DECLARED_CONSTEXPR_P (fn) = false;
|
||||||
if (!DECL_GENERATED_P (fn))
|
if (!DECL_GENERATED_P (fn) && !LAMBDA_TYPE_P (t))
|
||||||
{
|
{
|
||||||
error ("enclosing class of constexpr non-static member "
|
error ("enclosing class of constexpr non-static member "
|
||||||
"function %q+#D is not a literal type", fn);
|
"function %q+#D is not a literal type", fn);
|
||||||
|
|
|
@ -166,7 +166,7 @@ retrieve_constexpr_fundef (tree fun)
|
||||||
/* Check whether the parameter and return types of FUN are valid for a
|
/* Check whether the parameter and return types of FUN are valid for a
|
||||||
constexpr function, and complain if COMPLAIN. */
|
constexpr function, and complain if COMPLAIN. */
|
||||||
|
|
||||||
static bool
|
bool
|
||||||
is_valid_constexpr_fn (tree fun, bool complain)
|
is_valid_constexpr_fn (tree fun, bool complain)
|
||||||
{
|
{
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
|
@ -832,8 +832,9 @@ explain_invalid_constexpr_fn (tree fun)
|
||||||
static hash_set<tree> *diagnosed;
|
static hash_set<tree> *diagnosed;
|
||||||
tree body;
|
tree body;
|
||||||
location_t save_loc;
|
location_t save_loc;
|
||||||
/* Only diagnose defaulted functions or instantiations. */
|
/* Only diagnose defaulted functions, lambdas, or instantiations. */
|
||||||
if (!DECL_DEFAULTED_FN (fun)
|
if (!DECL_DEFAULTED_FN (fun)
|
||||||
|
&& !LAMBDA_TYPE_P (CP_DECL_CONTEXT (fun))
|
||||||
&& !is_instantiation_of_constexpr (fun))
|
&& !is_instantiation_of_constexpr (fun))
|
||||||
return;
|
return;
|
||||||
if (diagnosed == NULL)
|
if (diagnosed == NULL)
|
||||||
|
@ -843,14 +844,20 @@ explain_invalid_constexpr_fn (tree fun)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
save_loc = input_location;
|
save_loc = input_location;
|
||||||
input_location = DECL_SOURCE_LOCATION (fun);
|
if (!lambda_static_thunk_p (fun))
|
||||||
inform (input_location,
|
{
|
||||||
"%qD is not usable as a constexpr function because:", fun);
|
/* Diagnostics should completely ignore the static thunk, so leave
|
||||||
|
input_location set to our caller's location. */
|
||||||
|
input_location = DECL_SOURCE_LOCATION (fun);
|
||||||
|
inform (input_location,
|
||||||
|
"%qD is not usable as a constexpr function because:", fun);
|
||||||
|
}
|
||||||
/* First check the declaration. */
|
/* First check the declaration. */
|
||||||
if (is_valid_constexpr_fn (fun, true))
|
if (is_valid_constexpr_fn (fun, true))
|
||||||
{
|
{
|
||||||
/* Then if it's OK, the body. */
|
/* Then if it's OK, the body. */
|
||||||
if (!DECL_DECLARED_CONSTEXPR_P (fun))
|
if (!DECL_DECLARED_CONSTEXPR_P (fun)
|
||||||
|
&& !LAMBDA_TYPE_P (CP_DECL_CONTEXT (fun)))
|
||||||
explain_implicit_non_constexpr (fun);
|
explain_implicit_non_constexpr (fun);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1464,8 +1471,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
|
||||||
"definition is complete", fun);
|
"definition is complete", fun);
|
||||||
else if (DECL_INITIAL (fun))
|
else if (DECL_INITIAL (fun))
|
||||||
{
|
{
|
||||||
/* The definition of fun was somehow unsuitable. */
|
/* The definition of fun was somehow unsuitable. But pretend
|
||||||
error_at (loc, "%qD called in a constant expression", fun);
|
that lambda static thunks don't exist. */
|
||||||
|
if (!lambda_static_thunk_p (fun))
|
||||||
|
error_at (loc, "%qD called in a constant expression", fun);
|
||||||
explain_invalid_constexpr_fn (fun);
|
explain_invalid_constexpr_fn (fun);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -3096,14 +3105,30 @@ cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t,
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* True if T was declared in a function declared to be constexpr, and
|
||||||
|
therefore potentially constant in C++14. */
|
||||||
|
|
||||||
bool
|
bool
|
||||||
var_in_constexpr_fn (tree t)
|
var_in_constexpr_fn (tree t)
|
||||||
{
|
{
|
||||||
tree ctx = DECL_CONTEXT (t);
|
tree ctx = DECL_CONTEXT (t);
|
||||||
return (cxx_dialect >= cxx14 && ctx && TREE_CODE (ctx) == FUNCTION_DECL
|
return (ctx && TREE_CODE (ctx) == FUNCTION_DECL
|
||||||
&& DECL_DECLARED_CONSTEXPR_P (ctx));
|
&& DECL_DECLARED_CONSTEXPR_P (ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* True if T was declared in a function that might be constexpr: either a
|
||||||
|
function that was declared constexpr, or a C++17 lambda op(). */
|
||||||
|
|
||||||
|
bool
|
||||||
|
var_in_maybe_constexpr_fn (tree t)
|
||||||
|
{
|
||||||
|
if (cxx_dialect >= cxx1z
|
||||||
|
&& DECL_FUNCTION_SCOPE_P (t)
|
||||||
|
&& LAMBDA_FUNCTION_P (DECL_CONTEXT (t)))
|
||||||
|
return true;
|
||||||
|
return var_in_constexpr_fn (t);
|
||||||
|
}
|
||||||
|
|
||||||
/* Evaluate an INIT_EXPR or MODIFY_EXPR. */
|
/* Evaluate an INIT_EXPR or MODIFY_EXPR. */
|
||||||
|
|
||||||
static tree
|
static tree
|
||||||
|
@ -3665,6 +3690,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
|
||||||
return (*ctx->values->get (t));
|
return (*ctx->values->get (t));
|
||||||
|
|
||||||
case VAR_DECL:
|
case VAR_DECL:
|
||||||
|
if (is_capture_proxy (t))
|
||||||
|
return cxx_eval_constant_expression (ctx, DECL_VALUE_EXPR (t),
|
||||||
|
lval, non_constant_p, overflow_p);
|
||||||
|
/* else fall through. */
|
||||||
case CONST_DECL:
|
case CONST_DECL:
|
||||||
/* We used to not check lval for CONST_DECL, but darwin.c uses
|
/* We used to not check lval for CONST_DECL, but darwin.c uses
|
||||||
CONST_DECL for aggregate constants. */
|
CONST_DECL for aggregate constants. */
|
||||||
|
@ -4775,6 +4804,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
|
||||||
case BREAK_STMT:
|
case BREAK_STMT:
|
||||||
case CONTINUE_STMT:
|
case CONTINUE_STMT:
|
||||||
case REQUIRES_EXPR:
|
case REQUIRES_EXPR:
|
||||||
|
case STATIC_ASSERT:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case AGGR_INIT_EXPR:
|
case AGGR_INIT_EXPR:
|
||||||
|
@ -4900,7 +4930,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
|
||||||
|
|
||||||
case VAR_DECL:
|
case VAR_DECL:
|
||||||
if (want_rval
|
if (want_rval
|
||||||
&& !var_in_constexpr_fn (t)
|
&& !var_in_maybe_constexpr_fn (t)
|
||||||
&& !type_dependent_expression_p (t)
|
&& !type_dependent_expression_p (t)
|
||||||
&& !decl_constant_var_p (t)
|
&& !decl_constant_var_p (t)
|
||||||
&& (strict
|
&& (strict
|
||||||
|
@ -5042,6 +5072,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case RANGE_FOR_STMT:
|
||||||
|
if (!RECUR (RANGE_FOR_EXPR (t), any))
|
||||||
|
return false;
|
||||||
|
if (!RECUR (RANGE_FOR_BODY (t), any))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
|
||||||
case WHILE_STMT:
|
case WHILE_STMT:
|
||||||
if (!RECUR (WHILE_COND (t), rval))
|
if (!RECUR (WHILE_COND (t), rval))
|
||||||
return false;
|
return false;
|
||||||
|
@ -5172,7 +5209,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
|
||||||
case EH_SPEC_BLOCK:
|
case EH_SPEC_BLOCK:
|
||||||
case EXPR_STMT:
|
case EXPR_STMT:
|
||||||
case PAREN_EXPR:
|
case PAREN_EXPR:
|
||||||
case DECL_EXPR:
|
|
||||||
case NON_DEPENDENT_EXPR:
|
case NON_DEPENDENT_EXPR:
|
||||||
/* For convenience. */
|
/* For convenience. */
|
||||||
case RETURN_EXPR:
|
case RETURN_EXPR:
|
||||||
|
@ -5180,6 +5216,34 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
|
||||||
case EXIT_EXPR:
|
case EXIT_EXPR:
|
||||||
return RECUR (TREE_OPERAND (t, 0), want_rval);
|
return RECUR (TREE_OPERAND (t, 0), want_rval);
|
||||||
|
|
||||||
|
case DECL_EXPR:
|
||||||
|
tmp = DECL_EXPR_DECL (t);
|
||||||
|
if (VAR_P (tmp) && !DECL_ARTIFICIAL (tmp))
|
||||||
|
{
|
||||||
|
if (TREE_STATIC (tmp))
|
||||||
|
{
|
||||||
|
if (flags & tf_error)
|
||||||
|
error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared "
|
||||||
|
"%<static%> in %<constexpr%> function", tmp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (CP_DECL_THREAD_LOCAL_P (tmp))
|
||||||
|
{
|
||||||
|
if (flags & tf_error)
|
||||||
|
error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared "
|
||||||
|
"%<thread_local%> in %<constexpr%> function", tmp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (!DECL_NONTRIVIALLY_INITIALIZED_P (tmp))
|
||||||
|
{
|
||||||
|
if (flags & tf_error)
|
||||||
|
error_at (DECL_SOURCE_LOCATION (tmp), "uninitialized "
|
||||||
|
"variable %qD in %<constexpr%> function", tmp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RECUR (tmp, want_rval);
|
||||||
|
|
||||||
case TRY_FINALLY_EXPR:
|
case TRY_FINALLY_EXPR:
|
||||||
return (RECUR (TREE_OPERAND (t, 0), want_rval)
|
return (RECUR (TREE_OPERAND (t, 0), want_rval)
|
||||||
&& RECUR (TREE_OPERAND (t, 1), any));
|
&& RECUR (TREE_OPERAND (t, 1), any));
|
||||||
|
|
|
@ -6478,6 +6478,7 @@ extern tree current_nonlambda_scope (void);
|
||||||
extern bool generic_lambda_fn_p (tree);
|
extern bool generic_lambda_fn_p (tree);
|
||||||
extern void maybe_add_lambda_conv_op (tree);
|
extern void maybe_add_lambda_conv_op (tree);
|
||||||
extern bool is_lambda_ignored_entity (tree);
|
extern bool is_lambda_ignored_entity (tree);
|
||||||
|
extern bool lambda_static_thunk_p (tree);
|
||||||
|
|
||||||
/* in tree.c */
|
/* in tree.c */
|
||||||
extern int cp_tree_operand_length (const_tree);
|
extern int cp_tree_operand_length (const_tree);
|
||||||
|
@ -6922,6 +6923,7 @@ bool cilkplus_an_triplet_types_ok_p (location_t, tree, tree, tree,
|
||||||
extern void fini_constexpr (void);
|
extern void fini_constexpr (void);
|
||||||
extern bool literal_type_p (tree);
|
extern bool literal_type_p (tree);
|
||||||
extern tree register_constexpr_fundef (tree, tree);
|
extern tree register_constexpr_fundef (tree, tree);
|
||||||
|
extern bool is_valid_constexpr_fn (tree, bool);
|
||||||
extern bool check_constexpr_ctor_body (tree, tree, bool);
|
extern bool check_constexpr_ctor_body (tree, tree, bool);
|
||||||
extern tree ensure_literal_type_for_constexpr_object (tree);
|
extern tree ensure_literal_type_for_constexpr_object (tree);
|
||||||
extern bool potential_constant_expression (tree);
|
extern bool potential_constant_expression (tree);
|
||||||
|
@ -6940,6 +6942,7 @@ extern bool is_sub_constant_expr (tree);
|
||||||
extern bool reduced_constant_expression_p (tree);
|
extern bool reduced_constant_expression_p (tree);
|
||||||
extern bool is_instantiation_of_constexpr (tree);
|
extern bool is_instantiation_of_constexpr (tree);
|
||||||
extern bool var_in_constexpr_fn (tree);
|
extern bool var_in_constexpr_fn (tree);
|
||||||
|
extern bool var_in_maybe_constexpr_fn (tree);
|
||||||
extern void explain_invalid_constexpr_fn (tree);
|
extern void explain_invalid_constexpr_fn (tree);
|
||||||
extern vec<tree> cx_error_context (void);
|
extern vec<tree> cx_error_context (void);
|
||||||
extern tree fold_sizeof_expr (tree);
|
extern tree fold_sizeof_expr (tree);
|
||||||
|
|
|
@ -6305,7 +6305,7 @@ make_rtl_for_nonlocal_decl (tree decl, tree init, const char* asmspec)
|
||||||
DECL_EXPR is expanded. But with constexpr its function might never
|
DECL_EXPR is expanded. But with constexpr its function might never
|
||||||
be expanded, so go ahead and tell cgraph about the variable now. */
|
be expanded, so go ahead and tell cgraph about the variable now. */
|
||||||
defer_p = ((DECL_FUNCTION_SCOPE_P (decl)
|
defer_p = ((DECL_FUNCTION_SCOPE_P (decl)
|
||||||
&& !DECL_DECLARED_CONSTEXPR_P (DECL_CONTEXT (decl)))
|
&& !var_in_maybe_constexpr_fn (decl))
|
||||||
|| DECL_VIRTUAL_P (decl));
|
|| DECL_VIRTUAL_P (decl));
|
||||||
|
|
||||||
/* Defer template instantiations. */
|
/* Defer template instantiations. */
|
||||||
|
@ -14748,6 +14748,14 @@ finish_function (int flags)
|
||||||
if (DECL_DECLARED_CONCEPT_P (fndecl))
|
if (DECL_DECLARED_CONCEPT_P (fndecl))
|
||||||
check_function_concept (fndecl);
|
check_function_concept (fndecl);
|
||||||
|
|
||||||
|
/* Lambda closure members are implicitly constexpr if possible. */
|
||||||
|
if (cxx_dialect >= cxx1z
|
||||||
|
&& LAMBDA_TYPE_P (CP_DECL_CONTEXT (fndecl))
|
||||||
|
&& (processing_template_decl
|
||||||
|
|| is_valid_constexpr_fn (fndecl, /*complain*/false))
|
||||||
|
&& potential_constant_expression (DECL_SAVED_TREE (fndecl)))
|
||||||
|
DECL_DECLARED_CONSTEXPR_P (fndecl) = true;
|
||||||
|
|
||||||
/* Save constexpr function body before it gets munged by
|
/* Save constexpr function body before it gets munged by
|
||||||
the NRV transformation. */
|
the NRV transformation. */
|
||||||
maybe_save_function_definition (fndecl);
|
maybe_save_function_definition (fndecl);
|
||||||
|
|
|
@ -1509,6 +1509,7 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
|
||||||
|
|
||||||
/* Pretty print template instantiations only. */
|
/* Pretty print template instantiations only. */
|
||||||
if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
|
if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
|
||||||
|
&& !(flags & TFF_NO_TEMPLATE_BINDINGS)
|
||||||
&& flag_pretty_templates)
|
&& flag_pretty_templates)
|
||||||
{
|
{
|
||||||
tree tmpl;
|
tree tmpl;
|
||||||
|
@ -1989,6 +1990,7 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
|
||||||
case IDENTIFIER_NODE:
|
case IDENTIFIER_NODE:
|
||||||
dump_decl (pp, t, ((flags & ~(TFF_DECL_SPECIFIERS|TFF_RETURN_TYPE
|
dump_decl (pp, t, ((flags & ~(TFF_DECL_SPECIFIERS|TFF_RETURN_TYPE
|
||||||
|TFF_TEMPLATE_HEADER))
|
|TFF_TEMPLATE_HEADER))
|
||||||
|
| TFF_NO_TEMPLATE_BINDINGS
|
||||||
| TFF_NO_FUNCTION_ARGUMENTS));
|
| TFF_NO_FUNCTION_ARGUMENTS));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -154,6 +154,11 @@ begin_lambda_type (tree lambda)
|
||||||
LAMBDA_EXPR_CLOSURE (lambda) = type;
|
LAMBDA_EXPR_CLOSURE (lambda) = type;
|
||||||
CLASSTYPE_LAMBDA_EXPR (type) = lambda;
|
CLASSTYPE_LAMBDA_EXPR (type) = lambda;
|
||||||
|
|
||||||
|
/* In C++17, assume the closure is literal; we'll clear the flag later if
|
||||||
|
necessary. */
|
||||||
|
if (cxx_dialect >= cxx1z)
|
||||||
|
CLASSTYPE_LITERAL_P (type) = true;
|
||||||
|
|
||||||
/* Clear base types. */
|
/* Clear base types. */
|
||||||
xref_basetypes (type, /*bases=*/NULL_TREE);
|
xref_basetypes (type, /*bases=*/NULL_TREE);
|
||||||
|
|
||||||
|
@ -1004,6 +1009,7 @@ maybe_add_lambda_conv_op (tree type)
|
||||||
direct_argvec->address ());
|
direct_argvec->address ());
|
||||||
|
|
||||||
CALL_FROM_THUNK_P (call) = 1;
|
CALL_FROM_THUNK_P (call) = 1;
|
||||||
|
SET_EXPR_LOCATION (call, UNKNOWN_LOCATION);
|
||||||
|
|
||||||
tree stattype = build_function_type (fn_result, FUNCTION_ARG_CHAIN (callop));
|
tree stattype = build_function_type (fn_result, FUNCTION_ARG_CHAIN (callop));
|
||||||
stattype = (cp_build_type_attribute_variant
|
stattype = (cp_build_type_attribute_variant
|
||||||
|
@ -1141,6 +1147,18 @@ maybe_add_lambda_conv_op (tree type)
|
||||||
--function_depth;
|
--function_depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* True if FN is the static function "_FUN" that gets returned from the lambda
|
||||||
|
conversion operator. */
|
||||||
|
|
||||||
|
bool
|
||||||
|
lambda_static_thunk_p (tree fn)
|
||||||
|
{
|
||||||
|
return (fn && TREE_CODE (fn) == FUNCTION_DECL
|
||||||
|
&& DECL_ARTIFICIAL (fn)
|
||||||
|
&& DECL_STATIC_FUNCTION_P (fn)
|
||||||
|
&& LAMBDA_TYPE_P (CP_DECL_CONTEXT (fn)));
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns true iff VAL is a lambda-related declaration which should
|
/* Returns true iff VAL is a lambda-related declaration which should
|
||||||
be ignored by unqualified lookup. */
|
be ignored by unqualified lookup. */
|
||||||
|
|
||||||
|
|
|
@ -981,6 +981,7 @@ cp_keyword_starts_decl_specifier_p (enum rid keyword)
|
||||||
/* C++0x extensions. */
|
/* C++0x extensions. */
|
||||||
case RID_DECLTYPE:
|
case RID_DECLTYPE:
|
||||||
case RID_UNDERLYING_TYPE:
|
case RID_UNDERLYING_TYPE:
|
||||||
|
case RID_CONSTEXPR:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -1800,7 +1801,9 @@ enum
|
||||||
CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS = 0x4,
|
CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS = 0x4,
|
||||||
/* When parsing a decl-specifier-seq, only allow type-specifier or
|
/* When parsing a decl-specifier-seq, only allow type-specifier or
|
||||||
constexpr. */
|
constexpr. */
|
||||||
CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR = 0x8
|
CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR = 0x8,
|
||||||
|
/* When parsing a decl-specifier-seq, only allow mutable or constexpr. */
|
||||||
|
CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR = 0x10
|
||||||
};
|
};
|
||||||
|
|
||||||
/* This type is used for parameters and variables which hold
|
/* This type is used for parameters and variables which hold
|
||||||
|
@ -10035,7 +10038,7 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
|
||||||
< template-parameter-list [opt] >
|
< template-parameter-list [opt] >
|
||||||
( parameter-declaration-clause [opt] )
|
( parameter-declaration-clause [opt] )
|
||||||
attribute-specifier [opt]
|
attribute-specifier [opt]
|
||||||
mutable [opt]
|
decl-specifier-seq [opt]
|
||||||
exception-specification [opt]
|
exception-specification [opt]
|
||||||
lambda-return-type-clause [opt]
|
lambda-return-type-clause [opt]
|
||||||
|
|
||||||
|
@ -10054,6 +10057,8 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
|
||||||
tree exception_spec = NULL_TREE;
|
tree exception_spec = NULL_TREE;
|
||||||
tree template_param_list = NULL_TREE;
|
tree template_param_list = NULL_TREE;
|
||||||
tree tx_qual = NULL_TREE;
|
tree tx_qual = NULL_TREE;
|
||||||
|
cp_decl_specifier_seq lambda_specs;
|
||||||
|
clear_decl_specs (&lambda_specs);
|
||||||
|
|
||||||
/* The template-parameter-list is optional, but must begin with
|
/* The template-parameter-list is optional, but must begin with
|
||||||
an opening angle if present. */
|
an opening angle if present. */
|
||||||
|
@ -10097,12 +10102,20 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
|
||||||
|
|
||||||
attributes = cp_parser_attributes_opt (parser);
|
attributes = cp_parser_attributes_opt (parser);
|
||||||
|
|
||||||
/* Parse optional `mutable' keyword. */
|
/* In the decl-specifier-seq of the lambda-declarator, each
|
||||||
if (cp_lexer_next_token_is_keyword (parser->lexer, RID_MUTABLE))
|
decl-specifier shall either be mutable or constexpr. */
|
||||||
{
|
int declares_class_or_enum;
|
||||||
cp_lexer_consume_token (parser->lexer);
|
if (cp_lexer_next_token_is_decl_specifier_keyword (parser->lexer))
|
||||||
LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
|
cp_parser_decl_specifier_seq (parser,
|
||||||
}
|
CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR,
|
||||||
|
&lambda_specs, &declares_class_or_enum);
|
||||||
|
if (lambda_specs.storage_class == sc_mutable)
|
||||||
|
{
|
||||||
|
LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
|
||||||
|
if (lambda_specs.conflicting_specifiers_p)
|
||||||
|
error_at (lambda_specs.locations[ds_storage_class],
|
||||||
|
"duplicate %<mutable%>");
|
||||||
|
}
|
||||||
|
|
||||||
tx_qual = cp_parser_tx_qualifier_opt (parser);
|
tx_qual = cp_parser_tx_qualifier_opt (parser);
|
||||||
|
|
||||||
|
@ -10143,6 +10156,16 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
|
||||||
/* Maybe we will deduce the return type later. */
|
/* Maybe we will deduce the return type later. */
|
||||||
return_type_specs.type = make_auto ();
|
return_type_specs.type = make_auto ();
|
||||||
|
|
||||||
|
if (lambda_specs.locations[ds_constexpr])
|
||||||
|
{
|
||||||
|
if (cxx_dialect >= cxx1z)
|
||||||
|
return_type_specs.locations[ds_constexpr]
|
||||||
|
= lambda_specs.locations[ds_constexpr];
|
||||||
|
else
|
||||||
|
error_at (lambda_specs.locations[ds_constexpr], "%<constexpr%> "
|
||||||
|
"lambda only available with -std=c++1z or -std=gnu++1z");
|
||||||
|
}
|
||||||
|
|
||||||
p = obstack_alloc (&declarator_obstack, 0);
|
p = obstack_alloc (&declarator_obstack, 0);
|
||||||
|
|
||||||
declarator = make_id_declarator (NULL_TREE, ansi_opname (CALL_EXPR),
|
declarator = make_id_declarator (NULL_TREE, ansi_opname (CALL_EXPR),
|
||||||
|
@ -12776,6 +12799,13 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
|
||||||
&& token->keyword != RID_CONSTEXPR)
|
&& token->keyword != RID_CONSTEXPR)
|
||||||
error ("decl-specifier invalid in condition");
|
error ("decl-specifier invalid in condition");
|
||||||
|
|
||||||
|
if (found_decl_spec
|
||||||
|
&& (flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR)
|
||||||
|
&& token->keyword != RID_MUTABLE
|
||||||
|
&& token->keyword != RID_CONSTEXPR)
|
||||||
|
error_at (token->location, "%qD invalid in lambda",
|
||||||
|
ridpointers[token->keyword]);
|
||||||
|
|
||||||
if (ds != ds_last)
|
if (ds != ds_last)
|
||||||
set_and_check_decl_spec_loc (decl_specs, ds, token);
|
set_and_check_decl_spec_loc (decl_specs, ds, token);
|
||||||
|
|
||||||
|
|
12
gcc/cp/pt.c
12
gcc/cp/pt.c
|
@ -10334,6 +10334,9 @@ instantiate_class_template_1 (tree type)
|
||||||
tree decl = lambda_function (type);
|
tree decl = lambda_function (type);
|
||||||
if (decl)
|
if (decl)
|
||||||
{
|
{
|
||||||
|
if (cxx_dialect >= cxx1z)
|
||||||
|
CLASSTYPE_LITERAL_P (type) = true;
|
||||||
|
|
||||||
if (!DECL_TEMPLATE_INFO (decl)
|
if (!DECL_TEMPLATE_INFO (decl)
|
||||||
|| DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (decl)) != decl)
|
|| DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (decl)) != decl)
|
||||||
{
|
{
|
||||||
|
@ -16760,12 +16763,19 @@ tsubst_copy_and_build (tree t,
|
||||||
bool op = CALL_EXPR_OPERATOR_SYNTAX (t);
|
bool op = CALL_EXPR_OPERATOR_SYNTAX (t);
|
||||||
bool ord = CALL_EXPR_ORDERED_ARGS (t);
|
bool ord = CALL_EXPR_ORDERED_ARGS (t);
|
||||||
bool rev = CALL_EXPR_REVERSE_ARGS (t);
|
bool rev = CALL_EXPR_REVERSE_ARGS (t);
|
||||||
if (op || ord || rev)
|
bool thk = CALL_FROM_THUNK_P (t);
|
||||||
|
if (op || ord || rev || thk)
|
||||||
{
|
{
|
||||||
function = extract_call_expr (ret);
|
function = extract_call_expr (ret);
|
||||||
CALL_EXPR_OPERATOR_SYNTAX (function) = op;
|
CALL_EXPR_OPERATOR_SYNTAX (function) = op;
|
||||||
CALL_EXPR_ORDERED_ARGS (function) = ord;
|
CALL_EXPR_ORDERED_ARGS (function) = ord;
|
||||||
CALL_EXPR_REVERSE_ARGS (function) = rev;
|
CALL_EXPR_REVERSE_ARGS (function) = rev;
|
||||||
|
if (thk)
|
||||||
|
{
|
||||||
|
CALL_FROM_THUNK_P (function) = true;
|
||||||
|
/* The thunk location is not interesting. */
|
||||||
|
SET_EXPR_LOCATION (function, UNKNOWN_LOCATION);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Test for conversion from stateless lambda to function pointer.
|
// Test for conversion from stateless lambda to function pointer.
|
||||||
|
|
||||||
// { dg-do compile { target c++11 } }
|
// { dg-do compile { target c++11_only } }
|
||||||
// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZZ1fvENKUlvE_cvPFvvEEv" { target { ! { *-*-darwin* *-*-mingw* *-*-cygwin *-*-hpux10* } } } } }
|
// { dg-final { scan-assembler "weak\[^\n\r\]*_?_ZZ1fvENKUlvE_cvPFvvEEv" { target { ! { *-*-darwin* *-*-mingw* *-*-cygwin *-*-hpux10* } } } } }
|
||||||
|
|
||||||
inline void f()
|
inline void f()
|
||||||
|
|
|
@ -50,7 +50,8 @@ struct S {
|
||||||
template<typename T> struct R {
|
template<typename T> struct R {
|
||||||
static int x;
|
static int x;
|
||||||
};
|
};
|
||||||
template<typename T> int R<T>::x = []{return 1;}();
|
// "int i;" makes the op() non-constexpr in C++17.
|
||||||
|
template<typename T> int R<T>::x = []{int i; return 1;}();
|
||||||
template int R<int>::x;
|
template int R<int>::x;
|
||||||
// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
|
// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
|
||||||
// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
|
// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
template <class T>
|
template <class T>
|
||||||
struct A
|
struct A
|
||||||
{
|
{
|
||||||
// { dg-final { scan-assembler "_ZNK1AIcE1pMUlvE_cvPFvvEEv" } }
|
// { dg-final { scan-assembler "_ZNK1AIcE1pMUlvE_clEv" } }
|
||||||
// { dg-final { scan-assembler "_ZNK1AIiE1pMUlvE_cvPFvvEEv" } }
|
// { dg-final { scan-assembler "_ZNK1AIiE1pMUlvE_clEv" } }
|
||||||
void (*p)() = []{};
|
void (*p)() = []{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
// { dg-options -std=c++1z }
|
||||||
|
|
||||||
|
constexpr auto Add5 = [](int i) { return i+5; };
|
||||||
|
|
||||||
|
constexpr int x = Add5(4);
|
||||||
|
static_assert(x==9);
|
|
@ -0,0 +1,10 @@
|
||||||
|
// Testcase from P0170R1
|
||||||
|
// { dg-options -std=c++1z }
|
||||||
|
|
||||||
|
void g() {
|
||||||
|
const int n = 0;
|
||||||
|
[=] {
|
||||||
|
constexpr int i = n; // OK, 'n' is not odr-used and not captured here.
|
||||||
|
constexpr int j = *&n; // { dg-error "" } '&n' would be an odr-use of 'n'.
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Testcase from P0170R1
|
||||||
|
// { dg-options -std=c++1z }
|
||||||
|
|
||||||
|
// 'v' & 'm' are odr-used but do not occur in a constant-expression within the nested
|
||||||
|
// lambda, so are well-formed.
|
||||||
|
auto monad = [](auto v) { return [=] { return v; }; };
|
||||||
|
auto bind = [](auto m) {
|
||||||
|
return [=](auto fvm) { return fvm(m()); };
|
||||||
|
};
|
||||||
|
// OK to have captures to automatic objects created during constant expression evaluation.
|
||||||
|
static_assert(bind(monad(2))(monad)() == monad(2)());
|
|
@ -0,0 +1,10 @@
|
||||||
|
// { dg-options -std=c++1z }
|
||||||
|
|
||||||
|
void f(int i)
|
||||||
|
{
|
||||||
|
[i]() constexpr {
|
||||||
|
int j; // { dg-error "uninitialized" }
|
||||||
|
j = i;
|
||||||
|
return j;
|
||||||
|
}();
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
// { dg-options -std=c++1z }
|
||||||
|
|
||||||
|
auto l1 = []() constexpr constexpr { }; // { dg-error "duplicate" }
|
||||||
|
auto l2 = []() mutable mutable { }; // { dg-error "duplicate" }
|
||||||
|
auto l3 = []() static { }; // { dg-error "static" }
|
|
@ -0,0 +1,4 @@
|
||||||
|
// { dg-options -std=c++14 }
|
||||||
|
|
||||||
|
auto l = []() constexpr { return 42; }; // { dg-error "constexpr" }
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Testcase from P0170R1
|
||||||
|
// { dg-options -std=c++1z }
|
||||||
|
|
||||||
|
constexpr int AddEleven(int n){
|
||||||
|
return[n]{return n+11;}();
|
||||||
|
}
|
||||||
|
static_assert(AddEleven(5)==16,"");
|
|
@ -0,0 +1,8 @@
|
||||||
|
// { dg-options -std=c++1z }
|
||||||
|
|
||||||
|
constexpr auto add = [] (int n, int m) {
|
||||||
|
auto L = [=] { return n; };
|
||||||
|
auto R = [=] { return m; };
|
||||||
|
return [=] { return L() + R(); };
|
||||||
|
};
|
||||||
|
static_assert(add(3, 4)() == 7, "");
|
|
@ -0,0 +1,4 @@
|
||||||
|
// { dg-options -std=c++1z }
|
||||||
|
|
||||||
|
auto ID = [] (int n) constexpr { return n; };
|
||||||
|
constexpr int I = ID(3);
|
|
@ -0,0 +1,7 @@
|
||||||
|
// { dg-options -std=c++1z }
|
||||||
|
|
||||||
|
auto addOne = [] (int n) {
|
||||||
|
return n + 1;
|
||||||
|
};
|
||||||
|
constexpr int (*addOneFp)(int) = addOne;
|
||||||
|
static_assert(addOneFp(3) == addOne(3), "");
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Testcase from P0170R1
|
||||||
|
// { dg-do run }
|
||||||
|
// { dg-options -std=c++1z }
|
||||||
|
|
||||||
|
auto monoid = [](auto v) { return [=] { return v; }; };
|
||||||
|
auto add = [](auto m1) constexpr {
|
||||||
|
auto ret = m1();
|
||||||
|
return [=](auto m2) mutable {
|
||||||
|
auto m1val = m1();
|
||||||
|
auto plus = [=] (auto m2val) mutable constexpr
|
||||||
|
{ return m1val += m2val; };
|
||||||
|
ret = plus(m2());
|
||||||
|
return monoid(ret);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
constexpr auto zero = monoid(0);
|
||||||
|
constexpr auto one = monoid(1);
|
||||||
|
static_assert(add(one)(zero)() == one()); // OK
|
||||||
|
// Since 'two' below is not declared constexpr, an evaluation of its constexpr
|
||||||
|
// member function call operator can not perform an lvalue-to-rvalue conversion
|
||||||
|
// on one of its subobjects (that represents its capture) in a constant
|
||||||
|
// expression.
|
||||||
|
auto two = monoid(2);
|
||||||
|
if (!(two() == 2)) __builtin_abort(); // OK, not a constant expression.
|
||||||
|
static_assert(add(one)(one)() == two()); // { dg-error "" } two() is not a constant expression
|
||||||
|
static_assert(add(one)(one)() == monoid(2)()); // OK
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Testcase from P0170R1
|
||||||
|
// { dg-options -std=c++1z }
|
||||||
|
|
||||||
|
auto ID = [](auto a) { return a; };
|
||||||
|
static_assert( ID (3) == 3); // OK
|
||||||
|
struct NonLiteral {
|
||||||
|
NonLiteral(int n) : n(n) { }
|
||||||
|
int n;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert( ID (NonLiteral{3}).n == 3); // { dg-error "non-literal" }
|
||||||
|
// { dg-prune-output "static assertion" }
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Testcase from P0170R1
|
||||||
|
// { dg-options -std=c++1z }
|
||||||
|
|
||||||
|
auto Fwd = [](int (*fp)(int), auto a) { return fp(a); };
|
||||||
|
auto C = [](auto a) { return a; };
|
||||||
|
static_assert( Fwd(C ,3) == 3); // OK
|
||||||
|
// No specialization of the function call operator template can be constexpr
|
||||||
|
// (because of the local static).
|
||||||
|
auto NC = [](auto a) { static int s; return a; }; // { dg-error "static" }
|
||||||
|
// { dg-message "operator int" "" { target *-*-* } 11 }
|
||||||
|
static_assert( Fwd(NC ,3) == 3); // { dg-error "" }
|
||||||
|
|
||||||
|
// We look for the string "operator int" to check that we aren't trying to do
|
||||||
|
// template pretty-printing in an expression; that gets incredibly unwieldy
|
||||||
|
// with the decltype magic we do for lambdas.
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Testcase from P0170R1
|
||||||
|
// { dg-options -std=c++1z }
|
||||||
|
|
||||||
|
static_assert([](int n) { return [&n] { return ++n; }(); }(3) == 4);
|
|
@ -128,8 +128,8 @@
|
||||||
|
|
||||||
#ifndef __cpp_constexpr
|
#ifndef __cpp_constexpr
|
||||||
# error "__cpp_constexpr"
|
# error "__cpp_constexpr"
|
||||||
#elif __cpp_constexpr != 201304
|
#elif __cpp_constexpr != 201603
|
||||||
# error "__cpp_constexpr != 201304"
|
# error "__cpp_constexpr != 201603"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef __cpp_decltype_auto
|
#ifndef __cpp_decltype_auto
|
||||||
|
|
Loading…
Reference in New Issue