You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
First expressed at #529 (comment) (which occurred to me when working on that PR).
Will your feature suggestion eliminate X% of security vulnerabilities of a given kind in current C++ code? 馃憣 (zero).
Will your feature suggestion automate or eliminate X% of current C++ guidance literature?
If yes, please be specific about what current good guidance this helps make the default, and/or what guidelines we would no longer need to teach/learn or that would be simplified and how,
T.42 would be simplified by reducing the list of cases to which the enforcement can possibly apply.
with an example or two (especially a link to a real "Effective C++" or "C++ Core Guidelines" guideline or two).
Assuming that Cpp2 supports typename for disambiguation, just like Cpp1.
-x: typename T::type = ();+x: T::type = ();-x is typename T::type+x is T::type
Please limit suggestions to quantifiable improvements to C++ simplicity, safety, or toolability. Quantifiable means that there is some kind of measurable data that helps motivate the change and measure success. Two of the big ones that will get my attention are eliminating vulnerabilities and eliminating guidance, so use those below please.
There's 765 occurrences of 'typename ' in the C++ standard library,
not all disambiguating typenames
(extracted from the librarysources).
That's a 33% reduction.
Describe alternatives you've considered.
None.
There's nothing better than saying nothing at all when there's no room for another interpretation.
There's 765 occurrences of 'typename ' in the C++ standard library,
not all disambiguating typenames
(extracted from the librarysources).
That's a 33% reduction.
In Cpp2, the only grammar that could be an expression or type-id is a template-argument.
That's the only grammar production where disambiguating typename can't be made optional.
So if the C++ standard library were written in Cpp2, the reduction would be over 80%.
Now, the C++ standard library isn't such a good example because it's not actual C++ code.
It's written on prose, reducing the expressions that need disambiguating typename.
First: I love the title just as much now as when I first saw Nina and Daveed use it :)
Can you elaborate on what the proposal is though? Cpp2 currently doesn't use typename, and if I discover any places where it would be needed my first thought would be to fix the grammar so it's unambiguous.
Assuming that Cpp2 supports typename for disambiguation, just like Cpp1.
-x: typename T::type = ();+x: T::type = ();
The first line should not be allowed now, and the second is unambiguously a variable.
-x is typename T::type+x is T::type
is is interesting:
Dependent types: That x is T::type should work but doesn't yet, thanks for the bug report. It ought to emit typename for a dependent type... in this case it should generate cpp2::is<typename T::type>(x). But currently it emits cpp2::is<T::type>(x) which fails the Cpp1 compiler. I'll need to fix that.
Dependent (static) values:
a) Currently x is (T::value) works, and requires () which makes it unambiguous.
b) Note that to test this you have to write the type in Cpp1, because I haven't yet provided a way to author static values in Cpp2. I probably will allow them, but require them to be constinit or constexpr.
Here's a working test program:
struct Test {
using type = int;
static inline int value = 42;
};
dependent_is: <T> () = {
x := 42;
std::cout
// (x is T::type) << "\n" // doesn't work yet, need to inject `typename`
<< (x is (T::value)) << "\n";
}
main: () = {
x := 42;
std::cout << std::boolalpha
<< (x is int) << "\n" // "true"
<< (x is 42) << "\n"; // "true"
dependent_is<Test>(); // "true"
}
and if I discover any places where it would be needed my first thought would be to fix the grammar so it's unambiguous.
There is a grammar where it's needed: template-argument.
In identity<std::pointer_traits<ptr>::rebind<ptr>>, std::pointer_traits<ptr>::rebind<ptr> parses as an expression,
so typename would be required to make it a type.
See commit 8ae90d7.
Would you be solving the ambiguity in template-argument the same as with is-as-expression?
I.e., making type-id be the first production of template-argument.
I think that would make sense, and be consistent.
std::array<i32, 42> would continue working.
Most template arguments are types,
and will probably continue to be even with increasing NTTP support.
Adding disambiguating parenthesis around an expression isn't too noisy.
If done, there might be one difference between template-argument and is-as-expression.
e is e and e is (std::vector) fail at Cpp1 time if e is an expression
(the first one should be e is (e), and the second one e is std::vector).
t<e> would parse e as a type-id.
But if it's lowered as-is, it might not fail at Cpp1 time if e is actually an expression.
That's because the Cpp1 syntax t<x> is the same whether x is a type or expression.
So to get the same benefit of failing at Cpp1 time due to a kind-of-entity mismatch,
it'd be necessary to lower t<e> as t<cpp2::t<e>>,
where cpp2::t is defined to ensure e is a type:
template<classT> using t = T;
That would also
resolve #502,
resolve #456, and
close #521.
Would you be solving the ambiguity in template-argument the same as with is-as-expression?
Ack: You beat me to it -- I've been out of computer range but was going to reply saying I expected it would be the same as x is type and x is (value). The parens aren't needed for things like literals, of course.
I hope to finally be back in reliable computer range again on Friday...
If done, there might be one difference between template-argument and is-as-expression.
e is e and e is (std::vector) fail at Cpp1 time if e is an expression
(the first one should be e is (e), and the second one e is std::vector).
t<e> would parse e as a type-id.
But if it's lowered as-is, it might not fail at Cpp1 time if e is actually an expression.
That's because the Cpp1 syntax t<x> is the same whether x is a type or expression.
So to get the same benefit of failing at Cpp1 time due to a kind-of-entity mismatch,
it'd be necessary to lower t<e> as t<cpp2::t<e>>,
where cpp2::t is defined to ensure e is a type:
The same is true for an expression template argument that is lowered without the parentheses. 0 is (0) lowers to cpp2::is(0, (0)).
So if t<(i32)> lowers to t<(cpp2::i32)>, that's a Cpp1-time error 馃憤.
If it lowered to t<i32>, that'd be a semantic change from the parsed Cpp2 (from Cpp2 expression to Cpp1 type) 馃憥.
However, that means that changing template-argument to consider type-id before expression
breaks current uses of decltype template arguments, which happens to work today.
That's because t<decltype(x)> parses decltype(x) as an expression, lowers as-is, but is a Cpp1 type.
So if decltype isn't made a Cpp2 type-id before the change above, we lose the ability to use decltype in Cpp2.
Would you be solving the ambiguity in template-argument the same as with is-as-expression?
Ack: You beat me to it -- I've been out of computer range but was going to reply saying I expected it would be the same as x is type and x is (value). The parens aren't needed for things like literals, of course.
I hope to finally be back in reliable computer range again on Friday...
Note that although #533 marks this PR as to-be-fixed by merging it, it doesn't implement this idea.
So #533 doesn't provide a way to pass type template arguments.
Repository owner locked and limited conversation to collaborators on Aug 30, 2023
Activity
JohelEGP commentedon Jul 1, 2023
Note that because the Cpp2 grammar is unambiguous
we can have optional
typenamewhere Cpp1 can't.For example,
f: <T> (x: T::foo) = { }is a template function.The equivalent Cpp1 means something different if
typenameis omitted:JohelEGP commentedon Jul 1, 2023
In Cpp2, the only grammar that could be an expression or type-id is a template-argument.
That's the only grammar production where disambiguating
typenamecan't be made optional.So if the C++ standard library were written in Cpp2, the reduction would be over 80%.
Now, the C++ standard library isn't such a good example because it's not actual C++ code.
It's written on prose, reducing the expressions that need disambiguating
typename.hsutter commentedon Jul 1, 2023
First: I love the title just as much now as when I first saw Nina and Daveed use it :)
Can you elaborate on what the proposal is though? Cpp2 currently doesn't use
typename, and if I discover any places where it would be needed my first thought would be to fix the grammar so it's unambiguous.The first line should not be allowed now, and the second is unambiguously a variable.
isis interesting:Dependent types: That
x is T::typeshould work but doesn't yet, thanks for the bug report. It ought to emittypenamefor a dependent type... in this case it should generatecpp2::is<typename T::type>(x). But currently it emitscpp2::is<T::type>(x)which fails the Cpp1 compiler. I'll need to fix that.Dependent (static) values:
a) Currently
x is (T::value)works, and requires()which makes it unambiguous.b) Note that to test this you have to write the type in Cpp1, because I haven't yet provided a way to author static values in Cpp2. I probably will allow them, but require them to be
constinitorconstexpr.Here's a working test program:
JohelEGP commentedon Jul 1, 2023
For this to work:
All those
T::typeare type-only contexts,and should be emitted with
typenamein front (or omitted if it's already optional).I'm actually working on a solution.
It currently generates:
JohelEGP commentedon Jul 1, 2023
JohelEGP commentedon Jul 4, 2023
There is a grammar where it's needed: template-argument.
In
identity<std::pointer_traits<ptr>::rebind<ptr>>,std::pointer_traits<ptr>::rebind<ptr>parses as an expression,so
typenamewould be required to make it a type.See commit 8ae90d7.
JohelEGP commentedon Jul 5, 2023
Would you be solving the ambiguity in template-argument the same as with is-as-expression?
I.e., making type-id be the first production of template-argument.
I think that would make sense, and be consistent.
std::array<i32, 42>would continue working.and will probably continue to be even with increasing NTTP support.
If done, there might be one difference between template-argument and is-as-expression.
e is eande is (std::vector)fail at Cpp1 time ifeis an expression(the first one should be
e is (e), and the second onee is std::vector).t<e>would parseeas a type-id.But if it's lowered as-is, it might not fail at Cpp1 time if
eis actually an expression.That's because the Cpp1 syntax
t<x>is the same whetherxis a type or expression.So to get the same benefit of failing at Cpp1 time due to a kind-of-entity mismatch,
it'd be necessary to lower
t<e>ast<cpp2::t<e>>,where
cpp2::tis defined to ensureeis a type:That would also
resolve #502,
resolve #456, and
close #521.
hsutter commentedon Jul 5, 2023
Ack: You beat me to it -- I've been out of computer range but was going to reply saying I expected it would be the same as
x is typeandx is (value). The parens aren't needed for things like literals, of course.I hope to finally be back in reliable computer range again on Friday...
JohelEGP commentedon Jul 31, 2023
The same is true for an expression template argument that is lowered without the parentheses.
0 is (0)lowers tocpp2::is(0, (0)).So if
t<(i32)>lowers tot<(cpp2::i32)>, that's a Cpp1-time error 馃憤.If it lowered to
t<i32>, that'd be a semantic change from the parsed Cpp2 (from Cpp2 expression to Cpp1 type) 馃憥.However, that means that changing template-argument to consider type-id before expression
breaks current uses of
decltypetemplate arguments, which happens to work today.That's because
t<decltype(x)>parsesdecltype(x)as an expression, lowers as-is, but is a Cpp1 type.So if
decltypeisn't made a Cpp2 type-id before the change above, we lose the ability to usedecltypein Cpp2.JohelEGP commentedon Aug 27, 2023
Note that although #533 marks this PR as to-be-fixed by merging it, it doesn't implement this idea.
So #533 doesn't provide a way to pass type template arguments.