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.
{
/* 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);
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);
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,
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 *,
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)
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. */
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. */
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)
{
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
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. */
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. */
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);
}
}
&& 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))
{
/* 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;
maybe_warn_about_useless_cast (type, expr, complain);
maybe_warn_about_cast_ignoring_quals (type, complain);
}
+ if (processing_template_decl)
+ goto tmpl;
return result;
}
int main() {
constexpr int& y = x;
- [=] { z = y; }();
+ [] { z = y; }();
}
--- /dev/null
+// 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;
+}
--- /dev/null
+// { 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; };
+}
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);
}