Skip to content
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

Reject bounds in type aliases with edition 2024 #49441

Open
RalfJung opened this issue Mar 28, 2018 · 41 comments
Open

Reject bounds in type aliases with edition 2024 #49441

RalfJung opened this issue Mar 28, 2018 · 41 comments
Labels
A-traits Area: Trait system A-typesystem Area: The type system C-enhancement Category: An issue proposing an enhancement or a PR with one. T-lang Relevant to the language team, which will review and decide on the PR/issue. WG-epoch Working group: Epoch (2018) management

Comments

@RalfJung
Copy link
Member

RalfJung commented Mar 28, 2018

I propose to make the type_alias_bounds lint deny-by-default with the next edition. See #21903 for more information on the issue being linted, and #48326 and #48909 for the PRs implementing the lint.

Cc @nikomatsakis @Manishearth (and Manish told me to also Cc someone else but I forgot whom)

@RalfJung
Copy link
Member Author

Also maybe in the light of this, the lint message should be extended to also link to #21903 or something else providing further information?

@Manishearth
Copy link
Member

cc @withoutboats

We can make it a hard error instead, fwiw.

@RalfJung
Copy link
Member Author

We can make it a hard error instead, fwiw.

I thought deny-by-default-lint was as hard as we wanted to make things in the next edition, but yeah -- there is no good reason to still allow this code, other than to ease porting to the next edition by allowing the lint.

Would that mean this is a topic for rustfix?

@Manishearth
Copy link
Member

Yes -- edition breakages work by taking a warn lint in a certain group and making it a hard error; and having a good suggestion that rustfix can apply

@frewsxcv frewsxcv added the WG-epoch Working group: Epoch (2018) management label Mar 28, 2018
@RalfJung
Copy link
Member Author

@nikomatsakis you mentioned some plans to fix where clauses in type aliases properly, so that they are added as obligations when type aliases are expanded. What is the status of that? I think this lint should become an error in the next edition, but whether it becomes Deny-by-default or a hard error I guess depends on whether we need this to be rule out for a transition in the 2021 edition.

@XAMPPRocky XAMPPRocky added C-enhancement Category: An issue proposing an enhancement or a PR with one. T-lang Relevant to the language team, which will review and decide on the PR/issue. A-typesystem Area: The type system A-traits Area: Trait system labels Jun 29, 2018
@RalfJung
Copy link
Member Author

After some talking with @Centril, I think ideally we want a lint that

  • complains about type SendVec<T: Send> = Vec<T> because the bound is ignored
  • complains about type Baz<I> = <I as Iterator>::Item because associated items should have a bound
  • does not complain about type Baz<I: Iterator> = <I as Iterator>::Item because the bound is, de facto, enforced after expansion.

What makes this hard is that associated items are not the only way that a bound can be used; using a type like struct Foo<T: Send>(T) is another way -- but @eddyb said he had ideas.

@eddyb
Copy link
Member

eddyb commented Aug 20, 2018

My idea is to diff what WF needs (i.e. the things that would error if not satisfied) with the predicates (bounds) written by the user.

Without Chalk this will be tricky, but we can manually run trait selection (or just plug something optional into the fulfillment context) to peek at how each WF requirement is satisfied, and mark every predicate that actually ends up used as part of that process, as useful - everything else, we'll warn (we can even have a consistency check, that if we remove the "unused bounds", WF still passes).

EDIT: Hang on, we might be able to do "difference" much easier by trying every declared bound with WF bounds as assumed: if they fail, that means the WF conditions enforced onto the user of the type alias aren't enough to subsume it, which is really what we want to check for (equivalence)!

@eddyb
Copy link
Member

eddyb commented Aug 25, 2018

We actually have a very good reason to check "WF of RHS implies bounds" and not "used":

type DEIItem<T: DoubleEndedIterator> = T::Item;

The DoubleEndedIterator bound won't be checked in uses, only WF(T::Item) i.e. T: Iterator will.
And whenever we can start checking uses, then we don't really care if the bounds are used.

@eddyb
Copy link
Member

eddyb commented Sep 7, 2018

I guess this is the same problem I recognized last year (#44075 (comment)), but now I have a plan.

@RalfJung
Copy link
Member Author

RalfJung commented Sep 9, 2018

@eddyb What is the deadline for this so that it could actually be an error in the next edition? Seeing #54057 makes me think it is already too late.

@Manishearth
Copy link
Member

It's been too late for ages, there's significant work required to make an edition breakage robust.

@eddyb
Copy link
Member

eddyb commented Sep 9, 2018

@Manishearth Ugh, really? I already have half of it implemented, the hard part is actually warning on Rust 2015, erroring on Rust 2018 is pretty easy.
What if I finish both halves that error on Rust 2018 today? I wish there were less things to do for Rust 2018, and we had noticed enough things in time...

@Manishearth
Copy link
Member

I mean, I guess you could make it work but you need docs and testing and some time for it to bake. And you need the teams to approve this.

The "hard part" about warning on 2015 is precisely the problem, edition migration lints are tricky.

Make it a non breaking backwards compatible lint and we can eventually break it (it's not that major a breakage) or include it in the edition.

@eddyb
Copy link
Member

eddyb commented Sep 9, 2018

@Manishearth What I'm working is doing the proper checks, which bring type aliases in line with other types, reverting the "force people to remove almost all bounds" approach (which I think was a complete mistake caused by miscommunication).
It looks like a bug fix, and the errors make sense the same way they do for anything but type aliases.

@Centril
Copy link
Contributor

Centril commented Sep 9, 2018

I for one really want to take the opportunity to fix this glaring and large hole in the language in Rust 2018.

We fixed the anon types in traits and made it a hard error in 2018 not too long ago, so I think we should be able to do the same with this. We have different people doing different things; so I can for example work on the documentation side of this in the edition guide.

I don't think it is too late to do this sort of thing; RC2 lands on 2018-10-25 for example - if it needs to be extended a bit (which I don't think it does), then so be it - having this hole in the language for several extra years cause we couldn't muster a few extra days to fix it seems bad to me.

@Mark-Simulacrum
Copy link
Member

Yes, I don't think it's necessarily too late for lints to be added. However, we don't (I believe) have a great story for "migration" post-migration today which could be a blocker there - I'd want to see this lint as rustfix-able on both 2015 and 2018 codebases for those who migrate early (as we're asking them to).

@Manishearth
Copy link
Member

Manishearth commented Sep 9, 2018 via email

@eddyb
Copy link
Member

eddyb commented Sep 9, 2018

I'd want to see this lint as rustfix-able on both 2015 and 2018 codebases for those who migrate early (as we're asking them to).

But does it really need to be? What I want to do is figure out a way to measure the breakage. I suspect it's not as bad in practice (I already implemented the second half - the "reverse WF" - and I'm working on getting it up as a PR).

For the record, up to this point I haven't been informed about any requirement of automatic migration wrt the edition, for anything that's not universally used.

@Manishearth
Copy link
Member

Manishearth commented Sep 9, 2018 via email

@eddyb
Copy link
Member

eddyb commented Sep 9, 2018

@Manishearth The problem is that while it breaks somewhat rare, it breaks hard.
It's not rarely used code that does the things being broken, but commonly used code, just rarely so.

serde, for example, has one or two errors on #54033, that are trivially fixable (the error tells you to add a trait bound) - but we can't really break the entire ecosystem like that.

@Centril
Copy link
Contributor

Centril commented Sep 9, 2018

I feel this is too big to break via future incompat lints... If we can figure out smooth migration here to do hard errors in 2018, I'd love that, and I feel we should try to do it. It feels like a priority to me because it is too important a part of the language to leave broken in 2018. Anyways; I have nominated this for the next lang team meeting to see where the consensus lies.

@Manishearth
Copy link
Member

Manishearth commented Sep 9, 2018 via email

@Centril
Copy link
Contributor

Centril commented Sep 9, 2018

Feel free to discuss it, but please loop in the actual implementors (eddyb?) during the decision, a lot of the past issues around edition migration was a lack of communication around implementation.

Hopefully eddyb is in that meeting =P

We've put plenty of important things on the back burner so that we can make this edition.

Important things involving breakage?

My view is that we should try to fix inconsistencies earlier rather than later because delaying it will just make the problem worse; in addition, delaying it also adds more technical debt -- I'd rather aim for having incrementally less and less breakage each edition because problems were fixed early, instead of spreading breakage out.

@nikomatsakis
Copy link
Contributor

Hmm. So let's talk through the alternatives. I see two dimensions:

  • Settling on the behavior that we ideally want
  • Figuring out when we will require this behavior and how to migrate code

Let me dig into those two a bit to try and summarize the status:

What behavior do we want? There seem to be a few questions here:

  • Can people specify "extra" bounds, like type IsDebug<T: Debug> = T?
  • Do people have to handle Sized correctly? e.g., type Foo<T: ?Sized> = Box<T>

My initial inclination is that, if we are going to fix this, the answer should be "yes" to both, but I suppose that the answers interact with the next question.

When are we going to fix this? There are a few options here:

Treat this as a bug fix and fix for all editions. This would be a future incompatibility lint. We have to settle on a period of time — as this is a long-standing bug, and one where we've gone back and forth, we should probably have a long transition time (6 months or a year). We could probably move to a deny-by-default lint sooner, in order to help push things along.

Treat this as an edition fix. This is what I had assumed we will do, but I'm beginning to rethink that. @eddyb's data suggests that the breakage is relatively limited, and that a migration period might let us patch up the crates in question. Also, the current behavior does seem pretty clearly like a bug, so I think calling it a bugfix is not wrong. Moreover, while I think it's possible to support both behaviors in perpetuity, I'm not dying to do it -- I'd like to move this kind of expansion into the trait system for various reasons (among them: letting us use type aliases in error messages) and it feels like it'd be nicer if we could transition to a single behavior eventually.

These two questions are inter-related, since if we pick stricter rules, that may imply more breakage. @eddyb, I don't really have a clear picture on just which variants you have tested thus far and how the quantities of breakage line up?

@nikomatsakis
Copy link
Contributor

Some complications with treating this as an edition fix:

  • As @Manishearth says, we are trying very hard to have automatic migration for anything other than "quite rare" cases
    • and if it is quite rare, maybe we should just fix...
  • It imposes a timing deadline that doesn't exist otherwise

@Manishearth
Copy link
Member

Manishearth commented Sep 11, 2018 via email

@nikomatsakis
Copy link
Contributor

One clarification on what I wrote:

I was getting ahead of myself when I wrote that we should support "extra" bounds.

I do think we should do that, but the current check that @eddyb implemented can't handle that -- the extra bounds would be ignored -- so for now we should not allow those.

@nikomatsakis
Copy link
Contributor

nikomatsakis commented Sep 13, 2018

We discussed in the @rust-lang/lang team meeting and decided we would:

  • Make it a FCP warning in 2015
  • Make it a hard error in 2018

where "it" refers to having "incorrect bounds" -- i.e., too many, or too few. We may in fact land the 2018 Hard Error first, since that is logistically easier, and 2018 code is unstable.

Ideally, we would do a migration for adding the ?Sized bound.

This FCW for 2015 may or may not ever become a hard error, it depends on how effective we are at removing older code and/or whether we find better alternatives. This is always true for future-compatibility warnings.

This should fit out general migration story:

  • We believe that this formulation affects very few crates, therefore it is acceptable (although not ideal) that we don't have a fully automatic migration
  • However, the formulation affects crates with many dependencies, so changing 2015 behavior must be done with care.

It's basically a mildly stronger variant of what @Manishearth was saying: we are doing the FCW, because we think the impact is small, but we are also tightening the noose a bit for 2018 code.

Seem reasonable?

@eddyb
Copy link
Member

eddyb commented Sep 13, 2018

I should also note that the "too many" bounds warning/error, we can eventually turn off, when we have implemented the "check bounds at use sites" mechanism (probably involving injecting type aliases into the type-system, as projections, after we get lazy normalization).

Ignoring Sized, very few crates are affected by the "too many bounds" check (from the crater data we have so far), and it's plausible/likely that the bounds actually hold at the use sites, so we would simply let the code compile.

Whereas the "not enough bounds" warning can stay forever a warning on Rust 2015, since it only affects definition-site checking, not interactions between the type aliases and the typesystem.


As for automatic migration, we can probably do "add (un)bound to type-parameter" much easier than anything else, especially in simple cases like ?Sized.
Automatically removing bounds would be especially difficult (but ideally a non-problem).

@eddyb
Copy link
Member

eddyb commented Sep 18, 2018

We have the crater report for "not enough bounds": #54033 (comment).
Aaaand it's 379 regressions. A random sample looks entirely legitimate to me.
Who'd've thought people use type aliases so much!

The lint from #48326 and #48909 didn't help (in that it was telling people to remove all bounds that aren't needed to resolve T::Assoc), but it couldn't have been responsible for all of those.

Based on this, I think we can keep it a warn-by-default lint in both editions, with no plans to make missing bounds an error. We don't have the infrastructure to do automatically applicable suggestions (i.e. scope-aware path printing) - maybe we will by 2021, but maybe we won't.


This does make our job easier, because "too many bounds" is the only future-compat hazard and it affects much fewer crates (and none of them are widely-used dependencies AFAICT).

bors bot added a commit to gfx-rs/gfx that referenced this issue Sep 18, 2018
2409: Don't #[allow(type_alias_bounds)]. r=kvark a=eddyb

rust-lang/rust#54090 will fix false positives (not relevant here) and make it an error in the upcoming Rust 2018 edition, which means that if you want to migrate to Rust 2018, it shouldn't be ignored.

See also rust-lang/rust#49441 (comment) for the decision and some background.

Co-authored-by: Eduard-Mihai Burtescu <edy.burt@gmail.com>
@nikomatsakis
Copy link
Contributor

This does make our job easier, because "too many bounds" is the only future-compat hazard and it affects much fewer crates (and none of them are widely-used dependencies AFAICT).

Say more about this? I guess you are thinking that "too few bounds" would break too many things, so we shouldn't warn about it?

(FWIW, I suspect that we could use an implied bound setup to accept the existing crates in a principled way, as well.)

@eddyb
Copy link
Member

eddyb commented Sep 18, 2018

Well, we could warn, but we can't ever make it an error, or at least not in the near future.
And, yes, I agree about implied bounds, but it's almost meaningless/pointless until we're switching to the lazy normalization projection implementation.
Then it'd be a way to check the projection before/without expanding to the aliased type, by getting the implied bounds.

@RalfJung
Copy link
Member Author

Related: #55222

@traviscross traviscross changed the title Reject bounds in type aliases with edition 2018 Reject bounds in type aliases with edition 2024 Nov 15, 2023
@jpost-collegenet
Copy link

Since I see this being considered for edition 2024, I'll note that this lint fires on lifetime bounds which, while not enforced, are also not inert. In summary, if you want the below alias to behave like &T behaves with regards to dyn lifetime defaults, you need the bound (and removing the bound can break code relying on it).

pub type Borrow<'a, T: 'a> = &'a T;
// With the bound, `Borrow<'a, dyn Trait>` is `&'a (dyn Trait + 'a)`
// Without the bound, it is `&'a (dyn Trait + 'static)`

I.e. this applies to alias definitions in addition to struct (etc) definitions.

@fmease
Copy link
Member

fmease commented Jan 10, 2024

Superseded by #112792.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-traits Area: Trait system A-typesystem Area: The type system C-enhancement Category: An issue proposing an enhancement or a PR with one. T-lang Relevant to the language team, which will review and decide on the PR/issue. WG-epoch Working group: Epoch (2018) management
Projects
Status: Needs help: Impl
Development

No branches or pull requests

10 participants