From patchwork Tue Aug 9 14:06:18 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jason Merrill X-Patchwork-Id: 657274 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3s7x0x3CRhz9stc for ; Wed, 10 Aug 2016 00:06:57 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b=Zrj2vXLG; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :mime-version:from:date:message-id:subject:to:content-type; q= dns; s=default; b=YvZeUOsoabrXDTjpBdxRuhodlaoDXoqmXPnlPGfs5WuViy 3k7Ttn8st+dizy8IzunBfanwfNwhInaQCd1kHsR97zuYdcVoAfztgXkrxDovU4bS Gyouka9YPyyQei34DNGCFZT72PGYZt110Q6loRYjq2GRgikZaD0YKWHR/p6/Y= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :mime-version:from:date:message-id:subject:to:content-type; s= default; bh=QYIrksFGNdHSZu22g8PI52MZon0=; b=Zrj2vXLGhwFPMLVEU66X VQwvj+nBnJHsUWVqVV0rthyYQ6T1I6ltA0rjsu8gMHOzf+ATOqa66eM0EHNunIMz meFFyrHQmX4vABti1AWZjODo55dD4GxCptrdYxZd02A+82U0zL7VWeGRmQJbze0X IRdEAvHLqxnTBUdK0PiZ+fg= Received: (qmail 104439 invoked by alias); 9 Aug 2016 14:06:47 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 102186 invoked by uid 89); 9 Aug 2016 14:06:45 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-0.4 required=5.0 tests=AWL, BAYES_50, KAM_LAZY_DOMAIN_SECURITY, RCVD_IN_DNSWL_LOW autolearn=no version=3.3.2 spammy=1, 12, thk, 0x10, sk:generic X-HELO: mail-oi0-f48.google.com Received: from mail-oi0-f48.google.com (HELO mail-oi0-f48.google.com) (209.85.218.48) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-GCM-SHA256 encrypted) ESMTPS; Tue, 09 Aug 2016 14:06:41 +0000 Received: by mail-oi0-f48.google.com with SMTP id 4so18813137oih.2 for ; Tue, 09 Aug 2016 07:06:41 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=VbMOlVnYRZ+xMKvGUmXbGT0dXveHIxb5LU0/TLQRKXw=; b=XSewgQ36NsWBsvtUvm8hnaxW/39uZBB2bB3kDLq1/TPIa5ZDg6O7kOb184FzshOeus 6embD7iN1ns4Jrt+YEy8rb4XJs08Vu2BRnCe8kWox/dlVlf4VTsbLaycdMEVJH9V0AT6 siNvqHi9ZlJUSJZ3/QOcK+fGWIMxCZgfPU7rJ0xkX+3MrIzZMqxwIz1VZTk5Iyep50Hb 53wmr4oSboIPPsevLKw0zQ98U188MgJDSajSQGH7fKkAdCPf0Yi6PiQ1uy5QoZs5JbZb yVPVnkvJhuIb2MZI7Mm7n1cKj1izylrhzkPk1SxadDvkJFmnsb/aLZV4zkt2/gi1cmJu XzWQ== X-Gm-Message-State: AEkoouvsCN+vHW0alHattzpjEQ2m0jHTSPbi8JcKYamYe6NsvR4rgQL0vthtCxJ723XcAUlT43jFtC/wkYHt/d+X X-Received: by 10.157.7.137 with SMTP id 9mr16856843oto.155.1470751599329; Tue, 09 Aug 2016 07:06:39 -0700 (PDT) MIME-Version: 1.0 Received: by 10.182.105.169 with HTTP; Tue, 9 Aug 2016 07:06:18 -0700 (PDT) From: Jason Merrill Date: Tue, 9 Aug 2016 10:06:18 -0400 Message-ID: Subject: C++ PATCH for C++17 constexpr lambda To: gcc-patches List X-IsSubscribed: yes The first patch fixes a constexpr issue whereby we were complaining about passing a non-constant empty class to a constexpr function; this is OK because there's no actual copying involved. The second patch implements the C++17 constexpr lambda proposal. This was mostly pretty straightforward. A few notes: * We now call potential_constant_expression on the entire DECL_SAVED_TREE of a function, which turned up a few unhandled tree codes. * When we diagnose constexpr failure, we want to look through the static thunk returned from the lambda conversion operator, since it isn't part of the language. * Looking at those diagnostics also led me to notice that we were printing temploid functions as template and bindings even when the function appears as part of an expression. This is extremely convoluted and unhelpful when we're looking at a call to the conversion operator of a generic lambda, which involves a complicated decltype. So we don't do that anymore in expression context. Tested x86_64-pc-linux-gnu, applied to trunk. commit a906fc606677b942489aa21392aca1dc0837c856 Author: Jason Merrill Date: Sat Aug 6 23:46:27 2016 -0400 Fix empty class parameters with constexpr. PR c++/67131 * class.c (is_really_empty_class): Call complete_type. * constexpr.c (cxx_eval_constant_expression): Check is_really_empty_class. (potential_constant_expression_1): Likewise. Check for error type. commit 90159d8f9e5d28fcad4dd077184758b3c62ed5bc Author: Jason Merrill Date: Fri Aug 5 15:26:48 2016 -0400 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. 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/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 *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 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 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 72a494d..b11eb6a 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -980,6 +980,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: @@ -1799,7 +1800,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 @@ -10034,7 +10037,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] @@ -10053,6 +10056,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. */ @@ -10096,12 +10101,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); @@ -10142,6 +10155,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), @@ -12775,6 +12798,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 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 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 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 diff --git a/gcc/cp/class.c b/gcc/cp/class.c index f834965..d898c38 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -8419,7 +8419,7 @@ is_really_empty_class (tree type) /* CLASSTYPE_EMPTY_P isn't set properly until the class is actually laid out, but we'd like to be able to check this before then. */ - if (COMPLETE_TYPE_P (type) && is_empty_class (type)) + if (COMPLETE_TYPE_P (complete_type (type)) && is_empty_class (type)) return true; for (binfo = TYPE_BINFO (type), i = 0; diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index e7b08c8..2ced9c6 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -3670,7 +3670,13 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, CONST_DECL for aggregate constants. */ if (lval) return t; - if (ctx->strict) + if (is_really_empty_class (TREE_TYPE (t))) + { + /* If the class is empty, we aren't actually loading anything. */ + r = build_constructor (TREE_TYPE (t), NULL); + TREE_CONSTANT (r) = true; + } + else if (ctx->strict) r = decl_really_constant_value (t); else r = decl_constant_value (t); @@ -3705,7 +3711,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, /* Defer in case this is only used for its type. */; else if (TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE) /* Defer, there's no lvalue->rvalue conversion. */; - else if (is_empty_class (TREE_TYPE (t))) + else if (is_really_empty_class (TREE_TYPE (t))) { /* If the class is empty, we aren't actually loading anything. */ r = build_constructor (TREE_TYPE (t), NULL); @@ -4736,6 +4742,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, } if (CONSTANT_CLASS_P (t)) return true; + if (CODE_CONTAINS_STRUCT (TREE_CODE (t), TS_TYPED) + && TREE_TYPE (t) == error_mark_node) + return false; switch (TREE_CODE (t)) { @@ -4891,12 +4900,14 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, case VAR_DECL: if (want_rval + && !var_in_constexpr_fn (t) + && !type_dependent_expression_p (t) && !decl_constant_var_p (t) && (strict || !CP_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (t)) || !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (t)) - && !var_in_constexpr_fn (t) - && !type_dependent_expression_p (t)) + && COMPLETE_TYPE_P (TREE_TYPE (t)) + && !is_really_empty_class (TREE_TYPE (t))) { if (flags & tf_error) non_const_var_error (t); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-empty12.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-empty12.C new file mode 100644 index 0000000..7463442 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-empty12.C @@ -0,0 +1,5 @@ +// { dg-do compile { target c++11 } } + +struct A { } a; +constexpr int f (A a) { return 42; } +constexpr int i = f(a); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-empty13.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-empty13.C new file mode 100644 index 0000000..1896ead --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-empty13.C @@ -0,0 +1,7 @@ +// { dg-do compile { target c++11 } } + +struct A { + struct B { } b; +} a; +constexpr int f (A a) { return 42; } +constexpr int i = f(a); diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ42.C b/gcc/testsuite/g++.dg/cpp1y/var-templ42.C index a43149d..de11b26 100644 --- a/gcc/testsuite/g++.dg/cpp1y/var-templ42.C +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ42.C @@ -14,4 +14,4 @@ template