FWIW, you guys know the internals of the compiler, I do not. As such, I've already been convinced.
If you can come up with a sane grammar that allows both the existing T: (lifetime bound|trait_bound)+* and N: type, and have a rule to tell between them that's based only on path resolution results
This seems fun though, and helps me understand the compiler internals better, so I'll give it a try.
Disclaimer, I am not a type theorist, and I don't understand the internals of the compiler, so forgive me if I use the wrong terms, I'm just going off intuition.
In the grammar why not just treat them all as Kinds? T: (Kind)+*
; Where Kind: (lifetime|trait_bound|type)
Then some time after (or during) type resolution, but before monomorphization, we check for these generic parameter's "downkinds".
Where "downkind" is defined as:
trait_bound "downkinds to" const_immutable_type;
/// Where the type impls the trait bound.
/// By definition all types are const and immutable, but I'm calling it out explicitly.
type "downkinds to" const_immutable_value;
/// Where the value is of type.
// This gets weird I don't fully know how to describe it
lifetime(eg. 'a) "downkinds to" const_immutable_lifetime(eg. 'b);
/// Where 'b is >= 'a.
We could additionally, consider both Values and in the future HKTs as Kinds.
value "downkinds to" nothing.
/// As such any `struct Foo<'a, T: 1>` would throw a compile time error
HKT "downkinds to" (const_immutable_type|const_immutable_htk)
This Kind + "downkinding" rule is how my brain parses and groks how to use generic function definitions. Please let me know where my intuition is flawed. Thanks for putting up with me