@@ -699,16 +699,65 @@ trait Implicits { self: Typer =>
699699 if (ctx.inInlineMethod || enclosingInlineds.nonEmpty) ref(defn.TastyReflection_macroContext )
700700 else EmptyTree
701701
702- /** If `formal` is of the form Eq[T, U], where no `Eq` instance exists for
703- * either `T` or `U`, synthesize `Eq.eqAny[T, U]` as solution.
702+ /** If `formal` is of the form Eq[T, U], try to synthesize an
703+ * `Eq.eqAny[T, U]` as solution.
704704 */
705705 def synthesizedEq (formal : Type )(implicit ctx : Context ): Tree = {
706- // println(i"synth eq $formal / ${formal.argTypes}%, %")
706+
707+ /** Is there an `Eql[T, T]` instance, assuming -strictEquality? */
708+ def hasEq (tp : Type )(implicit ctx : Context ): Boolean = {
709+ val inst = inferImplicitArg(defn.EqType .appliedTo(tp, tp), span)
710+ ! inst.isEmpty && ! inst.tpe.isError
711+ }
712+
713+ /** Can we assume the eqAny instance for `tp1`, `tp2`?
714+ * This is the case if assumedCanEqual(tp1, tp2), or
715+ * one of `tp1`, `tp2` has a reflexive `Eql` instance.
716+ */
717+ def validEqAnyArgs (tp1 : Type , tp2 : Type )(implicit ctx : Context ) =
718+ assumedCanEqual(tp1, tp2) || {
719+ val nestedCtx = ctx.fresh.addMode(Mode .StrictEquality )
720+ ! hasEq(tp1)(nestedCtx) && ! hasEq(tp2)(nestedCtx)
721+ }
722+
723+ /** Is an `Eql[cls1, cls2]` instance assumed for predefined classes `cls1`, cls2`? */
724+ def canComparePredefinedClasses (cls1 : ClassSymbol , cls2 : ClassSymbol ): Boolean = {
725+ def cmpWithBoxed (cls1 : ClassSymbol , cls2 : ClassSymbol ) =
726+ cls2 == defn.boxedType(cls1.typeRef).symbol ||
727+ cls1.isNumericValueClass && cls2.derivesFrom(defn.BoxedNumberClass )
728+
729+ if (cls1.isPrimitiveValueClass)
730+ if (cls2.isPrimitiveValueClass)
731+ cls1 == cls2 || cls1.isNumericValueClass && cls2.isNumericValueClass
732+ else
733+ cmpWithBoxed(cls1, cls2)
734+ else if (cls2.isPrimitiveValueClass)
735+ cmpWithBoxed(cls2, cls1)
736+ else if (cls1 == defn.NullClass )
737+ cls1 == cls2 || cls2.derivesFrom(defn.ObjectClass )
738+ else if (cls2 == defn.NullClass )
739+ cls1.derivesFrom(defn.ObjectClass )
740+ else
741+ false
742+ }
743+
744+ /** Some simulated `Eql` instances for predefined types. It's more efficient
745+ * to do this directly instead of setting up a lot of `Eql` instances to
746+ * interpret.
747+ */
748+ def canComparePredefined (tp1 : Type , tp2 : Type ) =
749+ tp1.classSymbols.exists(cls1 =>
750+ tp2.classSymbols.exists(cls2 => canComparePredefinedClasses(cls1, cls2)))
751+
707752 formal.argTypes match {
708- case args @ (arg1 :: arg2 :: Nil )
709- if ! ctx.featureEnabled(defn.LanguageModuleClass , nme.strictEquality) &&
710- ctx.test(implicit ctx => validEqAnyArgs(arg1, arg2)) =>
711- ref(defn.Eq_eqAny ).appliedToTypes(args).withSpan(span)
753+ case args @ (arg1 :: arg2 :: Nil ) =>
754+ List (arg1, arg2).foreach(fullyDefinedType(_, " eq argument" , span))
755+ if (canComparePredefined(arg1, arg2)
756+ ||
757+ ! strictEquality &&
758+ ctx.test(implicit ctx => validEqAnyArgs(arg1, arg2)))
759+ ref(defn.Eq_eqAny ).appliedToTypes(args).withSpan(span)
760+ else EmptyTree
712761 case _ =>
713762 EmptyTree
714763 }
@@ -737,14 +786,6 @@ trait Implicits { self: Typer =>
737786 }
738787 }
739788
740- def hasEq (tp : Type ): Boolean =
741- inferImplicit(defn.EqType .appliedTo(tp, tp), EmptyTree , span).isSuccess
742-
743- def validEqAnyArgs (tp1 : Type , tp2 : Type )(implicit ctx : Context ) = {
744- List (tp1, tp2).foreach(fullyDefinedType(_, " eqAny argument" , span))
745- assumedCanEqual(tp1, tp2) || ! hasEq(tp1) && ! hasEq(tp2)
746- }
747-
748789 /** If `formal` is of the form `scala.reflect.Generic[T]` for some class type `T`,
749790 * synthesize an instance for it.
750791 */
@@ -885,16 +926,16 @@ trait Implicits { self: Typer =>
885926 em " parameter ${paramName} of $methodStr"
886927 }
887928
888- private def assumedCanEqual (ltp : Type , rtp : Type )(implicit ctx : Context ) = {
889- def eqNullable : Boolean = {
890- val other =
891- if (ltp.isRef(defn.NullClass )) rtp
892- else if (rtp.isRef(defn.NullClass )) ltp
893- else NoType
894-
895- (other ne NoType ) && ! other.derivesFrom(defn.AnyValClass )
896- }
929+ private def strictEquality (implicit ctx : Context ): Boolean =
930+ ctx.mode.is(Mode .StrictEquality ) ||
931+ ctx.featureEnabled(defn.LanguageModuleClass , nme.strictEquality)
897932
933+ /** An Eql[T, U] instance is assumed
934+ * - if one of T, U is an error type, or
935+ * - if one of T, U is a subtype of the lifted version of the other,
936+ * unless strict equality is set.
937+ */
938+ private def assumedCanEqual (ltp : Type , rtp : Type )(implicit ctx : Context ) = {
898939 // Map all non-opaque abstract types to their upper bound.
899940 // This is done to check whether such types might plausibly be comparable to each other.
900941 val lift = new TypeMap {
@@ -910,7 +951,13 @@ trait Implicits { self: Typer =>
910951 if (variance > 0 ) mapOver(t) else t
911952 }
912953 }
913- ltp.isError || rtp.isError || ltp <:< lift(rtp) || rtp <:< lift(ltp) || eqNullable
954+
955+ ltp.isError ||
956+ rtp.isError ||
957+ ! strictEquality && {
958+ ltp <:< lift(rtp) ||
959+ rtp <:< lift(ltp)
960+ }
914961 }
915962
916963 /** Check that equality tests between types `ltp` and `rtp` make sense */
0 commit comments