From 98e5a19af592e5329ae7d991ad8d9e9b7b81be37 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Tue, 9 Aug 2016 00:33:58 -0400 Subject: [PATCH] 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 --- gcc/c-family/ChangeLog | 5 ++ gcc/c-family/c-cppbuiltin.c | 4 +- gcc/cp/ChangeLog | 27 ++++++ gcc/cp/class.c | 2 +- gcc/cp/constexpr.c | 86 ++++++++++++++++--- gcc/cp/cp-tree.h | 3 + gcc/cp/decl.c | 10 ++- gcc/cp/error.c | 2 + gcc/cp/lambda.c | 18 ++++ gcc/cp/parser.c | 46 ++++++++-- gcc/cp/pt.c | 12 ++- .../g++.dg/cpp0x/lambda/lambda-conv.C | 2 +- .../g++.dg/cpp0x/lambda/lambda-mangle.C | 3 +- .../g++.dg/cpp0x/lambda/lambda-mangle4.C | 4 +- .../g++.dg/cpp1z/constexpr-lambda1.C | 6 ++ .../g++.dg/cpp1z/constexpr-lambda10.C | 10 +++ .../g++.dg/cpp1z/constexpr-lambda11.C | 11 +++ .../g++.dg/cpp1z/constexpr-lambda12.C | 10 +++ .../g++.dg/cpp1z/constexpr-lambda13.C | 5 ++ .../g++.dg/cpp1z/constexpr-lambda14.C | 4 + .../g++.dg/cpp1z/constexpr-lambda2.C | 7 ++ .../g++.dg/cpp1z/constexpr-lambda3.C | 8 ++ .../g++.dg/cpp1z/constexpr-lambda4.C | 4 + .../g++.dg/cpp1z/constexpr-lambda5.C | 7 ++ .../g++.dg/cpp1z/constexpr-lambda6.C | 30 +++++++ .../g++.dg/cpp1z/constexpr-lambda7.C | 12 +++ .../g++.dg/cpp1z/constexpr-lambda8.C | 15 ++++ .../g++.dg/cpp1z/constexpr-lambda9.C | 4 + gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C | 4 +- 29 files changed, 332 insertions(+), 29 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda1.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda10.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda11.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda14.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda2.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda3.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda4.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda5.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda6.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda7.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda9.C diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 5b264e9e7e2..4b24dd62a7e 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,8 @@ +2016-08-09 Jason Merrill + + * c-cppbuiltin.c (c_cpp_builtins): Update __cpp_constexpr for + C++17 constexpr lambdas. + 2016-08-08 David Malcolm PR c/64955 diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c index 3d4587e6db6..46c70ac1150 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 1ff70d4a8e7..1344443b2dc 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,30 @@ +2016-08-08 Jason Merrill + + 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 PR c++/67131 diff --git a/gcc/cp/class.c b/gcc/cp/class.c index d898c380057..e7cfabd4b59 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 2ced9c64e44..a65b817af88 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 *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 " + "% in % function", tmp); + return false; + } + else if (CP_DECL_THREAD_LOCAL_P (tmp)) + { + if (flags & tf_error) + error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared " + "% in % 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 % 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 d6fb387aced..70a42f87103 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 cx_error_context (void); extern tree fold_sizeof_expr (tree); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 04a0df6414d..45286d0bf67 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 36e26cc1b1d..0d466735f63 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 978fa0d841f..d51118571bc 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 cff735ba659..6db5e84475b 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 %"); + } 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], "% " + "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 d3d2d4e4c26..38840827ee7 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 08d8bbfc200..417c1859db2 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 220817a733f..20ef282ac24 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 struct R { static int x; }; -template int R::x = []{return 1;}(); +// "int i;" makes the op() non-constexpr in C++17. +template int R::x = []{int i; return 1;}(); template int R::x; // Type of lambda in intializer of R::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 0d37637fe9c..b63c277e56c 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 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 00000000000..a768cfb22ca --- /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 00000000000..ff65d6c7d47 --- /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 00000000000..f9e662d31c5 --- /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 00000000000..f5f3f385d3f --- /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 00000000000..077f8235d5f --- /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 00000000000..26d078b6db3 --- /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 00000000000..1d3ff826fa4 --- /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 00000000000..46ee84686b1 --- /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 00000000000..b3fd3d0030e --- /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 00000000000..71f1852a4bd --- /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 00000000000..bb20badd286 --- /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 00000000000..26f136b6c79 --- /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 00000000000..ac413066bd9 --- /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 00000000000..a5bc524f0ec --- /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 c7becc1cbb4..f5ed6ab9cc8 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 -- 2.30.2