Skip to content

Fix #5224: Make implicit shadowing conform to the rules in the spec #5227

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

Merged
merged 4 commits into from
Oct 26, 2018

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Oct 10, 2018

The previous version of implicit shadowing considered a nested reference
as a shadow only if it matched the prototype of the implicit search.
This is not backed up by the spec, and is different to what scalac does.
Arguably, the spec is right here: We should consider an implicit ineligible
if it is not visible as a simple name because a nested reference has the
same name, no matter what the type of the nested reference is.

The previous version of implicit shadowing considered a nested reference
as a shadow _only_ if it matched the prototype of the implicit search.
This is not backed up by the spec, and is different to what scalac does.
Arguably, the spec is right here: We should consider an implicit ineligible
if it is not visible as a simple name because a nested reference has the
same name, no matter what the type of the nested reference is.
Helps to trouble-shoot the culprit during parallel testing
@milessabin
Copy link
Contributor

Should a nested non-implicit definition shadow an implicit definition, ie. should the following not compile,

object Test {
  class Foo
  implicit def foo: Foo = ???
  {
    def foo = ???
    implicitly[Foo]  // outer foo is shadowed
  }
}

Since the type does not matter, we don't need to adapt it.
@odersky
Copy link
Contributor Author

odersky commented Oct 10, 2018

object Test {
  class Foo
  implicit def foo: Foo = ???

  {
    def foo = ???
    implicitly[Foo]  // outer foo is shadowed
  }

This should not compile, since the rules say "implicits are eligible if they can be referred to by a simple identifier". That's not the case in this example.

@odersky odersky requested a review from milessabin October 10, 2018 10:42
@milessabin
Copy link
Contributor

FWIW, it compiles both with Dotty prior to this PR and Scala 2.13.x.

@milessabin
Copy link
Contributor

I'd like to play around with this a bit ... I was digging around here because I had a hunch that shadowing might interact badly with your implicit caching strategy. I wasn't able to come up with a problematic case for caching because, prior to this PR, eligible implicits more or less only increased monotonically with more nested scopes. Now that shadowing can knock out arbitrary implicit definitions in outer scopes I think it might be possible to trick the caching mechanism into miscompiling.

@milessabin
Copy link
Contributor

@odersky that example still compiles after this PR.

The issue is in computeEligible ... shadowers are only the inner contexts ownEligible which must both be implicit and match the expected type.

@milessabin
Copy link
Contributor

Correction, the problem seems to be here: https://github.com/lampepfl/dotty/blob/876fb32f830602e3175eb19bbc043b6079d3c449/compiler/src/dotty/tools/dotc/typer/Implicits.scala#L963-L970

Second time though, contextual is false and the candidate which should have been shadowed is allowed through.

@milessabin
Copy link
Contributor

Is the issue that contextual implicits are retained in the cache and then found via a cache hit when only the implicit scope should be considered?

@odersky
Copy link
Contributor Author

odersky commented Oct 11, 2018

Second time though, contextual is false and the candidate which should have been shadowed is allowed through.

That's also as required. Implicits in the implicit scope cannot be shadowed. The rules are:

Contextual implicit: Any implicit value that can be referred to with a simple name qualifies
Type scope implicit: All implicit values in the implicit scope of the required type qualify.

Shadowing comes up only indirectly, to check that any contextual implicit that was found actually qualifies.

@milessabin
Copy link
Contributor

That's also as required. Implicits in the implicit scope cannot be shadowed.

Understood. But there's no implicit scope in play here.

@odersky
Copy link
Contributor Author

odersky commented Oct 12, 2018

Understood. But there's no implicit scope in play here.

I think there is: The full type is Test.Foo. Since foo is in Test, it's in the implicit scope.

(Aside: We should try whether we can reduce this aspect of type prefixes contributing to the implicit scope. In the past there have been some abuses where implicits were placed in package objects which made their implicit scope surprisingly large. So it might be good if we could at least avoid packages as parts of implicit scopes.)

@odersky odersky merged commit 981bdfd into scala:master Oct 26, 2018
@Blaisorblade Blaisorblade deleted the fix-#5224 branch November 12, 2018 11:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants