Skip to content

Multiversal Equality #1246

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 21 commits into from
May 25, 2016
Merged

Conversation

odersky
Copy link
Contributor

@odersky odersky commented May 4, 2016

The first 4 commits were taken from the previous PR to add mutliversal equals. Review by @smarter.

inferImplicit(pt, argument, pos)(ctx.addMode(Mode.OldOverloadingResolution)) match {
case altResult: SearchSuccess =>
ctx.migrationWarning(
s"According to new implicit resolution rules, this will be ambiguous:\n ${result.explanation}",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There was one test that failed under new rules: run/t2030.scala. It was moved to pos-scala2.

@odersky odersky changed the title Better handling of contravariance in implicit search Multiversal Equality May 7, 2016
@odersky odersky mentioned this pull request May 7, 2016
@odersky
Copy link
Contributor Author

odersky commented May 7, 2016

This implements #1247


/** A marker trait indicating that values of kind `T` can be compared to values of type `U`. */
@implicitNotFound("Values of types ${L} and ${R} cannot be compared with == or !=")
trait Eq[-L, -R]
Copy link
Member

@smarter smarter May 7, 2016

Choose a reason for hiding this comment

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

I think this trait should be sealed since the only subclass should be the object Eq

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed.

@odersky
Copy link
Contributor Author

odersky commented May 10, 2016

/rebuild

@smarter
Copy link
Member

smarter commented May 12, 2016

Can we change the signature of Any#== seen by the user to be:

  final def ==[T](that: T)(implicit _eq: Eq[this.type, T]): Boolean = this.equals(that)

?

@smarter
Copy link
Member

smarter commented May 12, 2016

Actually we might not even need to make == polymorphic if my understanding of dependent method types is correct:

  final def ==(that: Any)(implicit _eq: Eq[this.type, that.type]): Boolean =
    this.equals(that)

@odersky
Copy link
Contributor Author

odersky commented May 12, 2016

@smarter

Can we change the signature of Any#== seen by the user to be:
final def ==[T](that: T)(implicit _eq: Eq[this.type, T]): Boolean = this.equals(that)

This would be complicated. The problem is that Any#== calls are generated by the pattern matcher, but we cannot do an implicit search then. So if we do it (and I am sure why we should!) then it can only be done during Typer, afterwards we'd have to change the signature. Does not look very appealing to me.

@odersky
Copy link
Contributor Author

odersky commented May 17, 2016

Ready to merge?

@smarter
Copy link
Member

smarter commented May 17, 2016

I'll try to review this more deeply today or tomorrow

@smarter
Copy link
Member

smarter commented May 20, 2016

There was one test that failed under new rules: run/t2030.scala. It was moved to pos-scala2.

The two possible implicits are scala.collection.immutable.SortedSet.newCanBuildFrom and scala.collection.immutable.TreeSet.newCanBuildFrom, previously the first one was chosen, but now they're ambiguous. I was curious about what was going on here so I minimized it and explained it, this could be added as a testcase:

// Minimized from t2030.scala

class Both[-A, +B]

trait Factory[A] {
  implicit def make: Both[A, A] = new Both[A, A]
}

trait X
object X extends Factory[X] {
  override implicit def make: Both[X, X] = super.make
}

class Y extends X
object Y extends Factory[Y] {
  // override implicit def make: Both[Y, Y] = super.make
}

object Test {
  def get(implicit ev: Both[Y, X]) = ev

  // There are two possible implicits here: X.make and Y.make, neither are
  // subtype of each other, so who wins?
  // - Under the old scheme it's X.make because `isAsGood` sees that X.make is defined
  // in X whereas Y.make is defined in Factory
  // - Under the new scheme it's ambiguous because we replace contravariance by covariance
  // in top-level type parameters so Y.make is treated as a subtype of X.make
  // In both schemes we can get Y.make to win by uncommenting the override for make in Y
  // (Y wins against X because `isDerived` also considers the subtyping relationships
  // of companion classes)
  get
}

This means that we can fix the issue in the standard library by adding an override of newCanBuildFrom in TreeSet, as a bonus this means that res1 in t2030.scala gets the inferred type TreeSet[Int] instead of SortedSet[Int].

@odersky
Copy link
Contributor Author

odersky commented May 23, 2016

/rebuild

odersky added 12 commits May 23, 2016 16:10
If all overloaded variants of a method are uniary, we should
also allow auto-tupling. This is necessary to make overloaded
`==' methods work in cases like:

    xs == (1, 2)
tpd.applyOverloaded should not do type parameter inference; therefore it has
to make sure all eligible alternatives have the same number of type parameters
as there are type arguments.
Caches were set when information was not complete yet. Problem was
exhibited by missing some `eqName` implicits when adding safe equality
for Name.
This came up when tasty-checking Eq.scala.
Compare selected contravariant arguments as if they were covariant.
Which ones is explained in the doc comment for method `isAsSpecificValueType`
in Applications.scala.

This has the same motivation than what @paulp proposed around 2012. The solution is a bit
different from the one proposed then because it only affects top-level parameters.
Also, check that pattern matching against idents/selects/literals makes
sense.

The hooks perform an implicit search for an instance of `Eq[L, R]`, where
`L`, `R` are the argument types. So far this always succeeeds because Eq.eqAny
matches all such types. A separate commit will check the returned
search term for validity.
This is done by checking each instance of an eqAny implicit
so that it does not contain non-bottom instances of equality
types as instances.
(and add it to commit set).
odersky added 9 commits May 23, 2016 16:11
Name, Symbol, Denotation, Type.

This uncovered two nonsensical comparisons, one in CollectEntryPoints,
the other in DottyBackendInterface.
Needed a fix in string interpolation for suriviving inserted
types that contain `$` characters.
To make tests pass, this required a looser specification of
`assumedCanEquals`, so that an abstract type T can be compared to
arbitrary values, as long as its upper bound can be compared. E.g.

    T == null
    T == "abc"
@felixmulder
Copy link
Contributor

@odersky: 3c18a2a has not been properly fixed, if you want this to be merged quickly - I'd revert it.

Also, I'm investigating a more thorough fix using a MiniPhase that will be more of a general fix.

@odersky
Copy link
Contributor Author

odersky commented May 23, 2016

@felixmulder Why do you need a mini-phase? I am thinking of a simple fix in typer.

@odersky odersky force-pushed the add-multiversal-equals-2 branch from 3c18a2a to 2f3b895 Compare May 23, 2016 16:54
@odersky
Copy link
Contributor Author

odersky commented May 25, 2016

/rebuild

@odersky odersky merged commit 1f4c9f4 into scala:master May 25, 2016
@allanrenucci allanrenucci deleted the add-multiversal-equals-2 branch December 14, 2017 16:57
milessabin added a commit to milessabin/scala that referenced this pull request Mar 6, 2018
This is a backport of the following Dotty change to scalac,

scala/scala3@8954026

As in the Dotty implementation the specificity comparison which is used
for overload resolution and implicit selection is now performed as-if
all contravariant positions in top level type constructors were
covariant.

Currently this breaks test/files/run/t2030.scala in exactly the same way
as in Dotty as reported here,

scala/scala3#1246 (comment)

and in this PR it has been changed to a neg test. However,
test/files/run/t2030.scala passes when this PR is combined with,

scala#6139

so if/when that is merged it can be switched back to a pos test.

Fixes scala/bug#2509. Fixes scala/bug#7768.
milessabin added a commit to milessabin/scala that referenced this pull request Mar 7, 2018
Fixed ScalaCheck treeset.scala compiler error and reinstated t2030.scala
as a pos test by adding a newCanBuildFrom override in object TreeSet as
suggested in this comment on related Dotty changes,

scala/scala3#1246 (comment)

This change will be made obsolete when the new collections land.
milessabin added a commit to milessabin/scala that referenced this pull request Mar 22, 2018
This is a backport of the following Dotty change to scalac,

scala/scala3@8954026

As in the Dotty implementation the specificity comparison which is used
for overload resolution and implicit selection is now performed as-if
all contravariant positions in top level type constructors were
covariant.

Currently this breaks test/files/run/t2030.scala in exactly the same way
as in Dotty as reported here,

scala/scala3#1246 (comment)

and in this PR it has been changed to a neg test. However,
test/files/run/t2030.scala passes when this PR is combined with,

scala#6139

so if/when that is merged it can be switched back to a pos test.

Fixes scala/bug#2509. Fixes scala/bug#7768.
milessabin added a commit to milessabin/scala that referenced this pull request Mar 29, 2018
This is a backport of the following Dotty change to scalac,

scala/scala3@8954026

As in the Dotty implementation the specificity comparison which is used
for overload resolution and implicit selection is now performed as-if
all contravariant positions in top level type constructors were
covariant.

Currently this breaks test/files/run/t2030.scala in exactly the same way
as in Dotty as reported here,

scala/scala3#1246 (comment)

and in this PR it has been changed to a neg test. However,
test/files/run/t2030.scala passes when this PR is combined with,

scala#6139

so if/when that is merged it can be switched back to a pos test.

Fixes scala/bug#2509. Fixes scala/bug#7768.
milessabin added a commit to milessabin/scala that referenced this pull request Jul 26, 2018
This is a backport of the following Dotty change to scalac,

scala/scala3@8954026

As in the Dotty implementation the specificity comparison which is used
for overload resolution and implicit selection is now performed as-if
all contravariant positions in top level type constructors were
covariant.

Currently this breaks test/files/run/t2030.scala in exactly the same way
as in Dotty as reported here,

scala/scala3#1246 (comment)

and in this PR it has been changed to a neg test. However,
test/files/run/t2030.scala passes when this PR is combined with,

scala#6139

so if/when that is merged it can be switched back to a pos test.

Fixes scala/bug#2509. Fixes scala/bug#7768.
milessabin added a commit to milessabin/scala that referenced this pull request Jul 26, 2018
This is a backport of the following Dotty change to scalac,

scala/scala3@8954026

As in the Dotty implementation the specificity comparison which is used
for overload resolution and implicit selection is now performed as-if
all contravariant positions in type constructors were covariant.

Currently this breaks test/files/run/t2030.scala in exactly the same way
as in Dotty as reported here,

scala/scala3#1246 (comment)

and in this PR it has been changed to a neg test. However,
test/files/run/t2030.scala passes when this PR is combined with,

scala#6139

so if/when that is merged it can be switched back to a pos test.

Fixes scala/bug#2509. Fixes scala/bug#7768.
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