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

Ensure erased vals pass CheckRealizable #16111

Merged

Conversation

natsukagami
Copy link
Contributor

@natsukagami natsukagami commented Sep 27, 2022

Fix #4060: Ensure erased vals pass CheckRealizable

Per @odersky's comment, we wish to add realizability checks to all erased vals.

  • Add realization check to erased definitions typer
  • Add note about realizibility in the spec
  • Add tests for Unsoundness due to erased #4060 and small change in i11896

@natsukagami natsukagami requested review from nicolasstucki and removed request for nicolasstucki September 27, 2022 11:03
@natsukagami
Copy link
Contributor Author

natsukagami commented Sep 27, 2022

Tests are breaking because erasedValue in the library doesn't pass the realizability check...

@natsukagami natsukagami marked this pull request as draft September 27, 2022 11:09
@natsukagami natsukagami changed the title Ensure erased vals pass CheckReachable Ensure erased vals pass CheckRealizable Sep 27, 2022
@odersky
Copy link
Contributor

odersky commented Sep 27, 2022

I guess we need to treat erasedValue as syntax. I.e. exempt it from checking but check that every instance type is realizable.

@natsukagami
Copy link
Contributor Author

natsukagami commented Sep 28, 2022

The scala compiletime package is using erasedValue[T] as a case .. match value for any given type T without a concrete instance of the type. For example,
https://github.com/lampepfl/dotty/blob/731522a283fb6a2bed429fdda1984770e87aa185/library/src/scala/compiletime/package.scala#L116-L126
... and also encourages this use, as written on the doc comment above erasedValue[T]'s definition. I wonder if this is still something we want to do?

I don't think this use is related to our main goal of having erased definitions, and should be served by e.g. match types, or an inline trait derivation?
Nevertheless I have switched to checking realizibility of the types of the arguments in an Apply node instead. This seems to be sufficient: erased vals/defs must return a realizable type, and erased arguments must have realizable types.

@natsukagami natsukagami marked this pull request as ready for review September 28, 2022 15:24
@odersky odersky self-assigned this Oct 11, 2022

trait B { type L <: Nothing }
def upcast_dep_parameter(erased a: B)(x: a.L) : Int = x
erased val q : B { type L >: Any } = compiletime.erasedValue
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is no error emitted on this line? What happens if I try to use q's bounds directly on the line below, without passing it as an argument to any function?

I feel like realizability should be checked whenever we try to create an erased value.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Realizability checks are always performed when bounds are used, so using q's bounds directly will CE even without this PR.
This PR only adds the checks at function calls (they were performed at definition before, but they are not sufficient per the example).

I had a chat with @odersky about whether checks should be done when we create erased values. We concluded that it's okay to leave it at that to maximize flexibility of erased values.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah. Then everything looks good to me.

@abgruszecki abgruszecki self-requested a review November 8, 2022 07:49
... instead of checking every instantiated instance of erasedValue[T].
I think this is the better solution, since erasedValue have uses outside
of erasedDefinitions (i.e. in inlining) which has their own realizability checks
as well. We don't have to be pessimistic in those cases.

- Only check for realizability for erased parameters of result-dependent functions.
  Per talk with @odersky, it seems that it is enough to check for
  realizability of parameters of a dependent function in order to avoid
  unsoundness.

Note that the following actions have already been checked for,
with erased parameters:
- Using them as a dependent path in casting / variable definitions
- Using them as the input to an `inline match`

The check previously performed on `val`/`def` definitions are also
removed, so we are as liberal as possible, to avoid too much restriction.

I think with this, we can also remove the special case for `compiletime.erasedValue`.
@natsukagami natsukagami force-pushed the 4060-check-realizability-of-erased branch from 28b055e to 49906dd Compare November 8, 2022 11:42
@natsukagami natsukagami force-pushed the 4060-check-realizability-of-erased branch from 49906dd to e770df3 Compare November 8, 2022 11:45
@natsukagami natsukagami enabled auto-merge November 8, 2022 11:50
@natsukagami natsukagami merged commit b50a29a into scala:main Nov 8, 2022
@Kordyjan Kordyjan added this to the 3.3.0 milestone Aug 1, 2023
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.

Unsoundness due to erased
5 participants