aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2016-08-09 00:33:58 -0400
committerJason Merrill <jason@gcc.gnu.org>2016-08-09 00:33:58 -0400
commit98e5a19af592e5329ae7d991ad8d9e9b7b81be37 (patch)
treec6df51387a49c0bebc8f02921379e3e80cdba861 /gcc
parent7dc2b4a235481acda5ae9e51f4cc0401b1fb192f (diff)
downloadgcc-98e5a19af592e5329ae7d991ad8d9e9b7b81be37.zip
gcc-98e5a19af592e5329ae7d991ad8d9e9b7b81be37.tar.gz
gcc-98e5a19af592e5329ae7d991ad8d9e9b7b81be37.tar.bz2
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
Diffstat (limited to 'gcc')
-rw-r--r--gcc/c-family/ChangeLog5
-rw-r--r--gcc/c-family/c-cppbuiltin.c4
-rw-r--r--gcc/cp/ChangeLog27
-rw-r--r--gcc/cp/class.c2
-rw-r--r--gcc/cp/constexpr.c86
-rw-r--r--gcc/cp/cp-tree.h3
-rw-r--r--gcc/cp/decl.c10
-rw-r--r--gcc/cp/error.c2
-rw-r--r--gcc/cp/lambda.c18
-rw-r--r--gcc/cp/parser.c46
-rw-r--r--gcc/cp/pt.c12
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv.C2
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C3
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle4.C4
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/constexpr-lambda1.C6
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/constexpr-lambda10.C10
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/constexpr-lambda11.C11
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C10
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C5
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/constexpr-lambda14.C4
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/constexpr-lambda2.C7
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/constexpr-lambda3.C8
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/constexpr-lambda4.C4
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/constexpr-lambda5.C7
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/constexpr-lambda6.C30
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/constexpr-lambda7.C12
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C15
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/constexpr-lambda9.C4
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C4
29 files changed, 332 insertions, 29 deletions
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index 5b264e9..4b24dd6 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -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>
PR c/64955
diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
index 3d4587e..46c70ac 100644
--- a/gcc/c-family/c-cppbuiltin.c
+++ b/gcc/c-family/c-cppbuiltin.c
@@ -863,7 +863,8 @@ c_cpp_builtins (cpp_reader *pfile)
cpp_define (pfile, "__cpp_return_type_deduction=201304");
cpp_define (pfile, "__cpp_init_captures=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_aggregate_nsdmi=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_nontype_template_args=201411");
cpp_define (pfile, "__cpp_range_based_for=201603");
+ cpp_define (pfile, "__cpp_constexpr=201603");
}
if (flag_concepts)
/* Use a value smaller than the 201507 specified in
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 1ff70d4..1344443 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,32 @@
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>
+
PR c++/67131
* class.c (is_really_empty_class): Call complete_type.
* constexpr.c (cxx_eval_constant_expression): Check
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index d898c38..e7cfabd 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -5659,7 +5659,7 @@ finalize_literal_type_property (tree t)
&& !DECL_CONSTRUCTOR_P (fn))
{
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 "
"function %q+#D is not a literal type", fn);
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 2ced9c6..a65b817 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -166,7 +166,7 @@ retrieve_constexpr_fundef (tree fun)
/* Check whether the parameter and return types of FUN are valid for a
constexpr function, and complain if COMPLAIN. */
-static bool
+bool
is_valid_constexpr_fn (tree fun, bool complain)
{
bool ret = true;
@@ -832,8 +832,9 @@ explain_invalid_constexpr_fn (tree fun)
static hash_set<tree> *diagnosed;
tree body;
location_t save_loc;
- /* Only diagnose defaulted functions or instantiations. */
+ /* Only diagnose defaulted functions, lambdas, or instantiations. */
if (!DECL_DEFAULTED_FN (fun)
+ && !LAMBDA_TYPE_P (CP_DECL_CONTEXT (fun))
&& !is_instantiation_of_constexpr (fun))
return;
if (diagnosed == NULL)
@@ -843,14 +844,20 @@ explain_invalid_constexpr_fn (tree fun)
return;
save_loc = input_location;
- input_location = DECL_SOURCE_LOCATION (fun);
- inform (input_location,
- "%qD is not usable as a constexpr function because:", fun);
+ if (!lambda_static_thunk_p (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. */
if (is_valid_constexpr_fn (fun, true))
{
/* 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);
else
{
@@ -1464,8 +1471,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
"definition is complete", fun);
else if (DECL_INITIAL (fun))
{
- /* The definition of fun was somehow unsuitable. */
- error_at (loc, "%qD called in a constant expression", fun);
+ /* The definition of fun was somehow unsuitable. But pretend
+ 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);
}
else
@@ -3096,14 +3105,30 @@ cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t,
return val;
}
+/* True if T was declared in a function declared to be constexpr, and
+ therefore potentially constant in C++14. */
+
bool
var_in_constexpr_fn (tree 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));
}
+/* 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. */
static tree
@@ -3665,6 +3690,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
return (*ctx->values->get (t));
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:
/* We used to not check lval for CONST_DECL, but darwin.c uses
CONST_DECL for aggregate constants. */
@@ -4775,6 +4804,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
case BREAK_STMT:
case CONTINUE_STMT:
case REQUIRES_EXPR:
+ case STATIC_ASSERT:
return true;
case AGGR_INIT_EXPR:
@@ -4900,7 +4930,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
case VAR_DECL:
if (want_rval
- && !var_in_constexpr_fn (t)
+ && !var_in_maybe_constexpr_fn (t)
&& !type_dependent_expression_p (t)
&& !decl_constant_var_p (t)
&& (strict
@@ -5042,6 +5072,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
return false;
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:
if (!RECUR (WHILE_COND (t), rval))
return false;
@@ -5172,7 +5209,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
case EH_SPEC_BLOCK:
case EXPR_STMT:
case PAREN_EXPR:
- case DECL_EXPR:
case NON_DEPENDENT_EXPR:
/* For convenience. */
case RETURN_EXPR:
@@ -5180,6 +5216,34 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
case EXIT_EXPR:
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:
return (RECUR (TREE_OPERAND (t, 0), want_rval)
&& RECUR (TREE_OPERAND (t, 1), any));
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d6fb387..70a42f8 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6478,6 +6478,7 @@ extern tree current_nonlambda_scope (void);
extern bool generic_lambda_fn_p (tree);
extern void maybe_add_lambda_conv_op (tree);
extern bool is_lambda_ignored_entity (tree);
+extern bool lambda_static_thunk_p (tree);
/* in tree.c */
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 bool literal_type_p (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 tree ensure_literal_type_for_constexpr_object (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 is_instantiation_of_constexpr (tree);
extern bool var_in_constexpr_fn (tree);
+extern bool var_in_maybe_constexpr_fn (tree);
extern void explain_invalid_constexpr_fn (tree);
extern vec<tree> cx_error_context (void);
extern tree fold_sizeof_expr (tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 04a0df6..45286d0 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -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
be expanded, so go ahead and tell cgraph about the variable now. */
defer_p = ((DECL_FUNCTION_SCOPE_P (decl)
- && !DECL_DECLARED_CONSTEXPR_P (DECL_CONTEXT (decl)))
+ && !var_in_maybe_constexpr_fn (decl))
|| DECL_VIRTUAL_P (decl));
/* Defer template instantiations. */
@@ -14748,6 +14748,14 @@ finish_function (int flags)
if (DECL_DECLARED_CONCEPT_P (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
the NRV transformation. */
maybe_save_function_definition (fndecl);
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 36e26cc..0d46673 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -1509,6 +1509,7 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
/* Pretty print template instantiations only. */
if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
+ && !(flags & TFF_NO_TEMPLATE_BINDINGS)
&& flag_pretty_templates)
{
tree tmpl;
@@ -1989,6 +1990,7 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
case IDENTIFIER_NODE:
dump_decl (pp, t, ((flags & ~(TFF_DECL_SPECIFIERS|TFF_RETURN_TYPE
|TFF_TEMPLATE_HEADER))
+ | TFF_NO_TEMPLATE_BINDINGS
| TFF_NO_FUNCTION_ARGUMENTS));
break;
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index 978fa0d..d511185 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -154,6 +154,11 @@ begin_lambda_type (tree lambda)
LAMBDA_EXPR_CLOSURE (lambda) = type;
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. */
xref_basetypes (type, /*bases=*/NULL_TREE);
@@ -1004,6 +1009,7 @@ maybe_add_lambda_conv_op (tree type)
direct_argvec->address ());
CALL_FROM_THUNK_P (call) = 1;
+ SET_EXPR_LOCATION (call, UNKNOWN_LOCATION);
tree stattype = build_function_type (fn_result, FUNCTION_ARG_CHAIN (callop));
stattype = (cp_build_type_attribute_variant
@@ -1141,6 +1147,18 @@ maybe_add_lambda_conv_op (tree type)
--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
be ignored by unqualified lookup. */
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index cff735b..6db5e84 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -981,6 +981,7 @@ cp_keyword_starts_decl_specifier_p (enum rid keyword)
/* C++0x extensions. */
case RID_DECLTYPE:
case RID_UNDERLYING_TYPE:
+ case RID_CONSTEXPR:
return true;
default:
@@ -1800,7 +1801,9 @@ enum
CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS = 0x4,
/* When parsing a decl-specifier-seq, only allow type-specifier or
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
@@ -10035,7 +10038,7 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
< template-parameter-list [opt] >
( parameter-declaration-clause [opt] )
attribute-specifier [opt]
- mutable [opt]
+ decl-specifier-seq [opt]
exception-specification [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 template_param_list = 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
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);
- /* Parse optional `mutable' keyword. */
- if (cp_lexer_next_token_is_keyword (parser->lexer, RID_MUTABLE))
- {
- cp_lexer_consume_token (parser->lexer);
- LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
- }
+ /* In the decl-specifier-seq of the lambda-declarator, each
+ decl-specifier shall either be mutable or constexpr. */
+ int declares_class_or_enum;
+ if (cp_lexer_next_token_is_decl_specifier_keyword (parser->lexer))
+ 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);
@@ -10143,6 +10156,16 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
/* Maybe we will deduce the return type later. */
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);
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)
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)
set_and_check_decl_spec_loc (decl_specs, ds, token);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index d3d2d4e..3884082 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -10334,6 +10334,9 @@ instantiate_class_template_1 (tree type)
tree decl = lambda_function (type);
if (decl)
{
+ if (cxx_dialect >= cxx1z)
+ CLASSTYPE_LITERAL_P (type) = true;
+
if (!DECL_TEMPLATE_INFO (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 ord = CALL_EXPR_ORDERED_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);
CALL_EXPR_OPERATOR_SYNTAX (function) = op;
CALL_EXPR_ORDERED_ARGS (function) = ord;
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);
+ }
}
}
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv.C
index 08d8bbf..417c185 100644
--- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv.C
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv.C
@@ -1,6 +1,6 @@
// 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* } } } } }
inline void f()
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
index 220817a..20ef282 100644
--- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
@@ -50,7 +50,8 @@ struct S {
template<typename T> struct R {
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;
// Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
// Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle4.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle4.C
index 0d37637..b63c277 100644
--- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle4.C
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle4.C
@@ -4,8 +4,8 @@
template <class T>
struct A
{
- // { dg-final { scan-assembler "_ZNK1AIcE1pMUlvE_cvPFvvEEv" } }
- // { dg-final { scan-assembler "_ZNK1AIiE1pMUlvE_cvPFvvEEv" } }
+ // { dg-final { scan-assembler "_ZNK1AIcE1pMUlvE_clEv" } }
+ // { dg-final { scan-assembler "_ZNK1AIiE1pMUlvE_clEv" } }
void (*p)() = []{};
};
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda1.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda1.C
new file mode 100644
index 0000000..a768cfb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda1.C
@@ -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);
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda10.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda10.C
new file mode 100644
index 0000000..ff65d6c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda10.C
@@ -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'.
+ };
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda11.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda11.C
new file mode 100644
index 0000000..f9e662d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda11.C
@@ -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)());
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
new file mode 100644
index 0000000..f5f3f38
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
@@ -0,0 +1,10 @@
+// { dg-options -std=c++1z }
+
+void f(int i)
+{
+ [i]() constexpr {
+ int j; // { dg-error "uninitialized" }
+ j = i;
+ return j;
+ }();
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C
new file mode 100644
index 0000000..077f823
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C
@@ -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" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda14.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda14.C
new file mode 100644
index 0000000..26d078b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda14.C
@@ -0,0 +1,4 @@
+// { dg-options -std=c++14 }
+
+auto l = []() constexpr { return 42; }; // { dg-error "constexpr" }
+
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda2.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda2.C
new file mode 100644
index 0000000..1d3ff82
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda2.C
@@ -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,"");
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda3.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda3.C
new file mode 100644
index 0000000..46ee846
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda3.C
@@ -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, "");
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda4.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda4.C
new file mode 100644
index 0000000..b3fd3d0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda4.C
@@ -0,0 +1,4 @@
+// { dg-options -std=c++1z }
+
+auto ID = [] (int n) constexpr { return n; };
+constexpr int I = ID(3);
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda5.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda5.C
new file mode 100644
index 0000000..71f1852
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda5.C
@@ -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), "");
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda6.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda6.C
new file mode 100644
index 0000000..bb20bad
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda6.C
@@ -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
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda7.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda7.C
new file mode 100644
index 0000000..26f136b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda7.C
@@ -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" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C
new file mode 100644
index 0000000..ac41306
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C
@@ -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.
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda9.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda9.C
new file mode 100644
index 0000000..a5bc524
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda9.C
@@ -0,0 +1,4 @@
+// Testcase from P0170R1
+// { dg-options -std=c++1z }
+
+static_assert([](int n) { return [&n] { return ++n; }(); }(3) == 4);
diff --git a/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C b/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
index c7becc1..f5ed6ab 100644
--- a/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
+++ b/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
@@ -128,8 +128,8 @@
#ifndef __cpp_constexpr
# error "__cpp_constexpr"
-#elif __cpp_constexpr != 201304
-# error "__cpp_constexpr != 201304"
+#elif __cpp_constexpr != 201603
+# error "__cpp_constexpr != 201603"
#endif
#ifndef __cpp_decltype_auto