-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
non-local-definitions lint fires for impl using private types #125068
Comments
I think at the very least this lint should be downgraded from future-compat before this reaches stable, since we don't want people contorting their code to satisfy it. |
References to real-world code that triggered this: crate tor-persist , pattern introduced in 0.10.1 (2024-03-04). |
This is not a false-positive, the pub trait Trait {}
pub fn test() {
struct Local;
impl Trait for Option<Local> {} // with this impl this code compiles
}
fn do_stuff<U: Trait>() {}
fn main() {
do_stuff::<Option<_>>(); // but without the impl, it would error here
} Note that nowhere in Also note that the @rustbot labels -regression-from-stable-to-beta -I-prioritize -C-bug |
How exciting. I stand corrected. However, I nevertheless disagree with the categorisation as "not a bug", and with the categorisation as "not a regression". The compiler prints a new lint, but:
|
It is implied by the suggestion that not only the
It may be undesirable but it is nevertheless a solution. There is also another solution, moving the
Technical note here, the lint is not a future incompat lint, it's a regular lint, that may (although quite unlikely at this point) be moved to deny-by-default in Rust 2024. The goal of the lint, it to make it predictable to humans and machine where they need to search for So any |
I thought we were specifically excepting the cases where the items were only referenced inside the function. |
Well, I suppose an impl specifically for a |
This is an ambiguous case which the RFC does not address directly. We can still resolve this bug by specifying a disposition to these cases, but we cannot simply refer to the RFC's text to do so. It is empty of any such command. |
Indeed, one might naively assume it to be specifically permitted by this clause:
cc @joshtriplett Did you have an intention in mind regarding cases like this one? |
I don't think so, the
It then explain in the "Explanation" section how it should be determined if the I therefore disagree that this is (for now) a rule bug, T-lang may want to further refine the lint, but at least for now the lint (as in where it fires) is working as the RFC describes it. |
It is not, in my opinion, clear at all. It is thick as mud. Therefore, I disagree. It is a reasonable expectation that something might actually explain itself regarding such a case of composited (non-)locality before deciding to lint on it, but it does not. If the lint cannot explain itself to @ijackson why it applies, it is a bug. |
I agree, the lint should better explain it's reasons. T-compiler touch this in the latest T-compiler meeting. |
I think it needs to explain not just why it applies, but also suggest a reasonable alternative to the code it is impugning. (Assuming the code it is impugning is itself reasonable, which it is in this cae.) I think this is a fundamental point. It's all very well to say "the lint is working as expected". But a lint ought to trigger only for code which is bad in some way. In this case, the existing code isn't bad[1], and there isn't a good alternative. That makes linting it inappropriate. ([1] at least, no-one seems to be arguing here that the code is bad. At worst (reading the motivation for the RFC) it may be slightly awkward for certain IDEs. It's fine for the human reader. An argument that the code is bad ought to be accompanied by a suggestion for better code. It seems clear to me that "hoist the private types out of the fn" is worse.) |
As Urgau mentioned, the weird effect that was mentioned is a gotcha that might affect people's code negatively if they were relying on visibility to e.g. control a type's constructibility while still offering a public API based on a given trait: pub trait Trait {}
pub fn test() {
struct Local;
impl Trait for Option<Local> {}
}
fn do_stuff<U: Trait>() {}
fn main() {
do_stuff::<Option<_>>();
} In that case, we should:
|
We may also want to link them to an issue for collecting more feedback from people, just so everyone shows up and goes "huhwhatwherewhy?" in the same spot. |
Edited the tracking issue to link to this. |
Also, just to remove all doubt as to whether this is a Real Problem, I present a new example of a completed escape: pub trait Trait {}
pub fn test() {
// Private field to make it more annoying to construct!
#[derive(Default)] // oh, but what's this...?
struct Local(i32);
impl Trait for Option<Local> {}
}
fn do_stuff<U: Trait + Default>() -> U {
Default::default()
}
fn main() {
let x = do_stuff::<Option<_>>().unwrap_or_default();
println!("an instance of {} escaped", std::any::type_name_of_val(&x));
} I think that code would be something someone would probably write, especially if we're talking about a fairly big private function, not just the 4 lines in this example but more like 400 lines. They happen. But if someone was relying on that type not being constructible outside that fn, they are now dead wrong! Note that it doesn't work with just It's still not clear to me whether we should trigger the lint on the initial example here, as this requires two steps... a way to make the type constructible... but the answer to "is there something unexpected", i.e. is this making the code (and moreover, the code's implications) harder to understand... is probably Very Yes. As for "bad" or "good", well, I don't like the thought of calling this code "bad" because it doesn't seem inherently objectionable. You may simply not care that much. But I don't think it's a great shining example of code either. It's just... there. |
@ijackson We are trying to improve the diagnostic output in #125089. I've posted in #125089 (comment) the potential new diagnostic output for your case. If your have time, we would appreciate your opinion on it. |
…=estebank Improve diagnostic output the `non_local_definitions` lint This PR improves (or at least tries to improve) the diagnostic output the `non_local_definitions` lint, by simplifying the wording, by adding a "sort of" explanation of bounds interaction that leak the impl... This PR is best reviewed commit by commit and is voluntarily made a bit vague as to have a starting point to improve on. Related to https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/non_local_defs.20wording.20improvements Fixes rust-lang#125068 Fixes rust-lang#124396 cc `@workingjubilee` r? `@estebank`
…=estebank Improve diagnostic output the `non_local_definitions` lint This PR improves (or at least tries to improve) the diagnostic output the `non_local_definitions` lint, by simplifying the wording, by adding a "sort of" explanation of bounds interaction that leak the impl... This PR is best reviewed commit by commit and is voluntarily made a bit vague as to have a starting point to improve on. Related to https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/non_local_defs.20wording.20improvements Fixes rust-lang#125068 Fixes rust-lang#124396 cc ``@workingjubilee`` r? ``@estebank``
Rollup merge of rust-lang#125089 - Urgau:non_local_def-suggestions, r=estebank Improve diagnostic output the `non_local_definitions` lint This PR improves (or at least tries to improve) the diagnostic output the `non_local_definitions` lint, by simplifying the wording, by adding a "sort of" explanation of bounds interaction that leak the impl... This PR is best reviewed commit by commit and is voluntarily made a bit vague as to have a starting point to improve on. Related to https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/non_local_defs.20wording.20improvements Fixes rust-lang#125068 Fixes rust-lang#124396 cc ```@workingjubilee``` r? ```@estebank```
To reproduce
https://play.rust-lang.org/?version=beta&mode=debug&edition=2021&gist=6c4a42e00403c3a76de54d5010d7df08
Compile with Beta ("Build using the Beta version: 1.79.0-beta.4 (2024-05-10 a269819)")
Expected results
I expected this to compile without warning.
Actual results
Analysis
This is the new lint from RFC3373 (tracking issue).
In this code, the trait implementation cannot be moved out of the function, because it relies on types that are local to the function. The impl is coherence-legal only because the trait is within the same crate (so, this function couldn't be written outside
Trait
's crate), since by coherence rules it's an impl onOption
. But the impl is reasonable where it is because its effects are invisible outside the function - no-one outside this function can seeLocal
, so they can't seeOption<Local>
either - so they can't be affected by the impl.In principle, the types could be moved too. But this will usually be undesirable. The compiler definitely ought not to be suggesting the programmer needlessly expose private items, just to satisfy this lint. (Also moving the types might involve renaming them.)
ISTM that this demonstrates that the rules for this lint need to be considerably more complicated than they are.
@rustbot label +regression-from-stable-to-beta
The text was updated successfully, but these errors were encountered: