owned this note
owned this note
Published
Linked with GitHub
# method calls
We compute the `method_autoderef_steps` in a canonical query to prevent deref steps which don't matter from influencing candidate selection/inference.
## goal for opaque types
Allow them in the autoderef steps and use `predicate_may_hold_opaque_types_jank` in `consider_probe`.
Issues:
- method selection is using `select_where_possible`. Need an alternative which treats opaque types jank as a hard error :heavy_check_mark:
- could set a flag on the fulfillment context to treat opaque types as errors
- unsatisfied predicates get returned for diagnostics. going `_` does not implement `Trait` seems ass :shrug:
- if `Autoderef` returns not-yet-defined opaque types, we want to prevent constraining them during method selection? :thinking_face:
- keep track of the # of steps where we encountered opaques
- if it's less than the final number of steps, just check that way
- if it's more, continue jank in a probe and make sure the step still results in an unconstrained opaque
- issue: selection may change the deref chain
- alternative: instantiate steps which result in opaques. if the instantiate failed, the deref chain is no longer applicable. if the opaque type ended up constrained, we error
- `query method_autoderef_steps` uses old solver canonicalization, does not track opaque types
- switch to new solver canonical
- how, need to move new solver canonical out of the trait solver.
## `OpaqueTypesHack` why
```rust
trait Foo: Sized {
fn method(self) {}
}
impl Foo for u32 {}
trait Bar: Sized {
fn method(self) {}
}
impl Bar for u32 {}
fn recur() -> impl Foo {
if false {
// Selects `Foo` on stable, would be ambig
recur().method();
}
1u32
}
```
```rust
use std::ops::Deref;
trait Foo {
fn method(&self) {}
}
impl Foo for u32 {}
fn via_deref() -> impl Deref<Target = impl Foo> {
if false {
via_deref().method();
}
Box::new(1u32)
}
```
```rust
use std::ops::Deref;
struct Foo;
impl Foo {
fn method(&self) {}
}
fn via_deref() -> impl Deref<Target = Foo> {
// Currently errors on stable, but should not
if false {
via_deref().method();
}
Box::new(Foo)
}
```
## not constraining opaque types why
```rust
trait Trait {
fn foo(&self);
}
impl<T> Trait for T {
fn foo(&self) {}
}
fn recur() -> impl Sized {
if false {
// We first try to match `&self` to `?hidden_ty`
// which would constrain the opaque type to
// `&?unconstrained`.
recur().foo();
}
()
}
```
```rust
trait Trait {
fn foo(&&self);
}
impl<T> Trait for T {
fn foo(&&self) {}
}
fn recur() -> impl Sized {
if false {
let x = &recur();
// We first try to match `&self` to `?hidden_ty`
// which would constrain the opaque type to
// `&?unconstrained`.
x.foo();
}
()
}
```
we could restrict this requirement to opaques in the final deref chain. This would cause method selection to be unstable
```rust
trait Trait {
fn method(self);
}
struct Foo;
impl Trait for &Foo {
fn method(self) {
println!("trait");
}
}
impl Foo {
fn method(&self) {
println!("inherent");
}
}
fn recur(b: bool) -> impl Sized {
if b {
let x = &recur(!b);
// Given the self type `&?hidden_type`, we only
// match the `Trait` impl which constrains
// the hidden type to `Foo`. Repeated method
// selection now uses the inherent method.
x.method();
x.method();
}
Foo
}
```
## paths are fun
```rust
#![feature(type_alias_impl_trait)]
trait Trait {
fn take_me(self);
}
impl<T> Trait for T {
fn take_me(self) {}
}
type Tait = impl Sized;
#[define_opaque(Tait)]
fn main() {
<Tait>::take_me(1u32);
}
```
## what is in a `Canonical`
we've got inputs and outputs.
inputs contain
- `TypingMode`
- `ParamEnv`
- `predefined_opaques_in_body` (only in the new trait solver)
outputs contain
- the `CanonicalVarValues` to map vars back to the input
- `certainty`
- used by `solve::Response`, `infer::canonical::QueryResponse`
- ignored by `inspect::State`, `make_query_response_ignoring_pending_obligations`
- `region_constraints`
- used by `solve::Response`, `infer::canonical::QueryResponse`
- ignored by `inspect::State`, `make_query_response_ignoring_pending_obligations`
- `opaque_types`
- used by `solve::Response`, `infer::canonical::QueryResponse`
- ignored by `inspect::State`, `make_query_response_ignoring_pending_obligations`
- optional arbitrary `data/value`
- `Ty` in normalizaiton `type_op`s
- implied bounds in `query implied_outlives_bounds`
- added goals or impl args in `inspect::State`
- or `normalization_nested_goals` in `solve::Response`
### Differences
whether input contains `predefined_opaques_in_body`
`solve::Certainty` with `Maybe(MaybeCause)` vs `canonical::Certainty` with just `Ambiguous`
concepts of "state/`make_query_response_ignoring_pending_obligations`" don't care about the final result, just take a snapshot of the current state in a canonical query
new opaque type definitions: currently ignored by `state`, feels odd to do so? state should track opaque types
canonical results can either be applied, or instantiated:
- applying happens immediately after calling the query, infallible, only possible once
- instantiate does not eagerly constrain var_values and opaques
### How to impl
- move `query method_autoderef_steps` to new solver query input, include opaques https://github.com/lcnr/rust/pull/new/canonical-rustc_type_ir
- we now know which infer vars are opaque hidden types there
- don't care about the query output yet
- list of indices where the self type is an opaque
- confirm candidate checks whether any of them have gotten constraint
- if they are from a later autoderef step -> instantiate in probe
- THAT'S MAJOR ASS
## so gamer!
Passing `predefined_opaques_in_body` to `method_autoderef_steps`, tracking
which steps are opaque types, and erroring if the opaque gets constrained is gamer.
Problem:
- we don't want to error if applying a candidate causes later steps of the deref chain to no longer be reachable
- this means that if instantiating
## terminology
- deref[-chain] steps: `Box<T> -> T`
- method-receivers:
- for `Box<T>`: `Box<T>`, `&Box<T>`, `&mut Box<T>`, (`Pin<&T>` jank), then
- for `T`: `T`, `&T`, `&mut T` (`Pin<&T>` jank)
- candidate sources: derived from deref steps
- candidate: method-receiver + source pair
- `consider_probe`: test whether a pick is applicable :3
- `ProbeResult::BadReturnType` should be part of `NoMatch` :3