diff --git a/community-build/community-projects/scalaz b/community-build/community-projects/scalaz index ee85b0925809..6e7f3d9caf64 160000 --- a/community-build/community-projects/scalaz +++ b/community-build/community-projects/scalaz @@ -1 +1 @@ -Subproject commit ee85b0925809f6e04808a6124ae04dd89adba0d6 +Subproject commit 6e7f3d9caf64d8ad1c82804cf418882345f41930 diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 1dfa04822766..947c4824177c 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -550,6 +550,10 @@ trait ConstraintHandling { inst end approximation + private def isTransparent(tp: Type)(using Context): Boolean = tp match + case AndType(tp1, tp2) => isTransparent(tp1) && isTransparent(tp2) + case _ => tp.typeSymbol.isTransparentClass && !tp.isLambdaSub + /** If `tp` is an intersection such that some operands are transparent trait instances * and others are not, replace as many transparent trait instances as possible with Any * as long as the result is still a subtype of `bound`. But fall back to the @@ -557,28 +561,28 @@ trait ConstraintHandling { * types (since in this case the type was not a true intersection of transparent traits * and other types to start with). */ - def dropTransparentTraits(tp: Type, bound: Type)(using Context): Type = + def dropTransparentClasses(tp: Type, bound: Type)(using Context): Type = var kept: Set[Type] = Set() // types to keep since otherwise bound would not fit var dropped: List[Type] = List() // the types dropped so far, last one on top - def dropOneTransparentTrait(tp: Type): Type = + def dropOneTransparentClass(tp: Type): Type = val tpd = tp.dealias - if tpd.typeSymbol.isTransparentTrait && !tpd.isLambdaSub && !kept.contains(tpd) then + if isTransparent(tpd) && !kept.contains(tpd) then dropped = tpd :: dropped defn.AnyType else tpd match case AndType(tp1, tp2) => - val tp1w = dropOneTransparentTrait(tp1) + val tp1w = dropOneTransparentClass(tp1) if tp1w ne tp1 then tp1w & tp2 else - val tp2w = dropOneTransparentTrait(tp2) + val tp2w = dropOneTransparentClass(tp2) if tp2w ne tp2 then tp1 & tp2w else tpd case _ => tp def recur(tp: Type): Type = - val tpw = dropOneTransparentTrait(tp) + val tpw = dropOneTransparentClass(tp) if tpw eq tp then tp else if tpw <:< bound then recur(tpw) else @@ -595,7 +599,7 @@ trait ConstraintHandling { tp else tpw - end dropTransparentTraits + end dropTransparentClasses /** If `tp` is an applied match type alias which is also an unreducible application * of a higher-kinded type to a wildcard argument, widen to the match type's bound, @@ -621,7 +625,7 @@ trait ConstraintHandling { * union type (except for unions | Null, which are kept in the state they were). * 3. Widen some irreducible applications of higher-kinded types to wildcard arguments * (see @widenIrreducible). - * 4. Drop transparent traits from intersections (see @dropTransparentTraits). + * 4. Drop transparent traits from intersections (see @dropTransparentClasses). * * Don't do these widenings if `bound` is a subtype of `scala.Singleton`. * Also, if the result of these widenings is a TypeRef to a module class, @@ -648,7 +652,16 @@ trait ConstraintHandling { val wideInst = if isSingleton(bound) then inst - else dropTransparentTraits(widenIrreducible(widenOr(widenSingle(inst))), bound) + else + val widenedFromSingle = widenSingle(inst) + val widenedFromUnion = widenOr(widenedFromSingle) + val widened = + if (widenedFromUnion ne widenedFromSingle) && isTransparent(widenedFromUnion) then + widenedFromSingle + else + dropTransparentClasses(widenedFromUnion, bound) + widenIrreducible(widened) + wideInst match case wideInst: TypeRef if wideInst.symbol.is(Module) => TermRef(wideInst.prefix, wideInst.symbol.sourceModule) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 174244b4a456..e4cc7e673f72 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1827,20 +1827,53 @@ class Definitions { def isInfix(sym: Symbol)(using Context): Boolean = (sym eq Object_eq) || (sym eq Object_ne) - @tu lazy val assumedTransparentTraits = - Set[Symbol](ComparableClass, ProductClass, SerializableClass, - // add these for now, until we had a chance to retrofit 2.13 stdlib - // we should do a more through sweep through it then. - requiredClass("scala.collection.SortedOps"), - requiredClass("scala.collection.StrictOptimizedSortedSetOps"), - requiredClass("scala.collection.generic.DefaultSerializable"), - requiredClass("scala.collection.generic.IsIterable"), - requiredClass("scala.collection.generic.IsIterableOnce"), - requiredClass("scala.collection.generic.IsMap"), - requiredClass("scala.collection.generic.IsSeq"), - requiredClass("scala.collection.generic.Subtractable"), - requiredClass("scala.collection.immutable.StrictOptimizedSeqOps") - ) + @tu lazy val assumedTransparentNames: Map[Name, Set[Symbol]] = + // add these for now, until we had a chance to retrofit 2.13 stdlib + // we should do a more through sweep through it then. + val strs = Map( + "Any" -> Set("scala"), + "AnyVal" -> Set("scala"), + "Matchable" -> Set("scala"), + "Product" -> Set("scala"), + "Object" -> Set("java.lang"), + "Comparable" -> Set("java.lang"), + "Serializable" -> Set("java.io"), + "BitSetOps" -> Set("scala.collection"), + "IndexedSeqOps" -> Set("scala.collection", "scala.collection.mutable", "scala.collection.immutable"), + "IterableOnceOps" -> Set("scala.collection"), + "IterableOps" -> Set("scala.collection"), + "LinearSeqOps" -> Set("scala.collection", "scala.collection.immutable"), + "MapOps" -> Set("scala.collection", "scala.collection.mutable", "scala.collection.immutable"), + "SeqOps" -> Set("scala.collection", "scala.collection.mutable", "scala.collection.immutable"), + "SetOps" -> Set("scala.collection", "scala.collection.mutable", "scala.collection.immutable"), + "SortedMapOps" -> Set("scala.collection", "scala.collection.mutable", "scala.collection.immutable"), + "SortedOps" -> Set("scala.collection"), + "SortedSetOps" -> Set("scala.collection", "scala.collection.mutable", "scala.collection.immutable"), + "StrictOptimizedIterableOps" -> Set("scala.collection"), + "StrictOptimizedLinearSeqOps" -> Set("scala.collection"), + "StrictOptimizedMapOps" -> Set("scala.collection", "scala.collection.immutable"), + "StrictOptimizedSeqOps" -> Set("scala.collection", "scala.collection.immutable"), + "StrictOptimizedSetOps" -> Set("scala.collection", "scala.collection.immutable"), + "StrictOptimizedSortedMapOps" -> Set("scala.collection", "scala.collection.immutable"), + "StrictOptimizedSortedSetOps" -> Set("scala.collection", "scala.collection.immutable"), + "ArrayDequeOps" -> Set("scala.collection.mutable"), + "DefaultSerializable" -> Set("scala.collection.generic"), + "IsIterable" -> Set("scala.collection.generic"), + "IsIterableLowPriority" -> Set("scala.collection.generic"), + "IsIterableOnce" -> Set("scala.collection.generic"), + "IsIterableOnceLowPriority" -> Set("scala.collection.generic"), + "IsMap" -> Set("scala.collection.generic"), + "IsSeq" -> Set("scala.collection.generic")) + strs.map { case (simple, pkgs) => ( + simple.toTypeName, + pkgs.map(pkg => staticRef(pkg.toTermName, isPackage = true).symbol.moduleClass) + ) + } + + def isAssumedTransparent(sym: Symbol): Boolean = + assumedTransparentNames.get(sym.name) match + case Some(pkgs) => pkgs.contains(sym.owner) + case none => false // ----- primitive value class machinery ------------------------------------------ diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 8bf65ed8288f..a91da1cf5f96 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -357,7 +357,7 @@ object Flags { val (_, DefaultMethod @ _, _) = newFlags(38, "") /** Symbol is a transparent inline method or trait */ - val (Transparent @ _, _, _) = newFlags(39, "transparent") + val (Transparent @ _, _, TransparentType @ _) = newFlags(39, "transparent") /** Symbol is an enum class or enum case (if used with case) */ val (Enum @ _, EnumVal @ _, _) = newFlags(40, "enum") @@ -609,5 +609,4 @@ object Flags { val SyntheticParam: FlagSet = Synthetic | Param val SyntheticTermParam: FlagSet = Synthetic | TermParam val SyntheticTypeParam: FlagSet = Synthetic | TypeParam - val TransparentTrait: FlagSet = Trait | Transparent } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index d0bf0f4da6dc..f0ab8253da72 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1151,9 +1151,9 @@ object SymDenotations { final def isEffectivelySealed(using Context): Boolean = isOneOf(FinalOrSealed) || isClass && !isOneOf(EffectivelyOpenFlags) - final def isTransparentTrait(using Context): Boolean = - isAllOf(TransparentTrait) - || defn.assumedTransparentTraits.contains(symbol) + final def isTransparentClass(using Context): Boolean = + is(TransparentType) + || defn.isAssumedTransparent(symbol) || isClass && hasAnnotation(defn.TransparentTraitAnnot) /** The class containing this denotation which has the given effective name. */ diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 283a7e3a474e..eaaa93cfaa61 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -3007,8 +3007,8 @@ object TypeComparer { def widenInferred(inst: Type, bound: Type, widenUnions: Boolean)(using Context): Type = comparing(_.widenInferred(inst, bound, widenUnions)) - def dropTransparentTraits(tp: Type, bound: Type)(using Context): Type = - comparing(_.dropTransparentTraits(tp, bound)) + def dropTransparentClasses(tp: Type, bound: Type)(using Context): Type = + comparing(_.dropTransparentClasses(tp, bound)) def constrainPatternType(pat: Type, scrut: Type, forceInvariantRefinement: Boolean = false)(using Context): Boolean = comparing(_.constrainPatternType(pat, scrut, forceInvariantRefinement)) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 7bcceaed1112..ae96fe600a3c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1211,7 +1211,7 @@ trait Applications extends Compatibility { && tree.tpe.classSymbol.isEnumCase && tree.tpe.widen.isValueType then - val widened = TypeComparer.dropTransparentTraits( + val widened = TypeComparer.dropTransparentClasses( tree.tpe.parents.reduceLeft(TypeComparer.andType(_, _)), pt) if widened <:< pt then Typed(tree, TypeTree(widened)) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index c53213d7bd37..06cf74e5a83c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -495,7 +495,7 @@ object Checking { } if sym.is(Transparent) then if sym.isType then - if !sym.is(Trait) then fail(em"`transparent` can only be used for traits".toMessage) + if !sym.isExtensibleClass then fail(em"`transparent` can only be used for extensible classes and traits".toMessage) else if !sym.isInlineMethod then fail(em"`transparent` can only be used for inline methods".toMessage) if (!sym.isClass && sym.is(Abstract)) diff --git a/compiler/test/dotty/tools/repl/ShadowingTests.scala b/compiler/test/dotty/tools/repl/ShadowingTests.scala index 62a2322e38f0..457819966346 100644 --- a/compiler/test/dotty/tools/repl/ShadowingTests.scala +++ b/compiler/test/dotty/tools/repl/ShadowingTests.scala @@ -122,7 +122,7 @@ class ShadowingTests extends ReplTest(options = ShadowingTests.options): |val y: String = foo | |scala> if (true) x else y - |val res0: Matchable = 42 + |val res0: Int | String = 42 |""".stripMargin.linesIterator.toList ) diff --git a/docs/_docs/reference/new-types/union-types-spec.md b/docs/_docs/reference/new-types/union-types-spec.md index d250d3f11713..1093631e7c63 100644 --- a/docs/_docs/reference/new-types/union-types-spec.md +++ b/docs/_docs/reference/new-types/union-types-spec.md @@ -72,6 +72,10 @@ a non-union type, for this purpose we define the _join_ of a union type `T1 | `T1`,...,`Tn`. Note that union types might still appear as type arguments in the resulting type, this guarantees that the join is always finite. +The _visible join_ of a union type is its join where all operands of the intersection that +are instances of [transparent](../other-new-features/transparent-traits.md) traits or classes are removed. + + ### Example Given @@ -80,31 +84,50 @@ Given trait C[+T] trait D trait E -class A extends C[A] with D -class B extends C[B] with D with E +transparent trait X +class A extends C[A], D, X +class B extends C[B], D, E, X ``` -The join of `A | B` is `C[A | B] & D` +The join of `A | B` is `C[A | B] & D & X` and the visible join of `A | B` is `C[A | B] & D`. + +## Hard and Soft Union Types + +We distinguish between hard and soft union types. A _hard_ union type is a union type that's explicitly +written in the source. For instance, in +```scala +val x: Int | String = ... +``` +`Int | String` would be a hard union type. A _soft_ union type is a type that arises from type checking +an alternative of expressions. For instance, the type of the expression +```scala +val x = 1 +val y = "abc" +if cond then x else y +``` +is the soft unon type `Int | String`. Similarly for match expressions. The type of +```scala +x match + case 1 => x + case 2 => "abc" + case 3 => List(1, 2, 3) +``` +is the soft union type `Int | "abc" | List[Int]`. + ## Type inference When inferring the result type of a definition (`val`, `var`, or `def`) and the -type we are about to infer is a union type, then we replace it by its join. +type we are about to infer is a soft union type, then we replace it by its visible join, +provided it is not empty. Similarly, when instantiating a type argument, if the corresponding type parameter is not upper-bounded by a union type and the type we are about to -instantiate is a union type, we replace it by its join. This mirrors the +instantiate is a soft union type, we replace it by its visible join, provided it is not empty. +This mirrors the treatment of singleton types which are also widened to their underlying type unless explicitly specified. The motivation is the same: inferring types which are "too precise" can lead to unintuitive typechecking issues later on. -**Note:** Since this behavior limits the usability of union types, it might -be changed in the future. For example by not widening unions that have been -explicitly written down by the user and not inferred, or by not widening a type -argument when the corresponding type parameter is covariant. - -See [PR #2330](https://github.com/lampepfl/dotty/pull/2330) and -[Issue #4867](https://github.com/lampepfl/dotty/issues/4867) for further discussions. - ### Example ```scala diff --git a/docs/_docs/reference/new-types/union-types.md b/docs/_docs/reference/new-types/union-types.md index ebc4565e36fb..978e08649d9e 100644 --- a/docs/_docs/reference/new-types/union-types.md +++ b/docs/_docs/reference/new-types/union-types.md @@ -8,8 +8,9 @@ A union type `A | B` has as values all values of type `A` and also all values of ```scala -case class UserName(name: String) -case class Password(hash: Hash) +trait ID +case class UserName(name: String) extends ID +case class Password(hash: Hash) extends ID def help(id: UserName | Password) = val user = id match @@ -22,7 +23,10 @@ Union types are duals of intersection types. `|` is _commutative_: `A | B` is the same type as `B | A`. The compiler will assign a union type to an expression only if such a -type is explicitly given. This can be seen in the following [REPL](https://docs.scala-lang.org/overviews/repl/overview.html) transcript: +type is explicitly given or if the common supertype of all alternatives is [transparent](../other-new-features/transparent-traits.md). + + +This can be seen in the following [REPL](https://docs.scala-lang.org/overviews/repl/overview.html) transcript: ```scala scala> val password = Password(123) @@ -32,15 +36,36 @@ scala> val name = UserName("Eve") val name: UserName = UserName(Eve) scala> if true then name else password -val res2: Object = UserName(Eve) +val res1: ID = UserName(Eve) scala> val either: Password | UserName = if true then name else password -val either: Password | UserName = UserName(Eve) +val either: UserName | Password = UserName(Eve) ``` - -The type of `res2` is `Object & Product`, which is a supertype of -`UserName` and `Password`, but not the least supertype `Password | -UserName`. If we want the least supertype, we have to give it +The type of `res1` is `ID`, which is a supertype of +`UserName` and `Password`, but not the least supertype `UserName | Password`. +If we want the least supertype, we have to give it explicitly, as is done for the type of `either`. +The inference behavior changes if the common supertrait `ID` is declared `transparent`: +```scala +transparent trait ID +``` +In that case the union type is not widened. +```scala +scala> if true then name else password +val res2: UserName | Password = UserName(Eve) +``` +The more precise union type is also inferred if `UserName` and `Password` are declared without an explicit +parent, since in that case their implied superclass is `Object`, which is among the classes that are +assumed to be transparent. See [Transparent Traits and Classes](../other-new-features/transparent-traits.md) +for a list of such classes. +```scala +case class UserName(name: String) +case class Password(hash: Hash) + +scala> if true then UserName("Eve") else Password(123) +val res3: UserName | Password = UserName(Eve) +``` + + [More details](./union-types-spec.md) diff --git a/docs/_docs/reference/other-new-features/transparent-traits.md b/docs/_docs/reference/other-new-features/transparent-traits.md index 699ce0b9ddd8..f2837d53401c 100644 --- a/docs/_docs/reference/other-new-features/transparent-traits.md +++ b/docs/_docs/reference/other-new-features/transparent-traits.md @@ -1,6 +1,6 @@ --- layout: doc-page -title: "Transparent Traits" +title: "Transparent Traits and Classes" nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/transparent-traits.html --- @@ -20,12 +20,13 @@ val x = Set(if condition then Val else Var) Here, the inferred type of `x` is `Set[Kind & Product & Serializable]` whereas one would have hoped it to be `Set[Kind]`. The reasoning for this particular type to be inferred is as follows: -- The type of the conditional above is the [union type](../new-types/union-types.md) `Val | Var`. -- A union type is widened in type inference to the least supertype that is not a union type. - In the example, this type is `Kind & Product & Serializable` since all three traits are traits of both `Val` and `Var`. +- The type of the conditional above is the [union type](../new-types/union-types.md) `Val | Var`. This union type is treated as "soft", which means it was not explicitly written in the source program, but came from forming an upper bound of the types of +some alternatives. +- A soft union type is widened in type inference to the least product of class or trait types that is a supertype of the union type. + In the example, this type is `Kind & Product & Serializable` since all three traits are super-traits of both `Val` and `Var`. So that type becomes the inferred element type of the set. -Scala 3 allows one to mark a mixin trait as `transparent`, which means that it can be suppressed in type inference. Here's an example that follows the lines of the code above, but now with a new transparent trait `S` instead of `Product`: +Scala 3 allows one to mark a trait or class as `transparent`, which means that it can be suppressed in type inference. Here's an example that follows the lines of the code above, but now with a new transparent trait `S` instead of `Product`: ```scala transparent trait S @@ -38,13 +39,40 @@ val x = Set(if condition then Val else Var) Now `x` has inferred type `Set[Kind]`. The common transparent trait `S` does not appear in the inferred type. -## Transparent Traits +In the previous example, one could also declare `Kind` as `transparent`: +```scala +transparent trait Kind +``` +The widened union type of `if condition then Val else Var` would then +_only_ contain the transparent traits `Kind` and `S`. In this case, +the widening is not performed at all, so `x` would have type `Set[Val | Var]`. + +The root classes and traits `Any`, `AnyVal`, `Object`, and `Matchable` are +considered to be transparent. This means that an expression such +as +```scala +if condition then 1 else "hello" +``` +will have type `Int | String` instead of the widened type `Any`. + -The traits [`scala.Product`](https://scala-lang.org/api/3.x/scala/Product.html), [`java.io.Serializable`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/Serializable.html) and [`java.lang.Comparable`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Comparable.html) -are treated automatically as transparent. Other traits are turned into transparent traits using the modifier `transparent`. Scala 2 traits can also be made transparent -by adding a [`@transparentTrait`](https://scala-lang.org/api/3.x/scala/annotation/transparentTrait.html) annotation. This annotation is defined in [`scala.annotation`](https://scala-lang.org/api/3.x/scala/annotation.html). It will be deprecated and phased out once Scala 2/3 interoperability is no longer needed. -Typically, transparent traits are traits +## Which Traits and Classes Are Transparent? + +Traits and classes are declared transparent by adding the modifier `transparent`. Scala 2 traits and classes can also be declared transparent by adding a [`@transparentTrait`](https://scala-lang.org/api/3.x/scala/annotation/transparentTrait.html) annotation. This annotation is defined in [`scala.annotation`](https://scala-lang.org/api/3.x/scala/annotation.html). It will be deprecated and phased out once Scala 2/3 interoperability is no longer needed. + +The following classes and traits are automatically treated as transparent: +```scala + scala.Any + scala.AnyVal + scala.Matchable + scala.Product + java.lang.Object + java.lang.Comparable + java.io.Serializable +``` + +Typically, transparent types other than the root classes are traits that influence the implementation of inheriting classes and traits that are not usually used as types by themselves. Two examples from the standard collection library are: - [`IterableOps`](https://scala-lang.org/api/3.x/scala/collection/IterableOps.html), which provides method implementations for an [`Iterable`](https://scala-lang.org/api/3.x/scala/collection/Iterable.html). @@ -55,7 +83,10 @@ declared transparent. ## Rules for Inference -Transparent traits can be given as explicit types as usual. But they are often elided when types are inferred. Roughly, the rules for type inference say that transparent traits are dropped from intersections where possible. +Transparent traits and classes can be given as explicit types as usual. But they are often elided when types are inferred. Roughly, the rules for type inference imply the following. + + - Transparent traits and classes are dropped from intersections where possible. + - Union types are not widened if widening would result in only transparent supertypes. The precise rules are as follows: @@ -65,6 +96,6 @@ The precise rules are as follows: - If the type inferred so far is of the form `T1 & ... & Tn` where `n >= 1`, replace the maximal number of transparent `Ti`s by `Any`, while ensuring that the resulting type is still a subtype of the bound `B`. -- However, do not perform this widening if all transparent traits `Ti` can get replaced in that way. +- However, do not perform this widening if all transparent types `Ti` can get replaced in that way. This clause ensures that a single transparent trait instance such as [`Product`](https://scala-lang.org/api/3.x/scala/Product.html) is not widened to [`Any`](https://scala-lang.org/api/3.x/scala/Any.html). Transparent trait instances are only dropped when they appear in conjunction with some other type. -The last clause ensures that a single transparent trait instance such as [`Product`](https://scala-lang.org/api/3.x/scala/Product.html) is not widened to [`Any`](https://scala-lang.org/api/3.x/scala/Any.html). Transparent trait instances are only dropped when they appear in conjunction with some other type. +- If the original type was a is union type that got widened in a previous step to a product consisting only of transparent types, keep the original union type instead of its widened form. \ No newline at end of file diff --git a/tests/neg/harmonize.scala b/tests/neg/harmonize.scala index 0fe03d2d7600..72275a8f68fc 100644 --- a/tests/neg/harmonize.scala +++ b/tests/neg/harmonize.scala @@ -79,9 +79,9 @@ object Test { val a4 = ArrayBuffer(1.0f, 1L) val b4: ArrayBuffer[Double] = a4 // error: no widening val a5 = ArrayBuffer(1.0f, 1L, f()) - val b5: ArrayBuffer[AnyVal] = a5 + val b5: ArrayBuffer[Float | Long | Int] = a5 val a6 = ArrayBuffer(1.0f, 1234567890) - val b6: ArrayBuffer[AnyVal] = a6 + val b6: ArrayBuffer[Float | Int] = a6 def totalDuration(results: List[Long], cond: Boolean): Long = results.map(r => if (cond) r else 0).sum diff --git a/tests/neg/supertraits.scala b/tests/neg/supertraits.scala index 2fc79ca30f1d..6952c7640529 100644 --- a/tests/neg/supertraits.scala +++ b/tests/neg/supertraits.scala @@ -6,19 +6,20 @@ class C extends A, S val x = if ??? then B() else C() val x1: S = x // error -case object a -case object b +class Top +case object a extends Top +case object b extends Top val y = if ??? then a else b val y1: Product = y // error val y2: Serializable = y // error -enum Color { +enum Color extends Top { case Red, Green, Blue } -enum Nucleobase { +enum Nucleobase extends Top { case A, C, G, T } val z = if ??? then Color.Red else Nucleobase.G -val z1: reflect.Enum = z // error: Found: (z : Object) Required: reflect.Enum +val z1: reflect.Enum = z // error: Found: (z : Top) Required: reflect.Enum diff --git a/tests/neg/transparent.scala b/tests/neg/transparent.scala index b4d89478b0ac..95899bfa0b33 100644 --- a/tests/neg/transparent.scala +++ b/tests/neg/transparent.scala @@ -1,7 +1,8 @@ transparent def foo = 1 // error transparent inline def bar = 2 // ok transparent inline val x = 2 // error -transparent class c // error +transparent class c // ok +transparent final class d // error transparent object y // error transparent trait t // ok transparent type T = c // error diff --git a/tests/neg/union.scala b/tests/neg/union.scala index 0a702ab70058..c6fd42e6629e 100644 --- a/tests/neg/union.scala +++ b/tests/neg/union.scala @@ -11,8 +11,9 @@ object Test { } object O { - class A - class B + class Top + class A extends Top + class B extends Top def f[T](x: T, y: T): T = x val x: A = f(new A { }, new A) diff --git a/tests/neg-custom-args/allow-deep-subtypes/i15365.scala b/tests/pos-deep-subtype/i15365.scala similarity index 100% rename from tests/neg-custom-args/allow-deep-subtypes/i15365.scala rename to tests/pos-deep-subtype/i15365.scala diff --git a/tests/pos/i14494.scala b/tests/pos/i14494.scala index 33170dea035b..bb45b6d67073 100644 --- a/tests/pos/i14494.scala +++ b/tests/pos/i14494.scala @@ -1,6 +1,9 @@ object ImplNotFound: + class TOP + class STR(s: String) extends TOP + class INT(i: Int) extends TOP def main(args: Array[String]): Unit = - val res: Seq[String | Int] = (??? : Seq[Int]).collect { - case 1 => Seq("") - case 2 => Seq(1) + val res: Seq[STR | INT] = (??? : Seq[Int]).collect { + case 1 => Seq(STR("")) + case 2 => Seq(INT(1)) }.flatten \ No newline at end of file diff --git a/tests/pos/transparent.scala b/tests/pos/inlined-class.scala similarity index 100% rename from tests/pos/transparent.scala rename to tests/pos/inlined-class.scala diff --git a/tests/pos/transparent-union.scala b/tests/pos/transparent-union.scala new file mode 100644 index 000000000000..b2b75703b8e1 --- /dev/null +++ b/tests/pos/transparent-union.scala @@ -0,0 +1,8 @@ +transparent trait ID +case class UserName(name: String) extends ID +case class Password(hash: Int) extends ID + +val password: Password = Password(123) +val name = UserName("Eve") +val res = if ??? then name else password +val _: UserName | Password = res \ No newline at end of file diff --git a/tests/pos/unions.scala b/tests/pos/unions.scala new file mode 100644 index 000000000000..d51425788065 --- /dev/null +++ b/tests/pos/unions.scala @@ -0,0 +1,23 @@ +object Test: + + def test = + val x = if ??? then "" else 1 + val _: String | Int = x + +object Test2: + transparent class A + class B extends A + class C extends A + val x = if ??? then B() else C() + val _: B | C = x + +object Test3: + class A + class B extends A + class C extends A + val x = if ??? then B() else C() + val _: A = x + + + + diff --git a/tests/run/weak-conformance.scala b/tests/run/weak-conformance.scala index fdd28dbc524a..db551cf0ce15 100644 --- a/tests/run/weak-conformance.scala +++ b/tests/run/weak-conformance.scala @@ -26,11 +26,11 @@ object Test extends App { locally { def f(): Int = b + 1 val x1 = ArrayBuffer(b, 33, 5.5) ; x1: ArrayBuffer[Double] // b is an inline val - val x2 = ArrayBuffer(f(), 33, 5.5) ; x2: ArrayBuffer[AnyVal] // f() is not a constant + val x2 = ArrayBuffer(f(), 33, 5.5) ; x2: ArrayBuffer[Int | Double] // f() is not a constant val x3 = ArrayBuffer(5, 11L) ; x3: ArrayBuffer[Long] - val x4 = ArrayBuffer(5, 11L, 5.5) ; x4: ArrayBuffer[AnyVal] // Long and Double found + val x4 = ArrayBuffer(5, 11L, 5.5) ; x4: ArrayBuffer[Int | Long | Double] // Long and Double found val x5 = ArrayBuffer(1.0f, 2) ; x5: ArrayBuffer[Float] - val x6 = ArrayBuffer(1.0f, 1234567890); x6: ArrayBuffer[AnyVal] // loss of precision + val x6 = ArrayBuffer(1.0f, 1234567890); x6: ArrayBuffer[Float | Int] // loss of precision val x7 = ArrayBuffer(b, 33, 'a') ; x7: ArrayBuffer[Char] val x8 = ArrayBuffer(5.toByte, 11) ; x8: ArrayBuffer[Byte] diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 4f7d6692eee7..0b2d618262ec 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -1786,7 +1786,7 @@ Symbols: example/InstrumentTyper# => class InstrumentTyper extends Object { self: AnyRef & InstrumentTyper => +5 decls } example/InstrumentTyper#AnnotatedType# => type AnnotatedType = Int @param example/InstrumentTyper#``(). => primary ctor (): InstrumentTyper -example/InstrumentTyper#all(). => method all => List[Matchable] +example/InstrumentTyper#all(). => method all => List[Float | Double | List[Nothing] | Boolean | Unit | Char | String | LinkOption | Int | Long | Class[Option[Int]]] example/InstrumentTyper#clazzOf. => final val method clazzOf Option[Int] example/InstrumentTyper#singletonType(). => method singletonType (param x: Predef.type): Nothing example/InstrumentTyper#singletonType().(x) => param x: Predef.type @@ -1847,7 +1847,7 @@ Occurrences: [24:37..24:40): Int -> scala/Int# Synthetics: -[8:12..8:16):List => *.apply[Matchable] +[8:12..8:16):List => *.apply[Float | Double | List[Nothing] | Boolean | Unit | Char | String | LinkOption | Int | Long | Class[Option[Int]]] [20:4..20:8):List => *.apply[Nothing] expect/InventedNames.scala