Implement C++17 constexpr lambda.
authorJason Merrill <jason@redhat.com>
Tue, 9 Aug 2016 04:33:58 +0000 (00:33 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Tue, 9 Aug 2016 04:33:58 +0000 (00:33 -0400)
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

29 files changed:
gcc/c-family/ChangeLog
gcc/c-family/c-cppbuiltin.c
gcc/cp/ChangeLog
gcc/cp/class.c
gcc/cp/constexpr.c
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/error.c
gcc/cp/lambda.c
gcc/cp/parser.c
gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv.C
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle4.C
gcc/testsuite/g++.dg/cpp1z/constexpr-lambda1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-lambda10.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-lambda11.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-lambda14.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-lambda2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-lambda3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-lambda4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-lambda5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-lambda6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-lambda7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-lambda9.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C

index 5b264e9e7e29187acfcea23da6a66b2d2ccdc8d4..4b24dd62a7e18f39e45d9293a933ae36e23a1fc7 100644 (file)
@@ -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
index 3d4587e6db626db4aad7a22ca8b4186bd5c37fc4..46c70ac1150eb80941f24ae56e54bb79ad3e689d 100644 (file)
@@ -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
index 1ff70d4a8e748031d3a582c80321cf3771f39f3b..1344443b2dc3441c17f54dba6a38c222e457291c 100644 (file)
@@ -1,3 +1,30 @@
+2016-08-08  Jason Merrill  <jason@redhat.com>
+
+       Implement C++17 constexpr lambda.
+       * class.c (finalize_literal_type_property): Handle lambdas.
+       * constexpr.c (is_valid_constexpr_fn): Likewise.  No longer static.
+       (explain_invalid_constexpr_fn, cxx_eval_call_expression): Handle
+       lambdas.
+       (cxx_eval_constant_expression): Handle capture proxy.
+       (var_in_constexpr_fn): Don't check for C++14.
+       (var_in_maybe_constexpr_fn): New.
+       (potential_constant_expression_1): Use it.  Check DECL_EXPR for
+       declarations not allowed in constexpr function.  Handle
+       STATIC_ASSERT, RANGE_FOR_STMT.
+       * decl.c (make_rtl_for_nonlocal_decl): Use var_in_maybe_constexpr_fn.
+       (finish_function): Set DECL_DECLARED_CONSTEXPR_P on lambda members.
+       * lambda.c (begin_lambda_type): Set CLASSTYPE_LITERAL_P.
+       (maybe_add_lambda_conv_op): Clear thunk CALL_EXPR location.
+       (lambda_static_thunk_p): New.
+       * parser.c (cp_keyword_starts_decl_specifier_p): Add RID_CONSTEXPR.
+       (CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR): New enumerator.
+       (cp_parser_decl_specifier_seq): Handle it.
+       (cp_parser_lambda_declarator_opt): Use cp_parser_decl_specifier_seq.
+       * pt.c (instantiate_class_template_1): Set CLASSTYPE_LITERAL_P.
+       (tsubst_copy_and_build) [CALL_EXPR]: Propagate CALL_FROM_THUNK_P.
+       * error.c (dump_function_decl): Check TFF_NO_TEMPLATE_BINDINGS.
+       (dump_expr) [FUNCTION_DECL]: Pass it.
+
 2016-08-08  Jason Merrill  <jason@redhat.com>
 
        PR c++/67131
index d898c380057d2f64a8e8d1f47dc45c0faeb56ec0..e7cfabd4b5948de3a75e7c9bfcd92ce5e91d1312 100644 (file)
@@ -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);
index 2ced9c64e44cac6e90e73efe7dfe097ae7a61d98..a65b817af88e9b8eed0f87a8047658e67ba60b62 100644 (file)
@@ -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));
index d6fb387aced995026ed0f7a79d10c62200392290..70a42f87103abc814194c9febef1aaaa2cad0dfd 100644 (file)
@@ -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);
index 04a0df6414d99a3b8dc098aa428eddf3c8121e8b..45286d0bf67b71608e6394705c3f46b39ee53f53 100644 (file)
@@ -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);
index 36e26cc1b1da407be0c52ad1f8549b9a1ff3453c..0d466735f636306a322a80676343210c91af7aed 100644 (file)
@@ -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;
 
index 978fa0d841f4361a56a721f353bb9b3335462f66..d51118571bcac9505be000fdfd6683c27769fd18 100644 (file)
@@ -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.  */
 
index cff735ba65914b4167f558a087fa03735d1572b6..6db5e84475bd778aebcbf416633d6de00fa617b3 100644 (file)
@@ -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);
 
index d3d2d4e4c26b950bbc4a6607edd6c0d23b543f31..38840827ee7024cd5da99528303c825765292cb6 100644 (file)
@@ -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);
+                 }
              }
          }
 
index 08d8bbfc200f51504f8cd2e4b6ba04f49a3db704..417c1859db21556bac9576be3019439811d9144f 100644 (file)
@@ -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()
index 220817a733ff7628de94849b508c4f84ceb15d9a..20ef282ac24e5dd537b0e3258a12707c25baaf56 100644 (file)
@@ -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
index 0d37637fe9c2f0b717bc5e809f26d6ba00664157..b63c277e56cdcc6c6264f78c6a9c07bb1d7d9305 100644 (file)
@@ -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 (file)
index 0000000..a768cfb
--- /dev/null
@@ -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 (file)
index 0000000..ff65d6c
--- /dev/null
@@ -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 (file)
index 0000000..f9e662d
--- /dev/null
@@ -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 (file)
index 0000000..f5f3f38
--- /dev/null
@@ -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 (file)
index 0000000..077f823
--- /dev/null
@@ -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 (file)
index 0000000..26d078b
--- /dev/null
@@ -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 (file)
index 0000000..1d3ff82
--- /dev/null
@@ -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 (file)
index 0000000..46ee846
--- /dev/null
@@ -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 (file)
index 0000000..b3fd3d0
--- /dev/null
@@ -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 (file)
index 0000000..71f1852
--- /dev/null
@@ -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 (file)
index 0000000..bb20bad
--- /dev/null
@@ -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 (file)
index 0000000..26f136b
--- /dev/null
@@ -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 (file)
index 0000000..ac41306
--- /dev/null
@@ -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 (file)
index 0000000..a5bc524
--- /dev/null
@@ -0,0 +1,4 @@
+// Testcase from P0170R1
+// { dg-options -std=c++1z }
+
+static_assert([](int n) { return [&n] { return ++n; }(); }(3) == 4);
index c7becc1cbb47d05f99ec19a4ce03662782f04a7d..f5ed6ab9cc89eef477a3f90f71930da3072064a5 100644 (file)
 
 #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