-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Require TAITs to appear in the signature of items that register a hidden type #107073
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @Nilstrieb (or someone else) soon. Please see the contribution instructions for more information. |
5e391a5
to
1a8d017
Compare
Some changes occurred in src/tools/clippy cc @rust-lang/clippy |
This comment has been minimized.
This comment has been minimized.
1a8d017
to
b232bb8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So my main question is
What exactly counts as a "mention" of a TAIT?
I think there are 3 ways to "mention" a type:
- Directly —
Tait: Bound
,...: Bound<Tait>
,arg: Tait
,-> Tait
- Syntactically — as an unused generic of an alias
- Semantically — as a field of another type
Examples:
// "Direct"
fn f() -> Tait { 17 }
// "Syntactic"
type DropType<T> = ();
fn g() -> DropType<Tait> { let _: Tait = 42; }
// "Semantic"
struct Wrap(Tait);
fn h() -> Wrap { Wrap(f()) }
Which of these are considered as mention of the tait with the current implementation and which of these do we want to consider as such (cc @Veykril)?
All of these are considered as mentions of the TAIT |
After some thought: this is probably the only reasonable way to define mentiondness for this use-case. Disallowing "direct" and "syntactic" doesn't help anyone I don't think and disallowing "semantic" cuts into real use-cases, so probably a bad idea too. |
One thing I just realized, what about the following case: type Foo = impl Send;
struct Struct<
const C: usize = {
let _: Foo = ();
0
}
>; Are we considering const parameter defaults as part of the signature? (I would hope not, since this is another body we would otherwise have to look into). Hence raising this just to make sure that this isn't being missed. (It does fail to compile on current nightly) |
This PR only rejects more defining use sites. The only cases that didn't compile before and do now are
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to see more tests (which for example explicitly tests the cases we've discussed and the new restrictions), but generally this looks good.
The part that we can't change the restrictions either way is a bit scary, but I guess there is no way around it. Also, we could probably make the change over an edition (and basically have enum TaitDefScopesStyle { V1, V2 }
), given that the changes to def scopes are crate (and even module) local.
r=me when the tests are ready
During testing I realized I missed fixing the main cycle error that I wanted to fix (others were fixed). Have another look? @rustbot review |
I think we should do a crater run of this to assess the impact (even though I think we probably land it either way). |
This comment has been minimized.
This comment has been minimized.
To check how this affects unstable users? |
What about the following use case? I assume it will stop working now without a helper function? #![feature(type_alias_impl_trait)]
type MyIter = impl Iterator<Item = i32>;
struct Foo {
iter: MyIter,
}
impl Foo {
fn set_iter(&mut self) {
self.iter = [1, 2, 3].into_iter();
}
} |
Somewhat, more so to find out if there are any use cases that we're missing that get used in the wild.
The problem is that somewhere, you will need to have constructed a That being said, would have to check, but this might count as a defining use function, since |
@veber-alex it should continue to work (it's a good candidate for a test tho), since the signature does mention
You could change it to |
re: crater, do we have any way to filter targets to only include the ones which include |
@bors try |
⌛ Trying commit 28c5fe7 with merge db398c5410e92f4a42a260668961bb25e5d2b858... |
☀️ Try build successful - checks-actions |
@craterbot check |
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🚧 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🎉 Experiment
|
There was one legit failure: |
fn baz() -> Foo { | ||
let f: Foo = 22_u32; | ||
//~^ ERROR concrete type differs | ||
f | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why baz
is here in the first place? The cycle is reproducible without it: [play].
Also the comments are now outdated! :3
type Foo = impl Debug; | ||
is_yay::<Foo>(); //~ ERROR: the trait bound `Foo: Yay` is not satisfied |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's weird that this type doesn't define Foo
anywhere, was it meant to do
type Foo = impl Debug;
let _: Foo = 0u32;
In order to check that even if Foo
's internal type implements a trait, Foo
itself doesn't?
@@ -1,6 +1,7 @@ | |||
#![feature(type_alias_impl_trait)] | |||
|
|||
type Foo = impl std::fmt::Debug; | |||
//~^ ERROR: unconstrained opaque type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How was it constrained before? O_o
// Check that we cannot syntactically mention a TAIT without | ||
// it also being part of the type. | ||
|
||
type DropType<T> = (); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH this doesn't really make sense as a test, since type DropType<T> = ();
does not compile.
Other interesting-ish types to check would be projections I think (<Foo as Id>::Out
, <() as Map<Foo>>::Out
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to keep it as a sanity check
you wanted waffle to review it in the first place and assigned me on accident, so |
Does this PR forbid register hidden type for a TAIT via a nested function return type while the TAIT is not mentioned in the root function signature? #![feature(type_alias_impl_trait)]
type Alias = impl Default;
pub fn foo() {
#[derive(Default)] struct Foo;
fn _f() -> Alias { Foo }
}
pub fn bar() -> Alias {
Alias::default()
} |
no, that will compile |
That makes this PR kinda moot in regards to lazy parsing, since you still have to look into all bodies to check for defining uses due to local items. |
I assumed that nested items were problematic like that anyway out of other reasons. |
They are, I guess that part for TAITs should be addressed by rust-lang/rfcs#3373 then, right. |
@oli-obk while that's true, I think it's sensible to not introduce new problems, especially given that we might resolve the old ones. I'd like that snippet to not compile, at least for the initial stabilization. |
This PR changes which items can register hidden types for TAITs. The logic enables individual sites and rejects all other sites by default, so we can't accidentally allow something, even if we can accidentally forbid something. It is not backwards compatible to add or remove sites later, as adding more sites may cause additional cycle errors where there were none before, so we need to get all the sites right before stabilization.
At present we allow
fn foo() { type Foo = impl Send; let _: Foo = (); }
Note that for associated items this may require you to repeat where bounds found on the associated item's container, as we are not looking into parents' bounds or signatures.
Discussion: https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/TAITs.20and.20lazy.20parsing
Tracking issue: #63063
Corresponding stabilization concern: #63063 (comment)