Skip to content

Fix #7969: Don't let Typevars be bounded by ExprTypes #7985

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 2 commits into from
Jan 15, 2020

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Jan 14, 2020

The test case

object O{
  def f(i: => Int): Int = 1
  def m[A](a: A => Int) = 2
  def n = m(f)
}

now gives the following error message:

-- [E007] Type Mismatch Error: i7969.scala:4:12 --------------------------------
4 |  def n = m(f)
  |            ^
  |            Found:    => Int => Int
  |            Required: A => Int
  |
  |            where:    A is a type variable

@odersky
Copy link
Contributor Author

odersky commented Jan 14, 2020

This is tricky.

The most simple scheme would not allow to eta expand by-name functions. That was the rule initially, but at some point, the rule was dropped. Enforcing the restriction again now would break existing code.

Allowing by-name parameters in function types seems to be OK. After elimByName they are all converted to regular function types anyway.

In Dotty, we have the principles that ExprTypes are not value types. So the example has to be rejected since the type variable A must be bound to a value type.

scalac by contrast allows it. But letting type variables be ExprTypes has its own problems; starting with the fact that you cannot write the corresponding type instantiation as an explicit type argument since => A is rejected as a type argument. I have a hunch there are a lot of other complications as well, if one is inclined to dig deeper.

So this PR does the minimum to let existing code run and not pollute the type space with ExprTypes. It just allows the eta expansion but prevents further propagations into type parameters of other types.

@smarter
Copy link
Member

smarter commented Jan 14, 2020

Great explanation, thanks! Would you mind adding it as a comment in the code ?
On a related note, the current situation is fairly weird since (=> Int) => Int works but Function1[=> Int, Int] doesn't parse, so desugaring is doing a transformation that is not expressible in source code. Though if we ever move to a refinement-based encoding for all function types we could desugar it to:

Function {
  def apply(x: => Int): Int
}

@odersky
Copy link
Contributor Author

odersky commented Jan 15, 2020

On a related note, the current situation is fairly weird since (=> Int) => Int works but Function1[=> Int, Int] doesn't parse, so desugaring is doing a transformation that is not expressible in source code.

That's correct. So, we cheat one step, but not all the way. Without the cheat we could not eta expand by-name methods and that would mean existing code can't be compiled anymore.

Though if we ever move to a refinement-based encoding for all function types we could desugar it to:

Function {
  def apply(x: => Int): Int
}

Good to keep that in mind, yes!

@smarter smarter merged commit 9322e8c into scala:master Jan 15, 2020
@smarter smarter deleted the fix-#7969 branch January 15, 2020 12:47
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