From 548cb3d77c81104778f4cbc4d97410cb31a64971 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Thu, 9 Oct 2014 23:28:18 -0400 Subject: [PATCH] re PR c++/63207 (ICE in expand_expr_real_l when instantiating a template with a lambda that captures a const variable with a dependent initializer) PR c++/63207 * semantics.c (outer_var_p): Non-static. (process_outer_var_ref): Split out from finish_id_expression. * pt.c (tsubst_copy_and_build): Call them. * cp-tree.h: Declare them. From-SVN: r216056 --- gcc/cp/ChangeLog | 8 + gcc/cp/cp-tree.h | 2 + gcc/cp/pt.c | 3 + gcc/cp/semantics.c | 180 +++++++++--------- .../g++.dg/cpp0x/lambda/lambda-const4.C | 21 ++ 5 files changed, 129 insertions(+), 85 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const4.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 84996a45cba..7b2d09f1402 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,11 @@ +2014-10-09 Jason Merrill + + PR c++/63207 + * semantics.c (outer_var_p): Non-static. + (process_outer_var_ref): Split out from finish_id_expression. + * pt.c (tsubst_copy_and_build): Call them. + * cp-tree.h: Declare them. + 2014-10-09 Paolo Carlini * semantics.c (check_constexpr_ctor_body_1): New. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 3787c4a3f25..6d720c152a6 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5879,6 +5879,8 @@ extern void finish_template_decl (tree); extern tree finish_template_type (tree, tree, int); extern tree finish_base_specifier (tree, tree, bool); extern void finish_member_declaration (tree); +extern bool outer_automatic_var_p (tree); +extern tree process_outer_var_ref (tree, tsubst_flags_t); extern tree finish_id_expression (tree, tree, tree, cp_id_kind *, bool, bool, bool *, diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 85af59d7a5c..f2c21eec322 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -15460,6 +15460,7 @@ tsubst_copy_and_build (tree t, case PARM_DECL: { tree r = tsubst_copy (t, args, complain, in_decl); + /* ??? We're doing a subset of finish_id_expression here. */ if (VAR_P (r) && !processing_template_decl && !cp_unevaluated_operand @@ -15471,6 +15472,8 @@ tsubst_copy_and_build (tree t, a call to its wrapper. */ r = build_cxx_call (wrap, 0, NULL, tf_warning_or_error); } + else if (outer_automatic_var_p (r)) + r = process_outer_var_ref (r, complain); if (TREE_CODE (TREE_TYPE (t)) != REFERENCE_TYPE) /* If the original type was a reference, we'll be wrapped in diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 3ca91d88bfe..ab8c82ae5f6 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -3067,13 +3067,105 @@ outer_var_p (tree decl) /* As above, but also checks that DECL is automatic. */ -static bool +bool outer_automatic_var_p (tree decl) { return (outer_var_p (decl) && !TREE_STATIC (decl)); } +/* DECL satisfies outer_automatic_var_p. Possibly complain about it or + rewrite it for lambda capture. */ + +tree +process_outer_var_ref (tree decl, tsubst_flags_t complain) +{ + if (cp_unevaluated_operand) + /* It's not a use (3.2) if we're in an unevaluated context. */ + return decl; + + tree context = DECL_CONTEXT (decl); + tree containing_function = current_function_decl; + tree lambda_stack = NULL_TREE; + tree lambda_expr = NULL_TREE; + tree initializer = convert_from_reference (decl); + + /* Mark it as used now even if the use is ill-formed. */ + mark_used (decl); + + /* 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)) + return integral_constant_value (decl); + } + + if (parsing_nsdmi ()) + containing_function = NULL_TREE; + else + /* If we are in a lambda function, we can move out until we hit + 1. the context, + 2. a non-lambda function, or + 3. a non-default capturing lambda function. */ + while (context != containing_function + && LAMBDA_FUNCTION_P (containing_function)) + { + lambda_expr = CLASSTYPE_LAMBDA_EXPR + (DECL_CONTEXT (containing_function)); + + if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) + == CPLD_NONE) + break; + + lambda_stack = tree_cons (NULL_TREE, + lambda_expr, + lambda_stack); + + containing_function + = decl_function_context (containing_function); + } + + if (lambda_expr && TREE_CODE (decl) == VAR_DECL + && DECL_ANON_UNION_VAR_P (decl)) + { + if (complain & tf_error) + error ("cannot capture member %qD of anonymous union", decl); + return error_mark_node; + } + if (context == containing_function) + { + decl = add_default_capture (lambda_stack, + /*id=*/DECL_NAME (decl), + initializer); + } + else if (lambda_expr) + { + if (complain & tf_error) + error ("%qD is not captured", decl); + return error_mark_node; + } + else + { + if (complain & tf_error) + error (VAR_P (decl) + ? G_("use of local variable with automatic storage from containing function") + : G_("use of parameter from containing function")); + inform (input_location, "%q+#D declared here", decl); + return error_mark_node; + } + return decl; +} + /* ID_EXPRESSION is a representation of parsed, but unprocessed, id-expression. (See cp_parser_id_expression for details.) SCOPE, if non-NULL, is the type or namespace used to explicitly qualify @@ -3179,90 +3271,8 @@ finish_id_expression (tree id_expression, /* Disallow uses of local variables from containing functions, except within lambda-expressions. */ - if (!outer_var_p (decl)) - /* OK */; - else if (TREE_STATIC (decl) - /* It's not a use (3.2) if we're in an unevaluated context. */ - || cp_unevaluated_operand) - /* OK */; - else - { - tree context = DECL_CONTEXT (decl); - tree containing_function = current_function_decl; - tree lambda_stack = NULL_TREE; - tree lambda_expr = NULL_TREE; - tree initializer = convert_from_reference (decl); - - /* Mark it as used now even if the use is ill-formed. */ - mark_used (decl); - - /* 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)) - return integral_constant_value (decl); - } - - if (parsing_nsdmi ()) - containing_function = NULL_TREE; - /* If we are in a lambda function, we can move out until we hit - 1. the context, - 2. a non-lambda function, or - 3. a non-default capturing lambda function. */ - else while (context != containing_function - && LAMBDA_FUNCTION_P (containing_function)) - { - lambda_expr = CLASSTYPE_LAMBDA_EXPR - (DECL_CONTEXT (containing_function)); - - if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) - == CPLD_NONE) - break; - - lambda_stack = tree_cons (NULL_TREE, - lambda_expr, - lambda_stack); - - containing_function - = decl_function_context (containing_function); - } - - if (lambda_expr && TREE_CODE (decl) == VAR_DECL - && DECL_ANON_UNION_VAR_P (decl)) - { - error ("cannot capture member %qD of anonymous union", decl); - return error_mark_node; - } - if (context == containing_function) - { - decl = add_default_capture (lambda_stack, - /*id=*/DECL_NAME (decl), - initializer); - } - else if (lambda_expr) - { - error ("%qD is not captured", decl); - return error_mark_node; - } - else - { - error (VAR_P (decl) - ? G_("use of local variable with automatic storage from containing function") - : G_("use of parameter from containing function")); - inform (input_location, "%q+#D declared here", decl); - return error_mark_node; - } - } + if (outer_automatic_var_p (decl)) + decl = process_outer_var_ref (decl, tf_warning_or_error); /* Also disallow uses of function parameters outside the function body, except inside an unevaluated context (i.e. decltype). */ diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const4.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const4.C new file mode 100644 index 00000000000..02ad60224f5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const4.C @@ -0,0 +1,21 @@ +// PR c++/63207 +// { dg-do run { target c++11 } } + +template +struct Base { + T value; +}; + +template +struct Test : Base { + T test() { + const int x = this->value; + return ([&]{ return x; })(); + } +}; + +int main() { + Test t; + t.value = 0; + return t.test(); +} -- 2.30.2