diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index cb137d46476a..b7de824d2952 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -431,11 +431,18 @@ object desugar { // For all other classes, the parent is AnyRef. val companions = if (isCaseClass) { + def extractType(t: Tree): Tree = t match { + case Apply(t1, _) => extractType(t1) + case TypeApply(t1, ts) => AppliedTypeTree(extractType(t1), ts) + case Select(t1, nme.CONSTRUCTOR) => extractType(t1) + case New(t1) => t1 + case t1 => t1 + } // The return type of the `apply` method val applyResultTpt = if (isEnumCase) if (parents.isEmpty) enumClassTypeRef - else parents.reduceLeft(AndTypeTree) + else parents.map(extractType).reduceLeft(AndTypeTree) else TypeTree() val parent = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 644bcd7cbe0a..7df0f0dc4437 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -443,13 +443,19 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit tree.tpt match { case templ: untpd.Template => import untpd._ - templ.parents foreach { + var templ1 = templ + def isEligible(tp: Type) = tp.exists && !tp.typeSymbol.is(Final) + if (templ1.parents.isEmpty && + isFullyDefined(pt, ForceDegree.noBottom) && + isEligible(pt.underlyingClassRef(refinementOK = false))) + templ1 = cpy.Template(templ)(parents = untpd.TypeTree(pt) :: Nil) + templ1.parents foreach { case parent: RefTree => typedAheadImpl(parent, tree => inferTypeParams(typedType(tree), pt)) case _ => } val x = tpnme.ANON_CLASS - val clsDef = TypeDef(x, templ).withFlags(Final) + val clsDef = TypeDef(x, templ1).withFlags(Final) typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(x), Nil)), pt) case _ => var tpt1 = typedType(tree.tpt) diff --git a/docs/docs/index.md b/docs/docs/index.md index d4661a497503..b2c1f1b5dfc0 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -14,6 +14,14 @@ the code, setup Dotty with your favorite IDE and more! Contents ------- +* Dotty Language Reference + - [Intersection Types](reference/intersection-types.md) + - [Union Types](reference/union-types.md) + - [Trait Parameters](reference/trait-parameters.md) + - [Enumerations](reference/enums.md) + - [Algebraic Data Types](reference/adts.md) + - [Enum Translation](reference/desugarEnums.md) + - [By-Name Implicits](reference/implicit-by-name-parameters.md) * Usage - [Migrating from Scala 2](usage/migrating.md): migration information - [Dotty projects with cbt](usage/cbt-projects.md): using cbt diff --git a/docs/docs/reference/adts.md b/docs/docs/reference/adts.md new file mode 100644 index 000000000000..9c4f83a6db93 --- /dev/null +++ b/docs/docs/reference/adts.md @@ -0,0 +1,122 @@ +--- +layout: doc-page +title: "Algebraic Data Types" +--- + + +The `enum` concept is general enough to also support algebraic data +types (ADTs) and their generalized version (GADTs). Here's an example +how an `Option` type can be represented as an ADT: + +```scala +enum Option[+T] { + case Some[+T](x: T) + case None +} +``` + +This example introduces `Option` enum class with a covariant type +parameter `T`, together with two cases, `Some` and `None`. `Some` is +parameterized with a type parameter `T` and a value parameter `x`. It +is a shorthand for writing a case class that extends `Option`. Since +`None` is not parameterized it is treated as a normal enum value. + +The `extends` clauses that were omitted in the example above can also +be given explicitly: + +```scala +enum Option[+T] { + case Some[+T](x: T) extends Option[T] + case None extends Option[Nothing] +} +``` + +Note that the parent type of `None` is inferred as +`Option[Nothing]`. Generally, all covariant type parameters of the enum +class are minimized in a compiler-generated extends clause whereas all +contravariant type parameters are maximized. If `Option` was non-variant, +you'd need to give the extends clause of `None` explicitly. + +As for normal enum values, the cases of an `enum` are all defined in +the `enum`s companion object. So it's `Option.Some` and `Option.None` +unless the definitions are "pulled out" with an import: + +```scala +scala> Option.Some("hello") +val res1: t2.Option[String] = Some(hello) +scala> Option.None +val res2: t2.Option[Nothing] = None +``` + +Note that the type of the expressions above is always `Option`. That +is, the implementation case classes are not visible in the result +types of their `apply` methods. This is a subtle difference with +respect to normal case classes. The classes making up the cases do +exist, and can be unvealed by constructing them directly with a `new`. + +```scala +val res3: t2.Option.Some[Int] = Some(2) +scala> scala> new Option.Some(2) +``` + +As all other enums, ADTs can have methods on both the enum class and +its companion object. For instance, here is `Option` again, with an +`isDefined` method and an `Option(...)` constructor. + +```scala +enum class Option[+T] { + def isDefined: Boolean +} +object Option { + def apply[T >: Null](x: T): Option[T] = + if (x == null) None else Some(x) + + case Some[+T](x: T) { + def isDefined = true + } + case None { + def isDefined = false + } +} +``` + +Enumerations and ADTs have been presented as two different +concepts. But since they share the same syntactic construct, they can +be seen simply as two ends of a spectrum and it is perfectly possible +to conctruct hybrids. For instance, the code below gives an +implementation of `Color` either with three enum values or with a +parameterized case that takes an RGB value. + +```scala +enum Color(val rgb: Int) { + case Red extends Color(0xFF0000) + case Green extends Color(0x00FF00) + case Blue extends Color(0x0000FF) + case Mix(mix: Int) extends Color(mix) +} +``` + +## Syntax of Enums + +Changes to the syntax fall in two categories: enum classes and cases inside enums. +The changes are specified below as deltas with respect to the Scala syntax given [here](https://github.com/lampepfl/dotty/blob/master/docs/docs/internals/syntax.md) + + 1. Enum definitions and enum classes are defined as follows: + + TmplDef ::= `enum' `class’ ClassDef + | `enum' EnumDef + EnumDef ::= id ClassConstr [`extends' [ConstrApps]] + [nl] `{’ EnumCaseStat {semi EnumCaseStat} `}’ + +2. Cases of enums are defined as follows: + + EnumCaseStat ::= {Annotation [nl]} {Modifier} EnumCase + EnumCase ::= `case' (EnumClassDef | ObjectDef | ids) + EnumClassDef ::= id [ClsTpeParamClause | ClsParamClause] + ClsParamClauses TemplateOpt + TemplateStat ::= ... | EnumCaseStat + + + + + diff --git a/docs/docs/reference/desugarEnums.md b/docs/docs/reference/desugarEnums.md new file mode 100644 index 000000000000..c07d08d37d56 --- /dev/null +++ b/docs/docs/reference/desugarEnums.md @@ -0,0 +1,165 @@ +--- +layout: doc-page +title: "Translation of Enums and ADTs" +--- + +The compiler expands enum classes and cases to code that only uses +Scala's other language features. As such, enums in Scala are +convenient _syntactic sugar_, but they are not essential to understand +Scala's core. + +We now explain the expansion of enums is explained in detail. First, +some terminology and notational conventions: + + - We use `E` as a name of an enum class, and `C` as a name of an enum case that appears in the companion object of `E`. + - We use `<...>` for syntactic constructs that in some circumstances might be empty. For instance `` represents either the body of a case between `{...}` or nothing at all. + + - Enum cases fall into three categories: + + - _Class cases_ are those cases that are parameterized, either with a type parameter section `[...]` or with one or more (possibly empty) parameter sections `(...)`. + - _Simple cases_ are cases of a non-generic enum class that have neither parameters nor an extends clause or body. That is, they consist of a name only. + - _Value cases_ are all cases that do not have a parameter section but that do have a (possibly generated) extends clause and/or a body. + + Simple cases and value cases are called collectively _singleton cases_. + +The desugaring rules imply that class cases are mapped to case classes, and singleton cases are mapped to `val` definitions. + +There are eight desugaring rules. Rules (1) and (2) desugar enums and +enum classes. Rules (3) and (4) define extends clauses for cases that +are missing them. Rules (5 - 7) define how such expanded cases map +into case classes, case objects or vals. Finally, rule (8) expands +comma separated simple cases into a sequence of cases. + +1. An `enum` definition + + enum E ... { } + + expands to an enum class and a companion object + + enum class E ... + object E { } + +2. An enum class definition + + enum class E ... extends ... + + expands to a `sealed` `abstract` class that extends the `scala.Enum` trait: + + sealed abstract class E ... extends with scala.Enum ... + +3. If `E` is an enum class without type parameters, then a case in its companion object without an extends clause + + case C + + expands to + + case C extends E + +4. If `E` is an enum class with type parameters `Ts`, then a case in its + companion object without an extends clause + + case C + + expands according to two alternatives, depending whether `C` has type + parameters or not. If `C` has type parameters, they must have the same + names and appear in the same order as the enum type parameters `Ts` + (variances may be different, however). In this case + + case C [Ts] + + expands to + + case C[Ts] extends E[Ts] + + For the case where `C` does not have type parameters, assume `E`'s type + parameters are + + V1 T1 > L1 <: U1 , ... , Vn Tn >: Ln <: Un (n > 0) + + where each of the variances `Vi` is either `'+'` or `'-'`. Then the case + expands to + + case C extends E[B1, ..., Bn] + + where `Bi` is `Li` if `Vi = '+'` and `Ui` if `Vi = '-'`. It is an error if + `Bi` refers to some other type parameter `Tj (j = 0,..,n-1)`. It is also + an error if `E` has type parameters that are non-variant. + +5. A class case + + case C ... + + expands analogous to a case class: + + final case class C ... + + However, unlike for a regular case class, the return type of the associated + `apply` method is a fully parameterized type instance of the enum class `E` + itself instead of `C`. Also the enum case defines an `enumTag` method of + the form + + def enumTag = n + + where `n` is the ordinal number of the case in the companion object, + starting from 0. + +6. A value case + + case C extends + + expands to a value definition + + val C = new { ; def enumTag = n; $values.register(this) } + + where `n` is the ordinal number of the case in the companion object, + starting from 0. The statement `$values.register(this)` registers the value + as one of the `enumValues` of the enumeration (see below). `$values` is a + compiler-defined private value in the companion object. + +7. A simple case + + case C + + of an enum class `E` that does not take type parameters expands to + + val C = $new(n, "C") + + Here, `$new` is a private method that creates an instance of of `E` (see + below). + +8. A simple case consisting of a comma-separated list of enum names + + case C_1, ..., C_n + + expands to + + case C_1; ...; case C_n + + Any modifiers or annotations on the original case extend to all expanded + cases. + +## Translation of Enumerations + +Non-generic enum classes `E` that define one or more singleton cases +are called _enumerations_. Companion objects of enumerations define +the following additional members. + + - A method `enumValue` of type `scala.collection.immutable.Map[Int, E]`. + `enumValue(n)` returns the singleton case value with ordinal number `n`. + - A method `enumValueNamed` of type `scala.collection.immutable.Map[String, E]`. + `enumValueNamed(s)` returns the singleton case value whose `toString` + representation is `s`. + - A method `enumValues` which returns an `Iterable[E]` of all singleton case + values in `E`, in the order of their definitions. + +Companion objects that contain at least one simple case define in addition: + + - A private method `$new` which defines a new simple case value with given + ordinal number and name. This method can be thought as being defined as + follows. + + def $new(tag: Int, name: String): ET = new E { + def enumTag = tag + def toString = name + $values.register(this) // register enum value so that `valueOf` and `values` can return it. + } diff --git a/docs/docs/reference/enums.md b/docs/docs/reference/enums.md new file mode 100644 index 000000000000..795c0d6210a1 --- /dev/null +++ b/docs/docs/reference/enums.md @@ -0,0 +1,139 @@ +--- +layout: doc-page +title: "Enumerations" +--- + +An enumeration is used to define a type consisting of a set of named values. + +```scala +enum Color { + case Red, Green, Blue +} +``` + +This defines a new `sealed` class, `Color`, with three values, `Color.Red`, +`Color.Green`, `Color.Blue`. The color values are members of `Color`s +companion object. The `Color` definition above is equivalent to the +following more explicit definition of an _enum class_ and a companion +object: + +```scala +enum class Color +object Color { + case Red + case Green + case Blue +} +``` + +## Parameterized enums + +Enum classes can be parameterized. + +```scala +enum Color(val rgb: Int) { + case Red extends Color(0xFF0000) + case Green extends Color(0x00FF00) + case Blue extends Color(0x0000FF) +} +``` + +As the example shows, you can define the parameter value by using an +explicit extends clause. + +## Methods defined for enums + +The values of an enum correspond to unique integers. The integer +associated with an enum value is returned by its `enumTag` method: + +```scala +scala> val red = Color.Red +val red: Color = Red +scala> red.enumTag +val res0: Int = 0 +``` + +The companion object of an enum class also defines three utility methods. +The `enumValue` and `enumValueNamed` methods obtain an enum value +by its tag or its name. The `enumValues` method returns all enum values +defined in an enumeration in an `Iterable`. + +```scala +scala> Color.enumValue(1) +val res1: Color = Green +scala> Color.enumValueNamed("Blue") +val res2: Color = Blue +scala> Color.enumValues +val res3: collection.Iterable[Color] = MapLike(Red, Green, Blue) +``` + +## User-defined members of enums + +It is possible to add your own definitions to an enum class or its +companion object. To make clear what goes where you need to use the +longer syntax which defines an enum class alongside its companion +object explicitly. In the following example, we define some methods in +class `Planet` and a `main` method in its companion object. + +```scala +enum class Planet(mass: Double, radius: Double) { + private final val G = 6.67300E-11 + def surfaceGravity = G * mass / (radius * radius) + def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity +} + +object Planet { + case MERCURY extends Planet(3.303e+23, 2.4397e6) + case VENUS extends Planet(4.869e+24, 6.0518e6) + case EARTH extends Planet(5.976e+24, 6.37814e6) + case MARS extends Planet(6.421e+23, 3.3972e6) + case JUPITER extends Planet(1.9e+27, 7.1492e7) + case SATURN extends Planet(5.688e+26, 6.0268e7) + case URANUS extends Planet(8.686e+25, 2.5559e7) + case NEPTUNE extends Planet(1.024e+26, 2.4746e7) + + def main(args: Array[String]) = { + val earthWeight = args(0).toDouble + val mass = earthWeight/EARTH.surfaceGravity + for (p <- enumValues) + println(s"Your weight on $p is ${p.surfaceWeight(mass)}") + } +} +``` + +## Implementation + +Enum classes are represented as `sealed` classes that extend the `scala.Enum` trait. +This trait defines a single method, `enumTag`: + +```scala +package scala + +/** A base trait of all enum classes */ +trait Enum { + + /** A number uniquely identifying a case of an enum */ + def enumTag: Int +} +``` + +Enum values with `extends` clauses get expanded to anonymus class instances. +For instance, the `VENUS` value above would be defined like this: + +```scala +val VENUS: Planet = + new Planet(4.869E24, 6051800.0) { + def enumTag: Int = 1 + override def toString: String = "VENUS" + // internal code to register value + } +``` + +Enum values without `extends` clauses all share a single implementation +that can be instantiated using a private method that takes a tag and a name as arguments. +For instance, the first +definition of value `Color.Red` above would expand to: + +```scala +val Red: Color = $new(0, "Red") +``` diff --git a/docs/docs/reference/implicit-by-name-parameters.md b/docs/docs/reference/implicit-by-name-parameters.md new file mode 100644 index 000000000000..af341fb982ee --- /dev/null +++ b/docs/docs/reference/implicit-by-name-parameters.md @@ -0,0 +1,64 @@ +--- +layout: doc-page +title: "Implicit By-Name Parameters" +--- + +Call-by-name implicit parameters can be used to avoid a divergent implicit expansion. + +```scala +trait Codec[T] { + def write(x: T): Unit +} + +implicit def intCodec: Codec[Int] = ??? + +implicit def optionCodec[T] + (implicit ev: => Codec[T]): Codec[Option[T]] = + new { + def write(xo: Option[T]) = xo match { + case Some(x) => ev.write(x) + case None => + } + } + +val s = implicitly[Codec[Option[Int]]] + +s.write(Some(33)) +s.write(None) +``` +As is the case for a normal by-name parameter, the argument for the implicit parameter `ev` +is evaluated on demand. In the example above, if the option value `x` is `None`, it is +not evaluated at all. + +The synthesized argument for an implicit parameter is backed by a lazy +val if this is necessary to prevent an otherwise diverging expansion. + +The precise steps for constructing an implicit argument for a by-name parameter of type `=> T` are as follows. + + 1. Create a new implicit value with a fresh name _lv_, which has the signature of the following definition: + + ```scala + implicit lazy val lv: T + ``` + + The current implementation uses the prefix `$lazy_implicit$` followed by a unique integer for _lv_. + + 1. This lazy val is not immediately available as candidate for implicit search (making it immediately available would result in a looping implicit computation). But it becomes available in all nested contexts that look again for an implicit argument to a by-name parameter. + + 1. If this implicit search succeeds with expression `E`, and `E` contains references to the lazy implicit value _lv_, replace `E` by + + + ```scala + { implicit lazy val lv: T = E; lv } + ``` + + Otherwise, return `E` unchanged. + +In the example above, the definition of `s` would be expanded as follows. + +```scala +val s = implicitly[Test.Codec[Option[Int]]]( + optionCodec[Int](intCodec)) +``` + +No lazy val was generated because the synthesized argument is not recursive. diff --git a/docs/docs/reference/intersection-types.md b/docs/docs/reference/intersection-types.md new file mode 100644 index 000000000000..256fefa9742c --- /dev/null +++ b/docs/docs/reference/intersection-types.md @@ -0,0 +1,65 @@ +--- +layout: doc-page +title: "Intersection Types" +--- + +Used on types, the `&` operator creates an intersection type. + +```scala +trait Resetable { + def reset(): this.type +} +trait Growable[T] { + def add(x: T): this.type +} +def f(x: Resetable & Growable[String]) = { + x.reset() + x.add("first") +} +``` + +The value `x` is required to be _both_ a `Resetable` and a +`Growable[String]`. Intersection types `A & B` replace compound types +`A with B` in Scala 2 (for the moment, `A with B` is still allowed, but +it will be deprecated and removed in the future). + +Unlike `with` types, `&` is _commutative_: `A & B` is the same type as +`B & A`. + +The members of an intersection type `A & B` are all the members of `A` +and all the members of `B`. For instance `Resetable & Growable[String]` +has member methods `reset` and `add`. + +If a member appears in both `A` and `B`, its type in `A & B` is the +intersection of its type in `A` and its type in `B`. For instance, assume the definitions: + +```scala +trait A { + def children: List[A] +} +trait B { + def children: List[B] +} +val x: A & B = new C +val ys: List[A & B] = x.children +``` + +The type of `children` in `A & B` is the intersection of `children`'s +type in `A` and its type in `B`, which is `List[A] & List[B]`. This +can be further simplified to `List[A & B]` because `List` is +covariant. + +One might wonder how the compiler could come up with a definition for +`children` of type `List[A & B]` since all its is given are `children` +definitions of type `List[A]` and `List[B]`. The answer is it does not +need to. `A & B` is just a type that represents a set of requirements for +values of the type. At the point where a value is _constructed_, one +must make sure that all inherited members are correctly defined. +So if one defines a class `C` that inherits `A` and `B`, one needs +to give at that point a definition of a `children` method with the required type. + +```scala +class C extends A with B { + def children: List[A & B] = ??? +} +``` diff --git a/docs/docs/reference/trait-parameters.md b/docs/docs/reference/trait-parameters.md new file mode 100644 index 000000000000..62415e57f800 --- /dev/null +++ b/docs/docs/reference/trait-parameters.md @@ -0,0 +1,58 @@ +--- +layout: doc-page +title: "Trait Parameters" +--- + +Dotty allows traits to have parameters, just like classes have parameters. + +```scala +trait Greeting(val name: String) { + def msg = s"How are you, $name" +} + +class C extends Greeting("Bob") { + println(msg) +} +``` + +Arguments to a trait are evaluated immediately before the trait is initialized. + +One potential issue with trait parameters is how to prevent +ambiguities. For instance, you might try to extend `Greeting` twice, +with different parameters. + +```scala +/*!*/ class D extends C with Greeting("Bill") // error: parameters passed twice +``` + +Should this print "Bob" or "Bill"? In fact this program is illegal, +because it violates one of the following rules for trait parameters: + + 1. If a class `C` extends a parameterized trait `T`, and its superclass does not, `C` _must_ pass arguments to `T`. + + 2. If a class `C` extends a parameterized trait `T`, and its superclass does as well, `C` _may not_ pass arguments to `T`. + + 3. Traits may never pass arguments to parent traits. + +Here's a trait extending the parameterized trait `Greeting`. + +```scala +trait FormalGreeting extends Greeting { + override def msg = s"How do you do, $name" +} +``` +As is required, no arguments are passed to `Greeting`. However, this poses an issue +when defining a class that extends `FormalGreeting`: + +```scala +/*!*/ class E extends FormalGreeting // error: missing arguments for `Greeting`. +``` + +The correct way to write `E` is to extend both `Greeting` and +`FormalGreeting` (in either order): + +```scala +class E extends Greeting("Bob") with FormalGreeting +``` + + diff --git a/docs/docs/reference/union-types.md b/docs/docs/reference/union-types.md new file mode 100644 index 000000000000..14890d514331 --- /dev/null +++ b/docs/docs/reference/union-types.md @@ -0,0 +1,57 @@ +--- +layout: doc-page +title: "Union Types" +--- + +Used on types, the `|` operator creates a union type. + +```scala +case class UserName(name: String) { + def lookup(admin: Admin): UserData +} +case class Password(hash: Hash) { + def lookup(admin: Admin): UserData +} + +def help(id: UserName | PassWord) = { + val user = id match { + case UserName(name) => lookupName(name) + case Password(hash) => lookupPassword(hash) + } + // ... +} +``` + +Union types are dual of intersection types. Values of type `A | B` are +all values of type `A` and all values of type `B`. `|` is _commutative_: +`A | B` is the same type as `B | A`. + +The compiler will assign an expression a union type only if such a +type is explicitly given. +This can be seen in the folling REPL +transcript: + +```scala +scala> val password = Password(123) +val password: Password = Password(123) +scala> val name = UserName("Eve") +val name: UserName = UserName(Eve) +scala> if (true) name else password +val res2: Object & Product = UserName(Eve) +scala> val either: Password | UserName = if (true) name else password +val either: Password | UserName = UserName(Eve) +``` + +The type of `res2` is `Object | Product`, which is a supertype of +`UserName` and `Product`, but not the least supertype `Password | +UserName`. If we want the least supertype, we have to give it +explicitely, as is done for the type of `Either`. More precisely, the +typechecker will _widen_ a union type to a non-union type when +inferring the type of `val` or `var`, or the result type of a `def`, +or the argument to pass for a type parameter. The widened type of `A +| B` is usually the intersection of all class or trait types that are +supertypes of both `A` and `B`; it does not include any refinements. +Union types are in that sense analogous to singleton types `x.type` +which are also widened to their underlying type unless explicitly +specified. + diff --git a/docs/sidebar.yml b/docs/sidebar.yml index 90e950134636..bf7e48153190 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -1,6 +1,22 @@ sidebar: - title: Blog url: blog/index.html + - title: Reference + subsection: + - title: Intersection types + url: docs/reference/intersection-types.html + - title: Union types + url: docs/reference/union-types.html + - title: Trait Parameters + url: docs/reference/trait-parameters.html + - title: Enumerations + url: docs/reference/enums.html + - title: Algebraic Data Types + url: docs/reference/adts.html + - title: Enum Translation + url: docs/reference/desugarEnums.html + - title: By-Name Implicits + url: docs/reference/implicit-by-name-parameters.html - title: Usage subsection: - title: cbt-projects diff --git a/tests/pos/intersection.scala b/tests/pos/intersection.scala index d2e445dbafb0..faf9af401d58 100644 --- a/tests/pos/intersection.scala +++ b/tests/pos/intersection.scala @@ -15,4 +15,10 @@ object intersection { type needsA = A => Nothing type needsB = B => Nothing + + + class C[-T] + def f: C[A] & C[B] = ??? + def g: C[A | B] = f + def h: C[A] & C[B] = g } diff --git a/tests/pos/news.scala b/tests/pos/news.scala new file mode 100644 index 000000000000..2825044546e1 --- /dev/null +++ b/tests/pos/news.scala @@ -0,0 +1,10 @@ +object Test { + + abstract class C[T] { def f: T } + + val x: C[Int] = new { def f = 2 } + + val y = new { val name = "Bob" } + + +} diff --git a/tests/pos/objXfun.scala b/tests/pos/objXfun.scala new file mode 100644 index 000000000000..c05a08d9ba41 --- /dev/null +++ b/tests/pos/objXfun.scala @@ -0,0 +1,8 @@ +object Foo extends (Int => Int) { // OK + def apply(x: Int) = x +} + +enum class E(x: Int) // used to generate Int => new E(x) as the parent of object E --> crash +object E { + case C(x: Int) extends E(x) +} diff --git a/tests/pos/reference/adts.scala b/tests/pos/reference/adts.scala new file mode 100644 index 000000000000..6fc670ada71b --- /dev/null +++ b/tests/pos/reference/adts.scala @@ -0,0 +1,45 @@ +package adts +object t1 { + +enum Option[+T] { + case Some[T](x: T) + case None +} + +} + +object t2 { + +enum Option[+T] { + case Some[T](x: T) extends Option[T] + case None extends Option[Nothing] +} + + +} + +enum Color(val rgb: Int) { + case Red extends Color(0xFF0000) + case Green extends Color(0x00FF00) + case Blue extends Color(0x0000FF) + case Mix(mix: Int) extends Color(mix) +} + +object t3 { + +enum class Option[+T] { + def isDefined: Boolean +} +object Option { + def apply[T >: Null](x: T): Option[T] = + if (x == null) None else Some(x) + + case Some[+T](x: T) { + def isDefined = true + } + case None { + def isDefined = false + } +} + +} diff --git a/tests/pos/reference/enums.scala b/tests/pos/reference/enums.scala new file mode 100644 index 000000000000..1db1b055d9b0 --- /dev/null +++ b/tests/pos/reference/enums.scala @@ -0,0 +1,66 @@ +package enums + +object t1 { + +enum Color { + case Red, Green, Blue +} + +} + +object t2 { + +enum class Color +object Color { + case Red + case Green + case Blue +} + +} + +object t3 { + +enum class Color(val rgb: Int) +object Color { + case Red extends Color(0xFF0000) + case Green extends Color(0x00FF00) + case Blue extends Color(0x0000FF) +} + +} + +object t4 { + +enum Color(val rgb: Int) { + case Red extends Color(0xFF0000) + case Green extends Color(0x00FF00) + case Blue extends Color(0x0000FF) +} +} + + +enum class Planet(mass: Double, radius: Double) { + private final val G = 6.67300E-11 + def surfaceGravity = G * mass / (radius * radius) + def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity +} + +object Planet { + case MERCURY extends Planet(3.303e+23, 2.4397e6) + case VENUS extends Planet(4.869e+24, 6.0518e6) + case EARTH extends Planet(5.976e+24, 6.37814e6) + case MARS extends Planet(6.421e+23, 3.3972e6) + case JUPITER extends Planet(1.9e+27, 7.1492e7) + case SATURN extends Planet(5.688e+26, 6.0268e7) + case URANUS extends Planet(8.686e+25, 2.5559e7) + case NEPTUNE extends Planet(1.024e+26, 2.4746e7) + + def main(args: Array[String]) = { + val earthWeight = args(0).toDouble + val mass = earthWeight/EARTH.surfaceGravity + for (p <- enumValues) + println(s"Your weight on $p is ${p.surfaceWeight(mass)}") + } +} + diff --git a/tests/pos/reference/implicit-byname-parameters.scala b/tests/pos/reference/implicit-byname-parameters.scala new file mode 100644 index 000000000000..8b82d2b22c9e --- /dev/null +++ b/tests/pos/reference/implicit-byname-parameters.scala @@ -0,0 +1,24 @@ +package implicitByName + +object Test { + +trait Codec[T] { + def write(x: T): Unit +} + +implicit def intCodec: Codec[Int] = ??? + +implicit def optionCodec[T] + (implicit ev: => Codec[T]): Codec[Option[T]] = + new { + def write(xo: Option[T]) = xo match { + case Some(x) => ev.write(x) + case None => + } + } + +val s = implicitly[Codec[Option[Int]]] + +s.write(Some(33)) +s.write(None) +} diff --git a/tests/pos/reference/intersection-types.scala b/tests/pos/reference/intersection-types.scala new file mode 100644 index 000000000000..1d6a14ffa3b1 --- /dev/null +++ b/tests/pos/reference/intersection-types.scala @@ -0,0 +1,35 @@ +package intersectionTypes + +object t1 { + +trait Resetable { + def reset(): this.type +} +trait Growable[T] { + def add(x: T): this.type +} +def f(x: Resetable & Growable[String]) = { + x.reset() + x.add("first") +} + +} + +object t2 { + +trait A { + def children: List[A] +} +trait B { + def children: List[B] +} +val x: A & B = new C +val ys: List[A & B] = x.children + + +class C extends A with B { + def children: List[A & B] = ??? +} + + +} diff --git a/tests/pos/reference/trait-parameters.scala b/tests/pos/reference/trait-parameters.scala new file mode 100644 index 000000000000..8b043a4279d6 --- /dev/null +++ b/tests/pos/reference/trait-parameters.scala @@ -0,0 +1,19 @@ +trait Greeting(val name: String) { + def msg = s"How are you, $name" +} + +trait FormalGreeting extends Greeting { + override def msg = s"How do you do, $name" +} + +class C extends Greeting("Bob") { + println(msg) +} + +class D extends C with Greeting + +class E extends Greeting("Bob") with FormalGreeting + +// class D2 extends C with Greeting("Bill") // error + + diff --git a/tests/pos/reference/union-types.scala b/tests/pos/reference/union-types.scala new file mode 100644 index 000000000000..7366305ad1a1 --- /dev/null +++ b/tests/pos/reference/union-types.scala @@ -0,0 +1,37 @@ +package unionTypes + +object t1 { + +type Hash = Int + +case class UserName(name: String) +case class Password(hash: Hash) + +def help(id: UserName | Password) = { + val user = id match { + case UserName(name) => lookupName(name) + case Password(hash) => lookupPassword(hash) + } +} + +def lookupName(name: String) = ??? +def lookupPassword(hash: Hash) = ??? + +} + +object t2 { + import t1._ + +trait Admin +trait UserData + +trait L { def lookup(admin: Admin): Object } + +case class UserName(name: String) extends L { + def lookup(admin: Admin): UserData = ??? +} +case class Password(hash: Hash) extends L { + def lookup(admin: Admin): UserData = ??? +} + +}