Skip to content

Fix #3564: Allow annotated singleton types #3723

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 4, 2018

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Jan 1, 2018

Previously, singleton types were widened before an annotation was added. Now such
a widening is no longer performed. The change necessitated a systematic generalization
of what it means to be a singleton, where we now use in many places
an extractor instead of a type test. This has the added benefit that we now also
classify as singletons intersections involving a singleton type and aliases to a singleton
type.

@odersky
Copy link
Contributor Author

odersky commented Jan 1, 2018

test performance please

@dottybot
Copy link
Member

dottybot commented Jan 1, 2018

performance test scheduled: 1 job(s) in queue, 0 running.

@@ -802,7 +802,7 @@ object PatternMatcher {
}

expectedTp.dealias match {
case expectedTp: SingletonType =>
case SingletonType(expectedTp) =>
Copy link
Member

Choose a reason for hiding this comment

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

This is wrong because the extractor for SingletonType will take only one branch of an AndType:

trait Foo {
  def hi: Int
}

object Test {
  def foo(x: Any) = x match {
    case x: ("1" & Foo) =>
      x.hi
    case _ =>
  }

  def main(args: Array[String]): Unit = {
    foo("1")
  }
}

This code used to run correctly, but now crashes with java.lang.ClassCastException: java.lang.String cannot be cast to Foo. In general I would rather not consider AndType to be singleton types, I think it's too easy to get incorrect semantics that way like what's happening here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see. It looks easiest to revert and not let AndTypes be SingletonTypes, even though semantically it would be nice if they could be.

Previously, singleton types were widened before an annotation was added. Now such
a widening is no longer performed. The change necessitated a systematic generalization
of what it means to be a singleton, where we now need to dealias in several places
before checking for SingletonType.
@odersky
Copy link
Contributor Author

odersky commented Jan 1, 2018

test performance please

@dottybot
Copy link
Member

dottybot commented Jan 1, 2018

performance test scheduled: 1 job(s) in queue, 0 running.

@odersky
Copy link
Contributor Author

odersky commented Jan 1, 2018

@smarter I changed the scheme so that AndTypes no longer qualify as singletons

Copy link
Member

@smarter smarter left a comment

Choose a reason for hiding this comment

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

Somehow we now loose annotations in some situations:
on master:

scala> val x = 1: @unchecked 
val x: Int @unchecked = 1
scala> val x = 1: Int @unchecked 
val x: Int @unchecked = 1

With this PR:

scala> val x = 1: @unchecked 
val x: Int = 1
scala> val x = 1: Int @unchecked 
val x: Int @unchecked = 1

@dottybot
Copy link
Member

dottybot commented Jan 1, 2018

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/3723/ to see the changes.

Benchmarks is based on merging with master (b199bf4)

@odersky
Copy link
Contributor Author

odersky commented Jan 2, 2018

Somehow we now loose annotations in some situations

Yes, it's because widen drops annotations and local type inference does a widen. It is a change of behavior but it's not clear that the old behavior was the correct one. I tried to leave the annotation on when widening but that's unintuitive (for comparison, we drop the annotation on dealiasing), and it also caused a lot of failures e.g. in compileStdLib. So I think the new scheme is the least bad one.

@@ -1593,7 +1593,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
if (ctx.mode is Mode.Type)
assignType(cpy.Annotated(tree)(arg1, annot1), arg1, annot1)
else {
val tpt = TypeTree(AnnotatedType(arg1.tpe.widen, Annotation(annot1)))
val tpt = TypeTree(AnnotatedType(arg1.tpe, Annotation(annot1)))
Copy link
Member

Choose a reason for hiding this comment

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

Actually thinking about this more, I think we should keep the .widen here.
I think we can only reach this case when the user write:

val x = 1: @unchecked

In this case I think it's fine to interpret this code as if the user had written:

val x = 1: Int @unchecked

If the user really wants to put the annotation on the singleton type, which is rather unusual, he can still explicitly write:

val x = 1: 1 @unchecked

This one will work fine because of the .widen which was dropped in TypeAssigner

Copy link
Contributor Author

@odersky odersky Jan 2, 2018

Choose a reason for hiding this comment

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

But that means #3564 fails, because we have precisely the case where we annotate a singleton with @uncheckedVariance.

Also, I would argue that someone writing

expr: @unchecked

is really annotating the expression expr, not its type. The type is an artifact of the way we express annotated expressions. So it is better that the annotation does not leak into inferred types of the lhs, say. See also #3427, which is another bug that would be fixed by this PR, but would not be fixed by the proposed change.

Copy link
Member

Choose a reason for hiding this comment

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

But that means #3564 fails, because we have precisely the case where we annotate a singleton with @uncheckedvariance.

I don't think so, in #3564 a singleton type is annotated, so ctx.mode is Type is true, this case is handled two lines above the current one, and only the .widen that was in TypeAssigner is relevant there.

So it is better that the annotation does not leak into inferred types of the lhs, say.

OK. I'm fine with this but note that this is a change of behavior with respect to Scala 2.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, I changed the logic in Typer only, and #3564 started failing again.

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.

3 participants