-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Free type aliases should not require all generic parameters to be used #140230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The current behavior works as intended but I guess we could allow it? |
For context, if I guess we could permit lazy type aliases with bivariant type parameters (what about lifetime parameters, then?). |
Obviously, |
Regarding soundness, permitting bivariant type/lifetime parameters on lazy type aliases might just work out (tm) because in "critical" contexts (checking if generic parameters are constrained) we actually Meaning we'd still reject pathological things like #![feature(lazy_type_alias)]
type WeakDiscard</*bi*/ T> = (); // let's assume that were legal
type Demo = for<'flexible> fn(WeakDiscard<&'flexible ()>) -> &'flexible ();
//~^ ERROR return type references lifetime `'flexible`, which is not constrained by the fn input types |
This feels very confusing to me. free type aliases are aliases not real types why should there be a similarity between free type aliases and adts instead of free type aliases and other forms of aliases? Where can I read about this lang consensus and what it means to "behave very similarly to ADTs". I'm also somewhat surprised this was even a lang decision, making free type aliases behave "properly" and defining what "properly" means seems very much like types territory to me as it is entirely subtle type system stuff.
How come we do actual variance computation for free type aliases? Is it just so that uses of free type aliases in type definitions doesn't regress stable code from now making parameters invariant? More generally, is it just incidental that we forbid this because of the way we happen to implement things to allow In regards to that pathological case I wouldn't expect it to matter that we normalize free aliases from a soundness POV: trait Trait<'a> {
type Assoc;
}
impl<'a> Trait<'a> for () {
type Assoc = ();
}
type Func<T> = for<'a> fn(<T as Trait<'a>>::Assoc) -> &'a u32; We error on this code because we conservatively assume that aliases do not constrain there inputs. The worst case here should be that we regress stable code by being too conservative around free type aliases, rather than accidentally allowing too much. Similarly, if we don't normalize free type aliases here, I would expect us to treat them like any other alias and conservatively error because of In general this is why it feels wrong to me to think about free type aliases as "adt like". We already have "sound enough" system for aliases and shouldn't need to reinvent any wheels. If we treat free type aliases the same as other aliases then we know its sound and correct. Then all that's left is adding in some special cases to take advantage of the fact that they're "trivially normalizeable" to make some checks smarter (like variance computation, or determining if lifetimes are constrained). |
I admit I phrased this too hastily. I was referring to https://hackmd.io/LCUSAX5LSfqc4m7N4CWNPw#Type-aliases-today-leak-impl-details which talked about how LTAs should follow the behavior of ADTs wrt. implied outlives-bounds, not variances. So it's not accurate what I wrote. Obviously, conceptually free/weak alias types are more structural, less nominal, unlike ADTs. I guess in my head I like to compare LTAs (the def site) with ADTs (the def site) for superficial reasons (you can define an inherent impl "for them", same variance behavior, they're free item kinds, …). It doesn't make much sense, I agree. I should retire that comparison.
Yes, exactly. "Is it just so": Well, you have to remember that LTAs didn't spring into existence because of T-types (#21903) but because of T-rustdoc (rust-lang/compiler-team#504). The original idea was to introduce a Therefore any behavior differences to other alias kinds like
serve the single purpose of replicating the existing eager type alias expansion (i.e., Of course, the feature LTA in its current form is not suited for rustdoc's use case because of all the WF-checking we're now performing and because of limitations of the old solver and because of other small differences (e.g., unused lifetime params aren't allowed because we reject most bivariant params as you noticed) — at least not in the current editions (which is still far from ideal, tracked in Lazy Type Aliases (LTA) (view)). I remind you we both wrote privately about this in the past and you did suggest to me that we might be able to make WF-checking conditional on the edition which would be more than lovely. I'd love us to use |
You might wonder as to why we perform WF-checking for LTAs if their origin lies in that rustdoc/compiler MCP? Well, the PR that introduced Then *I* came along and introduced more and more WF-checks (#114228, #136432) and other incompatible changes (#114662) because I got carried away and saw LTAs as a way to finally fix #21903. So right now LTAs are in a bit of an identity crisis: |
Yes, I'm more than aware of the way we conservatively treat other alias kinds as non-constraining. And I know that it would be beneficial if we could reduce the number of differences between all Still, to me as a user of the language it would feel frustrating if free alias types would be treated as conservatively as projections (forcing its gen params to be invariant, unconditionally declaring its gen params unconstrained) given the fact that they can never be ridid, that they can always be reduced/normalized. Already, users open GH issues complaining about how restrained projections are handled (wrt. variance, constrainedness, no "arbitrary shorthand projections" (#22519), orphanck, …).
We can't normalize here without changing the existing behavior of normalizeable projections/alias types (and possibly introducing cycles), right? Therefore we |
But that's exactly how I implemented it and how I imagined it to be? In most cases, they are treated like every other |
Aren't these two statements of yours contradicting each other? Would you like us to treat |
cc @oli-obk for visibility (sry for the walls of text >.<) |
I tried this code:
This current fails but I believe it should compile. I Imagine the reason we currently forbid this on stable is that as we have no representation for free type aliases in
Ty
any unused arguments to free type aliases would never be seen. With free type aliases having a proper representation as part ofTy
under this feature gate it ought to be possible to soundly support this?Tested on: nightly rust 1.88.0 2025-04-07 e643f59
The text was updated successfully, but these errors were encountered: