-
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 #107809
Require TAITs to appear in the signature of items that register a hidden type #107809
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @cjgillot (or someone else) soon. Please see the contribution instructions for more information. |
cc @davidtwco, @compiler-errors, @JohnTitor, @TaKO8Ki Some changes occurred in src/tools/clippy cc @rust-lang/clippy |
Can you talk a bit about what's different between this and #107073? |
the difference is a bit subtle, but basically this PR avoids any risk of having to do backwards incompatible changes later, as it is forwards compatible with the other PR. So we can in the future decide to go with a more permissive implementation. The guarantees for IDEs are the same, they don't need to look at any items that don't have the TAIT in its signature. |
☔ The latest upstream changes (presumably #107652) made this pull request unmergeable. Please resolve the merge conflicts. |
@oli-obk can you put a precise writeup of the semantics for the lang team to review, either on this PR, or as a comment/description-edit on #107645 ? In particular, "we then error out if they did end up registering a hidden type but didn't have the TAIT in their signature" is open to interpretation about whether the where clauses of a function are part of its signature or not. (At least, I know Niko has sometimes considered the where-clauses to have a somewhat less significant role compared to e.g. the types of the formal parameters or return type.) |
I updated #107645 (which already explicitly mentioned |
7798e7c
to
8552b71
Compare
These commits modify the If this was intentional then you can ignore this comment. |
ec0331c
to
c83b23d
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.
Sorry for the (very) slow review @oli-obk. I don't have much to say, mostly a few questions about tests.
return true; | ||
} | ||
|
||
if tcx.is_closure(def_id.to_def_id()) { |
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.
Should this only apply for closures, or also for inline consts? IOW, should we check is_typeck_child
here?
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.
Is there a test for the ban on closure signatures? I saw tests/ui/type-alias-impl-trait/issue-63263-closure-return.rs is changed to accommodate it, but I missed the "fail" test.
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.
You're right on all accounts. There were no tests, and there were especially none for inline consts. I added them and fixed it so that inline consts actually work at all.
// In case we are inferring the return signature (via `_` types), ignore defining use | ||
// rules, as we'll error out anyway. This helps improve diagnostics, which otherwise | ||
// may just see `ty::Error` instead of `ty::Alias(Opaque, _)` and not produce better errors. | ||
if fn_sig_infer { DefiningAnchor::Bubble } else { DefiningAnchor::Bind(def_id) } |
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.
Which are the diagnostics changed by this?
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.
An example is
type Pointer<T> = impl std::ops::Deref<Target=T>;
fn test() -> Pointer<_> {
//~^ ERROR: the placeholder `_` is not allowed within types
Box::new(1)
}
which produces the following error:
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
--> $DIR/issue-77179.rs:7:22
|
LL | fn test() -> Pointer<_> {
| --------^-
| | |
| | not allowed in type signatures
| help: replace with the correct return type: `Pointer<i32>`
but without the change it would not see Pointer<i32>
but just TyKind::Error
and thus not report any suggestion, even if the main error would still be emitted
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.
isn't using Pointer<i32>
forbidden here anyways, it currently results in the following (bad) error: playground
error[E0792]: expected generic type parameter, found `i32`
--> src/lib.rs:5:5
|
2 | type Pointer<T> = impl std::ops::Deref<Target = T>;
| - this generic parameter must be used with a generic type parameter
...
5 | Box::new(1)
| ^^^^^^^^^^^
i personally feel like this is not worth it as it imo makes the code noticeably more confusing for a fairly small benefit.
9119da8
to
b465336
Compare
☔ The latest upstream changes (presumably #104833) made this pull request unmergeable. Please resolve the merge conflicts. |
…l feature gate for the other cases
…e that is supposed to be restricted
b465336
to
375dfa4
Compare
This comment has been minimized.
This comment has been minimized.
4c4d729
to
733e444
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.
by looking into adts you can probably get an infinite loop if you have the following type in the signature
struct Foo<T: 'static> {
field: &'static Foo<(T,)>,
other_field: T,
}
tcx: self.tcx, | ||
ct_op: |c| c, | ||
ty_op: |t| t, | ||
lt_op: |_| self.tcx.lifetimes.re_erased, |
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.
shouldn't erase bound regions here, only escaping bound regions 🤔
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.
you could implement fn fold_binder
to call https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.replace_bound_vars_uncached
, replacing bound vars with self.tcx.lifetime.re_erased
and eagerly replace all free regions that way
} | ||
// Types that have opaque type fields must get walked manually, they | ||
// would not be seen by the type visitor otherwise. | ||
ty::Adt(adt_def, substs) => { |
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 the type can be contained in an Adt
? please add that to the PR description.
also, this can be adt_def.all_fields
instead of the variants -> fields nested loop
// In case we are inferring the return signature (via `_` types), ignore defining use | ||
// rules, as we'll error out anyway. This helps improve diagnostics, which otherwise | ||
// may just see `ty::Error` instead of `ty::Alias(Opaque, _)` and not produce better errors. | ||
if fn_sig_infer { DefiningAnchor::Bubble } else { DefiningAnchor::Bind(def_id) } |
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.
isn't using Pointer<i32>
forbidden here anyways, it currently results in the following (bad) error: playground
error[E0792]: expected generic type parameter, found `i32`
--> src/lib.rs:5:5
|
2 | type Pointer<T> = impl std::ops::Deref<Target = T>;
| - this generic parameter must be used with a generic type parameter
...
5 | Box::new(1)
| ^^^^^^^^^^^
i personally feel like this is not worth it as it imo makes the code noticeably more confusing for a fairly small benefit.
Closing in favor of #110010 |
fixes #107645
We now error in case an opaque type gets constrained in a function/item that doesn't have the opaque type in its return type or argument types (Option 5 of #107645):
Any item in the defining scope can register hidden types, but we error out if the TAIT is not in the return type or argument types (post normalization, see below).
We still look at all functions in the defining scope, there's no change in cycle errors.
This is forward compatible to all other designs.
We normalize the argument types and return type before checking for opaque types in order to allow cases like
This means if you have a
-> <TAIT as Trait>::AssocType
that is not a constraining use and will error for now. We can later check both pre and post-normalization to allow that, too. The current design is entirely forward compatible with it.