Message ID | 20230328173732.1722425-1-ppalka@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: ICE on loopy var tmpl auto deduction [PR109300] | expand |
On 3/28/23 13:37, Patrick Palka wrote: > Now that we resolve non-dependent variable template-ids ahead of time, > cp_finish_decl needs to handle a new invalid situation: we can end up > trying to instantiate a variable template with deduced return type > before we fully parsed (and attached) its initializer. > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this OK for > trunK? > > PR c++/109300 > > gcc/cp/ChangeLog: > > * decl.cc (cp_finish_decl): Diagnose ordinary auto deduction > with no initializer instead of asserting. > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp1y/var-templ79.C: New test. > --- > gcc/cp/decl.cc | 15 ++++++++++++++- > gcc/testsuite/g++.dg/cpp1y/var-templ79.C | 5 +++++ > 2 files changed, 19 insertions(+), 1 deletion(-) > create mode 100644 gcc/testsuite/g++.dg/cpp1y/var-templ79.C > > diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc > index 20b980f68c8..2c91693b99d 100644 > --- a/gcc/cp/decl.cc > +++ b/gcc/cp/decl.cc > @@ -8276,7 +8276,20 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, > return; > } > > - gcc_assert (CLASS_PLACEHOLDER_TEMPLATE (auto_node)); > + if (CLASS_PLACEHOLDER_TEMPLATE (auto_node)) > + /* Class deduction with no initializer is OK. */; > + else > + { > + /* Ordinary auto deduction without an initializer, a situation > + which grokdeclarator already catches and rejects for the most > + part. But we can still get here if we're instantiating a > + variable template before we've fully parsed (and attached) its > + initializer, e.g. template<class> auto x = x<int>; */ In the case of recursively dependent instantiation I'd hope to have an error_mark_node initializer, rather than none? > + error_at (DECL_SOURCE_LOCATION (decl), > + "declaration of %q#D has no initializer", decl); > + TREE_TYPE (decl) = error_mark_node; > + return; > + } > } > d_init = init; > if (d_init) > diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ79.C b/gcc/testsuite/g++.dg/cpp1y/var-templ79.C > new file mode 100644 > index 00000000000..3c0d276153a > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ79.C > @@ -0,0 +1,5 @@ > +// PR c++/109300 > +// { dg-do compile { target c++14 } } > + > +template<class> > +auto x = x<int>; // { dg-error "" }
On Wed, 29 Mar 2023, Jason Merrill wrote: > On 3/28/23 13:37, Patrick Palka wrote: > > Now that we resolve non-dependent variable template-ids ahead of time, > > cp_finish_decl needs to handle a new invalid situation: we can end up > > trying to instantiate a variable template with deduced return type > > before we fully parsed (and attached) its initializer. > > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this OK for > > trunK? > > > > PR c++/109300 > > > > gcc/cp/ChangeLog: > > > > * decl.cc (cp_finish_decl): Diagnose ordinary auto deduction > > with no initializer instead of asserting. > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/cpp1y/var-templ79.C: New test. > > --- > > gcc/cp/decl.cc | 15 ++++++++++++++- > > gcc/testsuite/g++.dg/cpp1y/var-templ79.C | 5 +++++ > > 2 files changed, 19 insertions(+), 1 deletion(-) > > create mode 100644 gcc/testsuite/g++.dg/cpp1y/var-templ79.C > > > > diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc > > index 20b980f68c8..2c91693b99d 100644 > > --- a/gcc/cp/decl.cc > > +++ b/gcc/cp/decl.cc > > @@ -8276,7 +8276,20 @@ cp_finish_decl (tree decl, tree init, bool > > init_const_expr_p, > > return; > > } > > - gcc_assert (CLASS_PLACEHOLDER_TEMPLATE (auto_node)); > > + if (CLASS_PLACEHOLDER_TEMPLATE (auto_node)) > > + /* Class deduction with no initializer is OK. */; > > + else > > + { > > + /* Ordinary auto deduction without an initializer, a situation > > + which grokdeclarator already catches and rejects for the most > > + part. But we can still get here if we're instantiating a > > + variable template before we've fully parsed (and attached) > > its > > + initializer, e.g. template<class> auto x = x<int>; */ > > In the case of recursively dependent instantiation I'd hope to have an > error_mark_node initializer, rather than none? Do you mean setting the initializer to error_mark_node after the fact, e.g. @@ -8288,7 +8297,7 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, error_at (DECL_SOURCE_LOCATION (decl), "declaration of %q#D has no initializer", decl); TREE_TYPE (decl) = error_mark_node; - return; + init = error_mark_node; } } d_init = init; or before the fact, i.e. setting DECL_INITIAL to error_mark_node as a sentinel value for detecting recursion before we begin parsing a variable initializer? The former should work I suppose, but the latter is problematic because we also call cp_finish_decl with init=error_mark_node when the initializer is generally invalid, so by overloading the meaning of error_mark_node here and checking for it from cp_finish_decl we would end up emitting a bogus extra diagnostic in a bunch of cases e.g. g++.dg/pr53055.C: int i = p ->* p ; // invalid initializer I guess we would need to use a different sentinel value for detecting recursion, or expose and inspect the 'lambda_scope' stack which already keeps track of whether we're in the middle of a variable initializer... Dunno if it's worth it just for sake of a better diagnostic for this corner case, I notice e.g. Clang doesn't give a great diagnostic either: src/gcc/testsuite/g++.dg/cpp1y/var-templ79.C:5:6: error: declaration of variable 'x' with deduced type 'auto' requires an initializer auto x = x<int>; // { dg-error "" } ^ > > > + error_at (DECL_SOURCE_LOCATION (decl), > > + "declaration of %q#D has no initializer", decl); > > + TREE_TYPE (decl) = error_mark_node; > > + return; > > + } > > } > > d_init = init; > > if (d_init) > > diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ79.C > > b/gcc/testsuite/g++.dg/cpp1y/var-templ79.C > > new file mode 100644 > > index 00000000000..3c0d276153a > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ79.C > > @@ -0,0 +1,5 @@ > > +// PR c++/109300 > > +// { dg-do compile { target c++14 } } > > + > > +template<class> > > +auto x = x<int>; // { dg-error "" } > >
On 4/3/23 12:28, Patrick Palka wrote: > On Wed, 29 Mar 2023, Jason Merrill wrote: > >> On 3/28/23 13:37, Patrick Palka wrote: >>> Now that we resolve non-dependent variable template-ids ahead of time, >>> cp_finish_decl needs to handle a new invalid situation: we can end up >>> trying to instantiate a variable template with deduced return type >>> before we fully parsed (and attached) its initializer. >>> >>> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this OK for >>> trunK? >>> >>> PR c++/109300 >>> >>> gcc/cp/ChangeLog: >>> >>> * decl.cc (cp_finish_decl): Diagnose ordinary auto deduction >>> with no initializer instead of asserting. >>> >>> gcc/testsuite/ChangeLog: >>> >>> * g++.dg/cpp1y/var-templ79.C: New test. >>> --- >>> gcc/cp/decl.cc | 15 ++++++++++++++- >>> gcc/testsuite/g++.dg/cpp1y/var-templ79.C | 5 +++++ >>> 2 files changed, 19 insertions(+), 1 deletion(-) >>> create mode 100644 gcc/testsuite/g++.dg/cpp1y/var-templ79.C >>> >>> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc >>> index 20b980f68c8..2c91693b99d 100644 >>> --- a/gcc/cp/decl.cc >>> +++ b/gcc/cp/decl.cc >>> @@ -8276,7 +8276,20 @@ cp_finish_decl (tree decl, tree init, bool >>> init_const_expr_p, >>> return; >>> } >>> - gcc_assert (CLASS_PLACEHOLDER_TEMPLATE (auto_node)); >>> + if (CLASS_PLACEHOLDER_TEMPLATE (auto_node)) >>> + /* Class deduction with no initializer is OK. */; >>> + else >>> + { >>> + /* Ordinary auto deduction without an initializer, a situation >>> + which grokdeclarator already catches and rejects for the most >>> + part. But we can still get here if we're instantiating a >>> + variable template before we've fully parsed (and attached) >>> its >>> + initializer, e.g. template<class> auto x = x<int>; */ >> >> In the case of recursively dependent instantiation I'd hope to have an >> error_mark_node initializer, rather than none? > > Do you mean setting the initializer to error_mark_node after the fact, e.g. > > @@ -8288,7 +8297,7 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, > error_at (DECL_SOURCE_LOCATION (decl), > "declaration of %q#D has no initializer", decl); > TREE_TYPE (decl) = error_mark_node; > - return; > + init = error_mark_node; > } > } > d_init = init; > > or before the fact, i.e. setting DECL_INITIAL to error_mark_node as a > sentinel value for detecting recursion before we begin parsing a variable > initializer? The former should work I suppose, but the latter is > problematic because we also call cp_finish_decl with init=error_mark_node > when the initializer is generally invalid, so by overloading the meaning > of error_mark_node here and checking for it from cp_finish_decl we would > end up emitting a bogus extra diagnostic in a bunch of cases e.g. > g++.dg/pr53055.C: > > int i = p ->* p ; // invalid initializer > > I guess we would need to use a different sentinel value for detecting > recursion, or expose and inspect the 'lambda_scope' stack which already > keeps track of whether we're in the middle of a variable initializer... > Dunno if it's worth it just for sake of a better diagnostic for this > corner case, I notice e.g. Clang doesn't give a great diagnostic either: > > src/gcc/testsuite/g++.dg/cpp1y/var-templ79.C:5:6: error: declaration of variable 'x' with deduced type 'auto' requires an initializer > auto x = x<int>; // { dg-error "" } > ^ Yeah, let's just go with your patch, thanks. >> >>> + error_at (DECL_SOURCE_LOCATION (decl), >>> + "declaration of %q#D has no initializer", decl); >>> + TREE_TYPE (decl) = error_mark_node; >>> + return; >>> + } >>> } >>> d_init = init; >>> if (d_init) >>> diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ79.C >>> b/gcc/testsuite/g++.dg/cpp1y/var-templ79.C >>> new file mode 100644 >>> index 00000000000..3c0d276153a >>> --- /dev/null >>> +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ79.C >>> @@ -0,0 +1,5 @@ >>> +// PR c++/109300 >>> +// { dg-do compile { target c++14 } } >>> + >>> +template<class> >>> +auto x = x<int>; // { dg-error "" } >> >> >
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 20b980f68c8..2c91693b99d 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -8276,7 +8276,20 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, return; } - gcc_assert (CLASS_PLACEHOLDER_TEMPLATE (auto_node)); + if (CLASS_PLACEHOLDER_TEMPLATE (auto_node)) + /* Class deduction with no initializer is OK. */; + else + { + /* Ordinary auto deduction without an initializer, a situation + which grokdeclarator already catches and rejects for the most + part. But we can still get here if we're instantiating a + variable template before we've fully parsed (and attached) its + initializer, e.g. template<class> auto x = x<int>; */ + error_at (DECL_SOURCE_LOCATION (decl), + "declaration of %q#D has no initializer", decl); + TREE_TYPE (decl) = error_mark_node; + return; + } } d_init = init; if (d_init) diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ79.C b/gcc/testsuite/g++.dg/cpp1y/var-templ79.C new file mode 100644 index 00000000000..3c0d276153a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ79.C @@ -0,0 +1,5 @@ +// PR c++/109300 +// { dg-do compile { target c++14 } } + +template<class> +auto x = x<int>; // { dg-error "" }