diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index dc54d14b0d4b..40fe16d2d9db 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -229,6 +229,7 @@ object Types extends TypeUtils { def isAny(using Context): Boolean = isRef(defn.AnyClass, skipRefined = false) def isAnyRef(using Context): Boolean = isRef(defn.ObjectClass, skipRefined = false) + def isAnyVal(using Context): Boolean = isRef(defn.AnyValClass, skipRefined = false) def isAnyKind(using Context): Boolean = isRef(defn.AnyKindClass, skipRefined = false) def isTopType(using Context): Boolean = dealias match diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 342cc8508d54..a313caa123bf 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1668,15 +1668,14 @@ trait Implicits: def isUnderSpecifiedArgument(tp: Type): Boolean = tp.isRef(defn.NothingClass) || tp.isRef(defn.NullClass) || (tp eq NoPrefix) - private def isUnderspecified(tp: Type): Boolean = tp.stripTypeVar match + private def isUnderspecified(tp: Type): Boolean = tp.simplified match case tp: WildcardType => !tp.optBounds.exists || isUnderspecified(tp.optBounds.hiBound) case tp: ViewProto => isUnderspecified(tp.resType) || tp.resType.isRef(defn.UnitClass) || isUnderSpecifiedArgument(tp.argType.widen) - case _ => - tp.isAny || tp.isAnyRef + case tp => tp.isAny || tp.isAnyRef || tp.isAnyVal /** Search implicit in context `ctxImplicits` or else in implicit scope * of expected type if `ctxImplicits == null`. diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 322512cfe54b..f809208c2966 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4952,10 +4952,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ if ctx.mode.is(Mode.ImplicitsEnabled) && tree.tpe.isValueType => if tree.tpe.isNamedTupleType && pt.derivesFrom(defn.TupleClass) then readapt(typed(untpd.Select(untpd.TypedSplice(tree), nme.toTuple))) - else if pt.isRef(defn.AnyValClass, skipRefined = false) - || pt.isRef(defn.ObjectClass, skipRefined = false) - then - recover(TooUnspecific(pt)) else inferView(tree, pt) match case SearchSuccess(found, _, _, isExtension) => if isExtension then found diff --git a/tests/neg/i21304.check b/tests/neg/i21304.check new file mode 100644 index 000000000000..c89be246b752 --- /dev/null +++ b/tests/neg/i21304.check @@ -0,0 +1,23 @@ +-- [E007] Type Mismatch Error: tests/neg/i21304.scala:5:6 -------------------------------------------------------------- +5 | foo(10) // error + | ^^ + | Found: (10 : Int) + | Required: AnyRef | Null + | Note that implicit conversions were not tried because the result of an implicit conversion + | must be more specific than AnyRef | Null + | + | One of the following imports might fix the problem: + | + | import scala.math.BigDecimal.int2bigDecimal + | import scala.math.BigInt.int2bigInt + | import scala.math.Numeric.IntIsIntegral.mkNumericOps + | import scala.math.Numeric.IntIsIntegral.mkOrderingOps + | import scala.math.Ordering.Int.mkOrderingOps + | import scala.math.Integral.Implicits.infixIntegralOps + | import scala.math.Ordered.orderingToOrdered + | import scala.math.Ordering.Implicits.infixOrderingOps + | import scala.math.Numeric.Implicits.infixNumericOps + | import scala.reflect.Selectable.reflectiveSelectable + | + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i21304.scala b/tests/neg/i21304.scala new file mode 100644 index 000000000000..25e0a002f82a --- /dev/null +++ b/tests/neg/i21304.scala @@ -0,0 +1,5 @@ +def foo(x: AnyRef | Null): Unit = + println(x) + +@main def main(): Unit = + foo(10) // error diff --git a/tests/neg/implicits-numeric.check b/tests/neg/implicits-numeric.check new file mode 100644 index 000000000000..c0be9dfe50a8 --- /dev/null +++ b/tests/neg/implicits-numeric.check @@ -0,0 +1,5 @@ +-- [E172] Type Error: tests/neg/implicits-numeric.scala:6:28 ----------------------------------------------------------- +6 | println(implicitly[AnyVal]) // error + | ^ + | No implicit search was attempted for parameter e of method implicitly in object Predef + | since the expected type AnyVal is not specific enough diff --git a/tests/run/implicits-numeric.scala b/tests/neg/implicits-numeric.scala similarity index 68% rename from tests/run/implicits-numeric.scala rename to tests/neg/implicits-numeric.scala index dfb0ff46082a..630723d8bbe4 100644 --- a/tests/run/implicits-numeric.scala +++ b/tests/neg/implicits-numeric.scala @@ -3,5 +3,5 @@ object Test extends App { implicit def _1: Long = 1L implicit def _2: Int = 0 - println(implicitly[AnyVal]) + println(implicitly[AnyVal]) // error } diff --git a/tests/pos/hylolib/AnyValue.scala b/tests/pos/hylolib/AnyValue.scala index 6844135b646b..b06803b4374c 100644 --- a/tests/pos/hylolib/AnyValue.scala +++ b/tests/pos/hylolib/AnyValue.scala @@ -24,7 +24,7 @@ final class AnyValue private ( _copy(this.wrapped) /** Returns `true` iff `this` and `other` have an equivalent value. */ - def eq(other: AnyValue): Boolean = + infix def eq(other: AnyValue): Boolean = _eq(this.wrapped, other.wrapped) /** Hashes the salient parts of `this` into `hasher`. */ @@ -49,7 +49,7 @@ object AnyValue { AnyValue(a.asInstanceOf[Ref[T]].value.copy()) def eq(a: AnyRef, b: AnyRef): Boolean = - a.asInstanceOf[Ref[T]].value `eq` b.asInstanceOf[Ref[T]].value + a.asInstanceOf[Ref[T]].value eq b.asInstanceOf[Ref[T]].value def hashInto(a: AnyRef, hasher: Hasher): Hasher = a.asInstanceOf[Ref[T]].value.hashInto(hasher) @@ -62,6 +62,6 @@ given AnyValue is Value: extension (self: AnyValue) def copy(): AnyValue = self.copy() - def eq(other: AnyValue): Boolean = self `eq` other + infix def eq(other: AnyValue): Boolean = self eq other def hashInto(hasher: Hasher): Hasher = self.hashInto(hasher) diff --git a/tests/pos/hylolib/AnyValueTests.scala b/tests/pos/hylolib/AnyValueTests.scala index 96d3563f4f53..326aacfe1102 100644 --- a/tests/pos/hylolib/AnyValueTests.scala +++ b/tests/pos/hylolib/AnyValueTests.scala @@ -6,10 +6,10 @@ class AnyValueTests extends munit.FunSuite: test("eq"): val a = AnyValue(1) - assert(a `eq` a) + assert(a eq a) assert(!(a `neq` a)) val b = AnyValue(2) - assert(!(a `eq` b)) + assert(!(a eq b)) assert(a `neq` b) diff --git a/tests/pos/hylolib/BitArray.scala b/tests/pos/hylolib/BitArray.scala index 03c3c0663e20..08304cd4eb93 100644 --- a/tests/pos/hylolib/BitArray.scala +++ b/tests/pos/hylolib/BitArray.scala @@ -300,7 +300,7 @@ object BitArray { new Position(bucket, offsetInBucket) /** Returns `true` iff `this` and `other` have an equivalent value. */ - def eq(other: Position): Boolean = + infix def eq(other: Position): Boolean = (this.bucket == other.bucket) && (this.offsetInBucket == other.offsetInBucket) /** Hashes the salient parts of `self` into `hasher`. */ @@ -325,8 +325,8 @@ given BitArray.Position is Value: def copy(): BitArray.Position = self.copy() - def eq(other: BitArray.Position): Boolean = - self.eq(other) + infix def eq(other: BitArray.Position): Boolean = + self eq other def hashInto(hasher: Hasher): Hasher = self.hashInto(hasher) diff --git a/tests/pos/hylolib/Collection.scala b/tests/pos/hylolib/Collection.scala index bef86a967e6e..d082dcfec8f0 100644 --- a/tests/pos/hylolib/Collection.scala +++ b/tests/pos/hylolib/Collection.scala @@ -15,7 +15,7 @@ trait Collection: /** Returns `true` iff `self` is empty. */ def isEmpty: Boolean = - startPosition `eq` endPosition + startPosition eq endPosition /** Returns the number of elements in `self`. * @@ -25,7 +25,7 @@ trait Collection: def count: Int = val e = endPosition def loop(p: Position, n: Int): Int = - if p `eq` e then n else loop(self.positionAfter(p), n + 1) + if p eq e then n else loop(self.positionAfter(p), n + 1) loop(startPosition, 0) /** Returns the position of `self`'s first element', or `endPosition` if `self` is empty. @@ -70,12 +70,12 @@ trait Collection: */ def isBefore(i: Position, j: Position): Boolean = val e = self.endPosition - if i `eq` e then false - else if j `eq` e then true + if i eq e then false + else if j eq e then true else def recur(n: Position): Boolean = - if n `eq` j then true - else if n `eq` e then false + if n eq j then true + else if n eq e then false else recur(self.positionAfter(n)) recur(self.positionAfter(i)) @@ -110,7 +110,7 @@ extension [Self: Collection](self: Self) else val p = self.startPosition val q = self.positionAfter(p) - val t = Slice(self, Range(q, self.endPosition, (a, b) => (a `eq` b) || self.isBefore(a, b))) + val t = Slice(self, Range(q, self.endPosition, (a, b) => (a eq b) || self.isBefore(a, b))) Some((self.at(p), t)) def headAndTail2: Option[(Self.Element, Self.Slice2)] = @@ -119,7 +119,7 @@ extension [Self: Collection](self: Self) else val p = self.startPosition val q = self.positionAfter(p) - val t = Self.Slice2(self, Range(q, self.endPosition, (a, b) => (a `eq` b) || self.isBefore(a, b))) + val t = Self.Slice2(self, Range(q, self.endPosition, (a, b) => (a eq b) || self.isBefore(a, b))) Some((self.at(p), t)) /** Applies `combine` on `partialResult` and each element of `self`, in order. @@ -130,7 +130,7 @@ extension [Self: Collection](self: Self) def reduce[T](partialResult: T)(combine: (T, Self.Element) => T): T = val e = self.endPosition def loop(p: Self.Position, r: T): T = - if p `eq` e then r + if p eq e then r else loop(self.positionAfter(p), combine(r, self.at(p))) loop(self.startPosition, partialResult) @@ -146,7 +146,7 @@ extension [Self: Collection](self: Self) def forEach(action: Self.Element => Boolean): Boolean = val e = self.endPosition def loop(p: Self.Position): Boolean = - if p `eq` e then true + if p eq e then true else if !action(self.at(p)) then false else loop(self.positionAfter(p)) loop(self.startPosition) @@ -194,7 +194,7 @@ extension [Self: Collection](self: Self) def firstPositionWhere(predicate: Self.Element => Boolean): Option[Self.Position] = val e = self.endPosition def loop(p: Self.Position): Option[Self.Position] = - if p `eq` e then None + if p eq e then None else if predicate(self.at(p)) then Some(p) else loop(self.positionAfter(p)) loop(self.startPosition) @@ -243,7 +243,7 @@ extension [Self: Collection](self: Self) else val e = self.endPosition def loop(p: Self.Position, least: Self.Element): Self.Element = - if p `eq` e then + if p eq e then least else val x = self.at(p) @@ -255,9 +255,9 @@ extension [Self: Collection](self: Self) /** Returns `true` if `self` contains the same elements as `other`, in the same order. */ def elementsEqual[T: Collection { type Element = Self.Element } ](other: T): Boolean = def loop(i: Self.Position, j: T.Position): Boolean = - if i `eq` self.endPosition then - j `eq` other.endPosition - else if j `eq` other.endPosition then + if i eq self.endPosition then + j eq other.endPosition + else if j eq other.endPosition then false else if self.at(i) `neq` other.at(j)then false diff --git a/tests/pos/hylolib/CoreTraits.scala b/tests/pos/hylolib/CoreTraits.scala index f4b3699b430e..bccffe8071de 100644 --- a/tests/pos/hylolib/CoreTraits.scala +++ b/tests/pos/hylolib/CoreTraits.scala @@ -14,9 +14,9 @@ trait Value: def copy(): Self /** Returns `true` iff `self` and `other` have an equivalent value. */ - def eq(other: Self): Boolean + infix def eq(other: Self): Boolean - def neq(other: Self): Boolean = !self.eq(other) + def neq(other: Self): Boolean = !(self eq other) /** Hashes the salient parts of `self` into `hasher`. */ def hashInto(hasher: Hasher): Hasher diff --git a/tests/pos/hylolib/HyArray.scala b/tests/pos/hylolib/HyArray.scala index b386b8ed4375..f8da111de3e1 100644 --- a/tests/pos/hylolib/HyArray.scala +++ b/tests/pos/hylolib/HyArray.scala @@ -165,7 +165,7 @@ given [T: Value] => HyArray[T] is Value: def copy(): HyArray[T] = self.copy() - def eq(other: HyArray[T]): Boolean = + infix def eq(other: HyArray[T]): Boolean = self.elementsEqual(other) def hashInto(hasher: Hasher): Hasher = diff --git a/tests/pos/hylolib/Integers.scala b/tests/pos/hylolib/Integers.scala index f7334ae40786..8dd9e062d530 100644 --- a/tests/pos/hylolib/Integers.scala +++ b/tests/pos/hylolib/Integers.scala @@ -8,7 +8,7 @@ given Boolean is Value: // Note: Scala's `Boolean` has value semantics already. self - def eq(other: Boolean): Boolean = + infix def eq(other: Boolean): Boolean = self == other def hashInto(hasher: Hasher): Hasher = @@ -22,7 +22,7 @@ given Int is Value: // Note: Scala's `Int` has value semantics already. self - def eq(other: Int): Boolean = + infix def eq(other: Int): Boolean = self == other def hashInto(hasher: Hasher): Hasher = @@ -35,7 +35,7 @@ given Int is Comparable: def copy(): Int = self - def eq(other: Int): Boolean = + infix def eq(other: Int): Boolean = self == other def hashInto(hasher: Hasher): Hasher =