summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>2017-09-28 19:39:45 +0000
committerjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>2017-09-28 19:39:45 +0000
commit4cedc476c94a41551d965bc57fbbe0186983c3c4 (patch)
tree4ec4cc5dfce6282bcbc8d1adfbbb092073395b53
parent6f20c78581397079216efbe5edfd1800b2fd5988 (diff)
downloadgcc-4cedc476c94a41551d965bc57fbbe0186983c3c4.tar.gz
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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@253266 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/cp/ChangeLog13
-rw-r--r--gcc/cp/call.c2
-rw-r--r--gcc/cp/cp-tree.h4
-rw-r--r--gcc/cp/decl2.c5
-rw-r--r--gcc/cp/expr.c107
-rw-r--r--gcc/cp/init.c1
-rw-r--r--gcc/cp/pt.c26
-rw-r--r--gcc/cp/semantics.c24
-rw-r--r--gcc/cp/typeck.c14
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/constexpr-64462.C2
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const6.C15
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const7.C12
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4.C2
13 files changed, 196 insertions, 31 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 2936f22a47e..6e2e3a8539e 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,18 @@
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.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 99a7b77efb2..05dc8bbdab7 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -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);
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index a6349019543..f56c9517967 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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 *,
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 03e91b7e150..29d6c59f549 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -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)
diff --git a/gcc/cp/expr.c b/gcc/cp/expr.c
index 8bd341b814e..f5c8e801918 100644
--- a/gcc/cp/expr.c
+++ b/gcc/cp/expr.c
@@ -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. */
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index b01d662fef2..4bc0755cdcb 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -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)
{
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 2bdac6de6c4..0dae10e032b 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -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
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 4e87e47d9b3..d96423f2348 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -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))
{
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 028d56ff18c..326721eb5e0 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -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;
}
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-64462.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-64462.C
index 69193fd0db7..8200f871057 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-64462.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-64462.C
@@ -6,5 +6,5 @@ int z;
int main() {
constexpr int& y = x;
- [=] { z = y; }();
+ [] { z = y; }();
}
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const6.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const6.C
new file mode 100644
index 00000000000..4edfb70038f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const6.C
@@ -0,0 +1,15 @@
+// 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;
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const7.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const7.C
new file mode 100644
index 00000000000..64a37b80a9b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const7.C
@@ -0,0 +1,12 @@
+// { 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; };
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4.C
index 52f4373ccbd..d56f379c680 100644
--- a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4.C
+++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4.C
@@ -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);
}