Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upCan't provide non-overlapping impls with *any* type parameters, if a blanket impl exists #30191
Comments
This comment has been minimized.
This comment has been minimized.
|
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 comment has been minimized.
This comment has been minimized.
|
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
closed this
Dec 4, 2015
This comment has been minimized.
This comment has been minimized.
|
@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). |
This comment has been minimized.
This comment has been minimized.
|
Our trait checker uses an older, less-conservative version of the orphan rules. That might be it. |
arielb1
reopened this
Dec 4, 2015
This comment has been minimized.
This comment has been minimized.
arielb1
added
the
A-traits
label
Dec 4, 2015
This comment has been minimized.
This comment has been minimized.
|
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 |
arielb1
added
I-nominated
I-needs-decision
labels
Dec 4, 2015
This comment has been minimized.
This comment has been minimized.
|
tagging so this does not get lost. |
arielb1
removed
the
I-nominated
label
Dec 4, 2015
arielb1
self-assigned this
Dec 4, 2015
This comment has been minimized.
This comment has been minimized.
|
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() {} |
nikomatsakis
added
the
T-lang
label
Feb 2, 2016
This comment has been minimized.
This comment has been minimized.
|
This is perhaps a dup of #19032 --- have to read more deeply |
steveklabnik
referenced this issue
Mar 4, 2016
Open
Coherence and blanket impls interact in a suboptimal fashion with generic impls #19032
This comment has been minimized.
This comment has been minimized.
|
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
|
arielb1
added
the
I-nominated
label
Jul 21, 2016
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
With the orphan rules, anyone can implement |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
Also, I think this is definitely a dup of #19032. |
This comment has been minimized.
This comment has been minimized.
|
Closing as dup of #19032. |
nikomatsakis
closed this
Jul 26, 2016
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis I'm fine with closing as a dup, but based on your example shouldn't the blanket impl for |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
|
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? |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
|
|
sgrif commentedDec 3, 2015
This fails due to the blanket impl on
IntoforFromin libcore, even thoughFoodoes not implementFrom.Possibly related to, but appears to be distinct from #20400 and #23341