-
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
Adding or removing an item should not silently change behavior of downstream code #11878
Comments
As far as I know, fixing this requires removing the auto-reference and auto-dereference features used for |
Don't expect is quite different from shouldn't. And if we go down that path, we should at least do so with eyes open to the consequences, which are serious. (This is the same "feature" which earned |
@glaebhoerl perhaps you should start by adding a |
I don't think simply yanking out autoderef is the only possible, or best, solution (though if it were, I would favor it!). I can think of at least two others:
There might be other options: anyone else see one? I'd be happy to try implementing one or the other of these behind an experimental flag if it'd help move the ball forward. |
It's not really much better than using
A |
I think it's a lot more ergonomic, especially when chaining:
True. Again, we could follow C++ and have it resolve to the most-indirect method. Having pointers-to-pointers is not so common, and wanting to call a method on a pointer in the middle even less so, which can still be done explicitly (
This is more general than either EDIT: Another solution might be almost, but not quite, the inverse of the |
Before delving too deeply into potential solutions it might be wise to determine the scope of the problem. For instance, is this an issue with both auto_de_ref and autoref? (Once I have an answer I'll edit it in, but feel free to beat me to it.) Are there any other parts of the language which might have similar issues? I've updated the title to be more general. |
It's an issue for auto-dereferencing for sure. It currently doesn't find an implementation for |
Accepted for 1.0, P-backcompat-lang. (We need to discuss ways to deal with this, but I think an approach we could solve this would be to make "ambiguous" cases an error that need to be explicitly resolved, e.g. via a function-call syntax where you are explicit about the trait and impl.) |
UFCS: #11938 |
|
@glaebhoerl I agree that statics in patterns is another instance of this hypothetical property, but I would prefer that we not fork the comment thread to discuss the motivation for that here, since sharing that property is AFAICT the only way that the two topics are related. |
(Re statics: don't they have to be explicitly imported to be used in patterns? So the only way code can change behaviour accidentally is if is importing via globs. (Sorry for continuing the fork.)) |
That's true I think. Still, other items don't have this kind of effect (and rightly so). If someone adds a struct and you import it with a glob, your code won't end up doing different things than it did before. It also depends on how narrowly you define upstream/downstream. Even within the same module, having to worry about whether any code in that module has a pattern with the same name before you can safely add a @pnkfelix I was intending this ticket to be about the general property, so far |
@glaebhoerl oh you are correct, I misread the title of the bug as saying "adding or removing an impl should not ...", but you are correct, it says the more general "adding or removing an item should not ..." |
@pnkfelix Not totally surprising, that was the original title. :) I renamed it as I noted in an earlier comment. (We could have also separate issues for statics and impls and make a metabug or something, if you think that's better (though there's already some open issues for statics).) |
No, they do have this effect. use somemod::{Foo, bar};
use othermod::*;
fn main() {
bar(Foo { x: 1 })
} if Although, iirc, the unused import warning will pick this up, but I guess the first import could be use somemod::bar;
fn qux() { bar() }
fn main() {
use othermod::*;
qux();
bar(); // supposed to be somemod's bar
something_in_othermod();
} Rather than shadowing, we could have a warning for when an name is ambiguous (i.e. two imports of the same name). |
Interesting, thanks, this is good to know.
Yes, that's how I naively assumed it already worked. :) Local items should shadow imported ones, but either two local items, or two imported items with the same name (and namespace) should be ambiguous, and cause an error (not warning) to be raised at the point of attempted use. Your second example is tougher. You don't even need |
cc me (I am not sure that this is a bug) |
I propose closing this. Autoderef and autoref are pretty fundamental, and globs can cause hazards in many ways. Nominating for closing. |
Majority of team believes that support for autoderef and autoref is more fundamental/important than the hypothetical "Adding or removing an item should not silently change behavior of downstream code" philosophy espoused in the title, and thus the example in the description is not itself actionable. At least, not without finding some solution to the problem that does not involve removing autoref/autoderef. But this ticket, as described, is not actionable, since it does not propose such a solution. |
(closing as unactionable, see above comment.) |
…, r=Veykril internal: change generic parameter order tl;dr: This PR changes the `Substitution` for trait items and methods like so: ```rust trait Trait<TP, const CP: usize> { // note the implicit Self as first parameter type Type<TC, const CC: usize>; fn f<TC, const CC: usize>() {} } impl<TP, const CP: usize> S { fn f<TC, const CC: usize>() {} } ``` - before this PR: `[Self, TP, CP, TC, CC]` for each trait item, `[TP, CP, TC, CC]` for `S::f` - after this PR: `[TC, CC, Self, TP, CP]` for each trait item, `[TC, CC, TP, CP]` for `S::f` --- This PR "inverts" the generic parameters/arguments of an item and its parent. This is to fulfill [chalk's expectation](https://github.com/rust-lang/chalk/blob/d875af0ff196dd6430b5f5fd87a640fa5ab59d1e/chalk-solve/src/rust_ir.rs#L498-L502) on the order of generic arguments in `Substitution`s for generic associated types and it's one step forward for GATs support (hopefully). Although chalk doesn't put any constraint for other items, it feels more natural to get everything aligned than special casing GATs. One complication is that `TyBuilder` now demands its users to pass in parent's `Substitution` upon construction unless it's obvious that the the item has no parent (e.g. an ADT never has parent). All users *should* already know the parent of the item in question, and without this, it cannot be easily reasoned about whether we're pushing the argument for the item or for its parent. Some additional notes: - f8f5a5e: This isn't related to the change, but I felt it's nicer. - 78977cd: There's one major change here other than the generic param order: Default arguments are now bound by the same `Binder` as the item in question rather than a `Binder` limited to parameters they can refer to (i.e. arguments that syntactically appear before them). Now that the order of generic parameters is changed, it would be somewhat complicated to make such `Binder`s as before, and the "full" `Binder`s shouldn't be a problem because we already make sure that the default arguments don't refer to the generic arguments after them with `fallback_bound_vars()`. - 7556f74: This is split from 4385d3d to make it easy to revert if it turns out that the GATs with const generics panic is actually not resolved with this PR. cc rust-lang#11878 rust-lang#11957
…r=flip1995 uninhabited_reference: new lint Close rust-lang#11851 The lint is implemented on function parameters and return types, as this is the place where the risk of exchanging references to uninhabited types is the highest. Other constructs, such as in a local variable, would require the use of `unsafe` and will clearly be done on purpose. changelog: [`uninhabited_reference`]: new lint
If you add or remove a trait
impl
, or anonymousimpl
, or any other item in upstream code, this should have one of three effects on downstream code:What should not happen is
4. It continues to compile, but its runtime behavior is now different.
This property is currently known to be violated by method call autoderef combined with trait impls on reference types (#11818). For instance:
When compiled with
--cfg with_foo_impl
, this code will output "This is Foo". Otherwise it will say "Hi, I'm a borrowed reference!".Why this is important: it seriously impairs the ability to reason about interface stability and compatibility. In today's situation, upstreams cannot add or remove
impl
s for their types without having to worry about introducing silent breakage somewhere downstream. If this property held, then the worst case would be that downstream would be alerted to the change by a compile failure, which is much, much preferable to silent breakage. (This is exactly the kind of thing static type systems are supposed to ensure!)The provided example is only one instance I know of where the principle is violated, I don't know whether there might be others.
The text was updated successfully, but these errors were encountered: