diff options
author | Patrick Palka <ppalka@redhat.com> | 2023-12-08 16:57:13 -0500 |
---|---|---|
committer | Patrick Palka <ppalka@redhat.com> | 2023-12-08 16:57:13 -0500 |
commit | d9965fef40794d548021d2e34844e5fafeca4ce5 (patch) | |
tree | d1846780678b641da3ab33f077b3c0c4bac3ef9c | |
parent | 0bef72539e585d13941987369cf34726a7ac5b2e (diff) | |
download | gcc-d9965fef40794d548021d2e34844e5fafeca4ce5.zip gcc-d9965fef40794d548021d2e34844e5fafeca4ce5.tar.gz gcc-d9965fef40794d548021d2e34844e5fafeca4ce5.tar.bz2 |
c++: decltype of (non-captured variable) [PR83167]
For decltype((x)) within a lambda where x is not captured, we dubiously
require that the lambda has a capture default, unlike for decltype(x).
But according to [expr.prim.id.unqual]/3 we should just ignore the lambda
in this case. This patch narrowly fixes this issue by disabling the
capture_decltype handling and falling back to the ordinary handling when
the innermost lambda has no capture-default. In fact, we can restrict
the special handling to only by-copy lambdas since that's what
[expr.prim.id.unqual]/3 is concerned with; for by-ref implicit captures
both code paths should give the same result anyway.
During review some other issues were discovered which are documented in
a new FIXME.
PR c++/83167
gcc/cp/ChangeLog:
* semantics.cc (capture_decltype): Inline into its only caller ...
(finish_decltype_type): ... here. Update nearby comment to refer
to recent standard. Add FIXME. Restrict uncaptured variable type
transformation to happen only for lambdas with a by-copy
capture-default.
gcc/testsuite/ChangeLog:
* g++.dg/cpp0x/lambda/lambda-decltype4.C: New test.
-rw-r--r-- | gcc/cp/semantics.cc | 111 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp0x/lambda/lambda-decltype4.C | 15 |
2 files changed, 59 insertions, 67 deletions
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 6634acf..efd959d 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -53,7 +53,6 @@ along with GCC; see the file COPYING3. If not see static tree maybe_convert_cond (tree); static tree finalize_nrv_r (tree *, int *, void *); -static tree capture_decltype (tree); /* Used for OpenMP non-static data member privatization. */ @@ -11855,21 +11854,52 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p, } else { - /* Within a lambda-expression: - - Every occurrence of decltype((x)) where x is a possibly - parenthesized id-expression that names an entity of - automatic storage duration is treated as if x were - 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 (STRIP_REFERENCE_REF (expr)) && current_function_decl && LAMBDA_FUNCTION_P (current_function_decl)) { - type = capture_decltype (STRIP_REFERENCE_REF (expr)); - if (!type) - goto dependent; + /* [expr.prim.id.unqual]/3: If naming the entity from outside of an + unevaluated operand within S would refer to an entity captured by + copy in some intervening lambda-expression, then let E be the + innermost such lambda-expression. + + If there is such a lambda-expression and if P is in E's function + parameter scope but not its parameter-declaration-clause, then the + type of the expression is the type of a class member access + expression naming the non-static data member that would be declared + for such a capture in the object parameter of the function call + operator of E." */ + /* FIXME: This transformation needs to happen for all uses of an outer + local variable inside decltype, not just decltype((x)) (PR83167). + And we don't handle nested lambdas properly, where we need to + consider the outer lambdas as well (PR112926). */ + tree decl = STRIP_REFERENCE_REF (expr); + tree lam = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (current_function_decl)); + tree cap = lookup_name (DECL_NAME (decl), LOOK_where::BLOCK, + LOOK_want::HIDDEN_LAMBDA); + + if (cap && is_capture_proxy (cap)) + type = TREE_TYPE (cap); + else if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lam) == CPLD_COPY) + { + type = TREE_TYPE (decl); + if (TYPE_REF_P (type) + && TREE_CODE (TREE_TYPE (type)) != FUNCTION_TYPE) + type = TREE_TYPE (type); + } + + if (type && !TYPE_REF_P (type)) + { + tree obtype = TREE_TYPE (DECL_ARGUMENTS (current_function_decl)); + if (WILDCARD_TYPE_P (non_reference (obtype))) + /* We don't know what the eventual obtype quals will be. */ + goto dependent; + int quals = cp_type_quals (type); + if (INDIRECT_TYPE_P (obtype)) + quals |= cp_type_quals (TREE_TYPE (obtype)); + type = cp_build_qualified_type (type, quals); + type = build_reference_type (type); + } } else if (error_operand_p (expr)) type = error_mark_node; @@ -11877,7 +11907,8 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p, /* If the expression is just "this", we want the cv-unqualified pointer for the "this" type. */ type = TYPE_MAIN_VARIANT (TREE_TYPE (expr)); - else + + if (!type) { /* Otherwise, where T is the type of e, if e is an lvalue, decltype(e) is defined as T&; if an xvalue, T&&; otherwise, T. */ @@ -12766,60 +12797,6 @@ apply_deduced_return_type (tree fco, tree return_type) } } -/* DECL is a local variable or parameter from the surrounding scope of a - lambda-expression. Returns the decltype for a use of the capture field - for DECL even if it hasn't been captured yet. Or NULL_TREE if we can't give - a correct answer at this point and we should build a DECLTYPE_TYPE. */ - -static tree -capture_decltype (tree decl) -{ - tree lam = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (current_function_decl)); - tree cap = lookup_name (DECL_NAME (decl), LOOK_where::BLOCK, - LOOK_want::HIDDEN_LAMBDA); - tree type; - - if (cap && is_capture_proxy (cap)) - type = TREE_TYPE (cap); - else - switch (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lam)) - { - case CPLD_NONE: - error ("%qD is not captured", decl); - return error_mark_node; - - case CPLD_COPY: - type = TREE_TYPE (decl); - if (TYPE_REF_P (type) - && TREE_CODE (TREE_TYPE (type)) != FUNCTION_TYPE) - type = TREE_TYPE (type); - break; - - case CPLD_REFERENCE: - type = TREE_TYPE (decl); - if (!TYPE_REF_P (type)) - type = build_reference_type (TREE_TYPE (decl)); - break; - - default: - gcc_unreachable (); - } - - if (!TYPE_REF_P (type)) - { - tree obtype = TREE_TYPE (DECL_ARGUMENTS (current_function_decl)); - if (WILDCARD_TYPE_P (non_reference (obtype))) - /* We don't know what the eventual obtype quals will be. */ - return NULL_TREE; - int quals = cp_type_quals (type); - if (INDIRECT_TYPE_P (obtype)) - quals |= cp_type_quals (TREE_TYPE (obtype)); - type = cp_build_qualified_type (type, quals); - type = build_reference_type (type); - } - return type; -} - /* Build a unary fold expression of EXPR over OP. If IS_RIGHT is true, this is a right unary fold. Otherwise it is a left unary fold. */ diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-decltype4.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-decltype4.C new file mode 100644 index 0000000..0062d7b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-decltype4.C @@ -0,0 +1,15 @@ +// PR c++/83167 +// { dg-do compile { target c++11 } } + +int main() { + int x; + const int y = 42; + + [] { + using ty1 = decltype((x)); + using ty1 = int&; + + using ty2 = decltype((y)); + using ty2 = const int&; + }; +} |