New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resolve: the relative order of items and statements in a block can be important #33118

Open
jseyfried opened this Issue Apr 20, 2016 · 18 comments

Comments

Projects
None yet
6 participants
@jseyfried
Contributor

jseyfried commented Apr 20, 2016

For example,

fn foo() {}

fn f1() {
    fn bar() {
        foo(); // This resolves to the item
    }
    let foo = 0;
}

fn f2() {
    let foo = 0;
    fn bar() {
        foo(); // This is an error (cannot capture dynamic environment in a fn item)
    }
}

Since items in a block are above the block's local variables in the scope hierarchy, it might make sense to resolve the items' bodies in scopes that descend directly from the scope of the block's items, bypassing the scopes of the local variables.

That being said, I think I prefer the current behavior. However, since it will mildly complicate name resolution in the future, I wanted to make sure that we actually want this behavior.

@jseyfried

This comment has been minimized.

Contributor

jseyfried commented Apr 20, 2016

@petrochenkov

This comment has been minimized.

Contributor

petrochenkov commented Apr 21, 2016

My intuition says that function bodies (and constant initializers, etc) should be isolated from local variables in their containing scopes.

I suppose that current behavior is targeted at people coming from languages that allow nested functions to capture their environment, so they can get proper diagnostics - "you can't do that in Rust".
This can probably be done with a lint with the same success:

fn f2() {
    let x = 0;
    fn bar() {
        x += 1; // error: unresolved name `x`
                // note: hey, maybe you wanted a closure and not a function
                // ^ the note shouldn't even be precise and may be reported on best-effort basis
    }
}

If such isolation of function bodies gives some nice algorithmic properties, I'd do that without hesitation.

@nrc

This comment has been minimized.

Member

nrc commented Apr 21, 2016

I think I would prefer for inner function bodies not to 'see' the enclosing function bodies at all, i.e., f2 in the OP would not be an error. AIUI, the motivation is only that error comparing with closures, which I believe should be a lint, rather than an error (to be clear, there should be a warning lint when there are names in scope both outside the function and inside it, which today would be an error. Obvs, if there is only a name inside the function, then it must still be an error).

I believe the change is backwards compatible since it only admits more programs.

@jseyfried

This comment has been minimized.

Contributor

jseyfried commented Apr 21, 2016

@petrochenkov @nrc ok, you've convinced me that we should allow the example in the OP with a warning lint.

@jseyfried

This comment has been minimized.

Contributor

jseyfried commented Apr 22, 2016

@nrc Do you think the relative order of macro 2.0 definitions and statements in a block should matter?
For example,

fn foo() {}
fn f1() {
    macro_rules_v2! bar { () => { foo() } }
    let foo = 0;
    bar!(); // `foo` in this expansion would resolve to the item
}
fn f2() {
    let foo = 0;
    macro_rules_v2! bar { () => { foo() } }
    bar!(); // Should `foo` in this expansion resolve to the item or the local?
}
@jseyfried

This comment has been minimized.

Contributor

jseyfried commented Apr 22, 2016

If we decided the relative order of macro 2.0 definitions and statements should not matter, we would be able to use a macro in a statement before it was defined in the statement.

Perhaps a simpler way to state the question is "should a macro 2.0 definition in a block be analogous to a function item or to a local variable initialized with a closure?"

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Apr 22, 2016

A couple of points:

cannot capture dynamic environment in a fn item

Maybe I am unusual, but this error always confuses me every time I get it. It assumes (as @petrochenkov said) that I was trying to capture that variable in the first place, but -- because I know Rust's rules -- I never am. I would much prefer a straight-forward error with a HELP attached or something like that.

Just to clarify though, there are two distinct cases to be concerned about:

  1. References in the body of an inner fn.
  2. References in the body of the outer fn.

The example targets case 1, but I just want to be clear that consider this distinct from case 2:

fn bar() {
    let x = foo; // resolves to the inner fn `foo`
    let foo = 22;
    let y = foo; // resolves to the variable
    fn foo() { }
}

Right?

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Apr 22, 2016

In any case, I agree that -- in the original example -- the reference should resolve successfully in both cases to the foo at the module scope.

@jseyfried

This comment has been minimized.

Contributor

jseyfried commented Apr 22, 2016

@nikomatsakis

I would much prefer a straight-forward error with a HELP attached or something like that.

Agreed.

this [is] distinct from case 2 ... Right?

Right.

I think we're all pretty much in agreement on how to handle this issue.

@jseyfried

This comment has been minimized.

Contributor

jseyfried commented Apr 25, 2016

Related question -- should this compile? (currently, it doesn't)

enum E { V }

fn f<E>(_: E) {
    fn g() {
        let _ = E::V; // is the type parameter `E` in scope here? (currently, it is)
    }
}

Since the type parameter E is above the function item g in the scope hierarchy (unlike f's locals), we might want to consider E to be in the scope of g's body.

Of course, since we can't use the type parameter from g's body, we also might want to consider it to be not in scope. I don't have a strong opinion either way.

@nrc

This comment has been minimized.

Member

nrc commented Apr 26, 2016

@jseyfried well, we get into hygiene questions, but assuming we're only talking about stuff visible due to the definition (i.e., default hygiene rules), then I think macros should follow the same rules as functions, i.e., they don't 'see' the function body at all.

@jseyfried

This comment has been minimized.

Contributor

jseyfried commented Apr 26, 2016

@nrc

macros should follow the same rules as functions, i.e., they don't 'see' the function body at all.

Sounds good, I agree. Note that this differs from today's macros, which can see the function body.

@arielb1

This comment has been minimized.

Contributor

arielb1 commented Apr 26, 2016

@nrc

That's quite unlike Scheme macros! I think the interaction between macros and let is subtle enough and insufficiently-useful that we should not have it.

@nrc

This comment has been minimized.

Member

nrc commented Apr 26, 2016

That's quite unlike Scheme macros! I think the interaction between macros and let is subtle enough and insufficiently-useful that we should not have it.

I'm not aware of exactly how Scheme macros work here. I think I am proposing that there should not be interaction between macro definitions and let statements. I'm saying that when a macro is declared inside a function body, then it should (effectively) not see the rest of the function body, in the same way that functions inside function bodies don't.

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Apr 27, 2016

We have to be careful about backwards compatibility here.
On Apr 26, 2016 3:51 PM, "Nick Cameron" notifications@github.com wrote:

That's quite unlike Scheme macros! I think the interaction between macros
and let is subtle enough and insufficiently-useful that we should not have
it.

I'm not aware of exactly how Scheme macros work here. I think I am
proposing that there should not be interaction between macro definitions
and let statements. I'm saying that when a macro is declared inside a
function body, then it should (effectively) not see the rest of the
function body, in the same way that functions inside function bodies don't.


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#33118 (comment)

@jseyfried

This comment has been minimized.

Contributor

jseyfried commented Apr 27, 2016

@nikomatsakis

We have to be careful about backwards compatibility here.

The current plan is to implement a new, backwards incompatible "macro_rules_v2" that can be used alongside the today's macro_rules (which would continue to interact with let statements).

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Apr 27, 2016

@jseyfried

The current plan is to implement a new, backwards incompatible "macro_rules_v2" that can be used alongside the today's macro_rules (which would continue to interact with let statements).

I see, I thought that we were talking about the interaction of today's macros with let statements, not "macros 2.0".

@petrochenkov

This comment has been minimized.

Contributor

petrochenkov commented Jul 20, 2016

I wanted identifiers in syntactically unambiguous contexts to always be interpreted as fresh bindings, i.e.

const C: u8 = 10;
let C @ SUBPATTERN = 11; // OK, no error, C is interpreted as a fresh binding, because it cannot be anything else syntactically

but this issue needs to be fixed first because together they can result in some ridiculous behavior:

const C: u8 = 10; // C(0)
let C @ _ = 11; // C(1)
fn f() {
     match xxx {
         C => {} // C(2)
         _ => {}
     }
}

C(2) is interpreted as a fresh binding and not constant C(0), because it's first resolved as local C(1) and this resolution is rejected in favor of a fresh bindings.
(Note that before #34570 C(2) would be resolved to C(0) because the "disambiguation lookup" in patterns worked "unhygienically" and ignored local variables.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment