From 3cc37450bac03d3481060b185a85e9d9915f69b6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 28 Jun 2020 18:56:25 +0200 Subject: [PATCH 01/38] Unified extension methods Dotty currently uses three syntactically different schemes for extension methods 1. Single methods with prefix parameters such as `def (x: T).f(y: U)` or `def (x: T) op (y: U)`. These methods can be abstract, can override each other, etc, which is crucial for infix ops in type classes. 2. Given instances defining (only) extension methods with syntax `extension ops { ... }` 3. Collective extension methods with syntax `extension on (x: T) { ... }` This PR proposes another scheme that unifies (1) and (3) and all but eliminates the need for (2). The simplification is achieved by redefining many fundamentals of extension methods. In a sense, the price we pay for user-facing simplifications is a more complex implementation scheme. But that's probably a price worth paying. The first commit is docs only. I updated the doc pages for extension methods, type classes, and the grammar. --- docs/docs/internals/syntax.md | 12 +- .../reference/contextual/extension-methods.md | 375 ++++++++---------- .../docs/reference/contextual/type-classes.md | 228 ++++++----- 3 files changed, 282 insertions(+), 333 deletions(-) diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 8fef08a3474c..6d4c95b18513 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -248,6 +248,7 @@ Block ::= {BlockStat semi} [BlockResult] BlockStat ::= Import | {Annotation {nl}} [‘implicit’ | ‘lazy’] Def | {Annotation {nl}} {LocalModifier} TmplDef + | Extension | Expr1 | EndMarker @@ -392,7 +393,6 @@ TmplDef ::= ([‘case’] ‘class’ | [‘super’] ‘trait’) Cl | [‘case’] ‘object’ ObjectDef | ‘enum’ EnumDef | ‘given’ GivenDef - | ‘extension’ ExtensionDef ClassDef ::= id ClassConstr [Template] ClassDef(mods, name, tparams, templ) ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, , Nil, vparamss, EmptyTree, EmptyTree) as first stat ConstrMods ::= {Annotation} [AccessModifier] @@ -401,10 +401,10 @@ EnumDef ::= id ClassConstr InheritClauses EnumBody GivenDef ::= [GivenSig] Type ‘=’ Expr | [GivenSig] ConstrApps [TemplateBody] GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘as’ -ExtensionDef ::= [id] [‘on’ ExtParamClause {UsingParamClause}] - TemplateBody -ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ -ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ +Extension ::= ‘extension’ [DefTypeParamClause] ‘(’ DefParam ‘)’ + {UsingParamClause}] ExtMethods +ExtMethods ::= ExtMethod | [nl] ‘{’ ExtMethod {semi ExtMethod ‘}’ +ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef Template ::= InheritClauses [TemplateBody] Template(constr, parents, self, stats) InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp} @@ -419,6 +419,7 @@ TemplateStat ::= Import | Export | {Annotation [nl]} {Modifier} Def | {Annotation [nl]} {Modifier} Dcl + | Extension | Expr1 | EndMarker | @@ -435,6 +436,7 @@ TopStatSeq ::= TopStat {semi TopStat} TopStat ::= Import | Export | {Annotation [nl]} {Modifier} Def + | Extension | Packaging | PackageObject | EndMarker diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index 19a2a5d421da..4baa2272ef5f 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -8,7 +8,8 @@ Extension methods allow one to add methods to a type after the type is defined. ```scala case class Circle(x: Double, y: Double, radius: Double) -def (c: Circle).circumference: Double = c.radius * math.Pi * 2 +extension (c: Circle) + def circumference: Double = c.radius * math.Pi * 2 ``` Like regular methods, extension methods can be invoked with infix `.`: @@ -20,272 +21,224 @@ circle.circumference ### Translation of Extension Methods -Extension methods are methods that have a parameter clause in front of the defined identifier. -They translate to functions where the leading parameter section is turned into the first argument list of the function. -So, the definition of `circumference` above translates to the following function, and can also be invoked as such: +Extension methods are methods that have a parameter clause in front of the defined identifier. +An extension method named `f` translates to method named `extension_f` that takes the leading parameter section as its first argument list. +So, the definition of `circumference` above translates to the following method, and can also be invoked as such: ```scala -def circumference(c: Circle): Double = c.radius * math.Pi * 2 +def extension_circumference(c: Circle): Double = c.radius * math.Pi * 2 -assert(circle.circumference == circumference(circle)) +assert(circle.circumference == extension_circumference(circle)) ``` - -### Translation of Calls to Extension Methods - -When is an extension method applicable? There are two possibilities: - - 1. An extension method is applicable if it is visible under a simple name, by being defined - or inherited or imported in a scope enclosing the application. - 2. An extension method is applicable if it is a member of some given instance at the point of the application. - -Here is an example for the first rule: - -```scala -trait IntOps { - def (i: Int).isZero: Boolean = i == 0 - - def (i: Int).safeMod(x: Int): Option[Int] = - // extension method defined in same scope IntOps - if x.isZero then None - else Some(i % x) -} - -object IntOpsEx extends IntOps { - def (i: Int).safeDiv(x: Int): Option[Int] = - // extension method brought into scope via inheritance from IntOps - if x.isZero then None - else Some(i / x) -} - -trait SafeDiv { - import IntOpsEx._ // brings safeDiv and safeMod into scope - - def (i: Int) divide(d: Int) : Option[(Int, Int)] = - // extension methods imported and thus in scope - (i.safeDiv(d), i.safeMod(d)) match { - case (Some(d), Some(r)) => Some((d, r)) - case _ => None - } -} -``` - -We build up on the above example to outline the second point. -We can make an extension method available by defining a given instance containing it, like this: -```scala -given ops1 as IntOps // brings safeMod into scope - -1.safeMod(2) -``` - -Then `safeMod` is legal everywhere `ops1` is available. Anonymous givens (and any other form of givens) are supported as well: -```scala -given SafeDiv //brings divide into scope (safeMod and safeDiv are not automatically exported) - -1.divide(2) -``` - -The precise rules for resolving a selection to an extension method are as follows. - -Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type arguments `[Ts]` are optional, -and where `T` is the expected type. The following two rewritings are tried in order: - - 1. The selection is rewritten to `m[Ts](e)`. - 2. If the first rewriting does not typecheck with expected type `T`, and there is a given instance `g` - in either the current scope or in the context scope of `T`, and `g` defines an extension - method named `m`, then selection is expanded to `g.m[Ts](e)`. - This second rewriting is attempted at the time where the compiler also tries an implicit conversion - from `T` to a type containing `m`. If there is more than one way of rewriting, an ambiguity error results. - -So `circle.circumference` translates to `CircleOps.circumference(circle)`, provided -`circle` has type `Circle` and `CircleOps` is given (i.e. it is visible at the point of call or it is defined in the companion object of `Circle`). - ### Operators -The extension method syntax also applies to the definition of operators. -This case is indicated by omitting the period between the leading parameter list and the operator. In each case the definition syntax mirrors the way the operator is applied. -Examples: +The extension method syntax can also be used to define operators. Examples: ```scala -def (x: String) < (y: String) = ... -def (x: Elem) +: (xs: Seq[Elem]) = ... -def (x: Number) min (y: Number) = ... +extension (x: String) + def < (y: String) = ... +extension (x: Elem) + def +: (xs: Seq[Elem]) = ... +extension (x: Number) + @infix def min (y: Number) = ... "ab" < "c" 1 +: List(2, 3) x min 3 ``` -For alphanumeric extension operators like `min` an `@infix` annotation is implied. - - The three definitions above translate to ```scala -def < (x: String)(y: String) = ... -def +: (xs: Seq[Elem])(x: Elem) = ... -def min(x: Number)(y: Number) = ... +def extension_< (x: String)(y: String) = ... +def extension_+: (xs: Seq[Elem])(x: Elem) = ... +@infix def extension_min(x: Number)(y: Number) = ... ``` Note the swap of the two parameters `x` and `xs` when translating the right-associative operator `+:` to an extension method. This is analogous -to the implementation of right binding operators as normal methods. - +to the implementation of right binding operators as normal methods. The Scala +compiler preprocesses an infix operation `x +: xs` to `xs.+:(x)`, so the extension +method ends up being applied to the sequence as first argument (in other words, +the two swaps cancel each other out). ### Generic Extensions -The `IntOps` examples extended a non generic type. -It is also possible to extend a specific instance of a generic type (e.g. Seq[String] -- see `stringOps` further below). -Moreover, it is also possible to extend generic types by adding type parameters to an extension method. Examples: + It also possible to extend generic types by adding type parameters to an extension. For instance: -```scala -def [T](xs: List[T]) second = - xs.tail.head - -def [T](xs: List[List[T]]) flattened = - xs.foldLeft[List[T]](Nil)(_ ++ _) + ```scala + extension [T](xs: List[T]) + def second = xs.tail.head -def [T: Numeric](x: T) + (y: T): T = - summon[Numeric[T]].plus(x, y) + extension [T: Numeric](x: T) + def + (y: T): T = summon[Numeric[T]].plus(x, y) ``` -If an extension method has type parameters, they come immediately after the `def` and are followed by the extended parameter. -When calling a generic extension method, any explicitly given type arguments follow the method name. So the `second` method can be instantiated as follows: +If an extension method has type parameters, they come immediately after the `def` and are followed by the extended parameter. +When calling a generic extension method, any explicitly given type arguments follow the method name. So the `second` method could be instantiated as follows. ```scala -List(1, 2, 3).second[Int] + List(1, 2, 3).second[Int] ``` -(it's only a showcase, the compiler could of course infer the type). +Of course, the type argument here would usually be left out since it can be inferred. -### Extension Instances -It is quite common to wrap one or more extension methods in a given instance, -in order to make them available as methods without needing to be imported explicitly. -This pattern is supported by a special `extension` syntax. Example: +Extensions can also take using clauses. For instance the `+` extension above could equivalently be written with a using clause: ```scala -extension ops { - def (xs: Seq[String]).longestStrings: Seq[String] = { - val maxLength = xs.map(_.length).max - xs.filter(_.length == maxLength) - } - def (xs: Seq[String]).longestString: String = xs.longestStrings.head - def [T](xs: List[T]).second: T = xs.tail.head -} + extension [T](x: T)(using n: Numeric[T]) + def + (y: T): T = n.plus(x, y) ``` -An extension instance can only contain extension methods. Other definitions are not allowed. The name `ops` -of the extension is optional. It can be left out: -```scala -extension { - def (xs: Seq[String]).longestStrings: Seq[String] = ... - def [T](xs: List[T]).second: T = ... -} -``` -If the name of an extension is not explicitly given, it is synthesized from the name and type of the first implemented extension method. - -Extension instances map directly to given instances. The `ops` extension above -would expand to -```scala -given ops as AnyRef { - def (xs: Seq[String]).longestStrings: Seq[String] = ... - def (xs: Seq[String]).longestString: String = ... - def [T](xs: List[T]).second: T = ... -} -``` -The type "implemented" by this given instance is `AnyRef`, which -is not a type one can summon by itself. This means that the instance can -only be used for its extension methods. ### Collective Extensions Sometimes, one wants to define several extension methods that share the same -left-hand parameter type. In this case one can "pull out" the common parameters into the extension instance itself. Examples: +left-hand parameter type. In this case one can "pull out" the common parameters into +a single extension and enclose all methods in braces or an indented region following a '`:`'. +Example: ```scala -extension stringOps on (ss: Seq[String]) { - def longestStrings: Seq[String] = { +extension (ss: Seq[String]): + + def longestStrings: Seq[String] = val maxLength = ss.map(_.length).max ss.filter(_.length == maxLength) - } + def longestString: String = longestStrings.head -} +``` -extension listOps on [T](xs: List[T]) { - def second: T = xs.tail.head - def third: T = xs.tail.second -} +Note the right-hand side of `longestString`: it calls `longestStrings` directly, implicitly +assuming the common extended value `ss` as receiver. -extension on [T](xs: List[T])(using Ordering[T]) { - def largest(n: Int) = xs.sorted.takeRight(n) -} -``` -**Note**: If a collective extension defines type parameters in its prefix -(as the `listOps` extension above does), the extension methods themselves are not -allowed to have additional type parameters. This restriction might be lifted in the -future once we support multiple type parameter clauses in a method. - -Collective extensions like these are a shorthand for extension instances where -the parameters following the `on` are repeated for each implemented method. -For instance, the collective extensions above expand to the following extension instances: +Collective extensions like these are a shorthand for individual extensions +where each method is defined separately. For instance, the first extension above expands to ```scala -extension stringOps { - def (ss: Seq[String]).longestStrings: Seq[String] = { +extension (ss: Seq[String]) + def longestStrings: Seq[String] = val maxLength = ss.map(_.length).max ss.filter(_.length == maxLength) - } - def (ss: Seq[String]).longestString: String = - ss.longestStrings.head -} -extension listOps { - def [T](xs: List[T]).second: T = xs.tail.head - def [T](xs: List[T]).third: T = xs.tail.second -} -extension { - def [T](xs: List[T]).largest(using Ordering[T])(n: Int) = - xs.sorted.takeRight(n) -} + +extension (ss: Seq[String]) + def longestString: String = ss.longestStrings.head ``` -One special tweak is shown in the `longestString` method of the `stringOps` extension. It's original definition was +Collective extensions also can take type parameters and have using clauses. Example: ```scala -def longestString: String = longestStrings.head +extension [T](xs: List[T])(using Ordering[T]): + def smallest(n: Int): T = xs.sorted.take(n) + def smallestIndices(n: Int): List[Int] = + val X = smallest(n) + xs.zipWithIndex.collect { case (X, i) => i } ``` -This uses `longestStrings` as an implicit extension method call on the joint -parameter `ss`. The usage is made explicit when translating the method: +**Note**: If an extension defines type parameters in its prefix, the extension methods themselves are not allowed to have additional type parameters. This restriction might be lifted in the future once we support multiple type parameter clauses in a method. + + +### Translation of Calls to Extension Methods + +To convert a reference to an extension method, the compiler has to know about the extension +method. We say in this case that the extension method is _applicable_ at the point of reference. +There are four possible ways for an extension method to be applicable: + + 1. The extension method is visible under a simple name, by being defined or inherited or imported in a scope enclosing the reference. + 2. The extension method is a member of some given + instance that is visible at the point of the reference. + 3. The reference is of the form `r.m` and the extension method + is defined in the implicit scope of the type of `r`. + 4. The reference is of the form `r.m` and the extension method + is defined in some given instance in the implicit scope of the type of `r`. + +Here is an example for the first rule: + ```scala -def (ss: Seq[String]).longestString: String = - ss.longestStrings.head +trait IntOps: + extension (i: Int) def isZero: Boolean = i == 0 + + extension (i: Int) def safeMod(x: Int): Option[Int] = + // extension method defined in same scope IntOps + if x.isZero then None + else Some(i % x) + +object IntOpsEx extends IntOps: + extension (i: Int) def safeDiv(x: Int): Option[Int] = + // extension method brought into scope via inheritance from IntOps + if x.isZero then None + else Some(i / x) + +trait SafeDiv: + import IntOpsEx._ // brings safeDiv and safeMod into scope + + extension (i: Int) def divide(d: Int) : Option[(Int, Int)] = + // extension methods imported and thus in scope + (i.safeDiv(d), i.safeMod(d)) match + case (Some(d), Some(r)) => Some((d, r)) + case _ => None ``` -By contrast, the meaning of `this` in a collective extension is as usual -a reference to the enclosing object (i.e. the one implementing the extension methods). -It's not a reference to the shared parameter. So this means that the following -implementation of `longestString` would be illegal: +By the second rule, an extension method can be made available by defining a given instance containing it, like this: ```scala -def longestString: String = this.longestStrings.head // error: missing parameter +given ops1 as IntOps // brings safeMod into scope + +1.safeMod(2) ``` -But the following version would again be correct, since it calls the `longestString` -method as a regular non-extension method, passing the prefix parameter `ss` as a regular parameter: + +By the third and fourth rule, an extension method is available it is in the implicit scope of the receiver type or in a given instance in that scope. Example: ```scala -def longestString: String = this.longestStrings(ss).head +class List[T]: + ... +object List: + + extension [T](xs: List[List[T]]) + def flatten: List[T] = xs.foldLeft(Nil: List[T])(_ ++ _) + + given [T: Ordering] Ordering[List[T]]: + extension (xs: List[T]): + def < (ys: List[T]) = ... +end List + +// extension method available since it is in the implicit scope of List[List[Int]] +List(List(1, 2), List(3, 4)).flatten + +// extension method available since it is in the given Ordering[List[T]], +// which is itself in the implicit scope of List[Int] +List(1, 2) <: List(3) ``` +The precise rules for resolving a selection to an extension method are as follows. + +Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type arguments `[Ts]` are optional, and where `T` is the expected type. The following two rewritings are tried in order: + + 1. The selection is rewritten to `extension_m[Ts](e)`. + 2. If the first rewriting does not typecheck with expected type `T`, + and there is an extension method `m` in some eligible object `o`, the selection is rewritten to `o.extension_m[Ts](e)`. An object `o` is _eligible_ if + + - `o` forms part of the implicit scope of `T`, or + - `o` is a given instance that is visible at the point of the application, or + - `o` is a given instance in the implicit scope of `T`. + + This second rewriting is attempted at the time where the compiler also tries an implicit conversion + from `T` to a type containing `m`. If there is more than one way of rewriting, an ambiguity error results. + +An extension method can also be used as an identifier by itself. If an identifier `m` does not +resolve, the identifier is rewritten to: + + - `x.m` if the identifier appears in an extension with parameter `x` + - `this.m` otherwise + +Example: +```scala + extension (s: String) + def position(ch: Int, n: Int) = + if n < s.length && s(n) != ch then position(ch, n + 1) + else n +``` +This recursive call `position(ch, n + 1)` expands to `s.position(ch, n + 1)` in this case. + + ### Syntax Here are the syntax changes for extension methods and collective extensions relative to the [current syntax](../../internals/syntax.md). ``` -DefSig ::= ... - | ExtParamClause [nl] [‘.’] id DefParamClauses -ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ -TmplDef ::= ... - | ‘extension’ ExtensionDef -ExtensionDef ::= [id] [‘on’ ExtParamClause {GivenParamClause}] TemplateBody -``` -The template body of an extension must consist only of extension method definitions for a regular -extension instance, and only of normal method definitions for a collective extension instance. -It must not be empty. - -`extension` and `on` are soft keywords, recognized only when they appear at the start of a -statement in one of the patterns -```scala -extension on ... -extension on ... -extension { ... -extension { ... -``` +BlockStat ::= ... | Extension +TemplateStat ::= ... | Extension +TopStat ::= ... | Extension +Extension ::= ‘extension’ [DefTypeParamClause] ‘(’ DefParam ‘)’ + {UsingParamClause} ExtMethods +ExtMethods ::= ExtMethod | [nl] ‘{’ ExtMethod {semi ExtMethod ‘}’ +ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef +``` +`extension` is a soft keyword. It is recognized as a keyword only if it appears +at the start of a statement and is followed by `(` and a parameter binding. +In all other cases it is treated as an identifier. diff --git a/docs/docs/reference/contextual/type-classes.md b/docs/docs/reference/contextual/type-classes.md index f64213788a0a..11deccc072f2 100644 --- a/docs/docs/reference/contextual/type-classes.md +++ b/docs/docs/reference/contextual/type-classes.md @@ -5,110 +5,84 @@ title: "Implementing Type classes" A _type class_ is an abstract, parameterized type that lets you add new behavior to any closed data type without using sub-typing. This can be useful in multiple use-cases, for example: * expressing how a type you don't own (from the standard or 3rd-party library) conforms to such behavior -* expressing such a behavior for multiple types without involving sub-typing relationships (one `extends` another) between those types (see: [ad hoc polymorphism](https://en.wikipedia.org/wiki/Ad_hoc_polymorphism) for instance) +* expressing such a behavior for multiple types without involving sub-typing relationships (one `extends` another) between those types (see: [ad hoc polymorphism](https://en.wikipedia.org/wiki/Ad_hoc_polymorphism) for instance) -Therefore in Scala 3, _type classes_ are just _traits_ with one or more parameters whose implementations are not defined through the `extends` keyword, but by **given instances**. -Here are some examples of usual type classes: +Therefore in Scala 3, _type classes_ are just _traits_ with one or more parameters whose implementations are not defined through the `extends` keyword, but by **given instances**. +Here are some examples of common type classes: ### Semigroups and monoids: Here's the `Monoid` type class definition: ```scala -trait SemiGroup[T] { - def (x: T) combine (y: T): T -} +trait SemiGroup[T]: + extension (x: T) def combine (y: T): T -trait Monoid[T] extends SemiGroup[T] { +trait Monoid[T] extends SemiGroup[T]: def unit: T -} ``` -An implementation of this `Monoid` type class for the type `String` can be the following: +An implementation of this `Monoid` type class for the type `String` can be the following: ```scala -given Monoid[String] { - def (x: String) combine (y: String): String = x.concat(y) +given Monoid[String]: + extension (x: String) def combine (y: String): String = x.concat(y) def unit: String = "" -} ``` Whereas for the type `Int` one could write the following: ```scala -given Monoid[Int] { - def (x: Int) combine (y: Int): Int = x + y +given Monoid[Int]: + extension (x: Int) def combine (y: Int): Int = x + y def unit: Int = 0 -} ``` This monoid can now be used as _context bound_ in the following `combineAll` method: ```scala def combineAll[T: Monoid](xs: List[T]): T = - xs.foldLeft(summon[Monoid[T]].unit)(_ combine _) + xs.foldLeft(summon[Monoid[T]].unit)(_.combine(_)) ``` To get rid of the `summon[...]` we can define a `Monoid` object as follows: ```scala -object Monoid { +object Monoid: def apply[T](using m: Monoid[T]) = m -} ``` -Which would allow to re-write the `combineAll` method this way: +Which would allow to re-write the `combineAll` method this way: ```scala def combineAll[T: Monoid](xs: List[T]): T = - xs.foldLeft(Monoid[T].unit)(_ combine _) -``` - -We can also benefit from [extension methods](extension-methods.html) to make this `combineAll` function accessible as a method on the `List` type: - - -```scala -def [T: Monoid](xs: List[T]).combineAll: T = - xs.foldLeft(Monoid[T].unit)(_ combine _) -``` - -Which allows one to write: - -```scala -assert("ab" == List("a", "b").combineAll) -``` -or: -```scala -assert(3 == List(1, 2).combineAll) + xs.foldLeft(Monoid[T].unit)(_.combine(_)) ``` ### Functors: A `Functor` for a type provides the ability for its values to be "mapped over", i.e. apply a function that transforms inside a value while remembering its shape. For example, to modify every element of a collection without dropping or adding elements. We can represent all types that can be "mapped over" with `F`. It's a type constructor: the type of its values becomes concrete when provided a type argument. -Therefore we write it `F[?]`, hinting that it is a type with internal details we can inspect. +Therefore we write it `F[_]`, hinting that the type `F` takes another type as argument. The definition of a generic `Functor` would thus be written as: ```scala -trait Functor[F[?]] { - def map[A, B](original: F[A], mapper: A => B): F[B] -} +trait Functor[F[_]]: + def map[A, B](x: F[A], f: A => B): F[B] ``` - -Which could read as follows: "A `Functor` for the type constructor `F[?]` represents the ability to transform `F[A]` to `F[B]` through the application of the `mapper` function whose type is `A => B`". We call the `Functor` definition here a _type class_. -This way, we could define an instance of `Functor` for the `List` type: +Which could read as follows: "A `Functor` for the type constructor `F[_]` represents the ability to transform `F[A]` to `F[B]` through the application of function `f` with type `A => B`". We call the `Functor` definition here a _type class_. +This way, we could define an instance of `Functor` for the `List` type: ```scala -given Functor[List] { - def map[A, B](original: List[A], mapper: A => B): List[B] = - original.map(mapper) // List already has a `map` method -} +given Functor[List]: + def map[A, B](x: List[A], f: A => B): List[B] = + x.map(f) // List already has a `map` method ``` With this `given` instance in scope, everywhere a `Functor` is expected, the compiler will accept a `List` to be used. For instance, we may write such a testing method: ```scala -def assertTransformation[F[?]: Functor, A, B](expected: F[B], original: F[A], mapping: A => B): Unit = +def assertTransformation[F[_]: Functor, A, B](expected: F[B], original: F[A], mapping: A => B): Unit = assert(expected == summon[Functor[F]].map(original, mapping)) ``` @@ -119,124 +93,135 @@ assertTransformation(List("a1", "b1"), List("a", "b"), elt => s"${elt}1") ``` That's a first step, but in practice we probably would like the `map` function to be a method directly accessible on the type `F`. So that we can call `map` directly on instances of `F`, and get rid of the `summon[Functor[F]]` part. -As in the previous example of Monoids, [`extension` methods](extension-methods.html) help achieving that. Let's re-define the `Functor` _type class_ with extension methods. +As in the previous example of Monoids, [`extension` methods](extension-methods.html) help achieving that. Let's re-define the `Functor` type class with extension methods. ```scala -trait Functor[F[?]] { - def [A, B](original: F[A]).map(mapper: A => B): F[B] -} +trait Functor[F[_]]: + extension [A, B](x: F[A]) + def map(f: A => B): F[B] ``` The instance of `Functor` for `List` now becomes: ```scala -given Functor[List] { - def [A, B](original: List[A]).map(mapper: A => B): List[B] = - original.map(mapper) // List already has a `map` method -} +given Functor[List]: + extension def [A, B](xs: List[A]) + def map(f: A => B): List[B] = + xs.map(f) // List already has a `map` method + ``` It simplifies the `assertTransformation` method: ```scala -def assertTransformation[F[?]: Functor, A, B](expected: F[B], original: F[A], mapping: A => B): Unit = +def assertTransformation[F[_]: Functor, A, B](expected: F[B], original: F[A], mapping: A => B): Unit = assert(expected == original.map(mapping)) ``` - -The `map` method is now directly used on `original` since it is of type `F[A]` (where `F` is a `Functor`). +The `map` method is now directly used on `original`. It is available as an extension method +since `original`'s type is `F[A]` and a given instance for `Functor[F[A]]` which defines `map` +is in scope. ### Monads -Now we have a `Functor` for `List`. +Applying `map` in `Functor[List]` to a mapping function of type `A => B` results in a `List[B]`. So applying it to a mapping function of type `A => List[B]` results in a `List[List[B]]`. To avoid managing lists of lists, we may want to "flatten" the values in a single list. -Applying the `List.map` ability with the following mapping function as parameter: `mapping: A => B` would result in a `List[B]`. +That's where `Monad` comes in. A `Monad` for type `F[_]` is a `Functor[F]` with two more operations: +* `flatMap`, which turns an `F[A]` into an `F[B]` when given a function of type +`A => F[B]`, +* `pure`, which creates an `F[A]` from a single value `A`. -Now, applying the `List.map` ability with the following mapping function as parameter: `mapping: A => List[B]` would result in a `List[List[B]]`. +Here is the translation of this definition in Scala 3: -To avoid managing lists of lists, we may want to "flatten" the values in a single list. +```scala +trait Monad[F[_]] extends Functor[F]: -That's where `Monad` enters the party. A `Monad` for type `F[?]` is a `Functor[F]` with 2 more abilities: -* the flatten ability we just described: turning `F[A]` to `F[B]` when given a `mapping: A => F[B]` function -* the ability to create `F[A]` from a single value `A` + /** The unit value for a monad */ + def pure[A](x: A): F[A] -Here is the translation of this definition in Scala 3: + extension [A, B](x: F[A]): + /** The fundamental composition operation */ + def flatMap(f: A => F[B]): F[B] -```scala -trait Monad[F[?]] extends Functor[F] { // "A `Monad` for type `F[?]` is a `Functor[F]`" => thus has the `map` ability - def pure[A](x: A): F[A] // `pure` can construct F[A] from a single value A - def [A, B](x: F[A]).flatMap(f: A => F[B]): F[B] // the flattening ability is named `flatMap`, using extension methods as previous examples - def [A, B](x: F[A]).map(f: A => B) = x.flatMap(f `andThen` pure) // the `map(f)` ability is simply a combination of applying `f` then turning the result into an `F[A]` then applying `flatMap` to it -} + /** The `map` operation can now be defined in terms of `flatMap` */ + def map(f: A => B) = x.flatMap(f.andThen(pure)) + +end Monad ``` #### List -Let us declare the `Monad` ability for type `List` +A `List` can be turned into a monad via this `given` instance: ```scala -given listMonad as Monad[List] { +given listMonad as Monad[List]: def pure[A](x: A): List[A] = List(x) - def [A, B](xs: List[A]).flatMap(f: A => List[B]): List[B] = - xs.flatMap(f) // let's rely on the existing `flatMap` method of `List` -} + extension def [A, B](xs: List[A]) + def flatMap(f: A => List[B]): List[B] = + xs.flatMap(f) // rely on the existing `flatMap` method of `List` ``` - -`map` implementation is no longer needed. +Since `Monad` is a subtype of `Functor`, `List` is also a functor. The Functor's `map` +operation is already provided by the `Monad` trait, so the instance does not need to define +it explicitly. #### Option `Option` is an other type having the same kind of behaviour: -* the `map` ability turning `Option[A]` into `Option[B]` if passed a function `f: A => B` -* the `flatMap` ability turning `Option[A]` into `Option[B]` if passed a function `f: A => Option[B]` -* the `pure` ability turning `A` into `Option[A]` ```scala -given optionMonad as Monad[Option] { +given optionMonad as Monad[Option]: def pure[A](x: A): Option[A] = Option(x) - def [A, B](xs: Option[A]).flatMap(f: A => Option[B]): Option[B] = - xs.flatMap(f) // let's rely on the existing `flatMap` method of `Option` -} + extension [A, B](xo: Option[A]) + def flatMap(f: A => Option[B]): Option[B] = xo match + case Some(x) => f(x) + case None => None ``` -#### The Reader Monad +#### Reader -Another example of a `Monad` is the Reader Monad. It no longer acts on a type like `List` or `Option`, but on a function. -It can be used for example for combining functions that all need the same type of parameter. For instance multiple functions needing access to some configuration, context, environment variables, etc. +Another example of a `Monad` is the _Reader_ Monad, which acts on functions instead of +data types like `List` or `Option`. It can be used to combine multiple functions +that all need the same parameter. For instance multiple functions needing access to some configuration, context, environment variables, etc. -Let us have a `Config` type, and two functions using it: +Let's define a `Config` type, and two functions using it: ```scala trait Config // ... def compute(i: Int)(config: Config): String = ??? -def show(str: String)(config: Config): Unit = ??? +def layout(str: String)(config: Config): Unit = ??? ``` -We may want to combine `compute` and `show` into a single function, accepting a `Config` as parameter, and showing the result of the computation. -If we had a `flatMap` function as in the examples above, we would be able to write the following: - +We may want to combine `compute` and `show` into a single function, accepting a `Config` as parameter, and showing the result of the computation, and we'd like to use +a monad to avoid passing the parameter explicitly multiple times. So postulating +the right `flatMap` operation, we could write: ```scala def computeAndShow(i: Int): Config => Unit = compute(i).flatMap(show) ``` - -Let's define this `Monad` then. First, we are going to define a type named `ConfigDependent` representing a function that when passed a `Config` produces a `Result`. +instead of +```scala +show(compute(i)(config))(config) +``` +Let's define this m then. First, we are going to define a type named `ConfigDependent` representing a function that when passed a `Config` produces a `Result`. ```scala -trait Config // the Config defined above type ConfigDependent[Result] = Config => Result ``` -The monad will look like this: +The monad instance will look like this: ```scala -given configDependentMonad as Monad[ConfigDependent] { - def [A, B](r: ConfigDependent[A]).flatMap(f: A => ConfigDependent[B]): ConfigDependent[B] = - config => f(r(config))(config) +given configDependentMonad as Monad[ConfigDependent]: + def pure[A](x: A): ConfigDependent[A] = config => x -} + + extension [A, B](x: ConfigDependent[A]) + def flatMap(f: A => ConfigDependent[B]): ConfigDependent[B] = + config => f(x(config))(config) + +end configDependentMonad ``` The type `ConfigDependent` can be written using [type lambdas](../new-types/type-lambdas.html): @@ -245,34 +230,43 @@ The type `ConfigDependent` can be written using [type lambdas](../new-types/type type ConfigDependent = [Result] =>> Config => Result ``` -Using this syntax would turn the previous `configReaderMonad` into: +Using this syntax would turn the previous `configDependentMonad` into: ```scala given configDependentMonad as Monad[[Result] =>> Config => Result] - def [A, B](r: Config => A).flatMap(f: A => Config => B): Config => B = - config => f(r(config))(config) + def pure[A](x: A): Config => A = config => x -``` + extension [A, B](x: Config => A) + def flatMap(f: A => Config => B): Config => B = + config => f(x(config))(config) +end configDependentMonad +``` It is likely that we would like to use this pattern with other kinds of environments than our `Config` trait. The Reader monad allows us to abstract away `Config` as a type _parameter_, named `Ctx` in the following definition: ```scala -given readerMonad[Ctx] as Monad[[X] =>> Ctx => X] { - def [A, B](r: Ctx => A).flatMap(f: A => Ctx => B): Ctx => B = - ctx => f(r(ctx))(ctx) +given readerMonad[Ctx] as Monad[[X] =>> Ctx => X]: + def pure[A](x: A): Ctx => A = ctx => x -} + + extension [A, B](x: Ctx => A) + def flatMap(f: A => Ctx => B): Ctx => B = + ctx => f(x(ctx))(ctx) + +end readerMonad ``` ### Summary -The definition of a _type class_ is expressed via a parameterised type with abstract members, such as a `trait`. -The main difference between object oriented polymorphism, and ad-hoc polymorphism with _type classes_, is how the definition of the _type class_ is implemented, in relation to the type it acts upon. -In the case of a _type class_, its implementation for a concrete type is expressed through a `given` term definition, which is supplied as an implicit argument alongside the value it acts upon. With object oriented polymorphism, the implementation is mixed into the parents of a class, and only a single term is required to perform a polymorphic operation. +The definition of a _type class_ is expressed with a parameterised type with abstract members, such as a `trait`. +The main difference between subtype polymorphism and ad-hoc polymorphism with _type classes_ is how the definition of the _type class_ is implemented, in relation to the type it acts upon. +In the case of a _type class_, its implementation for a concrete type is expressed through a `given` instance definition, which is supplied as an implicit argument alongside the value it acts upon. With subtype polymorphism, the implementation is mixed into the parents of a class, and only a single term is required to perform a polymorphic operation. The type class solution +takes more effort to set up, but is more extensible: Adding a new interface to a class +class requires changing the source code of that class. But contrast, instances for type classes can be defined anywhere. -To conclude, in addition to given instances, other constructs like extension methods, context bounds and type lambdas allow a concise and natural expression of _type classes_. +To conclude, we have seen that traits and given instances, combined with other constructs like extension methods, context bounds and type lambdas allow a concise and natural expression of _type classes_. From d88a79617837eb7421d7c1ed3868e0f44285009c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 28 Jun 2020 19:09:29 +0200 Subject: [PATCH 02/38] Change parser to accept new extension syntax --- compiler/src/dotty/tools/dotc/ast/untpd.scala | 8 ++ .../dotty/tools/dotc/parsing/Parsers.scala | 95 +++++++++++++++---- .../dotty/tools/dotc/parsing/Scanners.scala | 4 + .../reference/contextual/extension-methods.md | 4 +- 4 files changed, 88 insertions(+), 23 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index ff0b57f08155..36ef435764a6 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -114,6 +114,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class ContextBounds(bounds: TypeBoundsTree, cxBounds: List[Tree])(implicit @constructorOnly src: SourceFile) extends TypTree case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree)(implicit @constructorOnly src: SourceFile) extends DefTree case class Export(expr: Tree, selectors: List[ImportSelector])(implicit @constructorOnly src: SourceFile) extends Tree + case class ExtMethods(tparams: List[TypeDef], vparamss: List[List[ValDef]], methods: List[DefDef])(implicit @constructorOnly src: SourceFile) extends Tree case class MacroTree(expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree case class ImportSelector(imported: Ident, renamed: Tree = EmptyTree, bound: Tree = EmptyTree)(implicit @constructorOnly src: SourceFile) extends Tree { @@ -617,6 +618,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case tree: Export if (expr eq tree.expr) && (selectors eq tree.selectors) => tree case _ => finalize(tree, untpd.Export(expr, selectors)(tree.source)) } + def ExtMethods(tree: Tree)(tparams: List[TypeDef], vparamss: List[List[ValDef]], methods: List[DefDef])(using Context): Tree = tree match + case tree: ExtMethods if (tparams eq tree.tparams) && (vparamss eq tree.vparamss) && (methods == tree.methods) => tree + case _ => finalize(tree, untpd.ExtMethods(tparams, vparamss, methods)(tree.source)) def ImportSelector(tree: Tree)(imported: Ident, renamed: Tree, bound: Tree)(implicit ctx: Context): Tree = tree match { case tree: ImportSelector if (imported eq tree.imported) && (renamed eq tree.renamed) && (bound eq tree.bound) => tree case _ => finalize(tree, untpd.ImportSelector(imported, renamed, bound)(tree.source)) @@ -683,6 +687,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs)) case Export(expr, selectors) => cpy.Export(tree)(transform(expr), selectors) + case ExtMethods(tparams, vparamss, methods) => + cpy.ExtMethods(tree)(transformSub(tparams), vparamss.mapConserve(transformSub(_)), transformSub(methods)) case ImportSelector(imported, renamed, bound) => cpy.ImportSelector(tree)(transformSub(imported), transform(renamed), transform(bound)) case Number(_, _) | TypedSplice(_) => @@ -742,6 +748,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { this(this(this(x, pats), tpt), rhs) case Export(expr, _) => this(x, expr) + case ExtMethods(tparams, vparamss, methods) => + this(vparamss.foldLeft(this(x, tparams))(apply), methods) case ImportSelector(imported, renamed, bound) => this(this(this(x, imported), renamed), bound) case Number(_, _) => diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index e12d3a7fb8e3..f5ec54375698 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -227,7 +227,7 @@ object Parsers { || defIntroTokens.contains(in.token) || allowedMods.contains(in.token) || in.isSoftModifierInModifierPosition && !excludedSoftModifiers.contains(in.name) - || isIdent(nme.extension) && followingIsExtension() + || isIdent(nme.extension) && followingIsOldExtension() def isStatSep: Boolean = in.isNewLine || in.token == SEMI @@ -369,7 +369,7 @@ object Parsers { def acceptStatSep(): Unit = if in.isNewLine then in.nextToken() else accept(SEMI) - def acceptStatSepUnlessAtEnd(stats: ListBuffer[Tree], altEnd: Token = EOF): Unit = + def acceptStatSepUnlessAtEnd[T <: Tree](stats: ListBuffer[T], altEnd: Token = EOF): Unit = in.observeOutdented() in.token match case SEMI | NEWLINE | NEWLINES => @@ -921,7 +921,11 @@ object Parsers { skipParams() lookahead.isIdent(nme.as) - def followingIsExtension() = + def followingIsNewExtension() = + val next = in.lookahead.token + next == LBRACKET || next == LPAREN + + def followingIsOldExtension() = val lookahead = in.LookaheadScanner() lookahead.nextToken() if lookahead.isIdent && !lookahead.isIdent(nme.on) then @@ -932,6 +936,8 @@ object Parsers { || lookahead.token == LBRACE || lookahead.token == COLON + def followingIsExtension() = followingIsOldExtension() || followingIsNewExtension() + /* --------- OPERAND/OPERATOR STACK --------------------------------------- */ var opStack: List[OpInfo] = Nil @@ -1301,7 +1307,7 @@ object Parsers { else newLineOptWhenFollowedBy(LBRACE) - def checkEndMarker(stats: ListBuffer[Tree]): Unit = + def checkEndMarker[T <: Tree](stats: ListBuffer[T]): Unit = def matches(stat: Tree): Boolean = stat match case stat: MemberDef if !stat.name.isEmpty => @@ -3177,11 +3183,6 @@ object Parsers { () => importSelection(simpleRef()) } - def posMods(start: Int, mods: Modifiers): Modifiers = { - in.nextToken() - mods - } - /** Def ::= val PatDef * | var VarDef * | def DefDef @@ -3202,9 +3203,9 @@ object Parsers { val mod1 = addMod(mods, mod) patDefOrDcl(start, mod1) case DEF => - defDefOrDcl(start, posMods(start, mods)) + defDefOrDcl(start, in.skipToken(mods)) case TYPE => - typeDefOrDcl(start, posMods(start, mods)) + typeDefOrDcl(start, in.skipToken(mods)) case CASE if inEnum => enumCase(start, mods) case _ => @@ -3269,7 +3270,7 @@ object Parsers { * DefSig ::= id [DefTypeParamClause] DefParamClauses * | ExtParamClause [nl] [‘.’] id DefParamClauses */ - def defDefOrDcl(start: Offset, mods: Modifiers): Tree = atSpan(start, nameStart) { + def defDefOrDcl(start: Offset, mods: Modifiers): DefDef = atSpan(start, nameStart) { def scala2ProcedureSyntax(resultTypeStr: String) = def toInsert = @@ -3449,23 +3450,23 @@ object Parsers { def tmplDef(start: Int, mods: Modifiers): Tree = in.token match { case TRAIT => - classDef(start, posMods(start, addFlag(mods, Trait))) + classDef(start, in.skipToken(addFlag(mods, Trait))) case SUPERTRAIT => - classDef(start, posMods(start, addFlag(mods, Trait | SuperTrait))) + classDef(start, in.skipToken(addFlag(mods, Trait | SuperTrait))) case CLASS => - classDef(start, posMods(start, mods)) + classDef(start, in.skipToken(mods)) case CASECLASS => - classDef(start, posMods(start, mods | Case)) + classDef(start, in.skipToken(mods | Case)) case OBJECT => - objectDef(start, posMods(start, mods | Module)) + objectDef(start, in.skipToken(mods | Module)) case CASEOBJECT => - objectDef(start, posMods(start, mods | Case | Module)) + objectDef(start, in.skipToken(mods | Case | Module)) case ENUM => - enumDef(start, posMods(start, mods | Enum)) + enumDef(start, in.skipToken(mods | Enum)) case GIVEN => givenDef(start, mods, atSpan(in.skipToken()) { Mod.Given() }) case _ => - if isIdent(nme.extension) && followingIsExtension() then + if isIdent(nme.extension) && followingIsOldExtension() then extensionDef(start, mods) else syntaxErrorOrIncomplete(ExpectedStartOfTopLevelDefinition()) @@ -3636,6 +3637,49 @@ object Parsers { val edef = atSpan(start, nameOffset, in.offset)(ModuleDef(name, templ)) finalizeDef(edef, addFlag(mods, Given | extensionFlag), start) + /** Extension ::= ‘extension’ [DefTypeParamClause] ‘(’ DefParam ‘)’ + * {UsingParamClause}] ExtMethods + */ + def extension(): ExtMethods = + val start = in.skipToken() + val tparams = typeParamClauseOpt(ParamOwner.Def) + val extParams = paramClause(0, prefix = true) + val givenParamss = paramClauses(givenOnly = true) + val methods = + if isDefIntro(modifierTokens) then + extMethod() :: Nil + else + possibleTemplateStart() + if in.isNestedStart then inDefScopeBraces(extMethods()) + else { syntaxError("Extension without extension methods"); Nil } + val result = ExtMethods(tparams, extParams :: givenParamss, methods) + val comment = in.getDocComment(start) + if comment.isDefined then + for meth <- methods do + if !meth.rawComment.isDefined then meth.setComment(comment) + result + end extension + + /** ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef + */ + def extMethod(): DefDef = + val start = in.offset + val mods = defAnnotsMods(modifierTokens) + accept(DEF) + defDefOrDcl(start, mods) + + /** ExtMethods ::= ExtMethod | [nl] ‘{’ ExtMethod {semi ExtMethod ‘}’ + */ + def extMethods(): List[DefDef] = checkNoEscapingPlaceholders { + val meths = new ListBuffer[DefDef] + val exitOnError = false + while !isStatSeqEnd && !exitOnError do + setLastStatOffset() + meths += extMethod() + acceptStatSepUnlessAtEnd(meths) + meths.toList + } + /* -------- TEMPLATES ------------------------------------------- */ /** ConstrApp ::= SimpleType1 {Annotation} {ParArgumentExprs} @@ -3750,6 +3794,7 @@ object Parsers { * | Annotations Modifiers Def * | Packaging * | package object objectDef + * | Extension * | */ def topStatSeq(outermost: Boolean = false): List[Tree] = { @@ -3768,7 +3813,9 @@ object Parsers { stats ++= importClause(IMPORT, mkImport(outermost)) else if (in.token == EXPORT) stats ++= importClause(EXPORT, Export.apply) - else if (in.token == AT || isDefIntro(modifierTokens)) + else if isIdent(nme.extension) && followingIsNewExtension() then + stats += extension() + else if isDefIntro(modifierTokens) stats +++= defOrDcl(in.offset, defAnnotsMods(modifierTokens)) else if (!isStatSep) if (in.token == CASE) @@ -3785,6 +3832,7 @@ object Parsers { * | Export * | Annotations Modifiers Def * | Annotations Modifiers Dcl + * | Extension * | Expr1 * | * EnumStat ::= TemplateStat @@ -3819,6 +3867,8 @@ object Parsers { stats ++= importClause(IMPORT, mkImport()) else if (in.token == EXPORT) stats ++= importClause(EXPORT, Export.apply) + else if isIdent(nme.extension) && followingIsNewExtension() then + stats += extension() else if (isDefIntro(modifierTokensOrCase)) stats +++= defOrDcl(in.offset, defAnnotsMods(modifierTokens)) else if (isExprIntro) @@ -3884,6 +3934,7 @@ object Parsers { * BlockStat ::= Import * | Annotations [implicit] [lazy] Def * | Annotations LocalModifiers TmplDef + * | Extension * | Expr1 * | */ @@ -3898,6 +3949,8 @@ object Parsers { stats += expr(Location.InBlock) else if in.token == IMPLICIT && !in.inModifierPosition() then stats += closure(in.offset, Location.InBlock, modifiers(BitSet(IMPLICIT))) + else if isIdent(nme.extension) && followingIsNewExtension() then + stats += extension() else if isDefIntro(localModifierTokens, excludedSoftModifiers = Set(nme.`opaque`)) then stats +++= localDef(in.offset) else if (!isStatSep && (in.token != CASE)) { diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 9c4f16137e36..f8f0d5b2be72 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -296,6 +296,10 @@ object Scanners { off } + def skipToken[T](result: T): T = + nextToken() + result + def adjustSepRegions(lastToken: Token): Unit = (lastToken: @switch) match { case LPAREN | LBRACKET => currentRegion = InParens(lastToken, currentRegion) diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index 4baa2272ef5f..1eaaf86a8c57 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -240,5 +240,5 @@ ExtMethods ::= ExtMethod | [nl] ‘{’ ExtMethod {semi ExtMethod ‘} ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef ``` `extension` is a soft keyword. It is recognized as a keyword only if it appears -at the start of a statement and is followed by `(` and a parameter binding. -In all other cases it is treated as an identifier. +at the start of a statement and is followed by `[` or `(`. In all other cases +it is treated as an identifier. From f0c2e19129595bf8982d25f774eac374ab04fb51 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 28 Jun 2020 19:32:53 +0200 Subject: [PATCH 03/38] Doc fixes --- .../reference/contextual/extension-methods.md | 23 +++++++++++-------- .../docs/reference/contextual/type-classes.md | 2 +- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index 1eaaf86a8c57..def262de9ba6 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -61,7 +61,7 @@ the two swaps cancel each other out). ### Generic Extensions - It also possible to extend generic types by adding type parameters to an extension. For instance: + It is also possible to extend generic types by adding type parameters to an extension. For instance: ```scala extension [T](xs: List[T]) @@ -71,7 +71,7 @@ the two swaps cancel each other out). def + (y: T): T = summon[Numeric[T]].plus(x, y) ``` -If an extension method has type parameters, they come immediately after the `def` and are followed by the extended parameter. +If an extension method has type parameters, they come immediately after `extension` and are followed by the extended parameter. When calling a generic extension method, any explicitly given type arguments follow the method name. So the `second` method could be instantiated as follows. ```scala List(1, 2, 3).second[Int] @@ -79,7 +79,7 @@ When calling a generic extension method, any explicitly given type arguments fol Of course, the type argument here would usually be left out since it can be inferred. -Extensions can also take using clauses. For instance the `+` extension above could equivalently be written with a using clause: +Extensions can also take using clauses. For instance, the `+` extension above could equivalently be written with a using clause: ```scala extension [T](x: T)(using n: Numeric[T]) def + (y: T): T = n.plus(x, y) @@ -120,8 +120,8 @@ Collective extensions also can take type parameters and have using clauses. Exam extension [T](xs: List[T])(using Ordering[T]): def smallest(n: Int): T = xs.sorted.take(n) def smallestIndices(n: Int): List[Int] = - val X = smallest(n) - xs.zipWithIndex.collect { case (X, i) => i } + val limit = smallest(n) + xs.zipWithIndex.collect { case (x, i) if x <= limit => i } ``` **Note**: If an extension defines type parameters in its prefix, the extension methods themselves are not allowed to have additional type parameters. This restriction might be lifted in the future once we support multiple type parameter clauses in a method. @@ -173,7 +173,7 @@ given ops1 as IntOps // brings safeMod into scope 1.safeMod(2) ``` -By the third and fourth rule, an extension method is available it is in the implicit scope of the receiver type or in a given instance in that scope. Example: +By the third and fourth rule, an extension method is available if it is in the implicit scope of the receiver type or in a given instance in that scope. Example: ```scala class List[T]: ... @@ -216,14 +216,19 @@ resolve, the identifier is rewritten to: - `x.m` if the identifier appears in an extension with parameter `x` - `this.m` otherwise -Example: +and the rewritten term is again tried as an application of an extension method. Example: ```scala extension (s: String) - def position(ch: Int, n: Int) = + def position(ch: Char, n: Int): Int = if n < s.length && s(n) != ch then position(ch, n + 1) else n ``` -This recursive call `position(ch, n + 1)` expands to `s.position(ch, n + 1)` in this case. +The recursive call `position(ch, n + 1)` expands to `s.position(ch, n + 1)` in this case. The whole extension method rewrites to +```scala +def extension_position(s: String)(ch: Char, n: Int): Int = + if n < s.length && s(n) != ch then extension_position(s)(ch, n + 1) + else n +``` ### Syntax diff --git a/docs/docs/reference/contextual/type-classes.md b/docs/docs/reference/contextual/type-classes.md index 11deccc072f2..38fff9494a48 100644 --- a/docs/docs/reference/contextual/type-classes.md +++ b/docs/docs/reference/contextual/type-classes.md @@ -105,7 +105,7 @@ The instance of `Functor` for `List` now becomes: ```scala given Functor[List]: - extension def [A, B](xs: List[A]) + extension [A, B](xs: List[A]) def map(f: A => B): List[B] = xs.map(f) // List already has a `map` method From f87ec07ba7ae9ed57920385b100e5dace5882cf4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 29 Jun 2020 14:50:46 +0200 Subject: [PATCH 04/38] Desugaring of extensions --- .../src/dotty/tools/dotc/ast/Desugar.scala | 18 ++++++++++++++++-- .../src/dotty/tools/dotc/core/NameOps.scala | 6 ++++++ .../src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 9 +++++++-- .../src/dotty/tools/dotc/typer/Namer.scala | 7 ++++--- .../src/dotty/tools/dotc/typer/Typer.scala | 5 ++++- 6 files changed, 38 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 36f71e6df03d..eef43c6a4b6b 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -559,7 +559,7 @@ object desugar { val copiedAccessFlags = if migrateTo3 then EmptyFlags else AccessFlags // Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams) - // def _1: T1 = this.p1 + // def _1: T1 = this.p1 // ... // def _N: TN = this.pN (unless already given as valdef or parameterless defdef) // def copy(p1: T1 = p1: @uncheckedVariance, ..., @@ -572,7 +572,7 @@ object desugar { val caseClassMeths = { def syntheticProperty(name: TermName, tpt: Tree, rhs: Tree) = DefDef(name, Nil, Nil, tpt, rhs).withMods(synthetic) - + def productElemMeths = val caseParams = derivedVparamss.head.toArray val selectorNamesInBody = normalizedBody.collect { @@ -850,6 +850,7 @@ object desugar { * where every definition in `body` is expanded to an extension method * taking type parameters `tparams` and a leading paramter `(x: T)`. * See: collectiveExtensionBody + * TODO: drop this part */ def moduleDef(mdef: ModuleDef)(implicit ctx: Context): Tree = { val impl = mdef.impl @@ -906,6 +907,19 @@ object desugar { } } + /** Transform extension construct to list of extension methods */ + def extMethods(ext: ExtMethods)(using Context): Tree = flatTree { + for mdef <- ext.methods yield + if ext.tparams.nonEmpty && mdef.tparams.nonEmpty then + ctx.error(em"extension method cannot have type parameters since some were already given in extension clause", + mdef.tparams.head.sourcePos) + cpy.DefDef(mdef)( + name = mdef.name.toExtensionName, + tparams = ext.tparams ++ mdef.tparams, + vparamss = ext.vparamss ++ mdef.vparamss + ).withMods(mdef.mods | Extension) + } + /** Transform the statements of a collective extension * @param stats the original statements as they were parsed * @param tparams the collective type parameters diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 550f3243df6e..52d7a96d7ede 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -131,6 +131,12 @@ object NameOps { else name.toTermName } + def isExtensionName: Boolean = name match + case name: SimpleName => name.startsWith("extension_") + case _ => false + + def toExtensionName = termName("extension_" ++ name.toString) + /** The expanded name. * This is the fully qualified name of `base` with `ExpandPrefixName` as separator, * followed by `kind` and the name. diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index f5ec54375698..9ef2580a3922 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3638,7 +3638,7 @@ object Parsers { finalizeDef(edef, addFlag(mods, Given | extensionFlag), start) /** Extension ::= ‘extension’ [DefTypeParamClause] ‘(’ DefParam ‘)’ - * {UsingParamClause}] ExtMethods + * {UsingParamClause} ExtMethods */ def extension(): ExtMethods = val start = in.skipToken() diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 5d5345262520..652c52c10a84 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -541,6 +541,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { keywordText("import ") ~ importText(expr, selectors) case Export(expr, selectors) => keywordText("export ") ~ importText(expr, selectors) + case ExtMethods(tparams, vparamss, mdefs) => + keywordText("extension ") + ~ addVparamssText(tparamsText(tparams), vparamss) + ~ " " ~ (if mdefs.length == 1 then toText(mdefs.head) else blockText(mdefs)) case packageDef: PackageDef => packageDefText(packageDef) case tree: Template => @@ -785,10 +789,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { import untpd._ dclTextOr(tree) { val defKeyword = modText(tree.mods, tree.symbol, keywordStr("def"), isType = false) - val isExtension = tree.hasType && tree.symbol.isExtensionMethod + val isOldExtension = + tree.hasType && tree.symbol.isExtensionMethod && !tree.name.isExtensionName withEnclosingDef(tree) { val (prefix, vparamss) = - if (isExtension) (defKeyword ~~ paramsText(tree.vparamss.head) ~~ valDefText(nameIdText(tree)), tree.vparamss.tail) + if isOldExtension then (defKeyword ~~ paramsText(tree.vparamss.head) ~~ valDefText(nameIdText(tree)), tree.vparamss.tail) else (defKeyword ~~ valDefText(nameIdText(tree)), tree.vparamss) addVparamssText(prefix ~ tparamsText(tree.tparams), vparamss) ~ diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index c2da3d90b668..a87a7af8f601 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -489,15 +489,16 @@ class Namer { typer: Typer => tree.pushAttachment(ExpandedTree, expanded) } tree match { - case tree: DefTree => record(desugar.defTree(tree)) + case tree: DefTree => record(desugar.defTree(tree)) case tree: PackageDef => record(desugar.packageDef(tree)) - case _ => + case tree: ExtMethods => record(desugar.extMethods(tree)) + case _ => } } /** The expanded version of this tree, or tree itself if not expanded */ def expanded(tree: Tree)(using Context): Tree = tree match { - case _: DefTree | _: PackageDef => tree.attachmentOrElse(ExpandedTree, tree) + case _: DefTree | _: PackageDef | _: ExtMethods => tree.attachmentOrElse(ExpandedTree, tree) case _ => tree } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 12051ad2433e..0fe21a94cc74 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2550,11 +2550,14 @@ class Typer extends Namer traverse(rest) } case Thicket(stats) :: rest => - traverse(stats ++ rest) + traverse(stats ::: rest) case (stat: untpd.Export) :: rest => buf ++= stat.attachmentOrElse(ExportForwarders, Nil) // no attachment can happen in case of cyclic references traverse(rest) + case (stat: untpd.ExtMethods) :: rest => + val xtree = stat.removeAttachment(ExpandedTree).get + traverse(xtree :: rest) case stat :: rest => val stat1 = typed(stat)(using ctx.exprContext(stat, exprOwner)) checkStatementPurity(stat1)(stat, exprOwner) From e5ca6d557840fe5a62a5b07f61d65d48b612861c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 29 Jun 2020 14:51:09 +0200 Subject: [PATCH 05/38] Doc fixes --- docs/docs/reference/contextual/extension-methods.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index def262de9ba6..6b301c029118 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -85,6 +85,8 @@ Extensions can also take using clauses. For instance, the `+` extension above co def + (y: T): T = n.plus(x, y) ``` +**Note**: If an extension defines type parameters in its prefix, the extension method itself is not allowed to have additional type parameters. This restriction might be lifted in the future once we support multiple type parameter clauses in a method. + ### Collective Extensions Sometimes, one wants to define several extension methods that share the same @@ -123,8 +125,6 @@ extension [T](xs: List[T])(using Ordering[T]): val limit = smallest(n) xs.zipWithIndex.collect { case (x, i) if x <= limit => i } ``` -**Note**: If an extension defines type parameters in its prefix, the extension methods themselves are not allowed to have additional type parameters. This restriction might be lifted in the future once we support multiple type parameter clauses in a method. - ### Translation of Calls to Extension Methods @@ -182,7 +182,7 @@ object List: extension [T](xs: List[List[T]]) def flatten: List[T] = xs.foldLeft(Nil: List[T])(_ ++ _) - given [T: Ordering] Ordering[List[T]]: + given [T: Ordering] as Ordering[List[T]]: extension (xs: List[T]): def < (ys: List[T]) = ... end List @@ -192,7 +192,7 @@ List(List(1, 2), List(3, 4)).flatten // extension method available since it is in the given Ordering[List[T]], // which is itself in the implicit scope of List[Int] -List(1, 2) <: List(3) +List(1, 2) < List(3) ``` The precise rules for resolving a selection to an extension method are as follows. From 02de5dcfdfd6bc85a0b0bfdefe4976af5ab8cafc Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 29 Jun 2020 18:36:10 +0200 Subject: [PATCH 06/38] Prefix extension methods with `extension_`. --- .../src/dotty/tools/dotc/ast/Desugar.scala | 1 + .../src/dotty/tools/dotc/core/NameOps.scala | 9 ++ .../tools/dotc/core/SymDenotations.scala | 9 ++ .../dotty/tools/dotc/parsing/Parsers.scala | 3 +- .../tools/dotc/printing/PlainPrinter.scala | 2 +- .../dotty/tools/dotc/printing/Printer.scala | 5 +- .../tools/dotc/transform/PostTyper.scala | 6 +- .../dotty/tools/dotc/typer/Applications.scala | 8 +- .../src/dotty/tools/dotc/typer/Checking.scala | 2 +- .../dotty/tools/dotc/typer/Implicits.scala | 4 +- .../tools/dotc/typer/ImportSuggestions.scala | 12 +- .../src/dotty/tools/dotc/typer/Namer.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 126 ++++++++++-------- tests/neg/i6779.check | 9 +- tests/neg/i9185.check | 12 +- tests/neg/i9185.scala | 2 +- tests/neg/missing-implicit1.check | 2 +- tests/neg/missing-implicit4.check | 2 +- .../override-extension_normal-methods.check | 10 -- .../override-extension_normal-methods.scala | 6 +- tests/pos/i5773.scala | 2 +- tests/pos/i7401.scala | 4 +- tests/pos/opaque-xm.scala | 2 +- tests/pos/reference/delegates.scala | 8 +- tests/pos/reference/extension-methods.scala | 16 ++- tests/run-macros/i6201/test_2.scala | 4 +- .../xml-interpolation-7/Macros_1.scala | 2 +- tests/run/collective-extensions.scala | 2 +- tests/run/extension-methods.scala | 6 +- tests/run/extmethods2.scala | 4 +- tests/run/instances-anonymous.scala | 4 +- tests/run/instances.scala | 6 +- 32 files changed, 167 insertions(+), 125 deletions(-) delete mode 100644 tests/neg/override-extension_normal-methods.check diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index eef43c6a4b6b..343ea26d7bac 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -948,6 +948,7 @@ object desugar { stat match case mdef: DefDef => cpy.DefDef(mdef)( + name = mdef.name.toExtensionName, tparams = tparams ++ mdef.tparams, vparamss = vparamss ::: mdef.vparamss, ).withMods(mdef.mods | Extension) diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 52d7a96d7ede..c1423b61b204 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -131,12 +131,21 @@ object NameOps { else name.toTermName } + /** Does this name start with `extension_`? */ def isExtensionName: Boolean = name match case name: SimpleName => name.startsWith("extension_") case _ => false + /** Add an `extension_` in front of this name */ def toExtensionName = termName("extension_" ++ name.toString) + /** Drop `extension_` in front of this name, if it has this prefix */ + def dropExtension = name match + case name: SimpleName if name.startsWith("extension_") => + name.drop("extension_".length) + case _ => + name + /** The expanded name. * This is the fully qualified name of `base` with `ExpandPrefixName` as separator, * followed by `kind` and the name. diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index bd61fcb4da01..e5e5c1cad574 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1190,6 +1190,15 @@ object SymDenotations { else if (this.exists) owner.enclosingMethod else NoSymbol + /** The closest enclosing extension method containing this definition, + * provided the extension method appears in the same class. + */ + final def enclosingExtensionMethod(using Context): Symbol = + if this.isAllOf(ExtensionMethod) then symbol + else if this.isClass then NoSymbol + else if this.exists then owner.enclosingExtensionMethod + else NoSymbol + /** The top-level class containing this denotation, * except for a toplevel module, where its module class is returned. */ diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 9ef2580a3922..11ec2b7e1583 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3320,7 +3320,8 @@ object Parsers { else (Nil, Nil) val ident = termIdent() - val name = ident.name.asTermName + var name = ident.name.asTermName + if mods1.is(Extension) then name = name.toExtensionName if isInfix && !name.isOperatorName then val infixAnnot = Apply(wrapNew(scalaAnnotationDot(tpnme.infix)), Nil) .withSpan(Span(start, start)) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 100891a50df0..4615e221635d 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -308,7 +308,7 @@ class PlainPrinter(_ctx: Context) extends Printer { } /** The string representation of this type used as a prefix, including separator */ - protected def toTextPrefix(tp: Type): Text = controlled { + def toTextPrefix(tp: Type): Text = controlled { homogenize(tp) match { case NoPrefix => "" case tp: SingletonType => toTextRef(tp) ~ "." diff --git a/compiler/src/dotty/tools/dotc/printing/Printer.scala b/compiler/src/dotty/tools/dotc/printing/Printer.scala index b2aae6e8bf2a..8584c889eeda 100644 --- a/compiler/src/dotty/tools/dotc/printing/Printer.scala +++ b/compiler/src/dotty/tools/dotc/printing/Printer.scala @@ -97,9 +97,12 @@ abstract class Printer { */ def toText(sym: Symbol): Text - /** Textual representation of singeton type reference */ + /** Textual representation of singleton type reference */ def toTextRef(tp: SingletonType): Text + /** Textual representation of a prefix of some reference, ending in `.` or `#` */ + def toTextPrefix(tp: Type): Text + /** Textual representation of symbol's declaration */ def dclText(sym: Symbol): Text diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 316a2ed87707..d86aa086d917 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -10,7 +10,7 @@ import dotty.tools.dotc.typer.VarianceChecker import Types._, Contexts._, Names._, Flags._, DenotTransformers._, Phases._ import SymDenotations._, StdNames._, Annotations._, Trees._, Scopes._ import Decorators._ -import Symbols._, SymUtils._ +import Symbols._, SymUtils._, NameOps._ import ContextFunctionResults.annotateContextResults import config.Printers.typr import reporting.messages._ @@ -349,7 +349,9 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase val seen = mutable.Set.empty[Name] def checkIdent(sel: untpd.ImportSelector): Unit = - if !exprTpe.member(sel.name).exists && !exprTpe.member(sel.name.toTypeName).exists then + if !exprTpe.member(sel.name).exists + && !exprTpe.member(sel.name.toTypeName).exists + && !exprTpe.member(sel.name.toExtensionName).exists then ctx.error(NotAMember(exprTpe, sel.name, "value"), sel.imported.sourcePos) if seen.contains(sel.name) then ctx.error(ImportRenamedTwice(sel.imported), sel.imported.sourcePos) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 6cafff446e8e..01438409d4f4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -18,6 +18,7 @@ import ErrorReporting._ import Trees._ import Names._ import StdNames._ +import NameOps._ import NameKinds.DefaultGetterName import ProtoTypes._ import Inferencing._ @@ -1338,13 +1339,14 @@ trait Applications extends Compatibility { followApply && tp.member(nme.apply).hasAltWith(d => p(TermRef(tp, nme.apply, d))) } - /** Does `tp` have an extension method named `name` with this-argument `argType` and + /** Does `tp` have an extension method named `extension_name` with this-argument `argType` and * result matching `resultType`? */ def hasExtensionMethod(tp: Type, name: TermName, argType: Type, resultType: Type)(using Context) = { + val xname = name.toExtensionName def qualifies(mbr: Denotation) = - mbr.exists && isApplicableType(tp.select(name, mbr), argType :: Nil, resultType) - tp.memberBasedOnFlags(name, required = ExtensionMethod) match { + mbr.exists && isApplicableType(tp.select(xname, mbr), argType :: Nil, resultType) + tp.memberBasedOnFlags(xname, required = ExtensionMethod) match { case mbr: SingleDenotation => qualifies(mbr) case mbr => mbr.hasAltWith(qualifies(_)) } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 465400e7cf0e..80b59d4f52f0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1125,7 +1125,7 @@ trait Checking { // would have produced the same symbol without errors def allowAccess(name: Name, sym: Symbol): Boolean = { val testCtx = caseCtx.fresh.setNewTyperState() - val ref = ctx.typer.typedIdent(untpd.Ident(name), WildcardType)(using testCtx) + val ref = ctx.typer.typedIdent(untpd.Ident(name).withSpan(stat.span), WildcardType)(using testCtx) ref.symbol == sym && !testCtx.reporter.hasErrors } checkRefsLegal(tree, cdef.symbol, allowAccess, "enum case") diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 85bec4651f96..31bd3cc6ad7b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -71,7 +71,7 @@ object Implicits { */ def hasExtMethod(tp: Type, expected: Type)(using Context) = expected match case SelectionProto(name, _, _, _) => - tp.memberBasedOnFlags(name, required = ExtensionMethod).exists + tp.memberBasedOnFlags(name.toExtensionName, required = ExtensionMethod).exists case _ => false def strictEquality(using Context): Boolean = @@ -1027,7 +1027,7 @@ trait Implicits { self: Typer => } pt match case SelectionProto(name: TermName, mbrType, _, _) if cand.isExtension => - val result = extMethodApply(untpd.Select(untpdGenerated, name), argument, mbrType) + val result = extMethodApply(untpd.Select(untpdGenerated, name.toExtensionName), argument, mbrType) if !ctx.reporter.hasErrors && cand.isConversion then val testCtx = ctx.fresh.setExploreTyperState() tryConversion(using testCtx) diff --git a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala index 6bcb816b1ce4..0bdb6d2507c6 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala @@ -6,6 +6,7 @@ import core._ import Contexts._, Types._, Symbols._, Names._, Decorators._, ProtoTypes._ import Flags._, SymDenotations._ import NameKinds.FlatName +import NameOps._ import config.Printers.{implicits, implicitsDetailed} import util.Spans.Span import ast.{untpd, tpd} @@ -215,7 +216,7 @@ trait ImportSuggestions: * applicable to `argType`. */ def extensionMethod(site: TermRef, name: TermName, argType: Type): Option[TermRef] = - site.member(name) + site.member(name.toExtensionName) .alternatives .map(mbr => TermRef(site, mbr.symbol)) .filter(ref => @@ -300,7 +301,7 @@ trait ImportSuggestions: if filled < n && rest.nonEmpty then rest.toList.best(n - filled) else Nil top.take(filled).toList ++ remaining - end best + //end best TODO: re-enable with new syntax /** An addendum to an error message where the error might be fixed * by some implicit value of type `pt` that is however not found. @@ -315,7 +316,12 @@ trait ImportSuggestions: if fullMatches.nonEmpty then (fullMatches, "fix") else (headMatches, "make progress towards fixing") def importString(ref: TermRef): String = - s" import ${ctx.printer.toTextRef(ref).show}" + val imported = + if ref.symbol.isAllOf(ExtensionMethod) then + s"${ctx.printer.toTextPrefix(ref.prefix).show}${ref.symbol.name.dropExtension}" + else + ctx.printer.toTextRef(ref).show + s" import $imported" val suggestions = suggestedRefs .zip(suggestedRefs.map(importString)) .filter((ref, str) => str.contains('.')) // must be a real import with `.` diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index a87a7af8f601..d6408d00776e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1156,7 +1156,7 @@ class Namer { typer: Typer => def addForwardersNamed(name: TermName, alias: TermName, span: Span): Unit = { val size = buf.size - val mbrs = List(name, name.toTypeName).flatMap(path.tpe.member(_).alternatives) + val mbrs = List(name, name.toTypeName, name.toExtensionName).flatMap(path.tpe.member(_).alternatives) mbrs.foreach(addForwarder(alias, _, span)) if (buf.size == size) { val reason = mbrs.map(whyNoForwarder).dropWhile(_ == SKIP) match { diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 0fe21a94cc74..b13066099244 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -213,6 +213,9 @@ class Typer extends Namer def namedImportRef(imp: ImportInfo)(using Context): Type = { val termName = name.toTermName + def adjustExtension(name: Name) = + if required.is(Extension) then name.toExtensionName else name + def recur(selectors: List[untpd.ImportSelector]): Type = selectors match case selector :: rest => def checkUnambiguous(found: Type) = @@ -221,12 +224,12 @@ class Typer extends Namer refctx.error(em"reference to `$name` is ambiguous; it is imported twice", posd.sourcePos) found - if selector.rename == termName then + if adjustExtension(selector.rename) == termName then val memberName = if selector.name == termName then name else if name.isTypeName then selector.name.toTypeName else selector.name - checkUnambiguous(selection(imp, memberName, checkBounds = false)) + checkUnambiguous(selection(imp, adjustExtension(memberName), checkBounds = false)) else recur(rest) @@ -422,7 +425,7 @@ class Typer extends Namer * (2) Change imported symbols to selections. * (3) Change pattern Idents id (but not wildcards) to id @ _ */ - def typedIdent(tree: untpd.Ident, pt: Type)(using Context): Tree = { + def typedIdent(tree: untpd.Ident, pt: Type)(using Context): Tree = record("typedIdent") val name = tree.name def kind = if (name.isTermName) "" else "type " @@ -439,18 +442,24 @@ class Typer extends Namer if (name == nme.ROOTPKG) return tree.withType(defn.RootPackage.termRef) - /** Convert a reference `f` to an extension method in a collective extension - * on parameter `x` to `x.f` + /** Convert a reference `f` to an extension method select `p.f`, where + * `p` is the closest enclosing extension parameter, or else `this`. */ - def extensionMethodSelect(xmethod: Symbol): untpd.Tree = - val leadParamName = xmethod.info.paramNamess.head.head - def isLeadParam(sym: Symbol) = - sym.is(Param) && sym.owner.owner == xmethod.owner && sym.name == leadParamName - def leadParam(ctx: Context): Symbol = - ctx.scope.lookupAll(leadParamName).find(isLeadParam) match - case Some(param) => param - case None => leadParam(ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next) - untpd.cpy.Select(tree)(untpd.ref(leadParam(ctx).termRef), name) + def extensionMethodSelect: untpd.Tree = + val xmethod = ctx.owner.enclosingExtensionMethod + val qualifier = + if xmethod.exists then // TODO: see whether we can use paramss for that + val leadParamName = xmethod.info.paramNamess.head.head + def isLeadParam(sym: Symbol) = + sym.is(Param) && sym.owner.owner == xmethod.owner && sym.name == leadParamName + def leadParam(ctx: Context): Symbol = + ctx.scope.lookupAll(leadParamName).find(isLeadParam) match + case Some(param) => param + case None => leadParam(ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next) + untpd.ref(leadParam(ctx).termRef) + else + untpd.This(untpd.EmptyTypeIdent) + untpd.cpy.Select(tree)(qualifier, name) val rawType = { val saved1 = unimported @@ -474,53 +483,50 @@ class Typer extends Namer } } - val ownType = - if (rawType.exists) - ensureAccessible(rawType, superAccess = false, tree.sourcePos) - else if (name == nme._scope) - // gross hack to support current xml literals. - // awaiting a better implicits based solution for library-supported xml - return ref(defn.XMLTopScopeModule.termRef) - else if (name.toTermName == nme.ERROR) - UnspecifiedErrorType - else if (ctx.owner.isConstructor && !ctx.owner.isPrimaryConstructor && - ctx.owner.owner.unforcedDecls.lookup(tree.name).exists) + def setType(ownType: Type): Tree = + val tree1 = ownType match + case ownType: NamedType if !prefixIsElidable(ownType) => + ref(ownType).withSpan(tree.span) + case _ => + tree.withType(ownType) + val tree2 = toNotNullTermRef(tree1, pt) + checkStableIdentPattern(tree2, pt) + tree2 + + def fail: Tree = + if ctx.owner.isConstructor && !ctx.owner.isPrimaryConstructor + && ctx.owner.owner.unforcedDecls.lookup(tree.name).exists + then // we are in the arguments of a this(...) constructor call - errorType(ex"$tree is not accessible from constructor arguments", tree.sourcePos) + errorTree(tree, ex"$tree is not accessible from constructor arguments") else - errorType(MissingIdent(tree, kind, name), tree.sourcePos) - - val tree1 = ownType match { - case ownType: NamedType => - val sym = ownType.symbol - if sym.isAllOf(ExtensionMethod) - && sym.owner.isCollectiveExtensionClass - && ctx.owner.isContainedIn(sym.owner) - then typed(extensionMethodSelect(sym), pt) - else if prefixIsElidable(ownType) then tree.withType(ownType) - else ref(ownType).withSpan(tree.span) - case _ => - tree.withType(ownType) - } - - val tree2 = toNotNullTermRef(tree1, pt) - - checkStableIdentPattern(tree2, pt) - } + errorTree(tree, MissingIdent(tree, kind, name)) + + if rawType.exists then + setType(ensureAccessible(rawType, superAccess = false, tree.sourcePos)) + else if name == nme._scope then + // gross hack to support current xml literals. + // awaiting a better implicits based solution for library-supported xml + ref(defn.XMLTopScopeModule.termRef) + else if name.toTermName == nme.ERROR then + setType(UnspecifiedErrorType) + else if name.isTermName then + tryEither(typed(extensionMethodSelect, pt))((_, _) => fail) + else + fail + end typedIdent /** Check that a stable identifier pattern is indeed stable (SLS 8.1.5) */ - private def checkStableIdentPattern(tree: Tree, pt: Type)(using Context): tree.type = { - if (ctx.mode.is(Mode.Pattern) && - !tree.isType && - !pt.isInstanceOf[ApplyingProto] && - !tree.tpe.isStable && - !isWildcardArg(tree)) + private def checkStableIdentPattern(tree: Tree, pt: Type)(using Context): Unit = + if ctx.mode.is(Mode.Pattern) + && !tree.isType + && !pt.isInstanceOf[ApplyingProto] + && !tree.tpe.isStable + && !isWildcardArg(tree) + then ctx.error(StableIdentPattern(tree, pt), tree.sourcePos) - tree - } - def typedSelect(tree: untpd.Select, pt: Type, qual: Tree)(using Context): Tree = qual match { case qual @ IntegratedTypeArgs(app) => pt.revealIgnored match { @@ -533,9 +539,13 @@ class Typer extends Namer if (tree.name.isTypeName) checkStable(qual.tpe, qual.sourcePos, "type prefix") - if (select1.tpe ne TryDynamicCallType) ConstFold(checkStableIdentPattern(select1, pt)) - else if (pt.isInstanceOf[FunOrPolyProto] || pt == AssignProto) select1 - else typedDynamicSelect(tree, Nil, pt) + if select1.tpe ne TryDynamicCallType then + checkStableIdentPattern(select1, pt) + ConstFold(select1) + else if pt.isInstanceOf[FunOrPolyProto] || pt == AssignProto then + select1 + else + typedDynamicSelect(tree, Nil, pt) } def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = { @@ -3383,7 +3393,7 @@ class Typer extends Namer case SelectionProto(name, mbrType, _, _) => def tryExtension(using Context): Tree = try - findRef(name, WildcardType, ExtensionMethod, tree.posd) match { + findRef(name.toExtensionName, WildcardType, ExtensionMethod, tree.posd) match { case ref: TermRef => extMethodApply(untpd.ref(ref).withSpan(tree.span), tree, mbrType) case _ => EmptyTree diff --git a/tests/neg/i6779.check b/tests/neg/i6779.check index 396e3f829d6a..48cf82ef2697 100644 --- a/tests/neg/i6779.check +++ b/tests/neg/i6779.check @@ -8,8 +8,9 @@ | ^ | Found: F[T] | Required: F[G[T]] --- [E007] Type Mismatch Error: tests/neg/i6779.scala:13:31 ------------------------------------------------------------- +-- [E006] Not Found Error: tests/neg/i6779.scala:13:27 ----------------------------------------------------------------- 13 |def g3[T](x: T): F[G[T]] = f(x)(using summon[Stuff]) // error - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: F[T] - | Required: F[G[T]] + | ^ + | Not found: f + +longer explanation available when compiling with `-explain` diff --git a/tests/neg/i9185.check b/tests/neg/i9185.check index 0e0b02b6029e..9fb873f57334 100644 --- a/tests/neg/i9185.check +++ b/tests/neg/i9185.check @@ -4,17 +4,17 @@ |value pure is not a member of String. |An extension method was tried, but could not be fully constructed: | - | M.pure[A, F]("ola")( + | M.extension_pure[A, F]("ola")( | /* ambiguous: both object listMonad in object M and object optionMonad in object M match type M[F] */summon[M[F]] | ) --- Error: tests/neg/i9185.scala:8:26 ----------------------------------------------------------------------------------- -8 | val value3 = pure("ola") // error - | ^ - |ambiguous implicit arguments: both object listMonad in object M and object optionMonad in object M match type M[F] of parameter m of method pure in object M +-- Error: tests/neg/i9185.scala:8:36 ----------------------------------------------------------------------------------- +8 | val value3 = extension_pure("ola") // error + | ^ + |ambiguous implicit arguments: both object listMonad in object M and object optionMonad in object M match type M[F] of parameter m of method extension_pure in object M -- [E008] Not Found Error: tests/neg/i9185.scala:11:16 ----------------------------------------------------------------- 11 | val l = "abc".len // error | ^^^^^^^^^ | value len is not a member of String. | An extension method was tried, but could not be fully constructed: | - | M.len("abc") + | M.extension_len("abc") diff --git a/tests/neg/i9185.scala b/tests/neg/i9185.scala index 911d380a98af..9cbb751cb9c5 100644 --- a/tests/neg/i9185.scala +++ b/tests/neg/i9185.scala @@ -5,7 +5,7 @@ object M { given optionMonad as M[Option] { def pure[A](x: A): Option[A] = Some(x) } val value1: List[String] = "ola".pure val value2 = "ola".pure // error - val value3 = pure("ola") // error + val value3 = extension_pure("ola") // error def (x: Int).len: Int = x val l = "abc".len // error diff --git a/tests/neg/missing-implicit1.check b/tests/neg/missing-implicit1.check index 4ff1d5d84225..5b3d0dc6a4e9 100644 --- a/tests/neg/missing-implicit1.check +++ b/tests/neg/missing-implicit1.check @@ -19,7 +19,7 @@ -- Error: tests/neg/missing-implicit1.scala:23:42 ---------------------------------------------------------------------- 23 | List(1, 2, 3).traverse(x => Option(x)) // error | ^ - |no implicit argument of type testObjectInstance.Zip[Option] was found for an implicit parameter of method traverse in trait Traverse + |no implicit argument of type testObjectInstance.Zip[Option] was found for an implicit parameter of method extension_traverse in trait Traverse | |The following import might fix the problem: | diff --git a/tests/neg/missing-implicit4.check b/tests/neg/missing-implicit4.check index 4653dd8df351..2baa37b0e5e9 100644 --- a/tests/neg/missing-implicit4.check +++ b/tests/neg/missing-implicit4.check @@ -19,7 +19,7 @@ -- Error: tests/neg/missing-implicit4.scala:20:42 ---------------------------------------------------------------------- 20 | List(1, 2, 3).traverse(x => Option(x)) // error | ^ - |no implicit argument of type Zip[Option] was found for an implicit parameter of method traverse in trait Traverse + |no implicit argument of type Zip[Option] was found for an implicit parameter of method extension_traverse in trait Traverse | |The following import might fix the problem: | diff --git a/tests/neg/override-extension_normal-methods.check b/tests/neg/override-extension_normal-methods.check deleted file mode 100644 index fdd94fe558ee..000000000000 --- a/tests/neg/override-extension_normal-methods.check +++ /dev/null @@ -1,10 +0,0 @@ --- Error: tests/neg/override-extension_normal-methods.scala:6:6 -------------------------------------------------------- -6 | def m[T](x: T): String = "normal method" // error: normal method, cannot override an extension method. Also needs `override' modifier (but this error should be obfuscated). - | ^ - | error overriding method m in trait A of type [T](t: T): String; - | method m of type [T](x: T): String is a normal method, cannot override an extension method --- Error: tests/neg/override-extension_normal-methods.scala:14:6 ------------------------------------------------------- -14 | def [T](t: T).m: String = "extrnsion method" // error: extension method, cannot override an normal method. Also needs `override' modifier (but this error should be obfuscated). - | ^ - | error overriding method m in trait B of type [T](x: T): String; - | method m of type [T](t: T): String is an extension method, cannot override a normal method diff --git a/tests/neg/override-extension_normal-methods.scala b/tests/neg/override-extension_normal-methods.scala index 1293bf8d6b14..b54268ff95de 100644 --- a/tests/neg/override-extension_normal-methods.scala +++ b/tests/neg/override-extension_normal-methods.scala @@ -1,9 +1,9 @@ trait A { - def [T](t: T).m: String = "extrnsion method" + def [T](t: T).m: String = "extension method" } trait AAA extends A { - def m[T](x: T): String = "normal method" // error: normal method, cannot override an extension method. Also needs `override' modifier (but this error should be obfuscated). + override def m[T](x: T): String = "normal method" // error: does not override } trait B { @@ -11,5 +11,5 @@ trait B { } trait BBB extends B { - def [T](t: T).m: String = "extrnsion method" // error: extension method, cannot override an normal method. Also needs `override' modifier (but this error should be obfuscated). + override def [T](t: T).m: String = "extension method" // error: does not override } \ No newline at end of file diff --git a/tests/pos/i5773.scala b/tests/pos/i5773.scala index 76f1e491a63e..ddedbb07c356 100644 --- a/tests/pos/i5773.scala +++ b/tests/pos/i5773.scala @@ -31,6 +31,6 @@ object Main { def f3 = { import Semigroup.SumSemiGroupDeco - sumSemigroup.append(1)(2) + sumSemigroup.extension_append(1)(2) } } \ No newline at end of file diff --git a/tests/pos/i7401.scala b/tests/pos/i7401.scala index 3d90bed0cfab..dea94f10f842 100644 --- a/tests/pos/i7401.scala +++ b/tests/pos/i7401.scala @@ -6,6 +6,6 @@ object Test { val x: Int = 5 x.foo(4) x.foo - ops.foo(x)(4) - ops.foo(x) + ops.extension_foo(x)(4) + ops.extension_foo(x) } \ No newline at end of file diff --git a/tests/pos/opaque-xm.scala b/tests/pos/opaque-xm.scala index a8619c287f58..7d54ba1fa7b3 100644 --- a/tests/pos/opaque-xm.scala +++ b/tests/pos/opaque-xm.scala @@ -30,6 +30,6 @@ object usesites { // as a contextual implicit this takes precedence over the // implicit scope implicit LogarithmOps. // TODO: Remove any2stringadd - val d = Logarithm.toDouble(l3) + val d = Logarithm.extension_toDouble(l3) val l5: Logarithm = (1.0).asInstanceOf[Logarithm] } diff --git a/tests/pos/reference/delegates.scala b/tests/pos/reference/delegates.scala index c624afecfcee..0df99c954bbf 100644 --- a/tests/pos/reference/delegates.scala +++ b/tests/pos/reference/delegates.scala @@ -65,7 +65,7 @@ object Instances extends Common: xs.reduceLeft((x, y) => if (x < y) y else x) def descending[T](using asc: Ord[T]): Ord[T] = new Ord[T]: - def (x: T).compareTo(y: T) = asc.compareTo(y)(x) + def (x: T).compareTo(y: T) = asc.extension_compareTo(y)(x) def minimum[T](xs: List[T])(using Ord[T]) = maximum(xs)(using descending) @@ -163,7 +163,7 @@ object AnonymousInstances extends Common: def second = xs.tail.head given [From, To](using c: Convertible[From, To]) as Convertible[List[From], List[To]]: - def (x: List[From]).convert: List[To] = x.map(c.convert) + def (x: List[From]).convert: List[To] = x.map(c.extension_convert) given Monoid[String]: def (x: String).combine(y: String): String = x.concat(y) @@ -190,7 +190,7 @@ object Implicits extends Common: class given_Convertible_List_List[From, To](implicit c: Convertible[From, To]) extends Convertible[List[From], List[To]]: - def (x: List[From]).convert: List[To] = x.map(c.convert) + def (x: List[From]).convert: List[To] = x.map(c.extension_convert) implicit def given_Convertible_List_List[From, To](implicit c: Convertible[From, To]) : Convertible[List[From], List[To]] = new given_Convertible_List_List[From, To] @@ -200,7 +200,7 @@ object Implicits extends Common: xs.reduceLeft((x, y) => if (x < y) y else x) def descending[T](implicit asc: Ord[T]): Ord[T] = new Ord[T]: - def (x: T).compareTo(y: T) = asc.compareTo(y)(x) + def (x: T).compareTo(y: T) = asc.extension_compareTo(y)(x) def minimum[T](xs: List[T])(implicit cmp: Ord[T]) = maximum(xs)(descending) diff --git a/tests/pos/reference/extension-methods.scala b/tests/pos/reference/extension-methods.scala index 78ba2233464f..c317db11a1a3 100644 --- a/tests/pos/reference/extension-methods.scala +++ b/tests/pos/reference/extension-methods.scala @@ -6,7 +6,7 @@ object ExtMethods: val circle = Circle(0, 0, 1) circle.circumference - assert(circle.circumference == circumference(circle)) + assert(circle.circumference == extension_circumference(circle)) trait StringSeqOps { def (xs: Seq[String]).longestStrings = { @@ -18,10 +18,18 @@ object ExtMethods: List("here", "is", "a", "list").longestStrings + trait StringSeqOps2 { + def (xs: Seq[String]).longestStrings2 = { + val maxLength = xs.map(_.length).max + xs.filter(_.length == maxLength) + } + } + locally { - object ops2 extends StringSeqOps - import ops2.longestStrings - List("here", "is", "a", "list").longestStrings + object ops2 extends StringSeqOps2 + import ops2.{longestStrings2, extension_longestStrings2} + List("here", "is", "a", "list").longestStrings2 + extension_longestStrings2(Nil) } def (x: String) < (y: String) = x.compareTo(y) < 0 diff --git a/tests/run-macros/i6201/test_2.scala b/tests/run-macros/i6201/test_2.scala index cb8c2053e673..86519b41eb3d 100644 --- a/tests/run-macros/i6201/test_2.scala +++ b/tests/run-macros/i6201/test_2.scala @@ -1,6 +1,6 @@ object Test { def main(args: Array[String]): Unit = { - assert(isHello(strip("hello"))) - assert(!isHello(strip("bonjour"))) + assert(isHello(extension_strip("hello"))) + assert(!isHello(extension_strip("bonjour"))) } } \ No newline at end of file diff --git a/tests/run-macros/xml-interpolation-7/Macros_1.scala b/tests/run-macros/xml-interpolation-7/Macros_1.scala index 1f753c7e47ee..97149956bcd0 100644 --- a/tests/run-macros/xml-interpolation-7/Macros_1.scala +++ b/tests/run-macros/xml-interpolation-7/Macros_1.scala @@ -27,7 +27,7 @@ object XmlQuote { def impl(receiver: Expr[XMLOps.StringContext], args: Expr[Seq[Any]])(using QuoteContext): Expr[Xml] = { val string = receiver match { - case '{ XMLOps.xml(${Unlifted(sc)}) } => sc.parts.mkString("??") + case '{ XMLOps.extension_xml(${Unlifted(sc)}) } => sc.parts.mkString("??") } '{new Xml(${string}, $args.toList)} } diff --git a/tests/run/collective-extensions.scala b/tests/run/collective-extensions.scala index 9c8c2d4de04d..12cdb69682eb 100644 --- a/tests/run/collective-extensions.scala +++ b/tests/run/collective-extensions.scala @@ -4,7 +4,7 @@ extension on (x: String): def baz(y: String): String = val x = y bar(x) - def bam(y: String): String = this.baz(x)(y) + def bam(y: String): String = this.extension_baz(x)(y) def ban(foo: String): String = x + foo def bao(y: String): String = val bam = "ABC" diff --git a/tests/run/extension-methods.scala b/tests/run/extension-methods.scala index ae5a9c4c2d2c..5d6f2713380d 100644 --- a/tests/run/extension-methods.scala +++ b/tests/run/extension-methods.scala @@ -2,7 +2,7 @@ object Test extends App { def (x: Int).em: Boolean = x > 0 - assert(1.em == em(1)) + assert(1.em == extension_em(1)) case class Circle(x: Double, y: Double, radius: Double) @@ -10,7 +10,7 @@ object Test extends App { val circle = new Circle(1, 1, 2.0) - assert(circle.circumference == circumference(circle)) + assert(circle.circumference == extension_circumference(circle)) def (xs: Seq[String]).longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max @@ -114,7 +114,7 @@ object Test extends App { def mappAll[F[_]: Monad, T](x: T, fs: List[T => T]): F[T] = fs.foldLeft(implicitly[Monad[F]].pure(x))((x: F[T], f: T => T) => - if (true) implicitly[Monad[F]].map(x)(f) + if (true) implicitly[Monad[F]].extension_map(x)(f) else if (true) x.map(f) else x.map[T, T](f) ) diff --git a/tests/run/extmethods2.scala b/tests/run/extmethods2.scala index f454612762ea..60eda2d80bea 100644 --- a/tests/run/extmethods2.scala +++ b/tests/run/extmethods2.scala @@ -34,8 +34,8 @@ object Test extends App { val xs = List(1, 2, 3) assert(xs.second[Int] == 2) assert(xs.third == 3) - assert(A.listOps.second[Int](xs) == 2) - assert(A.listOps.third(xs) == 3) + assert(A.listOps.extension_second[Int](xs) == 2) + assert(A.listOps.extension_third(xs) == 3) assert(xs.prod == 6) assert(xs.concat(xs).length == 6) assert(xs.zipp(xs).map(_ + _).prod == 36) diff --git a/tests/run/instances-anonymous.scala b/tests/run/instances-anonymous.scala index b71d2e775ddf..5202eb5aebe1 100644 --- a/tests/run/instances-anonymous.scala +++ b/tests/run/instances-anonymous.scala @@ -4,7 +4,7 @@ object Test extends App { def (x: Int).em: Boolean = x > 0 } - assert(1.em == O.em(1)) + assert(1.em == O.extension_em(1)) case class Circle(x: Double, y: Double, radius: Double) @@ -117,7 +117,7 @@ object Test extends App { def mappAll[F[_]: Monad, T](x: T, fs: List[T => T]): F[T] = fs.foldLeft(implicitly[Monad[F]].pure(x))((x: F[T], f: T => T) => - if (true) implicitly[Monad[F]].map(x)(f) + if (true) implicitly[Monad[F]].extension_map(x)(f) else if (true) x.map(f) else x.map[T, T](f) ) diff --git a/tests/run/instances.scala b/tests/run/instances.scala index c56054aeeb06..99b1820ecf72 100644 --- a/tests/run/instances.scala +++ b/tests/run/instances.scala @@ -4,7 +4,7 @@ object Test extends App { def (x: Int).em: Boolean = x > 0 } - assert(1.em == O.em(1)) + assert(1.em == O.extension_em(1)) case class Circle(x: Double, y: Double, radius: Double) @@ -13,7 +13,7 @@ object Test extends App { val circle = new Circle(1, 1, 2.0) - assert(circle.circumference == circleOps.circumference(circle)) + assert(circle.circumference == circleOps.extension_circumference(circle)) extension stringOps on (xs: Seq[String]): def longestStrings: Seq[String] = @@ -114,7 +114,7 @@ object Test extends App { def mapAll[F[_]: Monad, T](x: T, fs: List[T => T]): F[T] = fs.foldLeft(summon[Monad[F]].pure(x))((x: F[T], f: T => T) => - if true then summon[Monad[F]].map(x)(f) + if true then summon[Monad[F]].extension_map(x)(f) else if true then x.map(f) else x.map[T, T](f) ) From 70d5ec880f94d9bfbea91c9bc6c4c03b7fb3ecef Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 30 Jun 2020 09:45:49 +0200 Subject: [PATCH 07/38] Print extension methods with new syntax --- .../dotty/tools/dotc/printing/RefinedPrinter.scala | 14 +++++++++----- tests/printing/i620.check | 2 +- tests/semanticdb/expect/Givens.scala | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 652c52c10a84..ba456023a979 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -745,9 +745,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { private def useSymbol(tree: untpd.Tree) = tree.hasType && tree.symbol.exists && ctx.settings.YprintSyms.value - protected def nameIdText[T >: Untyped](tree: NameTree[T]): Text = + protected def nameIdText[T >: Untyped](tree: NameTree[T], dropExtension: Boolean = false): Text = if (tree.hasType && tree.symbol.exists) { - val str: Text = nameString(tree.symbol) + var str = nameString(tree.symbol) + if tree.symbol.isExtensionMethod && dropExtension && str.startsWith("extension_") then + str = str.drop("extension_".length) tree match { case tree: RefTree => withPos(str, tree.sourcePos) case tree: MemberDef => withPos(str, tree.sourcePos.withSpan(tree.nameSpan)) @@ -789,11 +791,13 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { import untpd._ dclTextOr(tree) { val defKeyword = modText(tree.mods, tree.symbol, keywordStr("def"), isType = false) - val isOldExtension = - tree.hasType && tree.symbol.isExtensionMethod && !tree.name.isExtensionName + val isExtension = tree.hasType && tree.symbol.isExtensionMethod withEnclosingDef(tree) { val (prefix, vparamss) = - if isOldExtension then (defKeyword ~~ paramsText(tree.vparamss.head) ~~ valDefText(nameIdText(tree)), tree.vparamss.tail) + if isExtension then + (keywordStr("extension") ~~ paramsText(tree.vparamss.head) + ~~ (defKeyword ~~ valDefText(nameIdText(tree, dropExtension = true))).close, + tree.vparamss.tail) else (defKeyword ~~ valDefText(nameIdText(tree)), tree.vparamss) addVparamssText(prefix ~ tparamsText(tree.tparams), vparamss) ~ diff --git a/tests/printing/i620.check b/tests/printing/i620.check index 4563190dcf16..ab21ddbb0d87 100644 --- a/tests/printing/i620.check +++ b/tests/printing/i620.check @@ -11,7 +11,7 @@ package O { protected[A] def f: Int = 0 def g: Int = 0 def g1(t: Int): Int = 0 - @_root_.scala.annotation.infix() def (c: D.this.C) g1: Int = 0 + extension (c: D.this.C) @_root_.scala.annotation.infix() def g1: Int = 0 } private[D] class E() extends Object() {} private[this] class F() extends Object() {} diff --git a/tests/semanticdb/expect/Givens.scala b/tests/semanticdb/expect/Givens.scala index 54a905a51579..6b5ab7912d33 100644 --- a/tests/semanticdb/expect/Givens.scala +++ b/tests/semanticdb/expect/Givens.scala @@ -24,4 +24,4 @@ object Givens: inline given int2String as Conversion[Int, String] = _.toString - def foo[A](using A: Monoid[A]): A = A.combine(A.empty)(A.empty) + def foo[A](using A: Monoid[A]): A = A.extension_combine(A.empty)(A.empty) From 28ef7b6a947e02bb7ebb7a2c110c4c501f4aaff7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 30 Jun 2020 09:47:04 +0200 Subject: [PATCH 08/38] Update semanticDB expect files We should check that semanticDB does the right thing for extension methods --- .../dotty/tools/dotc/typer/Implicits.scala | 2 + tests/semanticdb/expect/Enums.expect.scala | 12 +- tests/semanticdb/expect/Givens.expect.scala | 18 +- tests/semanticdb/expect/toplevel.expect.scala | 6 +- tests/semanticdb/metac.expect | 192 +++++++++--------- 5 files changed, 116 insertions(+), 114 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 31bd3cc6ad7b..7a28f32a47bf 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1084,6 +1084,7 @@ trait Implicits { self: Typer => * a diverging search */ def tryImplicit(cand: Candidate, contextual: Boolean): SearchResult = + //println(i"try $cand for $pt") if (ctx.searchHistory.checkDivergence(cand, pt)) SearchFailure(new DivergingImplicit(cand.ref, pt.widenExpr, argument)) else { @@ -1290,6 +1291,7 @@ trait Implicits { self: Typer => val eligible = if (contextual) ctx.implicits.eligible(wildProto) else implicitScope(wildProto).eligible + //println(i"eligible for $wildProto = $eligible%, %") searchImplicits(eligible, contextual) match { case result: SearchSuccess => result diff --git a/tests/semanticdb/expect/Enums.expect.scala b/tests/semanticdb/expect/Enums.expect.scala index 3baff23aa49c..be154809df0a 100644 --- a/tests/semanticdb/expect/Enums.expect.scala +++ b/tests/semanticdb/expect/Enums.expect.scala @@ -12,10 +12,10 @@ object Enums/*<-_empty_::Enums.*/: case Hearts/*<-_empty_::Enums.Suits.Hearts.*/, Spades/*<-_empty_::Enums.Suits.Spades.*/, Clubs/*<-_empty_::Enums.Suits.Clubs.*/, Diamonds/*<-_empty_::Enums.Suits.Diamonds.*/ object Suits/*<-_empty_::Enums.Suits.*/: - def (suit/*<-_empty_::Enums.Suits.isRed().*//*<-_empty_::Enums.Suits.isRed().(suit)*/: Suits/*->_empty_::Enums.Suits#*/).isRed: Boolean/*->scala::Boolean#*/ = - suit/*->_empty_::Enums.Suits.isRed().(suit)*/ ==/*->scala::Any#`==`().*/ Hearts/*->_empty_::Enums.Suits.Hearts.*/ ||/*->scala::Boolean#`||`().*/ suit/*->_empty_::Enums.Suits.isRed().(suit)*/ ==/*->scala::Any#`==`().*/ Diamonds/*->_empty_::Enums.Suits.Diamonds.*/ + def (suit: Suits).i/*<-_empty_::Enums.Suits.extension_isRed().*//*<-_empty_::Enums.Suits.extension_isRed().(suit)*//*->_empty_::Enums.Suits#*/sRed: Boolean/*->scala::Boolean#*/ = + suit/*->_empty_::Enums.Suits.extension_isRed().(suit)*/ ==/*->scala::Any#`==`().*/ Hearts/*->_empty_::Enums.Suits.Hearts.*/ ||/*->scala::Boolean#`||`().*/ suit/*->_empty_::Enums.Suits.extension_isRed().(suit)*/ ==/*->scala::Any#`==`().*/ Diamonds/*->_empty_::Enums.Suits.Diamonds.*/ - def (suit: /*<-_empty_::Enums.Suits.isBlack().*//*<-_empty_::Enums.Suits.isBlack().(suit)*/Suits/*->_empty_::Enums.Suits#*/).isBlack: Boolean/*->scala::Boolean#*/ = suit/*->_empty_::Enums.Suits.isBlack().(suit)*/ match + def (suit: Suits).isB/*<-_empty_::Enums.Suits.extension_isBlack().*//*<-_empty_::Enums.Suits.extension_isBlack().(suit)*//*->_empty_::Enums.Suits#*/lack: Boolean/*->scala::Boolean#*/ = suit/*->_empty_::Enums.Suits.extension_isBlack().(suit)*/ match case Spades/*->_empty_::Enums.Suits.Spades.*/ | Clubs/*->_empty_::Enums.Suits.Clubs.*/ => true case _ => false @@ -49,10 +49,10 @@ object Enums/*<-_empty_::Enums.*/: object <:_empty_::Enums.`<:<`.given_T().[T]*/ <:_empty_::Enums.`<:<`#*/ T/*->_empty_::Enums.`<:<`.given_T().[T]*/) = Refl/*->_empty_::Enums.`<:<`.Refl.*//*->_empty_::Enums.`<:<`.Refl.apply().*/() - def [A, B]/*<-_empty_::Enums.unwrap().*//*<-_empty_::Enums.unwrap().[A]*//*<-_empty_::Enums.unwrap().[B]*/(opt/*<-_empty_::Enums.unwrap().(opt)*/: Option/*->scala::Option#*/[A/*->_empty_::Enums.unwrap().[A]*/]) unwrap(using ev/*<-_empty_::Enums.unwrap().(ev)*/: A/*->_empty_::Enums.unwrap().[A]*/ <:_empty_::Enums.`<:<`#*/ Option/*->scala::Option#*/[B/*->_empty_::Enums.unwrap().[B]*/]): Option/*->scala::Option#*/[B/*->_empty_::Enums.unwrap().[B]*/] = ev/*->_empty_::Enums.unwrap().(ev)*/ match - case Refl/*->_empty_::Enums.`<:<`.Refl.*//*->_empty_::Enums.`<:<`.Refl.unapply().*/() => opt/*->_empty_::Enums.unwrap().(opt)*/.flatMap/*->scala::Option#flatMap().*/(identity/*->scala::Predef.identity().*//*->local0*/[Option/*->scala::Option#*/[B/*->_empty_::Enums.unwrap().[B]*/]]) + def [A, B](opt: Opti/*<-_empty_::Enums.extension_unwrap().*//*<-_empty_::Enums.extension_unwrap().[A]*//*<-_empty_::Enums.extension_unwrap().[B]*//*<-_empty_::Enums.extension_unwrap().(opt)*/on/*->scala::Option#*/[A/*->_empty_::Enums.extension_unwrap().[A]*/]) unwrap(using ev/*<-_empty_::Enums.extension_unwrap().(ev)*/: A/*->_empty_::Enums.extension_unwrap().[A]*/ <:_empty_::Enums.`<:<`#*/ Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/]): Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/] = ev/*->_empty_::Enums.extension_unwrap().(ev)*/ match + case Refl/*->_empty_::Enums.`<:<`.Refl.*//*->_empty_::Enums.`<:<`.Refl.unapply().*/() => opt/*->_empty_::Enums.extension_unwrap().(opt)*/.flatMap/*->scala::Option#flatMap().*/(identity/*->scala::Predef.identity().*//*->local0*/[Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/]]) - val some1/*<-_empty_::Enums.some1.*/ = /*->_empty_::Enums.unwrap().*/Some/*->scala::Some.*//*->scala::Some.apply().*/(Some/*->scala::Some.*//*->scala::Some.apply().*/(1))/*->_empty_::Enums.`<:<`.given_T().*/.unwrap + val some1/*<-_empty_::Enums.some1.*/ = /*->_empty_::Enums.extension_unwrap().*/Some/*->scala::Some.*//*->scala::Some.apply().*/(Some/*->scala::Some.*//*->scala::Some.apply().*/(1))/*->_empty_::Enums.`<:<`.given_T().*/.unwrap enum Planet/*<-_empty_::Enums.Planet#*/(mass/*<-_empty_::Enums.Planet#mass.*/: Double/*->scala::Double#*/, radius/*<-_empty_::Enums.Planet#radius.*/: Double/*->scala::Double#*/) extends java.lang.Enum/*->java::lang::Enum#*/[Planet/*->_empty_::Enums.Planet#*/]/*->java::lang::Enum#``().*/: private final val G/*<-_empty_::Enums.Planet#G.*/ = 6.67300E-11 diff --git a/tests/semanticdb/expect/Givens.expect.scala b/tests/semanticdb/expect/Givens.expect.scala index 7c3c543c1bc1..2bc1a4e688f0 100644 --- a/tests/semanticdb/expect/Givens.expect.scala +++ b/tests/semanticdb/expect/Givens.expect.scala @@ -4,24 +4,24 @@ package b object Givens/*<-a::b::Givens.*/: extension on [A](any: A): - /*<-a::b::Givens.extension_sayHello_A.*//*<-a::b::Givens.extension_sayHello_A.sayHello().[A]*//*<-a::b::Givens.extension_sayHello_A.sayHello().(any)*//*->a::b::Givens.extension_sayHello_A.sayHello().[A]*/def sayHello/*<-a::b::Givens.extension_sayHello_A.sayHello().*/ = s"/*->scala::StringContext.apply().*/Hello, I am $any/*->a::b::Givens.extension_sayHello_A.sayHello().(any)*/"/*->scala::StringContext#s().*/ + def sayHel/*<-a::b::Givens.extension_extension_sayHello_A.*//*<-a::b::Givens.extension_extension_sayHello_A.extension_sayHello().[A]*//*<-a::b::Givens.extension_extension_sayHello_A.extension_sayHello().(any)*//*->a::b::Givens.extension_extension_sayHello_A.extension_sayHello().[A]*/lo = s"Hello/*<-a::b::Givens.extension_extension_sayHello_A.extension_sayHello().*//*->scala::StringContext.apply().*/, I am $any/*->a::b::Givens.extension_extension_sayHello_A.extension_sayHello().(any)*/"/*->scala::StringContext#s().*/ extension on [B](any: B): - de/*<-a::b::Givens.extension_sayGoodbye_B.*//*<-a::b::Givens.extension_sayGoodbye_B.sayGoodbye().[B]*//*<-a::b::Givens.extension_sayGoodbye_B.saySoLong().[B]*//*<-a::b::Givens.extension_sayGoodbye_B.sayGoodbye().(any)*//*<-a::b::Givens.extension_sayGoodbye_B.saySoLong().(any)*//*->a::b::Givens.extension_sayGoodbye_B.sayGoodbye().[B]*//*->a::b::Givens.extension_sayGoodbye_B.saySoLong().[B]*/f sayGoodbye/*<-a::b::Givens.extension_sayGoodbye_B.sayGoodbye().*/ = s"/*->scala::StringContext.apply().*/Goodbye, from $any/*->a::b::Givens.extension_sayGoodbye_B.sayGoodbye().(any)*/"/*->scala::StringContext#s().*/ - def saySoLong/*<-a::b::Givens.extension_sayGoodbye_B.saySoLong().*/ = s"/*->scala::StringContext.apply().*/So Long, from $any/*->a::b::Givens.extension_sayGoodbye_B.saySoLong().(any)*/"/*->scala::StringContext#s().*/ + def sayGoodb/*<-a::b::Givens.extension_extension_sayGoodbye_B.*//*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().[B]*//*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().[B]*//*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().(any)*//*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().(any)*//*->a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().[B]*//*->a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().[B]*/ye = s"Goodb/*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().*//*->scala::StringContext.apply().*/ye, from $any/*->a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().(any)*/"/*->scala::StringContext#s().*/ + def saySoLong = s"So Lo/*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().*//*->scala::StringContext.apply().*/ng, from $any/*->a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().(any)*/"/*->scala::StringContext#s().*/ - val hello1/*<-a::b::Givens.hello1.*/ = /*->a::b::Givens.extension_sayHello_A.sayHello().*/1.sayHello - val goodbye1/*<-a::b::Givens.goodbye1.*/ = /*->a::b::Givens.extension_sayGoodbye_B.sayGoodbye().*/1.sayGoodbye - val soLong1/*<-a::b::Givens.soLong1.*/ = /*->a::b::Givens.extension_sayGoodbye_B.saySoLong().*/1.saySoLong + val hello1/*<-a::b::Givens.hello1.*/ = /*->a::b::Givens.extension_extension_sayHello_A.extension_sayHello().*/1.sayHello + val goodbye1/*<-a::b::Givens.goodbye1.*/ = /*->a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().*/1.sayGoodbye + val soLong1/*<-a::b::Givens.soLong1.*/ = /*->a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().*/1.saySoLong trait Monoid/*<-a::b::Givens.Monoid#*/[A/*<-a::b::Givens.Monoid#[A]*/]: def empty/*<-a::b::Givens.Monoid#empty().*/: A/*->a::b::Givens.Monoid#[A]*/ - def (x: A)./*<-a::b::Givens.Monoid#combine().*//*<-a::b::Givens.Monoid#combine().(x)*//*->a::b::Givens.Monoid#[A]*/combine(y/*<-a::b::Givens.Monoid#combine().(y)*/: A/*->a::b::Givens.Monoid#[A]*/): A/*->a::b::Givens.Monoid#[A]*/ + def (x: A).combine(y:/*<-a::b::Givens.Monoid#extension_combine().*//*<-a::b::Givens.Monoid#extension_combine().(x)*//*->a::b::Givens.Monoid#[A]*//*<-a::b::Givens.Monoid#extension_combine().(y)*/ A/*->a::b::Givens.Monoid#[A]*/): A/*->a::b::Givens.Monoid#[A]*/ given Monoid[String]: /*<-a::b::Givens.given_Monoid_String.*//*->a::b::Givens.Monoid#*//*->scala::Predef.String#*/ def empty/*<-a::b::Givens.given_Monoid_String.empty().*/ = "" - def (x: Str/*<-a::b::Givens.given_Monoid_String.combine().*//*<-a::b::Givens.given_Monoid_String.combine().(x)*/ing/*->scala::Predef.String#*/).combine(y/*<-a::b::Givens.given_Monoid_String.combine().(y)*/: String/*->scala::Predef.String#*/) = x/*->a::b::Givens.given_Monoid_String.combine().(x)*/ +/*->java::lang::String#`+`().*/ y/*->a::b::Givens.given_Monoid_String.combine().(y)*/ + def (x: String).combi/*<-a::b::Givens.given_Monoid_String.extension_combine().*//*<-a::b::Givens.given_Monoid_String.extension_combine().(x)*//*->scala::Predef.String#*/ne(y/*<-a::b::Givens.given_Monoid_String.extension_combine().(y)*/: String/*->scala::Predef.String#*/) = x/*->a::b::Givens.given_Monoid_String.extension_combine().(x)*/ +/*->java::lang::String#`+`().*/ y/*->a::b::Givens.given_Monoid_String.extension_combine().(y)*/ inline given int2String/*<-a::b::Givens.int2String().*/ as Conversion/*->scala::Conversion#*/[Int/*->scala::Int#*/, String/*->scala::Predef.String#*/] = _.toString/*->scala::Any#toString().*/ - def foo/*<-a::b::Givens.foo().*/[A/*<-a::b::Givens.foo().[A]*/](using A/*<-a::b::Givens.foo().(A)*/: Monoid/*->a::b::Givens.Monoid#*/[A/*->a::b::Givens.foo().[A]*/]): A/*->a::b::Givens.foo().[A]*/ = A/*->a::b::Givens.foo().(A)*/.combine/*->a::b::Givens.Monoid#combine().*/(A/*->a::b::Givens.foo().(A)*/.empty/*->a::b::Givens.Monoid#empty().*/)(A/*->a::b::Givens.foo().(A)*/.empty/*->a::b::Givens.Monoid#empty().*/) + def foo/*<-a::b::Givens.foo().*/[A/*<-a::b::Givens.foo().[A]*/](using A/*<-a::b::Givens.foo().(A)*/: Monoid/*->a::b::Givens.Monoid#*/[A/*->a::b::Givens.foo().[A]*/]): A/*->a::b::Givens.foo().[A]*/ = A/*->a::b::Givens.foo().(A)*/.extension_combine/*->a::b::Givens.Monoid#extension_combine().*/(A/*->a::b::Givens.foo().(A)*/.empty/*->a::b::Givens.Monoid#empty().*/)(A/*->a::b::Givens.foo().(A)*/.empty/*->a::b::Givens.Monoid#empty().*/) diff --git a/tests/semanticdb/expect/toplevel.expect.scala b/tests/semanticdb/expect/toplevel.expect.scala index b61e882fef36..1654caa5204a 100644 --- a/tests/semanticdb/expect/toplevel.expect.scala +++ b/tests/semanticdb/expect/toplevel.expect.scala @@ -1,5 +1,5 @@ inline val a = "/*<-_empty_::toplevel$package.*//*<-_empty_::toplevel$package.a.*/" -def (x: Int/*<-_empty_::toplevel$package.combine().*//*<-_empty_::toplevel$package.combine().(x)*//*->scala::Int#*/) combine (y/*<-_empty_::toplevel$package.combine().(y)*/: Int/*->scala::Int#*/) = x/*->_empty_::toplevel$package.combine().(x)*/ +/*->scala::Int#`+`(+4).*/ y/*->_empty_::toplevel$package.combine().(y)*/ -def combine/*<-_empty_::toplevel$package.combine(+1).*/(x/*<-_empty_::toplevel$package.combine(+1).(x)*/: Int/*->scala::Int#*/, y/*<-_empty_::toplevel$package.combine(+1).(y)*/: Int/*->scala::Int#*/, z/*<-_empty_::toplevel$package.combine(+1).(z)*/: Int/*->scala::Int#*/) = x/*->_empty_::toplevel$package.combine(+1).(x)*/ +/*->scala::Int#`+`(+4).*/ y/*->_empty_::toplevel$package.combine(+1).(y)*/ +/*->scala::Int#`+`(+4).*/ z/*->_empty_::toplevel$package.combine(+1).(z)*/ -def combine/*<-_empty_::toplevel$package.combine(+2).*/ = 0 +def (x: Int) combine /*<-_empty_::toplevel$package.extension_combine().*//*<-_empty_::toplevel$package.extension_combine().(x)*//*->scala::Int#*/(y/*<-_empty_::toplevel$package.extension_combine().(y)*/: Int/*->scala::Int#*/) = x/*->_empty_::toplevel$package.extension_combine().(x)*/ +/*->scala::Int#`+`(+4).*/ y/*->_empty_::toplevel$package.extension_combine().(y)*/ +def combine/*<-_empty_::toplevel$package.combine().*/(x/*<-_empty_::toplevel$package.combine().(x)*/: Int/*->scala::Int#*/, y/*<-_empty_::toplevel$package.combine().(y)*/: Int/*->scala::Int#*/, z/*<-_empty_::toplevel$package.combine().(z)*/: Int/*->scala::Int#*/) = x/*->_empty_::toplevel$package.combine().(x)*/ +/*->scala::Int#`+`(+4).*/ y/*->_empty_::toplevel$package.combine().(y)*/ +/*->scala::Int#`+`(+4).*/ z/*->_empty_::toplevel$package.combine().(z)*/ +def combine/*<-_empty_::toplevel$package.combine(+1).*/ = 0 def foo/*<-_empty_::toplevel$package.foo().*/ = "foo" diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 5bb0ed61903c..6ee471a31922 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -743,10 +743,10 @@ _empty_/Enums.Suits.Diamonds. => case val static enum method Diamonds _empty_/Enums.Suits.Hearts. => case val static enum method Hearts _empty_/Enums.Suits.Spades. => case val static enum method Spades _empty_/Enums.Suits.derived$Eql(). => implicit method derived$Eql -_empty_/Enums.Suits.isBlack(). => method isBlack -_empty_/Enums.Suits.isBlack().(suit) => param suit -_empty_/Enums.Suits.isRed(). => method isRed -_empty_/Enums.Suits.isRed().(suit) => param suit +_empty_/Enums.Suits.extension_isBlack(). => method extension_isBlack +_empty_/Enums.Suits.extension_isBlack().(suit) => param suit +_empty_/Enums.Suits.extension_isRed(). => method extension_isRed +_empty_/Enums.Suits.extension_isRed().(suit) => param suit _empty_/Enums.Suits.valueOf(). => method valueOf _empty_/Enums.Suits.valueOf().($name) => param $name _empty_/Enums.Suits.values(). => method values @@ -792,12 +792,12 @@ _empty_/Enums.`<:<`.Refl.unapply().(x$1) => param x$1 _empty_/Enums.`<:<`.Refl.unapply().[C] => typeparam C _empty_/Enums.`<:<`.given_T(). => final implicit method given_T _empty_/Enums.`<:<`.given_T().[T] => typeparam T +_empty_/Enums.extension_unwrap(). => method extension_unwrap +_empty_/Enums.extension_unwrap().(ev) => implicit param ev +_empty_/Enums.extension_unwrap().(opt) => param opt +_empty_/Enums.extension_unwrap().[A] => typeparam A +_empty_/Enums.extension_unwrap().[B] => typeparam B _empty_/Enums.some1. => val method some1 -_empty_/Enums.unwrap(). => method unwrap -_empty_/Enums.unwrap().(ev) => implicit param ev -_empty_/Enums.unwrap().(opt) => param opt -_empty_/Enums.unwrap().[A] => typeparam A -_empty_/Enums.unwrap().[B] => typeparam B local0 => param x Occurrences: @@ -824,22 +824,22 @@ Occurrences: [11:25..11:30): Clubs <- _empty_/Enums.Suits.Clubs. [11:32..11:40): Diamonds <- _empty_/Enums.Suits.Diamonds. [13:9..13:14): Suits <- _empty_/Enums.Suits. -[14:8..14:13): (suit <- _empty_/Enums.Suits.isRed(). -[14:9..14:13): suit <- _empty_/Enums.Suits.isRed().(suit) +[14:8..14:23): (suit: Suits).i <- _empty_/Enums.Suits.extension_isRed(). +[14:9..14:13): suit <- _empty_/Enums.Suits.extension_isRed().(suit) [14:15..14:20): Suits -> _empty_/Enums.Suits# [14:29..14:36): Boolean -> scala/Boolean# -[15:6..15:10): suit -> _empty_/Enums.Suits.isRed().(suit) +[15:6..15:10): suit -> _empty_/Enums.Suits.extension_isRed().(suit) [15:11..15:13): == -> scala/Any#`==`(). [15:14..15:20): Hearts -> _empty_/Enums.Suits.Hearts. [15:21..15:23): || -> scala/Boolean#`||`(). -[15:24..15:28): suit -> _empty_/Enums.Suits.isRed().(suit) +[15:24..15:28): suit -> _empty_/Enums.Suits.extension_isRed().(suit) [15:29..15:31): == -> scala/Any#`==`(). [15:32..15:40): Diamonds -> _empty_/Enums.Suits.Diamonds. -[17:8..17:15): (suit: <- _empty_/Enums.Suits.isBlack(). -[17:9..17:13): suit <- _empty_/Enums.Suits.isBlack().(suit) +[17:8..17:25): (suit: Suits).isB <- _empty_/Enums.Suits.extension_isBlack(). +[17:9..17:13): suit <- _empty_/Enums.Suits.extension_isBlack().(suit) [17:15..17:20): Suits -> _empty_/Enums.Suits# [17:31..17:38): Boolean -> scala/Boolean# -[17:41..17:45): suit -> _empty_/Enums.Suits.isBlack().(suit) +[17:41..17:45): suit -> _empty_/Enums.Suits.extension_isBlack().(suit) [18:11..18:17): Spades -> _empty_/Enums.Suits.Spades. [18:20..18:25): Clubs -> _empty_/Enums.Suits.Clubs. [21:7..21:15): WeekDays <- _empty_/Enums.WeekDays# @@ -917,30 +917,30 @@ Occurrences: [49:24..49:25): T -> _empty_/Enums.`<:<`.given_T().[T] [49:29..49:33): Refl -> _empty_/Enums.`<:<`.Refl. [49:33..49:33): -> _empty_/Enums.`<:<`.Refl.apply(). -[51:6..51:12): [A, B] <- _empty_/Enums.unwrap(). -[51:7..51:8): A <- _empty_/Enums.unwrap().[A] -[51:10..51:11): B <- _empty_/Enums.unwrap().[B] -[51:13..51:16): opt <- _empty_/Enums.unwrap().(opt) +[51:6..51:22): [A, B](opt: Opti <- _empty_/Enums.extension_unwrap(). +[51:7..51:8): A <- _empty_/Enums.extension_unwrap().[A] +[51:10..51:11): B <- _empty_/Enums.extension_unwrap().[B] +[51:13..51:16): opt <- _empty_/Enums.extension_unwrap().(opt) [51:18..51:24): Option -> scala/Option# -[51:25..51:26): A -> _empty_/Enums.unwrap().[A] -[51:42..51:44): ev <- _empty_/Enums.unwrap().(ev) -[51:46..51:47): A -> _empty_/Enums.unwrap().[A] +[51:25..51:26): A -> _empty_/Enums.extension_unwrap().[A] +[51:42..51:44): ev <- _empty_/Enums.extension_unwrap().(ev) +[51:46..51:47): A -> _empty_/Enums.extension_unwrap().[A] [51:48..51:51): <:< -> _empty_/Enums.`<:<`# [51:52..51:58): Option -> scala/Option# -[51:59..51:60): B -> _empty_/Enums.unwrap().[B] +[51:59..51:60): B -> _empty_/Enums.extension_unwrap().[B] [51:64..51:70): Option -> scala/Option# -[51:71..51:72): B -> _empty_/Enums.unwrap().[B] -[51:76..51:78): ev -> _empty_/Enums.unwrap().(ev) +[51:71..51:72): B -> _empty_/Enums.extension_unwrap().[B] +[51:76..51:78): ev -> _empty_/Enums.extension_unwrap().(ev) [52:9..52:13): Refl -> _empty_/Enums.`<:<`.Refl. [52:13..52:13): -> _empty_/Enums.`<:<`.Refl.unapply(). -[52:19..52:22): opt -> _empty_/Enums.unwrap().(opt) +[52:19..52:22): opt -> _empty_/Enums.extension_unwrap().(opt) [52:23..52:30): flatMap -> scala/Option#flatMap(). [52:31..52:39): identity -> scala/Predef.identity(). [52:31..52:31): -> local0 [52:40..52:46): Option -> scala/Option# -[52:47..52:48): B -> _empty_/Enums.unwrap().[B] +[52:47..52:48): B -> _empty_/Enums.extension_unwrap().[B] [54:6..54:11): some1 <- _empty_/Enums.some1. -[54:14..54:14): -> _empty_/Enums.unwrap(). +[54:14..54:14): -> _empty_/Enums.extension_unwrap(). [54:14..54:18): Some -> scala/Some. [54:18..54:18): -> scala/Some.apply(). [54:19..54:23): Some -> scala/Some. @@ -1212,29 +1212,29 @@ a/b/Givens. => final object Givens a/b/Givens.Monoid# => trait Monoid a/b/Givens.Monoid#[A] => typeparam A a/b/Givens.Monoid#``(). => primary ctor -a/b/Givens.Monoid#combine(). => abstract method combine -a/b/Givens.Monoid#combine().(x) => param x -a/b/Givens.Monoid#combine().(y) => param y a/b/Givens.Monoid#empty(). => abstract method empty -a/b/Givens.extension_sayGoodbye_B. => final implicit object extension_sayGoodbye_B -a/b/Givens.extension_sayGoodbye_B.sayGoodbye(). => method sayGoodbye -a/b/Givens.extension_sayGoodbye_B.sayGoodbye().(any) => param any -a/b/Givens.extension_sayGoodbye_B.sayGoodbye().[B] => typeparam B -a/b/Givens.extension_sayGoodbye_B.saySoLong(). => method saySoLong -a/b/Givens.extension_sayGoodbye_B.saySoLong().(any) => param any -a/b/Givens.extension_sayGoodbye_B.saySoLong().[B] => typeparam B -a/b/Givens.extension_sayHello_A. => final implicit object extension_sayHello_A -a/b/Givens.extension_sayHello_A.sayHello(). => method sayHello -a/b/Givens.extension_sayHello_A.sayHello().(any) => param any -a/b/Givens.extension_sayHello_A.sayHello().[A] => typeparam A +a/b/Givens.Monoid#extension_combine(). => abstract method extension_combine +a/b/Givens.Monoid#extension_combine().(x) => param x +a/b/Givens.Monoid#extension_combine().(y) => param y +a/b/Givens.extension_extension_sayGoodbye_B. => final implicit object extension_extension_sayGoodbye_B +a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye(). => method extension_sayGoodbye +a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().(any) => param any +a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().[B] => typeparam B +a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong(). => method extension_saySoLong +a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong().(any) => param any +a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong().[B] => typeparam B +a/b/Givens.extension_extension_sayHello_A. => final implicit object extension_extension_sayHello_A +a/b/Givens.extension_extension_sayHello_A.extension_sayHello(). => method extension_sayHello +a/b/Givens.extension_extension_sayHello_A.extension_sayHello().(any) => param any +a/b/Givens.extension_extension_sayHello_A.extension_sayHello().[A] => typeparam A a/b/Givens.foo(). => method foo a/b/Givens.foo().(A) => implicit param A a/b/Givens.foo().[A] => typeparam A a/b/Givens.given_Monoid_String. => final implicit object given_Monoid_String -a/b/Givens.given_Monoid_String.combine(). => method combine -a/b/Givens.given_Monoid_String.combine().(x) => param x -a/b/Givens.given_Monoid_String.combine().(y) => param y a/b/Givens.given_Monoid_String.empty(). => method empty +a/b/Givens.given_Monoid_String.extension_combine(). => method extension_combine +a/b/Givens.given_Monoid_String.extension_combine().(x) => param x +a/b/Givens.given_Monoid_String.extension_combine().(y) => param y a/b/Givens.goodbye1. => val method goodbye1 a/b/Givens.hello1. => val method hello1 a/b/Givens.int2String(). => final implicit macro int2String @@ -1244,58 +1244,58 @@ Occurrences: [0:8..0:9): a <- a/ [1:8..1:9): b <- a/b/ [3:7..3:13): Givens <- a/b/Givens. -[5:12..6:4): <- a/b/Givens.extension_sayHello_A. -[5:16..5:17): A <- a/b/Givens.extension_sayHello_A.sayHello().[A] -[5:19..5:22): any <- a/b/Givens.extension_sayHello_A.sayHello().(any) -[5:24..5:25): A -> a/b/Givens.extension_sayHello_A.sayHello().[A] -[6:8..6:16): sayHello <- a/b/Givens.extension_sayHello_A.sayHello(). +[5:12..6:14): <- a/b/Givens.extension_extension_sayHello_A. +[5:16..5:17): A <- a/b/Givens.extension_extension_sayHello_A.extension_sayHello().[A] +[5:19..5:22): any <- a/b/Givens.extension_extension_sayHello_A.extension_sayHello().(any) +[5:24..5:25): A -> a/b/Givens.extension_extension_sayHello_A.extension_sayHello().[A] +[6:8..6:26): sayHello = s"Hello <- a/b/Givens.extension_extension_sayHello_A.extension_sayHello(). [6:21..6:21): -> scala/StringContext.apply(). -[6:34..6:37): any -> a/b/Givens.extension_sayHello_A.sayHello().(any) +[6:34..6:37): any -> a/b/Givens.extension_extension_sayHello_A.extension_sayHello().(any) [6:37..6:38): " -> scala/StringContext#s(). -[8:12..9:6): <- a/b/Givens.extension_sayGoodbye_B. -[8:16..8:17): B <- a/b/Givens.extension_sayGoodbye_B.sayGoodbye().[B] -[8:16..8:17): B <- a/b/Givens.extension_sayGoodbye_B.saySoLong().[B] -[8:19..8:22): any <- a/b/Givens.extension_sayGoodbye_B.sayGoodbye().(any) -[8:19..8:22): any <- a/b/Givens.extension_sayGoodbye_B.saySoLong().(any) -[8:24..8:25): B -> a/b/Givens.extension_sayGoodbye_B.sayGoodbye().[B] -[8:24..8:25): B -> a/b/Givens.extension_sayGoodbye_B.saySoLong().[B] -[9:8..9:18): sayGoodbye <- a/b/Givens.extension_sayGoodbye_B.sayGoodbye(). +[8:12..9:16): <- a/b/Givens.extension_extension_sayGoodbye_B. +[8:16..8:17): B <- a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().[B] +[8:16..8:17): B <- a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong().[B] +[8:19..8:22): any <- a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().(any) +[8:19..8:22): any <- a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong().(any) +[8:24..8:25): B -> a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().[B] +[8:24..8:25): B -> a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong().[B] +[9:8..9:28): sayGoodbye = s"Goodb <- a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye(). [9:23..9:23): -> scala/StringContext.apply(). -[9:38..9:41): any -> a/b/Givens.extension_sayGoodbye_B.sayGoodbye().(any) +[9:38..9:41): any -> a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().(any) [9:41..9:42): " -> scala/StringContext#s(). -[10:8..10:17): saySoLong <- a/b/Givens.extension_sayGoodbye_B.saySoLong(). +[10:8..10:27): saySoLong = s"So Lo <- a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong(). [10:22..10:22): -> scala/StringContext.apply(). -[10:37..10:40): any -> a/b/Givens.extension_sayGoodbye_B.saySoLong().(any) +[10:37..10:40): any -> a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong().(any) [10:40..10:41): " -> scala/StringContext#s(). [12:6..12:12): hello1 <- a/b/Givens.hello1. -[12:15..12:15): -> a/b/Givens.extension_sayHello_A.sayHello(). +[12:15..12:15): -> a/b/Givens.extension_extension_sayHello_A.extension_sayHello(). [13:6..13:14): goodbye1 <- a/b/Givens.goodbye1. -[13:17..13:17): -> a/b/Givens.extension_sayGoodbye_B.sayGoodbye(). +[13:17..13:17): -> a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye(). [14:6..14:13): soLong1 <- a/b/Givens.soLong1. -[14:16..14:16): -> a/b/Givens.extension_sayGoodbye_B.saySoLong(). +[14:16..14:16): -> a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong(). [16:8..16:14): Monoid <- a/b/Givens.Monoid# [16:14..16:17): <- a/b/Givens.Monoid#``(). [16:15..16:16): A <- a/b/Givens.Monoid#[A] [17:8..17:13): empty <- a/b/Givens.Monoid#empty(). [17:15..17:16): A -> a/b/Givens.Monoid#[A] -[18:8..18:15): (x: A). <- a/b/Givens.Monoid#combine(). -[18:9..18:10): x <- a/b/Givens.Monoid#combine().(x) +[18:8..18:25): (x: A).combine(y: <- a/b/Givens.Monoid#extension_combine(). +[18:9..18:10): x <- a/b/Givens.Monoid#extension_combine().(x) [18:12..18:13): A -> a/b/Givens.Monoid#[A] -[18:23..18:24): y <- a/b/Givens.Monoid#combine().(y) +[18:23..18:24): y <- a/b/Givens.Monoid#extension_combine().(y) [18:26..18:27): A -> a/b/Givens.Monoid#[A] [18:30..18:31): A -> a/b/Givens.Monoid#[A] [20:8..21:3): <- a/b/Givens.given_Monoid_String. [20:8..20:14): Monoid -> a/b/Givens.Monoid# [20:15..20:21): String -> scala/Predef.String# [21:8..21:13): empty <- a/b/Givens.given_Monoid_String.empty(). -[22:8..22:15): (x: Str <- a/b/Givens.given_Monoid_String.combine(). -[22:9..22:10): x <- a/b/Givens.given_Monoid_String.combine().(x) +[22:8..22:25): (x: String).combi <- a/b/Givens.given_Monoid_String.extension_combine(). +[22:9..22:10): x <- a/b/Givens.given_Monoid_String.extension_combine().(x) [22:12..22:18): String -> scala/Predef.String# -[22:28..22:29): y <- a/b/Givens.given_Monoid_String.combine().(y) +[22:28..22:29): y <- a/b/Givens.given_Monoid_String.extension_combine().(y) [22:31..22:37): String -> scala/Predef.String# -[22:41..22:42): x -> a/b/Givens.given_Monoid_String.combine().(x) +[22:41..22:42): x -> a/b/Givens.given_Monoid_String.extension_combine().(x) [22:43..22:44): + -> java/lang/String#`+`(). -[22:45..22:46): y -> a/b/Givens.given_Monoid_String.combine().(y) +[22:45..22:46): y -> a/b/Givens.given_Monoid_String.extension_combine().(y) [24:15..24:25): int2String <- a/b/Givens.int2String(). [24:29..24:39): Conversion -> scala/Conversion# [24:40..24:43): Int -> scala/Int# @@ -1308,11 +1308,11 @@ Occurrences: [26:29..26:30): A -> a/b/Givens.foo().[A] [26:34..26:35): A -> a/b/Givens.foo().[A] [26:38..26:39): A -> a/b/Givens.foo().(A) -[26:40..26:47): combine -> a/b/Givens.Monoid#combine(). -[26:48..26:49): A -> a/b/Givens.foo().(A) -[26:50..26:55): empty -> a/b/Givens.Monoid#empty(). -[26:57..26:58): A -> a/b/Givens.foo().(A) -[26:59..26:64): empty -> a/b/Givens.Monoid#empty(). +[26:40..26:57): extension_combine -> a/b/Givens.Monoid#extension_combine(). +[26:58..26:59): A -> a/b/Givens.foo().(A) +[26:60..26:65): empty -> a/b/Givens.Monoid#empty(). +[26:67..26:68): A -> a/b/Givens.foo().(A) +[26:69..26:74): empty -> a/b/Givens.Monoid#empty(). expect/ImplicitConversion.scala ------------------------------- @@ -3865,36 +3865,36 @@ _empty_/toplevel$package.a. => val method a _empty_/toplevel$package.combine(). => method combine _empty_/toplevel$package.combine().(x) => param x _empty_/toplevel$package.combine().(y) => param y +_empty_/toplevel$package.combine().(z) => param z _empty_/toplevel$package.combine(+1). => method combine -_empty_/toplevel$package.combine(+1).(x) => param x -_empty_/toplevel$package.combine(+1).(y) => param y -_empty_/toplevel$package.combine(+1).(z) => param z -_empty_/toplevel$package.combine(+2). => method combine +_empty_/toplevel$package.extension_combine(). => method extension_combine +_empty_/toplevel$package.extension_combine().(x) => param x +_empty_/toplevel$package.extension_combine().(y) => param y _empty_/toplevel$package.foo(). => method foo Occurrences: [0:0..0:16): inline val a = " <- _empty_/toplevel$package. [0:11..0:12): a <- _empty_/toplevel$package.a. -[1:4..1:11): (x: Int <- _empty_/toplevel$package.combine(). -[1:5..1:6): x <- _empty_/toplevel$package.combine().(x) +[1:4..1:21): (x: Int) combine <- _empty_/toplevel$package.extension_combine(). +[1:5..1:6): x <- _empty_/toplevel$package.extension_combine().(x) [1:8..1:11): Int -> scala/Int# -[1:22..1:23): y <- _empty_/toplevel$package.combine().(y) +[1:22..1:23): y <- _empty_/toplevel$package.extension_combine().(y) [1:25..1:28): Int -> scala/Int# -[1:32..1:33): x -> _empty_/toplevel$package.combine().(x) +[1:32..1:33): x -> _empty_/toplevel$package.extension_combine().(x) [1:34..1:35): + -> scala/Int#`+`(+4). -[1:36..1:37): y -> _empty_/toplevel$package.combine().(y) -[2:4..2:11): combine <- _empty_/toplevel$package.combine(+1). -[2:12..2:13): x <- _empty_/toplevel$package.combine(+1).(x) +[1:36..1:37): y -> _empty_/toplevel$package.extension_combine().(y) +[2:4..2:11): combine <- _empty_/toplevel$package.combine(). +[2:12..2:13): x <- _empty_/toplevel$package.combine().(x) [2:15..2:18): Int -> scala/Int# -[2:20..2:21): y <- _empty_/toplevel$package.combine(+1).(y) +[2:20..2:21): y <- _empty_/toplevel$package.combine().(y) [2:23..2:26): Int -> scala/Int# -[2:28..2:29): z <- _empty_/toplevel$package.combine(+1).(z) +[2:28..2:29): z <- _empty_/toplevel$package.combine().(z) [2:31..2:34): Int -> scala/Int# -[2:38..2:39): x -> _empty_/toplevel$package.combine(+1).(x) +[2:38..2:39): x -> _empty_/toplevel$package.combine().(x) [2:40..2:41): + -> scala/Int#`+`(+4). -[2:42..2:43): y -> _empty_/toplevel$package.combine(+1).(y) +[2:42..2:43): y -> _empty_/toplevel$package.combine().(y) [2:44..2:45): + -> scala/Int#`+`(+4). -[2:46..2:47): z -> _empty_/toplevel$package.combine(+1).(z) -[3:4..3:11): combine <- _empty_/toplevel$package.combine(+2). +[2:46..2:47): z -> _empty_/toplevel$package.combine().(z) +[3:4..3:11): combine <- _empty_/toplevel$package.combine(+1). [4:4..4:7): foo <- _empty_/toplevel$package.foo(). From 74865c39ab2161aad78ee78c4e2c28e68fe68e1e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 30 Jun 2020 10:10:28 +0200 Subject: [PATCH 09/38] Disallow normal method names starting with `extension_` --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 4 ++++ .../reference/contextual/extension-methods.md | 15 +++++++++++++++ tests/neg/illegal-extension.scala | 3 +++ tests/pos/reference/extension-methods.scala | 7 +++++++ 4 files changed, 29 insertions(+) create mode 100644 tests/neg/illegal-extension.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index b13066099244..00aaf5672645 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1932,7 +1932,11 @@ class Typer extends Namer } val ddef2 = assignType(cpy.DefDef(ddef)(name, tparams1, vparamss1, tpt1, rhs1), sym) + checkSignatureRepeatedParam(sym) + if name.isExtensionName && !sym.is(Extension) then + ctx.error(em"illegal method name: $name may not start with `extension_`", + ddef.source.atSpan(ddef.nameSpan)) ddef2.setDefTree //todo: make sure dependent method types do not depend on implicits or by-name params } diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index 6b301c029118..5ed2dc461c7e 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -229,7 +229,22 @@ def extension_position(s: String)(ch: Char, n: Int): Int = if n < s.length && s(n) != ch then extension_position(s)(ch, n + 1) else n ``` +### More Details +1. To avoid confusion, names of normal methods are not allowed to start with `extension_`. + +2. A named import such as `import a.m` of an extension method in `a` will make `m` +only available as an extension method. To access it under +`extension_m` that name as to be imported separately. Example: +```scala +object DoubleOps: + extension (x: Double) def ** (exponent: Int): Double = + require(exponent > 0) + if exponent == 0 then 1 else x * (x ** (exponent - 1)) + +import DoubleOps.{**, extension_**} +assert(2.0 ** 3 == extension_**(2.0)(3)) +``` ### Syntax diff --git a/tests/neg/illegal-extension.scala b/tests/neg/illegal-extension.scala new file mode 100644 index 000000000000..5adbc87b8f18 --- /dev/null +++ b/tests/neg/illegal-extension.scala @@ -0,0 +1,3 @@ +trait A { + def extension_n: String = "illegal method" // error: illegal method name: extension_n may not start with `extension_` +} diff --git a/tests/pos/reference/extension-methods.scala b/tests/pos/reference/extension-methods.scala index c317db11a1a3..f68ccb37ac67 100644 --- a/tests/pos/reference/extension-methods.scala +++ b/tests/pos/reference/extension-methods.scala @@ -92,4 +92,11 @@ object ExtMethods: xs.sorted.takeRight(n) } + object DoubleOps: + extension (x: Double) def ** (exponent: Int): Double = + require(exponent > 0) + if exponent == 0 then 1 else x * (x ** (exponent - 1)) + + import DoubleOps.{**, extension_**} + assert(2.0 ** 3 == extension_**(2.0)(3)) end ExtMethods \ No newline at end of file From 4c50dbd308801bbbde626119832d1fa005f70057 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 30 Jun 2020 13:34:50 +0200 Subject: [PATCH 10/38] Adapt to new extension_ syntax in CB --- community-build/community-projects/utest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/utest b/community-build/community-projects/utest index 5823688a9ae1..5e50fa9433f2 160000 --- a/community-build/community-projects/utest +++ b/community-build/community-projects/utest @@ -1 +1 @@ -Subproject commit 5823688a9ae1d3b911e374d7d29c45e73c3be3c2 +Subproject commit 5e50fa9433f295add461e386bedca0b410e19e68 From 5b1a2f794a4f0f55cb08103a5363487f5bdfc125 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 30 Jun 2020 15:39:30 +0200 Subject: [PATCH 11/38] Look for extension methods directly in implicit scope Look for extension methods also directly in the objects that make up an implicit scope. --- .../dotty/tools/dotc/typer/Implicits.scala | 65 ++++++++++++------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 7a28f32a47bf..1dfc4fa68873 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -91,13 +91,18 @@ object Implicits { /** The implicit references */ def refs: List[ImplicitRef] - private var SingletonClass: ClassSymbol = null + /** If comes from an implicit scope of a type, the companion objects making + * up that implicit scope, otherwise the empty set. + */ + def companionRefs: TermRefSet = TermRefSet.empty + + private var mySingletonClass: ClassSymbol = null /** Widen type so that it is neither a singleton type nor a type that inherits from scala.Singleton. */ private def widenSingleton(tp: Type)(using Context): Type = { - if (SingletonClass == null) SingletonClass = defn.SingletonClass + if (mySingletonClass == null) mySingletonClass = defn.SingletonClass val wtp = tp.widenSingleton - if (wtp.derivesFrom(SingletonClass)) defn.AnyType else wtp + if (wtp.derivesFrom(mySingletonClass)) defn.AnyType else wtp } protected def isAccessible(ref: TermRef)(using Context): Boolean @@ -106,6 +111,10 @@ object Implicits { protected def filterMatching(pt: Type)(using Context): List[Candidate] = { record("filterMatching") + val considerExtension = pt match + case ViewProto(_, _: SelectionProto) => true + case _ => false + def candidateKind(ref: TermRef)(using Context): Candidate.Kind = { /*trace(i"candidateKind $ref $pt")*/ def viewCandidateKind(tpw: Type, argType: Type, resType: Type): Candidate.Kind = { @@ -134,8 +143,9 @@ object Implicits { case rtp => viewCandidateKind(wildApprox(rtp), argType, resType) } - case tpw: TermRef => - Candidate.Conversion | Candidate.Extension // can't discard overloaded refs + case tpw: TermRef => // can't discard overloaded refs + Candidate.Conversion + | (if considerExtension then Candidate.Extension else Candidate.None) case tpw => // Only direct instances of Function1 and direct or indirect instances of <:< are eligible as views. // However, Predef.$conforms is not eligible, because it is a no-op. @@ -162,12 +172,11 @@ object Implicits { val isImplicitConversion = tpw.derivesFrom(defn.ConversionClass) // An implementation of <:< counts as a view val isConforms = tpw.derivesFrom(defn.SubTypeClass) - val hasExtensions = hasExtMethod(tpw, resType) val conversionKind = if (isFunctionInS2 || isImplicitConversion || isConforms) Candidate.Conversion else Candidate.None val extensionKind = - if (hasExtensions) Candidate.Extension + if considerExtension && hasExtMethod(tpw, resType) then Candidate.Extension else Candidate.None conversionKind | extensionKind } @@ -231,13 +240,20 @@ object Implicits { else { val nestedCtx = ctx.fresh.addMode(Mode.TypevarsMissContext) - def matchingCandidate(ref: ImplicitRef): Option[Candidate] = - nestedCtx.test(candidateKind(ref.underlyingRef)) match { - case Candidate.None => None - case ckind => Some(new Candidate(ref, ckind, level)) - } - - refs.flatMap(matchingCandidate) + def matchingCandidate(ref: ImplicitRef, extensionOnly: Boolean): Option[Candidate] = + var ckind = nestedCtx.test(candidateKind(ref.underlyingRef)) + if extensionOnly then ckind &= Candidate.Extension + if ckind == Candidate.None then None + else Some(new Candidate(ref, ckind, level)) + + val extensionCandidates = + if considerExtension then + companionRefs.toList.flatMap(matchingCandidate(_, extensionOnly = true)) + else + Nil + val implicitCandidates = + refs.flatMap(matchingCandidate(_, extensionOnly = false)) + extensionCandidates ::: implicitCandidates } } } @@ -246,7 +262,7 @@ object Implicits { * @param tp the type determining the implicit scope * @param companionRefs the companion objects in the implicit scope. */ - class OfTypeImplicits(tp: Type, val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs(initctx) { + class OfTypeImplicits(tp: Type, override val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs(initctx) { assert(initctx.typer != null) implicits.println(i"implicit scope of type $tp = ${companionRefs.toList}%, %") @threadUnsafe lazy val refs: List[ImplicitRef] = { @@ -492,8 +508,6 @@ trait ImplicitRunInfo: private val implicitScopeCache = mutable.AnyRefMap[Type, OfTypeImplicits]() - private val EmptyTermRefSet = new TermRefSet(using NoContext) - private def isExcluded(sym: Symbol) = if migrateTo3 then false else sym.is(Package) || sym.isPackageObject @@ -564,7 +578,7 @@ trait ImplicitRunInfo: case None => if seen.contains(t) then incomplete.addEntry(tp) // all references for `t` will be accounted for in `seen` so we return `EmptySet`. - EmptyTermRefSet // on the other hand, the refs of `tp` are now inaccurate, so `tp` is marked incomplete. + TermRefSet.empty // on the other hand, the refs of `tp` are now inaccurate, so `tp` is marked incomplete. else seen.addEntry(t) val is = recur(t) @@ -755,7 +769,7 @@ trait Implicits { self: Typer => } } - private var synthesizer: Synthesizer | Null = null + private var synthesizer: Synthesizer = null /** Find an implicit argument for parameter `formal`. * Return a failure as a SearchFailureType in the type of the returned tree. @@ -767,7 +781,7 @@ trait Implicits { self: Typer => if fail.isAmbiguous then failed else if synthesizer == null then synthesizer = Synthesizer(this) - synthesizer.nn.tryAll(formal, span).orElse(failed) + synthesizer.tryAll(formal, span).orElse(failed) /** Search an implicit argument and report error if not found */ def implicitArgTree(formal: Type, span: Span)(using Context): Tree = { @@ -1635,7 +1649,7 @@ final class SearchRoot extends SearchHistory { } /** A set of term references where equality is =:= */ -final class TermRefSet(using Context) { +sealed class TermRefSet(using Context): private val elems = new java.util.LinkedHashMap[TermSymbol, List[Type]] def += (ref: TermRef): Unit = { @@ -1665,4 +1679,11 @@ final class TermRefSet(using Context) { } override def toString = toList.toString -} + +object TermRefSet: + + val empty = new TermRefSet(using NoContext): + override def += (ref: TermRef): Unit = throw UnsupportedOperationException("+=") + +end TermRefSet + From 71a853aa315e38829902ddb90326de1d123cfaba Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 30 Jun 2020 15:41:05 +0200 Subject: [PATCH 12/38] Fixes to parsing and desugaring of extension methods --- .../src/dotty/tools/dotc/ast/Desugar.scala | 16 +- .../dotty/tools/dotc/parsing/Parsers.scala | 1 + .../dotty/tools/dotc/typer/Implicits.scala | 2 +- .../reference/contextual/extension-methods.md | 20 +-- tests/pos/reference/extension-methods.scala | 157 ++++++++++-------- 5 files changed, 112 insertions(+), 84 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 343ea26d7bac..484020c3e9fb 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -913,11 +913,17 @@ object desugar { if ext.tparams.nonEmpty && mdef.tparams.nonEmpty then ctx.error(em"extension method cannot have type parameters since some were already given in extension clause", mdef.tparams.head.sourcePos) - cpy.DefDef(mdef)( - name = mdef.name.toExtensionName, - tparams = ext.tparams ++ mdef.tparams, - vparamss = ext.vparamss ++ mdef.vparamss - ).withMods(mdef.mods | Extension) + defDef( + cpy.DefDef(mdef)( + name = mdef.name.toExtensionName, + tparams = ext.tparams ++ mdef.tparams, + vparamss = mdef.vparamss match + case vparams1 :: vparamss1 if !isLeftAssoc(mdef.name) => + vparams1 :: ext.vparamss ::: vparamss1 + case _ => + ext.vparamss ++ mdef.vparamss + ).withMods(mdef.mods | Extension) + ) } /** Transform the statements of a collective extension diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 11ec2b7e1583..122f06c55512 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3646,6 +3646,7 @@ object Parsers { val tparams = typeParamClauseOpt(ParamOwner.Def) val extParams = paramClause(0, prefix = true) val givenParamss = paramClauses(givenOnly = true) + newLinesOpt() val methods = if isDefIntro(modifierTokens) then extMethod() :: Nil diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 1dfc4fa68873..0802dfe7d909 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1682,7 +1682,7 @@ sealed class TermRefSet(using Context): object TermRefSet: - val empty = new TermRefSet(using NoContext): + @sharable val empty = new TermRefSet(using NoContext): override def += (ref: TermRef): Unit = throw UnsupportedOperationException("+=") end TermRefSet diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index 5ed2dc461c7e..cd1a7b0561f7 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -35,11 +35,11 @@ assert(circle.circumference == extension_circumference(circle)) The extension method syntax can also be used to define operators. Examples: ```scala extension (x: String) - def < (y: String) = ... + def < (y: String): Boolean = ... extension (x: Elem) - def +: (xs: Seq[Elem]) = ... + def +: (xs: Seq[Elem]): Seq[Elem] = ... extension (x: Number) - @infix def min (y: Number) = ... + @infix def min (y: Number): Number = ... "ab" < "c" 1 +: List(2, 3) @@ -48,9 +48,9 @@ x min 3 The three definitions above translate to ```scala -def extension_< (x: String)(y: String) = ... -def extension_+: (xs: Seq[Elem])(x: Elem) = ... -@infix def extension_min(x: Number)(y: Number) = ... +def extension_< (x: String)(y: String): Boolean = ... +def extension_+: (xs: Seq[Elem])(x: Elem): Seq[Elem] = ... +@infix def extension_min(x: Number)(y: Number): Number = ... ``` Note the swap of the two parameters `x` and `xs` when translating the right-associative operator `+:` to an extension method. This is analogous @@ -82,7 +82,7 @@ Of course, the type argument here would usually be left out since it can be infe Extensions can also take using clauses. For instance, the `+` extension above could equivalently be written with a using clause: ```scala extension [T](x: T)(using n: Numeric[T]) - def + (y: T): T = n.plus(x, y) + def - (y: T): T = n.minus(x, y) ``` **Note**: If an extension defines type parameters in its prefix, the extension method itself is not allowed to have additional type parameters. This restriction might be lifted in the future once we support multiple type parameter clauses in a method. @@ -120,9 +120,9 @@ extension (ss: Seq[String]) Collective extensions also can take type parameters and have using clauses. Example: ```scala extension [T](xs: List[T])(using Ordering[T]): - def smallest(n: Int): T = xs.sorted.take(n) + def smallest(n: Int): List[T] = xs.sorted.take(n) def smallestIndices(n: Int): List[Int] = - val limit = smallest(n) + val limit = smallest(n).max xs.zipWithIndex.collect { case (x, i) if x <= limit => i } ``` @@ -184,7 +184,7 @@ object List: given [T: Ordering] as Ordering[List[T]]: extension (xs: List[T]): - def < (ys: List[T]) = ... + def < (ys: List[T]): Boolean = ... end List // extension method available since it is in the implicit scope of List[List[Int]] diff --git a/tests/pos/reference/extension-methods.scala b/tests/pos/reference/extension-methods.scala index f68ccb37ac67..2dcab8cfd59e 100644 --- a/tests/pos/reference/extension-methods.scala +++ b/tests/pos/reference/extension-methods.scala @@ -1,96 +1,116 @@ +import annotation.infix + object ExtMethods: case class Circle(x: Double, y: Double, radius: Double) - def (c: Circle).circumference: Double = c.radius * math.Pi * 2 + extension (c: Circle) + def circumference: Double = c.radius * math.Pi * 2 val circle = Circle(0, 0, 1) circle.circumference assert(circle.circumference == extension_circumference(circle)) - trait StringSeqOps { - def (xs: Seq[String]).longestStrings = { - val maxLength = xs.map(_.length).max - xs.filter(_.length == maxLength) - } - } - given ops1 as StringSeqOps - - List("here", "is", "a", "list").longestStrings - - trait StringSeqOps2 { - def (xs: Seq[String]).longestStrings2 = { - val maxLength = xs.map(_.length).max - xs.filter(_.length == maxLength) - } - } - - locally { - object ops2 extends StringSeqOps2 - import ops2.{longestStrings2, extension_longestStrings2} - List("here", "is", "a", "list").longestStrings2 - extension_longestStrings2(Nil) - } - - def (x: String) < (y: String) = x.compareTo(y) < 0 - def [Elem](x: Elem) #: (xs: Seq[Elem]) = x +: xs + extension (x: String) def < (y: String) = x.compareTo(y) < 0 + extension [Elem](x: Elem) def #: (xs: Seq[Elem]) = x +: xs + extension (x: Number) @infix def min (y: Number) = x assert("a" < "bb") val xs = 1 #: Vector(2, 3) + val n = java.lang.Integer(2) min java.lang.Double(3.0) + + extension [T](xs: List[T]) + def second = xs.tail.head - def [T](xs: List[T]) second = - xs.tail.head + assert(List(1, 2, 3).second[Int] == List(1, 2, 3).second) - def [T](xs: List[List[T]]) flattened = - xs.foldLeft[List[T]](Nil)(_ ++ _) + extension [T: Numeric](x: T) + def + (y: T): T = summon[Numeric[T]].plus(x, y) - def [T: Numeric](x: T) + (y: T): T = - summon[Numeric[T]].plus(x, y) + extension [T](x: T)(using n: Numeric[T]) + def - (y: T): T = n.minus(x, y) - List(1, 2, 3).second[Int] + extension (ss: Seq[String]): - extension stringOps on (xs: Seq[String]) { - def longestStrings: Seq[String] = { - val maxLength = xs.map(_.length).max - xs.filter(_.length == maxLength) - } - } + def longestStrings: Seq[String] = + val maxLength = ss.map(_.length).max + ss.filter(_.length == maxLength) - extension listOps on [T](xs: List[T]): - def second = xs.tail.head - def third: T = xs.tail.tail.head + def longestString: String = longestStrings.head + + import math.Ordered.orderingToOrdered + + extension [T](xs: List[T])(using Ordering[T]): + def smallest(n: Int): List[T] = xs.sorted.take(n) + def smallestIndices(n: Int): List[Int] = + val limit = smallest(n).max + xs.zipWithIndex.collect { case (x, i) if x <= limit => i } + + trait IntOps: + extension (i: Int) def isZero: Boolean = i == 0 + + extension (i: Int) def safeMod(x: Int): Option[Int] = + // extension method defined in same scope IntOps + if x.isZero then None + else Some(i % x) + end IntOps + object IntOpsEx extends IntOps: + extension (i: Int) def safeDiv(x: Int): Option[Int] = + // extension method brought into scope via inheritance from IntOps + if x.isZero then None + else Some(i / x) - extension on [T](xs: List[T])(using Ordering[T]): - def largest(n: Int) = xs.sorted.takeRight(n) + trait SafeDiv: + import IntOpsEx._ // brings safeDiv and safeMod into scope - extension ops: - def (xs: Seq[String]).longestStrings: Seq[String] = - val maxLength = xs.map(_.length).max - xs.filter(_.length == maxLength) - def (xs: Seq[String]).longestString: String = xs.longestStrings.head - def [T](xs: List[T]).second: T = xs.tail.head + extension (i: Int) def divide(d: Int) : Option[(Int, Int)] = + // extension methods imported and thus in scope + (i.safeDiv(d), i.safeMod(d)) match + case (Some(d), Some(r)) => Some((d, r)) + case _ => None + end SafeDiv - extension: - def [T](xs: List[T]) longest (using Ordering[T])(n: Int) = - xs.sorted.takeRight(n) + def test1 = + given ops1 as IntOps // brings safeMod into scope + 1.safeMod(2) - given stringOps2 as AnyRef { - def (xs: Seq[String]).longestStrings: Seq[String] = { - val maxLength = xs.map(_.length).max - xs.filter(_.length == maxLength) - } - } + class Lst[T](xs: T*): + private val elems = xs.toList + def foldLeft[U](x: U)(op: (U, T) => U): U = elems.foldLeft(x)(op) + def ++ (other: Lst[T]): Lst[T] = Lst(elems ++ other.elems: _*) - given listOps2 as AnyRef { - def [T](xs: List[T]) second = xs.tail.head - def [T](xs: List[T]) third: T = xs.tail.tail.head - } + trait Ord[T]: + extension (x: T) def less (y: T): Boolean + object Ord: + given Ord[Int]: + extension (x: Int) def less (y: Int): Boolean = x < y + end Ord - given AnyRef { - def [T](xs: List[T]) largest (using Ordering[T])(n: Int) = - xs.sorted.takeRight(n) - } + object Lst: + + extension [T](xs: Lst[Lst[T]]) + def flatten: Lst[T] = xs.foldLeft(Lst())(_ ++ _) + + given ord[T: Ord] as Ord[Lst[T]]: + extension (xs: Lst[T]) + def less (ys: Lst[T]): Boolean = ??? + end Lst + + def test2 = + val xss = Lst(Lst(1, 2), Lst(3, 4)) + val xs: Lst[Int] = xss.flatten + + summon[Ord[Lst[Lst[Int]]]] + + assert(Lst.ord[Lst[Int]].extension_less(xss)(Lst(Lst(3)))) + // fails type inference: assert(xss `less` Lst(Lst(3))) + assert(xss.flatten `less` Lst(3)) + + extension (s: String) + def position(ch: Char, n: Int): Int = + if n < s.length && s(n) != ch then position(ch, n + 1) + else n object DoubleOps: extension (x: Double) def ** (exponent: Int): Double = @@ -99,4 +119,5 @@ object ExtMethods: import DoubleOps.{**, extension_**} assert(2.0 ** 3 == extension_**(2.0)(3)) + end ExtMethods \ No newline at end of file From fb93f464f01c265c7582a3e9da56797583e665d7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 30 Jun 2020 17:16:41 +0200 Subject: [PATCH 13/38] Fix: wrap extensions in toplevel package objects --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 2 +- tests/pos/i6565.scala | 4 ++-- tests/pos/i7041.scala | 15 ++++++++------- tests/pos/i7458.scala | 2 +- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 484020c3e9fb..21274740fb5e 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1268,7 +1268,7 @@ object desugar { } private def isTopLevelDef(stat: Tree)(using Context): Boolean = stat match - case _: ValDef | _: PatDef | _: DefDef | _: Export => true + case _: ValDef | _: PatDef | _: DefDef | _: Export | _: ExtMethods => true case stat: ModuleDef => stat.mods.isOneOf(GivenOrImplicit) case stat: TypeDef => diff --git a/tests/pos/i6565.scala b/tests/pos/i6565.scala index da08245804ca..0672724724fd 100644 --- a/tests/pos/i6565.scala +++ b/tests/pos/i6565.scala @@ -3,8 +3,8 @@ class Err type Lifted[A] = Err | A def point[O](o: O): Lifted[O] = o -def [O, U](o: Lifted[O]).map(f: O => U): Lifted[U] = ??? -def [O, U](o: Lifted[O]).flatMap(f: O => Lifted[U]): Lifted[U] = ??? +extension [O, U](o: Lifted[O]) def map(f: O => U): Lifted[U] = ??? +extension [O, U](o: Lifted[O]) def flatMap(f: O => Lifted[U]): Lifted[U] = ??? val error: Err = Err() diff --git a/tests/pos/i7041.scala b/tests/pos/i7041.scala index e7a77576374c..32bafa304dfb 100644 --- a/tests/pos/i7041.scala +++ b/tests/pos/i7041.scala @@ -1,12 +1,13 @@ import scala.util.control.NonLocalReturns._ -inline def [T, E <: Throwable](op: => T) rescue (fallback: PartialFunction[E, T]) = - try op - catch { - case ex: ReturnThrowable[_] => throw ex - case ex: E => - if (fallback.isDefinedAt(ex)) fallback(ex) else throw ex - } +extension [T, E <: Throwable](op: => T) + inline def rescue (fallback: PartialFunction[E, T]) = + try op + catch { + case ex: ReturnThrowable[_] => throw ex + case ex: E => + if (fallback.isDefinedAt(ex)) fallback(ex) else throw ex + } def test: Unit = { 9 / 0 rescue { case _: ArithmeticException => 10 } diff --git a/tests/pos/i7458.scala b/tests/pos/i7458.scala index 530e97c9e7f6..83885895f55d 100644 --- a/tests/pos/i7458.scala +++ b/tests/pos/i7458.scala @@ -1,3 +1,3 @@ type Tr[+V1, +O1 <: V1] -def [V2, O2 <: V2](tr: Tr[V2, O2]) sl: Tr[V2, O2] = ??? +extension [V2, O2 <: V2](tr: Tr[V2, O2]) def sl: Tr[V2, O2] = ??? def as[V3, O3 <: V3](tr: Tr[V3, O3]) : Tr[V3, O3] = tr.sl From 1ad5ee9e431a9cf91d874e47965f02f54cb5f5ed Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 30 Jun 2020 17:37:26 +0200 Subject: [PATCH 14/38] Make type parameters after extension method `def` illegal We'd have to at least check that there's no use before definition. Forbid for now, and come back once we support multiple type parameter clauses. --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 5 ++--- docs/docs/reference/contextual/extension-methods.md | 3 ++- tests/neg-custom-args/extmethods-tparams.scala | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 21274740fb5e..4de27acc99dd 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -910,9 +910,8 @@ object desugar { /** Transform extension construct to list of extension methods */ def extMethods(ext: ExtMethods)(using Context): Tree = flatTree { for mdef <- ext.methods yield - if ext.tparams.nonEmpty && mdef.tparams.nonEmpty then - ctx.error(em"extension method cannot have type parameters since some were already given in extension clause", - mdef.tparams.head.sourcePos) + if mdef.tparams.nonEmpty then + ctx.error("no type parameters allowed here", mdef.tparams.head.sourcePos) defDef( cpy.DefDef(mdef)( name = mdef.name.toExtensionName, diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index cd1a7b0561f7..a32c6e0b053a 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -85,7 +85,8 @@ Extensions can also take using clauses. For instance, the `+` extension above co def - (y: T): T = n.minus(x, y) ``` -**Note**: If an extension defines type parameters in its prefix, the extension method itself is not allowed to have additional type parameters. This restriction might be lifted in the future once we support multiple type parameter clauses in a method. +**Note**: Type parameters have to be given after the `extension` keyword; +they cannot be given after the `def`. This restriction might be lifted in the future once we support multiple type parameter clauses in a method. ### Collective Extensions diff --git a/tests/neg-custom-args/extmethods-tparams.scala b/tests/neg-custom-args/extmethods-tparams.scala index 59a9780a7abf..9cf6e94c14c4 100644 --- a/tests/neg-custom-args/extmethods-tparams.scala +++ b/tests/neg-custom-args/extmethods-tparams.scala @@ -1,2 +1,2 @@ -def (self: T).foo[T] = ??? // error -def [T1](self: T1) bar[T2] = ??? // error // error \ No newline at end of file +extension (self: T) def foo[T] = ??? // error +extension [T1](self: T1) def bar[T2] = ??? // error \ No newline at end of file From 6ff0ac8694d4e3b84ed9b15fb277f429c6767faa Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 30 Jun 2020 17:59:48 +0200 Subject: [PATCH 15/38] Update tests and docs to new extension syntax --- .../dotty/tools/dotc/typer/Inferencing.scala | 2 +- docs/docs/contributing/debugging.md | 2 +- .../reference/contextual/context-functions.md | 4 +- .../reference/contextual/derivation-macro.md | 6 +- docs/docs/reference/contextual/givens.md | 4 +- .../contextual/relationship-implicits.md | 2 +- .../docs/reference/contextual/type-classes.md | 2 +- .../dropped-features/package-objects.md | 2 +- docs/docs/reference/metaprogramming/macros.md | 2 +- .../reference/other-new-features/opaques.md | 16 ++-- .../other-new-features/quoted-pattern-spec.md | 4 +- .../other-new-features/tupled-function.md | 9 +- tests/init/crash/i5606.scala | 2 +- tests/init/crash/i7821.scala | 4 +- .../fatal-warnings/i7821.scala | 4 +- tests/neg-custom-args/infix.scala | 7 +- tests/neg-macros/i6432/Macro_1.scala | 2 +- tests/neg-macros/i6432b/Macro_1.scala | 2 +- tests/neg-macros/i7698.scala | 2 +- .../neg-macros/reflect-inline/assert_1.scala | 2 +- .../Macro_1.scala | 2 +- .../Macro_1.scala | 2 +- tests/neg/capture1.scala | 6 +- tests/neg/extension-method-not-allowed.check | 8 +- tests/neg/extension-method-not-allowed.scala | 2 +- tests/neg/extension-methods.scala | 4 +- tests/neg/extmethod-override.scala | 4 +- tests/neg/i5773.scala | 10 +-- tests/neg/i6662.scala | 8 +- tests/neg/i6762b.scala | 2 +- tests/neg/i6779.scala | 2 +- tests/neg/i7060.scala | 2 +- tests/neg/i7438.scala | 4 +- tests/neg/i8894.scala | 2 +- tests/neg/i9185.scala | 4 +- tests/neg/indent.scala | 2 +- tests/neg/missing-implicit1.scala | 4 +- tests/neg/missing-implicit4.scala | 2 +- tests/neg/missing-implicit5.scala | 4 +- tests/neg/opaque-bounds.scala | 8 +- .../override-extension_normal-methods.scala | 4 +- tests/pos/combine.scala | 2 +- tests/pos/consume.scala | 10 +-- tests/pos/i5773.scala | 8 +- tests/pos/i5773a.scala | 6 +- tests/pos/i6395.scala | 4 +- tests/pos/i6705.scala | 2 +- tests/pos/i6734.scala | 20 +++-- tests/pos/i6847.scala | 4 +- tests/pos/i7070.scala | 2 +- tests/pos/i7119.scala | 2 +- tests/pos/i7413.scala | 2 +- tests/pos/i7700.scala | 2 +- tests/pos/indent.scala | 2 +- tests/pos/opaque-propability-xm.scala | 14 +-- tests/pos/opaque-xm.scala | 6 +- tests/pos/postconditions.scala | 2 +- tests/pos/reference/delegates.scala | 46 +++++----- tests/pos/reference/opaque.scala | 8 +- .../pos/toplevel-opaque-xm/Logarithm_1.scala | 6 +- tests/pos/typeclass-encoding3.scala | 30 +++---- tests/printing/i620.check | 2 +- tests/printing/i620.scala | 2 +- .../f-interpolator-neg/Macros_1.scala | 2 +- tests/run-macros/i6201/macro_1.scala | 2 +- tests/run-macros/i6253-b/quoted_1.scala | 2 +- tests/run-macros/i6253-c/quoted_1.scala | 4 +- tests/run-macros/i6253/quoted_1.scala | 2 +- tests/run-macros/i6270/Macro_1.scala | 4 +- tests/run-macros/i6772/Macro_1.scala | 2 +- tests/run-macros/i8007/Macro_3.scala | 2 +- .../quote-elide-prefix/quoted_1.scala | 2 +- .../quoted_1.scala | 2 +- .../quoted_1.scala | 2 +- .../quoted_1.scala | 2 +- .../run-macros/reflect-inline/assert_1.scala | 2 +- .../reflect-sourceCode/Macro_1.scala | 2 +- .../string-context-implicits/Macro_1.scala | 2 +- .../Macros_1.scala | 4 +- .../xml-interpolation-3/XmlQuote_1.scala | 2 +- .../xml-interpolation-4/Macros_1.scala | 2 +- .../xml-interpolation-5/Macros_1.scala | 6 +- .../xml-interpolation-6/Macros_1.scala | 6 +- .../xml-interpolation-7/Macros_1.scala | 6 +- tests/run/Pouring.scala | 2 +- tests/run/eq-xmethod.scala | 2 +- tests/run/exports.scala | 2 +- tests/run/extension-methods.scala | 29 +++--- tests/run/extmethod-overload.scala | 30 +++---- tests/run/extmethods2.scala | 4 +- tests/run/i5110/quoted_1.scala | 4 +- tests/run/i5606.scala | 2 +- tests/run/instances-anonymous.scala | 28 +++--- tests/run/instances.scala | 29 +++--- tests/run/opaque-immutable-array-xm.scala | 4 +- tests/run/rescue.scala | 4 +- tests/run/singleton-ops-flags.scala | 18 ++-- ...ng-context-implicits-with-conversion.scala | 2 +- tests/run/toplevel-implicits/a.b.scala | 2 +- tests/run/tupled-function-andThen.scala | 2 +- tests/run/tupled-function-apply.scala | 2 +- tests/run/tupled-function-compose.scala | 2 +- .../tupled-function-extension-method.scala | 13 +-- tests/run/tupled-function-tupled.scala | 2 +- tests/run/tupled-function-untupled.scala | 2 +- tests/semanticdb/expect/Enums.expect.scala | 6 +- tests/semanticdb/expect/Enums.scala | 6 +- tests/semanticdb/expect/Givens.expect.scala | 4 +- tests/semanticdb/expect/Givens.scala | 4 +- tests/semanticdb/expect/toplevel.expect.scala | 2 +- tests/semanticdb/expect/toplevel.scala | 2 +- tests/semanticdb/metac.expect | 90 +++++++++---------- 112 files changed, 359 insertions(+), 348 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index c6b241458ee1..401d44d40480 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -530,7 +530,7 @@ trait Inferencing { this: Typer => * is in i7558.scala: * * type Tr[+V1, +O1 <: V1] - * def [V2, O2 <: V2](tr: Tr[V2, O2]) sl: Tr[V2, O2] = ??? + * extension [V2, O2 <: V2](tr: Tr[V2, O2]) def sl: Tr[V2, O2] = ??? * def as[V3, O3 <: V3](tr: Tr[V3, O3]) : Tr[V3, O3] = tr.sl * * Here we interpolate at some point V2 and O2 given the constraint diff --git a/docs/docs/contributing/debugging.md b/docs/docs/contributing/debugging.md index f50cee91b076..6d26501f284a 100644 --- a/docs/docs/contributing/debugging.md +++ b/docs/docs/contributing/debugging.md @@ -88,7 +88,7 @@ But you can also do: assertPositioned(tree.reporting(s"Tree is: $result")) ``` -`def (a: A).reporting(f: WrappedResult[T] ?=> String, p: Printer = Printers.default): A` is defined on all types. The function `f` can be written without the argument since it is a context function`. The `result` variable is a part of the `WrapperResult` – a tiny framework powering the `reporting` function. Basically, whenever you are using `reporting` on an object `A`, you can use the `result: A` variable from this function and it will be equal to the object you are calling `reporting` on. +`extension (a: A) def reporting(f: WrappedResult[T] ?=> String, p: Printer = Printers.default): A` is defined on all types. The function `f` can be written without the argument since it is a context function`. The `result` variable is a part of the `WrapperResult` – a tiny framework powering the `reporting` function. Basically, whenever you are using `reporting` on an object `A`, you can use the `result: A` variable from this function and it will be equal to the object you are calling `reporting` on. ## Printing out trees after phases To print out the trees you are compiling after the FrontEnd (scanner, parser, namer, typer) phases: diff --git a/docs/docs/reference/contextual/context-functions.md b/docs/docs/reference/contextual/context-functions.md index d98b034de4bb..21e2dcf39fa3 100644 --- a/docs/docs/reference/contextual/context-functions.md +++ b/docs/docs/reference/contextual/context-functions.md @@ -16,7 +16,7 @@ the same way methods with context parameters are applied. For instance: given ec as ExecutionContext = ... def f(x: Int): ExecutionContext ?=> Int = ... - + // could be written as follows with the type alias from above // def f(x: Int): Executable[Int] = ... @@ -125,7 +125,7 @@ object PostConditions { def result[T](using r: WrappedResult[T]): T = r - def [T] (x: T).ensuring(condition: WrappedResult[T] ?=> Boolean): T = { + extension [T](x: T) def ensuring(condition: WrappedResult[T] ?=> Boolean): T = { assert(condition(using x)) x } diff --git a/docs/docs/reference/contextual/derivation-macro.md b/docs/docs/reference/contextual/derivation-macro.md index e00a8a4d3bfd..7c327a338f16 100644 --- a/docs/docs/reference/contextual/derivation-macro.md +++ b/docs/docs/reference/contextual/derivation-macro.md @@ -120,7 +120,8 @@ Alternatively and what is shown below is that we can call the `eqv` method directly. The `eqGen` can trigger the derivation. ```scala -inline def [T](x: =>T) === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) +extension [T](x: =>T) + inline def === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) implicit inline def eqGen[T]: Eq[T] = ${ Eq.derived[T] } ``` @@ -216,7 +217,8 @@ object Eq { } object Macro3 { - inline def [T](x: =>T) === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) + extension [T](x: =>T) + inline def === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) implicit inline def eqGen[T]: Eq[T] = ${ Eq.derived[T] } } diff --git a/docs/docs/reference/contextual/givens.md b/docs/docs/reference/contextual/givens.md index f13fee94e5fc..e4b72543545c 100644 --- a/docs/docs/reference/contextual/givens.md +++ b/docs/docs/reference/contextual/givens.md @@ -9,8 +9,8 @@ that serve for synthesizing arguments to [context parameters](./using-clauses.ht ```scala trait Ord[T] { def compare(x: T, y: T): Int - def (x: T) < (y: T) = compare(x, y) < 0 - def (x: T) > (y: T) = compare(x, y) > 0 + extension (x: T) def < (y: T) = compare(x, y) < 0 + extension (x: T) def > (y: T) = compare(x, y) > 0 } given intOrd as Ord[Int] { diff --git a/docs/docs/reference/contextual/relationship-implicits.md b/docs/docs/reference/contextual/relationship-implicits.md index 12c9ee3eb672..cbfcf614cbb7 100644 --- a/docs/docs/reference/contextual/relationship-implicits.md +++ b/docs/docs/reference/contextual/relationship-implicits.md @@ -111,7 +111,7 @@ will map to with clauses instead. Extension methods have no direct counterpart in Scala 2, but they can be simulated with implicit classes. For instance, the extension method ```scala -def (c: Circle).circumference: Double = c.radius * math.Pi * 2 +extension (c: Circle) def circumference: Double = c.radius * math.Pi * 2 ``` could be simulated to some degree by ```scala diff --git a/docs/docs/reference/contextual/type-classes.md b/docs/docs/reference/contextual/type-classes.md index 38fff9494a48..3760cbd85b58 100644 --- a/docs/docs/reference/contextual/type-classes.md +++ b/docs/docs/reference/contextual/type-classes.md @@ -156,7 +156,7 @@ A `List` can be turned into a monad via this `given` instance: given listMonad as Monad[List]: def pure[A](x: A): List[A] = List(x) - extension def [A, B](xs: List[A]) + extension [A, B](xs: List[A]) def flatMap(f: A => List[B]): List[B] = xs.flatMap(f) // rely on the existing `flatMap` method of `List` ``` diff --git a/docs/docs/reference/dropped-features/package-objects.md b/docs/docs/reference/dropped-features/package-objects.md index fc8a5fccbded..beb304a48b16 100644 --- a/docs/docs/reference/dropped-features/package-objects.md +++ b/docs/docs/reference/dropped-features/package-objects.md @@ -22,7 +22,7 @@ def b = a._2 case class C() implicit object Cops { - def (x: C).pair(y: C) = (x, y) + extension (x: C) def pair(y: C) = (x, y) } ``` There may be several source files in a package containing such toplevel definitions, and source files can freely mix toplevel value, method, and type definitions with classes and objects. diff --git a/docs/docs/reference/metaprogramming/macros.md b/docs/docs/reference/metaprogramming/macros.md index 8a918bf70a48..1a8d510ca595 100644 --- a/docs/docs/reference/metaprogramming/macros.md +++ b/docs/docs/reference/metaprogramming/macros.md @@ -717,7 +717,7 @@ This might be used to then perform an implicit search as in: ```scala -inline def (inline sc: StringContext).showMe(inline args: Any*): String = ${ showMeExpr('sc, 'args) } +extension (inline sc: StringContext) inline def showMe(inline args: Any*): String = ${ showMeExpr('sc, 'args) } private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(using QuoteContext): Expr[String] = { argsExpr match { diff --git a/docs/docs/reference/other-new-features/opaques.md b/docs/docs/reference/other-new-features/opaques.md index 69ab27ca367f..dc8c883cc4e3 100644 --- a/docs/docs/reference/other-new-features/opaques.md +++ b/docs/docs/reference/other-new-features/opaques.md @@ -29,15 +29,15 @@ object Logarithms { } ``` -This introduces `Logarithm` as a new abstract type, which is implemented as `Double`. +This introduces `Logarithm` as a new abstract type, which is implemented as `Double`. The fact that `Logarithm` is the same as `Double` is only known in the scope where `Logarithm` is defined which in the above example corresponds to the object `Logarithms`. -Or in other words, within the scope it is treated as type alias, but this is opaque to the outside world +Or in other words, within the scope it is treated as type alias, but this is opaque to the outside world where in consequence `Logarithm` is seen as an abstract type and has nothing to do with `Double`. -The public API of `Logarithm` consists of the `apply` and `safe` methods defined in the companion object. +The public API of `Logarithm` consists of the `apply` and `safe` methods defined in the companion object. They convert from `Double`s to `Logarithm` values. Moreover, a collective extension `logarithmOps` provides the extension methods `toDouble` that converts the other way, -and operations `+` and `*` on `Logarithm` values. +and operations `+` and `*` on `Logarithm` values. The following operations would be valid because they use functionality implemented in the `Logarithms` object. ```scala @@ -68,10 +68,10 @@ object Access { opaque type PermissionChoice = Int opaque type Permission <: Permissions & PermissionChoice = Int - def (x: Permissions) & (y: Permissions): Permissions = x | y - def (x: PermissionChoice) | (y: PermissionChoice): PermissionChoice = x | y - def (granted: Permissions).is(required: Permissions) = (granted & required) == required - def (granted: Permissions).isOneOf(required: PermissionChoice) = (granted & required) != 0 + extension (x: Permissions) def & (y: Permissions): Permissions = x | y + extension (x: PermissionChoice) def | (y: PermissionChoice): PermissionChoice = x | y + extension (granted: Permissions) def is(required: Permissions) = (granted & required) == required + extension (granted: Permissions) def isOneOf(required: PermissionChoice) = (granted & required) != 0 val NoPermission: Permission = 0 val Read: Permission = 1 diff --git a/docs/docs/reference/other-new-features/quoted-pattern-spec.md b/docs/docs/reference/other-new-features/quoted-pattern-spec.md index 9d805389778f..ad52b0e7a0dc 100644 --- a/docs/docs/reference/other-new-features/quoted-pattern-spec.md +++ b/docs/docs/reference/other-new-features/quoted-pattern-spec.md @@ -46,11 +46,11 @@ type Env def notMatched = None def matched = Some(()) // aka Some(Tuple0()) def matched[T](x: T) = Some(Tuple1(x)) -def (x: Matching) && (y: Matching) = if (x == None || y == None) None else Some(x.get ++ y.get) +extension (x: Matching) def && (y: Matching) = if (x == None || y == None) None else Some(x.get ++ y.get) def fold[T](m: Mattching*)(using Env): Matching = m.fold(matched)(_ && _) // `a =#= b` stands for `a` matches `b` -def (scrutinee: Tree) =#= pattern: Tree)(using Env): Matching // described by cases in the tables below +extension (scrutinee: Tree) def =#= pattern: Tree)(using Env): Matching // described by cases in the tables below def envWith(equiv: (Symbol, Symbol)*)(using Env): Env // Adds to the current environment the fact that s1 from the scrutinee is equivalent to s2 in the pattern diff --git a/docs/docs/reference/other-new-features/tupled-function.md b/docs/docs/reference/other-new-features/tupled-function.md index d89aef2cde0e..ad94169210ad 100644 --- a/docs/docs/reference/other-new-features/tupled-function.md +++ b/docs/docs/reference/other-new-features/tupled-function.md @@ -44,7 +44,8 @@ The following defines `tupled` as [extension method](../contextual/extension-met * @tparam Args the tuple type with the same types as the function arguments of F * @tparam R the return type of F */ -def [F, Args <: Tuple, R](f: F).tupled(using tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f) +extension [F, Args <: Tuple, R](f: F) + def tupled(using tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f) ``` `TupledFunction` can be used to generalize the `Function.untupled` to a function of any arities ([full example](https://github.com/lampepfl/dotty/blob/master/tests/run/tupled-function-untupled.scala)) @@ -59,7 +60,8 @@ def [F, Args <: Tuple, R](f: F).tupled(using tf: TupledFunction[F, Args => R]): * @tparam Args the tuple type with the same types as the function arguments of F * @tparam R the return type of F */ -def [F, Args <: Tuple, R](f: Args => R).untupled(using tf: TupledFunction[F, Args => R]): F = tf.untupled(f) +extension [F, Args <: Tuple, R](f: Args => R) + def untupled(using tf: TupledFunction[F, Args => R]): F = tf.untupled(f) ``` `TupledFunction` can also be used to generalize the [`Tuple1.compose`](https://github.com/lampepfl/dotty/blob/master/tests/run/tupled-function-compose.scala) and [`Tuple1.andThen`](https://github.com/lampepfl/dotty/blob/master/tests/run/tupled-function-andThen.scala) methods to compose functions of larger arities and with functions that return tuples. @@ -73,7 +75,8 @@ def [F, Args <: Tuple, R](f: Args => R).untupled(using tf: TupledFunction[F, Arg * @tparam GArgs the tuple type with the same types as the function arguments of G * @tparam R the return type of F */ -def [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F).compose(g: G)(using tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = { +extension [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F) + def compose(g: G)(using tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = { (x: GArgs) => tf.tupled(f)(tg.tupled(g)(x)) } ``` diff --git a/tests/init/crash/i5606.scala b/tests/init/crash/i5606.scala index f84c752e9b90..aedfa0617dfc 100644 --- a/tests/init/crash/i5606.scala +++ b/tests/init/crash/i5606.scala @@ -1,6 +1,6 @@ object Test extends App { - def [A, B](f: A => B) `$` (a: A): B = f(a) + extension [A, B](f: A => B) def `$` (a: A): B = f(a) assert((((a: Int) => a.toString()) `$` 10) == "10") diff --git a/tests/init/crash/i7821.scala b/tests/init/crash/i7821.scala index 5903b63219cd..523fa1e7306c 100644 --- a/tests/init/crash/i7821.scala +++ b/tests/init/crash/i7821.scala @@ -4,7 +4,7 @@ object XObject { def anX: X = 5 given ops as Object { - def (x: X) + (y: X): X = x + y + extension (x: X) def + (y: X): X = x + y } } @@ -14,7 +14,7 @@ object MyXObject { def anX: MyX = XObject.anX given ops as Object { - def (x: MyX) + (y: MyX): MyX = x + y // error: warring: Infinite recursive call + extension (x: MyX) def + (y: MyX): MyX = x + y // error: warring: Infinite recursive call } } diff --git a/tests/neg-custom-args/fatal-warnings/i7821.scala b/tests/neg-custom-args/fatal-warnings/i7821.scala index 5903b63219cd..523fa1e7306c 100644 --- a/tests/neg-custom-args/fatal-warnings/i7821.scala +++ b/tests/neg-custom-args/fatal-warnings/i7821.scala @@ -4,7 +4,7 @@ object XObject { def anX: X = 5 given ops as Object { - def (x: X) + (y: X): X = x + y + extension (x: X) def + (y: X): X = x + y } } @@ -14,7 +14,7 @@ object MyXObject { def anX: MyX = XObject.anX given ops as Object { - def (x: MyX) + (y: MyX): MyX = x + y // error: warring: Infinite recursive call + extension (x: MyX) def + (y: MyX): MyX = x + y // error: warring: Infinite recursive call } } diff --git a/tests/neg-custom-args/infix.scala b/tests/neg-custom-args/infix.scala index 1bc65e8205b1..b06ff1a04fa8 100644 --- a/tests/neg-custom-args/infix.scala +++ b/tests/neg-custom-args/infix.scala @@ -8,9 +8,10 @@ class C: object C: given AnyRef: - def (x: C) iop (y: Int) = ??? - def (x: C).mop (y: Int) = ??? - def (x: C) ++ (y: Int) = ??? + extension (x: C): + @infix def iop (y: Int) = ??? + def mop (y: Int) = ??? + def ++ (y: Int) = ??? val c = C() def test() = { diff --git a/tests/neg-macros/i6432/Macro_1.scala b/tests/neg-macros/i6432/Macro_1.scala index 9d3874a0afd6..90e4d5b91cf8 100644 --- a/tests/neg-macros/i6432/Macro_1.scala +++ b/tests/neg-macros/i6432/Macro_1.scala @@ -4,7 +4,7 @@ import scala.quoted.autolift object Macro { - inline def (inline sc: StringContext).foo(args: String*): Unit = ${ impl('sc) } + extension (inline sc: StringContext) inline def foo(args: String*): Unit = ${ impl('sc) } def impl(sc: Expr[StringContext])(using qctx: QuoteContext) : Expr[Unit] = { import qctx.tasty._ diff --git a/tests/neg-macros/i6432b/Macro_1.scala b/tests/neg-macros/i6432b/Macro_1.scala index 9d3874a0afd6..90e4d5b91cf8 100644 --- a/tests/neg-macros/i6432b/Macro_1.scala +++ b/tests/neg-macros/i6432b/Macro_1.scala @@ -4,7 +4,7 @@ import scala.quoted.autolift object Macro { - inline def (inline sc: StringContext).foo(args: String*): Unit = ${ impl('sc) } + extension (inline sc: StringContext) inline def foo(args: String*): Unit = ${ impl('sc) } def impl(sc: Expr[StringContext])(using qctx: QuoteContext) : Expr[Unit] = { import qctx.tasty._ diff --git a/tests/neg-macros/i7698.scala b/tests/neg-macros/i7698.scala index c3e2800cdfce..0cbc8ac0dcd3 100644 --- a/tests/neg-macros/i7698.scala +++ b/tests/neg-macros/i7698.scala @@ -10,4 +10,4 @@ def showInterpolatorImpl(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(usin case '[ Int ] => // error ??? -inline def (inline sc: StringContext) show (args: Any*): String = ${ showInterpolatorImpl('sc, 'args) } +extension (inline sc: StringContext) inline def show (args: Any*): String = ${ showInterpolatorImpl('sc, 'args) } diff --git a/tests/neg-macros/reflect-inline/assert_1.scala b/tests/neg-macros/reflect-inline/assert_1.scala index cee496888207..863cd9be32be 100644 --- a/tests/neg-macros/reflect-inline/assert_1.scala +++ b/tests/neg-macros/reflect-inline/assert_1.scala @@ -1,7 +1,7 @@ import scala.quoted._ object api { - inline def (inline x: String).stripMargin2: String = + extension (inline x: String) inline def stripMargin2: String = ${ stripImpl('x) } private def stripImpl(x: Expr[String])(using qctx: QuoteContext): Expr[String] = diff --git a/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala b/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala index 49503b208e2b..06556ce8e138 100644 --- a/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala +++ b/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions object Macro { - implicit inline def (strCtx: => StringContext).f2(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} + extension (strCtx: => StringContext) implicit inline def f2(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} } diff --git a/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala b/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala index bcf63788e3c2..1d9e849c5564 100644 --- a/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala +++ b/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions object Macro { - implicit inline def (strCtx: => StringContext).f3(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} + extension (strCtx: => StringContext) implicit inline def f3(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} } diff --git a/tests/neg/capture1.scala b/tests/neg/capture1.scala index 7b166bf5af1d..c08312c5af7a 100644 --- a/tests/neg/capture1.scala +++ b/tests/neg/capture1.scala @@ -4,11 +4,11 @@ object Test extends App { val l: mutable.Seq[String] = mutable.ArrayBuffer() - def [T, U](xs: List[T]) emap (f: T => U): List[U] = xs.map(f) + extension [T, U](xs: List[T]) def emap (f: T => U): List[U] = xs.map(f) - def [T](xs: List[T]) ereduce (f: (T, T) => T): T = xs.reduceLeft(f) + extension [T](xs: List[T]) def ereduce (f: (T, T) => T): T = xs.reduceLeft(f) - def [T](xs: mutable.Seq[T]) append (ys: mutable.Seq[T]): mutable.Seq[T] = xs ++ ys + extension [T](xs: mutable.Seq[T]) def append (ys: mutable.Seq[T]): mutable.Seq[T] = xs ++ ys List(l, mutable.ArrayBuffer(1)) .emap(list => list) diff --git a/tests/neg/extension-method-not-allowed.check b/tests/neg/extension-method-not-allowed.check index 8886f484b869..7b82a1e91901 100644 --- a/tests/neg/extension-method-not-allowed.check +++ b/tests/neg/extension-method-not-allowed.check @@ -1,4 +1,4 @@ --- Error: tests/neg/extension-method-not-allowed.scala:3:8 ------------------------------------------------------------- -3 | def (c: T).f: T = ??? // error : No extension method allowed here - | ^^^^^^^^^^^^^^^^^^^^^ - | no extension method allowed here since leading parameter was already given +-- Error: tests/neg/extension-method-not-allowed.scala:3:15 ------------------------------------------------------------ +3 | extension (c: T) def f: T = ??? // error : No extension method allowed here + | ^^^^^^^^^^^^^^^^^^^^ + | extension clause can only define methods diff --git a/tests/neg/extension-method-not-allowed.scala b/tests/neg/extension-method-not-allowed.scala index a50235481c22..ac4a21eafe8a 100644 --- a/tests/neg/extension-method-not-allowed.scala +++ b/tests/neg/extension-method-not-allowed.scala @@ -1,5 +1,5 @@ object Test { extension on[T] (t: T) { - def (c: T).f: T = ??? // error : No extension method allowed here + extension (c: T) def f: T = ??? // error : No extension method allowed here } } \ No newline at end of file diff --git a/tests/neg/extension-methods.scala b/tests/neg/extension-methods.scala index 8396070e3282..c576b837ac82 100644 --- a/tests/neg/extension-methods.scala +++ b/tests/neg/extension-methods.scala @@ -1,7 +1,7 @@ object Test { implicit object O { - def (x: String).l1 = x.length + extension (x: String) def l1 = x.length def l1(x: Int) = x * x def l2(x: String) = x.length } @@ -11,7 +11,7 @@ object Test { 1.l1 // error extension on [T](xs: List[T]) { - def (x: Int).f1: T = ??? // error: No extension method allowed here, since collective parameters are given + extension (x: Int) def f1: T = ??? // error: No extension method allowed here, since collective parameters are given def f2[T]: T = ??? // error: T is already defined as type T def f3(xs: List[T]) = ??? // error: xs is already defined as value xs } diff --git a/tests/neg/extmethod-override.scala b/tests/neg/extmethod-override.scala index 9b2e809f9baa..ec6f4770a6c7 100644 --- a/tests/neg/extmethod-override.scala +++ b/tests/neg/extmethod-override.scala @@ -1,8 +1,8 @@ class A { def f(x: Int)(y: Int): Int = 0 - def (x: Int).g(y: Int): Int = 1 + extension (x: Int) def g(y: Int): Int = 1 } class B extends A { - override def (x: Int).f(y: Int): Int = 1 // error + extension (x: Int) override def f(y: Int): Int = 1 // error override def g(x: Int)(y: Int): Int = 0 // error } \ No newline at end of file diff --git a/tests/neg/i5773.scala b/tests/neg/i5773.scala index 4ae1fa220599..617720e085cf 100644 --- a/tests/neg/i5773.scala +++ b/tests/neg/i5773.scala @@ -1,16 +1,16 @@ trait Semigroup[T] { - def (lhs: T).append(rhs: T): T - def (lhs: Int).appendS(rhs: T): T = ??? + extension (lhs: T) def append(rhs: T): T + extension (lhs: Int) def appendS(rhs: T): T = ??? } object Semigroup { implicit object stringAppend extends Semigroup[String] { - override def (lhs: String).append(rhs: String): String = lhs + rhs + extension (lhs: String) override def append(rhs: String): String = lhs + rhs } implicit def sumSemigroup[N](implicit N: Numeric[N]): Semigroup[N] = new { - override def (lhs: N).append(rhs: N): N = N.plus(lhs, rhs) - def (lhs: Int).appendS(rhs: N): N = ??? // N.plus(lhs, rhs) + extension (lhs: N) override def append(rhs: N): N = N.plus(lhs, rhs) + extension (lhs: Int) def appendS(rhs: N): N = ??? // N.plus(lhs, rhs) } } diff --git a/tests/neg/i6662.scala b/tests/neg/i6662.scala index 3c50327e718b..ccad0675da71 100644 --- a/tests/neg/i6662.scala +++ b/tests/neg/i6662.scala @@ -1,9 +1,9 @@ opaque type Opt[A >: Null] = A -inline def [A >: Null](x: Opt[A]) nonEmpty: Boolean = x.get != null // error: Implementation restriction -inline def [A >: Null](x: Opt[A]) isEmpty: Boolean = x.get == null // error: Implementation restriction -inline def [A >: Null](x: Opt[A]) isDefined: Boolean = x.nonEmpty // error: Implementation restriction -inline def [A >: Null](x: Opt[A]) get: A = Opt.unOpt(x) // error: Implementation restriction +extension [A >: Null](x: Opt[A]) inline def nonEmpty: Boolean = x.get != null // error: Implementation restriction +extension [A >: Null](x: Opt[A]) inline def isEmpty: Boolean = x.get == null // error: Implementation restriction +extension [A >: Null](x: Opt[A]) inline def isDefined: Boolean = x.nonEmpty // error: Implementation restriction +extension [A >: Null](x: Opt[A]) inline def get: A = Opt.unOpt(x) // error: Implementation restriction object Opt { diff --git a/tests/neg/i6762b.scala b/tests/neg/i6762b.scala index e5ad5cee17ed..66677a9639ec 100644 --- a/tests/neg/i6762b.scala +++ b/tests/neg/i6762b.scala @@ -9,5 +9,5 @@ type Liftable given Liftable = ??? implicit object ExprOps { - def [T](x: T).toExpr(using Liftable): Expr[T] = ??? + extension [T](x: T) def toExpr(using Liftable): Expr[T] = ??? } diff --git a/tests/neg/i6779.scala b/tests/neg/i6779.scala index dabf54f28db6..711361aa9d13 100644 --- a/tests/neg/i6779.scala +++ b/tests/neg/i6779.scala @@ -3,7 +3,7 @@ type G[T] type Stuff given Stuff = ??? -def [T](x: T).f(using Stuff): F[T] = ??? +extension [T](x: T) def f(using Stuff): F[T] = ??? def g1[T](x: T): F[G[T]] = x.f(using summon[Stuff]) // error diff --git a/tests/neg/i7060.scala b/tests/neg/i7060.scala index 38ab44d584a4..77f12a450a2c 100644 --- a/tests/neg/i7060.scala +++ b/tests/neg/i7060.scala @@ -12,7 +12,7 @@ object PostConditions { def res[T](using b: Box[T]): T = b.t - def [T](e: T) ensure (cond: Box[T] ?=> Boolean): T = { + extension [T](e: T) def ensure (cond: Box[T] ?=> Boolean): T = { if (cond(using Box(e))) e else throw new AssertionError("condition not fulfilled") } diff --git a/tests/neg/i7438.scala b/tests/neg/i7438.scala index a3168d28e703..32c375d55674 100644 --- a/tests/neg/i7438.scala +++ b/tests/neg/i7438.scala @@ -1,7 +1,7 @@ type Tr[+A] -inline def [A, B](tr: Tr[A]).map(f: A => B): Tr[B] = ??? +extension [A, B](tr: Tr[A]) inline def map(f: A => B): Tr[B] = ??? -def (d: Double).func: None.type => Some[Double] = ??? +extension (d: Double) def func: None.type => Some[Double] = ??? def run[A](query: None.type => Some[A]): Some[A] = ??? diff --git a/tests/neg/i8894.scala b/tests/neg/i8894.scala index 60b6937b3591..6bab91992582 100644 --- a/tests/neg/i8894.scala +++ b/tests/neg/i8894.scala @@ -2,7 +2,7 @@ trait Extractor { inline def unapplySeq(inline tn: String): Option[Seq[String]] } -transparent inline def (inline sc: StringContext).poql: Extractor = new Extractor { +extension (inline sc: StringContext) transparent inline def poql: Extractor = new Extractor { inline def unapplySeq(inline tn: String): Option[Seq[String]] = ??? // error: Implementation restriction: nested inline methods are not supported } diff --git a/tests/neg/i9185.scala b/tests/neg/i9185.scala index 9cbb751cb9c5..c4b47623ed0c 100644 --- a/tests/neg/i9185.scala +++ b/tests/neg/i9185.scala @@ -1,12 +1,12 @@ trait M[F[_]] { def pure[A](x: A): F[A] } object M { - def [A, F[A]](x: A).pure(using m: M[F]): F[A] = m.pure(x) + extension [A, F[A]](x: A) def pure(using m: M[F]): F[A] = m.pure(x) given listMonad as M[List] { def pure[A](x: A): List[A] = List(x) } given optionMonad as M[Option] { def pure[A](x: A): Option[A] = Some(x) } val value1: List[String] = "ola".pure val value2 = "ola".pure // error val value3 = extension_pure("ola") // error - def (x: Int).len: Int = x + extension (x: Int) def len: Int = x val l = "abc".len // error } \ No newline at end of file diff --git a/tests/neg/indent.scala b/tests/neg/indent.scala index 2bac87c9efb7..247698cb78b4 100644 --- a/tests/neg/indent.scala +++ b/tests/neg/indent.scala @@ -1,6 +1,6 @@ object Test { - def (x: Int).gt(y: Int) = x > y + extension (x: Int) def gt(y: Int) = x > y val y3 = if (1) max 10 gt 0 // error: end of statement expected but integer literal found // error // error // error 1 diff --git a/tests/neg/missing-implicit1.scala b/tests/neg/missing-implicit1.scala index 5c6ca73f5fa3..4bc611739f45 100644 --- a/tests/neg/missing-implicit1.scala +++ b/tests/neg/missing-implicit1.scala @@ -1,7 +1,7 @@ object testObjectInstance: trait Zip[F[_]] trait Traverse[F[_]] { - def [A, B, G[_] : Zip](fa: F[A]) traverse(f: A => G[B]): G[F[B]] + extension [A, B, G[_] : Zip](fa: F[A]) def traverse(f: A => G[B]): G[F[B]] } object instances { @@ -9,7 +9,7 @@ object testObjectInstance: given traverseList as Traverse[List] = ??? extension listExtension on [T](xs: List[T]): def second: T = xs.tail.head - def [T](xs: List[T]) first: T = xs.head + extension [T](xs: List[T]) def first: T = xs.head } def ff(using xs: Zip[Option]) = ??? diff --git a/tests/neg/missing-implicit4.scala b/tests/neg/missing-implicit4.scala index 13db252f8753..a02d543c12f8 100644 --- a/tests/neg/missing-implicit4.scala +++ b/tests/neg/missing-implicit4.scala @@ -1,7 +1,7 @@ def testLocalInstance = trait Zip[F[_]] trait Traverse[F[_]] { - def [A, B, G[_] : Zip](fa: F[A]) traverse(f: A => G[B]): G[F[B]] + extension [A, B, G[_] : Zip](fa: F[A]) def traverse(f: A => G[B]): G[F[B]] } object instances { diff --git a/tests/neg/missing-implicit5.scala b/tests/neg/missing-implicit5.scala index 4988229a1348..9e2544c66549 100644 --- a/tests/neg/missing-implicit5.scala +++ b/tests/neg/missing-implicit5.scala @@ -1,7 +1,7 @@ object testObjectInstance: trait Zip[F[_]] trait Traverse[F[_]] { - def [A, B, G[_] : Zip](fa: F[A]) traverse(f: A => G[B]): G[F[B]] + extension [A, B, G[_] : Zip](fa: F[A]) def traverse(f: A => G[B]): G[F[B]] } object instances { @@ -9,7 +9,7 @@ object testObjectInstance: given traverseList as Traverse[List] = ??? extension listExtension on [T](xs: List[T]): def second: T = xs.tail.head - def [T](xs: List[T]) first: T = xs.head + extension [T](xs: List[T]) def first: T = xs.head } List(1, 2, 3).first // error diff --git a/tests/neg/opaque-bounds.scala b/tests/neg/opaque-bounds.scala index acaebccbb6fb..2eb2d7ecc0d1 100644 --- a/tests/neg/opaque-bounds.scala +++ b/tests/neg/opaque-bounds.scala @@ -19,10 +19,10 @@ object Access { opaque type PermissionChoice = Int opaque type Permission <: Permissions & PermissionChoice = Int - def (x: Permissions) & (y: Permissions): Permissions = x & y - def (x: PermissionChoice) | (y: PermissionChoice): PermissionChoice = x | y - def (x: Permissions).is(y: Permissions) = (x & y) == y - def (x: Permissions).isOneOf(y: PermissionChoice) = (x & y) != 0 + extension (x: Permissions) def & (y: Permissions): Permissions = x & y + extension (x: PermissionChoice) def | (y: PermissionChoice): PermissionChoice = x | y + extension (x: Permissions) def is(y: Permissions) = (x & y) == y + extension (x: Permissions) def isOneOf(y: PermissionChoice) = (x & y) != 0 val NoPermission: Permission = 0 val ReadOnly: Permission = 1 diff --git a/tests/neg/override-extension_normal-methods.scala b/tests/neg/override-extension_normal-methods.scala index b54268ff95de..036458e91ac7 100644 --- a/tests/neg/override-extension_normal-methods.scala +++ b/tests/neg/override-extension_normal-methods.scala @@ -1,5 +1,5 @@ trait A { - def [T](t: T).m: String = "extension method" + extension [T](t: T) def m: String = "extension method" } trait AAA extends A { @@ -11,5 +11,5 @@ trait B { } trait BBB extends B { - override def [T](t: T).m: String = "extension method" // error: does not override + extension [T](t: T) override def m: String = "extension method" // error: does not override } \ No newline at end of file diff --git a/tests/pos/combine.scala b/tests/pos/combine.scala index 17ca3ecbb88b..fd7715b2ac19 100644 --- a/tests/pos/combine.scala +++ b/tests/pos/combine.scala @@ -1,5 +1,5 @@ trait Semigroup[A] { - def (x: A).combine(y: A): A + extension (x: A) def combine(y: A): A } given Semigroup[Int] = ??? given [A, B](using Semigroup[A], Semigroup[B]) as Semigroup[(A, B)] = ??? diff --git a/tests/pos/consume.scala b/tests/pos/consume.scala index 05662f6821ea..d17638bb84a0 100644 --- a/tests/pos/consume.scala +++ b/tests/pos/consume.scala @@ -18,13 +18,13 @@ object Test2 { object math3: trait Ord[T]: - def (x: T) > (t: T): Boolean = ??? - def (x: T) <= (t: T): Boolean = ??? + extension (x: T) def > (t: T): Boolean = ??? + extension (x: T) def <= (t: T): Boolean = ??? trait Numeric[T] extends Ord[T]: - def (x: T) + (y: T): T = ??? - def (x: T) - (y: T): T = ??? - def (x: Int).numeric: T = ??? + extension (x: T) def + (y: T): T = ??? + extension (x: T) def - (y: T): T = ??? + extension (x: Int) def numeric: T = ??? end math3 object Test3: diff --git a/tests/pos/i5773.scala b/tests/pos/i5773.scala index ddedbb07c356..0cc0bfdf9207 100644 --- a/tests/pos/i5773.scala +++ b/tests/pos/i5773.scala @@ -1,18 +1,18 @@ trait Semigroup[T] { - def (lhs: T).append(rhs: T): T + extension (lhs: T) def append(rhs: T): T } object Semigroup { implicit object stringAppend extends Semigroup[String] { - override def (lhs: String).append(rhs: String): String = lhs + rhs + extension (lhs: String) override def append(rhs: String): String = lhs + rhs } implicit def sumSemigroup[N](implicit N: Numeric[N]): Semigroup[N] = new { - override def (lhs: N).append(rhs: N): N = ??? // N.plus(lhs, rhs) + extension (lhs: N) override def append(rhs: N): N = ??? // N.plus(lhs, rhs) } implicit class SumSemiGroupDeco[N](implicit N: Numeric[N]) extends Semigroup[N] { - override def (lhs: N).append(rhs: N): N = ??? // N.plus(lhs, rhs) + extension (lhs: N) override def append(rhs: N): N = ??? // N.plus(lhs, rhs) } } diff --git a/tests/pos/i5773a.scala b/tests/pos/i5773a.scala index 801fa10f3f3f..214d0b061821 100644 --- a/tests/pos/i5773a.scala +++ b/tests/pos/i5773a.scala @@ -1,12 +1,12 @@ trait Semigroup[T] { - def (x: T).combine(y: T): T + extension (x: T) def combine(y: T): T } object Test { implicit val IntSemigroup: Semigroup[Int] = new { - def (x: Int).combine(y: Int): Int = x + y + extension (x: Int) def combine(y: Int): Int = x + y } implicit def OptionSemigroup[T: Semigroup]: Semigroup[Option[T]] = new { - def (x: Option[T]).combine(y: Option[T]): Option[T] = for { + extension (x: Option[T]) def combine(y: Option[T]): Option[T] = for { x0 <- x y0 <- y } yield x0.combine(y0) diff --git a/tests/pos/i6395.scala b/tests/pos/i6395.scala index 1e75d9ea42e7..6a80ea002bab 100644 --- a/tests/pos/i6395.scala +++ b/tests/pos/i6395.scala @@ -1,5 +1,5 @@ object Foo { - inline def (self: Int).foo(that: Int): Int = 5 - def (self: Int).bar: Int = self + extension (self: Int) inline def foo(that: Int): Int = 5 + extension (self: Int) def bar: Int = self 1.foo(2).bar } \ No newline at end of file diff --git a/tests/pos/i6705.scala b/tests/pos/i6705.scala index 40ad62cf1713..4375fd919a57 100644 --- a/tests/pos/i6705.scala +++ b/tests/pos/i6705.scala @@ -5,7 +5,7 @@ trait StringTempl { object Test { - def (x: String) shouldBe(y: String): Boolean = ??? + extension (x: String) def shouldBe(y: String): Boolean = ??? def test(tmpl: StringTempl): Unit = { tmpl.mkString shouldBe "hello" // error diff --git a/tests/pos/i6734.scala b/tests/pos/i6734.scala index d7ebc726ae79..fa7d9946ada4 100644 --- a/tests/pos/i6734.scala +++ b/tests/pos/i6734.scala @@ -1,11 +1,13 @@ -object Bug { +object Bug: - def [A, B, Z](ab: (A, B)) pipe2(f: (A, B) => Z): Z = f(ab._1, ab._2) + extension [A, B, Z](ab: (A, B)) + def pipe2(f: (A, B) => Z): Z = f(ab._1, ab._2) - def [A, B](a: A) leftErr(b: B): A = (a, b).pipe2((a, b) => a) //Did not compile before. - def [A, B](a: A) leftOk1(b: B): A = Tuple2(a, b).pipe2((a, b) => a) //Compiles - def [A, B](a: A) leftOk2(b: B): A = { - val t = (a, b) - t.pipe2((a, b) => a) //Compiles - } -} + extension [A, B](a: A) + def leftErr(b: B): A = (a, b).pipe2((a, b) => a) //Did not compile before. + extension [A, B](a: A) + def leftOk1(b: B): A = Tuple2(a, b).pipe2((a, b) => a) //Compiles + extension [A, B](a: A) + def leftOk2(b: B): A = + val t = (a, b) + t.pipe2((a, b) => a) //Compiles diff --git a/tests/pos/i6847.scala b/tests/pos/i6847.scala index 3908a38b9930..6a95e3a710ca 100644 --- a/tests/pos/i6847.scala +++ b/tests/pos/i6847.scala @@ -1,11 +1,11 @@ trait Syntax[F[_]] { - def [A](a: A) ret: F[A] + extension [A](a: A) def ret: F[A] } trait Instance[A] implicit val instanceSyntax: Syntax[Instance] = new Syntax[Instance] { - def [A](a: A) ret: Instance[A] = new Instance[A] {} + extension [A](a: A) def ret: Instance[A] = new Instance[A] {} } object Instance { diff --git a/tests/pos/i7070.scala b/tests/pos/i7070.scala index a7d4abe82840..ea08e7482a77 100644 --- a/tests/pos/i7070.scala +++ b/tests/pos/i7070.scala @@ -2,7 +2,7 @@ object Test { class C - def (str: String).foo: C ?=> Int = ??? + extension (str: String) def foo: C ?=> Int = ??? given C = ??? diff --git a/tests/pos/i7119.scala b/tests/pos/i7119.scala index 50da06cba682..3b797bebe770 100644 --- a/tests/pos/i7119.scala +++ b/tests/pos/i7119.scala @@ -1,6 +1,6 @@ class Impl -def (impl: Impl).prop(using Int) = ???//summon[Int] +extension (impl: Impl) def prop(using Int) = ???//summon[Int] def main(args: Array[String]): Unit = { diff --git a/tests/pos/i7413.scala b/tests/pos/i7413.scala index 329890ea79d2..1e90ca7e8d74 100644 --- a/tests/pos/i7413.scala +++ b/tests/pos/i7413.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions trait Fixture[A] extends Conversion[0, A] trait TestFramework[A]: - def (testName: String).in(test: Fixture[A] ?=> Unit): Unit = ??? + extension (testName: String) def in(test: Fixture[A] ?=> Unit): Unit = ??? trait Greeter: def greet(name: String): String = s"Hello $name" diff --git a/tests/pos/i7700.scala b/tests/pos/i7700.scala index f058acc5153b..6d19f0811cc4 100644 --- a/tests/pos/i7700.scala +++ b/tests/pos/i7700.scala @@ -4,7 +4,7 @@ trait Show[-A]: def show(a: A): String object Macros: - inline def (sc: StringContext).show(args: =>Any*): String = ??? + extension (sc: StringContext) inline def show(args: =>Any*): String = ??? object Show: def[A] (a: A) show(using S: Show[A]): String = S.show(a) diff --git a/tests/pos/indent.scala b/tests/pos/indent.scala index 21016a1ab864..da97b9abd7f1 100644 --- a/tests/pos/indent.scala +++ b/tests/pos/indent.scala @@ -28,7 +28,7 @@ object Test: 1 else 2 - def (x: Int).gt(y: Int) = x > y + extension (x: Int) def gt(y: Int) = x > y val y3 = if (1) max 10 gt 0 diff --git a/tests/pos/opaque-propability-xm.scala b/tests/pos/opaque-propability-xm.scala index 28b431e74a43..f77821fa0f3f 100644 --- a/tests/pos/opaque-propability-xm.scala +++ b/tests/pos/opaque-propability-xm.scala @@ -19,17 +19,17 @@ object prob { implicit val ordering: Ordering[Probability] = implicitly[Ordering[Double]] - def (p1: Probability).unary_~ : Probability = Certain - p1 - def (p1: Probability) & (p2: Probability): Probability = p1 * p2 - def (p1: Probability) | (p2: Probability): Probability = p1 + p2 - (p1 * p2) + extension (p1: Probability) def unary_~ : Probability = Certain - p1 + extension (p1: Probability) def & (p2: Probability): Probability = p1 * p2 + extension (p1: Probability) def | (p2: Probability): Probability = p1 + p2 - (p1 * p2) - def (p1: Probability).isImpossible: Boolean = p1 == Never - def (p1: Probability).isCertain: Boolean = p1 == Certain + extension (p1: Probability) def isImpossible: Boolean = p1 == Never + extension (p1: Probability) def isCertain: Boolean = p1 == Certain import scala.util.Random - def (p1: Probability).sample(r: Random = Random): Boolean = r.nextDouble <= p1 - def (p1: Probability).toDouble: Double = p1 + extension (p1: Probability) def sample(r: Random = Random): Boolean = r.nextDouble <= p1 + extension (p1: Probability) def toDouble: Double = p1 } val caughtTrain = Probability.unsafe(0.3) diff --git a/tests/pos/opaque-xm.scala b/tests/pos/opaque-xm.scala index 7d54ba1fa7b3..de2210d1ae3d 100644 --- a/tests/pos/opaque-xm.scala +++ b/tests/pos/opaque-xm.scala @@ -16,9 +16,9 @@ object opaquetypes { // Extension methods define opaque types' public APIs // This is the second way to unlift the logarithm type - def (x: Logarithm).toDouble: Double = math.exp(x) - def (x: Logarithm) + (y: Logarithm) = Logarithm(math.exp(x) + math.exp(y)) - def (x: Logarithm) * (y: Logarithm): Logarithm = Logarithm(x + y) + extension (x: Logarithm) def toDouble: Double = math.exp(x) + extension (x: Logarithm) def + (y: Logarithm) = Logarithm(math.exp(x) + math.exp(y)) + extension (x: Logarithm) def * (y: Logarithm): Logarithm = Logarithm(x + y) } } object usesites { diff --git a/tests/pos/postconditions.scala b/tests/pos/postconditions.scala index ce0611d558c2..90117c17b207 100644 --- a/tests/pos/postconditions.scala +++ b/tests/pos/postconditions.scala @@ -3,7 +3,7 @@ object PostConditions: def result[T](using r: WrappedResult[T]): T = r - def [T](x: T) ensuring (condition: WrappedResult[T] ?=> Boolean): T = + extension [T](x: T) def ensuring (condition: WrappedResult[T] ?=> Boolean): T = given WrappedResult[T] = x assert(condition) x diff --git a/tests/pos/reference/delegates.scala b/tests/pos/reference/delegates.scala index 0df99c954bbf..691e40c0063f 100644 --- a/tests/pos/reference/delegates.scala +++ b/tests/pos/reference/delegates.scala @@ -1,25 +1,25 @@ class Common: trait Ord[T]: - def (x: T).compareTo(y: T): Int - def (x: T) < (y: T) = x.compareTo(y) < 0 - def (x: T) > (y: T) = x.compareTo(y) > 0 + extension (x: T) def compareTo(y: T): Int + extension (x: T) def < (y: T) = x.compareTo(y) < 0 + extension (x: T) def > (y: T) = x.compareTo(y) > 0 trait Convertible[From, To]: - def (x: From).convert: To + extension (x: From) def convert: To trait SemiGroup[T]: - def (x: T).combine(y: T): T + extension (x: T) def combine(y: T): T trait Monoid[T] extends SemiGroup[T]: def unit: T trait Functor[F[_]]: - def [A, B](x: F[A]) map (f: A => B): F[B] + extension [A, B](x: F[A]) def map (f: A => B): F[B] trait Monad[F[_]] extends Functor[F]: - def [A, B](x: F[A]) flatMap (f: A => F[B]): F[B] - def [A, B](x: F[A]) map (f: A => B) = x.flatMap(f `andThen` pure) + extension [A, B](x: F[A]) def flatMap (f: A => F[B]): F[B] + extension [A, B](x: F[A]) def map (f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] end Common @@ -27,11 +27,11 @@ end Common object Instances extends Common: given intOrd as Ord[Int]: - def (x: Int).compareTo(y: Int) = + extension (x: Int) def compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 given listOrd[T](using Ord[T]) as Ord[List[T]]: - def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys) match + extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys) match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -50,13 +50,13 @@ object Instances extends Common: def third = xs.tail.tail.head given listMonad as Monad[List]: - def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] = + extension [A, B](xs: List[A]) def flatMap (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) given readerMonad[Ctx] as Monad[[X] =>> Ctx => X]: - def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B = + extension [A, B](r: Ctx => A) def flatMap (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x @@ -65,7 +65,7 @@ object Instances extends Common: xs.reduceLeft((x, y) => if (x < y) y else x) def descending[T](using asc: Ord[T]): Ord[T] = new Ord[T]: - def (x: T).compareTo(y: T) = asc.extension_compareTo(y)(x) + extension (x: T) def compareTo(y: T) = asc.extension_compareTo(y)(x) def minimum[T](xs: List[T])(using Ord[T]) = maximum(xs)(using descending) @@ -88,14 +88,14 @@ object Instances extends Common: trait TastyAPI: type Symbol trait SymDeco: - def (sym: Symbol).name: String + extension (sym: Symbol) def name: String def symDeco: SymDeco given SymDeco = symDeco object TastyImpl extends TastyAPI: type Symbol = String val symDeco = new SymDeco: - def (sym: Symbol).name = sym + extension (sym: Symbol) def name = sym class D[T] @@ -142,11 +142,11 @@ end PostConditions object AnonymousInstances extends Common: given Ord[Int]: - def (x: Int).compareTo(y: Int) = + extension (x: Int) def compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 given [T: Ord] as Ord[List[T]]: - def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match + extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys).match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -163,10 +163,10 @@ object AnonymousInstances extends Common: def second = xs.tail.head given [From, To](using c: Convertible[From, To]) as Convertible[List[From], List[To]]: - def (x: List[From]).convert: List[To] = x.map(c.extension_convert) + extension (x: List[From]) def convert: List[To] = x.map(c.extension_convert) given Monoid[String]: - def (x: String).combine(y: String): String = x.concat(y) + extension (x: String) def combine(y: String): String = x.concat(y) def unit: String = "" def sum[T: Monoid](xs: List[T]): T = @@ -175,11 +175,11 @@ end AnonymousInstances object Implicits extends Common: implicit object IntOrd extends Ord[Int]: - def (x: Int).compareTo(y: Int) = + extension (x: Int) def compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 class ListOrd[T: Ord] extends Ord[List[T]]: - def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match + extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys).match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -190,7 +190,7 @@ object Implicits extends Common: class given_Convertible_List_List[From, To](implicit c: Convertible[From, To]) extends Convertible[List[From], List[To]]: - def (x: List[From]).convert: List[To] = x.map(c.extension_convert) + extension (x: List[From]) def convert: List[To] = x.map(c.extension_convert) implicit def given_Convertible_List_List[From, To](implicit c: Convertible[From, To]) : Convertible[List[From], List[To]] = new given_Convertible_List_List[From, To] @@ -200,7 +200,7 @@ object Implicits extends Common: xs.reduceLeft((x, y) => if (x < y) y else x) def descending[T](implicit asc: Ord[T]): Ord[T] = new Ord[T]: - def (x: T).compareTo(y: T) = asc.extension_compareTo(y)(x) + extension (x: T) def compareTo(y: T) = asc.extension_compareTo(y)(x) def minimum[T](xs: List[T])(implicit cmp: Ord[T]) = maximum(xs)(descending) diff --git a/tests/pos/reference/opaque.scala b/tests/pos/reference/opaque.scala index 69ce019b6aee..58340efceb7f 100644 --- a/tests/pos/reference/opaque.scala +++ b/tests/pos/reference/opaque.scala @@ -36,10 +36,10 @@ object Access { opaque type PermissionChoice = Int opaque type Permission <: Permissions & PermissionChoice = Int - def (x: Permissions) & (y: Permissions): Permissions = x & y - def (x: PermissionChoice) | (y: PermissionChoice): PermissionChoice = x | y - def (x: Permissions).is(y: Permissions) = (x & y) == y - def (x: Permissions).isOneOf(y: PermissionChoice) = (x & y) != 0 + extension (x: Permissions) def & (y: Permissions): Permissions = x & y + extension (x: PermissionChoice) def | (y: PermissionChoice): PermissionChoice = x | y + extension (x: Permissions) def is(y: Permissions) = (x & y) == y + extension (x: Permissions) def isOneOf(y: PermissionChoice) = (x & y) != 0 val NoPermission: Permission = 0 val ReadOnly: Permission = 1 diff --git a/tests/pos/toplevel-opaque-xm/Logarithm_1.scala b/tests/pos/toplevel-opaque-xm/Logarithm_1.scala index 884e29a1ad25..436097fee478 100644 --- a/tests/pos/toplevel-opaque-xm/Logarithm_1.scala +++ b/tests/pos/toplevel-opaque-xm/Logarithm_1.scala @@ -16,8 +16,8 @@ object Logarithm { given AnyRef { // This is the second way to unlift the logarithm type - def (x: Logarithm).toDouble: Double = math.exp(x) - def (x: Logarithm) + (y: Logarithm) = Logarithm(math.exp(x) + math.exp(y)) - def (x: Logarithm) * (y: Logarithm): Logarithm = Logarithm(x + y) + extension (x: Logarithm) def toDouble: Double = math.exp(x) + extension (x: Logarithm) def + (y: Logarithm) = Logarithm(math.exp(x) + math.exp(y)) + extension (x: Logarithm) def * (y: Logarithm): Logarithm = Logarithm(x + y) } } diff --git a/tests/pos/typeclass-encoding3.scala b/tests/pos/typeclass-encoding3.scala index 37a4dd2670a0..070c52f4af4f 100644 --- a/tests/pos/typeclass-encoding3.scala +++ b/tests/pos/typeclass-encoding3.scala @@ -324,18 +324,18 @@ object Test { case class Circle(radius: Double) implicit object RectangleArea - def (x: Rectangle).area: Double = x.width * self.height + extension (x: Rectangle) def area: Double = x.width * self.height trait HasArea[T] - def (x: T).area: Double + extension (x: T) def area: Double implicit object CircleHasArea extends HasArea[Circle] - def (x: Circle).area = x.radius * 2 * math.Pi + extension (x: Circle) def area = x.radius * 2 * math.Pi --------------------------------------------------------------------------------- trait SemiGroup[T] - def (x: T).combine(y: T): T + extension (x: T) def combine(y: T): T trait Monoid[T] extends SemiGroup[T] def unit: T @@ -348,23 +348,23 @@ object Test { // Class `Str` is not definable implicit object StringMonoid extends Monoid[String] - def (x: String).combine(y: String): String = x + y + extension (x: String) def combine(y: String): String = x + y def unit = "" trait Ord[T] - def (x: T).compareTo(y: T): Int - def (x: T) < (that: T) = x.compareTo(y) < 0 - def (x: T) > (that: T) = x.compareTo(y) > 0 + extension (x: T) def compareTo(y: T): Int + extension (x: T) def < (that: T) = x.compareTo(y) < 0 + extension (x: T) def > (that: T) = x.compareTo(y) > 0 val minimum: T } implicit object IntOrd { - def (x: Int).compareTo(y: Int) = + extension (x: Int) def compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 } implicit class ListOrd[T: Ord] { - def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys) match + extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys) match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -381,19 +381,19 @@ object Test { --------------------------------------------------------------------------------- trait Functor[F[_]] - def (x: F[A]).map[A, B](f: A => B): F[B] + extension (x: F[A]) def map[A, B](f: A => B): F[B] trait Monad[F[_]] extends Functor[F] - def (x: F[A]).flatMap[A, B](f: A => F[B]): F[B] - def (x: F[A]).map[A, B](f: A => B) = x.flatMap(f `andThen` pure) + extension (x: F[A]) def flatMap[A, B](f: A => F[B]): F[B] + extension (x: F[A]) def map[A, B](f: A => B) = x.flatMap(f `andThen` pure) def pure[A]: F[A] implicit object ListMonad extends Monad[List] - def (xs: List[A]).flatMap[A, B](f: A => List[B]): List[B] = xs.flatMap(f) + extension (xs: List[A]) def flatMap[A, B](f: A => List[B]): List[B] = xs.flatMap(f) def pure[A]: List[A] = List.Nil implicit class ReaderMonad[Ctx] extends Monad[Ctx => _] - def (r: Ctx => A).flatMap[A, B](f: A => Ctx => B): Ctx => B = + extension (r: Ctx => A) def flatMap[A, B](f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x diff --git a/tests/printing/i620.check b/tests/printing/i620.check index ab21ddbb0d87..1dedb6d375a7 100644 --- a/tests/printing/i620.check +++ b/tests/printing/i620.check @@ -11,7 +11,7 @@ package O { protected[A] def f: Int = 0 def g: Int = 0 def g1(t: Int): Int = 0 - extension (c: D.this.C) @_root_.scala.annotation.infix() def g1: Int = 0 + extension (c: D.this.C) def g1: Int = 0 } private[D] class E() extends Object() {} private[this] class F() extends Object() {} diff --git a/tests/printing/i620.scala b/tests/printing/i620.scala index 21c9254f22d1..69c528c1e1e1 100644 --- a/tests/printing/i620.scala +++ b/tests/printing/i620.scala @@ -13,7 +13,7 @@ class D { protected[A] def f: Int = 0 def g: Int = 0 def g1(t: Int) = 0 - def (c: C) g1 = 0 + extension (c: C) def g1 = 0 } private[D] class E diff --git a/tests/run-macros/f-interpolator-neg/Macros_1.scala b/tests/run-macros/f-interpolator-neg/Macros_1.scala index 2c76709d6704..72d67301cb2d 100644 --- a/tests/run-macros/f-interpolator-neg/Macros_1.scala +++ b/tests/run-macros/f-interpolator-neg/Macros_1.scala @@ -6,7 +6,7 @@ import scala.language.implicitConversions object TestFooErrors { // Defined in tests implicit object StringContextOps { - inline def (inline ctx: StringContext).foo(inline args: Any*): List[(Boolean, Int, Int, Int, String)] = ${ Macro.fooErrors('ctx, 'args) } + extension (inline ctx: StringContext) inline def foo(inline args: Any*): List[(Boolean, Int, Int, Int, String)] = ${ Macro.fooErrors('ctx, 'args) } } } diff --git a/tests/run-macros/i6201/macro_1.scala b/tests/run-macros/i6201/macro_1.scala index ffbb1eb244b2..0fe7b233e51f 100644 --- a/tests/run-macros/i6201/macro_1.scala +++ b/tests/run-macros/i6201/macro_1.scala @@ -1,6 +1,6 @@ import scala.quoted._ -inline def (inline x: String) strip: String = +extension (inline x: String) inline def strip: String = ${ stripImpl('x) } def stripImpl(x: Expr[String])(using qctx: QuoteContext) : Expr[String] = diff --git a/tests/run-macros/i6253-b/quoted_1.scala b/tests/run-macros/i6253-b/quoted_1.scala index 593e7da59ae4..c6c4155f9516 100644 --- a/tests/run-macros/i6253-b/quoted_1.scala +++ b/tests/run-macros/i6253-b/quoted_1.scala @@ -3,7 +3,7 @@ import scala.quoted._ object Macros { - inline def (inline self: StringContext) xyz(args: => String*): String = ${impl('self, 'args)} + extension (inline self: StringContext) inline def xyz(args: => String*): String = ${impl('self, 'args)} private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using QuoteContext): Expr[String] = { self match { diff --git a/tests/run-macros/i6253-c/quoted_1.scala b/tests/run-macros/i6253-c/quoted_1.scala index c7a4a08e3256..a801b6b97ee2 100644 --- a/tests/run-macros/i6253-c/quoted_1.scala +++ b/tests/run-macros/i6253-c/quoted_1.scala @@ -3,8 +3,8 @@ import scala.quoted._ object Macros { - // Should be: inline def (inline self: StringContext) ... - inline def (self: => StringContext) xyz(args: => String*): String = ${impl('self, 'args)} + // Should be: extension (inline self: StringContext) inline def ... + extension (self: => StringContext) inline def xyz(args: => String*): String = ${impl('self, 'args)} private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using QuoteContext): Expr[String] = { self match { diff --git a/tests/run-macros/i6253/quoted_1.scala b/tests/run-macros/i6253/quoted_1.scala index 596db02e2fbc..3f36d0f69ab1 100644 --- a/tests/run-macros/i6253/quoted_1.scala +++ b/tests/run-macros/i6253/quoted_1.scala @@ -3,7 +3,7 @@ import scala.quoted._ object Macros { - inline def (inline self: StringContext) xyz(args: => String*): String = ${impl('self, 'args)} + extension (inline self: StringContext) inline def xyz(args: => String*): String = ${impl('self, 'args)} private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using QuoteContext): Expr[String] = { self match { diff --git a/tests/run-macros/i6270/Macro_1.scala b/tests/run-macros/i6270/Macro_1.scala index 40372b74107c..581dee3a9750 100644 --- a/tests/run-macros/i6270/Macro_1.scala +++ b/tests/run-macros/i6270/Macro_1.scala @@ -2,7 +2,7 @@ import scala.quoted._ import scala.quoted.show.SyntaxHighlight.ANSI object api { - inline def (inline x: String) reflect : String = + extension (inline x: String) inline def reflect : String = ${ reflImpl('x) } private def reflImpl(x: Expr[String])(using qctx: QuoteContext) : Expr[String] = { @@ -10,7 +10,7 @@ object api { Expr(x.show) } - inline def (x: => String) reflectColor : String = + extension (x: => String) inline def reflectColor : String = ${ reflImplColor('x) } private def reflImplColor(x: Expr[String])(using qctx: QuoteContext) : Expr[String] = { diff --git a/tests/run-macros/i6772/Macro_1.scala b/tests/run-macros/i6772/Macro_1.scala index 407b97ef87e6..cb8952618d43 100644 --- a/tests/run-macros/i6772/Macro_1.scala +++ b/tests/run-macros/i6772/Macro_1.scala @@ -7,7 +7,7 @@ object Macros { def mImpl()(using QuoteContext): Expr[Any] = List(Expr(1), Expr(2), Expr(3)).toExprOfList - def [T](list: List[Expr[T]]).toExprOfList(using Type[T], QuoteContext): Expr[List[T]] = '{ + extension [T](list: List[Expr[T]]) def toExprOfList(using Type[T], QuoteContext): Expr[List[T]] = '{ val buff = List.newBuilder[T] ${ Expr.block(list.map(v => '{ buff += $v }), '{ buff.result() }) } } diff --git a/tests/run-macros/i8007/Macro_3.scala b/tests/run-macros/i8007/Macro_3.scala index 7a01837eedca..a6e54a2bfc03 100644 --- a/tests/run-macros/i8007/Macro_3.scala +++ b/tests/run-macros/i8007/Macro_3.scala @@ -73,7 +73,7 @@ object Eq { } object Macro3 { - inline def [T](x: =>T) === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) + extension [T](x: =>T) inline def === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) implicit inline def eqGen[T]: Eq[T] = ${ Eq.derived[T] } } \ No newline at end of file diff --git a/tests/run-macros/quote-elide-prefix/quoted_1.scala b/tests/run-macros/quote-elide-prefix/quoted_1.scala index c7f86bccd281..cdad817e2a66 100644 --- a/tests/run-macros/quote-elide-prefix/quoted_1.scala +++ b/tests/run-macros/quote-elide-prefix/quoted_1.scala @@ -3,7 +3,7 @@ import scala.quoted._ object Macro { // By name StringContext is used to elide the prefix - inline def (inline sc: StringContext) ff (args: => Any*): String = ${ Macro.impl('sc, 'args) } + extension (inline sc: StringContext) inline def ff (args: => Any*): String = ${ Macro.impl('sc, 'args) } def impl(sc: Expr[StringContext], args: Expr[Seq[Any]])(using QuoteContext): Expr[String] = '{ $args.mkString } } diff --git a/tests/run-macros/quote-matcher-string-interpolator-2/quoted_1.scala b/tests/run-macros/quote-matcher-string-interpolator-2/quoted_1.scala index a6ff0769dc4e..f5acbfaca656 100644 --- a/tests/run-macros/quote-matcher-string-interpolator-2/quoted_1.scala +++ b/tests/run-macros/quote-matcher-string-interpolator-2/quoted_1.scala @@ -4,7 +4,7 @@ import scala.quoted._ object Macros { - inline def (inline self: StringContext) xyz(inline args: String*): String = ${impl('self, 'args)} + extension (inline self: StringContext) inline def xyz(inline args: String*): String = ${impl('self, 'args)} private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using QuoteContext): Expr[String] = { (self, args) match { diff --git a/tests/run-macros/quote-matcher-string-interpolator-3/quoted_1.scala b/tests/run-macros/quote-matcher-string-interpolator-3/quoted_1.scala index 8a0c5d182bc1..1e4f3d8d1b44 100644 --- a/tests/run-macros/quote-matcher-string-interpolator-3/quoted_1.scala +++ b/tests/run-macros/quote-matcher-string-interpolator-3/quoted_1.scala @@ -4,7 +4,7 @@ import scala.quoted._ object Macros { - inline def (inline self: StringContext) S(args: => String*): String = ${impl('self, 'args)} + extension (inline self: StringContext) inline def S(args: => String*): String = ${impl('self, 'args)} private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using QuoteContext): Expr[String] = { self match { diff --git a/tests/run-macros/quote-matcher-string-interpolator/quoted_1.scala b/tests/run-macros/quote-matcher-string-interpolator/quoted_1.scala index 128dd17a4679..385f1c00c26c 100644 --- a/tests/run-macros/quote-matcher-string-interpolator/quoted_1.scala +++ b/tests/run-macros/quote-matcher-string-interpolator/quoted_1.scala @@ -4,7 +4,7 @@ import scala.quoted._ object Macros { - inline def (inline self: StringContext) xyz(args: => String*): String = ${impl('self, 'args)} + extension (inline self: StringContext) inline def xyz(args: => String*): String = ${impl('self, 'args)} private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using QuoteContext): Expr[String] = { self match { diff --git a/tests/run-macros/reflect-inline/assert_1.scala b/tests/run-macros/reflect-inline/assert_1.scala index 54f4cc444863..a41e0bd7a81e 100644 --- a/tests/run-macros/reflect-inline/assert_1.scala +++ b/tests/run-macros/reflect-inline/assert_1.scala @@ -1,7 +1,7 @@ import scala.quoted._ object api { - inline def (inline x: String) stripMargin: String = + extension (inline x: String) inline def stripMargin: String = ${ stripImpl('x) } private def stripImpl(x: Expr[String])(using qctx: QuoteContext): Expr[String] = diff --git a/tests/run-macros/reflect-sourceCode/Macro_1.scala b/tests/run-macros/reflect-sourceCode/Macro_1.scala index 8be8239cae25..1dbcc0cfa6d5 100644 --- a/tests/run-macros/reflect-sourceCode/Macro_1.scala +++ b/tests/run-macros/reflect-sourceCode/Macro_1.scala @@ -1,7 +1,7 @@ import scala.quoted._ object api { - inline def [T](x: => T).reflect: String = + extension [T](x: => T) inline def reflect: String = ${ reflImpl('x) } private def reflImpl[T](x: Expr[T])(implicit qctx: QuoteContext): Expr[String] = { diff --git a/tests/run-macros/string-context-implicits/Macro_1.scala b/tests/run-macros/string-context-implicits/Macro_1.scala index 976cb948b542..877686ba2d9e 100644 --- a/tests/run-macros/string-context-implicits/Macro_1.scala +++ b/tests/run-macros/string-context-implicits/Macro_1.scala @@ -1,7 +1,7 @@ import scala.quoted._ -inline def (sc: StringContext) showMe(inline args: Any*): String = ${ showMeExpr('sc, 'args) } +extension (sc: StringContext) inline def showMe(inline args: Any*): String = ${ showMeExpr('sc, 'args) } private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(using qctx: QuoteContext): Expr[String] = { argsExpr match { diff --git a/tests/run-macros/tasty-string-interpolation-reporter-test/Macros_1.scala b/tests/run-macros/tasty-string-interpolation-reporter-test/Macros_1.scala index 5416ba8e8518..ac80e197c724 100644 --- a/tests/run-macros/tasty-string-interpolation-reporter-test/Macros_1.scala +++ b/tests/run-macros/tasty-string-interpolation-reporter-test/Macros_1.scala @@ -6,14 +6,14 @@ import scala.language.implicitConversions object Foo { implicit object StringContextOps { - inline def (inline ctx: StringContext) foo (inline args: Any*): String = ${ Macro.foo('ctx, 'args) } + extension (inline ctx: StringContext) inline def foo (inline args: Any*): String = ${ Macro.foo('ctx, 'args) } } } object TestFooErrors { // Defined in tests implicit object StringContextOps { - inline def (inline ctx: StringContext) foo (inline args: Any*): List[(Int, Int, Int, String)] = ${ Macro.fooErrors('ctx, 'args) } + extension (inline ctx: StringContext) inline def foo (inline args: Any*): List[(Int, Int, Int, String)] = ${ Macro.fooErrors('ctx, 'args) } } } diff --git a/tests/run-macros/xml-interpolation-3/XmlQuote_1.scala b/tests/run-macros/xml-interpolation-3/XmlQuote_1.scala index 6f6eb5b058d7..d9b312665913 100644 --- a/tests/run-macros/xml-interpolation-3/XmlQuote_1.scala +++ b/tests/run-macros/xml-interpolation-3/XmlQuote_1.scala @@ -8,7 +8,7 @@ case class Xml(parts: String, args: List[Any]) object XmlQuote { implicit object SCOps { - inline def (inline ctx: StringContext) xml (args: => Any*): Xml = + extension (inline ctx: StringContext) inline def xml (args: => Any*): Xml = ${XmlQuote.impl('ctx, 'args)} } diff --git a/tests/run-macros/xml-interpolation-4/Macros_1.scala b/tests/run-macros/xml-interpolation-4/Macros_1.scala index e5afa0c4c517..07772047356b 100644 --- a/tests/run-macros/xml-interpolation-4/Macros_1.scala +++ b/tests/run-macros/xml-interpolation-4/Macros_1.scala @@ -6,7 +6,7 @@ import scala.language.implicitConversions object XmlQuote { implicit object SCOps { - inline def (ctx: => StringContext) xml (args: => (Scope ?=> Any)*)(using Scope): String = + extension (ctx: => StringContext) inline def xml (args: => (Scope ?=> Any)*)(using Scope): String = ${XmlQuote.impl('ctx, 'args, '{implicitly[Scope]})} } diff --git a/tests/run-macros/xml-interpolation-5/Macros_1.scala b/tests/run-macros/xml-interpolation-5/Macros_1.scala index 887ec2007f82..e0a948a75135 100644 --- a/tests/run-macros/xml-interpolation-5/Macros_1.scala +++ b/tests/run-macros/xml-interpolation-5/Macros_1.scala @@ -19,10 +19,10 @@ object XmlQuote { opaque type StringContext = scala.StringContext def apply(sc: scala.StringContext): StringContext = sc } - transparent inline def (inline ctx: StringContext).xml: SCOps.StringContext = SCOps(ctx) - inline def (inline ctx: SCOps.StringContext).apply(inline args: Any*): Xml = + extension (inline ctx: StringContext) transparent inline def xml: SCOps.StringContext = SCOps(ctx) + extension (inline ctx: SCOps.StringContext) inline def apply(inline args: Any*): Xml = ${XmlQuote.impl('ctx, 'args)} - // inline def (inline ctx: SCOps.StringContext).unapplySeq(...): Xml = ... + // extension (inline ctx: SCOps.StringContext) inline def unapplySeq(...): Xml = ... def impl(receiver: Expr[SCOps.StringContext], args: Expr[Seq[Any]])(using QuoteContext): Expr[Xml] = { diff --git a/tests/run-macros/xml-interpolation-6/Macros_1.scala b/tests/run-macros/xml-interpolation-6/Macros_1.scala index b6913f3b8ce1..565857c3b6fb 100644 --- a/tests/run-macros/xml-interpolation-6/Macros_1.scala +++ b/tests/run-macros/xml-interpolation-6/Macros_1.scala @@ -19,10 +19,10 @@ object XmlQuote { opaque type StringContext = scala.StringContext def apply(sc: scala.StringContext): StringContext = sc } - inline def (inline ctx: StringContext).xml: SCOps.StringContext = SCOps(ctx) - inline def (inline ctx: SCOps.StringContext).apply(inline args: Any*): Xml = + extension (inline ctx: StringContext) inline def xml: SCOps.StringContext = SCOps(ctx) + extension (inline ctx: SCOps.StringContext) inline def apply(inline args: Any*): Xml = ${XmlQuote.impl('ctx, 'args)} - // inline def (inline ctx: SCOps.StringContext).unapplySeq(...): Xml = ... + // extension (inline ctx: SCOps.StringContext) inline def unapplySeq(...): Xml = ... def impl(receiver: Expr[SCOps.StringContext], args: Expr[Seq[Any]])(using QuoteContext): Expr[Xml] = { diff --git a/tests/run-macros/xml-interpolation-7/Macros_1.scala b/tests/run-macros/xml-interpolation-7/Macros_1.scala index 97149956bcd0..5977d5c4e498 100644 --- a/tests/run-macros/xml-interpolation-7/Macros_1.scala +++ b/tests/run-macros/xml-interpolation-7/Macros_1.scala @@ -17,12 +17,12 @@ object XmlQuote { // } object XMLOps { opaque type StringContext = scala.StringContext - def (ctx: scala.StringContext).xml: StringContext = ctx + extension (ctx: scala.StringContext) def xml: StringContext = ctx } - inline def (inline ctx: XMLOps.StringContext).apply(inline args: Any*): Xml = + extension (inline ctx: XMLOps.StringContext) inline def apply(inline args: Any*): Xml = ${XmlQuote.impl('ctx, 'args)} - // inline def (inline ctx: SCOps.StringContext).unapplySeq(...): Xml = ... + // extension (inline ctx: SCOps.StringContext) inline def unapplySeq(...): Xml = ... def impl(receiver: Expr[XMLOps.StringContext], args: Expr[Seq[Any]])(using QuoteContext): Expr[Xml] = { diff --git a/tests/run/Pouring.scala b/tests/run/Pouring.scala index 9097901f97de..6f4611af8bfc 100644 --- a/tests/run/Pouring.scala +++ b/tests/run/Pouring.scala @@ -8,7 +8,7 @@ class Pouring(capacity: Vector[Int]): case Fill(g) => content.updated(g, capacity(g)) case Pour(from, to) => val amount = content(from) min (capacity(to) - content(to)) - def (s: Content).adjust(g: Glass, delta: Int) = s.updated(g, s(g) + delta) + extension (s: Content) def adjust(g: Glass, delta: Int) = s.updated(g, s(g) + delta) content.adjust(from, -amount).adjust(to, amount) case Empty(glass: Glass) diff --git a/tests/run/eq-xmethod.scala b/tests/run/eq-xmethod.scala index 5231063bf62b..86805092e41a 100644 --- a/tests/run/eq-xmethod.scala +++ b/tests/run/eq-xmethod.scala @@ -6,7 +6,7 @@ object Test extends App { class N object N extends N - def (x: N)._eq(y: R | N) = y eq N + extension (x: N) def _eq(y: R | N) = y eq N val r1, r2 = new R assert(r1 _eq r1) diff --git a/tests/run/exports.scala b/tests/run/exports.scala index c796f330b9c7..c82143192152 100644 --- a/tests/run/exports.scala +++ b/tests/run/exports.scala @@ -12,7 +12,7 @@ object Test extends App { class Scanner { def scan() = println("scanning") - def (x: Any).scanned = scan() + extension (x: Any) def scanned = scan() } object Scanner extends Scanner diff --git a/tests/run/extension-methods.scala b/tests/run/extension-methods.scala index 5d6f2713380d..649c2c6aea93 100644 --- a/tests/run/extension-methods.scala +++ b/tests/run/extension-methods.scala @@ -1,35 +1,36 @@ object Test extends App { - def (x: Int).em: Boolean = x > 0 + extension (x: Int) def em: Boolean = x > 0 assert(1.em == extension_em(1)) case class Circle(x: Double, y: Double, radius: Double) - def (c: Circle).circumference: Double = c.radius * math.Pi * 2 + extension (c: Circle) def circumference: Double = c.radius * math.Pi * 2 val circle = new Circle(1, 1, 2.0) assert(circle.circumference == extension_circumference(circle)) - def (xs: Seq[String]).longestStrings: Seq[String] = { + extension (xs: Seq[String]) def longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - def [T](xs: Seq[T]) second = xs.tail.head + extension [T](xs: Seq[T]) def second = xs.tail.head assert(names.longestStrings.second == "world") - def [T](xs: List[List[T]]) flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) + extension [T](xs: List[List[T]]) + def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) assert(List(names, List("!")).flattened == names :+ "!") assert(Nil.flattened == Nil) trait SemiGroup[T] { - def (x: T).combine(y: T): T + extension (x: T) def combine(y: T): T } trait Monoid[T] extends SemiGroup[T] { def unit: T @@ -37,7 +38,7 @@ object Test extends App { // An instance declaration: implicit object StringMonoid extends Monoid[String] { - def (x: String).combine(y: String): String = x.concat(y) + extension (x: String) def combine(y: String): String = x.concat(y) def unit: String = "" } @@ -48,14 +49,14 @@ object Test extends App { println(sum(names)) trait Ord[T] { - def (x: T).compareTo(y: T): Int - def (x: T) < (y: T) = x.compareTo(y) < 0 - def (x: T) > (y: T) = x.compareTo(y) > 0 + extension (x: T) def compareTo(y: T): Int + extension (x: T) def < (y: T) = x.compareTo(y) < 0 + extension (x: T) def > (y: T) = x.compareTo(y) > 0 val minimum: T } implicit object IntOrd extends Ord[Int] { - def (x: Int).compareTo(y: Int) = + extension (x: Int) def compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 val minimum = Int.MinValue } @@ -84,7 +85,7 @@ object Test extends App { println(max(List(1, 2, 3), List(2))) trait Functor[F[_]] { - def [A, B](x: F[A]) map (f: A => B): F[B] + extension [A, B](x: F[A]) def map (f: A => B): F[B] } trait Monad[F[_]] extends Functor[F] { @@ -98,14 +99,14 @@ object Test extends App { } implicit object ListMonad extends Monad[List] { - def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] = + extension [A, B](xs: List[A]) def flatMap (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) } class ReaderMonad[Ctx] extends Monad[[X] =>> Ctx => X] { - def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B = + extension [A, B](r: Ctx => A) def flatMap (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x diff --git a/tests/run/extmethod-overload.scala b/tests/run/extmethod-overload.scala index 7ae67268957c..42f1109492fc 100644 --- a/tests/run/extmethod-overload.scala +++ b/tests/run/extmethod-overload.scala @@ -22,13 +22,13 @@ object Test extends App { // Test with extension methods in given object object test1 { - extension Foo: - def (x: Int) |+| (y: Int) = x + y - def (x: Int) |+| (y: String) = x + y.length + extension (x: Int): + def |+| (y: Int) = x + y + def |+| (y: String) = x + y.length - def [T](xs: List[T]) +++ (ys: List[T]): List[T] = xs ++ ys ++ ys - def [T](xs: List[T]) +++ (ys: Iterator[T]): List[T] = xs ++ ys ++ ys - end Foo + extension [T](xs: List[T]): + def +++ (ys: List[T]): List[T] = xs ++ ys ++ ys + def +++ (ys: Iterator[T]): List[T] = xs ++ ys ++ ys assert((1 |+| 2) == 3) assert((1 |+| "2") == 2) @@ -41,7 +41,7 @@ object Test extends App { // Test with imported extension methods object test2 { - import test1.Foo._ + import test1._ assert((1 |+| 2) == 3) assert((1 |+| "2") == 2) @@ -55,11 +55,11 @@ object Test extends App { // Test with given extension methods coming from base class object test3 { class Foo { - def (x: Int) |+| (y: Int) = x + y - def (x: Int) |+| (y: String) = x + y.length + extension (x: Int) def |+| (y: Int) = x + y + extension (x: Int) def |+| (y: String) = x + y.length - def [T](xs: List[T]) +++ (ys: List[T]): List[T] = xs ++ ys ++ ys - def [T](xs: List[T]) +++ (ys: Iterator[T]): List[T] = xs ++ ys ++ ys + extension [T](xs: List[T]) def +++ (ys: List[T]): List[T] = xs ++ ys ++ ys + extension [T](xs: List[T]) def +++ (ys: Iterator[T]): List[T] = xs ++ ys ++ ys } given Bar as Foo @@ -88,13 +88,13 @@ object Test extends App { class C { def xx (x: Any) = 2 } - def (c: C).xx(x: Int) = 1 + extension (c: C) def xx(x: Int) = 1 val c = new C assert(c.xx(1) == 2) // member method takes precedence object D { - def (x: Int).yy(y: Int) = x + y + extension (x: Int) def yy(y: Int) = x + y } extension on (x: Int) { @@ -114,8 +114,8 @@ object Test extends App { def b: Long = a } - def (rectangle: Rectangle).area: Long = 0 - def (square: Square).area: Long = square.a * square.a + extension (rectangle: Rectangle) def area: Long = 0 + extension (square: Square) def area: Long = square.a * square.a val rectangles = List(GenericRectangle(2, 3), Square(5)) val areas = rectangles.map(_.area) assert(areas.sum == 0) diff --git a/tests/run/extmethods2.scala b/tests/run/extmethods2.scala index 60eda2d80bea..ac423a3b395f 100644 --- a/tests/run/extmethods2.scala +++ b/tests/run/extmethods2.scala @@ -4,8 +4,8 @@ object Test extends App { given stringListOps(using TC) as Object { type T = List[String] - def (x: T).foo(y: T) = (x ++ y, summon[TC]) - def (x: T).bar(y: Int) = (x(0)(y), summon[TC]) + extension (x: T) def foo(y: T) = (x ++ y, summon[TC]) + extension (x: T) def bar(y: Int) = (x(0)(y), summon[TC]) } def test(using TC) = { diff --git a/tests/run/i5110/quoted_1.scala b/tests/run/i5110/quoted_1.scala index 9be1181f3dcf..3648a9f38a38 100644 --- a/tests/run/i5110/quoted_1.scala +++ b/tests/run/i5110/quoted_1.scala @@ -8,9 +8,9 @@ object Macro { throw new Boom } - inline def (boom: Bomb) foo(): Unit = () + extension (boom: Bomb) inline def foo(): Unit = () // By name Boom is used to elide the evaluation of the prefix - inline def (boom: => Bomb) bar(): Unit = () + extension (boom: => Bomb) inline def bar(): Unit = () } diff --git a/tests/run/i5606.scala b/tests/run/i5606.scala index f84c752e9b90..aedfa0617dfc 100644 --- a/tests/run/i5606.scala +++ b/tests/run/i5606.scala @@ -1,6 +1,6 @@ object Test extends App { - def [A, B](f: A => B) `$` (a: A): B = f(a) + extension [A, B](f: A => B) def `$` (a: A): B = f(a) assert((((a: Int) => a.toString()) `$` 10) == "10") diff --git a/tests/run/instances-anonymous.scala b/tests/run/instances-anonymous.scala index 5202eb5aebe1..5c0e53bd7ef4 100644 --- a/tests/run/instances-anonymous.scala +++ b/tests/run/instances-anonymous.scala @@ -1,7 +1,7 @@ object Test extends App { implicit object O { - def (x: Int).em: Boolean = x > 0 + extension (x: Int) def em: Boolean = x > 0 } assert(1.em == O.extension_em(1)) @@ -17,7 +17,7 @@ object Test extends App { println(circle.circumference) given AnyRef { - def (xs: Seq[String]).longestStrings: Seq[String] = { + extension (xs: Seq[String]) def longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } @@ -39,14 +39,14 @@ object Test extends App { assert(Nil.flattened == Nil) trait SemiGroup[T] { - def (x: T).combine(y: T): T + extension (x: T) def combine(y: T): T } trait Monoid[T] extends SemiGroup[T] { def unit: T } given Monoid[String] { - def (x: String).combine(y: String): String = x.concat(y) + extension (x: String) def combine(y: String): String = x.concat(y) def unit: String = "" } @@ -57,20 +57,20 @@ object Test extends App { println(sum(names)) trait Ord[T] { - def (x: T).compareTo(y: T): Int - def (x: T) < (y: T) = x.compareTo(y) < 0 - def (x: T) > (y: T) = x.compareTo(y) > 0 + extension (x: T) def compareTo(y: T): Int + extension (x: T) def < (y: T) = x.compareTo(y) < 0 + extension (x: T) def > (y: T) = x.compareTo(y) > 0 val minimum: T } given as Ord[Int] { - def (x: Int).compareTo(y: Int) = + extension (x: Int) def compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 val minimum = Int.MinValue } given [T: Ord] as Ord[List[T]] { - def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match { + extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys).match { case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -91,25 +91,25 @@ object Test extends App { println(max(List(1, 2, 3), List(2))) trait Functor[F[_]] { - def [A, B](x: F[A]) map (f: A => B): F[B] + extension [A, B](x: F[A]) def map (f: A => B): F[B] } trait Monad[F[_]] extends Functor[F] { - def [A, B](x: F[A]) flatMap (f: A => F[B]): F[B] - def [A, B](x: F[A]) map (f: A => B) = x.flatMap(f `andThen` pure) + extension [A, B](x: F[A]) def flatMap (f: A => F[B]): F[B] + extension [A, B](x: F[A]) def map (f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] } given Monad[List] { - def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] = + extension [A, B](xs: List[A]) def flatMap (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) } given [Ctx] as Monad[[X] =>> Ctx => X] { - def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B = + extension [A, B](r: Ctx => A) def flatMap (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x diff --git a/tests/run/instances.scala b/tests/run/instances.scala index 99b1820ecf72..a7ab9767021f 100644 --- a/tests/run/instances.scala +++ b/tests/run/instances.scala @@ -1,7 +1,7 @@ object Test extends App { implicit object O { - def (x: Int).em: Boolean = x > 0 + extension (x: Int) def em: Boolean = x > 0 } assert(1.em == O.extension_em(1)) @@ -31,8 +31,7 @@ object Test extends App { extension listListOps on [T](xs: List[List[T]]): def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) - extension prepend: - def [T](x: T) :: (xs: Seq[T]) = x +: xs + extension [T](x: T) def :: (xs: Seq[T]) = x +: xs val ss: Seq[Int] = List(1, 2, 3) val ss1 = 0 :: ss @@ -42,13 +41,13 @@ object Test extends App { assert(Nil.flattened == Nil) trait SemiGroup[T]: - def (x: T).combine(y: T): T + extension (x: T) def combine(y: T): T trait Monoid[T] extends SemiGroup[T]: def unit: T given StringMonoid as Monoid[String]: - def (x: String).combine(y: String): String = x.concat(y) + extension (x: String) def combine(y: String): String = x.concat(y) def unit: String = "" // Abstracting over a type class with a context bound: @@ -58,19 +57,19 @@ object Test extends App { println(sum(names)) trait Ord[T]: - def (x: T).compareTo(y: T): Int - def (x: T) < (y: T) = x.compareTo(y) < 0 - def (x: T) > (y: T) = x.compareTo(y) > 0 + extension (x: T) def compareTo(y: T): Int + extension (x: T) def < (y: T) = x.compareTo(y) < 0 + extension (x: T) def > (y: T) = x.compareTo(y) > 0 val minimum: T end Ord given Ord[Int]: - def (x: Int).compareTo(y: Int) = + extension (x: Int) def compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 val minimum = Int.MinValue given listOrd[T: Ord] as Ord[List[T]]: - def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match + extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys).match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -90,24 +89,24 @@ object Test extends App { println(max(List(1, 2, 3), List(2))) trait Functor[F[_]]: - def [A, B](x: F[A]) map (f: A => B): F[B] + extension [A, B](x: F[A]) def map (f: A => B): F[B] end Functor trait Monad[F[_]] extends Functor[F]: - def [A, B](x: F[A]) flatMap (f: A => F[B]): F[B] - def [A, B](x: F[A]) map (f: A => B) = x.flatMap(f `andThen` pure) + extension [A, B](x: F[A]) def flatMap (f: A => F[B]): F[B] + extension [A, B](x: F[A]) def map (f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] end Monad given listMonad as Monad[List]: - def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] = + extension [A, B](xs: List[A]) def flatMap (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) given readerMonad[Ctx] as Monad[[X] =>> Ctx => X]: - def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B = + extension [A, B](r: Ctx => A) def flatMap (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x diff --git a/tests/run/opaque-immutable-array-xm.scala b/tests/run/opaque-immutable-array-xm.scala index 4f90a982c9d1..49e131afcf00 100644 --- a/tests/run/opaque-immutable-array-xm.scala +++ b/tests/run/opaque-immutable-array-xm.scala @@ -11,8 +11,8 @@ object Test extends App { // These should be inline but that does not work currently. Try again // once inliner is moved to ReifyQuotes - def [A](ia: IArray[A]) length: Int = (ia: Array[A]).length - def [A](ia: IArray[A]) apply (i: Int): A = (ia: Array[A])(i) + extension [A](ia: IArray[A]) def length: Int = (ia: Array[A]).length + extension [A](ia: IArray[A]) def apply (i: Int): A = (ia: Array[A])(i) // return a sorted copy of the array def sorted[A <: AnyRef : math.Ordering](ia: IArray[A]): IArray[A] = { diff --git a/tests/run/rescue.scala b/tests/run/rescue.scala index 5ae9a78e63ee..a2b2e33c2cde 100644 --- a/tests/run/rescue.scala +++ b/tests/run/rescue.scala @@ -2,13 +2,13 @@ import scala.util.control.NonFatal import scala.util.control.NonLocalReturns._ object lib { - inline def [T](op: => T) rescue (fallback: => T) = + extension [T](op: => T) inline def rescue (fallback: => T) = try op catch { case NonFatal(_) => fallback // ReturnThrowable is fatal error, thus ignored } - inline def [T, E <: Throwable](op: => T) rescue (fallback: PartialFunction[E, T]) = + extension [T, E <: Throwable](op: => T) inline def rescue (fallback: PartialFunction[E, T]) = try op catch { // case ex: ReturnThrowable[_] => throw ex // bug #7041 diff --git a/tests/run/singleton-ops-flags.scala b/tests/run/singleton-ops-flags.scala index eaef14927b84..66fcc69e21b3 100644 --- a/tests/run/singleton-ops-flags.scala +++ b/tests/run/singleton-ops-flags.scala @@ -22,7 +22,7 @@ package example { type LastFlag = Open.idx.type - def (s: FlagSet).debug: String = + extension (s: FlagSet) def debug: String = if s == EmptyFlags then "EmptyFlags" else s.toSingletonSets[LastFlag].map ( [n <: Int] => (flag: SingletonFlagSet[n]) => flag match { case Erased => "Erased" @@ -48,18 +48,18 @@ package example { opaque type SingletonSets[N <: Int] = Int - private def [N <: Int](n: N).shift: 1 << N = ( 1 << n ).asInstanceOf - private def [N <: Int](n: N).succ : S[N] = ( n + 1 ).asInstanceOf + extension [N <: Int](n: N) private def shift: 1 << N = ( 1 << n ).asInstanceOf + extension [N <: Int](n: N) private def succ : S[N] = ( n + 1 ).asInstanceOf final val baseFlags: EmptyFlagSet = 0 - def (s: EmptyFlagSet).next: SingletonFlagSet[0] = 1 - def [N <: Int: ValueOf](s: SingletonFlagSet[N]).next: SingletonFlagSet[S[N]] = valueOf[N].succ.shift - def [N <: Int: ValueOf](s: SingletonFlagSet[N]).idx: N = valueOf[N] - def [N <: Int](s: FlagSet).toSingletonSets: SingletonSets[N] = s - def (s: FlagSet) | (t: FlagSet): FlagSet = s | t + extension (s: EmptyFlagSet) def next: SingletonFlagSet[0] = 1 + extension [N <: Int: ValueOf](s: SingletonFlagSet[N]) def next: SingletonFlagSet[S[N]] = valueOf[N].succ.shift + extension [N <: Int: ValueOf](s: SingletonFlagSet[N]) def idx: N = valueOf[N] + extension [N <: Int](s: FlagSet) def toSingletonSets: SingletonSets[N] = s + extension (s: FlagSet) def | (t: FlagSet): FlagSet = s | t - def [A, N <: Int: ValueOf](ss: SingletonSets[N]).map(f: [t <: Int] => (s: SingletonFlagSet[t]) => A): List[A] = + extension [A, N <: Int: ValueOf](ss: SingletonSets[N]) def map(f: [t <: Int] => (s: SingletonFlagSet[t]) => A): List[A] = val maxFlag = valueOf[N] val buf = List.newBuilder[A] var current = 0 diff --git a/tests/run/string-context-implicits-with-conversion.scala b/tests/run/string-context-implicits-with-conversion.scala index c3ec5af884ff..54e52ebdda8d 100644 --- a/tests/run/string-context-implicits-with-conversion.scala +++ b/tests/run/string-context-implicits-with-conversion.scala @@ -1,6 +1,6 @@ object Lib { - def (sc: StringContext).showMe(args: Showed*): String = sc.s(args: _*) + extension (sc: StringContext) def showMe(args: Showed*): String = sc.s(args: _*) opaque type Showed = String diff --git a/tests/run/toplevel-implicits/a.b.scala b/tests/run/toplevel-implicits/a.b.scala index a360f4d6e491..4811080ef78c 100644 --- a/tests/run/toplevel-implicits/a.b.scala +++ b/tests/run/toplevel-implicits/a.b.scala @@ -3,7 +3,7 @@ package implicits case class C() implicit object Cops { - def (x: C).pair(y: C) = (x, y) + extension (x: C) def pair(y: C) = (x, y) } class D { diff --git a/tests/run/tupled-function-andThen.scala b/tests/run/tupled-function-andThen.scala index 8bc7fa941a47..9080b8966522 100644 --- a/tests/run/tupled-function-andThen.scala +++ b/tests/run/tupled-function-andThen.scala @@ -32,7 +32,7 @@ object Test { * @tparam GArgs the tuple type with the same types as the function arguments of G and return type of F * @tparam R the return type of G */ - def [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F) andThen (g: G)(using tf: TupledFunction[F, FArgs => GArgs], tg: TupledFunction[G, GArgs => R]): FArgs => R = { + extension [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F) def andThen (g: G)(using tf: TupledFunction[F, FArgs => GArgs], tg: TupledFunction[G, GArgs => R]): FArgs => R = { x => tg.tupled(g)(tf.tupled(f)(x)) } diff --git a/tests/run/tupled-function-apply.scala b/tests/run/tupled-function-apply.scala index 16c90e952f6c..d37dba0a2f01 100644 --- a/tests/run/tupled-function-apply.scala +++ b/tests/run/tupled-function-apply.scala @@ -113,6 +113,6 @@ object Test { * @tparam Args the tuple type with the same types as the function arguments of F * @tparam R the return type of F */ - def [F, Args <: Tuple, R](f: F) apply (args: Args)(using tf: TupledFunction[F, Args => R]): R = + extension [F, Args <: Tuple, R](f: F) def apply (args: Args)(using tf: TupledFunction[F, Args => R]): R = tf.tupled(f)(args) } \ No newline at end of file diff --git a/tests/run/tupled-function-compose.scala b/tests/run/tupled-function-compose.scala index d929e9bb2956..71a918f1115c 100644 --- a/tests/run/tupled-function-compose.scala +++ b/tests/run/tupled-function-compose.scala @@ -33,7 +33,7 @@ object Test { * @tparam GArgs the tuple type with the same types as the function arguments of G * @tparam R the return type of F */ - def [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F) compose (g: G)(using tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = { + extension [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F) def compose (g: G)(using tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = { x => tf.tupled(f)(tg.tupled(g)(x)) } diff --git a/tests/run/tupled-function-extension-method.scala b/tests/run/tupled-function-extension-method.scala index 1281a5275511..2f5a0c4e8e67 100644 --- a/tests/run/tupled-function-extension-method.scala +++ b/tests/run/tupled-function-extension-method.scala @@ -34,14 +34,17 @@ object Test { class Expr[T](val x: T) // Specialized only for arity 0 and one as auto tupling will not provide the disired effect - def [R](e: Expr[() => R]) apply (): R = e.x() - def [Arg, R](e: Expr[Arg => R]) apply (arg: Arg): R = e.x(arg) - def [Arg, R](e: Expr[Arg ?=> R]) applyGiven(arg: Arg): R = e.x(using arg) + extension [R](e: Expr[() => R]) + def apply (): R = e.x() + extension [Arg, R](e: Expr[Arg => R]) + def apply (arg: Arg): R = e.x(arg) + extension [Arg, R](e: Expr[Arg ?=> R]) + def applyGiven(arg: Arg): R = e.x(using arg) // Applied to all funtions of arity 2 or more (including more than 22 parameters) - def [F, Args <: Tuple, R](e: Expr[F]) apply (args: Args)(using tf: TupledFunction[F, Args => R]): R = + extension [F, Args <: Tuple, R](e: Expr[F]) def apply (args: Args)(using tf: TupledFunction[F, Args => R]): R = tf.tupled(e.x)(args) - def [F, Args <: Tuple, R](e: Expr[F]) applyGiven (args: Args)(using tf: TupledFunction[F, Args ?=> R]): R = + extension [F, Args <: Tuple, R](e: Expr[F]) def applyGiven (args: Args)(using tf: TupledFunction[F, Args ?=> R]): R = tf.tupled(e.x)(using args) } \ No newline at end of file diff --git a/tests/run/tupled-function-tupled.scala b/tests/run/tupled-function-tupled.scala index ce42c13beb32..ce80ef983808 100644 --- a/tests/run/tupled-function-tupled.scala +++ b/tests/run/tupled-function-tupled.scala @@ -24,5 +24,5 @@ object Test { * @tparam Args the tuple type with the same types as the function arguments of F * @tparam R the return type of F */ - def [F, Args <: Tuple, R](f: F) tupled (using tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f) + extension [F, Args <: Tuple, R](f: F) def tupled (using tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f) } diff --git a/tests/run/tupled-function-untupled.scala b/tests/run/tupled-function-untupled.scala index 605836f16909..2d50169c0bd3 100644 --- a/tests/run/tupled-function-untupled.scala +++ b/tests/run/tupled-function-untupled.scala @@ -104,5 +104,5 @@ object Test { * @tparam Args the tuple type with the same types as the function arguments of F * @tparam R the return type of F */ - def [F, Args <: Tuple, R](f: Args => R) untupled(using tf: TupledFunction[F, Args => R]): F = tf.untupled(f) + extension [F, Args <: Tuple, R](f: Args => R) def untupled(using tf: TupledFunction[F, Args => R]): F = tf.untupled(f) } diff --git a/tests/semanticdb/expect/Enums.expect.scala b/tests/semanticdb/expect/Enums.expect.scala index be154809df0a..9359edd325d3 100644 --- a/tests/semanticdb/expect/Enums.expect.scala +++ b/tests/semanticdb/expect/Enums.expect.scala @@ -12,10 +12,10 @@ object Enums/*<-_empty_::Enums.*/: case Hearts/*<-_empty_::Enums.Suits.Hearts.*/, Spades/*<-_empty_::Enums.Suits.Spades.*/, Clubs/*<-_empty_::Enums.Suits.Clubs.*/, Diamonds/*<-_empty_::Enums.Suits.Diamonds.*/ object Suits/*<-_empty_::Enums.Suits.*/: - def (suit: Suits).i/*<-_empty_::Enums.Suits.extension_isRed().*//*<-_empty_::Enums.Suits.extension_isRed().(suit)*//*->_empty_::Enums.Suits#*/sRed: Boolean/*->scala::Boolean#*/ = + extension (suit: Suits) def i/*<-_empty_::Enums.Suits.extension_isRed().*//*<-_empty_::Enums.Suits.extension_isRed().(suit)*//*->_empty_::Enums.Suits#*/sRed: Boolean/*->scala::Boolean#*/ = suit/*->_empty_::Enums.Suits.extension_isRed().(suit)*/ ==/*->scala::Any#`==`().*/ Hearts/*->_empty_::Enums.Suits.Hearts.*/ ||/*->scala::Boolean#`||`().*/ suit/*->_empty_::Enums.Suits.extension_isRed().(suit)*/ ==/*->scala::Any#`==`().*/ Diamonds/*->_empty_::Enums.Suits.Diamonds.*/ - def (suit: Suits).isB/*<-_empty_::Enums.Suits.extension_isBlack().*//*<-_empty_::Enums.Suits.extension_isBlack().(suit)*//*->_empty_::Enums.Suits#*/lack: Boolean/*->scala::Boolean#*/ = suit/*->_empty_::Enums.Suits.extension_isBlack().(suit)*/ match + extension (suit: Suits) def isB/*<-_empty_::Enums.Suits.extension_isBlack().*//*<-_empty_::Enums.Suits.extension_isBlack().(suit)*//*->_empty_::Enums.Suits#*/lack: Boolean/*->scala::Boolean#*/ = suit/*->_empty_::Enums.Suits.extension_isBlack().(suit)*/ match case Spades/*->_empty_::Enums.Suits.Spades.*/ | Clubs/*->_empty_::Enums.Suits.Clubs.*/ => true case _ => false @@ -49,7 +49,7 @@ object Enums/*<-_empty_::Enums.*/: object <:_empty_::Enums.`<:<`.given_T().[T]*/ <:_empty_::Enums.`<:<`#*/ T/*->_empty_::Enums.`<:<`.given_T().[T]*/) = Refl/*->_empty_::Enums.`<:<`.Refl.*//*->_empty_::Enums.`<:<`.Refl.apply().*/() - def [A, B](opt: Opti/*<-_empty_::Enums.extension_unwrap().*//*<-_empty_::Enums.extension_unwrap().[A]*//*<-_empty_::Enums.extension_unwrap().[B]*//*<-_empty_::Enums.extension_unwrap().(opt)*/on/*->scala::Option#*/[A/*->_empty_::Enums.extension_unwrap().[A]*/]) unwrap(using ev/*<-_empty_::Enums.extension_unwrap().(ev)*/: A/*->_empty_::Enums.extension_unwrap().[A]*/ <:_empty_::Enums.`<:<`#*/ Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/]): Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/] = ev/*->_empty_::Enums.extension_unwrap().(ev)*/ match + extension [A, B](opt: Opti/*<-_empty_::Enums.extension_unwrap() def *//*<-_empty_::Enums.extension_unwrap().[A]*//*<-_empty_::Enums.extension_unwrap().[B]*//*<-_empty_::Enums.extension_unwrap().(opt)*/on/*->scala::Option#*/[A/*->_empty_::Enums.extension_unwrap().[A]*/]) unwrap(using ev/*<-_empty_::Enums.extension_unwrap().(ev)*/: A/*->_empty_::Enums.extension_unwrap().[A]*/ <:_empty_::Enums.`<:<`#*/ Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/]): Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/] = ev/*->_empty_::Enums.extension_unwrap().(ev)*/ match case Refl/*->_empty_::Enums.`<:<`.Refl.*//*->_empty_::Enums.`<:<`.Refl.unapply().*/() => opt/*->_empty_::Enums.extension_unwrap().(opt)*/.flatMap/*->scala::Option#flatMap().*/(identity/*->scala::Predef.identity().*//*->local0*/[Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/]]) val some1/*<-_empty_::Enums.some1.*/ = /*->_empty_::Enums.extension_unwrap().*/Some/*->scala::Some.*//*->scala::Some.apply().*/(Some/*->scala::Some.*//*->scala::Some.apply().*/(1))/*->_empty_::Enums.`<:<`.given_T().*/.unwrap diff --git a/tests/semanticdb/expect/Enums.scala b/tests/semanticdb/expect/Enums.scala index d32b8823c98f..f781e0878145 100644 --- a/tests/semanticdb/expect/Enums.scala +++ b/tests/semanticdb/expect/Enums.scala @@ -12,10 +12,10 @@ object Enums: case Hearts, Spades, Clubs, Diamonds object Suits: - def (suit: Suits).isRed: Boolean = + extension (suit: Suits) def isRed: Boolean = suit == Hearts || suit == Diamonds - def (suit: Suits).isBlack: Boolean = suit match + extension (suit: Suits) def isBlack: Boolean = suit match case Spades | Clubs => true case _ => false @@ -49,7 +49,7 @@ object Enums: object <:< : given [T] as (T <:< T) = Refl() - def [A, B](opt: Option[A]) unwrap(using ev: A <:< Option[B]): Option[B] = ev match + extension [A, B](opt: Option[A]) def unwrap(using ev: A <:< Option[B]): Option[B] = ev match case Refl() => opt.flatMap(identity[Option[B]]) val some1 = Some(Some(1)).unwrap diff --git a/tests/semanticdb/expect/Givens.expect.scala b/tests/semanticdb/expect/Givens.expect.scala index 2bc1a4e688f0..e84252a4cd0b 100644 --- a/tests/semanticdb/expect/Givens.expect.scala +++ b/tests/semanticdb/expect/Givens.expect.scala @@ -16,11 +16,11 @@ object Givens/*<-a::b::Givens.*/: trait Monoid/*<-a::b::Givens.Monoid#*/[A/*<-a::b::Givens.Monoid#[A]*/]: def empty/*<-a::b::Givens.Monoid#empty().*/: A/*->a::b::Givens.Monoid#[A]*/ - def (x: A).combine(y:/*<-a::b::Givens.Monoid#extension_combine().*//*<-a::b::Givens.Monoid#extension_combine().(x)*//*->a::b::Givens.Monoid#[A]*//*<-a::b::Givens.Monoid#extension_combine().(y)*/ A/*->a::b::Givens.Monoid#[A]*/): A/*->a::b::Givens.Monoid#[A]*/ + extension (x: A) def combine(y:/*<-a::b::Givens.Monoid#extension_combine().*//*<-a::b::Givens.Monoid#extension_combine().(x)*//*->a::b::Givens.Monoid#[A]*//*<-a::b::Givens.Monoid#extension_combine().(y)*/ A/*->a::b::Givens.Monoid#[A]*/): A/*->a::b::Givens.Monoid#[A]*/ given Monoid[String]: /*<-a::b::Givens.given_Monoid_String.*//*->a::b::Givens.Monoid#*//*->scala::Predef.String#*/ def empty/*<-a::b::Givens.given_Monoid_String.empty().*/ = "" - def (x: String).combi/*<-a::b::Givens.given_Monoid_String.extension_combine().*//*<-a::b::Givens.given_Monoid_String.extension_combine().(x)*//*->scala::Predef.String#*/ne(y/*<-a::b::Givens.given_Monoid_String.extension_combine().(y)*/: String/*->scala::Predef.String#*/) = x/*->a::b::Givens.given_Monoid_String.extension_combine().(x)*/ +/*->java::lang::String#`+`().*/ y/*->a::b::Givens.given_Monoid_String.extension_combine().(y)*/ + extension (x: String) def combi/*<-a::b::Givens.given_Monoid_String.extension_combine().*//*<-a::b::Givens.given_Monoid_String.extension_combine().(x)*//*->scala::Predef.String#*/ne(y/*<-a::b::Givens.given_Monoid_String.extension_combine().(y)*/: String/*->scala::Predef.String#*/) = x/*->a::b::Givens.given_Monoid_String.extension_combine().(x)*/ +/*->java::lang::String#`+`().*/ y/*->a::b::Givens.given_Monoid_String.extension_combine().(y)*/ inline given int2String/*<-a::b::Givens.int2String().*/ as Conversion/*->scala::Conversion#*/[Int/*->scala::Int#*/, String/*->scala::Predef.String#*/] = _.toString/*->scala::Any#toString().*/ diff --git a/tests/semanticdb/expect/Givens.scala b/tests/semanticdb/expect/Givens.scala index 6b5ab7912d33..b265a782dea3 100644 --- a/tests/semanticdb/expect/Givens.scala +++ b/tests/semanticdb/expect/Givens.scala @@ -16,11 +16,11 @@ object Givens: trait Monoid[A]: def empty: A - def (x: A).combine(y: A): A + extension (x: A) def combine(y: A): A given Monoid[String]: def empty = "" - def (x: String).combine(y: String) = x + y + extension (x: String) def combine(y: String) = x + y inline given int2String as Conversion[Int, String] = _.toString diff --git a/tests/semanticdb/expect/toplevel.expect.scala b/tests/semanticdb/expect/toplevel.expect.scala index 1654caa5204a..72d6a552e3f2 100644 --- a/tests/semanticdb/expect/toplevel.expect.scala +++ b/tests/semanticdb/expect/toplevel.expect.scala @@ -1,5 +1,5 @@ inline val a = "/*<-_empty_::toplevel$package.*//*<-_empty_::toplevel$package.a.*/" -def (x: Int) combine /*<-_empty_::toplevel$package.extension_combine().*//*<-_empty_::toplevel$package.extension_combine().(x)*//*->scala::Int#*/(y/*<-_empty_::toplevel$package.extension_combine().(y)*/: Int/*->scala::Int#*/) = x/*->_empty_::toplevel$package.extension_combine().(x)*/ +/*->scala::Int#`+`(+4).*/ y/*->_empty_::toplevel$package.extension_combine().(y)*/ +extension (x: Int) def combine /*<-_empty_::toplevel$package.extension_combine().*//*<-_empty_::toplevel$package.extension_combine().(x)*//*->scala::Int#*/(y/*<-_empty_::toplevel$package.extension_combine().(y)*/: Int/*->scala::Int#*/) = x/*->_empty_::toplevel$package.extension_combine().(x)*/ +/*->scala::Int#`+`(+4).*/ y/*->_empty_::toplevel$package.extension_combine().(y)*/ def combine/*<-_empty_::toplevel$package.combine().*/(x/*<-_empty_::toplevel$package.combine().(x)*/: Int/*->scala::Int#*/, y/*<-_empty_::toplevel$package.combine().(y)*/: Int/*->scala::Int#*/, z/*<-_empty_::toplevel$package.combine().(z)*/: Int/*->scala::Int#*/) = x/*->_empty_::toplevel$package.combine().(x)*/ +/*->scala::Int#`+`(+4).*/ y/*->_empty_::toplevel$package.combine().(y)*/ +/*->scala::Int#`+`(+4).*/ z/*->_empty_::toplevel$package.combine().(z)*/ def combine/*<-_empty_::toplevel$package.combine(+1).*/ = 0 def foo/*<-_empty_::toplevel$package.foo().*/ = "foo" diff --git a/tests/semanticdb/expect/toplevel.scala b/tests/semanticdb/expect/toplevel.scala index 37ff0172e2aa..a99265bc630e 100644 --- a/tests/semanticdb/expect/toplevel.scala +++ b/tests/semanticdb/expect/toplevel.scala @@ -1,5 +1,5 @@ inline val a = "" -def (x: Int) combine (y: Int) = x + y +extension (x: Int) def combine (y: Int) = x + y def combine(x: Int, y: Int, z: Int) = x + y + z def combine = 0 def foo = "foo" diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 6ee471a31922..948a90b5abea 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -824,10 +824,10 @@ Occurrences: [11:25..11:30): Clubs <- _empty_/Enums.Suits.Clubs. [11:32..11:40): Diamonds <- _empty_/Enums.Suits.Diamonds. [13:9..13:14): Suits <- _empty_/Enums.Suits. -[14:8..14:23): (suit: Suits).i <- _empty_/Enums.Suits.extension_isRed(). -[14:9..14:13): suit <- _empty_/Enums.Suits.extension_isRed().(suit) -[14:15..14:20): Suits -> _empty_/Enums.Suits# -[14:29..14:36): Boolean -> scala/Boolean# +[14:15..14:19): suit <- _empty_/Enums.Suits.extension_isRed().(suit) +[14:21..14:26): Suits -> _empty_/Enums.Suits# +[14:32..14:47): isRed: Boolean <- _empty_/Enums.Suits.extension_isRed(). +[14:39..14:46): Boolean -> scala/Boolean# [15:6..15:10): suit -> _empty_/Enums.Suits.extension_isRed().(suit) [15:11..15:13): == -> scala/Any#`==`(). [15:14..15:20): Hearts -> _empty_/Enums.Suits.Hearts. @@ -835,11 +835,11 @@ Occurrences: [15:24..15:28): suit -> _empty_/Enums.Suits.extension_isRed().(suit) [15:29..15:31): == -> scala/Any#`==`(). [15:32..15:40): Diamonds -> _empty_/Enums.Suits.Diamonds. -[17:8..17:25): (suit: Suits).isB <- _empty_/Enums.Suits.extension_isBlack(). -[17:9..17:13): suit <- _empty_/Enums.Suits.extension_isBlack().(suit) -[17:15..17:20): Suits -> _empty_/Enums.Suits# -[17:31..17:38): Boolean -> scala/Boolean# -[17:41..17:45): suit -> _empty_/Enums.Suits.extension_isBlack().(suit) +[17:15..17:19): suit <- _empty_/Enums.Suits.extension_isBlack().(suit) +[17:21..17:26): Suits -> _empty_/Enums.Suits# +[17:32..17:49): isBlack: Boolean <- _empty_/Enums.Suits.extension_isBlack(). +[17:41..17:48): Boolean -> scala/Boolean# +[17:51..17:55): suit -> _empty_/Enums.Suits.extension_isBlack().(suit) [18:11..18:17): Spades -> _empty_/Enums.Suits.Spades. [18:20..18:25): Clubs -> _empty_/Enums.Suits.Clubs. [21:7..21:15): WeekDays <- _empty_/Enums.WeekDays# @@ -917,20 +917,20 @@ Occurrences: [49:24..49:25): T -> _empty_/Enums.`<:<`.given_T().[T] [49:29..49:33): Refl -> _empty_/Enums.`<:<`.Refl. [49:33..49:33): -> _empty_/Enums.`<:<`.Refl.apply(). -[51:6..51:22): [A, B](opt: Opti <- _empty_/Enums.extension_unwrap(). -[51:7..51:8): A <- _empty_/Enums.extension_unwrap().[A] -[51:10..51:11): B <- _empty_/Enums.extension_unwrap().[B] -[51:13..51:16): opt <- _empty_/Enums.extension_unwrap().(opt) -[51:18..51:24): Option -> scala/Option# -[51:25..51:26): A -> _empty_/Enums.extension_unwrap().[A] -[51:42..51:44): ev <- _empty_/Enums.extension_unwrap().(ev) -[51:46..51:47): A -> _empty_/Enums.extension_unwrap().[A] -[51:48..51:51): <:< -> _empty_/Enums.`<:<`# -[51:52..51:58): Option -> scala/Option# -[51:59..51:60): B -> _empty_/Enums.extension_unwrap().[B] -[51:64..51:70): Option -> scala/Option# -[51:71..51:72): B -> _empty_/Enums.extension_unwrap().[B] -[51:76..51:78): ev -> _empty_/Enums.extension_unwrap().(ev) +[51:13..51:14): A <- _empty_/Enums.extension_unwrap().[A] +[51:16..51:17): B <- _empty_/Enums.extension_unwrap().[B] +[51:19..51:22): opt <- _empty_/Enums.extension_unwrap().(opt) +[51:24..51:30): Option -> scala/Option# +[51:31..51:32): A -> _empty_/Enums.extension_unwrap().[A] +[51:39..51:55): unwrap(using ev: <- _empty_/Enums.extension_unwrap(). +[51:52..51:54): ev <- _empty_/Enums.extension_unwrap().(ev) +[51:56..51:57): A -> _empty_/Enums.extension_unwrap().[A] +[51:58..51:61): <:< -> _empty_/Enums.`<:<`# +[51:62..51:68): Option -> scala/Option# +[51:69..51:70): B -> _empty_/Enums.extension_unwrap().[B] +[51:74..51:80): Option -> scala/Option# +[51:81..51:82): B -> _empty_/Enums.extension_unwrap().[B] +[51:86..51:88): ev -> _empty_/Enums.extension_unwrap().(ev) [52:9..52:13): Refl -> _empty_/Enums.`<:<`.Refl. [52:13..52:13): -> _empty_/Enums.`<:<`.Refl.unapply(). [52:19..52:22): opt -> _empty_/Enums.extension_unwrap().(opt) @@ -1278,24 +1278,24 @@ Occurrences: [16:15..16:16): A <- a/b/Givens.Monoid#[A] [17:8..17:13): empty <- a/b/Givens.Monoid#empty(). [17:15..17:16): A -> a/b/Givens.Monoid#[A] -[18:8..18:25): (x: A).combine(y: <- a/b/Givens.Monoid#extension_combine(). -[18:9..18:10): x <- a/b/Givens.Monoid#extension_combine().(x) -[18:12..18:13): A -> a/b/Givens.Monoid#[A] -[18:23..18:24): y <- a/b/Givens.Monoid#extension_combine().(y) -[18:26..18:27): A -> a/b/Givens.Monoid#[A] -[18:30..18:31): A -> a/b/Givens.Monoid#[A] +[18:15..18:16): x <- a/b/Givens.Monoid#extension_combine().(x) +[18:18..18:19): A -> a/b/Givens.Monoid#[A] +[18:25..19:0): <- a/b/Givens.Monoid#extension_combine(). +[18:33..18:34): y <- a/b/Givens.Monoid#extension_combine().(y) +[18:36..18:37): A -> a/b/Givens.Monoid#[A] +[18:40..18:41): A -> a/b/Givens.Monoid#[A] [20:8..21:3): <- a/b/Givens.given_Monoid_String. [20:8..20:14): Monoid -> a/b/Givens.Monoid# [20:15..20:21): String -> scala/Predef.String# [21:8..21:13): empty <- a/b/Givens.given_Monoid_String.empty(). -[22:8..22:25): (x: String).combi <- a/b/Givens.given_Monoid_String.extension_combine(). -[22:9..22:10): x <- a/b/Givens.given_Monoid_String.extension_combine().(x) -[22:12..22:18): String -> scala/Predef.String# -[22:28..22:29): y <- a/b/Givens.given_Monoid_String.extension_combine().(y) -[22:31..22:37): String -> scala/Predef.String# -[22:41..22:42): x -> a/b/Givens.given_Monoid_String.extension_combine().(x) -[22:43..22:44): + -> java/lang/String#`+`(). -[22:45..22:46): y -> a/b/Givens.given_Monoid_String.extension_combine().(y) +[22:15..22:16): x <- a/b/Givens.given_Monoid_String.extension_combine().(x) +[22:18..22:24): String -> scala/Predef.String# +[22:30..22:47): combine(y: String <- a/b/Givens.given_Monoid_String.extension_combine(). +[22:38..22:39): y <- a/b/Givens.given_Monoid_String.extension_combine().(y) +[22:41..22:47): String -> scala/Predef.String# +[22:51..22:52): x -> a/b/Givens.given_Monoid_String.extension_combine().(x) +[22:53..22:54): + -> java/lang/String#`+`(). +[22:55..22:56): y -> a/b/Givens.given_Monoid_String.extension_combine().(y) [24:15..24:25): int2String <- a/b/Givens.int2String(). [24:29..24:39): Conversion -> scala/Conversion# [24:40..24:43): Int -> scala/Int# @@ -3875,14 +3875,14 @@ _empty_/toplevel$package.foo(). => method foo Occurrences: [0:0..0:16): inline val a = " <- _empty_/toplevel$package. [0:11..0:12): a <- _empty_/toplevel$package.a. -[1:4..1:21): (x: Int) combine <- _empty_/toplevel$package.extension_combine(). -[1:5..1:6): x <- _empty_/toplevel$package.extension_combine().(x) -[1:8..1:11): Int -> scala/Int# -[1:22..1:23): y <- _empty_/toplevel$package.extension_combine().(y) -[1:25..1:28): Int -> scala/Int# -[1:32..1:33): x -> _empty_/toplevel$package.extension_combine().(x) -[1:34..1:35): + -> scala/Int#`+`(+4). -[1:36..1:37): y -> _empty_/toplevel$package.extension_combine().(y) +[1:11..1:12): x <- _empty_/toplevel$package.extension_combine().(x) +[1:14..1:17): Int -> scala/Int# +[1:23..1:40): combine (y: Int) <- _empty_/toplevel$package.extension_combine(). +[1:32..1:33): y <- _empty_/toplevel$package.extension_combine().(y) +[1:35..1:38): Int -> scala/Int# +[1:42..1:43): x -> _empty_/toplevel$package.extension_combine().(x) +[1:44..1:45): + -> scala/Int#`+`(+4). +[1:46..1:47): y -> _empty_/toplevel$package.extension_combine().(y) [2:4..2:11): combine <- _empty_/toplevel$package.combine(). [2:12..2:13): x <- _empty_/toplevel$package.combine().(x) [2:15..2:18): Int -> scala/Int# From c148218c5b59dbb84657a223424e7774674d9cd2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 30 Jun 2020 21:28:45 +0200 Subject: [PATCH 16/38] Update semanticDB expect files --- tests/neg/i9185.scala | 2 +- tests/semanticdb/expect/Enums.expect.scala | 6 +++--- tests/semanticdb/expect/Givens.expect.scala | 6 +++--- tests/semanticdb/expect/toplevel.expect.scala | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/neg/i9185.scala b/tests/neg/i9185.scala index c4b47623ed0c..f20f77217141 100644 --- a/tests/neg/i9185.scala +++ b/tests/neg/i9185.scala @@ -5,7 +5,7 @@ object M { given optionMonad as M[Option] { def pure[A](x: A): Option[A] = Some(x) } val value1: List[String] = "ola".pure val value2 = "ola".pure // error - val value3 = extension_pure("ola") // error + val value3 = extension.pure("ola") // error extension (x: Int) def len: Int = x val l = "abc".len // error diff --git a/tests/semanticdb/expect/Enums.expect.scala b/tests/semanticdb/expect/Enums.expect.scala index 9359edd325d3..0539adc8866e 100644 --- a/tests/semanticdb/expect/Enums.expect.scala +++ b/tests/semanticdb/expect/Enums.expect.scala @@ -12,10 +12,10 @@ object Enums/*<-_empty_::Enums.*/: case Hearts/*<-_empty_::Enums.Suits.Hearts.*/, Spades/*<-_empty_::Enums.Suits.Spades.*/, Clubs/*<-_empty_::Enums.Suits.Clubs.*/, Diamonds/*<-_empty_::Enums.Suits.Diamonds.*/ object Suits/*<-_empty_::Enums.Suits.*/: - extension (suit: Suits) def i/*<-_empty_::Enums.Suits.extension_isRed().*//*<-_empty_::Enums.Suits.extension_isRed().(suit)*//*->_empty_::Enums.Suits#*/sRed: Boolean/*->scala::Boolean#*/ = + extension (suit/*<-_empty_::Enums.Suits.extension_isRed().(suit)*/: Suits/*->_empty_::Enums.Suits#*/) def isRed: Boolean /*<-_empty_::Enums.Suits.extension_isRed().*//*->scala::Boolean#*/= suit/*->_empty_::Enums.Suits.extension_isRed().(suit)*/ ==/*->scala::Any#`==`().*/ Hearts/*->_empty_::Enums.Suits.Hearts.*/ ||/*->scala::Boolean#`||`().*/ suit/*->_empty_::Enums.Suits.extension_isRed().(suit)*/ ==/*->scala::Any#`==`().*/ Diamonds/*->_empty_::Enums.Suits.Diamonds.*/ - extension (suit: Suits) def isB/*<-_empty_::Enums.Suits.extension_isBlack().*//*<-_empty_::Enums.Suits.extension_isBlack().(suit)*//*->_empty_::Enums.Suits#*/lack: Boolean/*->scala::Boolean#*/ = suit/*->_empty_::Enums.Suits.extension_isBlack().(suit)*/ match + extension (suit/*<-_empty_::Enums.Suits.extension_isBlack().(suit)*/: Suits/*->_empty_::Enums.Suits#*/) def isBlack: Boolean /*<-_empty_::Enums.Suits.extension_isBlack().*//*->scala::Boolean#*/= suit/*->_empty_::Enums.Suits.extension_isBlack().(suit)*/ match case Spades/*->_empty_::Enums.Suits.Spades.*/ | Clubs/*->_empty_::Enums.Suits.Clubs.*/ => true case _ => false @@ -49,7 +49,7 @@ object Enums/*<-_empty_::Enums.*/: object <:_empty_::Enums.`<:<`.given_T().[T]*/ <:_empty_::Enums.`<:<`#*/ T/*->_empty_::Enums.`<:<`.given_T().[T]*/) = Refl/*->_empty_::Enums.`<:<`.Refl.*//*->_empty_::Enums.`<:<`.Refl.apply().*/() - extension [A, B](opt: Opti/*<-_empty_::Enums.extension_unwrap() def *//*<-_empty_::Enums.extension_unwrap().[A]*//*<-_empty_::Enums.extension_unwrap().[B]*//*<-_empty_::Enums.extension_unwrap().(opt)*/on/*->scala::Option#*/[A/*->_empty_::Enums.extension_unwrap().[A]*/]) unwrap(using ev/*<-_empty_::Enums.extension_unwrap().(ev)*/: A/*->_empty_::Enums.extension_unwrap().[A]*/ <:_empty_::Enums.`<:<`#*/ Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/]): Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/] = ev/*->_empty_::Enums.extension_unwrap().(ev)*/ match + extension [A/*<-_empty_::Enums.extension_unwrap().[A]*/, B/*<-_empty_::Enums.extension_unwrap().[B]*/](opt/*<-_empty_::Enums.extension_unwrap().(opt)*/: Option/*->scala::Option#*/[A/*->_empty_::Enums.extension_unwrap().[A]*/]) def unwrap(using ev:/*<-_empty_::Enums.extension_unwrap().*//*<-_empty_::Enums.extension_unwrap().(ev)*/ A/*->_empty_::Enums.extension_unwrap().[A]*/ <:_empty_::Enums.`<:<`#*/ Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/]): Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/] = ev/*->_empty_::Enums.extension_unwrap().(ev)*/ match case Refl/*->_empty_::Enums.`<:<`.Refl.*//*->_empty_::Enums.`<:<`.Refl.unapply().*/() => opt/*->_empty_::Enums.extension_unwrap().(opt)*/.flatMap/*->scala::Option#flatMap().*/(identity/*->scala::Predef.identity().*//*->local0*/[Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/]]) val some1/*<-_empty_::Enums.some1.*/ = /*->_empty_::Enums.extension_unwrap().*/Some/*->scala::Some.*//*->scala::Some.apply().*/(Some/*->scala::Some.*//*->scala::Some.apply().*/(1))/*->_empty_::Enums.`<:<`.given_T().*/.unwrap diff --git a/tests/semanticdb/expect/Givens.expect.scala b/tests/semanticdb/expect/Givens.expect.scala index e84252a4cd0b..5de094afbd0c 100644 --- a/tests/semanticdb/expect/Givens.expect.scala +++ b/tests/semanticdb/expect/Givens.expect.scala @@ -16,11 +16,11 @@ object Givens/*<-a::b::Givens.*/: trait Monoid/*<-a::b::Givens.Monoid#*/[A/*<-a::b::Givens.Monoid#[A]*/]: def empty/*<-a::b::Givens.Monoid#empty().*/: A/*->a::b::Givens.Monoid#[A]*/ - extension (x: A) def combine(y:/*<-a::b::Givens.Monoid#extension_combine().*//*<-a::b::Givens.Monoid#extension_combine().(x)*//*->a::b::Givens.Monoid#[A]*//*<-a::b::Givens.Monoid#extension_combine().(y)*/ A/*->a::b::Givens.Monoid#[A]*/): A/*->a::b::Givens.Monoid#[A]*/ - + extension (x/*<-a::b::Givens.Monoid#extension_combine().(x)*/: A/*->a::b::Givens.Monoid#[A]*/) def combine(y: A): A +/*<-a::b::Givens.Monoid#extension_combine().*//*<-a::b::Givens.Monoid#extension_combine().(y)*//*->a::b::Givens.Monoid#[A]*//*->a::b::Givens.Monoid#[A]*/ given Monoid[String]: /*<-a::b::Givens.given_Monoid_String.*//*->a::b::Givens.Monoid#*//*->scala::Predef.String#*/ def empty/*<-a::b::Givens.given_Monoid_String.empty().*/ = "" - extension (x: String) def combi/*<-a::b::Givens.given_Monoid_String.extension_combine().*//*<-a::b::Givens.given_Monoid_String.extension_combine().(x)*//*->scala::Predef.String#*/ne(y/*<-a::b::Givens.given_Monoid_String.extension_combine().(y)*/: String/*->scala::Predef.String#*/) = x/*->a::b::Givens.given_Monoid_String.extension_combine().(x)*/ +/*->java::lang::String#`+`().*/ y/*->a::b::Givens.given_Monoid_String.extension_combine().(y)*/ + extension (x/*<-a::b::Givens.given_Monoid_String.extension_combine().(x)*/: String/*->scala::Predef.String#*/) def combine(y: String/*<-a::b::Givens.given_Monoid_String.extension_combine().*//*<-a::b::Givens.given_Monoid_String.extension_combine().(y)*//*->scala::Predef.String#*/) = x/*->a::b::Givens.given_Monoid_String.extension_combine().(x)*/ +/*->java::lang::String#`+`().*/ y/*->a::b::Givens.given_Monoid_String.extension_combine().(y)*/ inline given int2String/*<-a::b::Givens.int2String().*/ as Conversion/*->scala::Conversion#*/[Int/*->scala::Int#*/, String/*->scala::Predef.String#*/] = _.toString/*->scala::Any#toString().*/ diff --git a/tests/semanticdb/expect/toplevel.expect.scala b/tests/semanticdb/expect/toplevel.expect.scala index 72d6a552e3f2..612bff8b0054 100644 --- a/tests/semanticdb/expect/toplevel.expect.scala +++ b/tests/semanticdb/expect/toplevel.expect.scala @@ -1,5 +1,5 @@ inline val a = "/*<-_empty_::toplevel$package.*//*<-_empty_::toplevel$package.a.*/" -extension (x: Int) def combine /*<-_empty_::toplevel$package.extension_combine().*//*<-_empty_::toplevel$package.extension_combine().(x)*//*->scala::Int#*/(y/*<-_empty_::toplevel$package.extension_combine().(y)*/: Int/*->scala::Int#*/) = x/*->_empty_::toplevel$package.extension_combine().(x)*/ +/*->scala::Int#`+`(+4).*/ y/*->_empty_::toplevel$package.extension_combine().(y)*/ +extension (x/*<-_empty_::toplevel$package.extension_combine().(x)*/: Int/*->scala::Int#*/) def combine (y: Int) /*<-_empty_::toplevel$package.extension_combine().*//*<-_empty_::toplevel$package.extension_combine().(y)*//*->scala::Int#*/= x/*->_empty_::toplevel$package.extension_combine().(x)*/ +/*->scala::Int#`+`(+4).*/ y/*->_empty_::toplevel$package.extension_combine().(y)*/ def combine/*<-_empty_::toplevel$package.combine().*/(x/*<-_empty_::toplevel$package.combine().(x)*/: Int/*->scala::Int#*/, y/*<-_empty_::toplevel$package.combine().(y)*/: Int/*->scala::Int#*/, z/*<-_empty_::toplevel$package.combine().(z)*/: Int/*->scala::Int#*/) = x/*->_empty_::toplevel$package.combine().(x)*/ +/*->scala::Int#`+`(+4).*/ y/*->_empty_::toplevel$package.combine().(y)*/ +/*->scala::Int#`+`(+4).*/ z/*->_empty_::toplevel$package.combine().(z)*/ def combine/*<-_empty_::toplevel$package.combine(+1).*/ = 0 def foo/*<-_empty_::toplevel$package.foo().*/ = "foo" From 330c32f58936c7367b38ed873fcfe6679330b93e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 30 Jun 2020 22:32:12 +0200 Subject: [PATCH 17/38] Fix test --- tests/neg/i9185.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/neg/i9185.scala b/tests/neg/i9185.scala index f20f77217141..c4b47623ed0c 100644 --- a/tests/neg/i9185.scala +++ b/tests/neg/i9185.scala @@ -5,7 +5,7 @@ object M { given optionMonad as M[Option] { def pure[A](x: A): Option[A] = Some(x) } val value1: List[String] = "ola".pure val value2 = "ola".pure // error - val value3 = extension.pure("ola") // error + val value3 = extension_pure("ola") // error extension (x: Int) def len: Int = x val l = "abc".len // error From b63da921b297f8236ef7f5728b505bca48f6636c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 30 Jun 2020 23:25:03 +0200 Subject: [PATCH 18/38] Disable check file for missing-implicit CI makes more suggestions than local on bootstrapped tests. I am not sure why. It might have to do with different setups what packages are forced and what not. --- tests/{ => pending}/neg/missing-implicit.check | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{ => pending}/neg/missing-implicit.check (100%) diff --git a/tests/neg/missing-implicit.check b/tests/pending/neg/missing-implicit.check similarity index 100% rename from tests/neg/missing-implicit.check rename to tests/pending/neg/missing-implicit.check From 3e89b56e04296171084d83b54f42c002c19e254d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 1 Jul 2020 09:45:46 +0200 Subject: [PATCH 19/38] Allow `end extension` as an endmarker --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- tests/pos/i8241.scala | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 122f06c55512..e084c1abec91 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1313,7 +1313,7 @@ object Parsers { case stat: MemberDef if !stat.name.isEmpty => if stat.name == nme.CONSTRUCTOR then in.token == THIS else in.isIdent && in.name == stat.name.toTermName - case ModuleDef(name, Template(_, Nil, _, _)) => + case ModuleDef(_, Template(_, Nil, _, _)) | ExtMethods(_, _, _) => in.token == IDENTIFIER && in.name == nme.extension case PackageDef(pid: RefTree, _) => in.isIdent && in.name == pid.name diff --git a/tests/pos/i8241.scala b/tests/pos/i8241.scala index e6d1f863e7ad..9bcc39f950b5 100644 --- a/tests/pos/i8241.scala +++ b/tests/pos/i8241.scala @@ -1,7 +1,7 @@ -extension NameOps on (name: String): +extension (name: String): def isWildcard = ??? -end NameOps +end extension -extension on (name: String): +extension (name: String): def f = ??? end extension From a7213cc48ac7ce798c851500809a37f32f3278e4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 1 Jul 2020 10:29:58 +0200 Subject: [PATCH 20/38] Fix extension method search in implicit scope The case where a scope contained only extension methods but no other givens was mishandled. --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 8 +++++--- tests/pos/implicit-scope.scala | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 0802dfe7d909..78f7a20ec250 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -236,8 +236,9 @@ object Implicits { } - if (refs.isEmpty) Nil - else { + if refs.isEmpty && (!considerExtension || companionRefs.isEmpty) then + Nil + else val nestedCtx = ctx.fresh.addMode(Mode.TypevarsMissContext) def matchingCandidate(ref: ImplicitRef, extensionOnly: Boolean): Option[Candidate] = @@ -254,7 +255,6 @@ object Implicits { val implicitCandidates = refs.flatMap(matchingCandidate(_, extensionOnly = false)) extensionCandidates ::: implicitCandidates - } } } @@ -1652,6 +1652,8 @@ final class SearchRoot extends SearchHistory { sealed class TermRefSet(using Context): private val elems = new java.util.LinkedHashMap[TermSymbol, List[Type]] + def isEmpty = elems.size == 0 + def += (ref: TermRef): Unit = { val pre = ref.prefix val sym = ref.symbol.asTerm diff --git a/tests/pos/implicit-scope.scala b/tests/pos/implicit-scope.scala index 5e013de27cda..0c5bf358bf21 100644 --- a/tests/pos/implicit-scope.scala +++ b/tests/pos/implicit-scope.scala @@ -9,7 +9,7 @@ object A { type FlagSet = opaques.FlagSet def FlagSet(bits: Long): FlagSet = opaques.FlagSet(bits) - extension on (xs: FlagSet) { + extension (xs: FlagSet) { def bits: Long = opaques.toBits(xs) def | (ys: FlagSet): FlagSet = FlagSet(xs.bits | ys.bits) } From 00cc25dbfd4fe5187afbd57cfeb0ffaa6d7a7575 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 1 Jul 2020 11:23:17 +0200 Subject: [PATCH 21/38] Fix syntax error messages for extensions --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 3 ++- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 4de27acc99dd..558458054420 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -911,7 +911,8 @@ object desugar { def extMethods(ext: ExtMethods)(using Context): Tree = flatTree { for mdef <- ext.methods yield if mdef.tparams.nonEmpty then - ctx.error("no type parameters allowed here", mdef.tparams.head.sourcePos) + ctx.error("extension method cannot have type parameters here, all type parameters go after `extension`", + mdef.tparams.head.sourcePos) defDef( cpy.DefDef(mdef)( name = mdef.name.toExtensionName, diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index e084c1abec91..9c28f88f8a87 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3679,6 +3679,7 @@ object Parsers { setLastStatOffset() meths += extMethod() acceptStatSepUnlessAtEnd(meths) + if meths.isEmpty then syntaxError("`def` expected") meths.toList } From 5cdb3dac73f3520beb898a6b8fa663d84dd88582 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 1 Jul 2020 11:23:56 +0200 Subject: [PATCH 22/38] Drop collective extensions from tests and docs --- compiler/test-resources/repl/i8716_i8717 | 10 +++++----- .../contextual/relationship-implicits.md | 17 ----------------- .../reference/other-new-features/indentation.md | 4 ++-- .../reference/other-new-features/opaques.md | 5 ++--- .../anonymous-instance-cannot-be-empty.check | 6 ------ .../anonymous-instance-cannot-be-empty.scala | 2 +- tests/neg/extension-cannot-have-type.check | 4 ---- tests/neg/extension-cannot-have-type.scala | 4 ++-- tests/neg/extension-method-not-allowed.check | 4 ---- tests/neg/extension-method-not-allowed.scala | 2 +- tests/neg/extension-methods.scala | 2 +- tests/neg/extensions-can-only-have-defs.check | 4 ---- tests/neg/extensions-can-only-have-defs.scala | 4 ++-- tests/neg/extmethod-overload.scala | 15 ++++++++------- tests/neg/i5455.scala | 2 +- tests/neg/i6801.scala | 2 +- tests/neg/i6900.scala | 2 +- tests/neg/i7529.scala | 2 +- tests/neg/i8050.scala | 2 +- tests/neg/missing-implicit1.scala | 2 +- tests/neg/missing-implicit5.check | 2 +- tests/neg/missing-implicit5.scala | 2 +- tests/pos-custom-args/i8875.scala | 2 +- tests/pos/endmarkers.scala | 2 +- tests/pos/i6900.scala | 2 +- tests/pos/i7084.scala | 2 +- tests/pos/i7087.scala | 2 +- tests/pos/i7401.scala | 6 +++--- tests/pos/i7403.scala | 2 +- tests/pos/i8181.scala | 2 +- tests/pos/i8188a.scala | 2 +- tests/pos/i9202.scala | 2 +- tests/pos/matrixOps.scala | 2 +- tests/pos/mirror-implicit-scope.scala | 4 ++-- tests/pos/reference/delegates.scala | 13 ++++++------- tests/pos/reference/opaque.scala | 2 +- tests/pos/tasty-reflect-opaque-api-proto.scala | 2 +- tests/run/LazyLists.scala | 6 +++--- tests/run/collective-extensions.scala | 2 +- tests/run/extension-specificity.scala | 4 ++-- tests/run/extmethod-overload.scala | 7 ++++--- tests/run/extmethods2.scala | 12 ++++++------ tests/run/i6902.scala | 4 ++-- tests/run/i8058.scala | 2 +- tests/run/inline-override-num.scala | 4 ++-- tests/run/instances-anonymous.scala | 6 +++--- tests/run/instances.scala | 10 +++++----- tests/semanticdb/expect/Givens.expect.scala | 4 ++-- tests/semanticdb/expect/Givens.scala | 4 ++-- 49 files changed, 88 insertions(+), 123 deletions(-) delete mode 100644 tests/neg/anonymous-instance-cannot-be-empty.check delete mode 100644 tests/neg/extension-cannot-have-type.check delete mode 100644 tests/neg/extension-method-not-allowed.check delete mode 100644 tests/neg/extensions-can-only-have-defs.check diff --git a/compiler/test-resources/repl/i8716_i8717 b/compiler/test-resources/repl/i8716_i8717 index 36c4b9197c0b..8d5058080c2c 100644 --- a/compiler/test-resources/repl/i8716_i8717 +++ b/compiler/test-resources/repl/i8716_i8717 @@ -2,13 +2,13 @@ scala>extension { } 1 |extension { } |^ |anonymous instance must implement a type or have at least one extension method -scala> extension as on -1 | extension as on +scala> extension as +1 | extension as | ^ | '(' expected, but eof found -scala> extension as on (s -1 | extension as on (s +scala> extension as (s +1 | extension as (s | ^ | ':' expected, but eof found -scala> extension as on (s: String) { def expand = ??? } +scala> extension as (s: String) { def expand = ??? } // defined object as diff --git a/docs/docs/reference/contextual/relationship-implicits.md b/docs/docs/reference/contextual/relationship-implicits.md index cbfcf614cbb7..7b22a84ad8ef 100644 --- a/docs/docs/reference/contextual/relationship-implicits.md +++ b/docs/docs/reference/contextual/relationship-implicits.md @@ -61,23 +61,6 @@ The synthesized type names are formed from Tuples are treated as transparent, i.e. a type `F[(X, Y)]` would get the synthesized name `F_X_Y`. Directly implemented function types `A => B` are represented as `A_to_B`. Function types used as arguments to other type constructors are represented as `Function`. -### Anonymous Collective Extensions - -Anonymous collective extensions also get compiler synthesized names, which are formed from - - - the prefix `extension_` - - the name of the first defined extension method - - the simple name of the first parameter type of this extension method - - the simple name(s) of the toplevel argument type constructors to this type. - -For example, the extension -```scala -extension on [T] (xs: List[T]) { - def second = ... -} -``` -gets the synthesized name `extension_second_List_T`. - ### Given Clauses Given clauses correspond largely to Scala-2's implicit parameter clauses. E.g. diff --git a/docs/docs/reference/other-new-features/indentation.md b/docs/docs/reference/other-new-features/indentation.md index f97163ac1296..80737afc3e3a 100644 --- a/docs/docs/reference/other-new-features/indentation.md +++ b/docs/docs/reference/other-new-features/indentation.md @@ -123,7 +123,7 @@ type T = A: given [T] with Ord[T] as Ord[List[T]]: def compare(x: List[T], y: List[T]) = ??? -extension on (xs: List[Int]): +extension (xs: List[Int]): def second: Int = xs.tail.head new A: @@ -256,7 +256,7 @@ For instance, the following end markers are all legal: end given end C - extension on (x: C): + extension (x: C): def ff: String = x.f ++ x.f end extension diff --git a/docs/docs/reference/other-new-features/opaques.md b/docs/docs/reference/other-new-features/opaques.md index dc8c883cc4e3..c5840fb34e15 100644 --- a/docs/docs/reference/other-new-features/opaques.md +++ b/docs/docs/reference/other-new-features/opaques.md @@ -21,7 +21,7 @@ object Logarithms { } // Extension methods define opaque types' public APIs - extension logarithmOps on (x: Logarithm) { + extension (x: Logarithm) { def toDouble: Double = math.exp(x) def + (y: Logarithm): Logarithm = Logarithm(math.exp(x) + math.exp(y)) def * (y: Logarithm): Logarithm = x + y @@ -36,8 +36,7 @@ Or in other words, within the scope it is treated as type alias, but this is opa where in consequence `Logarithm` is seen as an abstract type and has nothing to do with `Double`. The public API of `Logarithm` consists of the `apply` and `safe` methods defined in the companion object. -They convert from `Double`s to `Logarithm` values. Moreover, a collective extension `logarithmOps` provides the extension methods `toDouble` that converts the other way, -and operations `+` and `*` on `Logarithm` values. +They convert from `Double`s to `Logarithm` values. Moreover, an operation `toDouble` that converts the other way, and operations `+` and `*` are defined as extension methods on `Logarithm` values. The following operations would be valid because they use functionality implemented in the `Logarithms` object. ```scala diff --git a/tests/neg/anonymous-instance-cannot-be-empty.check b/tests/neg/anonymous-instance-cannot-be-empty.check deleted file mode 100644 index f6b50962ebc4..000000000000 --- a/tests/neg/anonymous-instance-cannot-be-empty.check +++ /dev/null @@ -1,6 +0,0 @@ --- [E154] Syntax Error: tests/neg/anonymous-instance-cannot-be-empty.scala:2:15 ---------------------------------------- -2 | extension on[T] (t: T) { } // error - | ^^^^^^^^ - | anonymous instance must implement a type or have at least one extension method - -longer explanation available when compiling with `-explain` diff --git a/tests/neg/anonymous-instance-cannot-be-empty.scala b/tests/neg/anonymous-instance-cannot-be-empty.scala index 3581b17ebe88..a36d85e115d9 100644 --- a/tests/neg/anonymous-instance-cannot-be-empty.scala +++ b/tests/neg/anonymous-instance-cannot-be-empty.scala @@ -1,3 +1,3 @@ object Test { - extension on[T] (t: T) { } // error + extension [T] (t: T) { } // error } \ No newline at end of file diff --git a/tests/neg/extension-cannot-have-type.check b/tests/neg/extension-cannot-have-type.check deleted file mode 100644 index b7bfc18ad778..000000000000 --- a/tests/neg/extension-cannot-have-type.check +++ /dev/null @@ -1,4 +0,0 @@ --- Error: tests/neg/extension-cannot-have-type.scala:3:10 -------------------------------------------------------------- -3 | def f[U](u: U): T = ??? // error : extension method cannot have type params - | ^ - | extension method cannot have type parameters since some were already given previously diff --git a/tests/neg/extension-cannot-have-type.scala b/tests/neg/extension-cannot-have-type.scala index 67470fe13f0f..c6fd2d3df4b4 100644 --- a/tests/neg/extension-cannot-have-type.scala +++ b/tests/neg/extension-cannot-have-type.scala @@ -1,5 +1,5 @@ object Test { - extension on[T] (t: T) { - def f[U](u: U): T = ??? // error : extension method cannot have type params + extension [T] (t: T) { + def f[U](u: U): T = ??? // error: extension method cannot have type parameters here, all type parameters go after `extension` } } \ No newline at end of file diff --git a/tests/neg/extension-method-not-allowed.check b/tests/neg/extension-method-not-allowed.check deleted file mode 100644 index 7b82a1e91901..000000000000 --- a/tests/neg/extension-method-not-allowed.check +++ /dev/null @@ -1,4 +0,0 @@ --- Error: tests/neg/extension-method-not-allowed.scala:3:15 ------------------------------------------------------------ -3 | extension (c: T) def f: T = ??? // error : No extension method allowed here - | ^^^^^^^^^^^^^^^^^^^^ - | extension clause can only define methods diff --git a/tests/neg/extension-method-not-allowed.scala b/tests/neg/extension-method-not-allowed.scala index ac4a21eafe8a..d5b85c54693f 100644 --- a/tests/neg/extension-method-not-allowed.scala +++ b/tests/neg/extension-method-not-allowed.scala @@ -1,5 +1,5 @@ object Test { - extension on[T] (t: T) { + extension [T] (t: T) { extension (c: T) def f: T = ??? // error : No extension method allowed here } } \ No newline at end of file diff --git a/tests/neg/extension-methods.scala b/tests/neg/extension-methods.scala index c576b837ac82..e075105762f9 100644 --- a/tests/neg/extension-methods.scala +++ b/tests/neg/extension-methods.scala @@ -10,7 +10,7 @@ object Test { "".l2 // error 1.l1 // error - extension on [T](xs: List[T]) { + extension [T](xs: List[T]) { extension (x: Int) def f1: T = ??? // error: No extension method allowed here, since collective parameters are given def f2[T]: T = ??? // error: T is already defined as type T def f3(xs: List[T]) = ??? // error: xs is already defined as value xs diff --git a/tests/neg/extensions-can-only-have-defs.check b/tests/neg/extensions-can-only-have-defs.check deleted file mode 100644 index c155678f6fed..000000000000 --- a/tests/neg/extensions-can-only-have-defs.check +++ /dev/null @@ -1,4 +0,0 @@ --- Error: tests/neg/extensions-can-only-have-defs.scala:3:8 ------------------------------------------------------------ -3 | val v: T = ??? // error : extensions can only have defs - | ^^^^^^^^^^^^^^ - | extension clause can only define methods diff --git a/tests/neg/extensions-can-only-have-defs.scala b/tests/neg/extensions-can-only-have-defs.scala index 225d065e66f7..ac21105a2c5f 100644 --- a/tests/neg/extensions-can-only-have-defs.scala +++ b/tests/neg/extensions-can-only-have-defs.scala @@ -1,5 +1,5 @@ object Test { - extension on[T] (t: T) { - val v: T = ??? // error : extensions can only have defs + extension [T] (t: T) { + val v: T = ??? // error // error } } \ No newline at end of file diff --git a/tests/neg/extmethod-overload.scala b/tests/neg/extmethod-overload.scala index 33fafb70ddd1..c680acda9144 100644 --- a/tests/neg/extmethod-overload.scala +++ b/tests/neg/extmethod-overload.scala @@ -1,11 +1,12 @@ object Test { - extension a on (x: Int) { - def |+| (y: Int) = x + y - } - - extension b on (x: Int) { - def |+| (y: String) = x + y.length - } + given a as AnyRef: + extension (x: Int) { + def |+| (y: Int) = x + y + } + given b as AnyRef: + extension (x: Int) { + def |+| (y: String) = x + y.length + } assert((1 |+| 2) == 3) // error ambiguous locally { diff --git a/tests/neg/i5455.scala b/tests/neg/i5455.scala index e496cd68541f..0d6c601ef985 100644 --- a/tests/neg/i5455.scala +++ b/tests/neg/i5455.scala @@ -11,7 +11,7 @@ object Library { def toInt(n: Nat): Int = n } - extension on (x: Nat) { + extension (x: Nat) { def * (y: Nat): Nat = x * y def toInt: Int = x } diff --git a/tests/neg/i6801.scala b/tests/neg/i6801.scala index b2a968cc7893..18295ca35246 100644 --- a/tests/neg/i6801.scala +++ b/tests/neg/i6801.scala @@ -1,4 +1,4 @@ -extension myNumericOps on [T](x: T) { +extension [T](x: T) { def + (y: T)(using n: Numeric[T]): T = n.plus(x,y) } def foo[T: Numeric](x: T) = 1f + x // error: no implicit argument of type Numeric[Any] diff --git a/tests/neg/i6900.scala b/tests/neg/i6900.scala index c9ead6a99051..7382c3a9df01 100644 --- a/tests/neg/i6900.scala +++ b/tests/neg/i6900.scala @@ -1,7 +1,7 @@ object Test2 { // Works with extension method - extension on [A](a: A): + extension [A](a: A): def foo[C]: C => A = _ => a // error: extension method cannot have type parameters 1.foo.foo diff --git a/tests/neg/i7529.scala b/tests/neg/i7529.scala index 55aa2902e348..cf4998de4af1 100644 --- a/tests/neg/i7529.scala +++ b/tests/neg/i7529.scala @@ -1,4 +1,4 @@ -extension fooOps on [A](a: A): +extension [A](a: A): @nonsense // error: not found: nonsense def foo = ??? \ No newline at end of file diff --git a/tests/neg/i8050.scala b/tests/neg/i8050.scala index 905361fe4e49..0bd19d0f14e8 100644 --- a/tests/neg/i8050.scala +++ b/tests/neg/i8050.scala @@ -1,5 +1,5 @@ object stuff: def exec(dir: Int) = ??? -extension on (a: Int): +extension (a: Int): inline def exec: Unit = stuff.exec("aaa") // error diff --git a/tests/neg/missing-implicit1.scala b/tests/neg/missing-implicit1.scala index 4bc611739f45..45ed1ed1857c 100644 --- a/tests/neg/missing-implicit1.scala +++ b/tests/neg/missing-implicit1.scala @@ -7,7 +7,7 @@ object testObjectInstance: object instances { given zipOption as Zip[Option] = ??? given traverseList as Traverse[List] = ??? - extension listExtension on [T](xs: List[T]): + extension [T](xs: List[T]): def second: T = xs.tail.head extension [T](xs: List[T]) def first: T = xs.head } diff --git a/tests/neg/missing-implicit5.check b/tests/neg/missing-implicit5.check index 62e5fb2c3a2d..810d570746ad 100644 --- a/tests/neg/missing-implicit5.check +++ b/tests/neg/missing-implicit5.check @@ -14,7 +14,7 @@ | | The following import might fix the problem: | - | import testObjectInstance.instances.listExtension + | import testObjectInstance.instances.second | -- [E008] Not Found Error: tests/neg/missing-implicit5.scala:17:17 ----------------------------------------------------- 17 | Array(1, 2, 3).first // error, no hint diff --git a/tests/neg/missing-implicit5.scala b/tests/neg/missing-implicit5.scala index 9e2544c66549..f7cc1679f280 100644 --- a/tests/neg/missing-implicit5.scala +++ b/tests/neg/missing-implicit5.scala @@ -7,7 +7,7 @@ object testObjectInstance: object instances { given zipOption as Zip[Option] = ??? given traverseList as Traverse[List] = ??? - extension listExtension on [T](xs: List[T]): + extension [T](xs: List[T]): def second: T = xs.tail.head extension [T](xs: List[T]) def first: T = xs.head } diff --git a/tests/pos-custom-args/i8875.scala b/tests/pos-custom-args/i8875.scala index ff2c394057ae..ac5e0bd5cf42 100644 --- a/tests/pos-custom-args/i8875.scala +++ b/tests/pos-custom-args/i8875.scala @@ -1,5 +1,5 @@ class A { - extension Ext on (a: Int) { + extension (a: Int) { def foo: Int = 1 } } \ No newline at end of file diff --git a/tests/pos/endmarkers.scala b/tests/pos/endmarkers.scala index f889e7d63f12..c4eb0d60771f 100644 --- a/tests/pos/endmarkers.scala +++ b/tests/pos/endmarkers.scala @@ -51,7 +51,7 @@ package p1.p2: end given end C - extension on (x: C): + extension (x: C): def ff: String = x.f end extension diff --git a/tests/pos/i6900.scala b/tests/pos/i6900.scala index 1975b641fcc2..45313c21921e 100644 --- a/tests/pos/i6900.scala +++ b/tests/pos/i6900.scala @@ -21,7 +21,7 @@ object Test1 { object Test2 { // Works with extension method - extension on [A, C](a: A): + extension [A, C](a: A): def foo: C => A = _ => a 1.foo.foo diff --git a/tests/pos/i7084.scala b/tests/pos/i7084.scala index 0eb5c5920570..2288cd3660d3 100644 --- a/tests/pos/i7084.scala +++ b/tests/pos/i7084.scala @@ -2,7 +2,7 @@ object Test { type Foo - extension on (y: Any) { + extension (y: Any) { def g(using Foo): Any = ??? } diff --git a/tests/pos/i7087.scala b/tests/pos/i7087.scala index f66a75ca4976..0948b83cee84 100644 --- a/tests/pos/i7087.scala +++ b/tests/pos/i7087.scala @@ -6,7 +6,7 @@ type F[T] = T match { case G[a] => String } -extension on [T](tup: T) { +extension [T](tup: T) { def g(using Foo: F[T]) = ??? } diff --git a/tests/pos/i7401.scala b/tests/pos/i7401.scala index dea94f10f842..fe7d11aba7a5 100644 --- a/tests/pos/i7401.scala +++ b/tests/pos/i7401.scala @@ -1,11 +1,11 @@ object Test { - extension ops on (a: Int) { + extension (a: Int) { def foo(i: Int): Unit = () def foo: Unit = () } val x: Int = 5 x.foo(4) x.foo - ops.extension_foo(x)(4) - ops.extension_foo(x) + Test.extension_foo(x)(4) + extension_foo(x) } \ No newline at end of file diff --git a/tests/pos/i7403.scala b/tests/pos/i7403.scala index 36817fc2c14c..36eec0e7c354 100644 --- a/tests/pos/i7403.scala +++ b/tests/pos/i7403.scala @@ -1,6 +1,6 @@ object Test { - extension on (i: Int): + extension (i: Int): def pow2 = i * i println(5.pow2) diff --git a/tests/pos/i8181.scala b/tests/pos/i8181.scala index 6dbf2b74660f..d7f1c337b6f9 100644 --- a/tests/pos/i8181.scala +++ b/tests/pos/i8181.scala @@ -1,6 +1,6 @@ object Main: def main(args: Array[String]): Unit = - extension on (a: AnyRef): + extension (a: AnyRef): def putout(): Unit = println(a) "blub".putout() diff --git a/tests/pos/i8188a.scala b/tests/pos/i8188a.scala index 3e9582e4ce9f..b360497d42c4 100644 --- a/tests/pos/i8188a.scala +++ b/tests/pos/i8188a.scala @@ -1,5 +1,5 @@ object Test { - extension StrDeco on (tree: String) { + extension (tree: String) { def show(using DummyImplicit): String = ??? def show(color: Boolean)(using DummyImplicit): String = ??? } diff --git a/tests/pos/i9202.scala b/tests/pos/i9202.scala index 5b744daefb5d..2579ffc17792 100644 --- a/tests/pos/i9202.scala +++ b/tests/pos/i9202.scala @@ -1,5 +1,5 @@ object i9202 { opaque type SomeUnrelatedOpaque = Int class A[T](val d: T) - extension Ex2SameNameToBeToImport on [T] (x: A[T]) { def value: T = x.d } + extension [T] (x: A[T]) { def value: T = x.d } } \ No newline at end of file diff --git a/tests/pos/matrixOps.scala b/tests/pos/matrixOps.scala index 28ea3c4d2013..7a635e7821bb 100644 --- a/tests/pos/matrixOps.scala +++ b/tests/pos/matrixOps.scala @@ -3,7 +3,7 @@ object Test: type Matrix = Array[Array[Double]] type Vector = Array[Double] - extension on (m: Matrix): + extension (m: Matrix): def nRows = m.length def nCols = m(0).length def row(i: Int): Vector = m(i) diff --git a/tests/pos/mirror-implicit-scope.scala b/tests/pos/mirror-implicit-scope.scala index dddf6da3123e..392675688b77 100644 --- a/tests/pos/mirror-implicit-scope.scala +++ b/tests/pos/mirror-implicit-scope.scala @@ -3,14 +3,14 @@ import scala.deriving._ object Test { object K0 { type Generic[T] = Mirror { type Scope = K0.type ; type MirroredType = T ; type MirroredElemTypes } - extension on [T <: Product](gen: Generic[T]) { + extension [T <: Product](gen: Generic[T]) { inline def toRepr (t: T): gen.MirroredElemTypes = Tuple.fromProduct(t).asInstanceOf } } object K1 { type Generic[F[_]] = Mirror { type Scope = K1.type ; type MirroredType = [X] =>> F[X] ; type MirroredElemTypes[_] } - extension on [F[_] <: Product, T](gen: Generic[F]) { + extension [F[_] <: Product, T](gen: Generic[F]) { inline def toRepr (t: F[T]): gen.MirroredElemTypes[T] = Tuple.fromProduct(t).asInstanceOf } } diff --git a/tests/pos/reference/delegates.scala b/tests/pos/reference/delegates.scala index 691e40c0063f..7e8cde964f2f 100644 --- a/tests/pos/reference/delegates.scala +++ b/tests/pos/reference/delegates.scala @@ -40,12 +40,12 @@ object Instances extends Common: if (fst != 0) fst else xs1.compareTo(ys1) end listOrd - extension stringOps on (xs: Seq[String]): + extension (xs: Seq[String]): def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) - extension on [T](xs: List[T]): + extension [T](xs: List[T]): def second = xs.tail.head def third = xs.tail.tail.head @@ -134,7 +134,7 @@ object PostConditions: def result[T](using x: WrappedResult[T]): T = x - extension on [T](x: T): + extension [T](x: T): def ensuring(condition: WrappedResult[T] ?=> Boolean): T = assert(condition(using x)) x @@ -154,12 +154,12 @@ object AnonymousInstances extends Common: val fst = x.compareTo(y) if (fst != 0) fst else xs1.compareTo(ys1) - extension on (xs: Seq[String]): + extension (xs: Seq[String]): def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) - extension on [T](xs: List[T]): + extension [T](xs: List[T]): def second = xs.tail.head given [From, To](using c: Convertible[From, To]) as Convertible[List[From], List[To]]: @@ -207,8 +207,7 @@ object Implicits extends Common: object Test extends App: Instances.test() - import PostConditions.result - import PostConditions.{given _} + import PostConditions.{result, ensuring} val s = List(1, 2, 3).sum s.ensuring(result == 6) end Test diff --git a/tests/pos/reference/opaque.scala b/tests/pos/reference/opaque.scala index 58340efceb7f..cf1271d01382 100644 --- a/tests/pos/reference/opaque.scala +++ b/tests/pos/reference/opaque.scala @@ -12,7 +12,7 @@ object Logarithms { } // Extension methods define opaque types' public APIs - extension on (x: Logarithm) { + extension (x: Logarithm) { def toDouble: Double = math.exp(x) def + (y: Logarithm): Logarithm = Logarithm(math.exp(x) + math.exp(y)) def * (y: Logarithm): Logarithm = Logarithm(x + y) diff --git a/tests/pos/tasty-reflect-opaque-api-proto.scala b/tests/pos/tasty-reflect-opaque-api-proto.scala index 563a43598e61..fe077fd5b15e 100644 --- a/tests/pos/tasty-reflect-opaque-api-proto.scala +++ b/tests/pos/tasty-reflect-opaque-api-proto.scala @@ -10,7 +10,7 @@ class Reflect(val internal: CompilerInterface) { opaque type Term <: Tree = internal.Term object Tree { - extension ops on (tree: Tree) { + extension (tree: Tree) { def show: String = ??? } } diff --git a/tests/run/LazyLists.scala b/tests/run/LazyLists.scala index c13949ce8454..b67c1e70fb42 100644 --- a/tests/run/LazyLists.scala +++ b/tests/run/LazyLists.scala @@ -55,7 +55,7 @@ package xcollections: if it.hasNext then set(it.next, fromIterator (it)) else empty - extension on [T, U >: T](xs: LazyList[T]): + extension [T, U >: T](xs: LazyList[T]): def #::(x: U): LazyList[U] = new: protected def force(): LazyList[U] = set(x, xs) @@ -65,7 +65,7 @@ package xcollections: if xs.isEmpty then ys.forced() else set(xs.head, xs.tail ++ ys) - extension on [T, U](xs: LazyList[T]): + extension [T, U](xs: LazyList[T]): def map(f: T => U): LazyList[U] = new: protected def force() = if xs.isEmpty then empty @@ -80,7 +80,7 @@ package xcollections: if xs.isEmpty then z else xs.tail.foldLeft(f(z, xs.head))(f) - extension on [T](xs: LazyList[T]): + extension [T](xs: LazyList[T]): def filter(p: T => Boolean): LazyList[T] = new: protected def force(): LazyList[T] = if xs.isEmpty then empty diff --git a/tests/run/collective-extensions.scala b/tests/run/collective-extensions.scala index 12cdb69682eb..352e5c671951 100644 --- a/tests/run/collective-extensions.scala +++ b/tests/run/collective-extensions.scala @@ -1,4 +1,4 @@ -extension on (x: String): +extension (x: String): def foo(y: String): String = x ++ y def bar(y: String): String = foo(y) def baz(y: String): String = diff --git a/tests/run/extension-specificity.scala b/tests/run/extension-specificity.scala index e60f22637ce8..0d9b6c9ceb88 100644 --- a/tests/run/extension-specificity.scala +++ b/tests/run/extension-specificity.scala @@ -1,10 +1,10 @@ class A class B extends A -extension a on (x: A): +extension (x: A): def foo: Int = 1 -extension b on (x: B): +extension (x: B): def foo: Int = 2 @main def Test = diff --git a/tests/run/extmethod-overload.scala b/tests/run/extmethod-overload.scala index 42f1109492fc..ec921c382185 100644 --- a/tests/run/extmethod-overload.scala +++ b/tests/run/extmethod-overload.scala @@ -97,9 +97,10 @@ object Test extends App { extension (x: Int) def yy(y: Int) = x + y } - extension on (x: Int) { - def yy (y: Int) = x - y - } + given AnyRef: + extension (x: Int) { + def yy (y: Int) = x - y + } import D._ assert((1 yy 2) == 3) // imported extension method takes precedence diff --git a/tests/run/extmethods2.scala b/tests/run/extmethods2.scala index ac423a3b395f..61766a4c3a79 100644 --- a/tests/run/extmethods2.scala +++ b/tests/run/extmethods2.scala @@ -16,26 +16,26 @@ object Test extends App { test(using TC()) object A { - extension listOps on [T](xs: List[T]) { + extension [T](xs: List[T]) { def second: T = xs.tail.head def third: T = xs.tail.tail.head def concat(ys: List[T]) = xs ++ ys } - extension polyListOps on [T, U](xs: List[T]) { + extension [T, U](xs: List[T]) { def zipp(ys: List[U]): List[(T, U)] = xs.zip(ys) } - extension on (xs: List[Int]) { + extension (xs: List[Int]) { def prod = (1 /: xs)(_ * _) } } object B { - import A.{given _} + import A._ val xs = List(1, 2, 3) assert(xs.second[Int] == 2) assert(xs.third == 3) - assert(A.listOps.extension_second[Int](xs) == 2) - assert(A.listOps.extension_third(xs) == 3) + assert(A.extension_second[Int](xs) == 2) + assert(A.extension_third(xs) == 3) assert(xs.prod == 6) assert(xs.concat(xs).length == 6) assert(xs.zipp(xs).map(_ + _).prod == 36) diff --git a/tests/run/i6902.scala b/tests/run/i6902.scala index 18729d1df937..1cfadd58108f 100644 --- a/tests/run/i6902.scala +++ b/tests/run/i6902.scala @@ -1,6 +1,6 @@ object Test { - extension on [A](a: A) { def <<< : A = a } - extension on (b: Int) { def <<<< : Int = b } + extension [A](a: A) { def <<< : A = a } + extension (b: Int) { def <<<< : Int = b } def main(args: Array[String]): Unit = { 1.<<< diff --git a/tests/run/i8058.scala b/tests/run/i8058.scala index b9ad34ba7282..4cef0ba6db1b 100644 --- a/tests/run/i8058.scala +++ b/tests/run/i8058.scala @@ -1,4 +1,4 @@ -extension on (x: Array[Char]): +extension (x: Array[Char]): inline def swap(i: Int, j: Int) : Unit = val v = x(i) x(i) = x(j) diff --git a/tests/run/inline-override-num.scala b/tests/run/inline-override-num.scala index 2a5acf1c2553..9770cba7f2da 100644 --- a/tests/run/inline-override-num.scala +++ b/tests/run/inline-override-num.scala @@ -8,12 +8,12 @@ object Num { } given IntNum - extension Extension on [T](inline x: T)(using inline num: Num[T]) { + extension [T](inline x: T)(using inline num: Num[T]) { inline def +(inline y: T): T = num.plus(x, y) } } -import Num.Extension._ +import Num.+ inline def twiceInlined[T: Num](x : T): T = x + x def twice[T: Num](x : T): T = x + x diff --git a/tests/run/instances-anonymous.scala b/tests/run/instances-anonymous.scala index 5c0e53bd7ef4..d2df14152cd4 100644 --- a/tests/run/instances-anonymous.scala +++ b/tests/run/instances-anonymous.scala @@ -8,7 +8,7 @@ object Test extends App { case class Circle(x: Double, y: Double, radius: Double) - extension on (c: Circle) { + extension (c: Circle) { def circumference: Double = c.radius * math.Pi * 2 } @@ -25,13 +25,13 @@ object Test extends App { val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - extension on [T](xs: Seq[T]) { + extension [T](xs: Seq[T]) { def second = xs.tail.head } assert(names.longestStrings.second == "world") - extension on [T](xs: List[List[T]]) { + extension [T](xs: List[List[T]]) { def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) } diff --git a/tests/run/instances.scala b/tests/run/instances.scala index a7ab9767021f..ea0e1038ba70 100644 --- a/tests/run/instances.scala +++ b/tests/run/instances.scala @@ -8,14 +8,14 @@ object Test extends App { case class Circle(x: Double, y: Double, radius: Double) - extension circleOps on (c: Circle): + extension (c: Circle): def circumference: Double = c.radius * math.Pi * 2 val circle = new Circle(1, 1, 2.0) - assert(circle.circumference == circleOps.extension_circumference(circle)) + assert(circle.circumference == Test.extension_circumference(circle)) - extension stringOps on (xs: Seq[String]): + extension (xs: Seq[String]): def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) @@ -23,12 +23,12 @@ object Test extends App { val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - extension on [T](xs: Seq[T]): + extension [T](xs: Seq[T]): def second = xs.tail.head assert(names.longestStrings.second == "world") - extension listListOps on [T](xs: List[List[T]]): + extension [T](xs: List[List[T]]): def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) extension [T](x: T) def :: (xs: Seq[T]) = x +: xs diff --git a/tests/semanticdb/expect/Givens.expect.scala b/tests/semanticdb/expect/Givens.expect.scala index 5de094afbd0c..d2259751ae91 100644 --- a/tests/semanticdb/expect/Givens.expect.scala +++ b/tests/semanticdb/expect/Givens.expect.scala @@ -3,10 +3,10 @@ package b object Givens/*<-a::b::Givens.*/: - extension on [A](any: A): + extension [A](any: A): def sayHel/*<-a::b::Givens.extension_extension_sayHello_A.*//*<-a::b::Givens.extension_extension_sayHello_A.extension_sayHello().[A]*//*<-a::b::Givens.extension_extension_sayHello_A.extension_sayHello().(any)*//*->a::b::Givens.extension_extension_sayHello_A.extension_sayHello().[A]*/lo = s"Hello/*<-a::b::Givens.extension_extension_sayHello_A.extension_sayHello().*//*->scala::StringContext.apply().*/, I am $any/*->a::b::Givens.extension_extension_sayHello_A.extension_sayHello().(any)*/"/*->scala::StringContext#s().*/ - extension on [B](any: B): + extension [B](any: B): def sayGoodb/*<-a::b::Givens.extension_extension_sayGoodbye_B.*//*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().[B]*//*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().[B]*//*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().(any)*//*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().(any)*//*->a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().[B]*//*->a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().[B]*/ye = s"Goodb/*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().*//*->scala::StringContext.apply().*/ye, from $any/*->a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().(any)*/"/*->scala::StringContext#s().*/ def saySoLong = s"So Lo/*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().*//*->scala::StringContext.apply().*/ng, from $any/*->a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().(any)*/"/*->scala::StringContext#s().*/ diff --git a/tests/semanticdb/expect/Givens.scala b/tests/semanticdb/expect/Givens.scala index b265a782dea3..90a6ebe2ad32 100644 --- a/tests/semanticdb/expect/Givens.scala +++ b/tests/semanticdb/expect/Givens.scala @@ -3,10 +3,10 @@ package b object Givens: - extension on [A](any: A): + extension [A](any: A): def sayHello = s"Hello, I am $any" - extension on [B](any: B): + extension [B](any: B): def sayGoodbye = s"Goodbye, from $any" def saySoLong = s"So Long, from $any" From ce6d9cabc3f1a57bda8cfd1619f445be5a9029d7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 1 Jul 2020 12:03:26 +0200 Subject: [PATCH 23/38] Drop outdated repl test --- compiler/test-resources/repl/i8716_i8717 | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 compiler/test-resources/repl/i8716_i8717 diff --git a/compiler/test-resources/repl/i8716_i8717 b/compiler/test-resources/repl/i8716_i8717 deleted file mode 100644 index 8d5058080c2c..000000000000 --- a/compiler/test-resources/repl/i8716_i8717 +++ /dev/null @@ -1,14 +0,0 @@ -scala>extension { } -1 |extension { } - |^ - |anonymous instance must implement a type or have at least one extension method -scala> extension as -1 | extension as - | ^ - | '(' expected, but eof found -scala> extension as (s -1 | extension as (s - | ^ - | ':' expected, but eof found -scala> extension as (s: String) { def expand = ??? } -// defined object as From 40667a683323ccfc131bba512db51fea221598ad Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 1 Jul 2020 12:36:12 +0200 Subject: [PATCH 24/38] Update semanticDB expect files --- tests/semanticdb/expect/Givens.expect.scala | 16 +++--- tests/semanticdb/metac.expect | 62 ++++++++++----------- 2 files changed, 37 insertions(+), 41 deletions(-) diff --git a/tests/semanticdb/expect/Givens.expect.scala b/tests/semanticdb/expect/Givens.expect.scala index d2259751ae91..76cbbab42812 100644 --- a/tests/semanticdb/expect/Givens.expect.scala +++ b/tests/semanticdb/expect/Givens.expect.scala @@ -3,16 +3,16 @@ package b object Givens/*<-a::b::Givens.*/: - extension [A](any: A): - def sayHel/*<-a::b::Givens.extension_extension_sayHello_A.*//*<-a::b::Givens.extension_extension_sayHello_A.extension_sayHello().[A]*//*<-a::b::Givens.extension_extension_sayHello_A.extension_sayHello().(any)*//*->a::b::Givens.extension_extension_sayHello_A.extension_sayHello().[A]*/lo = s"Hello/*<-a::b::Givens.extension_extension_sayHello_A.extension_sayHello().*//*->scala::StringContext.apply().*/, I am $any/*->a::b::Givens.extension_extension_sayHello_A.extension_sayHello().(any)*/"/*->scala::StringContext#s().*/ + extension [A/*<-a::b::Givens.extension_sayHello().[A]*/](any/*<-a::b::Givens.extension_sayHello().(any)*/: A/*->a::b::Givens.extension_sayHello().[A]*/): + def sayHello = s"Hello/*<-a::b::Givens.extension_sayHello().*//*->scala::StringContext.apply().*/, I am $any/*->a::b::Givens.extension_sayHello().(any)*/"/*->scala::StringContext#s().*/ - extension [B](any: B): - def sayGoodb/*<-a::b::Givens.extension_extension_sayGoodbye_B.*//*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().[B]*//*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().[B]*//*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().(any)*//*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().(any)*//*->a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().[B]*//*->a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().[B]*/ye = s"Goodb/*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().*//*->scala::StringContext.apply().*/ye, from $any/*->a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().(any)*/"/*->scala::StringContext#s().*/ - def saySoLong = s"So Lo/*<-a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().*//*->scala::StringContext.apply().*/ng, from $any/*->a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().(any)*/"/*->scala::StringContext#s().*/ + extension [B/*<-a::b::Givens.extension_sayGoodbye().[B]*//*<-a::b::Givens.extension_saySoLong().[B]*/](any/*<-a::b::Givens.extension_sayGoodbye().(any)*//*<-a::b::Givens.extension_saySoLong().(any)*/: B/*->a::b::Givens.extension_sayGoodbye().[B]*//*->a::b::Givens.extension_saySoLong().[B]*/): + def sayGoodbye = s"Goodb/*<-a::b::Givens.extension_sayGoodbye().*//*->scala::StringContext.apply().*/ye, from $any/*->a::b::Givens.extension_sayGoodbye().(any)*/"/*->scala::StringContext#s().*/ + def saySoLong = s"So Lo/*<-a::b::Givens.extension_saySoLong().*//*->scala::StringContext.apply().*/ng, from $any/*->a::b::Givens.extension_saySoLong().(any)*/"/*->scala::StringContext#s().*/ - val hello1/*<-a::b::Givens.hello1.*/ = /*->a::b::Givens.extension_extension_sayHello_A.extension_sayHello().*/1.sayHello - val goodbye1/*<-a::b::Givens.goodbye1.*/ = /*->a::b::Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().*/1.sayGoodbye - val soLong1/*<-a::b::Givens.soLong1.*/ = /*->a::b::Givens.extension_extension_sayGoodbye_B.extension_saySoLong().*/1.saySoLong + val hello1/*<-a::b::Givens.hello1.*/ = /*->a::b::Givens.extension_sayHello().*/1.sayHello + val goodbye1/*<-a::b::Givens.goodbye1.*/ = /*->a::b::Givens.extension_sayGoodbye().*/1.sayGoodbye + val soLong1/*<-a::b::Givens.soLong1.*/ = /*->a::b::Givens.extension_saySoLong().*/1.saySoLong trait Monoid/*<-a::b::Givens.Monoid#*/[A/*<-a::b::Givens.Monoid#[A]*/]: def empty/*<-a::b::Givens.Monoid#empty().*/: A/*->a::b::Givens.Monoid#[A]*/ diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 948a90b5abea..afb28b83d02f 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -1204,8 +1204,8 @@ Schema => SemanticDB v4 Uri => Givens.scala Text => empty Language => Scala -Symbols => 31 entries -Occurrences => 72 entries +Symbols => 29 entries +Occurrences => 70 entries Symbols: a/b/Givens. => final object Givens @@ -1216,17 +1216,15 @@ a/b/Givens.Monoid#empty(). => abstract method empty a/b/Givens.Monoid#extension_combine(). => abstract method extension_combine a/b/Givens.Monoid#extension_combine().(x) => param x a/b/Givens.Monoid#extension_combine().(y) => param y -a/b/Givens.extension_extension_sayGoodbye_B. => final implicit object extension_extension_sayGoodbye_B -a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye(). => method extension_sayGoodbye -a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().(any) => param any -a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().[B] => typeparam B -a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong(). => method extension_saySoLong -a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong().(any) => param any -a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong().[B] => typeparam B -a/b/Givens.extension_extension_sayHello_A. => final implicit object extension_extension_sayHello_A -a/b/Givens.extension_extension_sayHello_A.extension_sayHello(). => method extension_sayHello -a/b/Givens.extension_extension_sayHello_A.extension_sayHello().(any) => param any -a/b/Givens.extension_extension_sayHello_A.extension_sayHello().[A] => typeparam A +a/b/Givens.extension_sayGoodbye(). => method extension_sayGoodbye +a/b/Givens.extension_sayGoodbye().(any) => param any +a/b/Givens.extension_sayGoodbye().[B] => typeparam B +a/b/Givens.extension_sayHello(). => method extension_sayHello +a/b/Givens.extension_sayHello().(any) => param any +a/b/Givens.extension_sayHello().[A] => typeparam A +a/b/Givens.extension_saySoLong(). => method extension_saySoLong +a/b/Givens.extension_saySoLong().(any) => param any +a/b/Givens.extension_saySoLong().[B] => typeparam B a/b/Givens.foo(). => method foo a/b/Givens.foo().(A) => implicit param A a/b/Givens.foo().[A] => typeparam A @@ -1244,35 +1242,33 @@ Occurrences: [0:8..0:9): a <- a/ [1:8..1:9): b <- a/b/ [3:7..3:13): Givens <- a/b/Givens. -[5:12..6:14): <- a/b/Givens.extension_extension_sayHello_A. -[5:16..5:17): A <- a/b/Givens.extension_extension_sayHello_A.extension_sayHello().[A] -[5:19..5:22): any <- a/b/Givens.extension_extension_sayHello_A.extension_sayHello().(any) -[5:24..5:25): A -> a/b/Givens.extension_extension_sayHello_A.extension_sayHello().[A] -[6:8..6:26): sayHello = s"Hello <- a/b/Givens.extension_extension_sayHello_A.extension_sayHello(). +[5:13..5:14): A <- a/b/Givens.extension_sayHello().[A] +[5:16..5:19): any <- a/b/Givens.extension_sayHello().(any) +[5:21..5:22): A -> a/b/Givens.extension_sayHello().[A] +[6:8..6:26): sayHello = s"Hello <- a/b/Givens.extension_sayHello(). [6:21..6:21): -> scala/StringContext.apply(). -[6:34..6:37): any -> a/b/Givens.extension_extension_sayHello_A.extension_sayHello().(any) +[6:34..6:37): any -> a/b/Givens.extension_sayHello().(any) [6:37..6:38): " -> scala/StringContext#s(). -[8:12..9:16): <- a/b/Givens.extension_extension_sayGoodbye_B. -[8:16..8:17): B <- a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().[B] -[8:16..8:17): B <- a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong().[B] -[8:19..8:22): any <- a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().(any) -[8:19..8:22): any <- a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong().(any) -[8:24..8:25): B -> a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().[B] -[8:24..8:25): B -> a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong().[B] -[9:8..9:28): sayGoodbye = s"Goodb <- a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye(). +[8:13..8:14): B <- a/b/Givens.extension_sayGoodbye().[B] +[8:13..8:14): B <- a/b/Givens.extension_saySoLong().[B] +[8:16..8:19): any <- a/b/Givens.extension_sayGoodbye().(any) +[8:16..8:19): any <- a/b/Givens.extension_saySoLong().(any) +[8:21..8:22): B -> a/b/Givens.extension_sayGoodbye().[B] +[8:21..8:22): B -> a/b/Givens.extension_saySoLong().[B] +[9:8..9:28): sayGoodbye = s"Goodb <- a/b/Givens.extension_sayGoodbye(). [9:23..9:23): -> scala/StringContext.apply(). -[9:38..9:41): any -> a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye().(any) +[9:38..9:41): any -> a/b/Givens.extension_sayGoodbye().(any) [9:41..9:42): " -> scala/StringContext#s(). -[10:8..10:27): saySoLong = s"So Lo <- a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong(). +[10:8..10:27): saySoLong = s"So Lo <- a/b/Givens.extension_saySoLong(). [10:22..10:22): -> scala/StringContext.apply(). -[10:37..10:40): any -> a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong().(any) +[10:37..10:40): any -> a/b/Givens.extension_saySoLong().(any) [10:40..10:41): " -> scala/StringContext#s(). [12:6..12:12): hello1 <- a/b/Givens.hello1. -[12:15..12:15): -> a/b/Givens.extension_extension_sayHello_A.extension_sayHello(). +[12:15..12:15): -> a/b/Givens.extension_sayHello(). [13:6..13:14): goodbye1 <- a/b/Givens.goodbye1. -[13:17..13:17): -> a/b/Givens.extension_extension_sayGoodbye_B.extension_sayGoodbye(). +[13:17..13:17): -> a/b/Givens.extension_sayGoodbye(). [14:6..14:13): soLong1 <- a/b/Givens.soLong1. -[14:16..14:16): -> a/b/Givens.extension_extension_sayGoodbye_B.extension_saySoLong(). +[14:16..14:16): -> a/b/Givens.extension_saySoLong(). [16:8..16:14): Monoid <- a/b/Givens.Monoid# [16:14..16:17): <- a/b/Givens.Monoid#``(). [16:15..16:16): A <- a/b/Givens.Monoid#[A] From 3188b43a9dce92f37303055541d17eb66d0e6f88 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 1 Jul 2020 14:21:45 +0200 Subject: [PATCH 25/38] Avoid double-counting of extension methods --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 78f7a20ec250..98385507a1fa 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -249,7 +249,9 @@ object Implicits { val extensionCandidates = if considerExtension then - companionRefs.toList.flatMap(matchingCandidate(_, extensionOnly = true)) + companionRefs.toList + .filterConserve(!_.symbol.isOneOf(GivenOrImplicit)) // implicit objects are already in `refs` + .flatMap(matchingCandidate(_, extensionOnly = true)) else Nil val implicitCandidates = From 2cd1092fdd2e70be7dcd5d5f212a8daf99aed927 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 1 Jul 2020 15:57:15 +0200 Subject: [PATCH 26/38] Handle missing case for extension/conversion ambiguity If a candidate can be both a conversion and an extension, which happens if it is overloaded, and the extension is not applicable, try the conversion. --- .../dotty/tools/dotc/typer/Applications.scala | 2 +- .../dotty/tools/dotc/typer/Implicits.scala | 22 +++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 01438409d4f4..b8efedf721ab 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -2089,7 +2089,7 @@ trait Applications extends Compatibility { * * where comes from `pt` if it is a (possibly ignored) PolyProto. */ - def extMethodApply(methodRef: untpd.Tree, receiver: Tree, pt: Type)(using Context) = { + def extMethodApply(methodRef: untpd.Tree, receiver: Tree, pt: Type)(using Context): Tree = { /** Integrate the type arguments from `currentPt` into `methodRef`, and produce * a matching expected type. * If `currentPt` is ignored, the new expected type will be ignored too. diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 98385507a1fa..5833873e4b5b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1043,13 +1043,21 @@ trait Implicits { self: Typer => } pt match case SelectionProto(name: TermName, mbrType, _, _) if cand.isExtension => - val result = extMethodApply(untpd.Select(untpdGenerated, name.toExtensionName), argument, mbrType) - if !ctx.reporter.hasErrors && cand.isConversion then - val testCtx = ctx.fresh.setExploreTyperState() - tryConversion(using testCtx) - if testCtx.reporter.hasErrors then - ctx.error(em"ambiguous implicit: $generated is eligible both as an implicit conversion and as an extension method container") - result + def tryExtension(using Context) = + extMethodApply(untpd.Select(untpdGenerated, name.toExtensionName), argument, mbrType) + if cand.isConversion then + val extensionCtx, conversionCtx = ctx.fresh.setNewTyperState() + val extensionResult = tryExtension(using extensionCtx) + val conversionResult = tryConversion(using conversionCtx) + if !extensionCtx.reporter.hasErrors then + extensionCtx.typerState.commit() + if !conversionCtx.reporter.hasErrors then + ctx.error(em"ambiguous implicit: $generated is eligible both as an implicit conversion and as an extension method container") + extensionResult + else + conversionCtx.typerState.commit() + conversionResult + else tryExtension case _ => tryConversion } From 85298852d776e4207f6b21a81266e1710035d321 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 1 Jul 2020 21:08:10 +0200 Subject: [PATCH 27/38] Check no use of extension_ in definitions at Desugar --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 9 +++++++-- compiler/src/dotty/tools/dotc/typer/Typer.scala | 3 --- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 558458054420..7a0adf49e4ea 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -985,16 +985,21 @@ object desugar { /** The normalized name of `mdef`. This means * 1. Check that the name does not redefine a Scala core class. * If it does redefine, issue an error and return a mangled name instead of the original one. - * 2. If the name is missing (this can be the case for instance definitions), invent one instead. + * 2. Check that the name does not start with `extension_` unless the + * method is an extension method. + * 3. If the name is missing (this can be the case for instance definitions), invent one instead. */ def normalizeName(mdef: MemberDef, impl: Tree)(implicit ctx: Context): Name = { var name = mdef.name if (name.isEmpty) name = name.likeSpaced(inventGivenOrExtensionName(impl)) + def errPos = mdef.source.atSpan(mdef.nameSpan) if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) { val kind = if (name.isTypeName) "class" else "object" - ctx.error(IllegalRedefinitionOfStandardKind(kind, name), mdef.sourcePos) + ctx.error(IllegalRedefinitionOfStandardKind(kind, name), errPos) name = name.errorName } + if name.isExtensionName && !mdef.mods.is(Extension) then + ctx.error(em"illegal method name: $name may not start with `extension_`", errPos) name } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 00aaf5672645..df8de98353fc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1934,9 +1934,6 @@ class Typer extends Namer val ddef2 = assignType(cpy.DefDef(ddef)(name, tparams1, vparamss1, tpt1, rhs1), sym) checkSignatureRepeatedParam(sym) - if name.isExtensionName && !sym.is(Extension) then - ctx.error(em"illegal method name: $name may not start with `extension_`", - ddef.source.atSpan(ddef.nameSpan)) ddef2.setDefTree //todo: make sure dependent method types do not depend on implicits or by-name params } From c1b515003105683efc24afb217cea3887d520742 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 1 Jul 2020 21:23:29 +0200 Subject: [PATCH 28/38] Avoid printing same extension method attempt multiple times --- compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 540901935613..158214e38f18 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -157,10 +157,10 @@ object ErrorReporting { if qualType.derivesFrom(defn.DynamicClass) then "\npossible cause: maybe a wrong Dynamic method signature?" else if attempts.nonEmpty then + val attemptStrings = attempts.map(_.showIndented(4)).distinct val extMethods = - if attempts.length > 1 then "Extension methods were" + if attemptStrings.length > 1 then "Extension methods were" else "An extension method was" - val attemptStrings = attempts.map(_.showIndented(4)) i""". |$extMethods tried, but could not be fully constructed: | From e33d36f0ecb8c4b4cfac9a9f14e5a6d0fe44a2ca Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 1 Jul 2020 21:53:13 +0200 Subject: [PATCH 29/38] Drop check file Check files should be only added if they test a tricky error message, typically one with lots of configurable parts. A simple string error message should never lead to a check file. Having too many check file just causes unnecessary friction for me and the other maintainers. --- tests/neg/scalaStandardRedefinition.check | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 tests/neg/scalaStandardRedefinition.check diff --git a/tests/neg/scalaStandardRedefinition.check b/tests/neg/scalaStandardRedefinition.check deleted file mode 100644 index ca31f76ff523..000000000000 --- a/tests/neg/scalaStandardRedefinition.check +++ /dev/null @@ -1,6 +0,0 @@ --- [E149] Syntax Error: tests/neg/scalaStandardRedefinition.scala:2:8 -------------------------------------------------- -2 | class Any() // error - | ^^^^^^^^^^^ - | illegal redefinition of standard class Any - -longer explanation available when compiling with `-explain` From 026b58a023ceec1583caf8f3c469f13b96d5c78c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 1 Jul 2020 23:41:51 +0200 Subject: [PATCH 30/38] Fix #8311: Propagate more information about extension method arguments --- .../src/dotty/tools/dotc/typer/Typer.scala | 11 ++++++++++- tests/pos/i8311.scala | 19 +++++++++++++++++++ tests/pos/reference/extension-methods.scala | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i8311.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index df8de98353fc..355a3135be1c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3251,12 +3251,21 @@ class Typer extends Namer case IgnoredProto(_: FunOrPolyProto) => false case _ => true } + // If the expected type is a selection of an extension method, deepen it + // to also propagate the argument type (which is the receiver we have + // typechecked already). This is needed for i8311.scala. Doing so + // for all expected types does not work since it would block the case + // where we have an argument that must be converted with another + // implicit conversion to the receiver type. + def sharpenedPt = pt match + case pt: SelectionProto if pt.name.isExtensionName => pt.deepenProto + case _ => pt var resMatch: Boolean = false wtp match { case wtp: ExprType => readaptSimplified(tree.withType(wtp.resultType)) case wtp: MethodType if wtp.isImplicitMethod && - ({ resMatch = constrainResult(tree.symbol, wtp, pt); resMatch } || !functionExpected) => + ({ resMatch = constrainResult(tree.symbol, wtp, sharpenedPt); resMatch } || !functionExpected) => if (resMatch || ctx.mode.is(Mode.ImplicitsEnabled)) adaptNoArgsImplicitMethod(wtp) else diff --git a/tests/pos/i8311.scala b/tests/pos/i8311.scala new file mode 100644 index 000000000000..fa6c68b40536 --- /dev/null +++ b/tests/pos/i8311.scala @@ -0,0 +1,19 @@ + +trait Show[O]: + extension (o: O) + def show: String + +class Box[A] +class Foo + +object test: + + given box[A](using Show[A]) as Show[Box[A]] = _.toString + given foo as Show[Foo] = _.toString + + def run(s: Box[Box[Foo]]): Unit = + val x = summon[Show[Box[Box[Foo]]]] + x.extension_show(s) + val r: String = box.extension_show(s) + println(s"step: ${box[Box[Foo]].extension_show(s)}") + println(s"step: ${s.show}") diff --git a/tests/pos/reference/extension-methods.scala b/tests/pos/reference/extension-methods.scala index 2dcab8cfd59e..5a7a8a58964d 100644 --- a/tests/pos/reference/extension-methods.scala +++ b/tests/pos/reference/extension-methods.scala @@ -104,7 +104,7 @@ object ExtMethods: summon[Ord[Lst[Lst[Int]]]] assert(Lst.ord[Lst[Int]].extension_less(xss)(Lst(Lst(3)))) - // fails type inference: assert(xss `less` Lst(Lst(3))) + assert(xss `less` Lst(Lst(3))) assert(xss.flatten `less` Lst(3)) extension (s: String) From 01ec2888e0b4f85c00dbf0402965598fdf20e2ba Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 1 Jul 2020 23:46:32 +0200 Subject: [PATCH 31/38] Refine criterion for deepening after ambiguities --- .../src/dotty/tools/dotc/typer/Typer.scala | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 355a3135be1c..635b1cead7d7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2998,7 +2998,9 @@ class Typer extends Namer arg.tpe match { case failed: AmbiguousImplicits => val pt1 = pt.deepenProto - if ((pt1 `ne` pt) && constrainResult(tree.symbol, wtp, pt1)) implicitArgs(formals, argIndex, pt1) + if (pt1 `ne` pt) && (pt1 ne sharpenedPt) + && constrainResult(tree.symbol, wtp, pt1) + then implicitArgs(formals, argIndex, pt1) else arg :: implicitArgs(formals1, argIndex + 1, pt1) case failed: SearchFailureType if !hasDefaultParams => // no need to search further, the adapt fails in any case @@ -3243,6 +3245,16 @@ class Typer extends Namer case _ => tp } + // If the expected type is a selection of an extension method, deepen it + // to also propagate the argument type (which is the receiver we have + // typechecked already). This is needed for i8311.scala. Doing so + // for all expected types does not work since it would block the case + // where we have an argument that must be converted with another + // implicit conversion to the receiver type. + def sharpenedPt = pt match + case pt: SelectionProto if pt.name.isExtensionName => pt.deepenProto + case _ => pt + def adaptNoArgs(wtp: Type): Tree = { val ptNorm = underlyingApplied(pt) def functionExpected = defn.isFunctionType(ptNorm) @@ -3251,15 +3263,6 @@ class Typer extends Namer case IgnoredProto(_: FunOrPolyProto) => false case _ => true } - // If the expected type is a selection of an extension method, deepen it - // to also propagate the argument type (which is the receiver we have - // typechecked already). This is needed for i8311.scala. Doing so - // for all expected types does not work since it would block the case - // where we have an argument that must be converted with another - // implicit conversion to the receiver type. - def sharpenedPt = pt match - case pt: SelectionProto if pt.name.isExtensionName => pt.deepenProto - case _ => pt var resMatch: Boolean = false wtp match { case wtp: ExprType => From 17a802606d046438572487f1a7db7d36c89ff9ee Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 2 Jul 2020 08:58:29 +0200 Subject: [PATCH 32/38] Add clarification to doc page --- docs/docs/reference/contextual/extension-methods.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index a32c6e0b053a..b35044748b86 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -86,7 +86,7 @@ Extensions can also take using clauses. For instance, the `+` extension above co ``` **Note**: Type parameters have to be given after the `extension` keyword; -they cannot be given after the `def`. This restriction might be lifted in the future once we support multiple type parameter clauses in a method. +they cannot be given after the `def`. This restriction might be lifted in the future once we support multiple type parameter clauses in a method. By contrast, there can be using clauses in front as well as after the `def`. ### Collective Extensions From 7de235dcc0c486b615e1bd395d99fea8ca98572a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 3 Jul 2020 16:18:06 +0200 Subject: [PATCH 33/38] Add test --- tests/run/fragables-extension.scala | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/run/fragables-extension.scala diff --git a/tests/run/fragables-extension.scala b/tests/run/fragables-extension.scala new file mode 100644 index 000000000000..9476d1efd191 --- /dev/null +++ b/tests/run/fragables-extension.scala @@ -0,0 +1,28 @@ +trait Frag + +case class IntFrag(x: Int) extends Frag +case class StringFrag(x: String) extends Frag + +trait Fragable[T]: + extension (x: T) + def toFrags: List[Frag] + +given Fragable[Int] = + x => List(IntFrag(x)) +given Fragable[String] = + x => List(StringFrag(x)) +given [A: Fragable] as Fragable[List[A]] = + x => x.flatMap(_.toFrags) +given Fragable[EmptyTuple] = + x => Nil +given [A: Fragable, B <: Tuple: Fragable] as Fragable[A *: B] = + x => x.head.toFrags ++ x.tail.toFrags + +def f[T: Fragable](x: T) = + println(s"got: ${x.toFrags.mkString(", ")}") + +@main def Test = + f(1) + f("abc") + f(List(1, 2, 3)) + f(1, "a", List("c", "d")) From 4aae35c8c9ecfe1c3967d89c842d3f646b2a8528 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 3 Jul 2020 17:23:29 +0200 Subject: [PATCH 34/38] Fix printing right-associative extension methods --- .../src/dotty/tools/dotc/printing/RefinedPrinter.scala | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index ba456023a979..fa1218a80fec 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -795,9 +795,14 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { withEnclosingDef(tree) { val (prefix, vparamss) = if isExtension then - (keywordStr("extension") ~~ paramsText(tree.vparamss.head) + val (leadingParams, otherParamss) = (tree.vparamss: @unchecked) match + case vparams1 :: vparams2 :: rest if !isLeftAssoc(tree.name) => + (vparams2, vparams1 :: rest) + case vparams1 :: rest => + (vparams1, rest) + (keywordStr("extension") ~~ paramsText(leadingParams) ~~ (defKeyword ~~ valDefText(nameIdText(tree, dropExtension = true))).close, - tree.vparamss.tail) + otherParamss) else (defKeyword ~~ valDefText(nameIdText(tree)), tree.vparamss) addVparamssText(prefix ~ tparamsText(tree.tparams), vparamss) ~ From c8d7be1745616dabbc92613c034c188396b703c9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 3 Jul 2020 17:26:24 +0200 Subject: [PATCH 35/38] Address review comments --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 2 -- tests/neg/i6779.scala | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 5833873e4b5b..ad9a73c01aa2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1108,7 +1108,6 @@ trait Implicits { self: Typer => * a diverging search */ def tryImplicit(cand: Candidate, contextual: Boolean): SearchResult = - //println(i"try $cand for $pt") if (ctx.searchHistory.checkDivergence(cand, pt)) SearchFailure(new DivergingImplicit(cand.ref, pt.widenExpr, argument)) else { @@ -1315,7 +1314,6 @@ trait Implicits { self: Typer => val eligible = if (contextual) ctx.implicits.eligible(wildProto) else implicitScope(wildProto).eligible - //println(i"eligible for $wildProto = $eligible%, %") searchImplicits(eligible, contextual) match { case result: SearchSuccess => result diff --git a/tests/neg/i6779.scala b/tests/neg/i6779.scala index 711361aa9d13..bd40387be3d9 100644 --- a/tests/neg/i6779.scala +++ b/tests/neg/i6779.scala @@ -10,4 +10,4 @@ def g1[T](x: T): F[G[T]] = x.f(using summon[Stuff]) // error def g2[T](x: T): F[G[T]] = x.f // error -def g3[T](x: T): F[G[T]] = f(x)(using summon[Stuff]) // error +def g3[T](x: T): F[G[T]] = extension_f(x)(using summon[Stuff]) // error From a128dab3a435a9eb3aa7a8cba359a9989a73d9c2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 4 Jul 2020 12:11:25 +0200 Subject: [PATCH 36/38] Drop `:` for collective extensions --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 4 ++-- docs/docs/reference/contextual/extension-methods.md | 8 ++++---- docs/docs/reference/contextual/type-classes.md | 2 +- docs/docs/reference/other-new-features/indentation.md | 6 +++--- tests/neg-custom-args/infix.scala | 2 +- tests/neg/i6779.check | 11 +++++------ tests/neg/i6900.scala | 2 +- tests/neg/i7529.scala | 2 +- tests/neg/i8050.scala | 2 +- tests/neg/missing-implicit1.scala | 2 +- tests/neg/missing-implicit5.scala | 2 +- tests/pos/endmarkers.scala | 2 +- tests/pos/i6900.scala | 2 +- tests/pos/i7403.scala | 2 +- tests/pos/i8181.scala | 2 +- tests/pos/i8241.scala | 4 ++-- tests/pos/matrixOps.scala | 2 +- tests/pos/reference/delegates.scala | 10 +++++----- tests/pos/reference/extension-methods.scala | 4 ++-- tests/run/LazyLists.scala | 6 +++--- tests/run/collective-extensions.scala | 2 +- tests/run/extension-specificity.scala | 4 ++-- tests/run/extmethod-overload.scala | 4 ++-- tests/run/i8058.scala | 2 +- tests/run/instances.scala | 8 ++++---- tests/semanticdb/expect/Givens.expect.scala | 4 ++-- tests/semanticdb/expect/Givens.scala | 4 ++-- 27 files changed, 52 insertions(+), 53 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 9c28f88f8a87..9dd9e295078b 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3646,12 +3646,12 @@ object Parsers { val tparams = typeParamClauseOpt(ParamOwner.Def) val extParams = paramClause(0, prefix = true) val givenParamss = paramClauses(givenOnly = true) - newLinesOpt() val methods = if isDefIntro(modifierTokens) then extMethod() :: Nil else - possibleTemplateStart() + in.observeIndented() + newLineOptWhenFollowedBy(LBRACE) if in.isNestedStart then inDefScopeBraces(extMethods()) else { syntaxError("Extension without extension methods"); Nil } val result = ExtMethods(tparams, extParams :: givenParamss, methods) diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index b35044748b86..dc44d708ed50 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -95,7 +95,7 @@ left-hand parameter type. In this case one can "pull out" the common parameters a single extension and enclose all methods in braces or an indented region following a '`:`'. Example: ```scala -extension (ss: Seq[String]): +extension (ss: Seq[String]) def longestStrings: Seq[String] = val maxLength = ss.map(_.length).max @@ -118,9 +118,9 @@ extension (ss: Seq[String]) extension (ss: Seq[String]) def longestString: String = ss.longestStrings.head ``` -Collective extensions also can take type parameters and have using clauses. Example: +Collective extensions also can take type parameters and have using clauses. Example ```scala -extension [T](xs: List[T])(using Ordering[T]): +extension [T](xs: List[T])(using Ordering[T]) def smallest(n: Int): List[T] = xs.sorted.take(n) def smallestIndices(n: Int): List[Int] = val limit = smallest(n).max @@ -184,7 +184,7 @@ object List: def flatten: List[T] = xs.foldLeft(Nil: List[T])(_ ++ _) given [T: Ordering] as Ordering[List[T]]: - extension (xs: List[T]): + extension (xs: List[T]) def < (ys: List[T]): Boolean = ... end List diff --git a/docs/docs/reference/contextual/type-classes.md b/docs/docs/reference/contextual/type-classes.md index 3760cbd85b58..e8489d393fe5 100644 --- a/docs/docs/reference/contextual/type-classes.md +++ b/docs/docs/reference/contextual/type-classes.md @@ -139,7 +139,7 @@ trait Monad[F[_]] extends Functor[F]: /** The unit value for a monad */ def pure[A](x: A): F[A] - extension [A, B](x: F[A]): + extension [A, B](x: F[A]) /** The fundamental composition operation */ def flatMap(f: A => F[B]): F[B] diff --git a/docs/docs/reference/other-new-features/indentation.md b/docs/docs/reference/other-new-features/indentation.md index 80737afc3e3a..21e846865bba 100644 --- a/docs/docs/reference/other-new-features/indentation.md +++ b/docs/docs/reference/other-new-features/indentation.md @@ -94,7 +94,7 @@ at the toplevel, inside braces `{...}`, but not inside parentheses `(...)`, patt ### Optional Braces Around Template Bodies -The Scala grammar uses the term _template body_ for the definitions of a class, trait, object, given instance or extension that are normally enclosed in braces. The braces around a template body can also be omitted by means of the following rule: +The Scala grammar uses the term _template body_ for the definitions of a class, trait, object, given instance or extension that are normally enclosed in braces. The braces around a template body can also be omitted by means of the following rule If at the point where a template body can start there is a `:` that occurs at the end of a line, and that is followed by at least one indented statement, the recognized @@ -123,7 +123,7 @@ type T = A: given [T] with Ord[T] as Ord[List[T]]: def compare(x: List[T], y: List[T]) = ??? -extension (xs: List[Int]): +extension (xs: List[Int]) def second: Int = xs.tail.head new A: @@ -256,7 +256,7 @@ For instance, the following end markers are all legal: end given end C - extension (x: C): + extension (x: C) def ff: String = x.f ++ x.f end extension diff --git a/tests/neg-custom-args/infix.scala b/tests/neg-custom-args/infix.scala index b06ff1a04fa8..48827fb89558 100644 --- a/tests/neg-custom-args/infix.scala +++ b/tests/neg-custom-args/infix.scala @@ -8,7 +8,7 @@ class C: object C: given AnyRef: - extension (x: C): + extension (x: C) @infix def iop (y: Int) = ??? def mop (y: Int) = ??? def ++ (y: Int) = ??? diff --git a/tests/neg/i6779.check b/tests/neg/i6779.check index 48cf82ef2697..0a34f2a47321 100644 --- a/tests/neg/i6779.check +++ b/tests/neg/i6779.check @@ -8,9 +8,8 @@ | ^ | Found: F[T] | Required: F[G[T]] --- [E006] Not Found Error: tests/neg/i6779.scala:13:27 ----------------------------------------------------------------- -13 |def g3[T](x: T): F[G[T]] = f(x)(using summon[Stuff]) // error - | ^ - | Not found: f - -longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/i6779.scala:13:41 ------------------------------------------------------------- +13 |def g3[T](x: T): F[G[T]] = extension_f(x)(using summon[Stuff]) // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Found: F[T] + | Required: F[G[T]] diff --git a/tests/neg/i6900.scala b/tests/neg/i6900.scala index 7382c3a9df01..18231f4e6cda 100644 --- a/tests/neg/i6900.scala +++ b/tests/neg/i6900.scala @@ -1,7 +1,7 @@ object Test2 { // Works with extension method - extension [A](a: A): + extension [A](a: A) def foo[C]: C => A = _ => a // error: extension method cannot have type parameters 1.foo.foo diff --git a/tests/neg/i7529.scala b/tests/neg/i7529.scala index cf4998de4af1..593897210828 100644 --- a/tests/neg/i7529.scala +++ b/tests/neg/i7529.scala @@ -1,4 +1,4 @@ -extension [A](a: A): +extension [A](a: A) @nonsense // error: not found: nonsense def foo = ??? \ No newline at end of file diff --git a/tests/neg/i8050.scala b/tests/neg/i8050.scala index 0bd19d0f14e8..029e8d8195d5 100644 --- a/tests/neg/i8050.scala +++ b/tests/neg/i8050.scala @@ -1,5 +1,5 @@ object stuff: def exec(dir: Int) = ??? -extension (a: Int): +extension (a: Int) inline def exec: Unit = stuff.exec("aaa") // error diff --git a/tests/neg/missing-implicit1.scala b/tests/neg/missing-implicit1.scala index 45ed1ed1857c..c94d78099068 100644 --- a/tests/neg/missing-implicit1.scala +++ b/tests/neg/missing-implicit1.scala @@ -7,7 +7,7 @@ object testObjectInstance: object instances { given zipOption as Zip[Option] = ??? given traverseList as Traverse[List] = ??? - extension [T](xs: List[T]): + extension [T](xs: List[T]) def second: T = xs.tail.head extension [T](xs: List[T]) def first: T = xs.head } diff --git a/tests/neg/missing-implicit5.scala b/tests/neg/missing-implicit5.scala index f7cc1679f280..a2a0fd55315c 100644 --- a/tests/neg/missing-implicit5.scala +++ b/tests/neg/missing-implicit5.scala @@ -7,7 +7,7 @@ object testObjectInstance: object instances { given zipOption as Zip[Option] = ??? given traverseList as Traverse[List] = ??? - extension [T](xs: List[T]): + extension [T](xs: List[T]) def second: T = xs.tail.head extension [T](xs: List[T]) def first: T = xs.head } diff --git a/tests/pos/endmarkers.scala b/tests/pos/endmarkers.scala index c4eb0d60771f..5b85ce3763cd 100644 --- a/tests/pos/endmarkers.scala +++ b/tests/pos/endmarkers.scala @@ -51,7 +51,7 @@ package p1.p2: end given end C - extension (x: C): + extension (x: C) def ff: String = x.f end extension diff --git a/tests/pos/i6900.scala b/tests/pos/i6900.scala index 45313c21921e..6880777f017a 100644 --- a/tests/pos/i6900.scala +++ b/tests/pos/i6900.scala @@ -21,7 +21,7 @@ object Test1 { object Test2 { // Works with extension method - extension [A, C](a: A): + extension [A, C](a: A) def foo: C => A = _ => a 1.foo.foo diff --git a/tests/pos/i7403.scala b/tests/pos/i7403.scala index 36eec0e7c354..22aee2f0d6c0 100644 --- a/tests/pos/i7403.scala +++ b/tests/pos/i7403.scala @@ -1,6 +1,6 @@ object Test { - extension (i: Int): + extension (i: Int) def pow2 = i * i println(5.pow2) diff --git a/tests/pos/i8181.scala b/tests/pos/i8181.scala index d7f1c337b6f9..d36214c3068a 100644 --- a/tests/pos/i8181.scala +++ b/tests/pos/i8181.scala @@ -1,6 +1,6 @@ object Main: def main(args: Array[String]): Unit = - extension (a: AnyRef): + extension (a: AnyRef) def putout(): Unit = println(a) "blub".putout() diff --git a/tests/pos/i8241.scala b/tests/pos/i8241.scala index 9bcc39f950b5..c9de697affd1 100644 --- a/tests/pos/i8241.scala +++ b/tests/pos/i8241.scala @@ -1,7 +1,7 @@ -extension (name: String): +extension (name: String) def isWildcard = ??? end extension -extension (name: String): +extension (name: String) def f = ??? end extension diff --git a/tests/pos/matrixOps.scala b/tests/pos/matrixOps.scala index 7a635e7821bb..ab96a09389a5 100644 --- a/tests/pos/matrixOps.scala +++ b/tests/pos/matrixOps.scala @@ -3,7 +3,7 @@ object Test: type Matrix = Array[Array[Double]] type Vector = Array[Double] - extension (m: Matrix): + extension (m: Matrix) def nRows = m.length def nCols = m(0).length def row(i: Int): Vector = m(i) diff --git a/tests/pos/reference/delegates.scala b/tests/pos/reference/delegates.scala index 7e8cde964f2f..ea7c7f5d8ec1 100644 --- a/tests/pos/reference/delegates.scala +++ b/tests/pos/reference/delegates.scala @@ -40,12 +40,12 @@ object Instances extends Common: if (fst != 0) fst else xs1.compareTo(ys1) end listOrd - extension (xs: Seq[String]): + extension (xs: Seq[String]) def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) - extension [T](xs: List[T]): + extension [T](xs: List[T]) def second = xs.tail.head def third = xs.tail.tail.head @@ -134,7 +134,7 @@ object PostConditions: def result[T](using x: WrappedResult[T]): T = x - extension [T](x: T): + extension [T](x: T) def ensuring(condition: WrappedResult[T] ?=> Boolean): T = assert(condition(using x)) x @@ -154,12 +154,12 @@ object AnonymousInstances extends Common: val fst = x.compareTo(y) if (fst != 0) fst else xs1.compareTo(ys1) - extension (xs: Seq[String]): + extension (xs: Seq[String]) def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) - extension [T](xs: List[T]): + extension [T](xs: List[T]) def second = xs.tail.head given [From, To](using c: Convertible[From, To]) as Convertible[List[From], List[To]]: diff --git a/tests/pos/reference/extension-methods.scala b/tests/pos/reference/extension-methods.scala index 5a7a8a58964d..b74815fbdf82 100644 --- a/tests/pos/reference/extension-methods.scala +++ b/tests/pos/reference/extension-methods.scala @@ -30,7 +30,7 @@ object ExtMethods: extension [T](x: T)(using n: Numeric[T]) def - (y: T): T = n.minus(x, y) - extension (ss: Seq[String]): + extension (ss: Seq[String]) def longestStrings: Seq[String] = val maxLength = ss.map(_.length).max @@ -40,7 +40,7 @@ object ExtMethods: import math.Ordered.orderingToOrdered - extension [T](xs: List[T])(using Ordering[T]): + extension [T](xs: List[T])(using Ordering[T]) def smallest(n: Int): List[T] = xs.sorted.take(n) def smallestIndices(n: Int): List[Int] = val limit = smallest(n).max diff --git a/tests/run/LazyLists.scala b/tests/run/LazyLists.scala index b67c1e70fb42..6cf8c8cf8e50 100644 --- a/tests/run/LazyLists.scala +++ b/tests/run/LazyLists.scala @@ -55,7 +55,7 @@ package xcollections: if it.hasNext then set(it.next, fromIterator (it)) else empty - extension [T, U >: T](xs: LazyList[T]): + extension [T, U >: T](xs: LazyList[T]) def #::(x: U): LazyList[U] = new: protected def force(): LazyList[U] = set(x, xs) @@ -65,7 +65,7 @@ package xcollections: if xs.isEmpty then ys.forced() else set(xs.head, xs.tail ++ ys) - extension [T, U](xs: LazyList[T]): + extension [T, U](xs: LazyList[T]) def map(f: T => U): LazyList[U] = new: protected def force() = if xs.isEmpty then empty @@ -80,7 +80,7 @@ package xcollections: if xs.isEmpty then z else xs.tail.foldLeft(f(z, xs.head))(f) - extension [T](xs: LazyList[T]): + extension [T](xs: LazyList[T]) def filter(p: T => Boolean): LazyList[T] = new: protected def force(): LazyList[T] = if xs.isEmpty then empty diff --git a/tests/run/collective-extensions.scala b/tests/run/collective-extensions.scala index 352e5c671951..c76dda6d2a1b 100644 --- a/tests/run/collective-extensions.scala +++ b/tests/run/collective-extensions.scala @@ -1,4 +1,4 @@ -extension (x: String): +extension (x: String) def foo(y: String): String = x ++ y def bar(y: String): String = foo(y) def baz(y: String): String = diff --git a/tests/run/extension-specificity.scala b/tests/run/extension-specificity.scala index 0d9b6c9ceb88..19a7d5d01b44 100644 --- a/tests/run/extension-specificity.scala +++ b/tests/run/extension-specificity.scala @@ -1,10 +1,10 @@ class A class B extends A -extension (x: A): +extension (x: A) def foo: Int = 1 -extension (x: B): +extension (x: B) def foo: Int = 2 @main def Test = diff --git a/tests/run/extmethod-overload.scala b/tests/run/extmethod-overload.scala index ec921c382185..c3d0ddbf2c20 100644 --- a/tests/run/extmethod-overload.scala +++ b/tests/run/extmethod-overload.scala @@ -22,11 +22,11 @@ object Test extends App { // Test with extension methods in given object object test1 { - extension (x: Int): + extension (x: Int) def |+| (y: Int) = x + y def |+| (y: String) = x + y.length - extension [T](xs: List[T]): + extension [T](xs: List[T]) def +++ (ys: List[T]): List[T] = xs ++ ys ++ ys def +++ (ys: Iterator[T]): List[T] = xs ++ ys ++ ys diff --git a/tests/run/i8058.scala b/tests/run/i8058.scala index 4cef0ba6db1b..ad5343126af3 100644 --- a/tests/run/i8058.scala +++ b/tests/run/i8058.scala @@ -1,4 +1,4 @@ -extension (x: Array[Char]): +extension (x: Array[Char]) inline def swap(i: Int, j: Int) : Unit = val v = x(i) x(i) = x(j) diff --git a/tests/run/instances.scala b/tests/run/instances.scala index ea0e1038ba70..fd120fa2dabe 100644 --- a/tests/run/instances.scala +++ b/tests/run/instances.scala @@ -8,14 +8,14 @@ object Test extends App { case class Circle(x: Double, y: Double, radius: Double) - extension (c: Circle): + extension (c: Circle) def circumference: Double = c.radius * math.Pi * 2 val circle = new Circle(1, 1, 2.0) assert(circle.circumference == Test.extension_circumference(circle)) - extension (xs: Seq[String]): + extension (xs: Seq[String]) def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) @@ -23,12 +23,12 @@ object Test extends App { val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - extension [T](xs: Seq[T]): + extension [T](xs: Seq[T]) def second = xs.tail.head assert(names.longestStrings.second == "world") - extension [T](xs: List[List[T]]): + extension [T](xs: List[List[T]]) def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) extension [T](x: T) def :: (xs: Seq[T]) = x +: xs diff --git a/tests/semanticdb/expect/Givens.expect.scala b/tests/semanticdb/expect/Givens.expect.scala index 76cbbab42812..ecc6addedeba 100644 --- a/tests/semanticdb/expect/Givens.expect.scala +++ b/tests/semanticdb/expect/Givens.expect.scala @@ -3,10 +3,10 @@ package b object Givens/*<-a::b::Givens.*/: - extension [A/*<-a::b::Givens.extension_sayHello().[A]*/](any/*<-a::b::Givens.extension_sayHello().(any)*/: A/*->a::b::Givens.extension_sayHello().[A]*/): + extension [A/*<-a::b::Givens.extension_sayHello().[A]*/](any/*<-a::b::Givens.extension_sayHello().(any)*/: A/*->a::b::Givens.extension_sayHello().[A]*/) def sayHello = s"Hello/*<-a::b::Givens.extension_sayHello().*//*->scala::StringContext.apply().*/, I am $any/*->a::b::Givens.extension_sayHello().(any)*/"/*->scala::StringContext#s().*/ - extension [B/*<-a::b::Givens.extension_sayGoodbye().[B]*//*<-a::b::Givens.extension_saySoLong().[B]*/](any/*<-a::b::Givens.extension_sayGoodbye().(any)*//*<-a::b::Givens.extension_saySoLong().(any)*/: B/*->a::b::Givens.extension_sayGoodbye().[B]*//*->a::b::Givens.extension_saySoLong().[B]*/): + extension [B/*<-a::b::Givens.extension_sayGoodbye().[B]*//*<-a::b::Givens.extension_saySoLong().[B]*/](any/*<-a::b::Givens.extension_sayGoodbye().(any)*//*<-a::b::Givens.extension_saySoLong().(any)*/: B/*->a::b::Givens.extension_sayGoodbye().[B]*//*->a::b::Givens.extension_saySoLong().[B]*/) def sayGoodbye = s"Goodb/*<-a::b::Givens.extension_sayGoodbye().*//*->scala::StringContext.apply().*/ye, from $any/*->a::b::Givens.extension_sayGoodbye().(any)*/"/*->scala::StringContext#s().*/ def saySoLong = s"So Lo/*<-a::b::Givens.extension_saySoLong().*//*->scala::StringContext.apply().*/ng, from $any/*->a::b::Givens.extension_saySoLong().(any)*/"/*->scala::StringContext#s().*/ diff --git a/tests/semanticdb/expect/Givens.scala b/tests/semanticdb/expect/Givens.scala index 90a6ebe2ad32..2f6add1e12e1 100644 --- a/tests/semanticdb/expect/Givens.scala +++ b/tests/semanticdb/expect/Givens.scala @@ -3,10 +3,10 @@ package b object Givens: - extension [A](any: A): + extension [A](any: A) def sayHello = s"Hello, I am $any" - extension [B](any: B): + extension [B](any: B) def sayGoodbye = s"Goodbye, from $any" def saySoLong = s"So Long, from $any" From b179a8e1f1b091085a27ecbf82329e345a6a3e11 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 4 Jul 2020 13:06:34 +0200 Subject: [PATCH 37/38] Change indentation.md to reflect no colon in extensions --- docs/docs/reference/other-new-features/indentation.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/docs/reference/other-new-features/indentation.md b/docs/docs/reference/other-new-features/indentation.md index 21e846865bba..7b8d0c712d6d 100644 --- a/docs/docs/reference/other-new-features/indentation.md +++ b/docs/docs/reference/other-new-features/indentation.md @@ -59,6 +59,7 @@ There are two rules: An indentation region can start - after the condition of an `if-else`, or + - after the leading parameters of an `extension`, or - after a ": at end of line" token (see below) - after one of the following tokens: ``` @@ -94,7 +95,7 @@ at the toplevel, inside braces `{...}`, but not inside parentheses `(...)`, patt ### Optional Braces Around Template Bodies -The Scala grammar uses the term _template body_ for the definitions of a class, trait, object, given instance or extension that are normally enclosed in braces. The braces around a template body can also be omitted by means of the following rule +The Scala grammar uses the term _template body_ for the definitions of a class, trait, object or given instance that are normally enclosed in braces. The braces around a template body can also be omitted by means of the following rule If at the point where a template body can start there is a `:` that occurs at the end of a line, and that is followed by at least one indented statement, the recognized @@ -120,7 +121,7 @@ enum Color: type T = A: def f: Int -given [T] with Ord[T] as Ord[List[T]]: +given [T](using Ord[T]) as Ord[List[T]]: def compare(x: List[T], y: List[T]) = ??? extension (xs: List[Int]) From e1601400452edcac163e2d53a4fa60aaf191a95d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 5 Jul 2020 12:03:03 +0200 Subject: [PATCH 38/38] Allow optional `:` in extension There's will probably be a tendency to put a `:` in a collective extension by analogy to given and object, trait etc. We don't need to be picky about this. --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 2 ++ tests/neg/i6900.scala | 2 +- tests/pos/i6900.scala | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 9dd9e295078b..c0ae8fbc2e76 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3646,6 +3646,8 @@ object Parsers { val tparams = typeParamClauseOpt(ParamOwner.Def) val extParams = paramClause(0, prefix = true) val givenParamss = paramClauses(givenOnly = true) + in.observeColonEOL() + if (in.token == COLONEOL) in.nextToken() val methods = if isDefIntro(modifierTokens) then extMethod() :: Nil diff --git a/tests/neg/i6900.scala b/tests/neg/i6900.scala index 18231f4e6cda..7382c3a9df01 100644 --- a/tests/neg/i6900.scala +++ b/tests/neg/i6900.scala @@ -1,7 +1,7 @@ object Test2 { // Works with extension method - extension [A](a: A) + extension [A](a: A): def foo[C]: C => A = _ => a // error: extension method cannot have type parameters 1.foo.foo diff --git a/tests/pos/i6900.scala b/tests/pos/i6900.scala index 6880777f017a..45313c21921e 100644 --- a/tests/pos/i6900.scala +++ b/tests/pos/i6900.scala @@ -21,7 +21,7 @@ object Test1 { object Test2 { // Works with extension method - extension [A, C](a: A) + extension [A, C](a: A): def foo: C => A = _ => a 1.foo.foo