Commit 281e6c1d by Jason Merrill Committed by Jason Merrill

PR c++/56973, DR 696 - capture constant variables only as needed.

	* expr.c (mark_use): Split out from mark_rvalue_use and
	mark_lvalue_use.  Handle lambda capture of constant variables.
	(mark_lvalue_use_nonread): New.
	* semantics.c (process_outer_var_ref): Don't capture a constant
	variable until forced.
	* pt.c (processing_nonlambda_template): New.
	* call.c (build_this): Check it.
	* decl2.c (grok_array_decl): Call mark_rvalue_use and
	mark_lvalue_use_nonread.
	* init.c (constant_value_1): Don't call mark_rvalue_use.
	* typeck.c (build_static_cast): Handle lambda capture.

From-SVN: r253266
parent 5c263e84
2017-09-28 Jason Merrill <jason@redhat.com>
PR c++/56973, DR 696 - capture constant variables only as needed.
* expr.c (mark_use): Split out from mark_rvalue_use and
mark_lvalue_use. Handle lambda capture of constant variables.
(mark_lvalue_use_nonread): New.
* semantics.c (process_outer_var_ref): Don't capture a constant
variable until forced.
* pt.c (processing_nonlambda_template): New.
* call.c (build_this): Check it.
* decl2.c (grok_array_decl): Call mark_rvalue_use and
mark_lvalue_use_nonread.
* init.c (constant_value_1): Don't call mark_rvalue_use.
* typeck.c (build_static_cast): Handle lambda capture.
Use local_specializations to find capture proxies.
* cp-tree.h (DECL_CAPTURED_VARIABLE): New.
* lambda.c (build_capture_proxy): Set it.
......
......@@ -3362,7 +3362,7 @@ build_this (tree obj)
{
/* In a template, we are only concerned about the type of the
expression, so we can take a shortcut. */
if (processing_template_decl)
if (processing_nonlambda_template ())
return build_address (obj);
return cp_build_addr_expr (obj, tf_warning_or_error);
......
......@@ -6249,6 +6249,7 @@ extern tree mark_rvalue_use (tree,
location_t = UNKNOWN_LOCATION,
bool = true);
extern tree mark_lvalue_use (tree);
extern tree mark_lvalue_use_nonread (tree);
extern tree mark_type_use (tree);
extern void mark_exp_read (tree);
......@@ -6412,6 +6413,7 @@ extern tree lookup_template_variable (tree, tree);
extern int uses_template_parms (tree);
extern bool uses_template_parms_level (tree, int);
extern bool in_template_function (void);
extern bool processing_nonlambda_template (void);
extern tree instantiate_class_template (tree);
extern tree instantiate_template (tree, tree, tsubst_flags_t);
extern tree fn_type_unification (tree, tree, tree,
......@@ -6720,7 +6722,7 @@ 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 process_outer_var_ref (tree, tsubst_flags_t, bool force_use = false);
extern cp_expr finish_id_expression (tree, tree, tree,
cp_id_kind *,
bool, bool, bool *,
......
......@@ -427,6 +427,11 @@ grok_array_decl (location_t loc, tree array_expr, tree index_exp,
if (array_expr == error_mark_node || index_exp == error_mark_node)
error ("ambiguous conversion for array subscript");
if (TREE_CODE (TREE_TYPE (array_expr)) == POINTER_TYPE)
array_expr = mark_rvalue_use (array_expr);
else
array_expr = mark_lvalue_use_nonread (array_expr);
index_exp = mark_rvalue_use (index_exp);
expr = build_array_ref (input_location, array_expr, index_exp);
}
if (processing_template_decl && expr != error_mark_node)
......
......@@ -86,21 +86,105 @@ cplus_expand_constant (tree cst)
return cst;
}
/* We've seen an actual use of EXPR. Possibly replace an outer variable
reference inside with its constant value or a lambda capture. */
static tree
mark_use (tree expr, bool rvalue_p, bool read_p,
location_t loc /* = UNKNOWN_LOCATION */,
bool reject_builtin /* = true */)
{
#define RECUR(t) mark_use ((t), rvalue_p, read_p, loc, reject_builtin)
if (reject_builtin && reject_gcc_builtin (expr, loc))
return error_mark_node;
if (read_p)
mark_exp_read (expr);
bool recurse_op[3] = { false, false, false };
switch (TREE_CODE (expr))
{
case VAR_DECL:
if (outer_automatic_var_p (expr)
&& decl_constant_var_p (expr))
{
if (rvalue_p)
{
tree t = maybe_constant_value (expr);
if (TREE_CONSTANT (t))
{
expr = t;
break;
}
}
expr = process_outer_var_ref (expr, tf_warning_or_error, true);
expr = convert_from_reference (expr);
}
break;
case COMPONENT_REF:
recurse_op[0] = true;
break;
case COMPOUND_EXPR:
recurse_op[1] = true;
break;
case COND_EXPR:
recurse_op[2] = true;
if (TREE_OPERAND (expr, 1))
recurse_op[1] = true;
break;
case INDIRECT_REF:
if (REFERENCE_REF_P (expr))
{
/* Try to look through the reference. */
tree ref = TREE_OPERAND (expr, 0);
tree r = mark_rvalue_use (ref, loc, reject_builtin);
if (r != ref)
{
expr = copy_node (expr);
TREE_OPERAND (expr, 0) = r;
}
}
break;
default:
break;
}
bool changed = false;
tree ops[3];
for (int i = 0; i < 3; ++i)
if (recurse_op[i])
{
tree op = TREE_OPERAND (expr, i);
ops[i] = RECUR (op);
if (ops[i] != op)
changed = true;
}
if (changed)
{
expr = copy_node (expr);
for (int i = 0; i < 3; ++i)
if (recurse_op[i])
TREE_OPERAND (expr, i) = ops[i];
}
return expr;
#undef RECUR
}
/* Called whenever the expression EXPR is used in an rvalue context.
When REJECT_BUILTIN is true the expression is checked to make sure
it doesn't make it possible to obtain the address of a GCC built-in
function with no library fallback (or any of its bits, such as in
a conversion to bool). */
tree
mark_rvalue_use (tree expr,
mark_rvalue_use (tree e,
location_t loc /* = UNKNOWN_LOCATION */,
bool reject_builtin /* = true */)
{
if (reject_builtin && reject_gcc_builtin (expr, loc))
return error_mark_node;
mark_exp_read (expr);
return expr;
return mark_use (e, true, true, loc, reject_builtin);
}
/* Called whenever an expression is used in an lvalue context. */
......@@ -108,8 +192,15 @@ mark_rvalue_use (tree expr,
tree
mark_lvalue_use (tree expr)
{
mark_exp_read (expr);
return expr;
return mark_use (expr, false, true, input_location, false);
}
/* As above, but don't consider this use a read. */
tree
mark_lvalue_use_nonread (tree expr)
{
return mark_use (expr, false, false, input_location, false);
}
/* Called whenever an expression is used in a type use context. */
......
......@@ -2213,7 +2213,6 @@ constant_value_1 (tree decl, bool strict_p, bool return_aggregate_cst_ok_p)
initializer for the static data member is not processed
until needed; we need it now. */
mark_used (decl, tf_none);
mark_rvalue_use (decl);
init = DECL_INITIAL (decl);
if (init == error_mark_node)
{
......
......@@ -9494,6 +9494,32 @@ in_template_function (void)
return ret;
}
/* Returns true iff we are currently within a template other than a generic
lambda. We test this by finding the outermost closure type and checking
whether it is dependent. */
bool
processing_nonlambda_template (void)
{
if (!processing_template_decl)
return false;
tree outer_closure = NULL_TREE;
for (tree t = current_class_type; t;
t = decl_type_context (TYPE_MAIN_DECL (t)))
{
if (LAMBDA_TYPE_P (t))
outer_closure = t;
else
break;
}
if (outer_closure)
return dependent_type_p (outer_closure);
else
return true;
}
/* Returns true if T depends on any template parameter with level LEVEL. */
bool
......
......@@ -3282,7 +3282,7 @@ outer_automatic_var_p (tree decl)
rewrite it for lambda capture. */
tree
process_outer_var_ref (tree decl, tsubst_flags_t complain)
process_outer_var_ref (tree decl, tsubst_flags_t complain, bool force_use)
{
if (cp_unevaluated_operand)
/* It's not a use (3.2) if we're in an unevaluated context. */
......@@ -3303,6 +3303,12 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain)
if (parsing_nsdmi ())
containing_function = NULL_TREE;
/* Core issue 696: Only an odr-use of an outer automatic variable causes a
capture (or error), and a constant variable can decay to a prvalue
constant without odr-use. So don't capture yet. */
if (decl_constant_var_p (decl) && !force_use)
return decl;
if (containing_function && LAMBDA_FUNCTION_P (containing_function))
{
/* Check whether we've already built a proxy. */
......@@ -3314,7 +3320,7 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain)
return d;
else
/* We need to capture an outer proxy. */
return process_outer_var_ref (d, complain);
return process_outer_var_ref (d, complain, force_use);
}
}
......@@ -3353,20 +3359,6 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain)
&& uses_template_parms (DECL_TI_ARGS (containing_function)))
return 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_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))
{
......
......@@ -7044,16 +7044,24 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p,
/* Return an expression representing static_cast<TYPE>(EXPR). */
tree
build_static_cast (tree type, tree expr, tsubst_flags_t complain)
build_static_cast (tree type, tree oexpr, tsubst_flags_t complain)
{
tree expr = oexpr;
tree result;
bool valid_p;
if (type == error_mark_node || expr == error_mark_node)
return error_mark_node;
if (processing_template_decl)
bool dependent = (dependent_type_p (type)
|| type_dependent_expression_p (expr));
if (dependent)
{
tmpl:
expr = oexpr;
if (dependent)
/* Handle generic lambda capture. */
expr = mark_lvalue_use (expr);
expr = build_min (STATIC_CAST_EXPR, type, expr);
/* We don't know if it will or will not have side effects. */
TREE_SIDE_EFFECTS (expr) = 1;
......@@ -7076,6 +7084,8 @@ build_static_cast (tree type, tree expr, tsubst_flags_t complain)
maybe_warn_about_useless_cast (type, expr, complain);
maybe_warn_about_cast_ignoring_quals (type, complain);
}
if (processing_template_decl)
goto tmpl;
return result;
}
......
......@@ -6,5 +6,5 @@ int z;
int main() {
constexpr int& y = x;
[=] { z = y; }();
[] { z = y; }();
}
// PR c++/56973
// { dg-do compile { target c++11 } }
int f()
{
const int i = 42;
auto j = *[=]{ return &i; }();
auto k = []{ return i; }();
return j+k;
}
int main()
{
return f() != 84;
}
// { dg-do compile { target c++11 } }
// { dg-options -w }
int main()
{
const int i = 4;
[] { constexpr int x = i; };
[=] { &i; constexpr int x = i; };
[&] { &i; constexpr int x = i; };
[i] { &i; constexpr int x = i; };
[&i] { &i; constexpr int x = i; };
}
......@@ -13,7 +13,7 @@ template <typename T>
void bar (T) {
constexpr auto N = a<1>;
auto f = [&] (auto i) {
static_assert (static_cast<int>(N) == 1, "");
return static_cast<int>(N) == 1;
};
foo (f);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment