Skip to content
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

In Context::LookupUnqualifiedName(), prefer lexical result over non lexical result #4574

Closed
wants to merge 1 commit into from

Conversation

bricknerb
Copy link
Contributor

@bricknerb bricknerb commented Nov 22, 2024

No description provided.

@jonmeow jonmeow requested review from zygoloid and removed request for geoffromer November 22, 2024 16:05
@jonmeow
Copy link
Contributor

jonmeow commented Nov 22, 2024

This feels like a behavior change, but I'm having trouble coming up with a test case, so @zygoloid who put this together.

@jonmeow
Copy link
Contributor

jonmeow commented Nov 22, 2024

Note, it may be correct even if it's a behavior change -- but I think it changes behavior when there's a lexical result and later non-lexical scopes. The current code would look through the remaining non-lexical scopes first (starting with first_non_lexical_scope), whereas the change would use the lexical result.

@bricknerb bricknerb changed the title In Context::LookupUnqualifiedName(), early return when lexical_result is valid In Context::LookupUnqualifiedName(), prefer lexical result over non lexical result Nov 22, 2024
Copy link
Contributor

@zygoloid zygoloid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would do the wrong thing for a case like:

fn F();
class C {
  fn F() -> i32;
  fn G() -> i32;
}
fn C.G() -> i32 {
  return F();
}

Here, lexical lookup for F finds package.F, but the desired unqualified lookup result is instead C.F.

If we don't already have a testcase like this, we should add one.

@bricknerb
Copy link
Contributor Author

bricknerb commented Nov 25, 2024

This would do the wrong thing for a case like:

fn F();
class C {
  fn F() -> i32;
  fn G() -> i32;
}
fn C.G() -> i32 {
  return F();
}

Here, lexical lookup for F finds package.F, but the desired unqualified lookup result is instead C.F.

If we don't already have a testcase like this, we should add one.

I've tried to reproduce this and failed so far (can't see a difference in behavior).
I'll try to understand better when a lexical lookup would trigger, since the following code doesn't trigger a different behavior in Check.

fn F() {}

namespace N;
fn N.F() -> i32 { return 1; }
fn N.G() -> i32 { return F(); }

class C {
  fn F() -> i32 { return 2; }
  fn G() -> i32;
}
fn C.G() -> i32 {
  return F();
}

fn Main() {
  F();

  var namespace_func_result: i32;
  namespace_func_result = N.G();

  var class_func_result: i32;
  var class_instance: C;
  class_func_result = class_instance.G();
}

@zygoloid
Copy link
Contributor

I've tried to reproduce this and failed so far (can't see a difference in behavior).

Oh, right: the outer name will need to come from a lexical scope for us to see a difference here. (Package scope is a non-lexical scope, so the example I mentioned and the namespace example won't be affected by this.) You would see a difference if a lexical scope encloses a non-lexical scope. Because we don't support local classes yet, I think that means the only way this is observable right now is if a non-lexical name shadows a generic parameter:

class Class(F:! type) {
  class Inner {
    fn F() -> i32 { return 0; }
    fn G() -> i32;
  }
}

fn Class(F:! type).Inner.G() -> i32 { return F(); }

... which compiles prior to this change and is rejected afterwards.

@bricknerb
Copy link
Contributor Author

I've tried to reproduce this and failed so far (can't see a difference in behavior).

Oh, right: the outer name will need to come from a lexical scope for us to see a difference here. (Package scope is a non-lexical scope, so the example I mentioned and the namespace example won't be affected by this.) You would see a difference if a lexical scope encloses a non-lexical scope. Because we don't support local classes yet, I think that means the only way this is observable right now is if a non-lexical name shadows a generic parameter:

class Class(F:! type) {
  class Inner {
    fn F() -> i32 { return 0; }
    fn G() -> i32;
  }
}

fn Class(F:! type).Inner.G() -> i32 { return F(); }

... which compiles prior to this change and is rejected afterwards.

Thanks Richard!
This makes total sense.
I've copy-pasted your sample code into #4591.

github-merge-queue bot pushed a commit that referenced this pull request Nov 27, 2024
…on lexical scope over a lexical scope (#4591)

This adds missing test coverage.
See discussion in
#4574.
@bricknerb bricknerb closed this Nov 28, 2024
bricknerb added a commit to bricknerb/carbon-lang that referenced this pull request Nov 28, 2024
…on lexical scope over a lexical scope (carbon-language#4591)

This adds missing test coverage.
See discussion in
carbon-language#4574.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants