On Tue, 14 Nov 2023, Jason Merrill wrote: > On 11/14/23 11:10, Patrick Palka wrote: > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for > > trunk? > > > > -- >8 -- > > > > 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). > > This patch fixes this inconsistency; I couldn't find a justification for > > it in the standard. > > The relevant passage seems to be > > https://eel.is/c++draft/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 ([expr.ref]) naming > the non-static data member that would be declared for such a capture in the > object parameter ([dcl.fct]) of the function call operator of E." > > In this case I guess there is no such lambda-expression because naming x won't > refer to a capture by copy if the lambda doesn't capture anything, so we > ignore the lambda. > > Maybe refer to that in a comment? OK with that change. > > I'm surprised that it refers specifically to capture by copy, but I guess a > capture by reference should have the same decltype as the captured variable?
Ah, seems like it. So maybe we should get rid of the redundant by-reference capture-default handling, to more closely mirror the standard? Also now that r14-6026-g73e2bdbf9bed48 made capture_decltype return NULL_TREE to mean the capture is dependent, it seems we should just inline capture_decltype into finish_decltype_type rather than introducing another special return value to mean "fall back to ordinary handling". How does the following look? Bootstrapped and regtested on x86_64-pc-linux-gnu. -- >8 -- 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. Restrict uncaptured variable handling to just lambdas with a by-copy capture-default. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/lambda/lambda-decltype4.C: New test. --- gcc/cp/semantics.cc | 107 +++++++----------- .../g++.dg/cpp0x/lambda/lambda-decltype4.C | 15 +++ 2 files changed, 55 insertions(+), 67 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-decltype4.C diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index fbbc18336a0..fb4c3992e34 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. */ @@ -11856,21 +11855,48 @@ 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." */ + 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; @@ -11878,7 +11904,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. */ @@ -12767,60 +12794,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 00000000000..0062d7b8672 --- /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&; + }; +} -- 2.43.0.rc1