diff mbox

C++ PATCH for C++17 constexpr lambda

Message ID CADzB+2kHaApR224oZcC5iqGSp5XUfryNA7vS0XO3yhg0BGnAyw@mail.gmail.com
State New
Headers show

Commit Message

Jason Merrill Aug. 9, 2016, 2:06 p.m. UTC
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 <jason@redhat.com>
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 <jason@redhat.com>
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<tree> *diagnosed;
   tree body;
   location_t save_loc;
-  /* Only diagnose defaulted functions or instantiations.  */
+  /* Only diagnose defaulted functions, lambdas, or instantiations.  */
   if (!DECL_DEFAULTED_FN (fun)
+      && !LAMBDA_TYPE_P (CP_DECL_CONTEXT (fun))
       && !is_instantiation_of_constexpr (fun))
     return;
   if (diagnosed == NULL)
@@ -843,14 +844,20 @@ explain_invalid_constexpr_fn (tree fun)
     return;
 
   save_loc = input_location;
-  input_location = DECL_SOURCE_LOCATION (fun);
-  inform (input_location,
-	  "%qD is not usable as a constexpr function because:", fun);
+  if (!lambda_static_thunk_p (fun))
+    {
+      /* Diagnostics should completely ignore the static thunk, so leave
+	 input_location set to our caller's location.  */
+      input_location = DECL_SOURCE_LOCATION (fun);
+      inform (input_location,
+	      "%qD is not usable as a constexpr function because:", fun);
+    }
   /* First check the declaration.  */
   if (is_valid_constexpr_fn (fun, true))
     {
       /* Then if it's OK, the body.  */
-      if (!DECL_DECLARED_CONSTEXPR_P (fun))
+      if (!DECL_DECLARED_CONSTEXPR_P (fun)
+	  && !LAMBDA_TYPE_P (CP_DECL_CONTEXT (fun)))
 	explain_implicit_non_constexpr (fun);
       else
 	{
@@ -1464,8 +1471,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 			  "definition is complete", fun);
 	      else if (DECL_INITIAL (fun))
 		{
-		  /* The definition of fun was somehow unsuitable.  */
-		  error_at (loc, "%qD called in a constant expression", fun);
+		  /* The definition of fun was somehow unsuitable.  But pretend
+		     that lambda static thunks don't exist.  */
+		  if (!lambda_static_thunk_p (fun))
+		    error_at (loc, "%qD called in a constant expression", fun);
 		  explain_invalid_constexpr_fn (fun);
 		}
 	      else
@@ -3096,14 +3105,30 @@ cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t,
   return val;
 }
 
+/* True if T was declared in a function declared to be constexpr, and
+   therefore potentially constant in C++14.  */
+
 bool
 var_in_constexpr_fn (tree t)
 {
   tree ctx = DECL_CONTEXT (t);
-  return (cxx_dialect >= cxx14 && ctx && TREE_CODE (ctx) == FUNCTION_DECL
+  return (ctx && TREE_CODE (ctx) == FUNCTION_DECL
 	  && DECL_DECLARED_CONSTEXPR_P (ctx));
 }
 
+/* True if T was declared in a function that might be constexpr: either a
+   function that was declared constexpr, or a C++17 lambda op().  */
+
+bool
+var_in_maybe_constexpr_fn (tree t)
+{
+  if (cxx_dialect >= cxx1z
+      && DECL_FUNCTION_SCOPE_P (t)
+      && LAMBDA_FUNCTION_P (DECL_CONTEXT (t)))
+    return true;
+  return var_in_constexpr_fn (t);
+}
+
 /* Evaluate an INIT_EXPR or MODIFY_EXPR.  */
 
 static tree
@@ -3665,6 +3690,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       return (*ctx->values->get (t));
 
     case VAR_DECL:
+      if (is_capture_proxy (t))
+	return cxx_eval_constant_expression (ctx, DECL_VALUE_EXPR (t),
+					     lval, non_constant_p, overflow_p);
+      /* else fall through.  */
     case CONST_DECL:
       /* We used to not check lval for CONST_DECL, but darwin.c uses
 	 CONST_DECL for aggregate constants.  */
@@ -4775,6 +4804,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
     case BREAK_STMT:
     case CONTINUE_STMT:
     case REQUIRES_EXPR:
+    case STATIC_ASSERT:
       return true;
 
     case AGGR_INIT_EXPR:
@@ -4900,7 +4930,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
 
     case VAR_DECL:
       if (want_rval
-	  && !var_in_constexpr_fn (t)
+	  && !var_in_maybe_constexpr_fn (t)
 	  && !type_dependent_expression_p (t)
 	  && !decl_constant_var_p (t)
 	  && (strict
@@ -5042,6 +5072,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
 	return false;
       return true;
 
+    case RANGE_FOR_STMT:
+      if (!RECUR (RANGE_FOR_EXPR (t), any))
+	return false;
+      if (!RECUR (RANGE_FOR_BODY (t), any))
+	return false;
+      return true;
+
     case WHILE_STMT:
       if (!RECUR (WHILE_COND (t), rval))
 	return false;
@@ -5172,7 +5209,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
     case EH_SPEC_BLOCK:
     case EXPR_STMT:
     case PAREN_EXPR:
-    case DECL_EXPR:
     case NON_DEPENDENT_EXPR:
       /* For convenience.  */
     case RETURN_EXPR:
@@ -5180,6 +5216,34 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
     case EXIT_EXPR:
       return RECUR (TREE_OPERAND (t, 0), want_rval);
 
+    case DECL_EXPR:
+      tmp = DECL_EXPR_DECL (t);
+      if (VAR_P (tmp) && !DECL_ARTIFICIAL (tmp))
+	{
+	  if (TREE_STATIC (tmp))
+	    {
+	      if (flags & tf_error)
+		error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared "
+			  "%<static%> in %<constexpr%> function", tmp);
+	      return false;
+	    }
+	  else if (CP_DECL_THREAD_LOCAL_P (tmp))
+	    {
+	      if (flags & tf_error)
+		error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared "
+			  "%<thread_local%> in %<constexpr%> function", tmp);
+	      return false;
+	    }
+	  else if (!DECL_NONTRIVIALLY_INITIALIZED_P (tmp))
+	    {
+	      if (flags & tf_error)
+		error_at (DECL_SOURCE_LOCATION (tmp), "uninitialized "
+			  "variable %qD in %<constexpr%> function", tmp);
+	      return false;
+	    }
+	}
+      return RECUR (tmp, want_rval);
+
     case TRY_FINALLY_EXPR:
       return (RECUR (TREE_OPERAND (t, 0), want_rval)
 	      && RECUR (TREE_OPERAND (t, 1), any));
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d6fb387..70a42f8 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6478,6 +6478,7 @@ extern tree current_nonlambda_scope		(void);
 extern bool generic_lambda_fn_p			(tree);
 extern void maybe_add_lambda_conv_op            (tree);
 extern bool is_lambda_ignored_entity            (tree);
+extern bool lambda_static_thunk_p		(tree);
 
 /* in tree.c */
 extern int cp_tree_operand_length		(const_tree);
@@ -6922,6 +6923,7 @@ bool cilkplus_an_triplet_types_ok_p             (location_t, tree, tree, tree,
 extern void fini_constexpr			(void);
 extern bool literal_type_p                      (tree);
 extern tree register_constexpr_fundef           (tree, tree);
+extern bool is_valid_constexpr_fn		(tree, bool);
 extern bool check_constexpr_ctor_body           (tree, tree, bool);
 extern tree ensure_literal_type_for_constexpr_object (tree);
 extern bool potential_constant_expression       (tree);
@@ -6940,6 +6942,7 @@ extern bool is_sub_constant_expr                (tree);
 extern bool reduced_constant_expression_p       (tree);
 extern bool is_instantiation_of_constexpr       (tree);
 extern bool var_in_constexpr_fn                 (tree);
+extern bool var_in_maybe_constexpr_fn           (tree);
 extern void explain_invalid_constexpr_fn        (tree);
 extern vec<tree> cx_error_context               (void);
 extern tree fold_sizeof_expr			(tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 04a0df6..45286d0 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -6305,7 +6305,7 @@ make_rtl_for_nonlocal_decl (tree decl, tree init, const char* asmspec)
      DECL_EXPR is expanded.  But with constexpr its function might never
      be expanded, so go ahead and tell cgraph about the variable now.  */
   defer_p = ((DECL_FUNCTION_SCOPE_P (decl)
-	      && !DECL_DECLARED_CONSTEXPR_P (DECL_CONTEXT (decl)))
+	      && !var_in_maybe_constexpr_fn (decl))
 	     || DECL_VIRTUAL_P (decl));
 
   /* Defer template instantiations.  */
@@ -14748,6 +14748,14 @@ finish_function (int flags)
   if (DECL_DECLARED_CONCEPT_P (fndecl))
     check_function_concept (fndecl);
 
+  /* Lambda closure members are implicitly constexpr if possible.  */
+  if (cxx_dialect >= cxx1z
+      && LAMBDA_TYPE_P (CP_DECL_CONTEXT (fndecl))
+      && (processing_template_decl
+	  || is_valid_constexpr_fn (fndecl, /*complain*/false))
+      && potential_constant_expression (DECL_SAVED_TREE (fndecl)))
+    DECL_DECLARED_CONSTEXPR_P (fndecl) = true;
+
   /* Save constexpr function body before it gets munged by
      the NRV transformation.   */
   maybe_save_function_definition (fndecl);
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 36e26cc..0d46673 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -1509,6 +1509,7 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
 
   /* Pretty print template instantiations only.  */
   if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
+      && !(flags & TFF_NO_TEMPLATE_BINDINGS)
       && flag_pretty_templates)
     {
       tree tmpl;
@@ -1989,6 +1990,7 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
     case IDENTIFIER_NODE:
       dump_decl (pp, t, ((flags & ~(TFF_DECL_SPECIFIERS|TFF_RETURN_TYPE
                                     |TFF_TEMPLATE_HEADER))
+			 | TFF_NO_TEMPLATE_BINDINGS
                          | TFF_NO_FUNCTION_ARGUMENTS));
       break;
 
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index 978fa0d..d511185 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -154,6 +154,11 @@ begin_lambda_type (tree lambda)
   LAMBDA_EXPR_CLOSURE (lambda) = type;
   CLASSTYPE_LAMBDA_EXPR (type) = lambda;
 
+  /* In C++17, assume the closure is literal; we'll clear the flag later if
+     necessary.  */
+  if (cxx_dialect >= cxx1z)
+    CLASSTYPE_LITERAL_P (type) = true;
+
   /* Clear base types.  */
   xref_basetypes (type, /*bases=*/NULL_TREE);
 
@@ -1004,6 +1009,7 @@ maybe_add_lambda_conv_op (tree type)
 			 direct_argvec->address ());
 
   CALL_FROM_THUNK_P (call) = 1;
+  SET_EXPR_LOCATION (call, UNKNOWN_LOCATION);
 
   tree stattype = build_function_type (fn_result, FUNCTION_ARG_CHAIN (callop));
   stattype = (cp_build_type_attribute_variant
@@ -1141,6 +1147,18 @@ maybe_add_lambda_conv_op (tree type)
     --function_depth;
 }
 
+/* True if FN is the static function "_FUN" that gets returned from the lambda
+   conversion operator.  */
+
+bool
+lambda_static_thunk_p (tree fn)
+{
+  return (fn && TREE_CODE (fn) == FUNCTION_DECL
+	  && DECL_ARTIFICIAL (fn)
+	  && DECL_STATIC_FUNCTION_P (fn)
+	  && LAMBDA_TYPE_P (CP_DECL_CONTEXT (fn)));
+}
+
 /* Returns true iff VAL is a lambda-related declaration which should
    be ignored by unqualified lookup.  */
 
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 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 %<mutable%>");
+	}
 
       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], "%<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<typename T> struct R {
   static int x;
 };
-template<typename T> int R<T>::x = []{return 1;}();
+// "int i;" makes the op() non-constexpr in C++17.
+template<typename T> int R<T>::x = []{int i; return 1;}();
 template int R<int>::x;
 // Type of lambda in intializer of R<int>::x: N1RIiE1xMUlvE_E
 // Corresponding operator(): _ZNK1RIiE1xMUlvE_clEv
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle4.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle4.C
index 0d37637..b63c277 100644
--- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle4.C
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle4.C
@@ -4,8 +4,8 @@
 template <class T>
 struct A
 {
-  // { dg-final { scan-assembler "_ZNK1AIcE1pMUlvE_cvPFvvEEv" } }
-  // { dg-final { scan-assembler "_ZNK1AIiE1pMUlvE_cvPFvvEEv" } }
+  // { dg-final { scan-assembler "_ZNK1AIcE1pMUlvE_clEv" } }
+  // { dg-final { scan-assembler "_ZNK1AIiE1pMUlvE_clEv" } }
   void (*p)() = []{};
 };
 
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda1.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda1.C
new file mode 100644
index 0000000..a768cfb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda1.C
@@ -0,0 +1,6 @@
+// { dg-options -std=c++1z }
+
+constexpr auto Add5 = [](int i) { return i+5; };
+
+constexpr int x = Add5(4);
+static_assert(x==9);
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda10.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda10.C
new file mode 100644
index 0000000..ff65d6c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda10.C
@@ -0,0 +1,10 @@
+// Testcase from P0170R1
+// { dg-options -std=c++1z }
+
+void g() {
+  const int n = 0;
+  [=] {
+    constexpr int i = n; // OK, 'n' is not odr-used and not captured here.
+    constexpr int j = *&n; // { dg-error "" } '&n' would be an odr-use of 'n'.
+  };
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda11.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda11.C
new file mode 100644
index 0000000..f9e662d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda11.C
@@ -0,0 +1,11 @@
+// Testcase from P0170R1
+// { dg-options -std=c++1z }
+
+// 'v' & 'm' are odr-used but do not occur in a constant-expression within the nested
+// lambda, so are well-formed.
+auto monad = [](auto v) { return [=] { return v; }; };
+auto bind = [](auto m) {
+  return [=](auto fvm) { return fvm(m()); };
+};
+// OK to have captures to automatic objects created during constant expression evaluation.
+static_assert(bind(monad(2))(monad)() == monad(2)());
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
new file mode 100644
index 0000000..f5f3f38
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C
@@ -0,0 +1,10 @@
+// { dg-options -std=c++1z }
+
+void f(int i)
+{
+  [i]() constexpr {
+    int j;			// { dg-error "uninitialized" }
+    j = i;
+    return j;
+  }();
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C
new file mode 100644
index 0000000..077f823
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C
@@ -0,0 +1,5 @@
+// { dg-options -std=c++1z }
+
+auto l1 = []() constexpr constexpr { }; // { dg-error "duplicate" }
+auto l2 = []() mutable mutable { }; // { dg-error "duplicate" }
+auto l3 = []() static { };	    // { dg-error "static" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda14.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda14.C
new file mode 100644
index 0000000..26d078b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda14.C
@@ -0,0 +1,4 @@
+// { dg-options -std=c++14 }
+
+auto l = []() constexpr { return 42; }; // { dg-error "constexpr" }
+
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda2.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda2.C
new file mode 100644
index 0000000..1d3ff82
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda2.C
@@ -0,0 +1,7 @@
+// Testcase from P0170R1
+// { dg-options -std=c++1z }
+
+constexpr int AddEleven(int n){
+  return[n]{return n+11;}();
+}
+static_assert(AddEleven(5)==16,"");
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda3.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda3.C
new file mode 100644
index 0000000..46ee846
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda3.C
@@ -0,0 +1,8 @@
+// { dg-options -std=c++1z }
+
+constexpr auto add = [] (int n, int m) {
+  auto L = [=] { return n; };
+  auto R = [=] { return m; };
+  return [=] { return L() + R(); };
+};
+static_assert(add(3, 4)() == 7, "");
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda4.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda4.C
new file mode 100644
index 0000000..b3fd3d0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda4.C
@@ -0,0 +1,4 @@
+// { dg-options -std=c++1z }
+
+auto ID = [] (int n) constexpr { return n; };
+constexpr int I = ID(3);
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda5.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda5.C
new file mode 100644
index 0000000..71f1852
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda5.C
@@ -0,0 +1,7 @@
+// { dg-options -std=c++1z }
+
+auto addOne = [] (int n) {
+  return n + 1;
+};
+constexpr int (*addOneFp)(int) = addOne;
+static_assert(addOneFp(3) == addOne(3), "");
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda6.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda6.C
new file mode 100644
index 0000000..bb20bad
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda6.C
@@ -0,0 +1,30 @@
+// Testcase from P0170R1
+// { dg-do run }
+// { dg-options -std=c++1z }
+
+auto monoid = [](auto v) { return [=] { return v; }; };
+auto add = [](auto m1) constexpr {
+  auto ret = m1();
+  return [=](auto m2) mutable {
+    auto m1val = m1();
+    auto plus = [=] (auto m2val) mutable constexpr
+      { return m1val += m2val; };
+    ret = plus(m2());
+    return monoid(ret);
+  };
+};
+
+int main()
+{
+  constexpr auto zero = monoid(0);
+  constexpr auto one = monoid(1);
+  static_assert(add(one)(zero)() == one()); // OK
+  // Since 'two' below is not declared constexpr, an evaluation of its constexpr
+  // member function call operator can not perform an lvalue-to-rvalue conversion
+  // on one of its subobjects (that represents its capture) in a constant
+  // expression.
+  auto two = monoid(2);
+  if (!(two() == 2)) __builtin_abort(); // OK, not a constant expression.
+  static_assert(add(one)(one)() == two()); // { dg-error "" } two() is not a constant expression
+  static_assert(add(one)(one)() == monoid(2)()); // OK
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda7.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda7.C
new file mode 100644
index 0000000..26f136b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda7.C
@@ -0,0 +1,12 @@
+// Testcase from P0170R1
+// { dg-options -std=c++1z }
+
+auto ID = [](auto a) { return a; };
+static_assert( ID (3) == 3); // OK
+struct NonLiteral {
+  NonLiteral(int n) : n(n) { }
+  int n;
+};
+
+static_assert( ID (NonLiteral{3}).n == 3); // { dg-error "non-literal" }
+// { dg-prune-output "static assertion" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C
new file mode 100644
index 0000000..ac41306
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C
@@ -0,0 +1,15 @@
+// Testcase from P0170R1
+// { dg-options -std=c++1z }
+
+auto Fwd = [](int (*fp)(int), auto a) { return fp(a); };
+auto C = [](auto a) { return a; };
+static_assert( Fwd(C ,3) == 3); // OK
+// No specialization of the function call operator template can be constexpr
+// (because of the local static).
+auto NC = [](auto a) { static int s; return a; }; // { dg-error "static" }
+// { dg-message "operator int" "" { target *-*-* } 11 }
+static_assert( Fwd(NC ,3) == 3); // { dg-error "" }
+
+// We look for the string "operator int" to check that we aren't trying to do
+// template pretty-printing in an expression; that gets incredibly unwieldy
+// with the decltype magic we do for lambdas.
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda9.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda9.C
new file mode 100644
index 0000000..a5bc524
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda9.C
@@ -0,0 +1,4 @@
+// Testcase from P0170R1
+// { dg-options -std=c++1z }
+
+static_assert([](int n) { return [&n] { return ++n; }(); }(3) == 4);
diff --git a/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C b/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
index c7becc1..f5ed6ab 100644
--- a/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
+++ b/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
@@ -128,8 +128,8 @@
 
 #ifndef __cpp_constexpr
 #  error "__cpp_constexpr"
-#elif __cpp_constexpr != 201304
-#  error "__cpp_constexpr != 201304"
+#elif __cpp_constexpr != 201603
+#  error "__cpp_constexpr != 201603"
 #endif
 
 #ifndef __cpp_decltype_auto
diff mbox

Patch

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 <template <typename...> class F> struct A {
 template <typename F> auto valid_call(F f) -> decltype(f());
 constexpr auto valid_call(...) { return 0; }
 template <typename> struct no_type;
-static_assert(!valid_call(metafunction<no_type>),""); // { dg-error "" }
+static_assert(!valid_call(metafunction<no_type>),"");