Open xmh0511 opened 11 months ago
Looks similar to CWG2383
This is another example where the implementations have divergences
#include <iostream>
#include <tuple>
template<class ...T>
struct A{
template<template<T ...v> class...Tmpl>
void fun(){
using type = std::tuple<Tmpl<0>...>;
std::cout<< typeid(type).name();
}
};
template<int>
struct B{};
template<short>
struct C{};
int main(){
A<int, int>{}.fun<B,C>();
}
Clang accepts it while GCC rejects it.
I failed to recognize the potential defect in the standard wording. It seems clear to me that GCC is obviously buggy.
In the first example,
A<int, short>{}.fun<B>()
, then GCC raises ICE;A<int, short>{}.fun<>()
, then GCC incorrectly accepts it (which is similar to CWG2383).In the second example, GCC is just unable to determine what Tmpl
is, and the error message is definitely wrong.
I failed to recognize the potential defect in the standard wording. It seems clear to me that GCC is obviously buggy.
In the first example,
* if we change to write `A<int, short>{}.fun<B>()`, then GCC raises ICE; * if we change to write `A<int, short>{}.fun<>()`, then GCC incorrectly accepts it (which is similar to [CWG2383](https://cplusplus.github.io/CWG/issues/2383.html)).
In the second example, GCC is just unable to determine what
Tmpl
is, and the error message is definitely wrong.
No, the wording is not clear in this domain, I think.
A template parameter pack is a template parameter that accepts zero or more template arguments.
This permits A<int, short>{}.fun<B,B,B,B,B,B,B,...>()
;
In the second case, Clang also is wrong. the class template B
cannot match template<intm int> class...Tmpl
(after instantiation), I think.
[temp.variadic] p7 states:
[...] All of the packs expanded by a pack expansion shall have the same number of arguments specified. [...]
I think this sentence should apply to these examples.
How do you think [temp.variadic] p7 is relevant here? There is no more than one pack that is expanded by the pattern of a pack expansion in my example.
[temp.variadic] p7 applies the case like tuple<T, U>...
where T and U are packs and they have different numbers of elements.
How do you think [temp.variadic] p7 is relevant here?
The rationale mentioned in CWG2383 suggested that [temp.variadic] p7 should be relevant. Do you mean that it was wrong to consider these case to be subject to [temp.variadic] p7? (CWG2383 should be reopened if your reading is right.)
[temp.variadic] p7 applies the case like
tuple<T, U>...
where T and U are packs and they have different numbers of elements.
Not only in this case, see [temp.variadic] p5.3.
You quoted
All of the packs expanded by a pack expansion shall have the same number of arguments specified.
However, in my example, template<T v> class...Tmpl
is a pack expansion that only expands one pack T
. I didn't see how you think your quoted rule should apply to my example, anyway.
Not only in this case, see [temp.variadic] p5.3.
Again, I just gave an example of how your quoted rule should work, I didn't say that's the only case.
The conclusion in that CWG issue seems to imply we lack wording like:
A pack that is expanded by another parameter pack declaration that is a pack expansion shall have the same number of elements.
However, in my example,
template<T v> class...Tmpl
is a pack expansion that only expands one packT
. I didn't see how you think your quoted rule should apply to my example, anyway.
Because the Rationale shown CWG2383 clearly indicated this.
The old example was:
template<class ...Types> struct Tuple_ { // _VARIADIC_TEMPLATE
template<Types ...T> int f() {
return sizeof...(Types);
}
};
int main() {
Tuple_<char,int> a;
int b = a.f();
}
while the posted rationale was:
The example is ill-formed because the packs have different sizes:
Types
has 2,T
has 0 (from the call).
And nothing other than that quoted sentence can indicate such kind of ill-formedness.
The example is ill-formed because the packs have different sizes: Types has 2, T has 0 (from the call).
And nothing other than that quoted sentence can indicate such kind of ill-formedness.
However, your quoted sentence indeed does not apply to different packs in different pack expansions. The meaning of the quoted rule is just that simple: all packs that are expanded by the same pack expansion shall agree on their number of elements.
With that simplified example, class ...Types
declares a parameter pack Types
, and Types ...T
also declares another parameter pack T
, merely, the declaration is also a pack expansion. However, such two packs are not expanded by the same pack expansion, so your quote rule does not apply. Be carefully read your quoted rule
All of the packs expanded by a pack expansion shall have the same number of arguments specified.
Note that "a". I do think temp.variadic#example-5 is good to clarify what the rule intend to mean
template<class ... Args1> struct zip {
template<class ... Args2> struct with {
typedef Tuple<Pair<Args1, Args2> ... > type;
};
};
typedef zip<short>::with<unsigned short, unsigned>::type T2;
// error: different number of arguments specified for Args1 and Args2
Within this single pack expansion Pair<Args1, Args2> ...
, Args1
and Args2
do not agree with the number of elements, which is just the intend of the rule.
With that simplified example,
class ...Types
declares a parameter packTypes
, andTypes ...T
also declares another parameter packT
, merely, the declaration is also a pack expansion. However, such two packs are not expanded by the same pack expansion, so your quote rule does not apply.
So, do you mean that the rationale is not currently right, and additional wording is needed for justification?
When mentioning [temp.variadic] p5.3, I thought that "a template parameter pack that is a pack expansion is considered to expand itself", without which I didn't come up with a consistent reading.
This permits
A<int, short>{}.fun<B,B,B,B,B,B,B,...>()
;
Considering this case you suggested... what's T
if this is permitted?
So, do you mean that the rationale is not currently right, and additional wording is needed for justification?
In terms of the status quo, we lack that wording.
When mentioning [temp.variadic] p5.3, I thought that "a template parameter pack that is a pack expansion is considered to expand itself", without which I didn't come up with a consistent reading.
a template parameter pack that is a pack expansion does not expand the pack that is being declared, instead, the template parameter pack expands another pack that has been declared by another parameter pack declaration, see [temp.param] p17. Moreover [temp.variadic] p7 says
A pack whose name appears within the pattern of a pack expansion is expanded by that pack expansion.
For the pack expansion Types ...T
, it's the pack expansion of pack Types
rather than T
.
Considering this case you suggested... what's T if this is permitted?
This is the concerned issue here, template<T v> class ...Tmpl
declares a pack Tmpl
, then [temp.variadic] p1 says
A template parameter pack is a template parameter that accepts zero or more template arguments.
There is no wording to constrain how many arguments pack Tmpl
can actually accept when it is declared by expanding another pack T
.
I'm not understanding the confusion here. Here was the example in the original post:
template<class ...T>
struct A{
template<template<T v> class...Tmpl>
void fun(){}
};
The inner type-parameter is a pack expansion. When a specialization of A
is instantiated, the instantiation of the declaration of fun
results in the expansion of the inner pack. After expansion, it's no longer a pack; it's a list of 2 template parameters (assuming there are 2 elements in T
). Which of these steps, in your opinion, is not specified by the current wording?
Which of these steps, in your opinion, is not specified by the current wording?
When we say a template parameter is a pack, we are based on the grammar.
After expansion, it's no longer a pack; it's a list of 2 template parameters (assuming there are 2 elements in T).
No formal wording in the document says so. As you said, If Tmpl
were no longer a pack, the Tmpl
couldn't be expanded by other pack expansions anymore.
Which of these steps, in your opinion, is not specified by the current wording?
When we say a template parameter is a pack, we are based on the grammar.
After expansion, it's no longer a pack; it's a list of 2 template parameters (assuming there are 2 elements in T).
No formal wording in the document says so. As you said, If
Tmpl
were no longer a pack, theTmpl
couldn't be expanded by other pack expansions anymore.
Tmpl
still denotes a pack after the declaration of Tmpl
(i.e., in the template parameter list) has been expanded.
Which of these steps, in your opinion, is not specified by the current wording?
When we say a template parameter is a pack, we are based on the grammar.
After expansion, it's no longer a pack; it's a list of 2 template parameters (assuming there are 2 elements in T).
No formal wording in the document says so. As you said, If
Tmpl
were no longer a pack, theTmpl
couldn't be expanded by other pack expansions anymore.
Tmpl
still denotes a pack after the declaration ofTmpl
(i.e., in the template parameter list) has been expanded.
If you agree that Tmpl
is a template parameter pack, then [temp.variadic] p1 applies. If you don't agree that, then you should check [temp.param] p17
If a template-parameter is a type-parameter with an ellipsis prior to its optional identifier or is a parameter-declaration that declares a pack ([dcl.fct]), then the template-parameter is a template parameter pack.
Which of these steps, in your opinion, is not specified by the current wording?
When we say a template parameter is a pack, we are based on the grammar.
After expansion, it's no longer a pack; it's a list of 2 template parameters (assuming there are 2 elements in T).
No formal wording in the document says so. As you said, If
Tmpl
were no longer a pack, theTmpl
couldn't be expanded by other pack expansions anymore.
Tmpl
still denotes a pack after the declaration ofTmpl
(i.e., in the template parameter list) has been expanded.If you agree that
Tmpl
is a template parameter pack, then [temp.variadic] p1 applies. If you don't agree that, then you should check [temp.param] p17If a template-parameter is a type-parameter with an ellipsis prior to its optional identifier or is a parameter-declaration that declares a pack ([dcl.fct]), then the template-parameter is a template parameter pack.
No, again, you're conflating the declaration of Tmpl
(which is expanded when the outer template is instantiated) with the meaning of the id-expression Tmpl
should it appear within fun
, which would be a pack (and thus itself would need to be expanded).
[temp.param]/17 implies that the declaration of Tmpl
is a template parameter pack pre-expansion. It's because of this wording that that declaration ever gets expanded in the first place, i.e., the wording later in the same paragraph where it says that it's a pack expansion kicks in. After that has kicked in, the declaration of Tmpl
, having been expanded, is effectively replaced by 2 type template parameters. But as long as that Tmpl
is in scope, the id-expression Tmpl
denotes the pack that comprises the sequence of template template parameters produced by expanding the declaration of Tmpl
.
And I agree with @frederick-vs-ja that there is no real implementation divergence. GCC's behavior is not evidence that the other interpretation (i.e. that the inner pack accepts an arbitrary number of arguments, independently of how many arguments were provided to the outer pack) is plausible. If GCC were taking that interpretation, then GCC would actually accept an arbitrary number of arguments there (which it does not).
It's because of this wording that that declaration ever gets expanded in the first place, i.e., the wording later in the same paragraph where it says that it's a pack expansion kicks in. After that has kicked in, the declaration of Tmpl, having been expanded, is effectively replaced by 2 type template parameters. But as long as that Tmpl is in scope, the id-expression Tmpl denotes the pack that comprises the sequence of template template parameters produced by expanding the declaration of Tmpl.
This is totally what you understand, not the meaning of the formal wording talks. Given the formal example and relevant comments
template <class... T>
struct value_holder {
template <T... Values> struct apply { }; // Values is a non-type template parameter pack
}; // and a pack expansion
Values
is a template parameter pack and simultaneously its declaration is a pack expansion. Since Values
is a template parameter pack, then [temp.variadic] p1 applies. The rule is not in terms of whether the stuff it applies is the instantiated one or not.
(i.e. that the inner pack accepts an arbitrary number of arguments, independently of how many arguments were provided to the outer pack) is plausible. If GCC were taking that interpretation, then GCC would actually accept an arbitrary number of arguments there (which it does not).
Note that GCC accepts zero arguments A<int, short>{}.fun<>();
https://godbolt.org/z/4Esq73naq
Your issue is based on this premise:
When we say a template parameter is a pack, we are based on the grammar.
It sounds like by this you're saying that a pack is always something that appears in the input to phase 7 (and because of that, at every point during semantic analysis, it's still a pack, because the input of phase 7 doesn't change; it just gets used to produce the output of phase 7). But this premise is itself not supported by the text. The issue boils down to the fact that the standard is sloppy at distinguishing them. The standard is sloppy, but there are probably hundreds of other places in the standard where that's also true. Most of the time, this doesn't result in any actual confusion about the intended meaning. Here, as well, there shouldn't be any such confusion about the intended meaning.
IMO, the standard always defines the rules for grammar on the source code level. For example, [basic.def.odr] does not intend to apply to the instantiated declaration, such as https://github.com/cplusplus/CWG/issues/50#issuecomment-1165498636.
We lack a wording to constrain the number of elements in the declared pack P
and the number of elements in the pack that is used in the parameter pack declaration of P
Note that GCC accepts zero arguments
A<int, short>{}.fun<>();
https://godbolt.org/z/4Esq73naq
GCC only accepts zero argument, which doesn't conform to any viable reading (if the current specification is ambiguous).
We lack a wording to constrain the number of elements in the declared pack
P
and the number of elements in the pack that is used in the parameter pack declaration ofP
The lacked constrains (if any) are not limited to those on numbers. Per your reading (A<int, short>{}.fun<B,B,B,B,B,B,B,...>()
being allowed), it seems that A<short>{}.fun<B>()
is still allowed even when the constrains on numbers are added.
If your reading makes sense, then either
T
's for a single specialization of A
, orT
are effectively discarded when substituting into the declaration of fun
.In either case, there would be a larger issue than that on unequal numbers of elements.
there can be inconsistent T's for a single specialization of A, or
I don't know how you can read out that meaning. Imposing the requirement on one aspect does not mean the other aspects are not necessary to be obeyed. By your logic, for this rule
All of the packs expanded by a pack expansion shall have the same number of arguments specified.
That would mean that there can be inconsistent arguments for a single specialization for the pack expansion, wouldn't it?
there can be inconsistent T's for a single specialization of A, or
I don't know how you can read out that meaning.
Maybe I was wrong for your reading. But I think correcting a wrong reading of a "wrong" (possibly right, but merely about a potential defect of the standard wording) reading makes little sense.
If this is not what you meant, it's sufficient to just clarify this.
Imposing the requirement on one aspect does not mean the other aspects are not necessary to be obeyed. By your logic, for this rule
All of the packs expanded by a pack expansion shall have the same number of arguments specified.
That would mean that there can be inconsistent arguments for a single specialization for the pack expansion, wouldn't it?
Totally not what I meant.
Maybe I was wrong for your reading. But I think correcting a wrong reading of a "wrong" (possibly right, but merely about a potential defect of the standard wording) reading makes little sense.
As I said above, the status quo is we didn't impose the requirement between the number of elements in a template parameter pack P
that is a pack expansion and the number of elements of another pack P2
to which the prior template parameter pack P
is a pattern. For example:
template<class...T>
struct A{
template<T... v>
struct B{};
};
We should have a rule to impose the requirement to associate the number of elements in pack v
with the number of elements in pack T
Your quoted rule does not apply to such a case at all.
We should have a rule to impose the requirement to associate the number of elements in pack
v
with the number of elements in packT
Perhaps not only on the number. The requirement for number can be automatically added if proper requirements about matching are established (or is perhaps already added since the requirements are done).
Could you explain currently in this example what can v
be for a certain T
? E.g. is A<void*>::B<42>
well-formed?
Could you explain currently in this example what can v be for a certain T? E.g. is A<void*>::B<42> well-formed?
Whether according to the intuitive or the intent of the standard, this example should be ill-formed. If we have wording specifying that the template argument deduction occurs in these contexts, the issue will be easy to explain, for example, A<void*>
makes the pack T
be deduced to {void*}
, then B<42>
per [temp.deduct.type] p13
When the value of the argument corresponding to a non-type template parameter P that is declared with a dependent type is deduced from an expression, the template parameters in the type of P are deduced from the type of the value.
will make the pack T
be deduced to {int}
, then such two deduced results are conflicting, then according to [temp.deduct.type] p2
If type deduction cannot be done for any P/A pair, or if for any pair the deduction leads to more than one possible set of deduced values, or if different pairs yield different deduced values, or if any template argument remains neither deduced nor explicitly specified, template argument deduction fails.
Then, we can say the template argument deduction fails in this context is ill-formed. Moreover, if template argument deduction makes T
have a different number of elements, the construct will be ill-formed too. The consistent deduction for the same parameter can provide "matching".
However, we do not have such wording to say that the template argument deduction occurs when we specify the template arguments for A<void*>::B
.
IMO, T
after the instantiation of the specialization A<void*>
won't be considered as a dependent type, However, T...v
in the declaration of template class B
still be template parameter pack declaration, we do not have any wording says about what T...v
is in the instantiated declaration(that is, it's not a lexical product), so, we should have wording to additional specify the association between pack v
and the pack T
, which should be "matching".
Full name of submitter (unless configured in github; will be published with the issue): Jim X
[temp.param] p17 says:
Consider this example:
GCC and Clang have a divergence to this example. [temp.variadic] p1 just says
So, a template parameter pack can accept an arbitrary number of template arguments. However, in this example, the template parameter pack
Tmpl
is also a pack expansion that only has two elements, which intuitively should only accept two template-template arguments, I think.Suggested Resolution