+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
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");
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
+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
&& !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);
/* 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;
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)
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
{
"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
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
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. */
case BREAK_STMT:
case CONTINUE_STMT:
case REQUIRES_EXPR:
+ case STATIC_ASSERT:
return true;
case AGGR_INIT_EXPR:
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
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;
case EH_SPEC_BLOCK:
case EXPR_STMT:
case PAREN_EXPR:
- case DECL_EXPR:
case NON_DEPENDENT_EXPR:
/* For convenience. */
case RETURN_EXPR:
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));
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);
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);
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);
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. */
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);
/* Pretty print template instantiations only. */
if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
+ && !(flags & TFF_NO_TEMPLATE_BINDINGS)
&& flag_pretty_templates)
{
tree tmpl;
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;
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);
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
--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. */
/* C++0x extensions. */
case RID_DECLTYPE:
case RID_UNDERLYING_TYPE:
+ case RID_CONSTEXPR:
return true;
default:
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
< 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]
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. */
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);
/* 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),
&& 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);
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)
{
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);
+ }
}
}
// 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()
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
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)() = []{};
};
--- /dev/null
+// { dg-options -std=c++1z }
+
+constexpr auto Add5 = [](int i) { return i+5; };
+
+constexpr int x = Add5(4);
+static_assert(x==9);
--- /dev/null
+// 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'.
+ };
+}
--- /dev/null
+// 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)());
--- /dev/null
+// { dg-options -std=c++1z }
+
+void f(int i)
+{
+ [i]() constexpr {
+ int j; // { dg-error "uninitialized" }
+ j = i;
+ return j;
+ }();
+}
--- /dev/null
+// { 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" }
--- /dev/null
+// { dg-options -std=c++14 }
+
+auto l = []() constexpr { return 42; }; // { dg-error "constexpr" }
+
--- /dev/null
+// Testcase from P0170R1
+// { dg-options -std=c++1z }
+
+constexpr int AddEleven(int n){
+ return[n]{return n+11;}();
+}
+static_assert(AddEleven(5)==16,"");
--- /dev/null
+// { 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, "");
--- /dev/null
+// { dg-options -std=c++1z }
+
+auto ID = [] (int n) constexpr { return n; };
+constexpr int I = ID(3);
--- /dev/null
+// { dg-options -std=c++1z }
+
+auto addOne = [] (int n) {
+ return n + 1;
+};
+constexpr int (*addOneFp)(int) = addOne;
+static_assert(addOneFp(3) == addOne(3), "");
--- /dev/null
+// 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
+}
--- /dev/null
+// 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" }
--- /dev/null
+// 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.
--- /dev/null
+// Testcase from P0170R1
+// { dg-options -std=c++1z }
+
+static_assert([](int n) { return [&n] { return ++n; }(); }(3) == 4);
#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