Message ID | 20211210222909.649695-1-polacek@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: Allow constexpr decltype(auto) [PR102229] | expand |
On 12/10/21 17:29, Marek Polacek wrote: > My r11-2202 was trying to enforce [dcl.type.auto.deduct]/4, which says > "If the placeholder-type-specifier is of the form type-constraint[opt] > decltype(auto), T shall be the placeholder alone." But this made us > reject 'constexpr decltype(auto)', which, after clarification from CWG, > should be valid. [dcl.type.auto.deduct]/4 is supposed to be a syntactic > constraint, not semantic, so it's OK that the constexpr marks the object > as const. > > As a consequence, checking TYPE_QUALS in do_auto_deduction is too late, > and we have a FIXME there anyway. So in this patch I'm attempting to > detect 'const decltype(auto)' earlier. If I'm going to use TYPE_QUALS, > it needs to happen before we mark the object as const due to constexpr, > that is, before grokdeclarator's > > /* A `constexpr' specifier used in an object declaration declares > the object as `const'. */ > if (constexpr_p && innermost_code != cdk_function) > ... > > Constrained decltype(auto) was a little problem, hence the TYPENAME > check. But in a typename context you can't use decltype(auto) anyway, > I think. I wonder about checking even earlier, like in cp_parser_decl_specifier_seq? > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/11? > > PR c++/102229 > > gcc/cp/ChangeLog: > > * decl.c (check_decltype_auto): New. > (grokdeclarator): Call it. > * pt.c (do_auto_deduction): Don't check decltype(auto) here. > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp1y/decltype-auto5.C: New test. > --- > gcc/cp/decl.c | 58 ++++++++++++++------- > gcc/cp/pt.c | 13 ----- > gcc/testsuite/g++.dg/cpp1y/decltype-auto5.C | 35 +++++++++++++ > 3 files changed, 74 insertions(+), 32 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp1y/decltype-auto5.C > > diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c > index 56f80775ca0..196eac287eb 100644 > --- a/gcc/cp/decl.c > +++ b/gcc/cp/decl.c > @@ -11354,6 +11354,33 @@ name_unnamed_type (tree type, tree decl) > gcc_assert (!TYPE_UNNAMED_P (type)); > } > > +/* Check that decltype(auto) was well-formed: only plain decltype(auto) > + is allowed. TYPE might contain a decltype(auto). Returns true if > + there was a problem, false otherwise. */ > + > +static bool > +check_decltype_auto (location_t loc, tree type) > +{ > + if (tree a = type_uses_auto (type)) > + { > + if (AUTO_IS_DECLTYPE (a)) > + { > + if (a != type) > + { > + error_at (loc, "%qT as type rather than plain " > + "%<decltype(auto)%>", type); > + return true; > + } > + else if (TYPE_QUALS (type) != TYPE_UNQUALIFIED) > + { > + error_at (loc, "%<decltype(auto)%> cannot be cv-qualified"); > + return true; > + } > + } > + } > + return false; > +} > + > /* Given declspecs and a declarator (abstract or otherwise), determine > the name and type of the object declared and construct a DECL node > for it. > @@ -12702,25 +12729,9 @@ grokdeclarator (const cp_declarator *declarator, > "allowed"); > return error_mark_node; > } > - /* Only plain decltype(auto) is allowed. */ > - if (tree a = type_uses_auto (type)) > - { > - if (AUTO_IS_DECLTYPE (a)) > - { > - if (a != type) > - { > - error_at (typespec_loc, "%qT as type rather than " > - "plain %<decltype(auto)%>", type); > - return error_mark_node; > - } > - else if (TYPE_QUALS (type) != TYPE_UNQUALIFIED) > - { > - error_at (typespec_loc, "%<decltype(auto)%> cannot be " > - "cv-qualified"); > - return error_mark_node; > - } > - } > - } > + > + if (check_decltype_auto (typespec_loc, type)) > + return error_mark_node; > > if (ctype == NULL_TREE > && decl_context == FIELD > @@ -13080,6 +13091,15 @@ grokdeclarator (const cp_declarator *declarator, > > id_loc = declarator ? declarator->id_loc : input_location; > > + if (innermost_code != cdk_function > + /* Don't check this if it can be the artifical decltype(auto) > + we created when building a constraint in a compound-requirement: > + that the type-constraint is plain is going to be checked in > + cp_parser_compound_requirement. */ > + && decl_context != TYPENAME > + && check_decltype_auto (id_loc, type)) > + return error_mark_node; > + > /* A `constexpr' specifier used in an object declaration declares > the object as `const'. */ > if (constexpr_p && innermost_code != cdk_function) > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > index 9834baf34db..6d5da17b9d0 100644 > --- a/gcc/cp/pt.c > +++ b/gcc/cp/pt.c > @@ -29912,19 +29912,6 @@ do_auto_deduction (tree type, tree init, tree auto_node, > return error_mark_node; > targs = make_tree_vec (1); > TREE_VEC_ELT (targs, 0) = deduced; > - /* FIXME: These errors ought to be diagnosed at parse time. */ > - if (type != auto_node) > - { > - if (complain & tf_error) > - error ("%qT as type rather than plain %<decltype(auto)%>", type); > - return error_mark_node; > - } > - else if (TYPE_QUALS (type) != TYPE_UNQUALIFIED) > - { > - if (complain & tf_error) > - error ("%<decltype(auto)%> cannot be cv-qualified"); > - return error_mark_node; > - } > } > else > { > diff --git a/gcc/testsuite/g++.dg/cpp1y/decltype-auto5.C b/gcc/testsuite/g++.dg/cpp1y/decltype-auto5.C > new file mode 100644 > index 00000000000..01cc54fb71d > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp1y/decltype-auto5.C > @@ -0,0 +1,35 @@ > +// PR c++/102229 > +// { dg-do compile { target c++14 } } > + > +struct S { > + constexpr static decltype(auto) x = 42; > + const constexpr static decltype(auto) y = 42; // { dg-error "cannot be cv-qualified" } > + > + constexpr decltype(auto) mfn1 () { return 0; } > + const constexpr decltype(auto) mfn2 () { return 0; } // { dg-error "cannot be cv-qualified" } > +}; > + > +constexpr decltype(auto) i = 42; > +const constexpr decltype(auto) j = 42; // { dg-error "cannot be cv-qualified" } > + > +constexpr decltype(auto) fn() { return 42; } > +const decltype(auto) fn2() { return 42; } // { dg-error "cannot be cv-qualified" } > + > +auto constexpr foo() -> const decltype(auto) // { dg-error "cannot be cv-qualified" } > +{ > + return 0; > +} > + > +#if __cpp_concepts > +template<typename> > +concept C = true; > + > +constexpr C decltype(auto) x1 = 0; > +const constexpr C decltype(auto) x2 = 0; // { dg-error "cannot be cv-qualified" "" { target c++20 } } > + > +constexpr C decltype(auto) fn3() { return 0; } > +const constexpr C decltype(auto) fn4() { return 0; } // { dg-error "cannot be cv-qualified" "" { target c++20 } } > +#endif > + > +template<const decltype(auto) = 42> // { dg-error "cannot be cv-qualified" } > +void g (); > > base-commit: 1e2eee7b29ed2afbc7edea6f3da7e6c8f70b1a4e
On Mon, Dec 13, 2021 at 10:02:24AM -0500, Jason Merrill wrote: > On 12/10/21 17:29, Marek Polacek wrote: > > My r11-2202 was trying to enforce [dcl.type.auto.deduct]/4, which says > > "If the placeholder-type-specifier is of the form type-constraint[opt] > > decltype(auto), T shall be the placeholder alone." But this made us > > reject 'constexpr decltype(auto)', which, after clarification from CWG, > > should be valid. [dcl.type.auto.deduct]/4 is supposed to be a syntactic > > constraint, not semantic, so it's OK that the constexpr marks the object > > as const. > > > > As a consequence, checking TYPE_QUALS in do_auto_deduction is too late, > > and we have a FIXME there anyway. So in this patch I'm attempting to > > detect 'const decltype(auto)' earlier. If I'm going to use TYPE_QUALS, > > it needs to happen before we mark the object as const due to constexpr, > > that is, before grokdeclarator's > > > > /* A `constexpr' specifier used in an object declaration declares > > the object as `const'. */ > > if (constexpr_p && innermost_code != cdk_function) > > ... > > > > Constrained decltype(auto) was a little problem, hence the TYPENAME > > check. But in a typename context you can't use decltype(auto) anyway, > > I think. > > I wonder about checking even earlier, like in cp_parser_decl_specifier_seq? That _almost_ works except it wouldn't detect things like 'decltype(auto)*' because the '*' isn't parsed in cp_parser_decl_specifier_seq, only in declarator. So the if (a != type) { error_at (loc, "%qT as type rather than plain " "%<decltype(auto)%>", type); check wouldn't work. Maybe I could just check if the next token is * or & and give an error then. Marek
On 12/15/21 15:20, Marek Polacek wrote: > On Mon, Dec 13, 2021 at 10:02:24AM -0500, Jason Merrill wrote: >> On 12/10/21 17:29, Marek Polacek wrote: >>> My r11-2202 was trying to enforce [dcl.type.auto.deduct]/4, which says >>> "If the placeholder-type-specifier is of the form type-constraint[opt] >>> decltype(auto), T shall be the placeholder alone." But this made us >>> reject 'constexpr decltype(auto)', which, after clarification from CWG, >>> should be valid. [dcl.type.auto.deduct]/4 is supposed to be a syntactic >>> constraint, not semantic, so it's OK that the constexpr marks the object >>> as const. >>> >>> As a consequence, checking TYPE_QUALS in do_auto_deduction is too late, >>> and we have a FIXME there anyway. So in this patch I'm attempting to >>> detect 'const decltype(auto)' earlier. If I'm going to use TYPE_QUALS, >>> it needs to happen before we mark the object as const due to constexpr, >>> that is, before grokdeclarator's >>> >>> /* A `constexpr' specifier used in an object declaration declares >>> the object as `const'. */ >>> if (constexpr_p && innermost_code != cdk_function) >>> ... >>> >>> Constrained decltype(auto) was a little problem, hence the TYPENAME >>> check. But in a typename context you can't use decltype(auto) anyway, >>> I think. >> >> I wonder about checking even earlier, like in cp_parser_decl_specifier_seq? > > That _almost_ works except it wouldn't detect things like 'decltype(auto)*' > because the '*' isn't parsed in cp_parser_decl_specifier_seq, only in > declarator. So the Ah, right. > if (a != type) > { > error_at (loc, "%qT as type rather than plain " > "%<decltype(auto)%>", type); > > check wouldn't work. Maybe I could just check if the next token is * or & > and give an error then. No, checking in grokdeclarator makes sense. > Constrained decltype(auto) was a little problem, hence the TYPENAME > check. But in a typename context you can't use decltype(auto) anyway, > I think. Maybe check PLACEHOLDER_TYPE_CONSTRAINTS in check_decltype_auto instead? Jason
On Wed, Dec 15, 2021 at 04:17:37PM -0500, Jason Merrill wrote: > On 12/15/21 15:20, Marek Polacek wrote: > > On Mon, Dec 13, 2021 at 10:02:24AM -0500, Jason Merrill wrote: > > > On 12/10/21 17:29, Marek Polacek wrote: > > > > My r11-2202 was trying to enforce [dcl.type.auto.deduct]/4, which says > > > > "If the placeholder-type-specifier is of the form type-constraint[opt] > > > > decltype(auto), T shall be the placeholder alone." But this made us > > > > reject 'constexpr decltype(auto)', which, after clarification from CWG, > > > > should be valid. [dcl.type.auto.deduct]/4 is supposed to be a syntactic > > > > constraint, not semantic, so it's OK that the constexpr marks the object > > > > as const. > > > > > > > > As a consequence, checking TYPE_QUALS in do_auto_deduction is too late, > > > > and we have a FIXME there anyway. So in this patch I'm attempting to > > > > detect 'const decltype(auto)' earlier. If I'm going to use TYPE_QUALS, > > > > it needs to happen before we mark the object as const due to constexpr, > > > > that is, before grokdeclarator's > > > > > > > > /* A `constexpr' specifier used in an object declaration declares > > > > the object as `const'. */ > > > > if (constexpr_p && innermost_code != cdk_function) > > > > ... > > > > > > > > Constrained decltype(auto) was a little problem, hence the TYPENAME > > > > check. But in a typename context you can't use decltype(auto) anyway, > > > > I think. > > > > > > I wonder about checking even earlier, like in cp_parser_decl_specifier_seq? > > > > That _almost_ works except it wouldn't detect things like 'decltype(auto)*' > > because the '*' isn't parsed in cp_parser_decl_specifier_seq, only in > > declarator. So the > > Ah, right. > > > if (a != type) > > { > > error_at (loc, "%qT as type rather than plain " > > "%<decltype(auto)%>", type); > > > > check wouldn't work. Maybe I could just check if the next token is * or & > > and give an error then. > > No, checking in grokdeclarator makes sense. > > > Constrained decltype(auto) was a little problem, hence the TYPENAME > > check. But in a typename context you can't use decltype(auto) anyway, > > I think. > > Maybe check PLACEHOLDER_TYPE_CONSTRAINTS in check_decltype_auto instead? I've tried that, but that is also true for const constexpr C decltype(auto) x2 = 0; const constexpr C decltype(auto) fn4() { return 0; } where we do want to check if the auto has quals. Therefore the not very pretty TYPENAME check :/. Marek
On 12/15/21 16:28, Marek Polacek wrote: > On Wed, Dec 15, 2021 at 04:17:37PM -0500, Jason Merrill wrote: >> On 12/15/21 15:20, Marek Polacek wrote: >>> On Mon, Dec 13, 2021 at 10:02:24AM -0500, Jason Merrill wrote: >>>> On 12/10/21 17:29, Marek Polacek wrote: >>>>> My r11-2202 was trying to enforce [dcl.type.auto.deduct]/4, which says >>>>> "If the placeholder-type-specifier is of the form type-constraint[opt] >>>>> decltype(auto), T shall be the placeholder alone." But this made us >>>>> reject 'constexpr decltype(auto)', which, after clarification from CWG, >>>>> should be valid. [dcl.type.auto.deduct]/4 is supposed to be a syntactic >>>>> constraint, not semantic, so it's OK that the constexpr marks the object >>>>> as const. >>>>> >>>>> As a consequence, checking TYPE_QUALS in do_auto_deduction is too late, >>>>> and we have a FIXME there anyway. So in this patch I'm attempting to >>>>> detect 'const decltype(auto)' earlier. If I'm going to use TYPE_QUALS, >>>>> it needs to happen before we mark the object as const due to constexpr, >>>>> that is, before grokdeclarator's >>>>> >>>>> /* A `constexpr' specifier used in an object declaration declares >>>>> the object as `const'. */ >>>>> if (constexpr_p && innermost_code != cdk_function) >>>>> ... >>>>> >>>>> Constrained decltype(auto) was a little problem, hence the TYPENAME >>>>> check. But in a typename context you can't use decltype(auto) anyway, >>>>> I think. >>>> >>>> I wonder about checking even earlier, like in cp_parser_decl_specifier_seq? >>> >>> That _almost_ works except it wouldn't detect things like 'decltype(auto)*' >>> because the '*' isn't parsed in cp_parser_decl_specifier_seq, only in >>> declarator. So the >> >> Ah, right. >> >>> if (a != type) >>> { >>> error_at (loc, "%qT as type rather than plain " >>> "%<decltype(auto)%>", type); >>> >>> check wouldn't work. Maybe I could just check if the next token is * or & >>> and give an error then. >> >> No, checking in grokdeclarator makes sense. >> >>> Constrained decltype(auto) was a little problem, hence the TYPENAME >>> check. But in a typename context you can't use decltype(auto) anyway, >>> I think. >> >> Maybe check PLACEHOLDER_TYPE_CONSTRAINTS in check_decltype_auto instead? > > I've tried that, but that is also true for > > const constexpr C decltype(auto) x2 = 0; > const constexpr C decltype(auto) fn4() { return 0; } > > where we do want to check if the auto has quals. Therefore the not very > pretty TYPENAME check :/. Aha. The patch is OK. Jason
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 56f80775ca0..196eac287eb 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -11354,6 +11354,33 @@ name_unnamed_type (tree type, tree decl) gcc_assert (!TYPE_UNNAMED_P (type)); } +/* Check that decltype(auto) was well-formed: only plain decltype(auto) + is allowed. TYPE might contain a decltype(auto). Returns true if + there was a problem, false otherwise. */ + +static bool +check_decltype_auto (location_t loc, tree type) +{ + if (tree a = type_uses_auto (type)) + { + if (AUTO_IS_DECLTYPE (a)) + { + if (a != type) + { + error_at (loc, "%qT as type rather than plain " + "%<decltype(auto)%>", type); + return true; + } + else if (TYPE_QUALS (type) != TYPE_UNQUALIFIED) + { + error_at (loc, "%<decltype(auto)%> cannot be cv-qualified"); + return true; + } + } + } + return false; +} + /* Given declspecs and a declarator (abstract or otherwise), determine the name and type of the object declared and construct a DECL node for it. @@ -12702,25 +12729,9 @@ grokdeclarator (const cp_declarator *declarator, "allowed"); return error_mark_node; } - /* Only plain decltype(auto) is allowed. */ - if (tree a = type_uses_auto (type)) - { - if (AUTO_IS_DECLTYPE (a)) - { - if (a != type) - { - error_at (typespec_loc, "%qT as type rather than " - "plain %<decltype(auto)%>", type); - return error_mark_node; - } - else if (TYPE_QUALS (type) != TYPE_UNQUALIFIED) - { - error_at (typespec_loc, "%<decltype(auto)%> cannot be " - "cv-qualified"); - return error_mark_node; - } - } - } + + if (check_decltype_auto (typespec_loc, type)) + return error_mark_node; if (ctype == NULL_TREE && decl_context == FIELD @@ -13080,6 +13091,15 @@ grokdeclarator (const cp_declarator *declarator, id_loc = declarator ? declarator->id_loc : input_location; + if (innermost_code != cdk_function + /* Don't check this if it can be the artifical decltype(auto) + we created when building a constraint in a compound-requirement: + that the type-constraint is plain is going to be checked in + cp_parser_compound_requirement. */ + && decl_context != TYPENAME + && check_decltype_auto (id_loc, type)) + return error_mark_node; + /* A `constexpr' specifier used in an object declaration declares the object as `const'. */ if (constexpr_p && innermost_code != cdk_function) diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 9834baf34db..6d5da17b9d0 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -29912,19 +29912,6 @@ do_auto_deduction (tree type, tree init, tree auto_node, return error_mark_node; targs = make_tree_vec (1); TREE_VEC_ELT (targs, 0) = deduced; - /* FIXME: These errors ought to be diagnosed at parse time. */ - if (type != auto_node) - { - if (complain & tf_error) - error ("%qT as type rather than plain %<decltype(auto)%>", type); - return error_mark_node; - } - else if (TYPE_QUALS (type) != TYPE_UNQUALIFIED) - { - if (complain & tf_error) - error ("%<decltype(auto)%> cannot be cv-qualified"); - return error_mark_node; - } } else { diff --git a/gcc/testsuite/g++.dg/cpp1y/decltype-auto5.C b/gcc/testsuite/g++.dg/cpp1y/decltype-auto5.C new file mode 100644 index 00000000000..01cc54fb71d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/decltype-auto5.C @@ -0,0 +1,35 @@ +// PR c++/102229 +// { dg-do compile { target c++14 } } + +struct S { + constexpr static decltype(auto) x = 42; + const constexpr static decltype(auto) y = 42; // { dg-error "cannot be cv-qualified" } + + constexpr decltype(auto) mfn1 () { return 0; } + const constexpr decltype(auto) mfn2 () { return 0; } // { dg-error "cannot be cv-qualified" } +}; + +constexpr decltype(auto) i = 42; +const constexpr decltype(auto) j = 42; // { dg-error "cannot be cv-qualified" } + +constexpr decltype(auto) fn() { return 42; } +const decltype(auto) fn2() { return 42; } // { dg-error "cannot be cv-qualified" } + +auto constexpr foo() -> const decltype(auto) // { dg-error "cannot be cv-qualified" } +{ + return 0; +} + +#if __cpp_concepts +template<typename> +concept C = true; + +constexpr C decltype(auto) x1 = 0; +const constexpr C decltype(auto) x2 = 0; // { dg-error "cannot be cv-qualified" "" { target c++20 } } + +constexpr C decltype(auto) fn3() { return 0; } +const constexpr C decltype(auto) fn4() { return 0; } // { dg-error "cannot be cv-qualified" "" { target c++20 } } +#endif + +template<const decltype(auto) = 42> // { dg-error "cannot be cv-qualified" } +void g ();