Stream: t-compiler/rust-analyzer

Topic: implicit Sized bounds


Dawer (Jun 08 2021 at 15:21, on Zulip):

I've been working on adding implicit Sized bounds https://github.com/rust-analyzer/rust-analyzer/issues/8984
Sorry for the delay and sorry for the long writing.
TLDR I got a failing test of unsize coercion I cannot grasp how to fix it by myself.

The failing test is tests::coercion::coerce_unsize_trait_object_simple:

#[lang = "sized"]
pub trait Sized {}
#[lang = "unsize"]
pub trait Unsize<T: ?Sized> {}
#[lang = "coerce_unsized"]
pub trait CoerceUnsized<T: ?Sized> {}

impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&U> for &T {}

trait Foo<T, U> {}
trait Bar<U, T, X>: Foo<T, U> {}
trait Baz<T, X>: Bar<usize, T, X> {}

struct S<T, X>;
impl<T, X> Foo<T, usize> for S<T, X> {}
impl<T, X> Bar<usize, T, X> for S<T, X> {}
impl<T, X> Baz<T, X> for S<T, X> {}

fn test() {
    let obj: &dyn Baz<i8, i16> = &S;
    let obj: &dyn Bar<_, i8, i16> = &S;
    let obj: &dyn Foo<i8, _> = &S;
}
Expect:
----
457..572 '{     ... &S; }': ()
467..470 'obj': &dyn Baz<i8, i16>
492..494 '&S': &S<i8, i16>
493..494 'S': S<i8, i16>
504..507 'obj': &dyn Bar<usize, i8, i16>
532..534 '&S': &S<i8, i16>
533..534 'S': S<i8, i16>
544..547 'obj': &dyn Foo<i8, usize>
567..569 '&S': &S<i8, {unknown}>
568..569 'S': S<i8, {unknown}>

----

Actual:
----
457..572 '{     ... &S; }': ()
467..470 'obj': &dyn Baz<i8, i16>
492..494 '&S': &S<i8, i16>
493..494 'S': S<i8, i16>
504..507 'obj': &dyn Bar<usize, i8, i16>
532..534 '&S': &S<i8, i16>
533..534 'S': S<i8, i16>
544..547 'obj': &dyn Foo<i8, {unknown}>
567..569 '&S': &S<{unknown}, {unknown}>
568..569 'S': S<{unknown}, {unknown}>
567..569: expected &dyn Foo<i8, {unknown}>, got &S<{unknown}, {unknown}>

The last coercion fails. It is expected to success with 1 inference var left unbound but, with implicit Sized bounds, Chalk returns ambiguous. The ambiguity here is treated as no-success hir_ty/src/infer/coerce.rs#L409-L422:

        match solution {
            Solution::Unique(v) => {
                canonicalized.apply_solution(
                    &mut self.table,
                    Canonical {
                        binders: v.binders,
                        // FIXME handle constraints
                        value: v.value.subst,
                    },
                );
            }
            // FIXME: should we accept ambiguous results here?
            _ => return Err(TypeError),
        };

I can use Solution::Ambiguous(definitely_inferred) to update U in dyn Foo<i8, U=usize> but should we treat the ambiguity as successful coercion?

I recorded logs CHALK_DEBUG=info CHALK_PRINT=1 cargo test --package hir_ty --lib -- --nocapture tests::coercion::coerce_unsize_trait_object_simple --exact &> log . So for the last coercion it says:

┐solve_goal goal=UCanonical { canonical: Canonical { value: InEnvironment { environment: Env([]), goal: Implemented((&'static S<[?0 := ^0.0, ?1 := ^0.1]>): CoerceUnsized<(&'static dyn for<type> [for<> Implemented(^1.0: Foo<Int(I8), ^2.2>)] + 'static)>) }, binders: [U0 with kind type, U0 with kind type, U0 with kind type] }, universes: 1 }
<snip>
├─89ms INFO solve_goal: solution = Ok(Ambig(Definite(Canonical { value: [?0 := Int(I8), ?1 := ^0.0, ?2 := Uint(Usize)], binders: [U0 with kind type] }))) prio High
┘

Then I tried to reproduce the query in Chalk REPL (still not sure which one represents the query r-a does; Chalk program dump is in the log)

?- exists<T,U,X> {
    &'static S<T,X>: CoerceUnsized<&'static (dyn Foo<i8, U> + 'static)>
}
Ambiguous; no inference guidance

?- forall<X> {
    exists<T,U> {
        &'static S<T,X>: CoerceUnsized<&'static (dyn Foo<i8, U> + 'static)>
    }
}
No possible solution.

?- forall<X> {
    exists<T,U> {
        if (X:Sized) {
           &'static S<T,X>: CoerceUnsized<&'static (dyn Foo<i8, U> + 'static)>
        }
    }
}
Unique; substitution [?0 := Int(I8), ?1 := Uint(Usize)], lifetime constraints [InEnvironment { environment: Env([]), goal: S<Int(I8), !1_0>: 'static }]

?- forall<X> {
    exists<T,U> {
        if (FromEnv(S<T,X>)) {
           &'static S<T,X>: CoerceUnsized<&'static (dyn Foo<i8, U> + 'static)>
        }
    }
}
Unique; substitution [?0 := Int(I8), ?1 := Uint(Usize)], lifetime constraints [InEnvironment { environment: Env([]), goal: S<Int(I8), !1_0>: 'static }]

So chalk finds a solution with if(X:Sized) {..} or if(FromEnv(S<T,X>)) implications. But isn't it might infer this somehow? Does this mean we could provide to chalk some FromEnv(from_ty) && FromEnv(to_ty) assumptions about well-formedness?

Dawer (Jun 08 2021 at 15:25, on Zulip):

if (FromEnv(&'static S<T,X>)) does not work. Have to look into inner types then.. :thinking:

?- forall<X> {
    exists<T,U> {
        if (FromEnv(&'static S<T,X>)) {
           &'static S<T,X>: CoerceUnsized<&'static (dyn Foo<i8, U> + 'static)>
        }
    }
}
No possible solution.
Florian Diebold (Jun 08 2021 at 16:50, on Zulip):
exists<T,U,X> {
    &'static S<T,X>: CoerceUnsized<&'static (dyn Foo<i8, U> + 'static)>
}

is the correct query unless I'm missing something

Florian Diebold (Jun 08 2021 at 16:51, on Zulip):

I'm not sure this is supposed to work, since the expected result already has an uninferred type anyway

Florian Diebold (Jun 08 2021 at 16:52, on Zulip):

rustc actually doesn't do normal trait solving in that place, so it's really hard to tell whether we should be accepting ambiguous results or not or whether neither will do what we need

Florian Diebold (Jun 08 2021 at 16:53, on Zulip):

we could try seeing what happens if we allow ambiguous results there; we would need to return the obligation so it can be fully checked later

Florian Diebold (Jul 11 2021 at 11:20, on Zulip):

@Dawer did you make progress on this? you could put up a draft PR if it's not quite working yet

Last update: Jul 28 2021 at 03:45UTC