Stream: t-lang

Topic: impl Trait everywhere


nikomatsakis (May 08 2020 at 22:14, on Zulip):

Hey folks. So I've gotten pretty interested in trying to push impl Trait through to stability. I think it's crucial for async code, for one thing, but it's also pretty squarely in the Rust roadmap mandate of "finishing up unfinishing things".

I've spend a number of hours today preparing notes. They're not yet in a form that can be consumed by the public, but I will try to arrange them better. I want to propose that we use an upcoming design meeting for this topic -- perhaps May 20th?

Things I am trying to ultimately pull together:

I think the meeting would probably focus on the first two, plus perhaps some amount of "review".

cc @Taylor Cramer @oli @varkor @boats , folks who I know have had a lot of interest in impl Trait in the past.

nikomatsakis (May 08 2020 at 22:17, on Zulip):

If you're glutton for punishment, my notes are in this file -- the section on "philosophy" however didn't quite work out as beautifully as I hoped and has some unfinished ramblings for me to reconcile. =) Though I still think the core idea is reasonable.

nikomatsakis (May 08 2020 at 22:21, on Zulip):

(I would probably enjoy some "pre-design meeting" discussion too.)

varkor (May 08 2020 at 22:23, on Zulip):

I am interested in discussing this, but I'll need to refresh my memory on the whole topic; I'll try to do so over the weekend.

Josh Triplett (May 08 2020 at 22:23, on Zulip):

@nikomatsakis Thank you for the detailed example showing everywhere it might make sense.

nikomatsakis (May 08 2020 at 22:24, on Zulip):

it's missing a case or two still, notably structs

Josh Triplett (May 08 2020 at 22:25, on Zulip):

Do "where clause position" and "associated type binding position" remain identical when used in trait methods?

Josh Triplett (May 08 2020 at 22:25, on Zulip):

Or does there need to be a "trait method where clause position" and similar?

Josh Triplett (May 08 2020 at 22:25, on Zulip):

Also, the example right up at the top that you called "named" might benefit from an "also known as 'type alias'".

Josh Triplett (May 08 2020 at 22:26, on Zulip):

Another few cases, which may be identical to existing cases:

let x = |closure_arg: impl Trait| -> impl Trait { ... };
nikomatsakis (May 08 2020 at 22:26, on Zulip):

I believe so, but i'm going to add to the cod for now to remind me to think more about it

nikomatsakis (May 08 2020 at 22:27, on Zulip):

notably I think it's not entirely clear the precise circumstances in which impl Trait should be permitted in where causes (I think it largely should not, except for in associated type binding positions)

Josh Triplett (May 08 2020 at 22:28, on Zulip):

Interesting. Why is the one you labeled "where clause position" not something you'd want to support?

Josh Triplett (May 08 2020 at 22:28, on Zulip):

It seems generally useful.

nikomatsakis (May 08 2020 at 22:29, on Zulip):

because the obvious interpretation to me creates an unconstrained type parameter

nikomatsakis (May 08 2020 at 22:29, on Zulip):

and you have no way to specify its value

nikomatsakis (May 08 2020 at 22:29, on Zulip):

that said, there are other plausible interpretations

Josh Triplett (May 08 2020 at 22:29, on Zulip):

What do you mean by "unconstrained type parameter"?

nikomatsakis (May 08 2020 at 22:30, on Zulip):

consider

fn foo()
  where u32: PartialOrd<impl Debug>
nikomatsakis (May 08 2020 at 22:30, on Zulip):

I believe that probably "wants to" desugar to

fn foo<T>()
  where u32: PartialOrd<T>
nikomatsakis (May 08 2020 at 22:30, on Zulip):

but what is this T?

Josh Triplett (May 08 2020 at 22:31, on Zulip):

That doesn't seem like the right desugar.

nikomatsakis (May 08 2020 at 22:31, on Zulip):

in the latter case, the user can specify, but in the former case, they cannot, and there are many types that u32 can be ordered with

nikomatsakis (May 08 2020 at 22:31, on Zulip):

that is one possible interpretation, yes, another plausible desugar is

Josh Triplett (May 08 2020 at 22:31, on Zulip):

It should desugar to:

fn foo<T>()
  where u32: PartialOrd<T>, T: Debug
nikomatsakis (May 08 2020 at 22:31, on Zulip):

oh, that's not the imp't debug though, but that's true

nikomatsakis (May 08 2020 at 22:31, on Zulip):
fn foo()
  where u32: for<T: Debug> PartialOrd<T>
nikomatsakis (May 08 2020 at 22:31, on Zulip):

that would be another plausible desugar

Josh Triplett (May 08 2020 at 22:31, on Zulip):

Oh, I see.

nikomatsakis (May 08 2020 at 22:31, on Zulip):

we don't yet support that notation, but we have to and will soon enough

nikomatsakis (May 08 2020 at 22:31, on Zulip):

for GATs

nikomatsakis (May 08 2020 at 22:32, on Zulip):

that interpretation does make sense

nikomatsakis (May 08 2020 at 22:32, on Zulip):

this is (imo) related to '_ notation

Josh Triplett (May 08 2020 at 22:32, on Zulip):

So the question is, does this desugar to "you must accept any Debug" or "the call must be for a specific Debug"?

nikomatsakis (May 08 2020 at 22:32, on Zulip):

right now we don't I don't think accept

nikomatsakis (May 08 2020 at 22:32, on Zulip):
where T: Blah<'_>
nikomatsakis (May 08 2020 at 22:32, on Zulip):

but I think if we it should introduce a region in the same place as the T above, whatever we decided :)

Josh Triplett (May 08 2020 at 22:32, on Zulip):

I understand now.

nikomatsakis (May 08 2020 at 22:32, on Zulip):

I gotta go now, dinner time and end of day here

Josh Triplett (May 08 2020 at 22:33, on Zulip):

The specific case where I'd imagine using it would be for a specific impl, but this seems like an absolutely reasonable distinction. And if you already have a where clause, it's not that bad to have to extend it.

nikomatsakis (May 08 2020 at 22:33, on Zulip):

(I'm still reviewing the rustc impl code, that's the major to do I have left, plus to wrestle with my framing of things as scopes and see if I can make it make sense :)

Josh Triplett (May 08 2020 at 22:33, on Zulip):

Thanks for all this work!

varkor (May 09 2020 at 11:48, on Zulip):

I think Named should be Type alias and Associated type value position should be Associated type (alias), which are the terms that have been previously used (at least informally).

varkor (May 09 2020 at 11:50, on Zulip):

impl Trait for impl Type { } — is this added just to have a name to refer to? This is not being considered?

varkor (May 09 2020 at 11:53, on Zulip):

I think several of these positions behave identically, but that seems to be intentional for now.

varkor (May 09 2020 at 11:59, on Zulip):

Re.

Use the natural interpretation to guide the choice.

I think if the choice is at all ambiguous, it should be disallowed. This feeds into the point about "It may be that the interpretation is not clear for readers", though it's slightly stronger.

varkor (May 09 2020 at 12:01, on Zulip):

Existential types

Also, let's not use this terminology: it's continually caused confusion in the past, because many people don't really understand what existential types are. We're using "opaque type" in the compiler, and that seems reasonable to use technically as well.

varkor (May 09 2020 at 12:03, on Zulip):

Existential type aliases cannot be used in impl blocks

This would be slightly more readable as: "It is not possible to impl methods for opaque types.".

varkor (May 09 2020 at 12:09, on Zulip):

I think the general guidelines match with my intuition, though I need to think more carefully about the nesting impl Trait: it seems quite plausible that we would never want to permit this.

varkor (May 09 2020 at 12:15, on Zulip):

varkor said:

Existential types

Also, let's not use this terminology: it's continually caused confusion in the past, because many people don't really understand what existential types are. We're using "opaque type" in the compiler, and that seems reasonable to use technically as well.

To clarify, I don't think "universal/existential binders" cause confusion, but referring to impl Trait as an existential type does.

rpjohnst (May 09 2020 at 16:59, on Zulip):

varkor said:

varkor said:

Existential types

Also, let's not use this terminology: it's continually caused confusion in the past, because many people don't really understand what existential types are. We're using "opaque type" in the compiler, and that seems reasonable to use technically as well.

To clarify, I don't think "universal/existential binders" cause confusion, but referring to impl Trait as an existential type does.

I think in the case of niko's notes it actually made a lot of sense, compared to previous attempts. The way return-position impl Trait is an existential within the forall of the next generic scope out perfectly captures the way that the actual type can depend on those generic parameters but not the function's value-level parameters.

nikomatsakis (May 20 2020 at 15:13, on Zulip):

@Matthew Jasper are you around by any chance?

Matthew Jasper (May 20 2020 at 16:11, on Zulip):

I am now...

nikomatsakis (May 20 2020 at 16:41, on Zulip):

@Matthew Jasper I was going to ask some questions about the impl Trait code but I think I figured out enough for the time being.

nikomatsakis (May 20 2020 at 16:51, on Zulip):

Reminder @T-lang that we have a design meeting in 10 mnutes -- I forgot to update the calendar, but the planned topic was "impl Trait everywhere".
I'm currently preparing a hackmd that I expect to use to drive the meeting.

nikomatsakis (May 20 2020 at 17:01, on Zulip):

@T-lang design meeting is now =)

pnkfelix (May 20 2020 at 17:04, on Zulip):

(computer and/or zoom is behaving weird; will try to rejoin in a sec)

nikomatsakis (May 20 2020 at 17:55, on Zulip):

I just lost the meeting too

Lokathor (May 20 2020 at 18:18, on Zulip):

So i'm not sure if we should keep editing the hackmd or not, so i'll just say here, but Foo::foo::return could be a general "phrase" for accessing the return type of any method or function. It's not an "expression" since it resolves to a type instead of a value.

isHavvy (May 21 2020 at 06:59, on Zulip):

Would it be considered an inherent associated type?

Josh Triplett (May 21 2020 at 07:28, on Zulip):

@Lokathor I also wonder if somefunc::arg<2> would work as a phrase to access the second argument.

Josh Triplett (May 21 2020 at 07:28, on Zulip):

Well, we'd need a keyword in place of arg...

Josh Triplett (May 21 2020 at 07:29, on Zulip):

Maybe somefunc::2, since normally you'd need an identifier?

Lokathor (May 21 2020 at 07:30, on Zulip):

yeah i hadn't thought that you'd need to name arg types too, but i guess it might come up

Josh Triplett (May 21 2020 at 07:31, on Zulip):

Or somefunc::fn<2> .

Lokathor (May 21 2020 at 07:33, on Zulip):

there might be a limit to how much we can sanely reuse keywords XD

Lokathor (May 21 2020 at 07:43, on Zulip):

okay okay, if we need inputs and outputs, and also not to flood the possibility space of what the user has to read too much, we could do somefunc::type<N> to get the type of the Nth argument, or somefunc::type<return> for the type of the return value.

isHavvy (May 21 2020 at 07:46, on Zulip):

somefunc::in<0>

Lokathor (May 21 2020 at 07:58, on Zulip):

ooooh

nikomatsakis (May 21 2020 at 18:13, on Zulip):

The ::return syntax seems kind of wordy to me

nikomatsakis (May 21 2020 at 18:14, on Zulip):

e.g., imagine you have

trait Service {
    fn launch(&self) -> impl Future;
}

then you would write

fn launch_service<S: Service>()
where
    S::launch::return: Send
nikomatsakis (May 21 2020 at 18:14, on Zulip):

vs

fn launch_service<S: Service>()
where
    S::Launch: Send
nikomatsakis (May 21 2020 at 18:15, on Zulip):

or even S::launch: Send

nikomatsakis (May 21 2020 at 18:15, on Zulip):

that said, another thing that would be useful is some way to say all the async fns in this trait return Send futures (or all the impl Future ...)

nikomatsakis (May 21 2020 at 18:16, on Zulip):

something like (to use return notation)

where S::*::return: Send

(not that I actually think I would want this syntax)

Taylor Cramer (May 21 2020 at 22:08, on Zulip):

https://github.com/cramertj/impl-trait-goals/blob/impl-trait-in-traits/0000-impl-trait-in-traits.md is the RFC that I had written for inferred associated types btw. It's quite old and outdated now, but it shows where my head was at one point in time.

rpjohnst (May 21 2020 at 23:55, on Zulip):

Rather than ::return/::fn/::arg/etc, it might be nicer to come up with a syntax for the client to give that type a name it can use. (By analogy: pattern matching rather than projection, or C++20 requires-expressions rather than C++11 declval.) Maybe something like where (for(s: &S) { s.launch() }): F, F: Send?

As const generics gets further along I expect people will need similar tools there anyway- things like where doesnt_overflow({ X + 3 }) (or perhaps even where { X + 3 < Y }). Some sort of cohesive tool for talking about expressions, even expressions pulled out of thin air like for(s: &S) { s.launch() } might be a good way to handle impl Trait bounds as well.

nikomatsakis (May 22 2020 at 17:45, on Zulip):

I do want to say that

nikomatsakis (May 22 2020 at 17:45, on Zulip):

in general, I think that turbofish is kind of "meh"

nikomatsakis (May 22 2020 at 17:45, on Zulip):

especially when you have a long list of "optional" parameters

nikomatsakis (May 22 2020 at 17:45, on Zulip):

e.g., if we have HashMap<T, H(asher), A(llocator)>

nikomatsakis (May 22 2020 at 17:45, on Zulip):

it'd be nice to be able to specify the Allocator without speciying the hasher

nikomatsakis (May 22 2020 at 17:46, on Zulip):

I guess that being able to give some name might tie in with impl Trait

nikomatsakis (May 22 2020 at 17:46, on Zulip):

I'm not sure I quite understand @rpjohnst your const generics examples though

rpjohnst (May 22 2020 at 19:27, on Zulip):

nikomatsakis said:

I'm not sure I quite understand rpjohnst your const generics examples though

I could be totally wrong there, but my understanding is that, to use a const generic parameter in an expression, the parameter must be constrained enough that the use won't produce monomorphization-time errors- thus a need to put expressions in constraints.

varkor (May 23 2020 at 15:32, on Zulip):

Argh, I intended to attend this meeting, but it slipped my mind with everything else that's going on :weary:

nikomatsakis (May 26 2020 at 22:26, on Zulip):

Hey @Matthew Jasper -- I was wondering if you'd be interested in scheduling a time to chat to talk about impl Trait implementation issues. I just saw https://github.com/rust-lang/rust/pull/72080/ and I've been thinking about this as well.

Matthew Jasper (May 27 2020 at 16:36, on Zulip):

That sounds good. I'm generally free after 4:30 UTC

Matthew Jasper (Jun 02 2020 at 11:16, on Zulip):

Ping @nikomatsakis

nikomatsakis (Jun 02 2020 at 14:29, on Zulip):

Heh I was wondering where I started this thread, thanks @Matthew Jasper

nikomatsakis (Jun 02 2020 at 14:30, on Zulip):

Also cc @oli

nikomatsakis (Jun 02 2020 at 14:30, on Zulip):

I think best for me might be to try and talk more on Friday

nikomatsakis (Jun 02 2020 at 14:30, on Zulip):

well or maybe Wed

Matthew Jasper (Jun 02 2020 at 14:58, on Zulip):

Either of those sounds fine for me

nikomatsakis (Jun 02 2020 at 15:01, on Zulip):

I will try to review #72080 in the meantime

nikomatsakis (Jun 03 2020 at 18:24, on Zulip):

@Matthew Jasper let's do friday, say 13:00 Boston time == 17:00 UTC I believe

nikomatsakis (Jun 05 2020 at 17:01, on Zulip):

Hey @Matthew Jasper (and maybe @oli)

nikomatsakis (Jun 05 2020 at 17:02, on Zulip):

So... I was hoping to talk a bit about impl trait implementation questions

nikomatsakis (Jun 05 2020 at 17:03, on Zulip):

I had hoped to do prep but mostly I just read @Matthew Jasper's PR -- in any case, I'm going to start typing and leaving notes and thoughts and doing a bit of investigating, and maybe if you folks are around now, that's great, else we can discuss more later

nikomatsakis (Jun 05 2020 at 17:03, on Zulip):

hackmd

Matthew Jasper (Jun 05 2020 at 17:06, on Zulip):

I'm here

nikomatsakis (Jun 05 2020 at 17:07, on Zulip):

:wave:

nikomatsakis (Jun 05 2020 at 17:07, on Zulip):

sorry, was afk for a sec, back now

nikomatsakis (Jun 05 2020 at 17:08, on Zulip):

OK, so, looking at the questions of implementation...

nikomatsakis (Jun 05 2020 at 17:08, on Zulip):

I guess that there are a few categories of things going on

nikomatsakis (Jun 05 2020 at 17:08, on Zulip):

for me a big goal is enabling people to return futures/closures/etc in trait impls

nikomatsakis (Jun 05 2020 at 17:09, on Zulip):

which I guess is basically a pattern of

type Foo<P0...Pn> = impl Something;

impl<P0..Pn> SomeTrait for Type {
    type Output = Foo<P0..Pn>;

    fn method(..) -> Self::Output { .. }
}
nikomatsakis (Jun 05 2020 at 17:10, on Zulip):

I bring this up because there is a distinct case where the opaque type appears in the arguments of the fn

nikomatsakis (Jun 05 2020 at 17:10, on Zulip):

and I think that this case is more challenging and also harder for our implementation

nikomatsakis (Jun 05 2020 at 17:10, on Zulip):

so in particular i'm looking at "are there important use cases we could prioritize for stabilization"

nikomatsakis (Jun 05 2020 at 17:11, on Zulip):

I think that opaque types where the "defining uses" are in the return type only are such a case

Matthew Jasper (Jun 05 2020 at 17:12, on Zulip):

Yes

nikomatsakis (Jun 05 2020 at 17:13, on Zulip):

I'm trying now to find my older hackmd

nikomatsakis (Jun 05 2020 at 17:13, on Zulip):

where I had a more comprehensive list of where impl Trait could appear

nikomatsakis (Jun 05 2020 at 17:13, on Zulip):

I guess it was at the top of this topic

nikomatsakis (Jun 05 2020 at 17:13, on Zulip):

here they are

nikomatsakis (Jun 05 2020 at 17:14, on Zulip):

oh, right, it was not a gist..

nikomatsakis (Jun 05 2020 at 17:15, on Zulip):

so in particular the reason to separate out the return position

Matthew Jasper (Jun 05 2020 at 17:15, on Zulip):

There's also https://hackmd.io/w_xcaFmASEqzVMC3J2L8Dw?edit

nikomatsakis (Jun 05 2020 at 17:15, on Zulip):

is that it doesn't encounter the challenge of "how to think about the opaque type during inference"

nikomatsakis (Jun 05 2020 at 17:16, on Zulip):

oh yes

nikomatsakis (Jun 05 2020 at 17:16, on Zulip):

ok

nikomatsakis (Jun 05 2020 at 17:16, on Zulip):

so many notes :)

nikomatsakis (Jun 05 2020 at 17:16, on Zulip):

I guess I'm curious to see if there are any problems with other positions

nikomatsakis (Jun 05 2020 at 17:16, on Zulip):

I think usage in const/static doesn't present any particular difficulties, even if it currently errors more than we'd like?

nikomatsakis (Jun 05 2020 at 17:17, on Zulip):

I'm not 100% sure what the status is of let x: impl Trait = .. after your PR, I remember the impl before had some serious flaws

nikomatsakis (Jun 05 2020 at 17:18, on Zulip):

it seems to me that these are the "big categories" though:

Matthew Jasper (Jun 05 2020 at 17:18, on Zulip):

I think it's still pretty bad. I think that all I did was change one ICE for another that gives a slightly better message.

nikomatsakis (Jun 05 2020 at 17:18, on Zulip):

I probably ordered those wrong

nikomatsakis (Jun 05 2020 at 17:18, on Zulip):

in that if you do this order:

nikomatsakis (Jun 05 2020 at 17:18, on Zulip):
nikomatsakis (Jun 05 2020 at 17:18, on Zulip):

it might be more the order of priority?

nikomatsakis (Jun 05 2020 at 17:19, on Zulip):

I'm actually not sure about that

nikomatsakis (Jun 05 2020 at 17:19, on Zulip):

I mean you never need let x: impl Foo in a fn body, but it might be in some senses simpler, and I've found I really would like it when writing docs

nikomatsakis (Jun 05 2020 at 17:19, on Zulip):

but anyhow

Matthew Jasper (Jun 05 2020 at 17:19, on Zulip):

There's also the non-defining use in defining scope issue.

nikomatsakis (Jun 05 2020 at 17:19, on Zulip):

do you mean the "passthrough" use case?

Matthew Jasper (Jun 05 2020 at 17:19, on Zulip):

yes

nikomatsakis (Jun 05 2020 at 17:20, on Zulip):

specifically I mean that

nikomatsakis (Jun 05 2020 at 17:20, on Zulip):
fn foo(x: OpaqueType) -> OpaqueType { x }

is supposed to work

nikomatsakis (Jun 05 2020 at 17:20, on Zulip):

so, that is an issue, but I note that it requires a "defining use" in inputs

nikomatsakis (Jun 05 2020 at 17:20, on Zulip):

maybe I'm using "defining use" in a confusing way

nikomatsakis (Jun 05 2020 at 17:20, on Zulip):

I don't actually mean what the compiler means by it

nikomatsakis (Jun 05 2020 at 17:20, on Zulip):

I mean what the RFC means by, that is, basically any use that is inside the "scope" of the opaque type

nikomatsakis (Jun 05 2020 at 17:21, on Zulip):

let's call it .. in-scope use?

nikomatsakis (Jun 05 2020 at 17:22, on Zulip):

Ultimately all impl Trait outside of fn arguments desugar to a "opaque type" that has some "defining scope".

This scope may be a particular item or it may be the enclosing module.

A "in-scope use" is a reference to that opaque type from within the "defining scope".

nikomatsakis (Jun 05 2020 at 17:22, on Zulip):

That's my mental model, sound correct?

Matthew Jasper (Jun 05 2020 at 17:22, on Zulip):

Sounds good

nikomatsakis (Jun 05 2020 at 17:24, on Zulip):

Layer 1. All "in-scope uses" are in a "defining" location.

A "defining" location is one of the following:

nikomatsakis (Jun 05 2020 at 17:24, on Zulip):

What these have in common is that there are no expressions that the user can reference that yield values of the opaque type until the opaque type is fully inferred (it would result in a cycle error).

Claim: the current implementation in these cases matches the RFC.

nikomatsakis (Jun 05 2020 at 17:24, on Zulip):

Sound correct?

nikomatsakis (Jun 05 2020 at 17:24, on Zulip):

We should probably test that claim :)

nikomatsakis (Jun 05 2020 at 17:26, on Zulip):

I wonder how to think about

type OpaqueType = impl Trait;

struct Foo {
  x: OpaqueType
}
nikomatsakis (Jun 05 2020 at 17:26, on Zulip):

It is not a member of that layer, clearly :)

Matthew Jasper (Jun 05 2020 at 17:32, on Zulip):

What these have in common is that there are no expressions that the user can reference that yield values of the opaque type until the opaque type is fully inferred (it would result in a cycle error).

I'm not sure what you mean by this. It's possible to write:

type A = impl Sized;
fn f(rec: bool) -> A {
     if rec {
          let x = f(false);
     }
     String::new();
}
nikomatsakis (Jun 05 2020 at 17:35, on Zulip):

Hmm ok I guess that's just false :)

nikomatsakis (Jun 05 2020 at 17:35, on Zulip):

I guess it's only if we have to evaluate an auto trait

nikomatsakis (Jun 05 2020 at 17:35, on Zulip):

that you get the cycle error

nikomatsakis (Jun 05 2020 at 17:36, on Zulip):

that gets at something I was just thinking about

nikomatsakis (Jun 05 2020 at 17:36, on Zulip):

I'm not sure I entirely know what the expected behavior is in this case

nikomatsakis (Jun 05 2020 at 17:36, on Zulip):

is the type of x "opaque"

nikomatsakis (Jun 05 2020 at 17:36, on Zulip):

or "revealed"

nikomatsakis (Jun 05 2020 at 17:37, on Zulip):

similarly if you had

type OpaqueType = impl Debug;

fn foo() {
  let y: OpaqueType = 22_u32;
  y + 44;
}

does that compile?

nikomatsakis (Jun 05 2020 at 17:38, on Zulip):

I ask because I could imagine an alternate impl strategy where we don't "replace" opaque-types-being-inferred with fresh inference variables

nikomatsakis (Jun 05 2020 at 17:38, on Zulip):

but rather collect the constraints

nikomatsakis (Jun 05 2020 at 17:38, on Zulip):

it might be ever so slightly backwards incompatible

nikomatsakis (Jun 05 2020 at 17:38, on Zulip):

for a case like

Matthew Jasper (Jun 05 2020 at 17:38, on Zulip):

The current behaviour is opaque I think.

nikomatsakis (Jun 05 2020 at 17:39, on Zulip):
fn foo() -> impl Trait {
    if something { return XXX; }

    return YYY; // maybe the type of XXX informs  the expected type for YYY here?
}
nikomatsakis (Jun 05 2020 at 17:39, on Zulip):

but I imagine that would be .. very rare .. and perhaps solveable, at worst via a hack

nikomatsakis (Jun 05 2020 at 17:41, on Zulip):

I could imagine either behavior, I'm not sure what I expect

nikomatsakis (Jun 05 2020 at 17:41, on Zulip):

but I see some advantages to this impl strategy

nikomatsakis (Jun 05 2020 at 17:41, on Zulip):

it would I think help with the problems of impl Trait in argment position etc

nikomatsakis (Jun 05 2020 at 17:42, on Zulip):

not sure if what I sketched is clear

nikomatsakis (Jun 05 2020 at 17:42, on Zulip):

what I imagine is maybe something like this

nikomatsakis (Jun 05 2020 at 17:42, on Zulip):

we have in the inference context a list of "opaque types being inferred"

nikomatsakis (Jun 05 2020 at 17:43, on Zulip):

whenever we are asked to equate an opaque type .. xxx .. ok, what I was about to write would not work :)

nikomatsakis (Jun 05 2020 at 17:43, on Zulip):

well, I'll write it anyway, the idea would be that unification succeeds, but we collect the type you unified with

nikomatsakis (Jun 05 2020 at 17:44, on Zulip):

one reason it would not work is that you have things like "test if this impl applies"

nikomatsakis (Jun 05 2020 at 17:44, on Zulip):

(and you basically never want those impls to apply)

nikomatsakis (Jun 05 2020 at 17:44, on Zulip):

so we'd have to sort of distinguish a bit better what we mean there

nikomatsakis (Jun 05 2020 at 17:45, on Zulip):

nikomatsakis said:

(and you basically never want those impls to apply)

except maybe when we do? sometimes impl selection affects inference, after all

nikomatsakis (Jun 05 2020 at 17:47, on Zulip):

in terms of what behavior we want, @Taylor Cramer mentioned a "guideline" that maybe helps. As much as possible, you should be able to take a impl Trait and "extract" it to a type alias and things should kind of work the same. This is only sort of possible because of "universal-to-existential" conversion, though. But it would suggest that

let x: impl Debug = 22;
x + 44

is an error, so it should be the same if you do

type X = impl Debug;

let x: X = 22;
x + 44
nikomatsakis (Jun 05 2020 at 17:50, on Zulip):

I'm thinking about that recursive case and trying to decide whether it implies that I am wrong that we could potentially move faster towards stabilizing impl trait in return position

nikomatsakis (Jun 05 2020 at 17:51, on Zulip):

presumably the compiler treats such types as opaque

nikomatsakis (Jun 05 2020 at 17:51, on Zulip):

yes, it does

nikomatsakis (Jun 05 2020 at 17:51, on Zulip):

so e.g. this does not compile

nikomatsakis (Jun 05 2020 at 17:51, on Zulip):
fn foo() -> impl std::fmt::Debug {
    if false { let y: () = foo(); }
}

fn main() { }

playground

nikomatsakis (Jun 05 2020 at 17:54, on Zulip):

so I guess one thing to do might be to experiment with this idea I was describing, though I'd have to decide how to handle the distinct between "probing" whether a type can unify and "declaring" that it must unify

nikomatsakis (Jun 05 2020 at 17:55, on Zulip):

I was already thinking that the real way to handle this would be to have the "obligations" that get propagated back encode information about the constraints on opaque types that are required (similar to region constraints), which might be part of the answer

nikomatsakis (Jun 05 2020 at 17:55, on Zulip):

...this is also similar to the "semantic vs syntactic equality" split, maybe?, that we are pursuing in chalk...

nikomatsakis (Jun 05 2020 at 17:55, on Zulip):

if we could get it to work, it seems like it would behave much better and also fit the RFC more closely

nikomatsakis (Jun 05 2020 at 17:58, on Zulip):

got to go, will ponder a bit, I'm leaning towards "it's worth experimenting some to see what problems arise"

Last update: Jun 05 2020 at 23:15UTC