Skip to content

Add multiversal equals 3 #1279

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

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/dotty/DottyPredef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,43 @@ package dotty
import scala.reflect.runtime.universe.TypeTag
import scala.reflect.ClassTag
import scala.Predef.???
import scala.collection.Seq

/** unimplemented implicit for TypeTag */
object DottyPredef {
implicit def typeTag[T]: TypeTag[T] = ???

implicit def arrayTag[T](implicit ctag: ClassTag[T]): ClassTag[Array[T]] =
ctag.wrap

/** A fall-back implicit to compare values of any types.
* The compiler will restrict implicit instances of `eqAny`. An instance
* `eqAny[T, U]` is _valid_ if `T <: U` or `U <: T` or both `T` and `U` are
* Eq-free. A type `S` is Eq-free if there is no implicit instance of `Eq[S, S]`.
* An implicit search will fail instead of returning an invalid `eqAny` instance.
*/
implicit def eqAny[L, R]: Eq[L, R] = Eq

implicit def eqNumber : Eq[Number, Number] = Eq
implicit def eqString : Eq[String, String] = Eq

// true asymmetry, modeling the (somewhat problematic) nature of equals on Proxies
implicit def eqProxy : Eq[Proxy, Any] = Eq

implicit def eqSeq[T, U](implicit eq: Eq[T, U]): Eq[Seq[T], Seq[U]] = Eq

implicit def eqByteNum : Eq[Byte, Number] = Eq
implicit def eqNumByte : Eq[Number, Byte] = Eq
implicit def eqCharNum : Eq[Char, Number] = Eq
implicit def eqNumChar : Eq[Number, Char] = Eq
implicit def eqShortNum : Eq[Short, Number] = Eq
implicit def eqNumShort : Eq[Number, Short] = Eq
implicit def eqIntNum : Eq[Int, Number] = Eq
implicit def eqNumInt : Eq[Number, Int] = Eq
implicit def eqLongNum : Eq[Long, Number] = Eq
implicit def eqNumLong : Eq[Number, Long] = Eq
implicit def eqFloatNum : Eq[Float, Number] = Eq
implicit def eqNumFloat : Eq[Number, Float] = Eq
implicit def eqDoubleNum: Eq[Double, Number] = Eq
implicit def eqNumDouble: Eq[Number, Double] = Eq
}
10 changes: 5 additions & 5 deletions src/dotty/tools/backend/jvm/DottyBackendInterface.scala
Original file line number Diff line number Diff line change
Expand Up @@ -861,11 +861,11 @@ class DottyBackendInterface(outputDirectory: AbstractFile)(implicit ctx: Context
"If possible, please file a bug on issues.scala-lang.org.")

tp match {
case ThisType(ArrayClass) => ObjectReference.asInstanceOf[ct.bTypes.ClassBType] // was introduced in 9b17332f11 to fix SI-999, but this code is not reached in its test, or any other test
case ThisType(sym) => storage.getClassBTypeAndRegisterInnerClass(sym.asInstanceOf[ct.int.Symbol])
// case t: SingletonType => primitiveOrClassToBType(t.classSymbol)
case t: SingletonType => t.underlying.toTypeKind(ct)(storage)
case t: RefinedType => t.parent.toTypeKind(ct)(storage) //parents.map(_.toTypeKind(ct)(storage).asClassBType).reduceLeft((a, b) => a.jvmWiseLUB(b))
case tp: ThisType if tp.cls == ArrayClass => ObjectReference.asInstanceOf[ct.bTypes.ClassBType] // was introduced in 9b17332f11 to fix SI-999, but this code is not reached in its test, or any other test
case tp: ThisType => storage.getClassBTypeAndRegisterInnerClass(tp.cls.asInstanceOf[ct.int.Symbol])
// case t: SingletonType => primitiveOrClassToBType(t.classSymbol)
case t: SingletonType => t.underlying.toTypeKind(ct)(storage)
case t: RefinedType => t.parent.toTypeKind(ct)(storage) //parents.map(_.toTypeKind(ct)(storage).asClassBType).reduceLeft((a, b) => a.jvmWiseLUB(b))
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -847,7 +847,13 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
assert(denot.exists, i"no member $receiver . $method, members = ${receiver.tpe.decls}")
val selected =
if (denot.isOverloaded) {
val allAlts = denot.alternatives.map(_.termRef)
def typeParamCount(tp: Type) = tp.widen match {
case tp: PolyType => tp.paramBounds.length
case _ => 0
}
var allAlts = denot.alternatives
.map(_.termRef).filter(tr => typeParamCount(tr) == targs.length)
if (targs.isEmpty) allAlts = allAlts.filterNot(_.widen.isInstanceOf[PolyType])
val alternatives =
ctx.typer.resolveOverloaded(allAlts, proto, Nil)
assert(alternatives.size == 1,
Expand Down
7 changes: 7 additions & 0 deletions src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@ class Definitions {

lazy val DottyPredefModuleRef = ctx.requiredModuleRef("dotty.DottyPredef")
def DottyPredefModule(implicit ctx: Context) = DottyPredefModuleRef.symbol

def Predef_eqAny(implicit ctx: Context) = DottyPredefModule.requiredMethod(nme.eqAny)

lazy val DottyArraysModuleRef = ctx.requiredModuleRef("dotty.runtime.Arrays")
def DottyArraysModule(implicit ctx: Context) = DottyArraysModuleRef.symbol
def newGenericArrayMethod(implicit ctx: Context) = DottyArraysModule.requiredMethod("newGenericArray")
Expand Down Expand Up @@ -424,10 +427,14 @@ class Definitions {
lazy val LanguageModuleRef = ctx.requiredModule("dotty.language")
def LanguageModuleClass(implicit ctx: Context) = LanguageModuleRef.symbol.moduleClass.asClass
lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl")

lazy val ClassTagType = ctx.requiredClassRef("scala.reflect.ClassTag")
def ClassTagClass(implicit ctx: Context) = ClassTagType.symbol.asClass
def ClassTagModule(implicit ctx: Context) = ClassTagClass.companionModule

lazy val EqType = ctx.requiredClassRef("scala.Eq")
def EqClass(implicit ctx: Context) = EqType.symbol.asClass

// Annotation base classes
lazy val AnnotationType = ctx.requiredClassRef("scala.annotation.Annotation")
def AnnotationClass(implicit ctx: Context) = AnnotationType.symbol.asClass
Expand Down
2 changes: 2 additions & 0 deletions src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ import Decorators.SymbolIteratorDecorator
*/
object Denotations {

implicit def eqDenotation: Eq[Denotation, Denotation] = Eq

/** A denotation is the result of resolving
* a name (either simple identifier or select) during a given period.
*
Expand Down
3 changes: 3 additions & 0 deletions src/dotty/tools/dotc/core/Mode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,8 @@ object Mode {
/** We are currently unpickling Scala2 info */
val Scala2Unpickling = newMode(13, "Scala2Unpickling")

/** Use Scala2 scheme for overloading and implicit resolution */
val OldOverloadingResolution = newMode(14, "OldOverloadingResolution")

val PatternOrType = Pattern | Type
}
2 changes: 2 additions & 0 deletions src/dotty/tools/dotc/core/Names.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ object Names {
def toTermName: TermName
}

implicit def eqName: Eq[Name, Name] = Eq

/** A name is essentially a string, with three differences
* 1. Names belong in one of two name spaces: they are type names or term names.
* Term names have a sub-category of "local" field names.
Expand Down
1 change: 1 addition & 0 deletions src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ object StdNames {
val equals_ : N = "equals"
val error: N = "error"
val eval: N = "eval"
val eqAny: N = "eqAny"
val ex: N = "ex"
val experimental: N = "experimental"
val f: N = "f"
Expand Down
2 changes: 2 additions & 0 deletions src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,8 @@ trait Symbols { this: Context =>

object Symbols {

implicit def eqSymbol: Eq[Symbol, Symbol] = Eq

/** A Symbol represents a Scala definition/declaration or a package.
* @param coord The coordinates of the symbol (a position or an index)
* @param id A unique identifier of the symbol (unique per ContextBase)
Expand Down
2 changes: 2 additions & 0 deletions src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ object Types {

@sharable private var nextId = 0

implicit def eqType: Eq[Type, Type] = Eq

/** The class of types.
* The principal subclasses and sub-objects are as follows:
*
Expand Down
2 changes: 2 additions & 0 deletions src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class PlainPrinter(_ctx: Context) extends Printer {
case tp @ TypeRef(_, tpnme.hkApply) =>
val tp1 = tp.reduceProjection
if (tp1 eq tp) tp else homogenize(tp1)
case tp: LazyRef =>
homogenize(tp.ref)
case _ =>
tp
}
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/transform/CollectEntryPoints.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class CollectEntryPoints extends MiniPhaseTransform {
val javaPlatform = ctx.platform.asInstanceOf[JavaPlatform]
if (javaPlatform.hasJavaMainMethod(companion))
failNoForwarder("companion contains its own main method")
else if (companion != NoSymbol && companion.info.member(nme.main) != NoSymbol)
else if (companion.exists && companion.info.member(nme.main).exists)
// this is only because forwarders aren't smart enough yet
failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)")
else if (companion.flags is Flags.Trait)
Expand Down
57 changes: 54 additions & 3 deletions src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,17 @@ trait Applications extends Compatibility { self: Typer =>
failedVal
}
}
else realApply
else {
val app = realApply
app match {
case Apply(fn @ Select(left, _), right :: Nil) if fn.hasType =>
val op = fn.symbol
if (op == defn.Any_== || op == defn.Any_!=)
checkCanEqual(left.tpe.widen, right.tpe.widen, app.pos)
case _ =>
}
app
}
}

/** Overridden in ReTyper to handle primitive operations that can be generated after erasure */
Expand Down Expand Up @@ -915,13 +925,54 @@ trait Applications extends Compatibility { self: Typer =>

{
implicit val ctx: Context = nestedCtx
isCompatible(tp1, constrained(tp2).resultType)
isAsSpecificValueType(tp1, constrained(tp2).resultType)
}
case _ => // (3b)
isCompatible(tp1, tp2)
isAsSpecificValueType(tp1, tp2)
}
}}

/** Test whether value type `tp1` is as specific as value type `tp2`.
* Let's abbreviate this to `tp1 <:s tp2`.
* Previously, `<:s` was the same as `<:`. This behavior is still
* available under mode `Mode.OldOverloadingResolution`. The new behavior
* is different, however. Here, `T <:s U` iff
*
* flip(T) <: flip(U)
*
* where `flip` changes top-level contravariant type aliases to covariant ones.
* Intuitively `<:s` means subtyping `<:`, except that all top-level arguments
* to contravariant parameters are compared as if they were covariant. E.g. given class
*
* class Cmp[-X]
*
* `Cmp[T] <:s Cmp[U]` if `T <: U`. On the other hand, nested occurrences
* of parameters are not affected.
* So `T <: U` would imply `List[Cmp[U]] <:s List[Cmp[T]]`, as usual.
*
* This relation might seem strange, but it models closely what happens for methods.
* Indeed, if we integrate the existing rules for methods into `<:s` we have now that
*
* (T)R <:s (U)R
*
* iff
*
* T => R <:s U => R
*/
def isAsSpecificValueType(tp1: Type, tp2: Type)(implicit ctx: Context) =
if (ctx.mode.is(Mode.OldOverloadingResolution))
isCompatible(tp1, tp2)
else {
val flip = new TypeMap {
def apply(t: Type) = t match {
case t: TypeAlias if variance > 0 && t.variance < 0 => t.derivedTypeAlias(t.alias, 1)
case t: TypeBounds => t
case _ => mapOver(t)
}
}
isCompatible(flip(tp1), flip(tp2))
}

/** Drop any implicit parameter section */
def stripImplicit(tp: Type): Type = tp match {
case mt: ImplicitMethodType if !mt.isDependent =>
Expand Down
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/typer/ErrorReporting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import util.Positions._
import reporting.Diagnostic
import printing.Showable
import printing.Disambiguation.disambiguated
import java.util.regex.Matcher.quoteReplacement

object ErrorReporting {

Expand Down Expand Up @@ -124,7 +125,7 @@ object ErrorReporting {
def implicitNotFoundString(raw: String, paramNames: List[String], args: List[Type]): String = {
def translate(name: String): Option[String] = {
val idx = paramNames.indexOf(name)
if (idx >= 0) Some(args(idx).show) else None
if (idx >= 0) Some(quoteReplacement(args(idx).show)) else None
}
"""\$\{\w*\}""".r.replaceSomeIn(raw, m => translate(m.matched.drop(2).init))
}
Expand Down
Loading