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
This commit is contained in:
Jason Merrill 2014-10-09 23:28:18 -04:00 committed by Jason Merrill
parent 018e891a75
commit 548cb3d77c
5 changed files with 129 additions and 85 deletions

View file

@ -1,3 +1,11 @@
2014-10-09 Jason Merrill <jason@redhat.com>
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 <paolo.carlini@oracle.com>
* semantics.c (check_constexpr_ctor_body_1): New.

View file

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

View file

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

View file

@ -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). */

View file

@ -0,0 +1,21 @@
// PR c++/63207
// { dg-do run { target c++11 } }
template <typename T>
struct Base {
T value;
};
template <typename T>
struct Test : Base<T> {
T test() {
const int x = this->value;
return ([&]{ return x; })();
}
};
int main() {
Test<int> t;
t.value = 0;
return t.test();
}