From 72013ec51a97b6a709ac96845a247119e4602254 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Sun, 20 Dec 2015 13:38:37 -0500 Subject: [PATCH] re PR c++/67411 (internal compiler error: in tsubst_copy, at cp/pt.c:13473) PR c++/67411 * lambda.c (generic_lambda_fn_p): Split out from... (maybe_add_lambda_conv_op): ...here. * semantics.c (process_outer_var_ref): Don't defer maybe-constant variables in a generic lambda. * pt.c (instantiate_non_dependent_or_null): New. * init.c (constant_value_1): Use it. * cp-tree.h: Declare it and generic_lambda_fn_p. From-SVN: r231863 --- gcc/cp/ChangeLog | 9 ++++ gcc/cp/cp-tree.h | 2 + gcc/cp/init.c | 2 + gcc/cp/lambda.c | 14 +++-- gcc/cp/pt.c | 22 ++++++++ gcc/cp/semantics.c | 54 +++++++++++-------- .../g++.dg/cpp1y/lambda-generic-const2.C | 19 +++++++ 7 files changed, 98 insertions(+), 24 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1y/lambda-generic-const2.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 40ae390507d..e7adad38228 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,14 @@ 2015-12-20 Jason Merrill + PR c++/67411 + * lambda.c (generic_lambda_fn_p): Split out from... + (maybe_add_lambda_conv_op): ...here. + * semantics.c (process_outer_var_ref): Don't defer maybe-constant + variables in a generic lambda. + * pt.c (instantiate_non_dependent_or_null): New. + * init.c (constant_value_1): Use it. + * cp-tree.h: Declare it and generic_lambda_fn_p. + PR c++/67411 * decl2.c (decl_maybe_constant_var_p): A proxy isn't constant. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 058324f32a5..f0f7e36cbb4 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6158,6 +6158,7 @@ extern bool reregister_specialization (tree, tree, tree); extern tree instantiate_non_dependent_expr (tree); extern tree instantiate_non_dependent_expr_sfinae (tree, tsubst_flags_t); extern tree instantiate_non_dependent_expr_internal (tree, tsubst_flags_t); +extern tree instantiate_non_dependent_or_null (tree); extern bool variable_template_specialization_p (tree); extern bool alias_type_or_template_p (tree); extern bool alias_template_specialization_p (const_tree); @@ -6473,6 +6474,7 @@ extern tree maybe_resolve_dummy (tree, bool); extern tree current_nonlambda_function (void); extern tree nonlambda_method_basetype (void); 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); diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 1d5cc65ef2d..09c1183157d 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -2080,6 +2080,8 @@ constant_value_1 (tree decl, bool strict_p, bool return_aggregate_cst_ok_p) && TREE_CODE (init) == TREE_LIST && TREE_CHAIN (init) == NULL_TREE) init = TREE_VALUE (init); + /* Instantiate a non-dependent initializer. */ + init = instantiate_non_dependent_or_null (init); if (!init || !TREE_TYPE (init) || !TREE_CONSTANT (init) diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index 8d1ee14ca39..d50e48d71c3 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -851,6 +851,16 @@ prepare_op_call (tree fn, int nargs) return t; } +/* Return true iff CALLOP is the op() for a generic lambda. */ + +bool +generic_lambda_fn_p (tree callop) +{ + return (LAMBDA_FUNCTION_P (callop) + && DECL_TEMPLATE_INFO (callop) + && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (callop))); +} + /* If the closure TYPE has a static op(), also add a conversion to function pointer. */ @@ -867,9 +877,7 @@ maybe_add_lambda_conv_op (tree type) if (processing_template_decl) return; - bool const generic_lambda_p - = (DECL_TEMPLATE_INFO (callop) - && DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (callop)) == callop); + bool const generic_lambda_p = generic_lambda_fn_p (callop); if (!generic_lambda_p && DECL_INITIAL (callop) == NULL_TREE) { diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index da57fb2a45c..209e65f23b6 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -5661,6 +5661,28 @@ instantiate_non_dependent_expr (tree expr) return instantiate_non_dependent_expr_sfinae (expr, tf_error); } +/* Like instantiate_non_dependent_expr, but return NULL_TREE rather than + an uninstantiated expression. */ + +tree +instantiate_non_dependent_or_null (tree expr) +{ + if (expr == NULL_TREE) + return NULL_TREE; + if (processing_template_decl) + { + if (instantiation_dependent_expression_p (expr) + || !potential_constant_expression (expr)) + expr = NULL_TREE; + else + { + processing_template_decl_sentinel s; + expr = instantiate_non_dependent_expr_internal (expr, tf_error); + } + } + return expr; +} + /* True iff T is a specialization of a variable template. */ bool diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index b8f4e8f7ce7..ab9989a5a12 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -3231,27 +3231,7 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain) if (!mark_used (decl, complain) && !(complain & tf_error)) return error_mark_node; - /* Core issue 696: "[At the July 2009 meeting] the CWG expressed - support for an approach in which a reference to a local - [constant] automatic variable in a nested class or lambda body - would enter the expression as an rvalue, which would reduce - the complexity of the problem" - - FIXME update for final resolution of core issue 696. */ - if (decl_maybe_constant_var_p (decl)) - { - if (processing_template_decl) - /* In a template, the constant value may not be in a usable - form, so wait until instantiation time. */ - return decl; - else if (decl_constant_var_p (decl)) - { - tree t = maybe_constant_value (convert_from_reference (decl)); - if (TREE_CONSTANT (t)) - return t; - } - } - + bool saw_generic_lambda = false; if (parsing_nsdmi ()) containing_function = NULL_TREE; else @@ -3265,6 +3245,9 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain) tree closure = DECL_CONTEXT (containing_function); lambda_expr = CLASSTYPE_LAMBDA_EXPR (closure); + if (generic_lambda_fn_p (containing_function)) + saw_generic_lambda = true; + if (TYPE_CLASS_SCOPE_P (closure)) /* A lambda in an NSDMI (c++/64496). */ break; @@ -3281,6 +3264,35 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain) = decl_function_context (containing_function); } + /* Core issue 696: "[At the July 2009 meeting] the CWG expressed + support for an approach in which a reference to a local + [constant] automatic variable in a nested class or lambda body + would enter the expression as an rvalue, which would reduce + the complexity of the problem" + + FIXME update for final resolution of core issue 696. */ + if (decl_maybe_constant_var_p (decl)) + { + if (processing_template_decl && !saw_generic_lambda) + /* In a non-generic lambda within a template, wait until instantiation + time to decide whether to capture. For a generic lambda, we can't + wait until we instantiate the op() because the closure class is + already defined at that point. FIXME to get the semantics exactly + right we need to partially-instantiate the lambda body so the only + dependencies left are on the generic parameters themselves. This + probably means moving away from our current model of lambdas in + templates (instantiating the closure type) to one based on creating + the closure type when instantiating the lambda context. That is + probably also the way to handle lambdas within pack expansions. */ + return decl; + else if (decl_constant_var_p (decl)) + { + tree t = maybe_constant_value (convert_from_reference (decl)); + if (TREE_CONSTANT (t)) + return t; + } + } + if (lambda_expr && VAR_P (decl) && DECL_ANON_UNION_VAR_P (decl)) { diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const2.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const2.C new file mode 100644 index 00000000000..9a00e22eef9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const2.C @@ -0,0 +1,19 @@ +// PR c++/67411 +// { dg-do compile { target c++14 } } + +template +void f() +{ + int i = 42; + [=] { + const int x = i; + [&](auto) { + [=] { return x; }(); + }(1); + }(); +} + +int main() +{ + f(); +} -- 2.30.2