Message ID | DM5PR0501MB3733D17BA1A833ACD13AAD76B5CC9@DM5PR0501MB3733.namprd05.prod.outlook.com |
---|---|
State | New |
Headers | show |
Series | c++: fix cases of core1001/1322 by not dropping cv-qualifier of function parameter of type of typename or decltype[PR101402,PR102033,PR102034,PR102039,PR102044] | expand |
On 8/31/21 09:55, nick huang via Gcc-patches wrote: > These bugs are considered duplicate cases of PR51851 which has been suspended > since 2012, an issue known as "core1001/1322". Considering this background, > it deserves a long comment to explain. > > Many people believed the root cause of this family of bugs is related with > the nature of how and when the array type is converted to pointer type during > function signature is calculated. This is true, but we may need to go into details > to understand the exact reason. > > There is a pattern for these bugs(PR101402,PR102033,PR102034,PR102039). In the > template function declaration, the function parameter is consisted of a "const" > followed by a typename-type which is actually an array type. According to > standard, function signature is calculated by dropping so-called > "top-level-cv-qualifier". As a result, the templater specialization complains > no matching to declaration can be found because specialization has const and > template function declaration doesn't have const which is dropped as mentioned. > Obviously the template function declaration should NOT drop the const. But why? > Let's review the procedure of standard first. > (https://timsong-cpp.github.io/cppwp/dcl.fct#5.sentence-3) > > "After determining the type of each parameter, any parameter of type “array of T” > or of function type T is adjusted to be “pointer to T”. After producing the list > of parameter types, any top-level cv-qualifiers modifying a parameter type are > deleted when forming the function type." > > Please note the action of deleting top-level cv-qualifiers happens at last stage > after array type is converted to pointer type. More importantly, there are two > conditions: > a) Each type must be able to be determined. > b) The cv-qualifier must be top-level. > Let's analysis if these two conditions can be met one by one. > 1) Keyword "typename" indicates inside template it involves dependent name > (https://timsong-cpp.github.io/cppwp/n4659/temp.res#2) for which the name lookup > can be postponed until template instantiation. Clearly the type of dependent > name cannot be determined without name lookup. Then we can NOT proceed to next > step until concrete template argument type is determined during specialization. > 2) After “array of T” is converted to “pointer to T”, the cv-qualifiers are no > longer top-level! Unfortunately in standard there is no definition > of "top-level". Mr. Dan Saks's articals (https://www.dansaks.com/articles.shtml) > are tremendous help! Especially this wonderful paper (https://www.dansaks.com/articles/2000-02%20Top-Level%20cv-Qualifiers%20in%20Function%20Parameters.pdf) > discusses this topic in details. In one short sentence, the "const" before > array type is NOT top-level-cv-qualifier and should NOT be dropped. > > So, understanding the root cause makes the fix very clear: Let's NOT drop > cv-qualifier for typename-type inside template. Leave this task for template > substitution later when template specialization locks template argument types. > > Similarly inside template, "decltype" may also include dependent name and > the best strategy for parser is to preserve all original declaration and > postpone the task till template substitution. > > Here is an interesting observation to share. Originally my fix is trying to > use function "resolve_typename_type" to see if the "typename-type" is indeed > an array type so as to decide whether the const should be dropped. It works > for cases of PR101402,PR102033(with a small fix of function), but cannot > succeed on cases of PR102034,PR102039. Especially PR102039 is impossible > because it depends on template argument. This helps me realize that parser > should not do any work if it cannot be 100% successful. All can wait. > > At last I want to acknowledge other efforts to tackle this core 1001/1322 from > PR92010 which is an irreplaceable different approach from this fix by doing > rebuilding template function signature during template substitution stage. > After all, this fix can only deal with dependent type started with "typename" > or "decltype" which is not the case of pr92010. Unfortunately, your patch breaks template <class T> struct A { void f(T); }; template <class T> void A<T>::f(const T) { } which is certainly questionable code, but is currently also accepted by clang and EDG compilers. Why doesn't the PR92010 fix address these testcases as well? > gcc/cp/ChangeLog: > > 2021-08-30 qingzhe huang <nickhuang99@hotmail.com> > > * decl.c (grokparms): > > gcc/testsuite/ChangeLog: > > 2021-08-30 qingzhe huang <nickhuang99@hotmail.com> > > * g++.dg/parse/pr101402.C: New test. > * g++.dg/parse/pr102033.C: New test. > * g++.dg/parse/pr102034.C: New test. > * g++.dg/parse/pr102039.C: New test. > * g++.dg/parse/pr102044.C: New test. > > > diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c > index e0c603aaab6..940c43ce707 100644 > --- a/gcc/cp/decl.c > +++ b/gcc/cp/decl.c > @@ -14384,7 +14384,16 @@ grokparms (tree parmlist, tree *parms) > > /* Top-level qualifiers on the parameters are > ignored for function types. */ > - type = cp_build_qualified_type (type, 0); > + > + int type_quals = 0; > + /* Inside template declaration, typename and decltype indicating > + dependent name and cv-qualifier are preserved until > + template instantiation. > + PR101402/PR102033/PR102034/PR102039/PR102044 */ > + if (processing_template_decl > + && (TREE_CODE (type) == TYPENAME_TYPE || TREE_CODE (type) == DECLTYPE_TYPE)) > + type_quals = CP_TYPE_CONST_P(type); > + type = cp_build_qualified_type (type, type_quals); > if (TREE_CODE (type) == METHOD_TYPE) > { > error ("parameter %qD invalidly declared method type", decl); > diff --git a/gcc/testsuite/g++.dg/parse/pr101402.C b/gcc/testsuite/g++.dg/parse/pr101402.C > new file mode 100644 > index 00000000000..58d9c4f8542 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/parse/pr101402.C > @@ -0,0 +1,5 @@ > +template<class T> struct A { > + typedef T arr[3]; > +}; > +template<class T> void f(const typename A<T>::arr) { } // #1 > +template void f<int>(const A<int>::arr); > diff --git a/gcc/testsuite/g++.dg/parse/pr102033.C b/gcc/testsuite/g++.dg/parse/pr102033.C > new file mode 100644 > index 00000000000..0d5cc17620f > --- /dev/null > +++ b/gcc/testsuite/g++.dg/parse/pr102033.C > @@ -0,0 +1,34 @@ > +/* {dg-do compile } */ > +/* {dg-options "-std=c++11" } */ > + > +namespace test1 > +{ > +template<class TA> > +struct A{ > + template<class TB> > + using Type=TB[3]; > +}; > +template<class TA, class TB> > +void f(const typename A<TA>::template Type<TB>){} > +template <> > +void f<int, char>(const typename A<int>::template Type<char>){} > +} > +namespace test2 > +{ > +template<class TA> > +struct A{ > + template<class TB> > + struct B{ > + using TB_Alias=TB; > + template<class TC=TB_Alias> > + struct C{ > + typedef TC Arr3[3]; > + }; > + }; > +}; > +template<class TA, class TB> > +void f(const typename A<TA>::template B<TB>::template C<>::Arr3){} > +template <> > +void f<int, char>(const typename A<int>::template B<char>::template C<>::Arr3){} > +} > + > diff --git a/gcc/testsuite/g++.dg/parse/pr102034.C b/gcc/testsuite/g++.dg/parse/pr102034.C > new file mode 100644 > index 00000000000..37fdce52912 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/parse/pr102034.C > @@ -0,0 +1,13 @@ > +/*{dg-do compile} */ > +template<class TA> > +struct A{ > + template<class TB> > + struct B{ > + typedef TB Arr3[3]; > + }; > +}; > +template<class TA, class TB> > +void f(const typename A<TA>::template B<TB>::Arr3){} > +template <> > +void f<int, char>(const typename A<int>::B<char>::Arr3){} > + > diff --git a/gcc/testsuite/g++.dg/parse/pr102039.C b/gcc/testsuite/g++.dg/parse/pr102039.C > new file mode 100644 > index 00000000000..25d3e77fd74 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/parse/pr102039.C > @@ -0,0 +1,24 @@ > +namespace test1 > +{ > +struct A{ > + typedef int Arr3[3]; > +}; > + > +template<class T> > +void f(const typename T::Arr3){} > + > +template<> > +void f<A>(const int[3]){} > +} > + > +namespace test2 > +{ > +struct A{ > + typedef int Arr3[3]; > +}; > +template<class T> > +void f(const typename T::Arr3){} > +template<> > +void f<A>(const int*){} > +} > + > diff --git a/gcc/testsuite/g++.dg/parse/pr102044.C b/gcc/testsuite/g++.dg/parse/pr102044.C > new file mode 100644 > index 00000000000..bef6a920b47 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/parse/pr102044.C > @@ -0,0 +1,32 @@ > +/* {dg-do compile } */ > +/* {dg-options "-std=c++11" } */ > +namespace test1 > +{ > +template<unsigned int N, class T> > +void f(const T[N]){} > + > +template<unsigned int N, class T> > +using fPtr=decltype(f<N,T>)*; > + > +template<unsigned int N, class T> > +fPtr<N,T> af[N]={&f<N,T>}; > + > +template<unsigned int N, class T> > +void g(const decltype(af<N,T>)){} > + > +template<> > +void g<1,int>(const fPtr<1,int>[1]){} > +} > + > +namespace test2 > +{ > +template <class T> > +struct A{ > +T arr3[3]; > +}; > +template <class T> > +void f(const decltype(A<T>::arr3)){} > +template <> > +void f<int>(const int[3]){} > +} > + > >
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index e0c603aaab6..940c43ce707 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -14384,7 +14384,16 @@ grokparms (tree parmlist, tree *parms) /* Top-level qualifiers on the parameters are ignored for function types. */ - type = cp_build_qualified_type (type, 0); + + int type_quals = 0; + /* Inside template declaration, typename and decltype indicating + dependent name and cv-qualifier are preserved until + template instantiation. + PR101402/PR102033/PR102034/PR102039/PR102044 */ + if (processing_template_decl + && (TREE_CODE (type) == TYPENAME_TYPE || TREE_CODE (type) == DECLTYPE_TYPE)) + type_quals = CP_TYPE_CONST_P(type); + type = cp_build_qualified_type (type, type_quals); if (TREE_CODE (type) == METHOD_TYPE) { error ("parameter %qD invalidly declared method type", decl); diff --git a/gcc/testsuite/g++.dg/parse/pr101402.C b/gcc/testsuite/g++.dg/parse/pr101402.C new file mode 100644 index 00000000000..58d9c4f8542 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/pr101402.C @@ -0,0 +1,5 @@ +template<class T> struct A { + typedef T arr[3]; +}; +template<class T> void f(const typename A<T>::arr) { } // #1 +template void f<int>(const A<int>::arr); diff --git a/gcc/testsuite/g++.dg/parse/pr102033.C b/gcc/testsuite/g++.dg/parse/pr102033.C new file mode 100644 index 00000000000..0d5cc17620f --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/pr102033.C @@ -0,0 +1,34 @@ +/* {dg-do compile } */ +/* {dg-options "-std=c++11" } */ + +namespace test1 +{ +template<class TA> +struct A{ + template<class TB> + using Type=TB[3]; +}; +template<class TA, class TB> +void f(const typename A<TA>::template Type<TB>){} +template <> +void f<int, char>(const typename A<int>::template Type<char>){} +} +namespace test2 +{ +template<class TA> +struct A{ + template<class TB> + struct B{ + using TB_Alias=TB; + template<class TC=TB_Alias> + struct C{ + typedef TC Arr3[3]; + }; + }; +}; +template<class TA, class TB> +void f(const typename A<TA>::template B<TB>::template C<>::Arr3){} +template <> +void f<int, char>(const typename A<int>::template B<char>::template C<>::Arr3){} +} + diff --git a/gcc/testsuite/g++.dg/parse/pr102034.C b/gcc/testsuite/g++.dg/parse/pr102034.C new file mode 100644 index 00000000000..37fdce52912 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/pr102034.C @@ -0,0 +1,13 @@ +/*{dg-do compile} */ +template<class TA> +struct A{ + template<class TB> + struct B{ + typedef TB Arr3[3]; + }; +}; +template<class TA, class TB> +void f(const typename A<TA>::template B<TB>::Arr3){} +template <> +void f<int, char>(const typename A<int>::B<char>::Arr3){} + diff --git a/gcc/testsuite/g++.dg/parse/pr102039.C b/gcc/testsuite/g++.dg/parse/pr102039.C new file mode 100644 index 00000000000..25d3e77fd74 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/pr102039.C @@ -0,0 +1,24 @@ +namespace test1 +{ +struct A{ + typedef int Arr3[3]; +}; + +template<class T> +void f(const typename T::Arr3){} + +template<> +void f<A>(const int[3]){} +} + +namespace test2 +{ +struct A{ + typedef int Arr3[3]; +}; +template<class T> +void f(const typename T::Arr3){} +template<> +void f<A>(const int*){} +} + diff --git a/gcc/testsuite/g++.dg/parse/pr102044.C b/gcc/testsuite/g++.dg/parse/pr102044.C new file mode 100644 index 00000000000..bef6a920b47 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/pr102044.C @@ -0,0 +1,32 @@ +/* {dg-do compile } */ +/* {dg-options "-std=c++11" } */ +namespace test1 +{ +template<unsigned int N, class T> +void f(const T[N]){} + +template<unsigned int N, class T> +using fPtr=decltype(f<N,T>)*; + +template<unsigned int N, class T> +fPtr<N,T> af[N]={&f<N,T>}; + +template<unsigned int N, class T> +void g(const decltype(af<N,T>)){} + +template<> +void g<1,int>(const fPtr<1,int>[1]){} +} + +namespace test2 +{ +template <class T> +struct A{ +T arr3[3]; +}; +template <class T> +void f(const decltype(A<T>::arr3)){} +template <> +void f<int>(const int[3]){} +} +