aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Palka <ppalka@redhat.com>2023-11-10 10:58:06 -0500
committerPatrick Palka <ppalka@redhat.com>2023-11-10 10:58:06 -0500
commit0410b754e56c0868203c2412e0585ba070ea938d (patch)
tree3c2a2bd33d3585d2314b142b2639dda65f33f3b1
parent705ab7927c81b77503d229513fac991106617766 (diff)
downloadgcc-0410b754e56c0868203c2412e0585ba070ea938d.zip
gcc-0410b754e56c0868203c2412e0585ba070ea938d.tar.gz
gcc-0410b754e56c0868203c2412e0585ba070ea938d.tar.bz2
c++: decltype of (by-value captured reference) [PR79620]
The capture_decltype handling in finish_decltype_type wasn't looking through implicit INDIRECT_REF (added by convert_from_reference), which caused us to incorrectly resolve decltype((r)) to float& below. This patch fixes this, and adds an assert to outer_automatic_var_p to help prevent against such bugs. We still don't fully accept the example ultimately because for the decltype inside the lambda's trailing return type, at that point we're in lambda type scope but not yet in lambda function scope that the capture_decltype handling looks for (which is an orthogonal bug). PR c++/79620 gcc/cp/ChangeLog: * cp-tree.h (STRIP_REFERENCE_REF): Define. * semantics.cc (outer_var_p): Assert REFERENCE_REF_P is false. (finish_decltype_type): Look through implicit INDIRECT_REF when deciding whether to call capture_decltype. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/lambda/lambda-decltype3.C: New test. Reviewed-by: Jason Merrill <jason@redhat.com>
-rw-r--r--gcc/cp/cp-tree.h4
-rw-r--r--gcc/cp/semantics.cc7
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/lambda/lambda-decltype3.C28
3 files changed, 37 insertions, 2 deletions
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index b2603d4..1fa710d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4084,6 +4084,10 @@ struct GTY(()) lang_decl {
&& TREE_TYPE (TREE_OPERAND (NODE, 0)) \
&& TYPE_REF_P (TREE_TYPE (TREE_OPERAND ((NODE), 0))))
+/* Look through an implicit INDIRECT_REF from convert_from_reference. */
+#define STRIP_REFERENCE_REF(NODE) \
+ (REFERENCE_REF_P (NODE) ? TREE_OPERAND (NODE, 0) : NODE)
+
/* True iff this represents an lvalue being treated as an rvalue during return
or throw as per [class.copy.elision]. */
#define IMPLICIT_RVALUE_P(NODE) \
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index f5ae8b5..8090c71 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -3932,6 +3932,9 @@ baselink_for_fns (tree fns)
static bool
outer_var_p (tree decl)
{
+ /* These should have been stripped or otherwise handled by the caller. */
+ gcc_checking_assert (!REFERENCE_REF_P (decl));
+
return ((VAR_P (decl) || TREE_CODE (decl) == PARM_DECL)
&& DECL_FUNCTION_SCOPE_P (decl)
/* Don't get confused by temporaries. */
@@ -11719,10 +11722,10 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
transformed into an access to a corresponding data member
of the closure type that would have been declared if x
were a use of the denoted entity. */
- if (outer_automatic_var_p (expr)
+ if (outer_automatic_var_p (STRIP_REFERENCE_REF (expr))
&& current_function_decl
&& LAMBDA_FUNCTION_P (current_function_decl))
- type = capture_decltype (expr);
+ type = capture_decltype (STRIP_REFERENCE_REF (expr));
else if (error_operand_p (expr))
type = error_mark_node;
else if (expr == current_class_ptr)
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-decltype3.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-decltype3.C
new file mode 100644
index 0000000..7fc157a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-decltype3.C
@@ -0,0 +1,28 @@
+// PR c++/79620
+// [expr.prim.id.unqual] example 1
+// { dg-do compile { target c++11 } }
+
+void f() {
+ float x, &r = x;
+
+ [=]() -> decltype((x)) { // lambda returns float const& because this lambda is not mutable and
+ // x is an lvalue
+ decltype(x) y1; // y1 has type float
+ decltype((x)) y2 = y1; // y2 has type float const&
+ decltype(r) r1 = y1; // r1 has type float&
+ decltype((r)) r2 = y2; // r2 has type float const&
+ return y2; // { dg-bogus "'float&' to 'const float'" "" { xfail *-*-* } }
+ };
+
+ [=](decltype((x)) y) {
+ decltype((x)) z = x; // OK, y has type float&, z has type float const&
+ };
+
+ [=] {
+ [](decltype((x)) y) {}; // OK, lambda takes a parameter of type float const&
+
+ [x=1](decltype((x)) y) {
+ decltype((x)) z = x; // OK, y has type int&, z has type int const&
+ };
+ };
+}