-
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
Can't provide non-overlapping impls with *any* type parameters, if a blanket impl exists #30191
Comments
So I've spent a bit of time poking at this, and ultimately the type parameter is being treated as ambiguously fulfilling the obligation here: https://github.com/rust-lang/rust/blob/e9ac440/src/librustc/middle/traits/select.rs#L560-L590 While this example gets more interesting when you consider that the same code can be written as However, this seems like it's actually easy to disambiguate. An impl can never overlap with itself. We can safely ignore that obligation. (I'm likely conflating the concepts of an obligation being fulfilled with traits overlapping. I'm not sure how separate those are meant to be in the compiler). Anyway, I'm fiddling with some changes to try and see if we can simply ignore the ambiguity when the candidate is where we started from, though I'd love to know if this sounds like it's on the right track. |
This is not, in fact, a bug. Any sibling dependency could define the following struct: #![feature(optin_builtin_traits)]
trait Marker {}
impl Marker for .. {}
impl !Marker for Bar {}
struct Bar;
impl<T: Marker> From<T> for Bar { fn from(_: T) -> Self { Bar } } In that case, both |
@arielb1 Yes, it appears the specific example I gave is unsound, I was trying to get something that I could throw on playground. The problem still exists in cases that cannot be mucked up by sibling crates. Here's a simplified version of the actual case that I'm running into this, which I'm fairly certain could not be made ambiguous by sibling crates. use std::error::Error
trait NativeSqlType;
struct Row;
pub trait FromSql<A: NativeSqlType>: Sized {
fn from_sql(bytes: Option<&[u8]>) -> Result<Self, Box<Error>>;
}
pub trait FromSqlRow<A: NativeSqlType>: Sized {
fn build_from_row(row: &mut Row) -> Result<Self, Box<Error>>;
}
impl<ST, T> FromSqlRow<ST> for T where
ST: NativeSqlType,
T: FromSql<ST>,
{
fn build_from_row(row: &mut Row) -> Result<Self, Box<Error>> {
// ...
}
} Now in a separate crate, I am unable to write this impl: struct User {
id: i32,
name: String,
}
impl<ST> FromSqlRow<ST> for User where
ST: NativeSqlType,
(i32, String): FromSqlRow<ST>
{
fn build_from_row(row: &mut Row) -> Result<Self, Box<Error>> {
let (id, name) = try!(<(i32, String) as FromSqlRow<ST>>::build_from_row(row));
Ok(User {
id: id,
name: name,
})
}
} The only way this can ever become ambiguous is for another crate to create a blanket impl for a trait it does not control (disallowed). |
Our trait checker uses an older, less-conservative version of the orphan rules. That might be it. |
For reference, the problem is the check at https://github.com/rust-lang/rust/blob/master/src/librustc/middle/traits/select.rs#L587. It was introduced before the |
tagging so this does not get lost. |
For the record, a full example that is broken: struct Row;
pub trait FromSql<A> {}
pub trait FromSqlRow<A> {
fn foo(&self);
}
impl<ST, T> FromSqlRow<ST> for T where
T: FromSql<ST>,
{
fn foo(&self) {}
}
struct User;
impl<ST> FromSqlRow<ST> for User where
(i32, String): FromSqlRow<ST>
{
fn foo(&self) {}
}
fn main() {} |
This is perhaps a dup of #19032 --- have to read more deeply |
I can't say for sure if this the same situation (and I don't want to polute useless issues), but the following code reports a "conflicting implementations of trait
|
With the orphan rules, anyone can implement |
So, I believe the orphan check is doing the right thing here. The danger is that a downstream crate might do something like:
which they are legally able to do, for better or worse. One solution here would be to adopt one of the negative impl proposals (such as what I describe in this gist]) that would permit |
Another solution that @aturon and I considered at some point but rejected is to implicitly derive the fact that the impls in the parent crate are only consistent if |
Also, I think this is definitely a dup of #19032. |
Closing as dup of #19032. |
@nikomatsakis I'm fine with closing as a dup, but based on your example shouldn't the blanket impl for |
I guess you mean an impl like the following? impl<T,U> Into<T> for U where T: From<U> This impl is allowed, but only in the crate that defines the trait struct Foo;
impl<T> Into<T> for Foo { .. } but they would also be required to prove this impl is disjoint from the impls in the crate where In other words, the crate that defines the trait has the ability to define blanket impls of that kind, since any other crate that might overlap would be able to detect the overlap easily enough. |
I think I misunderstood #30191 (comment). It assumes that there's a crate further downstream. Shouldn't we be able to omit that assumption for binary projects, or for types which are not public? |
We have not (currently) drawn any distinction between binary / library projects. I am reluctant to do so -- it seems to create refactoring hazards where you expose a binary as a library and so forth. But at the same time it seems clear that a number of coherence issues go away for binary projects -- so maybe there is a good way to handle these "at most once" decisions. Regarding privacy, that's another thing that coherence doesn't currently consider, but yes I could imagine taking it into account. |
👍 Thanks for clarifying |
This fails due to the blanket impl on
Into
forFrom
in libcore, even thoughFoo
does not implementFrom
.Possibly related to, but appears to be distinct from #20400 and #23341
The text was updated successfully, but these errors were encountered: