Implement C++17 constexpr lambda.

gcc/c-family/
	* c-cppbuiltin.c (c_cpp_builtins): Update __cpp_constexpr for
	C++17 constexpr lambdas.
gcc/cp/
	* class.c (finalize_literal_type_property): Handle lambdas.
	* constexpr.c (is_valid_constexpr_fn): Likewise.  No longer static.
	(explain_invalid_constexpr_fn, cxx_eval_call_expression): Handle
	lambdas.
	(cxx_eval_constant_expression): Handle capture proxy.
	(var_in_constexpr_fn): Don't check for C++14.
	(var_in_maybe_constexpr_fn): New.
	(potential_constant_expression_1): Use it.  Check DECL_EXPR for
	declarations not allowed in constexpr function.
	* decl.c (make_rtl_for_nonlocal_decl): Use var_in_maybe_constexpr_fn.
	(finish_function): Set DECL_DECLARED_CONSTEXPR_P on lambda members.
	* lambda.c (begin_lambda_type): Set CLASSTYPE_LITERAL_P.
	(maybe_add_lambda_conv_op): Clear thunk CALL_EXPR location.
	(lambda_static_thunk_p): New.
	* parser.c (cp_keyword_starts_decl_specifier_p): Add RID_CONSTEXPR.
	(CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR): New enumerator.
	(cp_parser_decl_specifier_seq): Handle it.
	(cp_parser_lambda_declarator_opt): Use cp_parser_decl_specifier_seq.
	* pt.c (instantiate_class_template_1): Set CLASSTYPE_LITERAL_P.
	(tsubst_copy_and_build) [CALL_EXPR]: Propagate CALL_FROM_THUNK_P.
	* error.c (dump_function_decl): Check TFF_NO_TEMPLATE_BINDINGS.
	(dump_expr) [FUNCTION_DECL]: Pass it.

From-SVN: r239268
This commit is contained in:
Jason Merrill 2016-08-09 00:33:58 -04:00 committed by Jason Merrill
parent 7dc2b4a235
commit 98e5a19af5
29 changed files with 332 additions and 29 deletions

View 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

View 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

View 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

View 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);

View 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));

View 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);

View 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);

View 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;

View 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. */

View 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);

View 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);
}
}
}

View 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()

View 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

View 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)() = []{};
};

View File

@ -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);

View File

@ -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'.
};
}

View File

@ -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)());

View File

@ -0,0 +1,10 @@
// { dg-options -std=c++1z }
void f(int i)
{
[i]() constexpr {
int j; // { dg-error "uninitialized" }
j = i;
return j;
}();
}

View File

@ -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" }

View File

@ -0,0 +1,4 @@
// { dg-options -std=c++14 }
auto l = []() constexpr { return 42; }; // { dg-error "constexpr" }

View File

@ -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,"");

View File

@ -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, "");

View File

@ -0,0 +1,4 @@
// { dg-options -std=c++1z }
auto ID = [] (int n) constexpr { return n; };
constexpr int I = ID(3);

View File

@ -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), "");

View File

@ -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
}

View File

@ -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" }

View File

@ -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.

View File

@ -0,0 +1,4 @@
// Testcase from P0170R1
// { dg-options -std=c++1z }
static_assert([](int n) { return [&n] { return ++n; }(); }(3) == 4);

View File

@ -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