Stream: t-compiler/wg-nll

Topic: #55526 error on closure but not on "equiv" fn


pnkfelix (Dec 04 2018 at 10:21, on Zulip):

I wanted to just jot some thoughts down here rather than continuing to spam the issue with ill-formed ideas

pnkfelix (Dec 04 2018 at 10:21, on Zulip):

So the heart of the problem is in how we are supposed to handle a case like this:

fn main() {
    let _v_lambda = |v: &str| -> &str { v };
}
pnkfelix (Dec 04 2018 at 10:24, on Zulip):

in particular, from what I can tell, the region on the return type is a FreeRegion (specificially &ReScope(CallSite(10)) str), even though the argument type is BoundRegion (i.e. the type we have, prior to NLL region renumbering, is for<'r> fn(&ReLateBound(DebruinIndex(0)) str) -> &ReScope(CallSite(10)) str

pnkfelix (Dec 04 2018 at 10:25, on Zulip):

So here's the question I have: What is region-renumbering supposed to do here to handle this case? In the most general case, I would assume that we would like to be able to infer that the same region (introduced by the for <'r> binding) is used for both the input argument and also the return type.

pnkfelix (Dec 04 2018 at 10:27, on Zulip):

But... am I correct that NLL region inference does not look at the MIR for the closure body and its containing item simultaneously? If we are considering them independently, then I do not see how we could hope to infer a correct usable region from the containing item alone (at least not in the general case where the closure body could return other longer lived stuff; but maybe I should generalize the example to use &mut before I get subregion relations mixed in here)

pnkfelix (Dec 04 2018 at 10:31, on Zulip):

sorry, I realized I left out a step when I was trying to establish the context of the problem: The reason I pointed out that the region on the return type is a free region (and not the BoundRegion from the argument type) is that free regions get renumbered by the NLL region renumbering pass, effectively turning them into inference variables.

pnkfelix (Dec 04 2018 at 10:31, on Zulip):

and so what I am musing about is whether we should be handling region-renumbering of closure types in a more nuanced way.

pnkfelix (Dec 04 2018 at 10:32, on Zulip):

(or if the we need to change the way regions are assigned for the types of closures themselves at the outset, so that the region here would be the bound-region?)

nikomatsakis (Dec 04 2018 at 12:28, on Zulip):

@pnkfelix yes I figured this would be the problem -- I've not read all your comments yet but this has been an area with a lot of iteration (how to handle user type annot on closure)

pnkfelix (Dec 04 2018 at 12:28, on Zulip):

Note that this problem arises with no type annotation

pnkfelix (Dec 04 2018 at 12:29, on Zulip):

I just added that as a way to try to narrow down the problem

nikomatsakis (Dec 04 2018 at 12:29, on Zulip):

MIR does not (and I feel strongly that it should not) look at closure container and closure simultaneously

nikomatsakis (Dec 04 2018 at 12:29, on Zulip):

yes I figured that

nikomatsakis (Dec 04 2018 at 12:29, on Zulip):

right now, it is "normal" type check's job to define where lifetime binders appear

nikomatsakis (Dec 04 2018 at 12:29, on Zulip):

that's it's only lifetime-related job

pnkfelix (Dec 04 2018 at 12:29, on Zulip):

can/should we be analyzing the closure body and then using that to inform the region inference for the container?

nikomatsakis (Dec 04 2018 at 12:29, on Zulip):

we do that already

nikomatsakis (Dec 04 2018 at 12:29, on Zulip):

the closure passes up constraints to its creator

pnkfelix (Dec 04 2018 at 12:30, on Zulip):

well should we be doing it "better" ?

nikomatsakis (Dec 04 2018 at 12:30, on Zulip):

maybe? let me read in a bit more detail.

pnkfelix (Dec 04 2018 at 12:30, on Zulip):

I guess that may be more what I mean: Should I be looking at the format of the constraints and seeing if I can use them to properly infer a region to assign here

nikomatsakis (Dec 04 2018 at 12:31, on Zulip):

but if I recall what will happen here is:

the type annotation just enforces the view from "inside the closure", so it wouldn't change that, I don't think.

In which case the error for NLL is just "correct"

pnkfelix (Dec 04 2018 at 12:31, on Zulip):

no bound regions? So you don't expect to see ReEarlyBound ?

nikomatsakis (Dec 04 2018 at 12:32, on Zulip):

ReEarlyBound is not a bound region :P

pnkfelix (Dec 04 2018 at 12:32, on Zulip):

oh I misinterpreted

nikomatsakis (Dec 04 2018 at 12:32, on Zulip):

what I really meant is no late-bound regions...

nikomatsakis (Dec 04 2018 at 12:32, on Zulip):

well

nikomatsakis (Dec 04 2018 at 12:32, on Zulip):

sorry what I wrote is probably wrong

nikomatsakis (Dec 04 2018 at 12:32, on Zulip):

I mean it's correct

nikomatsakis (Dec 04 2018 at 12:32, on Zulip):

there would be no late-bound regions

nikomatsakis (Dec 04 2018 at 12:32, on Zulip):

but that's not the only relevant question here

pnkfelix (Dec 04 2018 at 12:32, on Zulip):

actually I might have meant ReLateBound

nikomatsakis (Dec 04 2018 at 12:32, on Zulip):

basically the question is whether the input/output have same region, wherever it is bound

nikomatsakis (Dec 04 2018 at 12:33, on Zulip):

anyway let me get a local build going

nikomatsakis (Dec 04 2018 at 12:33, on Zulip):

with some debuginfo

nikomatsakis (Dec 04 2018 at 12:33, on Zulip):

"closure annotations my old friend...I've come to talk with you again..."

nikomatsakis (Dec 04 2018 at 12:33, on Zulip):

if I could go back in time and make one change to Rust, sometimes I think it would be to give a hard error if closure signatures did not have an expected type (and not permit user annotation)

nikomatsakis (Dec 04 2018 at 12:34, on Zulip):

or at least to make an alternate, full-feature annotation form that must be completely specified

pnkfelix (Dec 04 2018 at 12:34, on Zulip):

...... you would not permit user annotation as the way to provide the expected type?

pnkfelix (Dec 04 2018 at 12:35, on Zulip):

anyway I don't mind continuing to poke at this; I feel like I want to sink my toes more deeply into this code again

nikomatsakis (Dec 04 2018 at 12:35, on Zulip):

well if we did I would require complete annotation like a fn

nikomatsakis (Dec 04 2018 at 12:35, on Zulip):

ok, I want to investigate a bit too just to have an informed opinion

nikomatsakis (Dec 04 2018 at 12:36, on Zulip):

right now we permit things like |x: u32| foo -- return type unannotated

nikomatsakis (Dec 04 2018 at 12:36, on Zulip):

etc

nikomatsakis (Dec 04 2018 at 12:36, on Zulip):

we also have no way to use named lifetimes

nikomatsakis (Dec 04 2018 at 12:36, on Zulip):

it's sort of a mess

pnkfelix (Dec 04 2018 at 12:36, on Zulip):

yes I agree wit hthat

nikomatsakis (Dec 04 2018 at 12:36, on Zulip):

I of course want there to be some way to specify types

pnkfelix (Dec 04 2018 at 12:36, on Zulip):

it would be nice to at least allow let f = for <'a> |arg: &'a T| -> &'a Answer { ... };

nikomatsakis (Dec 04 2018 at 12:37, on Zulip):

yes, presumably we'll ultimately use some syntax like that

pnkfelix (Dec 04 2018 at 12:37, on Zulip):

it is sort of amazing we've gotten this far without adding it as an unstable feature...

nikomatsakis (Dec 04 2018 at 12:37, on Zulip):

heh yes

pnkfelix (Dec 04 2018 at 13:24, on Zulip):

the closure passes up constraints to its creator

pnkfelix (Dec 04 2018 at 13:25, on Zulip):

So RegionConstraintData, or at least its doc comment, implies that in some places we can encode both conditions that must be verified in addition to assumptions that can be made

pnkfelix (Dec 04 2018 at 13:26, on Zulip):

But AFAICT the QueryRegionConstraint is not expressive enough to encode both kinds of things? I assume it is only passing along conditions that must be verified?

pnkfelix (Dec 04 2018 at 13:28, on Zulip):

(The RegionConstraintData I'm talking about is in rustc::infer::region_constraints, so perhaps it is not the right thing for me to be taking inspiration from. But I nonetheless was wondering whether QueryRegionConstraint should be expanded to allow one to encode assumptions that can be made, so that one might e.g. express "the region on the return type can be assumed to outlive the bound-region attached to the argument type")

pnkfelix (Dec 04 2018 at 13:32, on Zulip):

/me goes off to see if the rustc guide talks about this

pnkfelix (Dec 04 2018 at 14:24, on Zulip):

(its possible that the answer here is that it was an internal bug that AST-borrowck accepted this code in the first place. At least, I'm having difficultly understanding how the region it infers for the return type can make sense; and the code does get rejected if you start trying to call the closure...)

nikomatsakis (Dec 04 2018 at 15:33, on Zulip):

So RegionConstraintData, or at least its doc comment, implies that in some places we can encode both conditions that must be verified in addition to assumptions that can be made

I don't think we can presently encode that

nikomatsakis (Dec 04 2018 at 15:33, on Zulip):

I don't quite get why you want to yet but I'm in plenary so it's hard to concentrate

pnkfelix (Dec 04 2018 at 15:35, on Zulip):

Well its possible that all of this is arising from a different bug that matthewjasper identified

pnkfelix (Dec 04 2018 at 15:35, on Zulip):

see PR #56486

pnkfelix (Dec 04 2018 at 15:37, on Zulip):

(the reason I was thinking we'd want to encode Assumptions, in addition to constraints, is that I figured that when you are starting with for <'r> (&'r T1) -> &'_#1r T2 , that you might want to allow '_#1r to be unified with 'r

pnkfelix (Dec 04 2018 at 15:37, on Zulip):

but I figure in order to do that, you would need to propagate knowledge that the closure body actually allows that unification.

pnkfelix (Dec 04 2018 at 15:45, on Zulip):

(okay, no, PR #56486 alone will not fix this)

pnkfelix (Dec 05 2018 at 12:02, on Zulip):

more investigation: I've started looking at the nll.0.mir for the closure

pnkfelix (Dec 05 2018 at 12:02, on Zulip):

which, once I started understanding the equivalences, helps illuminate some things, I think

pnkfelix (Dec 05 2018 at 12:03, on Zulip):

in particular, the "Free Region Mapping" has two relevant entries for the running example let _v_lambda = |v: &str| { v };

pnkfelix (Dec 05 2018 at 12:03, on Zulip):

the relevant entries are:

| Free Region Mapping
| '_#0r | Global | ['_#0r, '_#4r, '_#1r, '_#2r, '_#3r]
| '_#1r | External | ['_#4r, '_#1r]
| '_#2r | Local | ['_#4r, '_#2r]
| '_#3r | Local | ['_#4r, '_#3r]
pnkfelix (Dec 05 2018 at 12:07, on Zulip):

and the type for the closure, when we start checking it (and after substituting in inferred-to-be equivalent regions to simplify presentation here): fn main::{{closure}}(_1: &'_#2r [closure-env], _2: &'_#3r str) -> &'_#1r str

nikomatsakis (Dec 05 2018 at 12:08, on Zulip):

argh I've failed to follow up on this

pnkfelix (Dec 05 2018 at 12:08, on Zulip):

so, okay, that means the closure gets the input _2: &'_#3r str and will just pass it back as the result '_#1r str

nikomatsakis (Dec 05 2018 at 12:08, on Zulip):

I can prob do that this morning once I find some more coffee

pnkfelix (Dec 05 2018 at 12:08, on Zulip):

so clearly that would mean _#3r needs to outlive '_#1r

nikomatsakis (Dec 05 2018 at 12:09, on Zulip):

(this damn hotel room only gives enough for 1 cup of :coffee: , but 2 cups of decaf... what's up with that...)

pnkfelix (Dec 05 2018 at 12:09, on Zulip):

but the thing I'm now trying to understand: the Free Region Mapping says that '_#1r is an External region

pnkfelix (Dec 05 2018 at 12:10, on Zulip):

which is meant to denote a region that is defined externally to the closure itself. E.g. if the containing item had a region param 'a or some such.

pnkfelix (Dec 05 2018 at 12:11, on Zulip):

I think that the Free Region Mapping is tagging '_#1r as an external region because prior to the remapping, it was initially region-inferred to be ReScope(CallSite(10))

pnkfelix (Dec 05 2018 at 12:11, on Zulip):

(I could be wrong about that hypothesis though)

pnkfelix (Dec 05 2018 at 12:12, on Zulip):

In any case, it doesn't seem right to tag the region here as External

pnkfelix (Dec 05 2018 at 12:13, on Zulip):

I think Bill Gates-Foldgers once famously said 8 oz of caffeinated coffee should be enough for anyone.

pnkfelix (Dec 05 2018 at 12:18, on Zulip):

In any case, it doesn't seem right to tag the region here as External

at least, not in this instance. There are straight-forward modifications to the closure body where it wouldn't be wrong to have an External region there (e.g. returning a &'static str or similarly sufficiently-long-lived reference)

nikomatsakis (Dec 05 2018 at 12:57, on Zulip):

In any case, it doesn't seem right to tag the region here as External

I think it is right

nikomatsakis (Dec 05 2018 at 12:58, on Zulip):

at least, according to the setup

nikomatsakis (Dec 05 2018 at 12:58, on Zulip):

in particular, given that we have no expected type, I think we will infer the types to be type variables

nikomatsakis (Dec 05 2018 at 12:58, on Zulip):

but let me review the logic...

nikomatsakis (Dec 05 2018 at 12:58, on Zulip):

(and that implies that the region is not late-bound, and therefore it is external)

nikomatsakis (Dec 05 2018 at 12:59, on Zulip):

well, we should respect the user given annotations

pnkfelix (Dec 05 2018 at 12:59, on Zulip):

let me rephrase

pnkfelix (Dec 05 2018 at 13:00, on Zulip):

if the region is treated as external

pnkfelix (Dec 05 2018 at 13:00, on Zulip):

then there's no way we can accept the code

pnkfelix (Dec 05 2018 at 13:00, on Zulip):

right?

pnkfelix (Dec 05 2018 at 13:01, on Zulip):

the closure has type for <'_3r> fn(&'_3r str) -> '_#1r str. If '_#1r is external (i.e., if we are assuming that it is some region that is defined by the container), then we cannot generally let '_3r be equated with '_#1r, and therefore we will be forced to reject the code.

pnkfelix (Dec 05 2018 at 13:02, on Zulip):

(which is what we do today.)

pnkfelix (Dec 05 2018 at 13:03, on Zulip):

My claim is that it would be better, if possible, for us to accept the code, by some means. I don't know if the right means would be to provide some way for '_#1r to be unified with _3r above, or if it is to avoid introducing '_#1r in the first place, or some other change.

pnkfelix (Dec 05 2018 at 13:06, on Zulip):

I agree that the return type of the closure is a type variable (not provided by the user). And the compiler manages to figure out that it has type &str. The debug output includes the line

DEBUG 2018-12-05T11:34:17Z: rustc_typeck::check::regionck: constrain_regions_in_type_of_node(ty=&'_#0r str, ty0=&'_#0r str, id=HirId { owner: DefIndex(0:3), local_id: 10 }, minimum_lifetime=ReScope(CallSite(10)))
pnkfelix (Dec 05 2018 at 13:07, on Zulip):

and I infer that that minimum_lifetime=ReScope(CallSite(10)) is what causes the compiler to eventually assign the closure the return type &ReScope(CallSite(10)) str

pnkfelix (Dec 05 2018 at 13:08, on Zulip):

my claim, however, is that the region ReScope(CallSite(10)) is an unusual ReScope

pnkfelix (Dec 05 2018 at 13:09, on Zulip):

in that it is, in my head at least, kind of an existential scope. It just represents some region that is a bit longer than the scope(s) for the closure arguments.

pnkfelix (Dec 05 2018 at 13:10, on Zulip):

to be clear, I meant that it is existential from the viewpoint while we are checking the body of the closure

pnkfelix (Dec 05 2018 at 13:11, on Zulip):

In some ways, I would like it if we could somehow treat such an ReScope(CallSite(10)) (assuming 10 is the ID for the closure we are currently checking) as if it were bound by the for <'r, ...>

pnkfelix (Dec 05 2018 at 13:11, on Zulip):

or something where every bound region introduced by that for <..> is known to outlive the ReScope(CallSite(10))

nikomatsakis (Dec 05 2018 at 13:14, on Zulip):

my claim, however, is that the region ReScope(CallSite(10)) is an unusual ReScope

I think this is irrelevant

pnkfelix (Dec 05 2018 at 13:15, on Zulip):

Ah okay lets discuss that then

nikomatsakis (Dec 05 2018 at 13:15, on Zulip):

this is not to disagree with any of your other claims

nikomatsakis (Dec 05 2018 at 13:15, on Zulip):

well let me back up

nikomatsakis (Dec 05 2018 at 13:15, on Zulip):

first off, I want to be clear about what code we are talking about

nikomatsakis (Dec 05 2018 at 13:16, on Zulip):

is it this example, or another one?

nikomatsakis (Dec 05 2018 at 13:16, on Zulip):
fn main() {
    let _v_lambda = |v: &str| -> &str { v };
}
nikomatsakis (Dec 05 2018 at 13:16, on Zulip):

I guess we are most interested in the original example:

nikomatsakis (Dec 05 2018 at 13:16, on Zulip):
fn main() {
    let _v_lambda = |v: &Vec<String>| v.iter().chain(v.iter()).collect::<Vec<&String>>();
}

fn _v_fn(v: &Vec<String>) -> Vec<&String> {
    v.iter().chain(v.iter()).collect::<Vec<&String>>()
}
pnkfelix (Dec 05 2018 at 13:16, on Zulip):

okay. My running example is:

fn main() {
    let _v_lambda = |v: &str| { v };
}
pnkfelix (Dec 05 2018 at 13:17, on Zulip):

which is very similar to the reduced case that I posted in a comment

pnkfelix (Dec 05 2018 at 13:17, on Zulip):

I think we should eventually discuss the original example

nikomatsakis (Dec 05 2018 at 13:17, on Zulip):

I think for the purposes of this discussion

nikomatsakis (Dec 05 2018 at 13:17, on Zulip):

your reduced example is probably equivalent

nikomatsakis (Dec 05 2018 at 13:18, on Zulip):

key point is: it has no return type annotation

pnkfelix (Dec 05 2018 at 13:18, on Zulip):

but in the interest of keeping the example minimal, I would like to focus on either |v: &str| -> &str { v } or |v: &str| { v }

pnkfelix (Dec 05 2018 at 13:18, on Zulip):

right. Lets assume no return type annotation.

nikomatsakis (Dec 05 2018 at 13:18, on Zulip):

it's a bit strange to me that the former doesn't work

nikomatsakis (Dec 05 2018 at 13:18, on Zulip):

but the only reason I expect it to work is by leaning on the return type annot

pnkfelix (Dec 05 2018 at 13:18, on Zulip):

neither works. They both infer &str

nikomatsakis (Dec 05 2018 at 13:18, on Zulip):

it might be worth investigating why that doesn't happen but sort of neither here nor there

pnkfelix (Dec 05 2018 at 13:19, on Zulip):

but they have no information to use for the region

nikomatsakis (Dec 05 2018 at 13:19, on Zulip):

I think the flaw with the first one, perhaps, is that for some reason it is not using lifetime elision rules

pnkfelix (Dec 05 2018 at 13:19, on Zulip):

and so regionck plugs in ReScope(CallSite(10)), which is subsequently replaced with _#1 in defining_ty

nikomatsakis (Dec 05 2018 at 13:19, on Zulip):

right

nikomatsakis (Dec 05 2018 at 13:19, on Zulip):

so the question is, how would we fix it?

pnkfelix (Dec 05 2018 at 13:20, on Zulip):

I wholeheartedly agree that we should explore whether the lifetime elision rules are supposed to be applied in the former case.

nikomatsakis (Dec 05 2018 at 13:20, on Zulip):

the reason that I said that ReScope was "irrelevant" is that those regions are erased

nikomatsakis (Dec 05 2018 at 13:20, on Zulip):

but it's not totally irrelevant

pnkfelix (Dec 05 2018 at 13:20, on Zulip):

(but again, we don't need to immediately explore that Q)

nikomatsakis (Dec 05 2018 at 13:20, on Zulip):

I wholeheartedly agree that we should explore whether the lifetime elision rules are supposed to be applied in the former case.

I think they should be, it's prob a bug I introduced when I reworked this stuff the last time

nikomatsakis (Dec 05 2018 at 13:20, on Zulip):

but yeah separate problem

nikomatsakis (Dec 05 2018 at 13:20, on Zulip):

right now, typeck has the job of determining bound vs free and that's it

pnkfelix (Dec 05 2018 at 13:20, on Zulip):

Yes, they are erased. But that erasing process plugs in an external-tagged region varaible '_#1r

nikomatsakis (Dec 05 2018 at 13:21, on Zulip):

and I think according to that scheme, NLL is giving a "proper" error here

nikomatsakis (Dec 05 2018 at 13:21, on Zulip):

so I could see two routes to fix this:

nikomatsakis (Dec 05 2018 at 13:21, on Zulip):
nikomatsakis (Dec 05 2018 at 13:21, on Zulip):
nikomatsakis (Dec 05 2018 at 13:21, on Zulip):

the latter is what I always hoped we'd find a way to do

nikomatsakis (Dec 05 2018 at 13:21, on Zulip):

but I also hoped not to think about it yet :)

nikomatsakis (Dec 05 2018 at 13:21, on Zulip):

Yes, they are erased. But that erasing process plugs in an external-tagged region varaible '_#1r

right, and correctly so (I claim)

pnkfelix (Dec 05 2018 at 13:22, on Zulip):

See that brings us back to my Q about CallSite's nature

pnkfelix (Dec 05 2018 at 13:22, on Zulip):

If the ReScope were describing a region statically defined by the containing item

pnkfelix (Dec 05 2018 at 13:22, on Zulip):

then I would agree that external would make sense

nikomatsakis (Dec 05 2018 at 13:22, on Zulip):

I don't think NLL should consider rescope at all

nikomatsakis (Dec 05 2018 at 13:23, on Zulip):

as something special

nikomatsakis (Dec 05 2018 at 13:23, on Zulip):

one could imagine though

pnkfelix (Dec 05 2018 at 13:23, on Zulip):

but that's not how I interpret the ReScope being used here. Its not an ReScope that is meaningful to the container; it is an ReScope that is meaningful to teh closure body alone

nikomatsakis (Dec 05 2018 at 13:23, on Zulip):

typeck concluding that a smaller scope can lead to a bound region

nikomatsakis (Dec 05 2018 at 13:23, on Zulip):

but this .. I don't like it, doesn't feel like it's moving in the right direction

nikomatsakis (Dec 05 2018 at 13:23, on Zulip):

since it still requires typeck to have complete constraints

pnkfelix (Dec 05 2018 at 13:23, on Zulip):

I agree that it doesn't feel right to have NLL inspect the ReScope

pnkfelix (Dec 05 2018 at 13:24, on Zulip):

in terms of the mechanics of it

pnkfelix (Dec 05 2018 at 13:24, on Zulip):

but in terms of the abstract semantics

pnkfelix (Dec 05 2018 at 13:24, on Zulip):

it just seems to me like the enum variant ReScope is being used to mean "this is a scope that had meaning within the container"

nikomatsakis (Dec 05 2018 at 13:25, on Zulip):

note that if the closure were actually invoked

pnkfelix (Dec 05 2018 at 13:25, on Zulip):

and that doesn't match my understanding of what it means in this case

nikomatsakis (Dec 05 2018 at 13:25, on Zulip):

I think you would no longer get that result

pnkfelix (Dec 05 2018 at 13:25, on Zulip):

I agree with that

nikomatsakis (Dec 05 2018 at 13:25, on Zulip):

it's basically just a bug of AST borrowck imo

pnkfelix (Dec 05 2018 at 13:25, on Zulip):

you get totally different results when you try to invoke the closure. Both AST-borrowck and NLL start rejecting the code then

nikomatsakis (Dec 05 2018 at 13:26, on Zulip):

(though it's perhaps debatable; depends I suppose on the specifics of how we lower things)

nikomatsakis (Dec 05 2018 at 13:27, on Zulip):

so, if we wanted to fix this failure of our inference

nikomatsakis (Dec 05 2018 at 13:27, on Zulip):

we're basically talking about adding a kind of "generalization" step

pnkfelix (Dec 05 2018 at 13:27, on Zulip):

the tail comments on #55526 have some back-and-forth between me and the issue filer where I was trying to find out whether they were starting from a code base where they were defining a closure and successfully calling it.

nikomatsakis (Dec 05 2018 at 13:27, on Zulip):

(did you figure that out?)

pnkfelix (Dec 05 2018 at 13:28, on Zulip):

(and AFAICT, they do not have such an example. The path they took to this bug was that they were not happy with the signature on a locally defined fn and so they tried replacing it with a closure.)

pnkfelix (Dec 05 2018 at 13:28, on Zulip):

and then they were surprised to see the closure get rejected.

pnkfelix (Dec 05 2018 at 13:28, on Zulip):

But I haven't gotten final confirmation on that version of the events

nikomatsakis (Dec 05 2018 at 13:29, on Zulip):

ok

pnkfelix (Dec 05 2018 at 13:29, on Zulip):

So

pnkfelix (Dec 05 2018 at 13:29, on Zulip):

in terms of potential fixes

pnkfelix (Dec 05 2018 at 13:29, on Zulip):

there are two very different paths I could imagine

nikomatsakis (Dec 05 2018 at 13:30, on Zulip):

that's somewhat relevant in that it suggests we can be more liesurely about things

nikomatsakis (Dec 05 2018 at 13:30, on Zulip):

and potentially aim to fix things "the right way"

nikomatsakis (Dec 05 2018 at 13:30, on Zulip):

whatever we decide that is

pnkfelix (Dec 05 2018 at 13:30, on Zulip):

one path would be focused on just trying to make us accept the code where the closure is not called

nikomatsakis (Dec 05 2018 at 13:30, on Zulip):

sounds not good :)

pnkfelix (Dec 05 2018 at 13:30, on Zulip):

right, I only mention it

pnkfelix (Dec 05 2018 at 13:30, on Zulip):

because, well

pnkfelix (Dec 05 2018 at 13:30, on Zulip):

I was wondering why, when a closure isn't called here

pnkfelix (Dec 05 2018 at 13:30, on Zulip):

why we don't infer that the return type has &'ReEmpty str

pnkfelix (Dec 05 2018 at 13:31, on Zulip):

since no one ever tries to read from it

nikomatsakis (Dec 05 2018 at 13:31, on Zulip):

well we have a constraint from the closure body

nikomatsakis (Dec 05 2018 at 13:31, on Zulip):

(in lexical inference)

nikomatsakis (Dec 05 2018 at 13:31, on Zulip):

that the return type must be in scope where the return occurs

nikomatsakis (Dec 05 2018 at 13:31, on Zulip):

something like that

pnkfelix (Dec 05 2018 at 13:31, on Zulip):

(and then the for <'r> fn(v: &'r str) -> &ReEmpty str { v } would pass, I assume, since `'r outlives ReEmpty)

pnkfelix (Dec 05 2018 at 13:31, on Zulip):

ah okya

nikomatsakis (Dec 05 2018 at 13:31, on Zulip):

I think we actually require that the return type outlives the fn body

nikomatsakis (Dec 05 2018 at 13:31, on Zulip):

I think you added that, even

pnkfelix (Dec 05 2018 at 13:31, on Zulip):

Never mind that then. :)

nikomatsakis (Dec 05 2018 at 13:31, on Zulip):

some time ago

pnkfelix (Dec 05 2018 at 13:32, on Zulip):

It was a shower thought.

nikomatsakis (Dec 05 2018 at 13:32, on Zulip):

in my ideal world

pnkfelix (Dec 05 2018 at 13:32, on Zulip):

A hypothetical "quick, minimal, ill-thought out fix"

nikomatsakis (Dec 05 2018 at 13:32, on Zulip):

we would have NLL borrowck take charge of inferring the binding sites for the regions

nikomatsakis (Dec 05 2018 at 13:32, on Zulip):

but I'm not sure how feasible that is

nikomatsakis (Dec 05 2018 at 13:33, on Zulip):

it depends at minimum on my universes PR landing

nikomatsakis (Dec 05 2018 at 13:33, on Zulip):

we need to be able to solve traits, in particular, without knowing the answer

pnkfelix (Dec 05 2018 at 13:33, on Zulip):

Here's another Q I have, before we get into long term solutions

nikomatsakis (Dec 05 2018 at 13:33, on Zulip):

go for it

pnkfelix (Dec 05 2018 at 13:33, on Zulip):

given that the closure's return type is inferred to be &'ReScope(CallSite(10)) str

pnkfelix (Dec 05 2018 at 13:33, on Zulip):

why is AST-borrowck accepting the body?

pnkfelix (Dec 05 2018 at 13:34, on Zulip):

oh

pnkfelix (Dec 05 2018 at 13:34, on Zulip):

wait that's a silly question. beucase the argument outlives CallSite(10)

nikomatsakis (Dec 05 2018 at 13:34, on Zulip):

it's because of a quirk of the definition of the WF relation for clsoures

nikomatsakis (Dec 05 2018 at 13:34, on Zulip):

also

nikomatsakis (Dec 05 2018 at 13:34, on Zulip):

which is debatably a bug

pnkfelix (Dec 05 2018 at 13:34, on Zulip):

hmm can you say more about this quirk?

nikomatsakis (Dec 05 2018 at 13:35, on Zulip):

note that this PR of mine https://github.com/rust-lang/rust/pull/55988 would make the ast borrowck reject just like NLL

nikomatsakis (Dec 05 2018 at 13:35, on Zulip):

hmm can you say more about this quirk?

why yes I can

nikomatsakis (Dec 05 2018 at 13:36, on Zulip):

in particular, the closure outlives rules here:

https://github.com/rust-lang/rust/blob/21cb46a6e94906c2fdfda4df33c822ba28422b7f/src/librustc/ty/outlives.rs#L74-L78

nikomatsakis (Dec 05 2018 at 13:36, on Zulip):

just look at the "upvar types" from the closure

nikomatsakis (Dec 05 2018 at 13:36, on Zulip):

ignoring the regions on the closure signature

nikomatsakis (Dec 05 2018 at 13:36, on Zulip):

I think...this is probably just a bug

nikomatsakis (Dec 05 2018 at 13:36, on Zulip):

the more I think about it

pnkfelix (Dec 05 2018 at 13:37, on Zulip):

okay

nikomatsakis (Dec 05 2018 at 13:37, on Zulip):

possibly relevant refresher on ClosureSubsts

nikomatsakis (Dec 05 2018 at 13:37, on Zulip):

so anyway the reason this matters is

pnkfelix (Dec 05 2018 at 13:37, on Zulip):

if it is likely that we are going to move to a world where AST-borrowck rejects this code

nikomatsakis (Dec 05 2018 at 13:37, on Zulip):

the closure creator requires its type to outlive the local variable in which it is stored

nikomatsakis (Dec 05 2018 at 13:37, on Zulip):

but that does not affect the return type

pnkfelix (Dec 05 2018 at 13:37, on Zulip):

(and justify it by saying "this is a soundness fix")

nikomatsakis (Dec 05 2018 at 13:37, on Zulip):

because of this def'n of "outlives"

pnkfelix (Dec 05 2018 at 13:37, on Zulip):

I'm super-inclined to run with that.

nikomatsakis (Dec 05 2018 at 13:37, on Zulip):

yes

nikomatsakis (Dec 05 2018 at 13:37, on Zulip):

basically my take is:

nikomatsakis (Dec 05 2018 at 13:37, on Zulip):

given the existing limitations of the system

nikomatsakis (Dec 05 2018 at 13:37, on Zulip):

this code ought to be rejected

nikomatsakis (Dec 05 2018 at 13:37, on Zulip):

but those limitations are unfortunate

nikomatsakis (Dec 05 2018 at 13:38, on Zulip):

and -- at some point -- it would be nice to do better

pnkfelix (Dec 05 2018 at 13:38, on Zulip):

okay.

nikomatsakis (Dec 05 2018 at 13:38, on Zulip):

that said, I think that the |x: &str| -> &str should work

nikomatsakis (Dec 05 2018 at 13:38, on Zulip):

and that I think is a bug

pnkfelix (Dec 05 2018 at 13:38, on Zulip):

So that would be part 1: reject the code when no return tyep annotation is provided

pnkfelix (Dec 05 2018 at 13:38, on Zulip):

and then, yes, part 2 is get us to accept |x: &str| -> &str

pnkfelix (Dec 05 2018 at 13:38, on Zulip):

I think

nikomatsakis (Dec 05 2018 at 13:39, on Zulip):

seems right

nikomatsakis (Dec 05 2018 at 13:39, on Zulip):

the code for handling closure signatures is a bit separate I guess I forgot about elision or something

nikomatsakis (Dec 05 2018 at 13:39, on Zulip):

it took a while to figure out how to reconcile expected types + user-supplied signatures

nikomatsakis (Dec 05 2018 at 13:39, on Zulip):

we used to do some really ridiculously bogus stuff :)

pnkfelix (Dec 05 2018 at 13:40, on Zulip):

(though it continues to be a little odd that we would take that approach, at least to me ... since, if we can apply lifetime elision rules and get a usable result ... why not use those same rules for an inferred -> &str ?)

nikomatsakis (Dec 05 2018 at 13:40, on Zulip):

(in this case, of course, there is no expected signature)

nikomatsakis (Dec 05 2018 at 13:40, on Zulip):

(though it continues to be a little odd that we would take that approach, at least to me ... since, if we can apply lifetime elision rules and get a usable result ... why not use those same rules for an inferred -> &str ?)

well note that |x: &str| -> _ { .. } would still error

nikomatsakis (Dec 05 2018 at 13:40, on Zulip):

and this is what |x: &str| ... is equivalent to

pnkfelix (Dec 05 2018 at 13:40, on Zulip):

yes, but I guess it still feels strange, since the error you get will tend to make it clear that the compiler managed to plug in -> &str

nikomatsakis (Dec 05 2018 at 13:41, on Zulip):

I did say it was unfortunate

nikomatsakis (Dec 05 2018 at 13:41, on Zulip):

it comes down to the order of the exists and forall binders, of course

pnkfelix (Dec 05 2018 at 13:41, on Zulip):

is the justification essentiallythat the lifetime elision rules are meant to be syntactic in some sense ?

nikomatsakis (Dec 05 2018 at 13:41, on Zulip):

yes

pnkfelix (Dec 05 2018 at 13:41, on Zulip):

and that if there is no surface syntax showing the &T, then we're going to pretend like we couldn't have done better?

nikomatsakis (Dec 05 2018 at 13:42, on Zulip):

I don't consider it pretending, but yes

nikomatsakis (Dec 05 2018 at 13:42, on Zulip):

I guess I would explain like this:

nikomatsakis (Dec 05 2018 at 13:42, on Zulip):
nikomatsakis (Dec 05 2018 at 13:42, on Zulip):
nikomatsakis (Dec 05 2018 at 13:43, on Zulip):
nikomatsakis (Dec 05 2018 at 13:43, on Zulip):

it would be nice to lift the second restriction, but it's a "feature request" basically

pnkfelix (Dec 05 2018 at 13:43, on Zulip):

do we have '_ at this point? What would let v_lambda: |x: &str| -> &'_ str { x } do?

nikomatsakis (Dec 05 2018 at 13:43, on Zulip):

that would be equivalent to &str

nikomatsakis (Dec 05 2018 at 13:44, on Zulip):

'_ in a closure signature does not create a variable

nikomatsakis (Dec 05 2018 at 13:44, on Zulip):

but rather applies elision rules

pnkfelix (Dec 05 2018 at 13:44, on Zulip):

okay.

nikomatsakis (Dec 05 2018 at 13:45, on Zulip):

one "flaw" in my explanation is that _ in a closure body can refer to late-bound things

nikomatsakis (Dec 05 2018 at 13:45, on Zulip):

I put "flaw" in quotes because my explanation is not wrong

nikomatsakis (Dec 05 2018 at 13:45, on Zulip):

just that the current behavior is not especially intuitive nor useful

pnkfelix (Dec 05 2018 at 13:45, on Zulip):

right, its just a subtle distinction between closure signature and closure body?

nikomatsakis (Dec 05 2018 at 13:46, on Zulip):

yeah

nikomatsakis (Dec 05 2018 at 13:46, on Zulip):

though also '_ behaves differently etc

nikomatsakis (Dec 05 2018 at 13:46, on Zulip):

so the fact that signature vs body have a distinction doesn't seem itself bad

pnkfelix (Dec 05 2018 at 13:46, on Zulip):

Okay. So it sounds like we have identified two work items here

pnkfelix (Dec 05 2018 at 13:47, on Zulip):

1. For the |v: &str| { v } case, we'll let NLL continue to break. And (maybe? hopefully?) we'll also break AST-borrowck in that case, for consistency?

pnkfelix (Dec 05 2018 at 13:47, on Zulip):

2. For the |v: &str| -> &str { v } case, I'll start trying to identify why the lifetime elision rules aren't making it work out of the box.

pnkfelix (Dec 05 2018 at 13:48, on Zulip):

(regarding 2., I hypothesized a month ago that lifetime elision might be related to this...)

pnkfelix (Dec 05 2018 at 13:49, on Zulip):

Do the above two points sound right to you? And since you already have a PR that you say breaks AST-borrowck on case 1., we'll just let the debate carry on in PR #55988 ?

nikomatsakis (Dec 05 2018 at 13:51, on Zulip):

that sounds correct to me

pnkfelix (Dec 05 2018 at 14:13, on Zulip):

Hmm there's this comment that may contradict our hypothesis about the -> &str case: https://github.com/rust-lang/rust/blob/master/src/librustc/middle/resolve_lifetime.rs#L2112

pnkfelix (Dec 05 2018 at 14:13, on Zulip):
// Everything else (only closures?) doesn't
// actually enjoy elision in return types.
nikomatsakis (Dec 05 2018 at 14:14, on Zulip):

we certainly used to use elision

pnkfelix (Dec 05 2018 at 14:14, on Zulip):

time to go read the lifetime elision RFC(s).

nikomatsakis (Dec 05 2018 at 14:14, on Zulip):

I'm not sure what that "only closures?" would mean

pnkfelix (Dec 05 2018 at 14:14, on Zulip):

oh

pnkfelix (Dec 05 2018 at 14:14, on Zulip):

its right before a catch-all

nikomatsakis (Dec 05 2018 at 14:14, on Zulip):

but I think it means "also closures" =)

nikomatsakis (Dec 05 2018 at 14:15, on Zulip):

regardless I think that all fn sigs currently use elision

pnkfelix (Dec 05 2018 at 14:15, on Zulip):

as in, I think the author thought the only remaining case that could occur in the catch-all was closures.

nikomatsakis (Dec 05 2018 at 14:15, on Zulip):

and I cannot imagine why closures would be exempted

nikomatsakis (Dec 05 2018 at 14:15, on Zulip):

I see

nikomatsakis (Dec 05 2018 at 14:15, on Zulip):

well, I consider that comment wrong, but it's worth digging a bit more

pnkfelix (Dec 05 2018 at 14:16, on Zulip):

the comment seems to correctly reflect what the code does.

pnkfelix (Dec 05 2018 at 14:16, on Zulip):

but it may be trivial to fix this. :)

pnkfelix (Dec 05 2018 at 14:16, on Zulip):

(as in, change the code and bring comment in sync with revised expectations)

pnkfelix (Dec 06 2018 at 09:28, on Zulip):

This is a bummer: applying lifetime elision to -> &T on closure expressions might break code. (But maybe I can make my patch a bit smarter here, and only try to handle the return type if the closure expressions also had argument types...?)

nikomatsakis (Dec 06 2018 at 13:51, on Zulip):

I was wondering about those examples

nikomatsakis (Dec 06 2018 at 13:51, on Zulip):

er, about examples like that

nikomatsakis (Dec 06 2018 at 13:51, on Zulip):

that is, after signing off yesterday I got to thinking about all the edge cases

nikomatsakis (Dec 06 2018 at 13:52, on Zulip):

right now, if we have an expected type, something like |x: &str, y: &str| is setup so that the regions kind of 'don't matter'

nikomatsakis (Dec 06 2018 at 13:52, on Zulip):

e.g., the expected signature might be fn(&'a str, &'a str)

nikomatsakis (Dec 06 2018 at 13:52, on Zulip):

but x: &str, y: &str is "short for" &'a str, &'b str, in some sense

nikomatsakis (Dec 06 2018 at 13:53, on Zulip):

basically we are saying something like: there exists a subst S such that S[provided-signature] = expected-signature

nikomatsakis (Dec 06 2018 at 13:53, on Zulip):

applying elision makes this break in some cases

nikomatsakis (Dec 06 2018 at 13:53, on Zulip):

e.g., |&str| -> &str -- or the example you gave, of course

nikomatsakis (Dec 06 2018 at 13:53, on Zulip):

I am trying to reason in my head if there is an interpretation that is compatible with both

nikomatsakis (Dec 06 2018 at 13:53, on Zulip):

e.g., applying elision -- but only if there is no expected type? feels like a mess

nikomatsakis (Dec 06 2018 at 13:54, on Zulip):

so maybe this answers my question? it still feels sort of wrong that elision doesn't trigger though

nikomatsakis (Dec 06 2018 at 13:56, on Zulip):

(just summarized this in the issue)

pnkfelix (Dec 06 2018 at 13:59, on Zulip):

In any case this is definitely starting to feel like "any change here will need an RFC" ...

pnkfelix (Dec 06 2018 at 13:59, on Zulip):

well, not any change

pnkfelix (Dec 06 2018 at 14:00, on Zulip):

e.g. if we can hold off on fixing this and instead push on having NLL infer when to put references to bound regions (right? that's what you had mused about yesterday I think?)

pnkfelix (Dec 06 2018 at 14:00, on Zulip):

then that has potential to be very much a DWIM solution that might bypass an RFC. Or it might be even more in need of an RFC. :)

pnkfelix (Dec 06 2018 at 14:06, on Zulip):

(just summarized this in the issue)

In that comment, you meant to write for <'a, 'b> fn (&'a str, &'b str), not for <'a> fn (&'a str, &'b str), right?

pnkfelix (Dec 06 2018 at 14:06, on Zulip):

@nikomatsakis ^

nikomatsakis (Dec 06 2018 at 14:06, on Zulip):

probably

nikomatsakis (Dec 06 2018 at 14:06, on Zulip):

yes

nikomatsakis (Dec 06 2018 at 14:07, on Zulip):

In any case this is definitely starting to feel like "any change here will need an RFC" ...

it does feel like an area where a "quasi-retroactive" RFC might be nice

nikomatsakis (Dec 06 2018 at 14:07, on Zulip):

i.e., let's lay out a proposed design, document how it aligns with current behavior, document where it diverges and the expected impact

nikomatsakis (Dec 06 2018 at 14:07, on Zulip):

I feel like we should be doing this gradually for lots of parts of the language

nikomatsakis (Dec 06 2018 at 14:07, on Zulip):

might be a good "proving ground" for such a concept

pnkfelix (Dec 06 2018 at 14:08, on Zulip):

I do wonder whether closures are sufficiently different from fn items that the standard arguments for the existing lifetime elision rules simply don't hold up

pnkfelix (Dec 06 2018 at 14:08, on Zulip):

i.e the ability to return free variables that use free regions

nikomatsakis (Dec 06 2018 at 14:10, on Zulip):

so

nikomatsakis (Dec 06 2018 at 14:10, on Zulip):

another nice thing we should address

nikomatsakis (Dec 06 2018 at 14:10, on Zulip):

is the ability to specify explicit signatures

nikomatsakis (Dec 06 2018 at 14:10, on Zulip):

with named lifetimes

nikomatsakis (Dec 06 2018 at 14:10, on Zulip):

maybe the answer would then be that elision applies in that case

nikomatsakis (Dec 06 2018 at 14:10, on Zulip):

where you are using the "fully explicit form"

nikomatsakis (Dec 06 2018 at 14:10, on Zulip):

and maybe we just deprecate return type annotations outside of that form

nikomatsakis (Dec 06 2018 at 14:10, on Zulip):

and hence sidestep the issue

nikomatsakis (Dec 06 2018 at 14:10, on Zulip):

something like that

nikomatsakis (Dec 06 2018 at 14:11, on Zulip):

I don't know what this form would look like

pnkfelix (Dec 06 2018 at 14:11, on Zulip):

with named lifetimes

is this the same as what I recently wrote?

pnkfelix (Dec 06 2018 at 14:11, on Zulip):

no, I think you mean something more subtle.

nikomatsakis (Dec 06 2018 at 14:13, on Zulip):

well what you wrote is along similar lines

nikomatsakis (Dec 06 2018 at 14:14, on Zulip):

I do wonder whether closures are sufficiently different from fn items that the standard arguments for the existing lifetime elision rules simply don't hold up

it may be true, but there is a strong sort of 'consistency argument' going on too. I don't know how to square those things.

nikomatsakis (Dec 06 2018 at 14:15, on Zulip):

@pnkfelix if I understood what you wrote, you're saying "maybe we should just give people the ability to use named lifetimes and then have them use it instead of elision"

nikomatsakis (Dec 06 2018 at 14:15, on Zulip):

right?

pnkfelix (Dec 06 2018 at 14:15, on Zulip):

yes, the ability to name the lifetime parameters on a closure

nikomatsakis (Dec 06 2018 at 14:15, on Zulip):

I could imagine e.g. deprecating closure return type annotations that elide lifetimes

nikomatsakis (Dec 06 2018 at 14:15, on Zulip):

(if they don't follow elision)

nikomatsakis (Dec 06 2018 at 14:16, on Zulip):

and thus guiding people towards explicit named lifetimes, as you suggested

pnkfelix (Dec 06 2018 at 14:16, on Zulip):

Right. I don't know what we'd do about elision after that point.

nikomatsakis (Dec 06 2018 at 14:16, on Zulip):

I was hoping for some syntax that would also give you explicit, but I have no idea what that would be

pnkfelix (Dec 06 2018 at 14:16, on Zulip):

I don't understand what you mean by "would also give you explicit"

pnkfelix (Dec 06 2018 at 14:18, on Zulip):

maybe I need to rephrase: where does for <'r1, 'r2> |a1: &'r1 _, a2: TyCtor<'r2, _>| -> &'r2 _ { ... } fall down?

pnkfelix (Dec 06 2018 at 14:18, on Zulip):

because i have been assuming that gives you all the explicitness you might need

pnkfelix (Dec 06 2018 at 14:19, on Zulip):

(modulo potentially having to plug in more concrete type information into the _'s that I put into that example.)

nikomatsakis (Dec 06 2018 at 14:21, on Zulip):

because i have been assuming that gives you all the explicitness you might need

it does

nikomatsakis (Dec 06 2018 at 14:21, on Zulip):

my concern is just that &str -> &str in closures behaves differently than everywhere else

nikomatsakis (Dec 06 2018 at 14:21, on Zulip):

like, any other place that a -> appears

nikomatsakis (Dec 06 2018 at 14:22, on Zulip):

so I would like that to be deprecated, if it's not going to mean the same thing

pnkfelix (Dec 06 2018 at 14:22, on Zulip):

right. But my claim is that closures are the only place where the -> &str can have a useful lifetime attached to it that isn't the argument's

pnkfelix (Dec 06 2018 at 14:22, on Zulip):

Ah okay

nikomatsakis (Dec 06 2018 at 14:22, on Zulip):

that's not true

nikomatsakis (Dec 06 2018 at 14:22, on Zulip):

e.g.,

trait Foo<'a> {
  fn bar(&self) -> &'a u32;
}
pnkfelix (Dec 06 2018 at 14:22, on Zulip):

okay yes methods are the counter example.

nikomatsakis (Dec 06 2018 at 14:23, on Zulip):

this is of course what closures are desugaring to

nikomatsakis (Dec 06 2018 at 14:23, on Zulip):

in some sense

nikomatsakis (Dec 06 2018 at 14:23, on Zulip):

not literally that, I just mean "methods"

pnkfelix (Dec 06 2018 at 14:23, on Zulip):

right

nikomatsakis (Dec 06 2018 at 14:23, on Zulip):

"that refer to lifetimes found in the input types of the trait" :)

nikomatsakis (Dec 06 2018 at 14:24, on Zulip):

anyway what I was imaging ws something like this:

nikomatsakis (Dec 06 2018 at 14:24, on Zulip):

you could imagine saying

nikomatsakis (Dec 06 2018 at 14:24, on Zulip):

well hold up

nikomatsakis (Dec 06 2018 at 14:24, on Zulip):

so

nikomatsakis (Dec 06 2018 at 14:24, on Zulip):

one bit of complexity -- as I wrote -- is that right now

nikomatsakis (Dec 06 2018 at 14:24, on Zulip):

we have to reconcile the "expected signature" and the "provided signature"

nikomatsakis (Dec 06 2018 at 14:25, on Zulip):

and we currently give "primacy" to the expected signature if it is available

nikomatsakis (Dec 06 2018 at 14:25, on Zulip):

(make sense?)

nikomatsakis (Dec 06 2018 at 14:25, on Zulip):

what I was imagining was: maybe there is some syntax that lets you provide the full, explicit signature of the closure (no _ or inference)

nikomatsakis (Dec 06 2018 at 14:25, on Zulip):

and that if you use this syntax, you get elision

nikomatsakis (Dec 06 2018 at 14:25, on Zulip):

but I don't know what that syntax is

pnkfelix (Dec 06 2018 at 14:25, on Zulip):

i think so... though I had been assuming that we still enforce some notion of compatibility between the expected sig and the provided sig

nikomatsakis (Dec 06 2018 at 14:26, on Zulip):

yes we enforce that the provided sig is an "instance" of the expected sig

pnkfelix (Dec 06 2018 at 14:26, on Zulip):

okay yes that the substitution you had talked about, okay

nikomatsakis (Dec 06 2018 at 14:26, on Zulip):

as a (horrible) strawperson, imagine

|x| as fn(&str) -> &str { x }
pnkfelix (Dec 06 2018 at 14:27, on Zulip):

what I was imagining was: maybe there is some syntax that lets you provide the full, explicit signature of the closure (no _ or inference)

this phrasing continues to be confusing. the crucial word its missing, I think, is you want some new syntax.

nikomatsakis (Dec 06 2018 at 14:27, on Zulip):

anyway what you suggested (plus maybe deprecation) maybe ultimately a better plan

nikomatsakis (Dec 06 2018 at 14:27, on Zulip):

ah, yes, some new syntax :)

pnkfelix (Dec 06 2018 at 14:27, on Zulip):

i.e. one can encode explicit signature using existing syntax, but our whole problem is that we have a given semantics for how elision operates on (or rather, ignores) that syntax.

nikomatsakis (Dec 06 2018 at 14:27, on Zulip):

well that and

nikomatsakis (Dec 06 2018 at 14:28, on Zulip):

the current syntax can be incomplete

nikomatsakis (Dec 06 2018 at 14:28, on Zulip):

it permits you to annotate one parameter and not the other, etc

nikomatsakis (Dec 06 2018 at 14:28, on Zulip):

which gets us into trouble and makes it hard to see how to apply elision

pnkfelix (Dec 06 2018 at 14:28, on Zulip):

yes, but I figured it was "obvious" that one could restrict one's attention to only when all annotations are provided.

nikomatsakis (Dec 06 2018 at 14:28, on Zulip):

e.g., your example of |x| -> &str { .. }

nikomatsakis (Dec 06 2018 at 14:28, on Zulip):

well, that's not obvious to me :)

nikomatsakis (Dec 06 2018 at 14:29, on Zulip):

i.e., does elision start to apply when "all annotations" are provided?

pnkfelix (Dec 06 2018 at 14:29, on Zulip):

i.e. |x: _| -> &str { ... } would not have elision applied

pnkfelix (Dec 06 2018 at 14:29, on Zulip):

but |x: &'_ str| -> &str { ... } would.

nikomatsakis (Dec 06 2018 at 14:29, on Zulip):

right, we could go that way

nikomatsakis (Dec 06 2018 at 14:29, on Zulip):

it seems a bit surprising

nikomatsakis (Dec 06 2018 at 14:29, on Zulip):

but I don't know that there's a perfect answer here

pnkfelix (Dec 06 2018 at 14:29, on Zulip):

yes, it would be surprising

pnkfelix (Dec 06 2018 at 14:29, on Zulip):

but then again

nikomatsakis (Dec 06 2018 at 14:29, on Zulip):

that said, I think that explicit return type annot are unusual

pnkfelix (Dec 06 2018 at 14:30, on Zulip):

I was surprised that you expected the introduction of -> &str to make a difference.

nikomatsakis (Dec 06 2018 at 14:30, on Zulip):

hence my thought "maybe we can just deprecate elision in that position"

nikomatsakis (Dec 06 2018 at 14:30, on Zulip):

and instead require you to use an explicit syntax

nikomatsakis (Dec 06 2018 at 14:30, on Zulip):

I was surprised that you expected the introduction of -> &str to make a difference.

you mean way back in the original example?

pnkfelix (Dec 06 2018 at 14:30, on Zulip):

as in, if you have a return type, '_ (explicit or implicit) is unacceptable?

nikomatsakis (Dec 06 2018 at 14:30, on Zulip):

right

nikomatsakis (Dec 06 2018 at 14:31, on Zulip):

I mean it returns its current meaning, presumably, but since that is different from any other return type

pnkfelix (Dec 06 2018 at 14:31, on Zulip):

I was surprised that you expected the introduction of -> &str to make a difference.

you mean way back in the original example?

yes. That is my way of just saying we already have a "surprise budget"

nikomatsakis (Dec 06 2018 at 14:31, on Zulip):

well

nikomatsakis (Dec 06 2018 at 14:31, on Zulip):

I view that as a "quasi-bug"

nikomatsakis (Dec 06 2018 at 14:31, on Zulip):

i.e., when you have -> _ in a closure, we presently require a type that is "early bound"

nikomatsakis (Dec 06 2018 at 14:32, on Zulip):

but I think it would be better if we could infer a type that may refer to argument lifetimes

nikomatsakis (Dec 06 2018 at 14:32, on Zulip):

(in which case maybe -> &str would be ok?)

pnkfelix (Dec 06 2018 at 14:32, on Zulip):

.... okay and that would bring us back to the POV that I was trying to espouse

pnkfelix (Dec 06 2018 at 14:32, on Zulip):

when we were speaking yesterday...

nikomatsakis (Dec 06 2018 at 14:33, on Zulip):

er by which I mean: maybe -> &str would no longer be surprising, because it works "sort of like" elision, even though in fact it's more general. By "sort of like" I mean that: we are checking that the signature you gave is "an instance" of what was needed, and so if elision (&'a str -> &'a str) was needed, it would be an instance of that

pnkfelix (Dec 06 2018 at 14:34, on Zulip):

huh

pnkfelix (Dec 06 2018 at 14:34, on Zulip):

so rather than uniformly applying elision in ... syntactic (?) manner ...

pnkfelix (Dec 06 2018 at 14:35, on Zulip):

we would potentially check different instantiations of the generalized signature, until we found something that worked?

nikomatsakis (Dec 06 2018 at 14:35, on Zulip):

no

nikomatsakis (Dec 06 2018 at 14:36, on Zulip):

I mean maybe yes but that mechanism suggests some sort of "search"

pnkfelix (Dec 06 2018 at 14:36, on Zulip):

That's why I said "huh"

pnkfelix (Dec 06 2018 at 14:36, on Zulip):

Adding "more" search to rustc seems bad. :)

nikomatsakis (Dec 06 2018 at 14:36, on Zulip):

I am just saying that if our inference was able to do (the equivalent of) "let generalization"

nikomatsakis (Dec 06 2018 at 14:37, on Zulip):

it would make |x: &str| -> &str { x } infer to an elision signature

pnkfelix (Dec 06 2018 at 14:37, on Zulip):

okay that might be an analogy I could understand

nikomatsakis (Dec 06 2018 at 14:37, on Zulip):

even though it would also accept |x: &str, y: &str| -> &str { x }

nikomatsakis (Dec 06 2018 at 14:37, on Zulip):

which elision would not

nikomatsakis (Dec 06 2018 at 14:37, on Zulip):

(that would be an error outside of a closure)

pnkfelix (Dec 06 2018 at 14:38, on Zulip):

mmm

nikomatsakis (Dec 06 2018 at 14:38, on Zulip):

(all of this is assuming no "expected signature" of course)

pnkfelix (Dec 06 2018 at 14:38, on Zulip):

maybe revise those examples to put ... in the body

nikomatsakis (Dec 06 2018 at 14:38, on Zulip):

no

nikomatsakis (Dec 06 2018 at 14:38, on Zulip):

the bdoy is important

pnkfelix (Dec 06 2018 at 14:38, on Zulip):

that is, I want to be clear about when our rules use the body text

nikomatsakis (Dec 06 2018 at 14:38, on Zulip):

the point is

nikomatsakis (Dec 06 2018 at 14:38, on Zulip):

we would type-check the body

pnkfelix (Dec 06 2018 at 14:38, on Zulip):

Okay so then you are willing to use the body text in this case

nikomatsakis (Dec 06 2018 at 14:38, on Zulip):

we would observe the resulting constraints

nikomatsakis (Dec 06 2018 at 14:38, on Zulip):

we would observe that the return type lifetime only flows from the input x

nikomatsakis (Dec 06 2018 at 14:39, on Zulip):

and hence infer for<'a> (&'a u32, &'b u32) -> &'a u32 as the signature of the closure

nikomatsakis (Dec 06 2018 at 14:39, on Zulip):

(this is how "let generalization" works)

nikomatsakis (Dec 06 2018 at 14:39, on Zulip):

essentially once you've type-checked the closure body, you examine the constraints that arose from that

nikomatsakis (Dec 06 2018 at 14:39, on Zulip):

(in the case of ML, you do it at the point of let func arg = body, hence the term "let generalization")

pnkfelix (Dec 06 2018 at 14:40, on Zulip):

but how far does this go? what does |x: &str, y: &str| -> &str { if TEST { x } else { y } } do in your scheme?

nikomatsakis (Dec 06 2018 at 14:40, on Zulip):

it infers x: &'a str, y: &'a str -> &'a str

nikomatsakis (Dec 06 2018 at 14:40, on Zulip):

presumably

nikomatsakis (Dec 06 2018 at 14:40, on Zulip):

I don't know how to do this, mind you, I just feel like we "should" be able to do it, at least some of the time

nikomatsakis (Dec 06 2018 at 14:40, on Zulip):

that is what we would need to accept the original code I think

pnkfelix (Dec 06 2018 at 14:41, on Zulip):

okay

nikomatsakis (Dec 06 2018 at 14:41, on Zulip):

the only reason it's complicated is subtyping I guess

nikomatsakis (Dec 06 2018 at 14:41, on Zulip):

so e.g. your example probably really infers

nikomatsakis (Dec 06 2018 at 14:41, on Zulip):

for<'a, 'b, 'c> (x: &'a str, y: &'b str) -> &'c str where 'a: 'c, 'b: 'c

nikomatsakis (Dec 06 2018 at 14:41, on Zulip):

or at least wants to

nikomatsakis (Dec 06 2018 at 14:42, on Zulip):

but I don't know if it can because of how the closure trait is setup...

nikomatsakis (Dec 06 2018 at 14:42, on Zulip):

er, I think we could

nikomatsakis (Dec 06 2018 at 14:42, on Zulip):

those where clauses conceptually go on the impl<'a, 'b> Fn<(&'a str, &'b str)> for ClosureType

nikomatsakis (Dec 06 2018 at 14:43, on Zulip):

well, the challenge is that 'c can't appear in the type Output since it does not appear in an input type

nikomatsakis (Dec 06 2018 at 14:43, on Zulip):

anyway, this is non-trivial, clearly :)

pnkfelix (Dec 06 2018 at 14:44, on Zulip):

for <'a, 'b> (x: &'a str, y: &'b str) -> &'(a \isect b) str

pnkfelix (Dec 06 2018 at 14:45, on Zulip):

because, you know, Intersection types are so simple

nikomatsakis (Dec 06 2018 at 14:46, on Zulip):

right..

nikomatsakis (Dec 06 2018 at 14:46, on Zulip):

I was thinking about that cool paper, I forget the authors, where they extended HM inference with subtyping by tracking upper/lower bounds, then modeled types as finite state machines and applied finite state machine minimization techniques to try and keep the type size under control

nikomatsakis (Dec 06 2018 at 14:46, on Zulip):

I've been meaning to re-implement that scheme in my spare time at some point

nikomatsakis (Dec 06 2018 at 14:46, on Zulip):

so I can actually understand it

nikomatsakis (Dec 06 2018 at 14:46, on Zulip):

"copious spare time" is perhaps the right phrase :P

Last update: Nov 21 2019 at 14:20UTC