Skip to content

Commit

Permalink
add tests to a few edge cases in method lookup
Browse files Browse the repository at this point in the history
These aren't fixed by this PR, but were broken in a few older attempts
at it. Make sure they don't regress.
  • Loading branch information
arielb1 committed Jan 28, 2019
1 parent 8d26c75 commit 0ceb30d
Show file tree
Hide file tree
Showing 3 changed files with 290 additions and 0 deletions.
@@ -0,0 +1,177 @@
#![feature(arbitrary_self_types, coerce_unsized, dispatch_from_dyn, unsize, unsized_locals)]

// This tests a few edge-cases around `arbitrary_self_types`. Most specifically,
// it checks that the `ObjectCandidate` you get from method matching can't
// match a trait with the same DefId as a supertrait but a bad type parameter.

use std::marker::PhantomData;

mod internal {
use std::ops::{CoerceUnsized, Deref, DispatchFromDyn};
use std::marker::{PhantomData, Unsize};

pub struct Smaht<T: ?Sized, MISC>(pub Box<T>, pub PhantomData<MISC>);

impl<T: ?Sized, MISC> Deref for Smaht<T, MISC> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: ?Sized + Unsize<U>, U: ?Sized, MISC> CoerceUnsized<Smaht<U, MISC>>
for Smaht<T, MISC>
{}
impl<T: ?Sized + Unsize<U>, U: ?Sized, MISC> DispatchFromDyn<Smaht<U, MISC>>
for Smaht<T, MISC>
{}

pub trait Foo: X<u32> {}
pub trait X<T> {
fn foo(self: Smaht<Self, T>) -> T;
}

impl X<u32> for () {
fn foo(self: Smaht<Self, u32>) -> u32 {
0
}
}

pub trait Marker {}
impl Marker for dyn Foo {}
impl<T: Marker + ?Sized> X<u64> for T {
fn foo(self: Smaht<Self, u64>) -> u64 {
1
}
}

impl Deref for dyn Foo {
type Target = ();
fn deref(&self) -> &() { &() }
}

impl Foo for () {}
}

pub trait FinalFoo {
fn foo(&self) -> u8;
}

impl FinalFoo for () {
fn foo(&self) -> u8 { 0 }
}

mod nuisance_foo {
pub trait NuisanceFoo {
fn foo(self);
}

impl<T: ?Sized> NuisanceFoo for T {
fn foo(self) {}
}
}


fn objectcandidate_impl() {
let x: internal::Smaht<(), u32> = internal::Smaht(Box::new(()), PhantomData);
let x: internal::Smaht<dyn internal::Foo, u32> = x;

// This picks `<dyn internal::Foo as X<u32>>::foo` via `ObjectCandidate`.
//
// The `TraitCandidate` is not relevant because `X` is not in scope.
let z = x.foo();

// Observe the type of `z` is `u32`
let _seetype: () = z; //~ ERROR mismatched types
//~| expected (), found u32
}

fn traitcandidate_impl() {
use internal::X;

let x: internal::Smaht<(), u64> = internal::Smaht(Box::new(()), PhantomData);
let x: internal::Smaht<dyn internal::Foo, u64> = x;

// This picks `<dyn internal::Foo as X<u64>>::foo` via `TraitCandidate`.
//
// The `ObjectCandidate` does not apply, as it only applies to
// `X<u32>` (and not `X<u64>`).
let z = x.foo();

// Observe the type of `z` is `u64`
let _seetype: () = z; //~ ERROR mismatched types
//~| expected (), found u64
}

fn traitcandidate_impl_with_nuisance() {
use internal::X;
use nuisance_foo::NuisanceFoo;

let x: internal::Smaht<(), u64> = internal::Smaht(Box::new(()), PhantomData);
let x: internal::Smaht<dyn internal::Foo, u64> = x;

// This picks `<dyn internal::Foo as X<u64>>::foo` via `TraitCandidate`.
//
// The `ObjectCandidate` does not apply, as it only applies to
// `X<u32>` (and not `X<u64>`).
//
// The NuisanceFoo impl has the same priority as the `X` impl,
// so we get a conflict.
let z = x.foo(); //~ ERROR multiple applicable items in scope
}


fn neither_impl() {
let x: internal::Smaht<(), u64> = internal::Smaht(Box::new(()), PhantomData);
let x: internal::Smaht<dyn internal::Foo, u64> = x;

// This can't pick the `TraitCandidate` impl, because `Foo` is not
// imported. However, this also can't pick the `ObjectCandidate`
// impl, because it only applies to `X<u32>` (and not `X<u64>`).
//
// Therefore, neither of the candidates is applicable, and we pick
// the `FinalFoo` impl after another deref, which will return `u8`.
let z = x.foo();

// Observe the type of `z` is `u8`
let _seetype: () = z; //~ ERROR mismatched types
//~| expected (), found u8
}

fn both_impls() {
use internal::X;

let x: internal::Smaht<(), u32> = internal::Smaht(Box::new(()), PhantomData);
let x: internal::Smaht<dyn internal::Foo, u32> = x;

// This can pick both the `TraitCandidate` and the `ObjectCandidate` impl.
//
// However, the `ObjectCandidate` is considered an "inherent candidate",
// and therefore has priority over both the `TraitCandidate` as well as
// any other "nuisance" candidate" (if present).
let z = x.foo();

// Observe the type of `z` is `u32`
let _seetype: () = z; //~ ERROR mismatched types
//~| expected (), found u32
}


fn both_impls_with_nuisance() {
// Similar to the `both_impls` example, except with a nuisance impl to
// make sure the `ObjectCandidate` indeed has a higher priority.

use internal::X;
use nuisance_foo::NuisanceFoo;

let x: internal::Smaht<(), u32> = internal::Smaht(Box::new(()), PhantomData);
let x: internal::Smaht<dyn internal::Foo, u32> = x;
let z = x.foo();

// Observe the type of `z` is `u32`
let _seetype: () = z; //~ ERROR mismatched types
//~| expected (), found u32
}

fn main() {
}
@@ -0,0 +1,72 @@
error[E0308]: mismatched types
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:85:24
|
LL | let _seetype: () = z; //~ ERROR mismatched types
| ^ expected (), found u32
|
= note: expected type `()`
found type `u32`

error[E0308]: mismatched types
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:102:24
|
LL | let _seetype: () = z; //~ ERROR mismatched types
| ^ expected (), found u64
|
= note: expected type `()`
found type `u64`

error[E0034]: multiple applicable items in scope
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:120:15
|
LL | let z = x.foo(); //~ ERROR multiple applicable items in scope
| ^^^ multiple `foo` found
|
note: candidate #1 is defined in an impl of the trait `internal::X` for the type `_`
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:43:9
|
LL | fn foo(self: Smaht<Self, u64>) -> u64 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: candidate #2 is defined in an impl of the trait `nuisance_foo::NuisanceFoo` for the type `_`
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:70:9
|
LL | fn foo(self) {}
| ^^^^^^^^^^^^
note: candidate #3 is defined in the trait `FinalFoo`
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:57:5
|
LL | fn foo(&self) -> u8;
| ^^^^^^^^^^^^^^^^^^^^
= help: to disambiguate the method call, write `FinalFoo::foo(x)` instead

error[E0308]: mismatched types
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:137:24
|
LL | let _seetype: () = z; //~ ERROR mismatched types
| ^ expected (), found u8
|
= note: expected type `()`
found type `u8`

error[E0308]: mismatched types
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:155:24
|
LL | let _seetype: () = z; //~ ERROR mismatched types
| ^ expected (), found u32
|
= note: expected type `()`
found type `u32`

error[E0308]: mismatched types
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:172:24
|
LL | let _seetype: () = z; //~ ERROR mismatched types
| ^ expected (), found u32
|
= note: expected type `()`
found type `u32`

error: aborting due to 6 previous errors

Some errors occurred: E0034, E0308.
For more information about an error, try `rustc --explain E0034`.
41 changes: 41 additions & 0 deletions src/test/ui/methods/method-trait-object-with-hrtb.rs
@@ -0,0 +1,41 @@
// compile-pass

// Check that method probing ObjectCandidate works in the presence of
// auto traits and/or HRTBs.

mod internal {
pub trait MyObject<'a> {
type Output;

fn foo(&self) -> Self::Output;
}

impl<'a> MyObject<'a> for () {
type Output = &'a u32;

fn foo(&self) -> Self::Output { &4 }
}
}

fn t1(d: &dyn for<'a> internal::MyObject<'a, Output=&'a u32>) {
d.foo();
}

fn t2(d: &dyn internal::MyObject<'static, Output=&'static u32>) {
d.foo();
}

fn t3(d: &(dyn for<'a> internal::MyObject<'a, Output=&'a u32> + Sync)) {
d.foo();
}

fn t4(d: &(dyn internal::MyObject<'static, Output=&'static u32> + Sync)) {
d.foo();
}

fn main() {
t1(&());
t2(&());
t3(&());
t4(&());
}

0 comments on commit 0ceb30d

Please sign in to comment.