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