-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
🔬 Tracking issue for RFC 2089: Extended Implied bounds #44491
Comments
I have a question not covered in the RFC (that not necessarily has to be part of the RFC): what will error reporting on bounds violations look like? Will it point to the original definition of the bound? |
I'm not part of this, but I'll summarize what appears to be the current state of this feature from what I can find: This tracking issue was posted shortly before the 2017 impl period began, and it looks like it fell under the scope of the WG-compiler-traits workgroup. On the workgroup's dropbox doc I see (under Query model):
I searched the issue tracker for recent issues/PRs about bound cycles or the query model, but nothing stood out. |
Is this ready for being implemented? And if so, could I get some mentoring instructions? |
Sorry, it seems like this issue has been remaining silent for a long time!
We would definitely appreciate contributions for this second point, which may eventually enable really cool features in rustc like implied bounds, generic associated types and more generally better type inference. See you on the Zulip stream! |
I don't know if this is feasible, but if it is, I think it'd be a nice restriction, so wanted to record it for future consideration: I think this feature is great for direct uses, but weird for "sideways" uses. To try a concrete example: // in a world where `struct HashSet<T: Hash+Eq>`...
fn foo<T>(s: &mut HashSet<T>, a: T, b: T) -> bool {
let r = a == b; // not allowed, because _this_ method doesn't know T: PartialEq
s.insert(a); // allowed, because the method's `Self` type is HashSet, where we always know Hash+eq
r
} I think that would keep the core "look, I'm calling a hashset method, so obviously it's Hash+Eq; I shouldn't need to say so again" but still wouldn't let you directly call things on traits that you never mentioned. (I do, however, think that in that world, |
Implement the new-style trait solver Final PR of what I believe to be a minimally working implementation of the new-style trait solver. The new trait solver can be used by providing the `-Z chalk` command line flag. It is currently used everywhere in `rustc_typeck`, and for everything relying on `rustc::infer::canonical::query_response::enter_canonical_trait_query`. The trait solver is invoked in rustc by using the `evaluate_goal` canonical query. This is not optimal because each call to `evaluate_goal` creates a new `chalk_engine::Forest`, hence rustc cannot use answers to intermediate goals produced by the root goal. We'll need to change that but I guess that's ok for now. Some next steps, I think, are: * handle region constraints: region constraints are computed but are completely ignored for now, I think we may need additional support from `chalk_engine` (as a side effect, types or trait references with outlive requirements cannot be proved well-formed) * deactivate eager normalization in the presence of `-Z chalk` in order to leverage the lazy normalization strategy of the new-style trait solver * add the remaining built-in impls (only `Sized` is supported currently) * transition the compiler to using generic goals instead of predicates that still refer to named type parameters etc I added a few very simple tests to check that the new solver has the right behavior, they won't be needed anymore once it is mature enough. Additionally it shows off that we get [implied bounds](#44491) for free. r? @nikomatsakis
Is anyone working on implementing this RFC yet? @scalexm, maybe? |
Chalk already handles implied bounds. I don’t know of any plan for implied bounds in Rust outside of pushing on chalk integration. |
@scalexm Oh right. So once Chalk is fully integrated, they'll just "start working" eh? In that case, I'd love to help with Chalk and speed things along... though I feel I'm a bit overstretched and underqualified as it is. |
Note that they "work" if you enable But since you cannot write any useful code with |
This is necessary since there is no particular lock implementation that "makes sense" on `no_std`. This patch changes all types to be generic over any lock type that implements `lock_api::RawMutex`, and defaults to `parking_lot::RawMutex` on `std`. Note that this patch forks the struct definition for `HashMap` into one for `std` (which has a default for `L`) and one for `no_std` (which does not). This is fine since any code written _without_ the `std` feature _will_ compile with the `std` feature enabled (`indexmap` does the same; the reverse is not true). We _could_ introduce an intermediate private struct to avoid repeating the definition, but it would require writing `self.inner` all over the place, which would be sad. This seemed marginally cleaner. Also, this diff makes me want implied bounds a lot: rust-lang/rust#44491
More updates to come :) |
I'd like to copy a possible concern I raised on URLO. The concern is not with implied bounds directly, but with the practice of putting bounds on the type (which this feature will undoubtedly encourage). It is already commonly recognized that placing bounds on a However. What I don't see people talking about is the fact that bounds on the type are enforced even if the object is never constructed. Most notably this affects enums, and it means that the workaround of "putting it in an Option" wouldn't actually work when the bounds are on the type! This can have strong implications on downstream code: // in crate `upstream`
pub struct Set<K: Hash + Eq>(HashSet<K>);
// in crate `downstream`
enum MaybeSet<K> {
Single(K),
Set(upstream::Set<K>), // Error: K requires Hash + Eq
}
Personally, I'm still excited to see progress on this feature, and don't believe that the above problem is necessarily a dealbreaker (especially as there are still types like |
It does seem weird that bounds on a struct can produce a type even more restrictive than struct N<K: Hash>(PhantomData<K>);
type ActuallyNever = N<f32>;
fn main() {
let foo: Option<!> = None; // Ok
let foo: Option<ActuallyNever> = None; // ERROR!
} |
To address unnecessary bounds: what if there was a Clippy |
Being able to add bounds in more places (e.g., enum variants) would be nice here. Orthogonal, but plausible. |
This makes me wonder about a potentially-much-simpler version of this: could we say that a function doesn't need to "prove" the bounds needed for types it mentions in its signature? So, for example, I could write this: fn is_borrowed<T>(x: Cow<T>) -> bool {
if let Cow::Borrowed(_) = x { true }
else { false }
} And that would be fine, because I'm not doing anything that needs the bound. What would need the bound? Any constructor (struct literals, variant functions, etc), and any call to a function (like Do you think that would be sound? It feels like it ought to be -- though I'm nowhere near sophisticated enough to prove it -- as the cases that don't meet the bounds would be rather like the |
For those looking for an alternative that works today using macros, try impl-tools: impl_scope! {
struct Foo<T: Debug> { .. }
// expands to impl<T: Debug> Self { .. }
impl Self { .. }
// where bounds supported too:
impl Self where T: PartialEq { .. }
} Obviously this has the same drawback as mentioned above: stronger bounds on the type than are required just to construct the type. I haven't found this an issue in my own usage, but appreciate that it could be in some cases. |
@Andlon Have you or has someone else here been able to find some kind of workaround/hack for this problem you described, while this issue is still open?
I came across this problem and was pulling my hair out, until I found out that this is a "shortcoming" of the Rust compiler. I agree that it is absolutely not feasible to repeat those redundant trait bounds over and over everywhere the trait itself is used. Even the following MRE (playground) is enough to stump the compiler: use std::ops::Add;
trait RefAddable: Sized
where
for<'a> &'a Self: Add<Output = Self>,
{}
fn do_stuff<T: RefAddable>(x: &T) {} Causes the following error: fn do_stuff<T: RefAddable>(x: &T) {}
^^^^^^^^^^ no implementation for `&'a T + &'a T` Telling me I need to add the bound again: fn do_stuff<T: RefAddable>(x: &T) where for<'a> &'a T: Add {}
++++++++++++++++++++++++ Can this be worked around with a proc macro somehow? PSI just found out about trait aliases. So at least in nightly it seems you could work around this with a trait alias: #![feature(trait_alias)]
use std::ops::Add;
trait RefAddable = where for<'a> &'a Self: Sized + Add<Output = Self>;
fn do_stuff<T: RefAddable>(x: &T, y: &T) -> T {
x + y
} (playground with test) Seems good enough at first glance. But I am still curious, if someone found another workaround. |
Does anyone have a sense of what is blocking this at this point? Surely would be nice to have... |
Rust 1.79 has just stabilized implied bounds ability on Supertraits! (Full ability I mean, including Associated Type bounds) |
It would make sense to have a bound like `for<'frame> R::Frame<'frame>: AsGlowFrame<'frame>`. But that appears to not behave properly due to current limitations of the borrow checker: https://blog.rust-lang.org/2022/10/28/gats-stabilization.html#implied-static-requirement-from-higher-ranked-trait-bounds Instead, this makes `glow_frame` and `glow_frame_mut` associated functions of the `AsGlowRenderer` trait. Then it is pretty straightforward to make the `RenderElement` implementations generic using that and `FromGlesError`. It would make sense to make `Self::Error: FromGlessError` a requirement of the `AsGlowRenderer` trait, but due to the lack of implied bounds support, that produces a bunch of errors about missing bounds. If Rustc improves that eventually, some bounds could be cleaned up a bit: rust-lang/rust#44491
It would make sense to have a bound like `for<'frame> R::Frame<'frame>: AsGlowFrame<'frame>`. But that appears to not behave properly due to current limitations of the borrow checker: https://blog.rust-lang.org/2022/10/28/gats-stabilization.html#implied-static-requirement-from-higher-ranked-trait-bounds Instead, this makes `glow_frame` and `glow_frame_mut` associated functions of the `AsGlowRenderer` trait. Then it is pretty straightforward to make the `RenderElement` implementations generic using that and `FromGlesError`. It would make sense to make `Self::Error: FromGlessError` a requirement of the `AsGlowRenderer` trait, but due to the lack of implied bounds support, that produces a bunch of errors about missing bounds. If Rustc improves that eventually, some bounds could be cleaned up a bit: rust-lang/rust#44491
This is a tracking issue for the RFC "Implied bounds" (rust-lang/rfcs#2089).
Steps:
Unresolved questions:
The text was updated successfully, but these errors were encountered: