Skip to content

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

@jseyfried

Description

@jseyfried
Contributor

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.

Activity

jseyfried

jseyfried commented on Apr 20, 2016

@jseyfried
ContributorAuthor
petrochenkov

petrochenkov commented on Apr 21, 2016

@petrochenkov
Contributor

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

nrc commented on Apr 21, 2016

@nrc
Member

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.

added
T-langRelevant to the language team
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
on Apr 21, 2016
jseyfried

jseyfried commented on Apr 21, 2016

@jseyfried
ContributorAuthor

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

jseyfried

jseyfried commented on Apr 22, 2016

@jseyfried
ContributorAuthor

@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

jseyfried commented on Apr 22, 2016

@jseyfried
ContributorAuthor

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

nikomatsakis commented on Apr 22, 2016

@nikomatsakis
Contributor

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

nikomatsakis commented on Apr 22, 2016

@nikomatsakis
Contributor

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

jseyfried commented on Apr 22, 2016

@jseyfried
ContributorAuthor

@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

jseyfried commented on Apr 25, 2016

@jseyfried
ContributorAuthor

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

nrc commented on Apr 26, 2016

@nrc
Member

@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

jseyfried commented on Apr 26, 2016

@jseyfried
ContributorAuthor

@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

arielb1 commented on Apr 26, 2016

@arielb1
Contributor

@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.

19 remaining items

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-resolveArea: Name/path resolution done by `rustc_resolve` specificallyC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @Enselic@nikomatsakis@nrc@arielb1@Mark-Simulacrum

        Issue actions

          resolve: the relative order of items and statements in a block can be important · Issue #33118 · rust-lang/rust