2018-11-12 Jason Merrill <jason@redhat.com>
+ Implement P0315R4, Lambdas in unevaluated contexts.
+ * decl2.c (min_vis_expr_r): Handle LAMBDA_EXPR.
+ * mangle.c (write_expression): Handle LAMBDA_EXPR.
+ * parser.c (cp_parser_lambda_expression): Allow lambdas in
+ unevaluated context. Start the tentative firewall sooner.
+ (cp_parser_lambda_body): Use cp_evaluated.
+ * pt.c (iterative_hash_template_arg): Handle LAMBDA_EXPR.
+ (tsubst_function_decl): Substitute a lambda even if it isn't
+ dependent.
+ (tsubst_lambda_expr): Use cp_evaluated. Always complain.
+ (tsubst_copy_and_build) [LAMBDA_EXPR]: Do nothing if tf_partial.
+ * semantics.c (begin_class_definition): Allow in template parm list.
+ * tree.c (strip_typedefs_expr): Pass through LAMBDA_EXPR.
+ (cp_tree_equal): Handle LAMBDA_EXPR.
+
* pt.c (fn_type_unification): If we have a full set of explicit
arguments, go straight to substitution.
case DYNAMIC_CAST_EXPR:
case NEW_EXPR:
case CONSTRUCTOR:
+ case LAMBDA_EXPR:
tpvis = type_visibility (TREE_TYPE (*tp));
break;
write_expression (val);
write_char ('E');
}
+ else if (code == LAMBDA_EXPR)
+ {
+ /* [temp.over.link] Two lambda-expressions are never considered
+ equivalent.
+
+ So just use the closure type mangling. */
+ write_string ("tl");
+ write_type (LAMBDA_EXPR_CLOSURE (expr));
+ write_char ('E');
+ }
else if (dependent_name (expr))
{
write_unqualified_id (dependent_name (expr));
LAMBDA_EXPR_LOCATION (lambda_expr) = token->location;
- if (cp_unevaluated_operand)
+ if (cxx_dialect >= cxx2a)
+ /* C++20 allows lambdas in unevaluated context. */;
+ else if (cp_unevaluated_operand)
{
if (!token->error_reported)
{
error_at (LAMBDA_EXPR_LOCATION (lambda_expr),
- "lambda-expression in unevaluated context");
+ "lambda-expression in unevaluated context"
+ " only available with -std=c++2a or -std=gnu++2a");
token->error_reported = true;
}
ok = false;
{
if (!token->error_reported)
{
- error_at (token->location, "lambda-expression in template-argument");
+ error_at (token->location, "lambda-expression in template-argument"
+ " only available with -std=c++2a or -std=gnu++2a");
token->error_reported = true;
}
ok = false;
push_deferring_access_checks (dk_no_deferred);
cp_parser_lambda_introducer (parser, lambda_expr);
+ if (cp_parser_error_occurred (parser))
+ return error_mark_node;
type = begin_lambda_type (lambda_expr);
if (type == error_mark_node)
/* By virtue of defining a local class, a lambda expression has access to
the private variables of enclosing classes. */
+ if (cp_parser_start_tentative_firewall (parser))
+ start = token;
+
ok &= cp_parser_lambda_declarator_opt (parser, lambda_expr);
if (ok && cp_parser_error_occurred (parser))
if (ok)
{
- if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)
- && cp_parser_start_tentative_firewall (parser))
- start = token;
cp_parser_lambda_body (parser, lambda_expr);
}
else if (cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE))
bool local_variables_forbidden_p = parser->local_variables_forbidden_p;
bool in_function_body = parser->in_function_body;
+ /* The body of a lambda-expression is not a subexpression of the enclosing
+ expression. */
+ cp_evaluated ev;
+
if (nested)
push_function_context ();
else
return iterative_hash_template_arg (TREE_OPERAND (arg, 2), val);
case LAMBDA_EXPR:
- /* A lambda can't appear in a template arg, but don't crash on
- erroneous input. */
- gcc_assert (seen_error ());
- return val;
+ /* [temp.over.link] Two lambda-expressions are never considered
+ equivalent.
+
+ So just hash the closure type. */
+ return iterative_hash_template_arg (TREE_TYPE (arg), val);
case CAST_EXPR:
case IMPLICIT_CONV_EXPR:
if (TREE_CODE (DECL_TI_TEMPLATE (t)) == TEMPLATE_DECL)
{
/* If T is not dependent, just return it. */
- if (!uses_template_parms (DECL_TI_ARGS (t)))
+ if (!uses_template_parms (DECL_TI_ARGS (t))
+ && !LAMBDA_FUNCTION_P (t))
return t;
/* Calculate the most general template of which R is a
/* Let finish_function set this. */
DECL_DECLARED_CONSTEXPR_P (fn) = false;
+ /* The body of a lambda-expression is not a subexpression of the
+ enclosing expression. */
+ cp_evaluated ev;
+
bool nested = cfun;
if (nested)
push_function_context ();
current_function_infinite_loop = ol->infinite_loop;
}
+ /* [temp.deduct] A lambda-expression appearing in a function type or a
+ template parameter is not considered part of the immediate context for
+ the purposes of template argument deduction. */
+ complain = tf_warning_or_error;
+
tsubst_expr (DECL_SAVED_TREE (oldfn), args, complain, r,
/*constexpr*/false);
case LAMBDA_EXPR:
{
+ if (complain & tf_partial)
+ {
+ /* We don't have a full set of template arguments yet; don't touch
+ the lambda at all. */
+ gcc_assert (processing_template_decl);
+ return t;
+ }
tree r = tsubst_lambda_expr (t, args, complain, in_decl);
RETURN (build_lambda_object (r));
if (error_operand_p (t) || error_operand_p (TYPE_MAIN_DECL (t)))
return error_mark_node;
- if (processing_template_parmlist)
- {
- error ("definition of %q#T inside template parameter list", t);
- return error_mark_node;
- }
-
/* According to the C++ ABI, decimal classes defined in ISO/IEC TR 24733
are passed the same as decimal scalar types. */
if (TREE_CODE (t) == RECORD_TYPE
}
case LAMBDA_EXPR:
- error ("lambda-expression in a constant expression");
- return error_mark_node;
+ return t;
case STATEMENT_LIST:
error ("statement-expression in a constant expression");
{
tree r;
- /* There's no point in checking linkage on template functions; we
+ /* Lambda types that don't have mangling scope have no linkage. We
+ check CLASSTYPE_LAMBDA_EXPR for error_mark_node because
+ when we get here from pushtag none of the lambda information is
+ set up yet, so we want to assume that the lambda has linkage and
+ fix it up later if not. We need to check this even in templates so
+ that we properly handle a lambda-expression in the signature. */
+ if (LAMBDA_TYPE_P (t)
+ && CLASSTYPE_LAMBDA_EXPR (t) != error_mark_node
+ && LAMBDA_TYPE_EXTRA_SCOPE (t) == NULL_TREE)
+ return t;
+
+ /* Otherwise there's no point in checking linkage on template functions; we
can't know their complete types. */
if (processing_template_decl)
return NULL_TREE;
case RECORD_TYPE:
if (TYPE_PTRMEMFUNC_P (t))
goto ptrmem;
- /* Lambda types that don't have mangling scope have no linkage. We
- check CLASSTYPE_LAMBDA_EXPR for error_mark_node because
- when we get here from pushtag none of the lambda information is
- set up yet, so we want to assume that the lambda has linkage and
- fix it up later if not. */
- if (CLASSTYPE_LAMBDA_EXPR (t)
- && CLASSTYPE_LAMBDA_EXPR (t) != error_mark_node
- && LAMBDA_TYPE_EXTRA_SCOPE (t) == NULL_TREE)
- return t;
/* Fall through. */
case UNION_TYPE:
if (!CLASS_TYPE_P (t))
DECL_NAME (t2)));
return false;
+ case LAMBDA_EXPR:
+ /* Two lambda-expressions are never considered equivalent. */
+ return false;
+
default:
break;
}
// PR c++/51464
// { dg-do compile { target c++11 } }
-template<int = sizeof([])> struct A {}; // { dg-error "lambda" }
+template<int = sizeof([])> struct A {}; // { dg-error "" }
template <typename T>
struct AddRvalueReferenceImpl<T, typename BoolSink<false &&
- [] { // { dg-error "lambda" }
+ [] { // { dg-error "lambda" "" { target c++17_down } }
extern T &&tref;
}>::type> {
typedef T &&type;
namespace ImplHelpers {
template <typename T>
- typename AddRvalueReference<T>::type create(void) { }
+ typename AddRvalueReference<T>::type create(void);
}
template <typename T, typename U, typename ...Args>
template <typename T, typename ...Args>
struct IsConstructibleImpl<T, typename BoolSink<false &&
- [] { // { dg-error "lambda" }
- T t( ::ImplHelpers::create<Args>() ...);
- }>::type, Args ...> {
+ [] { T t( ::ImplHelpers::create<Args>() ...); } // { dg-error "" }
+ >::type, Args ...> {
enum { value = 1 };
};
template <class T>
struct A { };
-A<decltype([]{ return 1; }())> a; // { dg-error "lambda.*unevaluated context" }
+A<decltype([]{ return 1; }())> a; // { dg-error "lambda.*unevaluated context" "" { target c++17_down } }
// { dg-prune-output "template argument" }
// { dg-prune-output "invalid type" }
struct A
{
- decltype( [](){ return this; }() ) x; // { dg-error "unevaluated" }
+ decltype( [](){ return this; }() ) x; // { dg-error "unevaluated" "" { target c++17_down } }
+ // { dg-error "not captured" "" { target c++2a } .-1 }
};
+
+// { dg-prune-output "declared void" }
--- /dev/null
+// { dg-do compile { target c++2a } }
+
+typedef decltype([]{}) C; // the closure type has no name for linkage purposes
+
+// { dg-final { scan-assembler-not "globl\[ \t]*_Z1f" } }
+// { dg-final { scan-assembler-not "_Z1f1C" } }
+void f(C) {}
+
+int main()
+{
+ C c;
+ c();
+ f(c);
+}
+
+
--- /dev/null
+// { dg-do compile { target c++2a } }
+
+// ill-formed, no diagnostic required: the two expressions are
+// functionally equivalent but not equivalent
+template <int N> void foo(const char (&s)[([]{}, N)]);
+template <int N> void foo(const char (&s)[([]{}, N)]);
+
+// two different declarations because the non-dependent portions are not
+// considered equivalent
+template <class T> void spam(decltype([]{}) (*s)[sizeof(T)]);
+template <class T> void spam(decltype([]{}) (*s)[sizeof(T)]);
+
+template <class T>
+using A = decltype([] { });
+// A<int> and A<char> refer to different closure types
+
+template <class T>
+auto f(T) -> decltype([]() { T::invalid; } ()); // { dg-error "invalid" }
+void f(...);
+
+template <class T, unsigned = sizeof([]() { T::invalid; })> // { dg-error "invalid" }
+void g(T);
+void g(...);
+
+template <class T>
+auto h(T) -> decltype([x = T::invalid]() { });
+void h(...);
+
+template <class T>
+auto i(T) -> decltype([]() -> typename T::invalid { });
+void i(...);
+
+template <class T>
+auto j(T t) -> decltype([](auto x) -> decltype(x.invalid) { } (t));
+void j(...);
+
+template <class,class> struct different {};
+template <class T> struct different<T,T> { typename T::invalid t; };
+
+template <class,class> struct same;
+template <class T> struct same<T,T> {};
+
+int main()
+{
+ foo<1>(""); // { dg-error "ambiguous" }
+ spam<char>(nullptr); // { dg-error "ambiguous" }
+ different<A<int>,A<char>>();
+ same<A<int>,A<int>>();
+ f(0); // error: invalid expression not part of the immediate context
+ g(0); // error: invalid expression not part of the immediate context
+ h(0); // error: invalid expression not part of the immediate context
+ i(0); // error: invalid expression not part of the immediate context
+ j(0); // deduction fails on #1, calls #2
+}
--- /dev/null
+// { dg-do compile { target c++2a } }
+
+template <int N> void foo(const char (*s)[([]{}, N)]) {}
+template <class T> void spam(decltype([]{}) (*s)[sizeof(T)]) {}
+
+int main()
+{
+ foo<1>(nullptr);
+ spam<char>(nullptr);
+}
+
+// { dg-final { scan-assembler-not "weak.*_Z" } }
--- /dev/null
+// { dg-do link { target c++2a } }
+
+template <class T> T f(T t) { return t; }
+using L = decltype([]{ return f(42); });
+int main()
+{
+ return L()();
+}
--- /dev/null
+// { dg-do compile { target c++2a } }
+
+using L = decltype([]{ });
+void f(L) { }
+// { dg-final { scan-assembler-not "globl.*_Z1f" } }
--- /dev/null
+// { dg-do compile { target c++2a } }
+
+static decltype([] { }) f();
+static decltype([] { }) f(); // { dg-error "ambiguating" }
+
+static decltype([] { }) g();
+static decltype(g()) g(); // okay
+
+static void h(decltype([] { }) *) { }
+static void h(decltype([] { }) *) { }
+void x1() { h(nullptr); } // { dg-error "ambiguous" }
+
+using A = decltype([] { });
+static void i(A *);
+static void i(A *) { }
+void x2() { i(nullptr); } // okay
+
+template <typename T>
+using B = decltype([] { });
+static void j(B<char16_t> *) { }
+static void j(B<char32_t> *) { }
+void x3() { j(nullptr); } // { dg-error "ambiguous" }
+
+template <int N> static void k(decltype([]{ return 0; }()));
+template <int N> static void k(decltype([]{ return 0; }())); // okay
+template <int N> static void k(int); // okay
--- /dev/null
+// { dg-do compile { target c++2a } }
+
+template <int N>
+struct A { };
+
+template <int N>
+void g(A<[]{return N;}()>) {}
+
+int main()
+{
+ g<1>({});
+}
--- /dev/null
+// { dg-do compile { target c++2a } }
+
+template <auto N>
+struct A {
+ static constexpr auto n = N;
+};
+
+template <auto N>
+constexpr auto g(A<[]{return N;}> a) {
+ return a.n();
+}
+
+static_assert(g<42>({}) == 42);
--- /dev/null
+// { dg-do run { target c++2a } }
+// { dg-additional-sources "lambda-uneval9.cc" }
+
+#include "lambda-uneval9.h"
+int foo() { return f(); }
+extern int bar();
+
+int main()
+{
+ if (foo() != 1) __builtin_abort();
+ if (bar() != 2) __builtin_abort();
+}
--- /dev/null
+#include "lambda-uneval9.h"
+
+int bar() { return f(); }
--- /dev/null
+// a.h:
+template <typename T>
+int counter() {
+ static int cnt = 0;
+ return ++cnt;
+}
+inline int f() {
+ return counter<decltype([] {})>();
+}