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

Require TAITs to appear in the signature of items that register a hidden type #107073

Closed
wants to merge 7 commits into from

Conversation

oli-obk
Copy link
Contributor

@oli-obk oli-obk commented Jan 19, 2023

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

  • constants and statics that mention the opaque type in their type
  • functions that mention the opaque type in their signature or where bounds
  • bodies within which the TAIT is declared (e.g. 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)

@rustbot
Copy link
Collaborator

rustbot commented Jan 19, 2023

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.

@rustbot rustbot added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jan 19, 2023
@rustbot
Copy link
Collaborator

rustbot commented Jan 19, 2023

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

@rust-log-analyzer

This comment has been minimized.

Copy link
Member

@WaffleLapkin WaffleLapkin left a 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:

  1. Directly — Tait: Bound, ...: Bound<Tait>, arg: Tait, -> Tait
  2. Syntactically ­— as an unused generic of an alias
  3. 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)?

@oli-obk
Copy link
Contributor Author

oli-obk commented Jan 19, 2023

Which of these are considered as mention of the tait with the current implementation

All of these are considered as mentions of the TAIT

@WaffleLapkin
Copy link
Member

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.

@Veykril
Copy link
Member

Veykril commented Jan 20, 2023

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)

@oli-obk
Copy link
Contributor Author

oli-obk commented Jan 20, 2023

This PR only rejects more defining use sites. The only cases that didn't compile before and do now are

  • cycle errors if the auto trait of a Tait is requested in a function without the Tait in the signature.
  • TAITs where one TAIT is the hidden type of another can work if only one of the TAITs is in the signature.

Copy link
Member

@WaffleLapkin WaffleLapkin left a 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

@oli-obk
Copy link
Contributor Author

oli-obk commented Jan 23, 2023

During testing I realized I missed fixing the main cycle error that I wanted to fix (others were fixed).

Have another look?

@rustbot review

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jan 23, 2023
@jackh726
Copy link
Member

I think we should do a crater run of this to assess the impact (even though I think we probably land it either way).

@rust-log-analyzer

This comment has been minimized.

@oli-obk
Copy link
Contributor Author

oli-obk commented Jan 23, 2023

I think we should do a crater run of this to assess the impact (even though I think we probably land it either way).

To check how this affects unstable users?

@veber-alex
Copy link
Contributor

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();
    }
}

@jackh726
Copy link
Member

To check how this affects unstable users?

Somewhat, more so to find out if there are any use cases that we're missing that get used in the wild.

What about the following use case? I assume it will stop working now without a helper function?

The problem is that somewhere, you will need to have constructed a Foo (the one getting passed as &mut self), and during that construction, you would have had to have returned a Foo.

That being said, would have to check, but this might count as a defining use function, since &mut self should desugar to self: &mut Self, which is self: &mut Foo, which is the "semantic mention" talked about above. Would be a good test...

@WaffleLapkin
Copy link
Member

WaffleLapkin commented Jan 24, 2023

@veber-alex it should continue to work (it's a good candidate for a test tho), since the signature does mention MyIter through self.

The problem is that somewhere, you will need to have constructed a Foo (the one getting passed as &mut self), and during that construction, you would have had to have returned a Foo.

You could change it to iter: Option<MyIter> and initialize it with None originally, as an example.

@WaffleLapkin
Copy link
Member

re: crater, do we have any way to filter targets to only include the ones which include #![feature(type_alias_impl_trait)], so we don't waste time on unrelated crates?

@oli-obk
Copy link
Contributor Author

oli-obk commented Jan 24, 2023

@bors try

@bors
Copy link
Contributor

bors commented Jan 24, 2023

⌛ Trying commit 28c5fe7 with merge db398c5410e92f4a42a260668961bb25e5d2b858...

@bors
Copy link
Contributor

bors commented Jan 24, 2023

☀️ Try build successful - checks-actions
Build commit: db398c5410e92f4a42a260668961bb25e5d2b858 (db398c5410e92f4a42a260668961bb25e5d2b858)

@oli-obk
Copy link
Contributor Author

oli-obk commented Jan 24, 2023

@craterbot check

@craterbot
Copy link
Collaborator

👌 Experiment pr-107073 created and queued.
🤖 Automatically detected try build db398c5410e92f4a42a260668961bb25e5d2b858
🔍 You can check out the queue and this experiment's details.

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-crater Status: Waiting on a crater run to be completed. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 24, 2023
@craterbot
Copy link
Collaborator

🚧 Experiment pr-107073 is now running

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot
Copy link
Collaborator

🎉 Experiment pr-107073 is completed!
📊 23 regressed and 18 fixed (253470 total)
📰 Open the full report.

⚠️ If you notice any spurious failure please add them to the blacklist!
ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-crater Status: Waiting on a crater run to be completed. labels Jan 25, 2023
@oli-obk
Copy link
Contributor Author

oli-obk commented Jan 25, 2023

There was one legit failure: bind_it generates TAITs with proc macros, and I don't see why it should stop working. Need to look at the desugarings.

Comment on lines +17 to 21
fn baz() -> Foo {
let f: Foo = 22_u32;
//~^ ERROR concrete type differs
f
}
Copy link
Member

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

Comment on lines +9 to 10
type Foo = impl Debug;
is_yay::<Foo>(); //~ ERROR: the trait bound `Foo: Yay` is not satisfied
Copy link
Member

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
Copy link
Member

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> = ();
Copy link
Member

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).

Copy link
Contributor Author

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

@WaffleLapkin WaffleLapkin added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 27, 2023
@Noratrieb
Copy link
Member

you wanted waffle to review it in the first place and assigned me on accident, so
r? WaffleLapkin

@vlad20012
Copy link
Member

vlad20012 commented Feb 4, 2023

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()
}

@oli-obk
Copy link
Contributor Author

oli-obk commented Feb 4, 2023

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?

no, that will compile

@Veykril
Copy link
Member

Veykril commented Feb 4, 2023

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.

@oli-obk
Copy link
Contributor Author

oli-obk commented Feb 4, 2023

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.

@Veykril
Copy link
Member

Veykril commented Feb 4, 2023

They are, I guess that part for TAITs should be addressed by rust-lang/rfcs#3373 then, right.

@WaffleLapkin
Copy link
Member

WaffleLapkin commented Feb 10, 2023

no, that will compile

I assumed that nested items were problematic like that anyway out of other reasons.

@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.

@oli-obk
Copy link
Contributor Author

oli-obk commented Mar 10, 2023

closing in favor of #107809 as per the lang team decision on #107645

@oli-obk oli-obk closed this Mar 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.