@@ -699,16 +699,65 @@ trait Implicits { self: Typer =>
699
699
if (ctx.inInlineMethod || enclosingInlineds.nonEmpty) ref(defn.TastyReflection_macroContext )
700
700
else EmptyTree
701
701
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 Eql [T, U], try to synthesize an
703
+ * `Eql.eqlAny [T, U]` as solution.
704
704
*/
705
705
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.EqlType .appliedTo(tp, tp), span)
710
+ ! inst.isEmpty && ! inst.tpe.isError
711
+ }
712
+
713
+ /** Can we assume the eqlAny 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
+
707
752
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.Eql_eqlAny ).appliedToTypes(args).withSpan(span)
760
+ else EmptyTree
712
761
case _ =>
713
762
EmptyTree
714
763
}
@@ -737,14 +786,6 @@ trait Implicits { self: Typer =>
737
786
}
738
787
}
739
788
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
-
748
789
/** If `formal` is of the form `scala.reflect.Generic[T]` for some class type `T`,
749
790
* synthesize an instance for it.
750
791
*/
@@ -776,7 +817,7 @@ trait Implicits { self: Typer =>
776
817
trySpecialCase(defn.QuotedTypeClass , synthesizedTypeTag,
777
818
trySpecialCase(defn.GenericClass , synthesizedGeneric,
778
819
trySpecialCase(defn.TastyReflectionClass , synthesizedTastyContext,
779
- trySpecialCase(defn.EqClass , synthesizedEq,
820
+ trySpecialCase(defn.EqlClass , synthesizedEq,
780
821
trySpecialCase(defn.ValueOfClass , synthesizedValueOf, failed))))))
781
822
}
782
823
}
@@ -885,16 +926,16 @@ trait Implicits { self: Typer =>
885
926
em " parameter ${paramName} of $methodStr"
886
927
}
887
928
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)
897
932
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 ) = {
898
939
// Map all non-opaque abstract types to their upper bound.
899
940
// This is done to check whether such types might plausibly be comparable to each other.
900
941
val lift = new TypeMap {
@@ -910,14 +951,20 @@ trait Implicits { self: Typer =>
910
951
if (variance > 0 ) mapOver(t) else t
911
952
}
912
953
}
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
+ }
914
961
}
915
962
916
963
/** Check that equality tests between types `ltp` and `rtp` make sense */
917
964
def checkCanEqual (ltp : Type , rtp : Type , span : Span )(implicit ctx : Context ): Unit =
918
965
if (! ctx.isAfterTyper && ! assumedCanEqual(ltp, rtp)) {
919
- val res = implicitArgTree(defn.EqType .appliedTo(ltp, rtp), span)
920
- implicits.println(i " Eq witness found for $ltp / $rtp: $res: ${res.tpe}" )
966
+ val res = implicitArgTree(defn.EqlType .appliedTo(ltp, rtp), span)
967
+ implicits.println(i " Eql witness found for $ltp / $rtp: $res: ${res.tpe}" )
921
968
}
922
969
923
970
/** Find an implicit parameter or conversion.
@@ -985,7 +1032,7 @@ trait Implicits { self: Typer =>
985
1032
if (argument.isEmpty) f(resultType) else ViewProto (f(argument.tpe.widen), f(resultType))
986
1033
// Not clear whether we need to drop the `.widen` here. All tests pass with it in place, though.
987
1034
988
- private def isCoherent = pt.isRef(defn.EqClass )
1035
+ private def isCoherent = pt.isRef(defn.EqlClass )
989
1036
990
1037
private val cmpContext = nestedContext()
991
1038
private val cmpCandidates = (c1 : Candidate , c2 : Candidate ) => compare(c1.ref, c2.ref, c1.level, c2.level)(cmpContext)
0 commit comments