Open chrysn opened 2 years ago
Thanks for reporting this, I was indeed unaware of this issue.
A minimal reproduction of the problematic code emitted by nougat is, I believe:
macro_rules! Where {(
$T:ty : $lt:lifetime $(,)?
) => (
[&$lt $T; 0]
)}
trait Trait
:
for<'any> TraitItem<'any> +
for<'any> TraitIterator<'any> +
{}
trait TraitItem<'a, Bound = Where!(Self : 'a)> {
type T;
}
trait TraitIterator<'b, Bound = Where!(Self : 'b)> {
type T : Iterator<Item = <Self as TraitItem<'b>>::T>;
}
Now, generating something like:
trait Trait
:
for<'any> TraitItem<'any> +
for<'any> TraitIterator<'any> +
{}
trait TraitItem<'a, Bound = Where!(Self : 'a)>
:
- 'a + for<'any> TraitIterator<'any> +
+ // if it were added, we'd have a "cyclic requirements" error
{
type T;
}
trait TraitIterator<'b, Bound = Where!(Self : 'b)>
:
+ 'b + for<'any> TraitItem<'any>
{
type T : Iterator<Item = <Self as TraitItem<'b>>::T>;
}
seems to fix the issue, but now we have another problem: the macro cannot really distinguish between the nature of the Item
and Iterator
associated types (at least not without involving some probably unaccurate heuristics regarding the complexity of the assoc item bounds), and yet the only expansion that works is by introducing some form of hierarchy between them: the Iterator
assoc type comes after the Item
one.
So in order to support this, we'd need some kind of annotation on the original trait definition to nudge nougat
in the right direction:
#[gat]
pub trait Trait {
type Item<'a> where Self : 'a;
+ #[gat(needs(Item))]
type Iterator<'b> : Iterator<Item=Self::Item<'b>> where Self : 'b;
}
Otherwise we could risk a heuristic looking for Self::Assoc<'lt>
kind of types, verbatim, in the bounds of other assoc types 🤷
Thoughts?
I don't follow the details, but the annotations would be easily manageable.
My first gut feeling was that there might arise the need to indicate how deeply chained the GATs are in the trait. These explicit annotations are simpler to provide than that full information.
Speaking of nesting: Would the last item in this extended example be annotated like that?
#[gat]
pub trait Trait {
type Item<'a> where Self : 'a;
#[gat(needs(Item))]
type Iterator<'b> : Iterator<Item=Self::Item<'b>> where Self : 'b;
#[gat(needs(Iterator, Item)]
type DeepIterator<'c>: Iterator<Item=Self::Iterator<'c>> + Foo<Bar=Self::Item> where Self: 'c;
}
(Disclaimer: pure curiosity, my types don't go that deep AFAIR; probably a "this doesn't nest deeper than ..." would even work for me.)
I think the "chaining" would be automatically figured out, so I suspect that last theoretical example could just have needs(Item)
on Iterator
and needs(Iterator)
on DeepIterator
🙂
Evaluating whether nougat could help get coap-message rid of its nightly wart, I found what appears to be another limitation of nougat that might warrant being pointed out in the list (or maybe fixed if it's easy): GATs do not work when referring to each other in a their required traits' associated types.
This code works with
#![feature(generic_associated_types)]
, but not with#[nougat::gat]
:The relevant error message with macro-backtrace is: