From df88c0f6cb2f07fa77fbdd8514312f2bdc7e81b4 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Sat, 5 Feb 2022 16:44:09 +0100 Subject: [PATCH 01/19] Document new changes The new syntax allows arbitrary interleaving of type and term clauses for methods TypelessClause{|s} is added for class constructors since they still don't allow interleaving of type parameters Also unifies the affected grammar variable names so that s means "one or more " Implicit clauses are moved apart from term clause to make it more obvious they can only be the last clause --- docs/_docs/internals/syntax.md | 36 +++--- .../reference/contextual/using-clauses.md | 4 +- .../generalized-method-syntax.md | 105 ++++++++++++++++++ docs/_docs/reference/syntax.md | 36 +++--- 4 files changed, 151 insertions(+), 30 deletions(-) create mode 100644 docs/_docs/reference/other-new-features/generalized-method-syntax.md diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index 76664569bb17..dfdad496d446 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -347,9 +347,6 @@ ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’ ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] TypeDef(Modifiers, name, tparams, bounds) id [HkTypeParamClause] TypeParamBounds Bound(below, above, context) -DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ -DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds - TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’ TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds @@ -363,13 +360,24 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param -Param ::= id ‘:’ ParamType [‘=’ Expr] -DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] -DefParamClause ::= [nl] ‘(’ DefParams ‘)’ | UsingParamClause -UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) ‘)’ -DefParams ::= DefParam {‘,’ DefParam} -DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id. +DefParamClauses ::= DefParamClause { DefParamClause } +DefParamClause ::= DefTypeParamClause + | DefTermParamClause + | UsingParamClause +TypelessClauses ::= TypelessClause {TypelessClause} +TypelessClause ::= DefTermParamClause + | UsingParamClause + +DefTypeParamClause::= [nl] ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ +DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds +DefTermParamClause::= [nl] ‘(’ [DefTermParams] ‘)’ +UsingParamClause ::= [nl] ‘(’ ‘using’ (DefTermParams | FunArgTypes) ‘)’ +DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’ + +DefTermParams ::= DefTermParam {‘,’ DefTermParam} +DefTermParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id. +Param ::= id ‘:’ ParamType [‘=’ Expr] ``` ### Bindings and Imports @@ -420,7 +428,7 @@ Dcl ::= RefineDcl ValDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) VarDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) DefDcl ::= DefSig ‘:’ Type DefDef(_, name, tparams, vparamss, tpe, EmptyTree) -DefSig ::= id [DefTypeParamClause] DefParamClauses +DefSig ::= id [DefParamClauses] [DefImplicitClause] TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds TypeDefTree(_, name, tparams, bound [‘=’ Type] @@ -431,8 +439,8 @@ Def ::= ‘val’ PatDef | TmplDef PatDef ::= ids [‘:’ Type] ‘=’ Expr | Pattern2 [‘:’ Type] ‘=’ Expr PatDef(_, pats, tpe?, expr) -DefDef ::= DefSig [‘:’ Type] ‘=’ Expr DefDef(_, name, tparams, vparamss, tpe, expr) - | ‘this’ DefParamClause DefParamClauses ‘=’ ConstrExpr DefDef(_, , Nil, vparamss, EmptyTree, expr | Block) +DefDef ::= DefSig [‘:’ Type] ‘=’ Expr DefDef(_, name, paramss, tpe, expr) + | ‘this’ TypelessClauses [DefImplicitClause] ‘=’ ConstrExpr DefDef(_, , vparamss, EmptyTree, expr | Block) TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef | [‘case’] ‘object’ ObjectDef @@ -444,10 +452,10 @@ ConstrMods ::= {Annotation} [AccessModifier] ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor EnumDef ::= id ClassConstr InheritClauses EnumBody GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance) -GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present +GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefTypeParamClause`, `UsingParamClause` must be present StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ WithTemplateBody] Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} - ‘(’ DefParam ‘)’ {UsingParamClause} ExtMethods + ‘(’ DefTermParam ‘)’ {UsingParamClause} ExtMethods ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>> ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef | Export diff --git a/docs/_docs/reference/contextual/using-clauses.md b/docs/_docs/reference/contextual/using-clauses.md index 8f410ebc026e..61b80ee27d91 100644 --- a/docs/_docs/reference/contextual/using-clauses.md +++ b/docs/_docs/reference/contextual/using-clauses.md @@ -153,8 +153,8 @@ Here is the new syntax of parameters and arguments seen as a delta from the [sta ``` ClsParamClause ::= ... | UsingClsParamClause -DefParamClauses ::= ... | UsingParamClause +DefParamClause ::= ... | UsingParamClause UsingClsParamClause ::= ‘(’ ‘using’ (ClsParams | Types) ‘)’ -UsingParamClause ::= ‘(’ ‘using’ (DefParams | Types) ‘)’ +UsingParamClause ::= ‘(’ ‘using’ (DefTermParams | Types) ‘)’ ParArgumentExprs ::= ... | ‘(’ ‘using’ ExprsInParens ‘)’ ``` diff --git a/docs/_docs/reference/other-new-features/generalized-method-syntax.md b/docs/_docs/reference/other-new-features/generalized-method-syntax.md new file mode 100644 index 000000000000..dbe9c6eb0a61 --- /dev/null +++ b/docs/_docs/reference/other-new-features/generalized-method-syntax.md @@ -0,0 +1,105 @@ +--- +layout: doc-page +title: "Generalized Method Syntax" +movedTo: https://docs.scala-lang.org/scala3/reference/other-new-features/generalized-method-syntax.html +--- + +The inclusion of using clauses is not the only way in which methods have been updated, type parameter clauses are now allowed in any number and at any position. + +## Syntax Changes + +### In Scala 2 + +The old syntax only allowed zero or one type parameter clause, followed by any number of term clauses, optionnally followed by an implicit clause: + +```scala +def foo[T, U](x: T)(y: U)(z: Int, s: String)(a: Array[T])(implicit ordInt: Ord[Int], l: List[U]) +``` + +### In Scala 3 + +The new syntax allows any number of type, term and using clause, in any order, optionnaly followed by an implicit clause: +(do note however that [implicit clause are discouraged, in favor of using clauses](https://docs.scala-lang.org/scala3/reference/contextual/relationship-implicits.html)) + +```scala +def foo[T, U](x: T)(y: U)[V](z: V, s: String)[A](a: Array[A])(implicit List[U]) +``` + +### Unchanged + +Class definitions and type declarations are unaffected, there can only be up to one type clause, in leading posion. + +## Motivation + +The new syntax is a powerful but natural extension of the old one, it allows new design patterns while staying intuitive and legible. + +### Dependent Type Clauses + +As type clauses can come after term clauses, it is now possible to have type parameters that depend on term parameters: + +```scala +trait Key { type Value } +trait DB { + def get(k: Key): Option[k.Value] // dependent result type + def getOrElse(k: Key)[V >: k.Value](default: V): V // dependent type parameter +} +``` + +Note that simply replacing `V` by `k.Value` would not be equivalent. For example, if `k.Value` is `Some[Int]`, only the above allows: +`getOrElse(k)[Option[Int]](None)`, which returns a `Number`. + +### Partial Inference + +It is now possible to only infer some of the type parameters, this reduces boilerplate at the use site: +```scala +trait StaticSizeList[S <: Int & Singleton, T] +def filled[S <: Int & Singleton][T](x: T): StaticSizeList[S,T] = ??? +val helloes = filled[4]("Hello!") // S=4, and T is inferred +``` + +## Details + +### Application + +Method application is unchanged. +When multiple type clauses are expected but not all are passed, the rightmost ones are inferred. + +In particular, the following does not type check, even though the argument `Char` is only valid for `C`: +```scala +def triple[I <: Int](using Ordering[I])[C <: Char](a: I, b: C) = ??? +triple[Char](0, 'c') // error: Char does not conform to upperbound Int +``` + +### Extension Methods + +Extension methods follow the same syntax, for example the following is valid: +```scala +extension [T](l1: List[T]) + def zipWith[U](l2: List[U])[V](l3: List[V]): List[(T,U,V)] +``` + +### When to use + +We recommand to always put a unique type clause at the beginning, unless it is not possible to do so. +For example, the extension method `zipWith` above should be written `zipWith[U, V](l2: List[U], l3: List[V]): List[(T,U,V)]` instead. +On the other hand, the `getOrElse` method is recommended as-is, as it cannot be written with a leading type clause. + +### Formal syntax + +``` +DefDcl ::= DefSig ‘:’ Type +DefDef ::= DefSig [‘:’ Type] ‘=’ Expr +DefSig ::= id [DefParamClauses] [DefImplicitClause] +DefParamClauses ::= DefParamClause { DefParamClause } +DefParamClause ::= DefTypeParamClause + | DefTermParamClause + | UsingParamClause +DefTypeParamClause::= [nl] ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ +DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds +DefTermParamClause::= [nl] ‘(’ [DefTermParams] ‘)’ +UsingParamClause ::= [nl] ‘(’ ‘using’ (DefTermParams | FunArgTypes) ‘)’ +DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’ +DefTermParams ::= DefTermParam {‘,’ DefTermParam} +DefTermParam ::= {Annotation} [‘inline’] Param +Param ::= id ‘:’ ParamType [‘=’ Expr] +``` diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index 7e4b81b1ef5a..1d0c45030d2c 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -338,9 +338,6 @@ ArgumentPatterns ::= ‘(’ [Patterns] ‘)’ ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’ ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] id [HkTypeParamClause] TypeParamBounds -DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ -DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds - TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’ TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds @@ -352,13 +349,24 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ | [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’ ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param -Param ::= id ‘:’ ParamType [‘=’ Expr] -DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] -DefParamClause ::= [nl] ‘(’ DefParams ‘)’ | UsingParamClause -UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) ‘)’ -DefParams ::= DefParam {‘,’ DefParam} -DefParam ::= {Annotation} [‘inline’] Param +DefParamClauses ::= DefParamClause { DefParamClause } +DefParamClause ::= DefTypeParamClause + | DefTermParamClause + | UsingParamClause +TypelessClauses ::= TypelessClause {TypelessClause} +TypelessClause ::= DefTermParamClause + | UsingParamClause + +DefTypeParamClause::= [nl] ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ +DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds +DefTermParamClause::= [nl] ‘(’ [DefTermParams] ‘)’ +UsingParamClause ::= [nl] ‘(’ ‘using’ (DefTermParams | FunArgTypes) ‘)’ +DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’ + +DefTermParams ::= DefTermParam {‘,’ DefTermParam} +DefTermParam ::= {Annotation} [‘inline’] Param +Param ::= id ‘:’ ParamType [‘=’ Expr] ``` ### Bindings and Imports @@ -409,8 +417,8 @@ Dcl ::= RefineDcl ValDcl ::= ids ‘:’ Type VarDcl ::= ids ‘:’ Type DefDcl ::= DefSig ‘:’ Type -DefSig ::= id [DefTypeParamClause] DefParamClauses -TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds [‘=’ Type] +DefSig ::= id [DefParamClauses] [DefImplicitClause] +TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds Def ::= ‘val’ PatDef | ‘var’ PatDef @@ -420,7 +428,7 @@ Def ::= ‘val’ PatDef PatDef ::= ids [‘:’ Type] ‘=’ Expr | Pattern2 [‘:’ Type] ‘=’ Expr DefDef ::= DefSig [‘:’ Type] ‘=’ Expr - | ‘this’ DefParamClause DefParamClauses ‘=’ ConstrExpr + | ‘this’ TypelessClauses [DefImplicitClause] ‘=’ ConstrExpr TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef | [‘case’] ‘object’ ObjectDef @@ -432,10 +440,10 @@ ConstrMods ::= {Annotation} [AccessModifier] ObjectDef ::= id [Template] EnumDef ::= id ClassConstr InheritClauses EnumBody GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance) -GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present +GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefTypeParamClause`, `UsingParamClause` must be present StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ WithTemplateBody] Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} - ‘(’ DefParam ‘)’ {UsingParamClause} ExtMethods + ‘(’ DefTermParam ‘)’ {UsingParamClause} ExtMethods ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>> ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef | Export From 897deaf35f4509b5acebd7a3a1ac87e8122f491c Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 26 Jan 2022 10:08:17 +0100 Subject: [PATCH 02/19] Update Parsers.scala to accomodate new syntax Interweaved methods still fail at use-cite, see next commit --- .../dotty/tools/dotc/parsing/Parsers.scala | 80 ++++++++++++++++--- 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index ff5e95f3aa03..99359ac06a37 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3079,6 +3079,61 @@ object Parsers { /* -------- PARAMETERS ------------------------------------------- */ + /** DefParamClause ::= DefTypeParamClause + * | DefTermParamClause + * | UsingParamClause + */ + def typeOrTermParamClause(nparams: Int, // number of parameters preceding this clause + ofClass: Boolean = false, // owner is a class + ofCaseClass: Boolean = false, // owner is a case class + prefix: Boolean = false, // clause precedes name of an extension method + givenOnly: Boolean = false, // only given parameters allowed + firstClause: Boolean = false, // clause is the first in regular list of clauses + ownerKind: ParamOwner + ): List[TypeDef] | List[ValDef] = + if (in.token == LPAREN) + paramClause(nparams, ofClass, ofCaseClass, prefix, givenOnly, firstClause) + else if (in.token == LBRACKET) + typeParamClause(ownerKind) + else + Nil + + end typeOrTermParamClause + + /** DefParamClauses ::= DefParamClause { DefParamClause } + */ + def typeOrTermParamClauses( + ownerKind: ParamOwner, + ofClass: Boolean = false, + ofCaseClass: Boolean = false, + givenOnly: Boolean = false, + numLeadParams: Int = 0 + ): List[List[TypeDef] | List[ValDef]] = + + def recur(firstClause: Boolean, nparams: Int): List[List[TypeDef] | List[ValDef]] = + newLineOptWhenFollowedBy(LPAREN) + newLineOptWhenFollowedBy(LBRACKET) + if in.token == LPAREN then + val paramsStart = in.offset + val params = paramClause( + nparams, + ofClass = ofClass, + ofCaseClass = ofCaseClass, + givenOnly = givenOnly, + firstClause = firstClause) + val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit) + params :: ( + if lastClause then Nil + else recur(firstClause = false, nparams + params.length)) + else if in.token == LBRACKET then + typeParamClause(ownerKind) :: recur(firstClause, nparams) + else Nil + end recur + + recur(firstClause = true, nparams = numLeadParams) + end typeOrTermParamClauses + + /** ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’ * ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] * id [HkTypeParamClause] TypeParamBounds @@ -3143,11 +3198,15 @@ object Parsers { * UsingClsParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’ * ClsParams ::= ClsParam {‘,’ ClsParam} * ClsParam ::= {Annotation} + * + * TypelessClause ::= DefTermParamClause + * | UsingParamClause * - * DefParamClause ::= ‘(’ [‘erased’] DefParams ‘)’ | UsingParamClause - * UsingParamClause ::= ‘(’ ‘using’ [‘erased’] (DefParams | ContextTypes) ‘)’ - * DefParams ::= DefParam {‘,’ DefParam} - * DefParam ::= {Annotation} [‘inline’] Param + * DefTermParamClause::= [nl] ‘(’ [DefTermParams] ‘)’ + * UsingParamClause ::= ‘(’ ‘using’ [‘erased’] (DefTermParams | ContextTypes) ‘)’ + * DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’ + * DefTermParams ::= DefTermParam {‘,’ DefTermParam} + * DefTermParam ::= {Annotation} [‘inline’] Param * * Param ::= id `:' ParamType [`=' Expr] * @@ -3246,7 +3305,7 @@ object Parsers { } /** ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] - * DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] + * TypelessClauses ::= TypelessClause {TypelessClause} * * @return The parameter definitions */ @@ -3515,9 +3574,9 @@ object Parsers { } /** DefDef ::= DefSig [‘:’ Type] ‘=’ Expr - * | this ParamClause ParamClauses `=' ConstrExpr + * | this TypelessClauses [DefImplicitClause] `=' ConstrExpr * DefDcl ::= DefSig `:' Type - * DefSig ::= id [DefTypeParamClause] DefParamClauses + * DefSig ::= id [DefParamClauses] [DefImplicitClause] * | ExtParamClause [nl] [‘.’] id DefParamClauses */ def defDefOrDcl(start: Offset, mods: Modifiers, numLeadParams: Int = 0): DefDef = atSpan(start, nameStart) { @@ -3555,8 +3614,7 @@ object Parsers { val mods1 = addFlag(mods, Method) val ident = termIdent() var name = ident.name.asTermName - val tparams = typeParamClauseOpt(ParamOwner.Def) - val vparamss = paramClauses(numLeadParams = numLeadParams) + val paramss = typeOrTermParamClauses(ParamOwner.Def, numLeadParams = numLeadParams) var tpt = fromWithinReturnType { typedOpt() } if (migrateTo3) newLineOptWhenFollowedBy(LBRACE) val rhs = @@ -3574,7 +3632,7 @@ object Parsers { accept(EQUALS) expr() - val ddef = DefDef(name, joinParams(tparams, vparamss), tpt, rhs) + val ddef = DefDef(name, paramss, tpt, rhs) if (isBackquoted(ident)) ddef.pushAttachment(Backquoted, ()) finalizeDef(ddef, mods1, start) } @@ -3837,7 +3895,7 @@ object Parsers { finalizeDef(gdef, mods1, start) } - /** Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} ‘(’ DefParam ‘)’ + /** Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} ‘(’ DefTermParam ‘)’ * {UsingParamClause} ExtMethods */ def extension(): ExtMethods = From f1c5855d3e3c0af25f1323d10bea474b6e8d0c7a Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 26 Jan 2022 10:17:53 +0100 Subject: [PATCH 03/19] Fix assumptions in Applications.scala A check is removed that forbids interleaved methods at use-site This however breaks named type parameters, some tests are thus removed methType implicitly assumed there could only be one leading term parameter clause, this has been changed --- .../dotty/tools/dotc/typer/Applications.scala | 17 +++++++++++------ tests/neg/namedTypeParams.scala | 2 ++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index cd33fe9cef24..19b96f00d365 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -444,10 +444,17 @@ trait Applications extends Compatibility { /** The function's type after widening and instantiating polytypes * with TypeParamRefs in constraint set */ - @threadUnsafe lazy val methType: Type = liftedFunType.widen match { - case funType: MethodType => funType - case funType: PolyType => instantiateWithTypeVars(funType) - case tp => tp //was: funType + @threadUnsafe lazy val methType: Type = { + def rec(t: Type): Type = { + t.widen match{ + case funType: MethodType => funType + case funType: PolyType => + rec(instantiateWithTypeVars(funType)) + case tp => tp + } + } + + rec(liftedFunType) } @threadUnsafe lazy val liftedFunType: Type = @@ -1144,8 +1151,6 @@ trait Applications extends Compatibility { val typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_)) record("typedTypeApply") typedExpr(tree.fun, PolyProto(typedArgs, pt)) match { - case _: TypeApply if !ctx.isAfterTyper => - errorTree(tree, em"illegal repeated type application") case typedFn => typedFn.tpe.widen match { case pt: PolyType => diff --git a/tests/neg/namedTypeParams.scala b/tests/neg/namedTypeParams.scala index 8ed7c92241ea..197fc6a9a790 100644 --- a/tests/neg/namedTypeParams.scala +++ b/tests/neg/namedTypeParams.scala @@ -17,6 +17,7 @@ object Test { def f[X, Y](x: X, y: Y): Int = ??? f[X = Int, String](1, "") // error // error + /* Conflicts with Clause Interweaving, stems from named type parameters assuming one type clause f[X = Int][X = Int][Y = String](1, "") // error: illegal repeated type application f[X = Int][Y = String](1, "") // error: illegal repeated type application @@ -24,4 +25,5 @@ object Test { f[Y = String][X = Int](1, "") // error: illegal repeated type application f[Y = String][Int](1, "") // error: illegal repeated type application + */ } From b45d44dfd0ffe649cd51ef6d307e5f32c6e6d6b2 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 26 Jan 2022 10:19:36 +0100 Subject: [PATCH 04/19] Add interleaving tests --- tests/neg/interleaving-ab.scala | 8 +++++++ tests/neg/interleaving-nameCollision.scala | 3 +++ tests/neg/interleaving-params.scala | 7 ++++++ tests/neg/interleaving-typeApply.scala | 12 ++++++++++ tests/neg/interleaving-unmatched.scala | 3 +++ tests/neg/overrides.scala | 12 ++++++++++ tests/pos/interleaving-ba.scala | 11 ++++++++++ tests/pos/interleaving-chainedParams.scala | 18 +++++++++++++++ tests/pos/interleaving-classless.scala | 4 ++++ tests/pos/interleaving-functorCurrying.scala | 17 ++++++++++++++ .../interleaving-functorInterleaving.scala | 17 ++++++++++++++ tests/pos/interleaving-nameCollision.scala | 5 +++++ tests/pos/interleaving-newline.scala | 9 ++++++++ tests/pos/interleaving-overload.scala | 19 ++++++++++++++++ tests/pos/interleaving-params.scala | 6 +++++ tests/pos/interleaving-typeApply.scala | 22 +++++++++++++++++++ tests/pos/overrides.scala | 3 +++ 17 files changed, 176 insertions(+) create mode 100644 tests/neg/interleaving-ab.scala create mode 100644 tests/neg/interleaving-nameCollision.scala create mode 100644 tests/neg/interleaving-params.scala create mode 100644 tests/neg/interleaving-typeApply.scala create mode 100644 tests/neg/interleaving-unmatched.scala create mode 100644 tests/pos/interleaving-ba.scala create mode 100644 tests/pos/interleaving-chainedParams.scala create mode 100644 tests/pos/interleaving-classless.scala create mode 100644 tests/pos/interleaving-functorCurrying.scala create mode 100644 tests/pos/interleaving-functorInterleaving.scala create mode 100644 tests/pos/interleaving-nameCollision.scala create mode 100644 tests/pos/interleaving-newline.scala create mode 100644 tests/pos/interleaving-overload.scala create mode 100644 tests/pos/interleaving-params.scala create mode 100644 tests/pos/interleaving-typeApply.scala diff --git a/tests/neg/interleaving-ab.scala b/tests/neg/interleaving-ab.scala new file mode 100644 index 000000000000..0d6649bbb3be --- /dev/null +++ b/tests/neg/interleaving-ab.scala @@ -0,0 +1,8 @@ +object Ab: + given String = "" + given Double = 0 + + def ab[A][B](x: A)(using B): B = summon[B] + + def test = + ab[Int](0: Int) // error \ No newline at end of file diff --git a/tests/neg/interleaving-nameCollision.scala b/tests/neg/interleaving-nameCollision.scala new file mode 100644 index 000000000000..44aa1e169db4 --- /dev/null +++ b/tests/neg/interleaving-nameCollision.scala @@ -0,0 +1,3 @@ +object nameCollision: + def f[T](x: T)[U](y: U) = (x,y) + def f[T](x: T, y: T) = (x,y) // error \ No newline at end of file diff --git a/tests/neg/interleaving-params.scala b/tests/neg/interleaving-params.scala new file mode 100644 index 000000000000..e8fa5361a9ab --- /dev/null +++ b/tests/neg/interleaving-params.scala @@ -0,0 +1,7 @@ +class Params{ + def bar[T](x: T)[T]: String = ??? // error + def zoo(x: Int)[T, U](x: U): T = ??? // error + def bbb[T <: U](x: U)[U]: U = ??? // error // error + def f0[T](implicit x: T)[U](y: U) = (x,y) // error + def f1[T](implicit x: T)[U] = (x,y) // error +} \ No newline at end of file diff --git a/tests/neg/interleaving-typeApply.scala b/tests/neg/interleaving-typeApply.scala new file mode 100644 index 000000000000..2e5f652dc21a --- /dev/null +++ b/tests/neg/interleaving-typeApply.scala @@ -0,0 +1,12 @@ +object typeApply: + + def f3[T <: Int][U <: String](): T => T = ??? + def f5[T <: Int][U <: String]: [X <: Unit] => X => X = ??? + def f7[T <: Int][U <: String]()[X <: Unit]: X => X = ??? + + @main def test = { + f3[String]() // error + f5[Int][Unit] // error + f5[String][Unit] // error // error + f7[String]()[Unit] // error + } \ No newline at end of file diff --git a/tests/neg/interleaving-unmatched.scala b/tests/neg/interleaving-unmatched.scala new file mode 100644 index 000000000000..b2afcf5b5d45 --- /dev/null +++ b/tests/neg/interleaving-unmatched.scala @@ -0,0 +1,3 @@ +object unmatched: + def f1[T (x: T)] = ??? // error + def f2(x: Any[)T] = ??? // error // error \ No newline at end of file diff --git a/tests/neg/overrides.scala b/tests/neg/overrides.scala index 48f3260721e9..ee7a8b73ba0d 100644 --- a/tests/neg/overrides.scala +++ b/tests/neg/overrides.scala @@ -39,6 +39,7 @@ package p2 { // all being in the same package compiles fine class A[T] { def f(x: T)(y: T = x) = y + def b[U <: T](x: Int)[V >: T](y: String) = false def next: T = ??? @@ -49,11 +50,22 @@ class B extends A[Int] { def f(x: Int)(y: Int) = y // error: needs `override' modifier f(2)() + override def b[T <: Int](x: Int)(y: String) = true // error override def next(): Int = ??? // error: incompatible type } +class C extends A[String] { + + override def f(x: String) = x // error + + override def b[T <: String](x: Int)[U >: Int](y: String) = true // error: incompatible type + + override def next: Int = ??? // error: incompatible type + +} + class X { def f: A[Int] = ??? } diff --git a/tests/pos/interleaving-ba.scala b/tests/pos/interleaving-ba.scala new file mode 100644 index 000000000000..74c15d4e87e9 --- /dev/null +++ b/tests/pos/interleaving-ba.scala @@ -0,0 +1,11 @@ + + +object BA { + given String = "" + given Double = 0 + + def ba[B][A](x: A)(using B): B = summon[B] + + def test = ba[String](0) + +} \ No newline at end of file diff --git a/tests/pos/interleaving-chainedParams.scala b/tests/pos/interleaving-chainedParams.scala new file mode 100644 index 000000000000..6ed970a69458 --- /dev/null +++ b/tests/pos/interleaving-chainedParams.scala @@ -0,0 +1,18 @@ +object chainedParams{ + + trait Chain{ + type Tail <: Chain + } + + def f[C1 <: Chain](c1: C1)[C2 <: c1.Tail](c2: C2)[C3 <: c2.Tail](c3: C3): c3.Tail = ??? + + val self = new Chain{ type Tail = this.type } + val res: self.type = f(self)(self)(self) + + type C <: Chain + + val c3 = new Chain{ type Tail = C } + val c2 = new Chain{ type Tail = c3.type } + val c1 = new Chain{ type Tail = c2.type } + val u: C = f(c1)(c2)(c3) +} diff --git a/tests/pos/interleaving-classless.scala b/tests/pos/interleaving-classless.scala new file mode 100644 index 000000000000..4308030f66a6 --- /dev/null +++ b/tests/pos/interleaving-classless.scala @@ -0,0 +1,4 @@ +def f1[T][U](x: T, y: U): (T, U) = (x, y) +def f2[T](x: T)[U](y: U): (T, U) = (x, y) +def f3[T, U][V](x: T): U = ??? +def f4[T](x: T)[U <: x.type](y: U): (T, U) = (x, y) \ No newline at end of file diff --git a/tests/pos/interleaving-functorCurrying.scala b/tests/pos/interleaving-functorCurrying.scala new file mode 100644 index 000000000000..078d65233997 --- /dev/null +++ b/tests/pos/interleaving-functorCurrying.scala @@ -0,0 +1,17 @@ +object functorCurrying: + //taken from https://dotty.epfl.ch/docs/reference/contextual/type-classes.html + //at version 3.1.1-RC1-bin-20210930-01f040b-NIGHTLY + //modified to have type currying + trait Functor[F[_]]: + def map[A][B](x: F[A], f: A => B): F[B] + + + given Functor[List] with + def map[A][B](x: List[A], f: A => B): List[B] = + x.map(f) + + def assertTransformation[F[_]: Functor][A][B](expected: F[B], original: F[A], mapping: A => B): Unit = + assert(expected == summon[Functor[F]].map(original, mapping)) + + @main def testCurrying = + assertTransformation(List("a1", "b1"), List("a", "b"), elt => s"${elt}1") diff --git a/tests/pos/interleaving-functorInterleaving.scala b/tests/pos/interleaving-functorInterleaving.scala new file mode 100644 index 000000000000..fb14c151387c --- /dev/null +++ b/tests/pos/interleaving-functorInterleaving.scala @@ -0,0 +1,17 @@ +object functorInterweaving: + //taken from https://dotty.epfl.ch/docs/reference/contextual/type-classes.html + //at version 3.1.1-RC1-bin-20210930-01f040b-NIGHTLY + //modified to have type interveawing + trait Functor[F[_]]: + def map[A](x: F[A])[B](f: A => B): F[B] + + + given Functor[List] with + def map[A](x: List[A])[B](f: A => B): List[B] = + x.map(f) + + def assertTransformation[F[_]: Functor][A](original: F[A])[B](expected: F[B])(mapping: A => B): Unit = + assert(expected == summon[Functor[F]].map(original)(mapping)) + + @main def testInterweaving = + assertTransformation(List("a", "b"))(List("a1", "b1")){elt => s"${elt}1"} diff --git a/tests/pos/interleaving-nameCollision.scala b/tests/pos/interleaving-nameCollision.scala new file mode 100644 index 000000000000..b66f63b9f92c --- /dev/null +++ b/tests/pos/interleaving-nameCollision.scala @@ -0,0 +1,5 @@ +import scala.annotation.targetName + +object nameCollision: + def f[T](x: T)[U](y: U) = (x,y) + @targetName("g") def f[T](x: T, y: T) = (x,y) \ No newline at end of file diff --git a/tests/pos/interleaving-newline.scala b/tests/pos/interleaving-newline.scala new file mode 100644 index 000000000000..dbd30bca4ef7 --- /dev/null +++ b/tests/pos/interleaving-newline.scala @@ -0,0 +1,9 @@ +object newline { + def multipleLines + [T] + (x: T) + [U] + (using (T,U)) + (y: U) + = ??? +} diff --git a/tests/pos/interleaving-overload.scala b/tests/pos/interleaving-overload.scala new file mode 100644 index 000000000000..3dcb0a911704 --- /dev/null +++ b/tests/pos/interleaving-overload.scala @@ -0,0 +1,19 @@ + +class A{ + + def f1[T][U](x: Any) = ??? + def f1[T][U](x: Int) = ??? + + f1(1) + f1("hello") + + case class B[U](x: Int) + def b[U](x: Int) = B[U](x) + + def f2[T]: [U] => Int => B[U] = [U] => (x: Int) => b[U](x) + + f2(1) + f2[Any](1) + f2[Any][Any](1) + +} \ No newline at end of file diff --git a/tests/pos/interleaving-params.scala b/tests/pos/interleaving-params.scala new file mode 100644 index 000000000000..fb55d451b2bd --- /dev/null +++ b/tests/pos/interleaving-params.scala @@ -0,0 +1,6 @@ +class Params{ + type U + def foo[T](x: T)[U >: x.type <: T][L <: List[U]](l: L): L = ??? + def aaa(x: U): U = ??? + def bbb[T <: U](x: U)[U]: U = ??? +} \ No newline at end of file diff --git a/tests/pos/interleaving-typeApply.scala b/tests/pos/interleaving-typeApply.scala new file mode 100644 index 000000000000..b637d59e2239 --- /dev/null +++ b/tests/pos/interleaving-typeApply.scala @@ -0,0 +1,22 @@ +object typeApply: + + def f0[T]: [U] => T => T = ??? + def f1[T][U]: T => T = ??? + def f2[T][U](): T => T = ??? + def f3[T <: Int][U <: String](): T => T = ??? + def f4[T <: Int][U <: String]: T => T = ??? + def f5[T <: Int][U <: String]: [X <: Unit] => X => X = ??? + def f6[T <: Int][U <: String](): [X <: Unit] => X => X = ??? + def f7[T <: Int][U <: String]()[X <: Unit]: X => X = ??? + + @main def test = { + f0[Int][String] + f1[Int][String] + f2[Int][String]() + f3[Int][String]() + f4[Int][String] + f5[Int][String] + f5[Int][String][Unit] + f6[Int]()[Unit] + f7[Int]()[Unit] + } \ No newline at end of file diff --git a/tests/pos/overrides.scala b/tests/pos/overrides.scala index 97402f773082..0f5d12367ca2 100644 --- a/tests/pos/overrides.scala +++ b/tests/pos/overrides.scala @@ -1,6 +1,7 @@ class A[T] { def f(x: T)(y: T = x) = y + def b[U <: T](x: Int)[V >: T](y: String) = false } @@ -10,4 +11,6 @@ class B extends A[Int] { f(2)() + override def b[T <: Int](x: Int)[U >: Int](y: String) = true + } From 61fef6697ce97864d5fc07e16c3a4851ba8a2306 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 23 Aug 2022 15:51:04 +0200 Subject: [PATCH 05/19] Generalise SymOps to more than one def type clause --- library/src/scala/quoted/Quotes.scala | 2 +- .../dotty/tools/scaladoc/tasty/SymOps.scala | 51 +++++++++---------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 48a387e64169..22ec107aeae8 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -3143,7 +3143,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Extension methods of `MethodType` */ trait MethodTypeMethods: extension (self: MethodType) - /** Is this the type of given parameter clause `(implicit X1, ..., Xn)`, `(given X1, ..., Xn)` or `(given x1: X1, ..., xn: Xn)` */ + /** Is this the type of using parameter clause `(implicit X1, ..., Xn)`, `(using X1, ..., Xn)` or `(using x1: X1, ..., xn: Xn)` */ def isImplicit: Boolean def isErased: Boolean def param(idx: Int): TypeRepr diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala index b4a1fc197d9a..584ef1f54267 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala @@ -143,7 +143,9 @@ object SymOps: import reflect._ sym.flags.is(Flags.Artifact) - def isLeftAssoc: Boolean = !sym.name.endsWith(":") + def isRightAssoc: Boolean = sym.name.endsWith(":") + + def isLeftAssoc: Boolean = !sym.isRightAssoc def extendedSymbol: Option[reflect.ValDef] = import reflect.* @@ -172,41 +174,36 @@ object SymOps: ) case _ => Nil -> Nil + def extendedParamLists: List[reflect.ParamClause] = sym.splitExtensionParamList._1 + + def extendedTypeParamLists: List[reflect.TypeParamClause] = + sym.extendedParamLists.collect { + case typeClause: reflect.TypeParamClause => typeClause + } + def extendedTypeParams: List[reflect.TypeDef] = - import reflect.* - sym.tree match - case tree: DefDef => - tree.leadingTypeParams - case _ => Nil + sym.extendedTypeParamLists.headOption.map(_.params).getOrElse(List()) def extendedTermParamLists: List[reflect.TermParamClause] = - import reflect.* - sym.splitExtensionParamList._1.collect { - case tpc: TermParamClause => tpc + sym.extendedParamLists.collect { + case tpc: reflect.TermParamClause => tpc } - def nonExtensionTermParamLists: List[reflect.TermParamClause] = - import reflect.* - if sym.nonExtensionLeadingTypeParams.nonEmpty then - sym.nonExtensionParamLists.dropWhile { - case _: TypeParamClause => false - case _ => true - }.drop(1).collect { - case tpc: TermParamClause => tpc - } - else - sym.nonExtensionParamLists.collect { - case tpc: TermParamClause => tpc - } - def nonExtensionParamLists: List[reflect.ParamClause] = sym.splitExtensionParamList._2 + def nonExtensionTermParamLists: List[reflect.TermParamClause] = + sym.nonExtensionParamLists.collect { + case tpc: reflect.TermParamClause => tpc + } + + def nonExtensionTypeParamLists: List[reflect.TypeParamClause] = + sym.nonExtensionParamLists.collect { + case typeClause: reflect.TypeParamClause => typeClause + } + def nonExtensionLeadingTypeParams: List[reflect.TypeDef] = - import reflect.* - sym.nonExtensionParamLists.collectFirst { - case TypeParamClause(params) => params - }.toList.flatten + sym.nonExtensionTypeParamLists.headOption.map(_.params).getOrElse(List()) end extension From 77c53c853ea5fa860dde0e74b18fa00454b6a9f7 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 23 Aug 2022 16:06:39 +0200 Subject: [PATCH 06/19] Uniformize api's Parameter case classes --- scaladoc/src/dotty/tools/scaladoc/api.scala | 20 ++++++++++--------- .../scaladoc/tasty/ClassLikeSupport.scala | 8 ++++---- .../translators/ScalaSignatureUtils.scala | 4 ++-- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/api.scala b/scaladoc/src/dotty/tools/scaladoc/api.scala index 90a03658c90e..9c8f17f7a074 100644 --- a/scaladoc/src/dotty/tools/scaladoc/api.scala +++ b/scaladoc/src/dotty/tools/scaladoc/api.scala @@ -44,24 +44,24 @@ enum Modifier(val name: String, val prefix: Boolean): case Transparent extends Modifier("transparent", true) case Infix extends Modifier("infix", true) -case class ExtensionTarget(name: String, typeParams: Seq[TypeParameter], argsLists: Seq[ParametersList], signature: Signature, dri: DRI, position: Long) +case class ExtensionTarget(name: String, typeParams: Seq[TypeParameter], argsLists: Seq[TermParameterList], signature: Signature, dri: DRI, position: Long) case class ImplicitConversion(from: DRI, to: DRI) trait ImplicitConversionProvider { def conversion: Option[ImplicitConversion] } trait Classlike: def typeParams: Seq[TypeParameter] = Seq.empty - def argsLists: Seq[ParametersList] = Seq.empty + def argsLists: Seq[TermParameterList] = Seq.empty enum Kind(val name: String): case RootPackage extends Kind("") case Package extends Kind("package") - case Class(override val typeParams: Seq[TypeParameter], override val argsLists: Seq[ParametersList]) + case Class(override val typeParams: Seq[TypeParameter], override val argsLists: Seq[TermParameterList]) extends Kind("class") with Classlike case Object extends Kind("object") with Classlike - case Trait(override val typeParams: Seq[TypeParameter], override val argsLists: Seq[ParametersList]) + case Trait(override val typeParams: Seq[TypeParameter], override val argsLists: Seq[TermParameterList]) extends Kind("trait") with Classlike - case Enum(override val typeParams: Seq[TypeParameter], override val argsLists: Seq[ParametersList]) extends Kind("enum") with Classlike + case Enum(override val typeParams: Seq[TypeParameter], override val argsLists: Seq[TermParameterList]) extends Kind("enum") with Classlike case EnumCase(kind: Object.type | Kind.Type | Val.type | Class) extends Kind("case") - case Def(typeParams: Seq[TypeParameter], argsLists: Seq[ParametersList]) + case Def(typeParams: Seq[TypeParameter], argsLists: Seq[TermParameterList]) extends Kind("def") case Extension(on: ExtensionTarget, m: Kind.Def) extends Kind("def") case Constructor(base: Kind.Def) extends Kind("def") @@ -97,12 +97,12 @@ object Annotation: case class LinkParameter(name: Option[String] = None, dri: DRI, value: String) extends AnnotationParameter case class UnresolvedParameter(name: Option[String] = None, unresolvedText: String) extends AnnotationParameter -case class ParametersList( - parameters: Seq[Parameter], +case class TermParameterList( + parameters: Seq[TermParameter], modifiers: String ) -case class Parameter( +case class TermParameter( annotations: Seq[Annotation], modifiers: String, name: Option[String], @@ -112,6 +112,8 @@ case class Parameter( isGrouped: Boolean = false ) +type TypeParameterList = Seq[TypeParameter] + case class TypeParameter( annotations: Seq[Annotation], variance: "" | "+" | "-", diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 38cc90330265..8419e0275fa8 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -45,7 +45,7 @@ trait ClassLikeSupport: .filter(s => s.exists && !s.isHiddenByVisibility) .map( _.tree.asInstanceOf[DefDef]) constr.fold(Nil)( - _.termParamss.map(pList => ParametersList(pList.params.map(p => mkParameter(p, parameterModifier)), paramListModifier(pList.params))) + _.termParamss.map(pList => TermParameterList(pList.params.map(p => mkParameter(p, parameterModifier)), paramListModifier(pList.params))) ) if classDef.symbol.flags.is(Flags.Module) then Kind.Object @@ -146,7 +146,7 @@ trait ClassLikeSupport: memberInfo.paramLists(index) match case EvidenceOnlyParameterList => Nil case info: RegularParameterList => - Seq(ParametersList(paramList.params.map(mkParameter(_, memberInfo = info)), paramListModifier(paramList.params))) + Seq(TermParameterList(paramList.params.map(mkParameter(_, memberInfo = info)), paramListModifier(paramList.params))) } val target = ExtensionTarget( extSym.symbol.normalizedName, @@ -351,7 +351,7 @@ trait ClassLikeSupport: memberInfo.paramLists(index) match case EvidenceOnlyParameterList => Nil case info: RegularParameterList => - Seq(ParametersList(pList.params.map( + Seq(TermParameterList(pList.params.map( mkParameter(_, paramPrefix, memberInfo = info)), paramListModifier(pList.params) )) } @@ -404,7 +404,7 @@ trait ClassLikeSupport: val inlinePrefix = if argument.symbol.flags.is(Flags.Inline) then "inline " else "" val nameIfNotSynthetic = Option.when(!argument.symbol.flags.is(Flags.Synthetic))(argument.symbol.normalizedName) val name = argument.symbol.normalizedName - Parameter( + TermParameter( argument.symbol.getAnnotations(), inlinePrefix + prefix(argument.symbol), nameIfNotSynthetic, diff --git a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala index acbfe87b5d25..4303cf6422b0 100644 --- a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala +++ b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala @@ -26,7 +26,7 @@ case class SignatureBuilder(content: Signature = Nil) extends ScalaSignatureUtil def annotationsBlock(d: Member): SignatureBuilder = d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation)} - def annotationsInline(d: Parameter): SignatureBuilder = + def annotationsInline(d: TermParameter): SignatureBuilder = d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation) } def annotationsInline(t: TypeParameter): SignatureBuilder = @@ -78,7 +78,7 @@ case class SignatureBuilder(content: Signature = Nil) extends ScalaSignatureUtil bdr.annotationsInline(e).keyword(e.variance).tpe(e.name, Some(e.dri)).signature(e.signature) } - def functionParameters(params: Seq[ParametersList]) = + def functionParameters(params: Seq[TermParameterList]) = if params.isEmpty then this.plain("") else if params.size == 1 && params(0).parameters == Nil then this.plain("()") else this.list(params, separator = List(Plain(""))) { (bld, pList) => From 908e5c8a656ae399f313d557c0ef6594d5647348 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 23 Aug 2022 16:21:29 +0200 Subject: [PATCH 07/19] Uniformize ScalaSignatureUtils param methods --- .../scaladoc/renderers/MemberRenderer.scala | 4 +-- .../tools/scaladoc/renderers/Resources.scala | 4 +-- .../translators/ScalaSignatureProvider.scala | 10 +++---- .../translators/ScalaSignatureUtils.scala | 30 +++++++++++-------- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala index 5d5f3e9b20d5..f2501378bf72 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala @@ -409,10 +409,10 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext case (Some(on), members) => val typeSig = SignatureBuilder() .keyword("extension ") - .generics(on.typeParams) + .typeParamList(on.typeParams) .content val argsSig = SignatureBuilder() - .functionParameters(on.argsLists) + .functionTermParameters(on.argsLists) .content val sig = typeSig ++ Signature(Plain(s"(${on.name}: ")) ++ on.signature ++ Signature(Plain(")")) ++ argsSig MGroup(span(cls := "groupHeader")(sig.map(renderElement(_))), members.sortBy(_.name).toSeq, on.name) -> on.position diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala index bae43980a11d..43b4440bce2c 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala @@ -180,10 +180,10 @@ trait Resources(using ctx: DocContext) extends Locations, Writer: case Kind.Extension(on, _) => val typeSig = SignatureBuilder() .keyword("extension ") - .generics(on.typeParams) + .typeParamList(on.typeParams) .content val argsSig = SignatureBuilder() - .functionParameters(on.argsLists) + .functionTermParameters(on.argsLists) .content flattenToText(typeSig ++ argsSig) case _ => "" diff --git a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala index 88561282afb0..02de12666666 100644 --- a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala +++ b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala @@ -58,8 +58,8 @@ class ScalaSignatureProvider: builder.kind(showKind), builder.name(member.name, member.dri), builder - .generics(kind.typeParams) - .functionParameters(kind.argsLists) + .typeParamList(kind.typeParams) + .functionTermParameters(kind.argsLists) .parentsSignature(member) ) @@ -106,8 +106,8 @@ class ScalaSignatureProvider: builder.kind(showKind), builder.name(method.name, method.dri), builder - .generics(kind.typeParams) - .functionParameters(kind.argsLists) + .typeParamList(kind.typeParams) + .functionTermParameters(kind.argsLists) .pipe { builder => instance.fold(builder)(i => builder.plain(": ").signature(i)) } @@ -151,7 +151,7 @@ class ScalaSignatureProvider: builder.modifiersAndVisibility(typeDef), builder.kind(tpe), builder.name(typeDef.name, typeDef.dri), - builder.generics(tpe.typeParams).pipe { bdr => + builder.typeParamList(tpe.typeParams).pipe { bdr => if (!tpe.opaque) { (if tpe.concreate then bdr.plain(" = ") else bdr) .signature(typeDef.signature) diff --git a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala index 4303cf6422b0..d28dd6ca18fe 100644 --- a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala +++ b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala @@ -74,21 +74,27 @@ case class SignatureBuilder(content: Signature = Nil) extends ScalaSignatureUtil def kind(k: Kind) = keyword(k.name + " ") - def generics(on: Seq[TypeParameter]) = list(on.toList, List(Plain("[")), List(Plain("]"))){ (bdr, e) => + + def functionParameters(paramss: Seq[ Either[TermParameterList,TypeParameterList] ]) = + this.list(paramss, separator = List(Plain(""))) { + case (bld, Left(params: TermParameterList)) => bld.termParamList(params) + case (bld, Right(params: TypeParameterList)) => bld.typeParamList(params) + } + + def termParamList(params: TermParameterList) = + this.list(params.parameters, prefix = List(Plain("("), Keyword(params.modifiers)), suffix = List(Plain(")")), forcePrefixAndSuffix = true) { (bld, p) => + val annotationsAndModifiers = bld.annotationsInline(p) + .keyword(p.modifiers) + val name = p.name.fold(annotationsAndModifiers)(annotationsAndModifiers.name(_, p.dri).plain(": ")) + name.signature(p.signature) + } + + def typeParamList(on: TypeParameterList) = list(on.toList, List(Plain("[")), List(Plain("]"))){ (bdr, e) => bdr.annotationsInline(e).keyword(e.variance).tpe(e.name, Some(e.dri)).signature(e.signature) } - def functionParameters(params: Seq[TermParameterList]) = - if params.isEmpty then this.plain("") - else if params.size == 1 && params(0).parameters == Nil then this.plain("()") - else this.list(params, separator = List(Plain(""))) { (bld, pList) => - bld.list(pList.parameters, prefix = List(Plain("("), Keyword(pList.modifiers)), suffix = List(Plain(")")), forcePrefixAndSuffix = true) { (bld, p) => - val annotationsAndModifiers = bld.annotationsInline(p) - .keyword(p.modifiers) - val name = p.name.fold(annotationsAndModifiers)(annotationsAndModifiers.name(_, p.dri).plain(": ")) - name.signature(p.signature) - } - } + def functionTermParameters(paramss: Seq[TermParameterList]) = + this.list(paramss, separator = List(Plain(""))) { (bld, pList) => bld.termParamList(pList) } trait ScalaSignatureUtils: extension (tokens: Seq[String]) def toSignatureString(): String = From a617c483fa7e0f8e100bd3b8f94faec2db9d62e4 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 23 Aug 2022 16:33:31 +0200 Subject: [PATCH 08/19] Refactor Kind.Def to use one paramLists --- scaladoc/src/dotty/tools/scaladoc/api.scala | 2 +- .../src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala | 8 ++++---- .../ImplicitMembersExtensionTransformer.scala | 2 +- .../scaladoc/translators/ScalaSignatureProvider.scala | 3 +-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/api.scala b/scaladoc/src/dotty/tools/scaladoc/api.scala index 9c8f17f7a074..5af55f76a211 100644 --- a/scaladoc/src/dotty/tools/scaladoc/api.scala +++ b/scaladoc/src/dotty/tools/scaladoc/api.scala @@ -61,7 +61,7 @@ enum Kind(val name: String): extends Kind("trait") with Classlike case Enum(override val typeParams: Seq[TypeParameter], override val argsLists: Seq[TermParameterList]) extends Kind("enum") with Classlike case EnumCase(kind: Object.type | Kind.Type | Val.type | Class) extends Kind("case") - case Def(typeParams: Seq[TypeParameter], argsLists: Seq[TermParameterList]) + case Def(paramLists: Seq[Either[TermParameterList,TypeParameterList]]) extends Kind("def") case Extension(on: ExtensionTarget, m: Kind.Def) extends Kind("def") case Constructor(base: Kind.Def) extends Kind("def") diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 8419e0275fa8..a95d5f3e10f2 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -346,14 +346,14 @@ trait ClassLikeSupport: val memberInfo = unwrapMemberInfo(c, methodSymbol) val basicKind: Kind.Def = Kind.Def( - genericTypes.map(mkTypeArgument(_, memberInfo.genericTypes, memberInfo.contextBounds)), + Right(genericTypes.map(mkTypeArgument(_, memberInfo.genericTypes, memberInfo.contextBounds))) +: paramLists.zipWithIndex.flatMap { (pList, index) => memberInfo.paramLists(index) match - case EvidenceOnlyParameterList => Nil + case EvidenceOnlyParameterList => None case info: RegularParameterList => - Seq(TermParameterList(pList.params.map( + Some(Left(TermParameterList(pList.params.map( mkParameter(_, paramPrefix, memberInfo = info)), paramListModifier(pList.params) - )) + ))) } ) diff --git a/scaladoc/src/dotty/tools/scaladoc/transformers/ImplicitMembersExtensionTransformer.scala b/scaladoc/src/dotty/tools/scaladoc/transformers/ImplicitMembersExtensionTransformer.scala index 44eba3a39807..8ed7436bb11d 100644 --- a/scaladoc/src/dotty/tools/scaladoc/transformers/ImplicitMembersExtensionTransformer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/transformers/ImplicitMembersExtensionTransformer.scala @@ -29,7 +29,7 @@ class ImplicitMembersExtensionTransformer(using DocContext) extends(Module => Mo case m @ Member(_, _, _, Kind.Extension(ExtensionTarget(_, _, _, _, MyDri, _), _), Origin.RegularlyDefined) => val kind = m.kind match case Kind.Extension(_, d) => d - case _ => Kind.Def(Nil, Nil) + case _ => Kind.Def(Nil) Seq(m.withOrigin(Origin.ExtensionFrom(source.name, source.dri)).withKind(kind)) case m @ Member(_, _, _, conversionProvider: ImplicitConversionProvider, Origin.RegularlyDefined) => diff --git a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala index 02de12666666..fd8dfc4f5b6c 100644 --- a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala +++ b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala @@ -106,8 +106,7 @@ class ScalaSignatureProvider: builder.kind(showKind), builder.name(method.name, method.dri), builder - .typeParamList(kind.typeParams) - .functionTermParameters(kind.argsLists) + .functionParameters(kind.paramLists) .pipe { builder => instance.fold(builder)(i => builder.plain(": ").signature(i)) } From 2126c4645304b97f5a28896db21f3f3754b5e761 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 23 Aug 2022 16:46:24 +0200 Subject: [PATCH 09/19] Refactor MemberInfo (breaks tests) This commit is only here for the sake of legibility, it breaks tests If you want to come back in the history, go to either the previous commit or the next --- .../scaladoc/tasty/ClassLikeSupport.scala | 62 ++++++++++++------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index a95d5f3e10f2..6a0201f5fb0b 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -12,6 +12,9 @@ import NameNormalizer._ import SyntheticsSupport._ import dotty.tools.dotc.core.NameKinds +// Please use this only for things defined in the api.scala file +import dotty.tools.{scaladoc => api} + trait ClassLikeSupport: self: TastyParser => import qctx.reflect._ @@ -45,7 +48,7 @@ trait ClassLikeSupport: .filter(s => s.exists && !s.isHiddenByVisibility) .map( _.tree.asInstanceOf[DefDef]) constr.fold(Nil)( - _.termParamss.map(pList => TermParameterList(pList.params.map(p => mkParameter(p, parameterModifier)), paramListModifier(pList.params))) + _.termParamss.map(pList => api.TermParameterList(pList.params.map(p => mkParameter(p, parameterModifier)), paramListModifier(pList.params))) ) if classDef.symbol.flags.is(Flags.Module) then Kind.Object @@ -142,11 +145,12 @@ trait ClassLikeSupport: dd.symbol.extendedSymbol.map { extSym => val memberInfo = unwrapMemberInfo(c, dd.symbol) val typeParams = dd.symbol.extendedTypeParams.map(mkTypeArgument(_, memberInfo.genericTypes)) - val termParams = dd.symbol.extendedTermParamLists.zipWithIndex.flatMap { case (paramList, index) => - memberInfo.paramLists(index) match - case EvidenceOnlyParameterList => Nil - case info: RegularParameterList => - Seq(TermParameterList(paramList.params.map(mkParameter(_, memberInfo = info)), paramListModifier(paramList.params))) + val termParams = dd.symbol.extendedTermParamLists.zipWithIndex.flatMap { case (termParamList, index) => + memberInfo.termParamLists(index) match + case MemberInfo.EvidenceOnlyParameterList => None + case MemberInfo.RegularParameterList(info) => + Some(api.TermParameterList(termParamList.params.map(mkParameter(_, memberInfo = info)), paramListModifier(termParamList.params))) + case _ => assert(false, "memberInfo.termParamLists contains a type parameter list !") } val target = ExtensionTarget( extSym.symbol.normalizedName, @@ -349,11 +353,12 @@ trait ClassLikeSupport: Right(genericTypes.map(mkTypeArgument(_, memberInfo.genericTypes, memberInfo.contextBounds))) +: paramLists.zipWithIndex.flatMap { (pList, index) => memberInfo.paramLists(index) match - case EvidenceOnlyParameterList => None - case info: RegularParameterList => + case MemberInfo.EvidenceOnlyParameterList => None + case MemberInfo.RegularParameterList(info) => Some(Left(TermParameterList(pList.params.map( mkParameter(_, paramPrefix, memberInfo = info)), paramListModifier(pList.params) ))) + case _ => assert(false, "memberInfo.termParamLists contains a type parameter list !") } ) @@ -404,7 +409,7 @@ trait ClassLikeSupport: val inlinePrefix = if argument.symbol.flags.is(Flags.Inline) then "inline " else "" val nameIfNotSynthetic = Option.when(!argument.symbol.flags.is(Flags.Synthetic))(argument.symbol.normalizedName) val name = argument.symbol.normalizedName - TermParameter( + api.TermParameter( argument.symbol.getAnnotations(), inlinePrefix + prefix(argument.symbol), nameIfNotSynthetic, @@ -514,16 +519,26 @@ trait ClassLikeSupport: experimental = experimental ) - object EvidenceOnlyParameterList - type RegularParameterList = Map[String, TypeRepr] - type ParameterList = RegularParameterList | EvidenceOnlyParameterList.type case class MemberInfo( - genericTypes: Map[String, TypeBounds], - paramLists: List[ParameterList], + paramLists: List[MemberInfo.ParameterList], res: TypeRepr, contextBounds: Map[String, DSignature] = Map.empty, - ) + ){ + val genericTypes: Map[String, TypeBounds] = paramLists.collect{ case MemberInfo.TypeParameterList(types) => types }.headOption.getOrElse(Map()) + + val termParamLists: List[MemberInfo.ParameterList] = paramLists.filter(_.isTerm) + } + + object MemberInfo: + enum ParameterList(val isTerm: Boolean, val isUsing: Boolean): + inline def isType = !isTerm + case EvidenceOnlyParameterList extends ParameterList(isTerm = true, isUsing = false) + case RegularParameterList(m: Map[String, TypeRepr])(isUsing: Boolean) extends ParameterList(isTerm = true, isUsing) + case TypeParameterList(m: Map[String, TypeBounds]) extends ParameterList(isTerm = false, isUsing = false) + + export ParameterList.{RegularParameterList, EvidenceOnlyParameterList, TypeParameterList} + def unwrapMemberInfo(c: ClassDef, symbol: Symbol): MemberInfo = @@ -541,10 +556,12 @@ trait ClassLikeSupport: symbol.paramSymss.flatten.find(_.name == name).exists(_.flags.is(Flags.Implicit)) def handlePolyType(memberInfo: MemberInfo, polyType: PolyType): MemberInfo = - MemberInfo(polyType.paramNames.zip(polyType.paramBounds).toMap, memberInfo.paramLists, polyType.resType) + val typeParamList = MemberInfo.TypeParameterList(polyType.paramNames.zip(polyType.paramBounds).toMap) + MemberInfo(memberInfo.paramLists :+ typeParamList, polyType.resType) def handleMethodType(memberInfo: MemberInfo, methodType: MethodType): MemberInfo = val rawParams = methodType.paramNames.zip(methodType.paramTypes).toMap + val isUsing = methodType.isImplicit val (evidences, notEvidences) = rawParams.partition(e => isSyntheticEvidence(e._1)) def findParamRefs(t: TypeRepr): Seq[ParamRef] = t match @@ -573,14 +590,15 @@ trait ClassLikeSupport: val newParams = notEvidences ++ paramsThatLookLikeContextBounds - val newLists: List[ParameterList] = if newParams.isEmpty && contextBounds.nonEmpty - then memberInfo.paramLists ++ Seq(EvidenceOnlyParameterList) - else memberInfo.paramLists ++ Seq(newParams) + val termParamList = if newParams.isEmpty && contextBounds.nonEmpty + then MemberInfo.EvidenceOnlyParameterList + else MemberInfo.RegularParameterList(newParams)(isUsing) + - MemberInfo(memberInfo.genericTypes, newLists , methodType.resType, contextBounds.toMap) + MemberInfo(memberInfo.paramLists :+ termParamList, methodType.resType, contextBounds.toMap) def handleByNameType(memberInfo: MemberInfo, byNameType: ByNameType): MemberInfo = - MemberInfo(memberInfo.genericTypes, memberInfo.paramLists, byNameType.underlying) + MemberInfo(memberInfo.paramLists, byNameType.underlying) def recursivelyCalculateMemberInfo(memberInfo: MemberInfo): MemberInfo = memberInfo.res match case p: PolyType => recursivelyCalculateMemberInfo(handlePolyType(memberInfo, p)) @@ -588,7 +606,7 @@ trait ClassLikeSupport: case b: ByNameType => handleByNameType(memberInfo, b) case _ => memberInfo - recursivelyCalculateMemberInfo(MemberInfo(Map.empty, List.empty, baseTypeRepr)) + recursivelyCalculateMemberInfo(MemberInfo(List.empty, baseTypeRepr)) private def paramListModifier(parameters: Seq[ValDef]): String = if parameters.size > 0 then From 9e8f91d28ebfc9c0589a468446c63b2e887f43de Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 23 Aug 2022 16:49:58 +0200 Subject: [PATCH 10/19] Add scaladoc support for interleaved clauses --- .../src/tests/extensionParams.scala | 5 ++ .../src/tests/methodsAndConstructors.scala | 3 + .../scaladoc/tasty/ClassLikeSupport.scala | 73 ++++++++++++------- 3 files changed, 55 insertions(+), 26 deletions(-) diff --git a/scaladoc-testcases/src/tests/extensionParams.scala b/scaladoc-testcases/src/tests/extensionParams.scala index 7892676af2c4..58b95c5dac1f 100644 --- a/scaladoc-testcases/src/tests/extensionParams.scala +++ b/scaladoc-testcases/src/tests/extensionParams.scala @@ -52,3 +52,8 @@ extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Num extension (using String)(using Unit)(a: Animal)(using Int)(using Number) def f11(b: Any)(c: Any): Any = ??? + def f13(b: Any)[T](c: T): T + = ??? + def f14[D](b: D)[T](c: T): T + = ??? + diff --git a/scaladoc-testcases/src/tests/methodsAndConstructors.scala b/scaladoc-testcases/src/tests/methodsAndConstructors.scala index b8925c593b4c..b4c354d174c4 100644 --- a/scaladoc-testcases/src/tests/methodsAndConstructors.scala +++ b/scaladoc-testcases/src/tests/methodsAndConstructors.scala @@ -60,3 +60,6 @@ class Methods: def withImplicitParam2(v: String)(implicit ab: Double, a: Int, b: String): String = ??? + def clauseInterleaving[T](x: T)[U](y: U)(using (T, U)): (T, U) + = ??? + diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 6a0201f5fb0b..c7178acac312 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -339,45 +339,66 @@ trait ClassLikeSupport: def parseMethod( c: ClassDef, methodSymbol: Symbol, - emptyParamsList: Boolean = false, paramPrefix: Symbol => String = _ => "", specificKind: (Kind.Def => Kind) = identity ): Member = val method = methodSymbol.tree.asInstanceOf[DefDef] - val paramLists: List[TermParamClause] = methodSymbol.nonExtensionTermParamLists - val genericTypes: List[TypeDef] = if (methodSymbol.isClassConstructor) Nil else methodSymbol.nonExtensionLeadingTypeParams + val paramLists = methodSymbol.nonExtensionParamLists val memberInfo = unwrapMemberInfo(c, methodSymbol) - val basicKind: Kind.Def = Kind.Def( - Right(genericTypes.map(mkTypeArgument(_, memberInfo.genericTypes, memberInfo.contextBounds))) +: - paramLists.zipWithIndex.flatMap { (pList, index) => - memberInfo.paramLists(index) match - case MemberInfo.EvidenceOnlyParameterList => None - case MemberInfo.RegularParameterList(info) => - Some(Left(TermParameterList(pList.params.map( + val unshuffledMemberInfoParamLists = + if methodSymbol.isExtensionMethod && methodSymbol.isRightAssoc then + // Taken from RefinedPrinter.scala + val (leadingTyParamss, rest1) = memberInfo.paramLists.span(_.isType) + val (leadingUsing, rest2) = rest1.span(_.isUsing) + val (rightTyParamss, rest3) = rest2.span(_.isType) + val (rightParamss, rest4) = rest3.splitAt(1) + val (leftParamss, rest5) = rest4.splitAt(1) + val (trailingUsing, rest6) = rest5.span(_.isUsing) + if leftParamss.nonEmpty then + // leadingTyParamss ::: leadingUsing ::: leftParamss ::: trailingUsing ::: rightTyParamss ::: rightParamss ::: rest6 + // because of takeRight after, this is equivalent to the following: + rightTyParamss ::: rightParamss ::: rest6 + else + memberInfo.paramLists // it wasn't a binary operator, after all. + else + memberInfo.paramLists + + val croppedUnshuffledMemberInfoParamLists = unshuffledMemberInfoParamLists.takeRight(paramLists.length) + + val basicDefKind: Kind.Def = Kind.Def( + paramLists.zip(croppedUnshuffledMemberInfoParamLists).flatMap{ + case (_: TermParamClause, MemberInfo.EvidenceOnlyParameterList) => Nil + case (pList: TermParamClause, MemberInfo.RegularParameterList(info)) => + Some(Left(api.TermParameterList(pList.params.map( mkParameter(_, paramPrefix, memberInfo = info)), paramListModifier(pList.params) ))) - case _ => assert(false, "memberInfo.termParamLists contains a type parameter list !") + case (TypeParamClause(genericTypeList), MemberInfo.TypeParameterList(memInfoTypes)) => + Some(Right(genericTypeList.map(mkTypeArgument(_, memInfoTypes, memberInfo.contextBounds)))) + case (_,_) => + assert(false, s"croppedUnshuffledMemberInfoParamLists and SymOps.nonExtensionParamLists disagree on whether this clause is a type or term one") } ) val methodKind = - if methodSymbol.isClassConstructor then Kind.Constructor(basicKind) - else if methodSymbol.flags.is(Flags.Implicit) then extractImplicitConversion(method.returnTpt.tpe) match - case Some(conversion) if paramLists.size == 0 || (paramLists.size == 1 && paramLists.head.params.size == 0) => - Kind.Implicit(basicKind, Some(conversion)) - case None if paramLists.size == 1 && paramLists(0).params.size == 1 => - Kind.Implicit(basicKind, Some( - ImplicitConversion( - paramLists(0).params(0).tpt.tpe.typeSymbol.dri, - method.returnTpt.tpe.typeSymbol.dri - ) - )) - case _ => - Kind.Implicit(basicKind, None) - else if methodSymbol.flags.is(Flags.Given) then Kind.Given(basicKind, Some(method.returnTpt.tpe.asSignature), extractImplicitConversion(method.returnTpt.tpe)) - else specificKind(basicKind) + if methodSymbol.isClassConstructor then Kind.Constructor(basicDefKind) + else if methodSymbol.flags.is(Flags.Implicit) then + val termParamLists: List[TermParamClause] = methodSymbol.nonExtensionTermParamLists + extractImplicitConversion(method.returnTpt.tpe) match + case Some(conversion) if termParamLists.size == 0 || (termParamLists.size == 1 && termParamLists.head.params.size == 0) => + Kind.Implicit(basicDefKind, Some(conversion)) + case None if termParamLists.size == 1 && termParamLists(0).params.size == 1 => + Kind.Implicit(basicDefKind, Some( + ImplicitConversion( + termParamLists(0).params(0).tpt.tpe.typeSymbol.dri, + method.returnTpt.tpe.typeSymbol.dri + ) + )) + case _ => + Kind.Implicit(basicDefKind, None) + else if methodSymbol.flags.is(Flags.Given) then Kind.Given(basicDefKind, Some(method.returnTpt.tpe.asSignature), extractImplicitConversion(method.returnTpt.tpe)) + else specificKind(basicDefKind) val origin = if !methodSymbol.isOverridden then Origin.RegularlyDefined else val overriddenSyms = methodSymbol.allOverriddenSymbols.map(_.owner) From b7de2b20519fca6b7a73207755cec1b0a6fbbbda Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Thu, 25 Aug 2022 15:03:34 +0200 Subject: [PATCH 11/19] Uniformize Parsers ParamClause methods --- .../dotty/tools/dotc/parsing/Parsers.scala | 118 ++++++++++-------- 1 file changed, 63 insertions(+), 55 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 99359ac06a37..a56c5175be02 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3083,16 +3083,17 @@ object Parsers { * | DefTermParamClause * | UsingParamClause */ - def typeOrTermParamClause(nparams: Int, // number of parameters preceding this clause - ofClass: Boolean = false, // owner is a class - ofCaseClass: Boolean = false, // owner is a case class - prefix: Boolean = false, // clause precedes name of an extension method - givenOnly: Boolean = false, // only given parameters allowed - firstClause: Boolean = false, // clause is the first in regular list of clauses - ownerKind: ParamOwner + def typeOrTermParamClause( + ownerKind: ParamOwner, + nparams: Int, // number of parameters preceding this clause + ofClass: Boolean = false, // owner is a class + ofCaseClass: Boolean = false, // owner is a case class + prefix: Boolean = false, // clause precedes name of an extension method + givenOnly: Boolean = false, // only given parameters allowed + firstClause: Boolean = false // clause is the first in regular list of clauses ): List[TypeDef] | List[ValDef] = if (in.token == LPAREN) - paramClause(nparams, ofClass, ofCaseClass, prefix, givenOnly, firstClause) + termParamClause(nparams, ofClass, ofCaseClass, prefix, givenOnly, firstClause) else if (in.token == LBRACKET) typeParamClause(ownerKind) else @@ -3103,20 +3104,20 @@ object Parsers { /** DefParamClauses ::= DefParamClause { DefParamClause } */ def typeOrTermParamClauses( - ownerKind: ParamOwner, - ofClass: Boolean = false, - ofCaseClass: Boolean = false, - givenOnly: Boolean = false, - numLeadParams: Int = 0 - ): List[List[TypeDef] | List[ValDef]] = - - def recur(firstClause: Boolean, nparams: Int): List[List[TypeDef] | List[ValDef]] = + ownerKind: ParamOwner, + ofClass: Boolean = false, + ofCaseClass: Boolean = false, + givenOnly: Boolean = false, + numLeadParams: Int = 0 + ): List[List[TypeDef] | List[ValDef]] = + + def recur(firstClause: Boolean, numLeadParams: Int): List[List[TypeDef] | List[ValDef]] = newLineOptWhenFollowedBy(LPAREN) newLineOptWhenFollowedBy(LBRACKET) if in.token == LPAREN then val paramsStart = in.offset - val params = paramClause( - nparams, + val params = termParamClause( + numLeadParams, ofClass = ofClass, ofCaseClass = ofCaseClass, givenOnly = givenOnly, @@ -3124,13 +3125,13 @@ object Parsers { val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit) params :: ( if lastClause then Nil - else recur(firstClause = false, nparams + params.length)) + else recur(firstClause = false, numLeadParams + params.length)) else if in.token == LBRACKET then - typeParamClause(ownerKind) :: recur(firstClause, nparams) + typeParamClause(ownerKind) :: recur(firstClause, numLeadParams) else Nil end recur - recur(firstClause = true, nparams = numLeadParams) + recur(firstClause = true, numLeadParams = numLeadParams) end typeOrTermParamClauses @@ -3187,15 +3188,15 @@ object Parsers { /** ContextTypes ::= FunArgType {‘,’ FunArgType} */ - def contextTypes(ofClass: Boolean, nparams: Int, impliedMods: Modifiers): List[ValDef] = + def contextTypes(ofClass: Boolean, numLeadParams: Int, impliedMods: Modifiers): List[ValDef] = val tps = commaSeparated(funArgType) - var counter = nparams + var counter = numLeadParams def nextIdx = { counter += 1; counter } val paramFlags = if ofClass then LocalParamAccessor else Param tps.map(makeSyntheticParameter(nextIdx, _, paramFlags | Synthetic | impliedMods.flags)) - /** ClsParamClause ::= ‘(’ [‘erased’] ClsParams ‘)’ | UsingClsParamClause - * UsingClsParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’ + /** ClsTermParamClause ::= ‘(’ [‘erased’] ClsParams ‘)’ | UsingClsTermParamClause + * UsingClsTermParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’ * ClsParams ::= ClsParam {‘,’ ClsParam} * ClsParam ::= {Annotation} * @@ -3212,13 +3213,14 @@ object Parsers { * * @return the list of parameter definitions */ - def paramClause(nparams: Int, // number of parameters preceding this clause - ofClass: Boolean = false, // owner is a class - ofCaseClass: Boolean = false, // owner is a case class - prefix: Boolean = false, // clause precedes name of an extension method - givenOnly: Boolean = false, // only given parameters allowed - firstClause: Boolean = false // clause is the first in regular list of clauses - ): List[ValDef] = { + def termParamClause( + numLeadParams: Int, // number of parameters preceding this clause + ofClass: Boolean = false, // owner is a class + ofCaseClass: Boolean = false, // owner is a case class + prefix: Boolean = false, // clause precedes name of an extension method + givenOnly: Boolean = false, // only given parameters allowed + firstClause: Boolean = false // clause is the first in regular list of clauses + ): List[ValDef] = { var impliedMods: Modifiers = EmptyModifiers def addParamMod(mod: () => Mod) = impliedMods = addMod(impliedMods, atSpan(in.skipToken()) { mod() }) @@ -3283,7 +3285,7 @@ object Parsers { checkVarArgsRules(rest) } - // begin paramClause + // begin termParamClause inParens { if in.token == RPAREN && !prefix && !impliedMods.is(Given) then Nil else @@ -3298,28 +3300,30 @@ object Parsers { || startParamTokens.contains(in.token) || isIdent && (in.name == nme.inline || in.lookahead.isColon) if isParams then commaSeparated(() => param()) - else contextTypes(ofClass, nparams, impliedMods) + else contextTypes(ofClass, numLeadParams, impliedMods) checkVarArgsRules(clause) clause } } - /** ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] - * TypelessClauses ::= TypelessClause {TypelessClause} + /** ClsTermParamClauses ::= {ClsTermParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] + * TypelessClauses ::= TypelessClause {TypelessClause} * * @return The parameter definitions */ - def paramClauses(ofClass: Boolean = false, - ofCaseClass: Boolean = false, - givenOnly: Boolean = false, - numLeadParams: Int = 0): List[List[ValDef]] = + def termParamClauses( + ofClass: Boolean = false, + ofCaseClass: Boolean = false, + givenOnly: Boolean = false, + numLeadParams: Int = 0 + ): List[List[ValDef]] = - def recur(firstClause: Boolean, nparams: Int): List[List[ValDef]] = + def recur(firstClause: Boolean, numLeadParams: Int): List[List[ValDef]] = newLineOptWhenFollowedBy(LPAREN) if in.token == LPAREN then val paramsStart = in.offset - val params = paramClause( - nparams, + val params = termParamClause( + numLeadParams, ofClass = ofClass, ofCaseClass = ofCaseClass, givenOnly = givenOnly, @@ -3327,12 +3331,12 @@ object Parsers { val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit) params :: ( if lastClause then Nil - else recur(firstClause = false, nparams + params.length)) + else recur(firstClause = false, numLeadParams + params.length)) else Nil end recur recur(firstClause = true, numLeadParams) - end paramClauses + end termParamClauses /* -------- DEFS ------------------------------------------- */ @@ -3573,11 +3577,15 @@ object Parsers { } } + + /** DefDef ::= DefSig [‘:’ Type] ‘=’ Expr * | this TypelessClauses [DefImplicitClause] `=' ConstrExpr * DefDcl ::= DefSig `:' Type + * DefSig ::= id [DefTypeParamClause] DefTermParamClauses + * + * if clauseInterleaving is enabled: * DefSig ::= id [DefParamClauses] [DefImplicitClause] - * | ExtParamClause [nl] [‘.’] id DefParamClauses */ def defDefOrDcl(start: Offset, mods: Modifiers, numLeadParams: Int = 0): DefDef = atSpan(start, nameStart) { @@ -3596,7 +3604,7 @@ object Parsers { if (in.token == THIS) { in.nextToken() - val vparamss = paramClauses(numLeadParams = numLeadParams) + val vparamss = termParamClauses(numLeadParams = numLeadParams) if (vparamss.isEmpty || vparamss.head.take(1).exists(_.mods.isOneOf(GivenOrImplicit))) in.token match { case LBRACKET => syntaxError(em"no type parameters allowed here") @@ -3753,12 +3761,12 @@ object Parsers { val templ = templateOpt(constr) finalizeDef(TypeDef(name, templ), mods, start) - /** ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses + /** ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsTermParamClauses */ def classConstr(isCaseClass: Boolean = false): DefDef = atSpan(in.lastOffset) { val tparams = typeParamClauseOpt(ParamOwner.Class) val cmods = fromWithinClassConstr(constrModsOpt()) - val vparamss = paramClauses(ofClass = true, ofCaseClass = isCaseClass) + val vparamss = termParamClauses(ofClass = true, ofCaseClass = isCaseClass) makeConstructor(tparams, vparamss).withMods(cmods) } @@ -3860,7 +3868,7 @@ object Parsers { newLineOpt() val vparamss = if in.token == LPAREN && in.lookahead.isIdent(nme.using) - then paramClauses(givenOnly = true) + then termParamClauses(givenOnly = true) else Nil newLinesOpt() val noParams = tparams.isEmpty && vparamss.isEmpty @@ -3902,13 +3910,13 @@ object Parsers { val start = in.skipToken() val tparams = typeParamClauseOpt(ParamOwner.Def) val leadParamss = ListBuffer[List[ValDef]]() - def nparams = leadParamss.map(_.length).sum + def numLeadParams = leadParamss.map(_.length).sum while - val extParams = paramClause(nparams, prefix = true) + val extParams = termParamClause(numLeadParams, prefix = true) leadParamss += extParams isUsingClause(extParams) do () - leadParamss ++= paramClauses(givenOnly = true, numLeadParams = nparams) + leadParamss ++= termParamClauses(givenOnly = true, numLeadParams = numLeadParams) if in.isColon then syntaxError(em"no `:` expected here") in.nextToken() @@ -3916,11 +3924,11 @@ object Parsers { if in.token == EXPORT then exportClause() else if isDefIntro(modifierTokens) then - extMethod(nparams) :: Nil + extMethod(numLeadParams) :: Nil else in.observeIndented() newLineOptWhenFollowedBy(LBRACE) - if in.isNestedStart then inDefScopeBraces(extMethods(nparams)) + if in.isNestedStart then inDefScopeBraces(extMethods(numLeadParams)) else { syntaxErrorOrIncomplete(em"Extension without extension methods") ; Nil } val result = atSpan(start)(ExtMethods(joinParams(tparams, leadParamss.toList), methods)) val comment = in.getDocComment(start) From cd27721adbf3b425dc150bffc3e97274e77d5ac1 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 29 Nov 2022 13:18:29 +0100 Subject: [PATCH 12/19] Remove type currying This adds back the TypeApply case that was present in Applications.scala. With it is an improved error message that shows a potential fix. --- .../dotty/tools/dotc/parsing/Parsers.scala | 15 ++++++++++----- .../dotty/tools/dotc/typer/Applications.scala | 6 ++++++ docs/_docs/internals/syntax.md | 4 ++-- .../generalized-method-syntax.md | 19 +++++-------------- docs/_docs/reference/syntax.md | 2 +- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index a56c5175be02..bac5d4e94d99 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3101,7 +3101,7 @@ object Parsers { end typeOrTermParamClause - /** DefParamClauses ::= DefParamClause { DefParamClause } + /** DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent */ def typeOrTermParamClauses( ownerKind: ParamOwner, @@ -3111,7 +3111,7 @@ object Parsers { numLeadParams: Int = 0 ): List[List[TypeDef] | List[ValDef]] = - def recur(firstClause: Boolean, numLeadParams: Int): List[List[TypeDef] | List[ValDef]] = + def recur(firstClause: Boolean, numLeadParams: Int, prevIsTypeClause: Boolean): List[List[TypeDef] | List[ValDef]] = newLineOptWhenFollowedBy(LPAREN) newLineOptWhenFollowedBy(LBRACKET) if in.token == LPAREN then @@ -3125,13 +3125,18 @@ object Parsers { val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit) params :: ( if lastClause then Nil - else recur(firstClause = false, numLeadParams + params.length)) + else recur(firstClause = false, numLeadParams + params.length, prevIsTypeClause = false)) else if in.token == LBRACKET then - typeParamClause(ownerKind) :: recur(firstClause, numLeadParams) + if prevIsTypeClause then + syntaxError( + em"Type parameter lists must be separated by a term or using parameter list", + in.offset + ) + typeParamClause(ownerKind) :: recur(firstClause, numLeadParams, prevIsTypeClause = true) else Nil end recur - recur(firstClause = true, numLeadParams = numLeadParams) + recur(firstClause = true, numLeadParams = numLeadParams, prevIsTypeClause = false) end typeOrTermParamClauses diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 19b96f00d365..e15121ff1636 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1151,6 +1151,12 @@ trait Applications extends Compatibility { val typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_)) record("typedTypeApply") typedExpr(tree.fun, PolyProto(typedArgs, pt)) match { + case fun: TypeApply if !ctx.isAfterTyper => + val function = fun.fun + val args = (fun.args ++ tree.args).map(_.show).mkString(", ") + errorTree(tree, em"""illegal repeated type application + |You might have meant something like: + |${function}[${args}]""") case typedFn => typedFn.tpe.widen match { case pt: PolyType => diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index dfdad496d446..1fbb7a34b078 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -361,7 +361,7 @@ ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param -DefParamClauses ::= DefParamClause { DefParamClause } +DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent DefParamClause ::= DefTypeParamClause | DefTermParamClause | UsingParamClause @@ -427,7 +427,7 @@ Dcl ::= RefineDcl | ‘var’ VarDcl ValDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) VarDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) -DefDcl ::= DefSig ‘:’ Type DefDef(_, name, tparams, vparamss, tpe, EmptyTree) +DefDcl ::= DefSig ‘:’ Type DefDef(_, name, paramss, tpe, EmptyTree) DefSig ::= id [DefParamClauses] [DefImplicitClause] TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds TypeDefTree(_, name, tparams, bound [‘=’ Type] diff --git a/docs/_docs/reference/other-new-features/generalized-method-syntax.md b/docs/_docs/reference/other-new-features/generalized-method-syntax.md index dbe9c6eb0a61..c78a5df8a3f4 100644 --- a/docs/_docs/reference/other-new-features/generalized-method-syntax.md +++ b/docs/_docs/reference/other-new-features/generalized-method-syntax.md @@ -18,11 +18,11 @@ def foo[T, U](x: T)(y: U)(z: Int, s: String)(a: Array[T])(implicit ordInt: Ord[I ### In Scala 3 -The new syntax allows any number of type, term and using clause, in any order, optionnaly followed by an implicit clause: +The new syntax allows any number of type clauses, as long as they are not adjacent: (do note however that [implicit clause are discouraged, in favor of using clauses](https://docs.scala-lang.org/scala3/reference/contextual/relationship-implicits.html)) ```scala -def foo[T, U](x: T)(y: U)[V](z: V, s: String)[A](a: Array[A])(implicit List[U]) +def foo[T, U](x: T)(y: U)[V](z: V, s: String)(using Ord[Int])[A](a: Array[A])(implicit List[U]) ``` ### Unchanged @@ -48,15 +48,6 @@ trait DB { Note that simply replacing `V` by `k.Value` would not be equivalent. For example, if `k.Value` is `Some[Int]`, only the above allows: `getOrElse(k)[Option[Int]](None)`, which returns a `Number`. -### Partial Inference - -It is now possible to only infer some of the type parameters, this reduces boilerplate at the use site: -```scala -trait StaticSizeList[S <: Int & Singleton, T] -def filled[S <: Int & Singleton][T](x: T): StaticSizeList[S,T] = ??? -val helloes = filled[4]("Hello!") // S=4, and T is inferred -``` - ## Details ### Application @@ -90,9 +81,9 @@ On the other hand, the `getOrElse` method is recommended as-is, as it cannot be DefDcl ::= DefSig ‘:’ Type DefDef ::= DefSig [‘:’ Type] ‘=’ Expr DefSig ::= id [DefParamClauses] [DefImplicitClause] -DefParamClauses ::= DefParamClause { DefParamClause } -DefParamClause ::= DefTypeParamClause - | DefTermParamClause +DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent +DefParamClause ::= DefTypeParamClause + | DefTermParamClause | UsingParamClause DefTypeParamClause::= [nl] ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index 1d0c45030d2c..dde70d2131ad 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -350,7 +350,7 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param -DefParamClauses ::= DefParamClause { DefParamClause } +DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent DefParamClause ::= DefTypeParamClause | DefTermParamClause | UsingParamClause From d59a3e61d78a1a5ddc5248d4929b9a3111879335 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 6 Dec 2022 16:41:53 +0100 Subject: [PATCH 13/19] Update tests --- tests/neg/interleaving-ab.scala | 5 +++-- tests/neg/interleaving-typeApply.scala | 6 +++--- tests/neg/namedTypeParams.scala | 7 +++++-- tests/pos/interleaving-ba.scala | 4 ++-- tests/pos/interleaving-classless.scala | 6 +++--- ...leaving.scala => interleaving-functor.scala} | 2 +- tests/pos/interleaving-functorCurrying.scala | 17 ----------------- tests/pos/interleaving-overload.scala | 4 ++-- tests/pos/interleaving-params.scala | 2 +- tests/pos/interleaving-typeApply.scala | 14 +++++++------- tests/pos/namedTypeParams.scala | 6 ++++++ 11 files changed, 33 insertions(+), 40 deletions(-) rename tests/pos/{interleaving-functorInterleaving.scala => interleaving-functor.scala} (90%) delete mode 100644 tests/pos/interleaving-functorCurrying.scala diff --git a/tests/neg/interleaving-ab.scala b/tests/neg/interleaving-ab.scala index 0d6649bbb3be..281002ba37fc 100644 --- a/tests/neg/interleaving-ab.scala +++ b/tests/neg/interleaving-ab.scala @@ -2,7 +2,8 @@ object Ab: given String = "" given Double = 0 - def ab[A][B](x: A)(using B): B = summon[B] - + def illegal[A][B](x: A)(using B): B = summon[B] // error: Type parameter lists must be separated by a term or using parameter list + + def ab[A](x: A)[B](using B): B = summon[B] def test = ab[Int](0: Int) // error \ No newline at end of file diff --git a/tests/neg/interleaving-typeApply.scala b/tests/neg/interleaving-typeApply.scala index 2e5f652dc21a..498873abae91 100644 --- a/tests/neg/interleaving-typeApply.scala +++ b/tests/neg/interleaving-typeApply.scala @@ -1,8 +1,8 @@ object typeApply: - def f3[T <: Int][U <: String](): T => T = ??? - def f5[T <: Int][U <: String]: [X <: Unit] => X => X = ??? - def f7[T <: Int][U <: String]()[X <: Unit]: X => X = ??? + def f3[T <: Int](using DummyImplicit)[U <: String](): T => T = ??? + def f5[T <: Int](using DummyImplicit)[U <: String]: [X <: Unit] => X => X = ??? + def f7[T <: Int](using DummyImplicit)[U <: String]()[X <: Unit]: X => X = ??? @main def test = { f3[String]() // error diff --git a/tests/neg/namedTypeParams.scala b/tests/neg/namedTypeParams.scala index 197fc6a9a790..1b85ee6349f4 100644 --- a/tests/neg/namedTypeParams.scala +++ b/tests/neg/namedTypeParams.scala @@ -17,7 +17,6 @@ object Test { def f[X, Y](x: X, y: Y): Int = ??? f[X = Int, String](1, "") // error // error - /* Conflicts with Clause Interweaving, stems from named type parameters assuming one type clause f[X = Int][X = Int][Y = String](1, "") // error: illegal repeated type application f[X = Int][Y = String](1, "") // error: illegal repeated type application @@ -25,5 +24,9 @@ object Test { f[Y = String][X = Int](1, "") // error: illegal repeated type application f[Y = String][Int](1, "") // error: illegal repeated type application - */ + + def f2[X](using DummyImplicit)[Y](x: X, y: Y): Int = ??? + + f2[Y = String][X = Int](1, "") // error: Y is undefined + f2[Y = String](1, "") // error: Y is undefined } diff --git a/tests/pos/interleaving-ba.scala b/tests/pos/interleaving-ba.scala index 74c15d4e87e9..dc2b36202891 100644 --- a/tests/pos/interleaving-ba.scala +++ b/tests/pos/interleaving-ba.scala @@ -4,8 +4,8 @@ object BA { given String = "" given Double = 0 - def ba[B][A](x: A)(using B): B = summon[B] + def ba[A](x: A)[B](using B): B = summon[B] - def test = ba[String](0) + def test = ba(0)[String] } \ No newline at end of file diff --git a/tests/pos/interleaving-classless.scala b/tests/pos/interleaving-classless.scala index 4308030f66a6..924202673029 100644 --- a/tests/pos/interleaving-classless.scala +++ b/tests/pos/interleaving-classless.scala @@ -1,4 +1,4 @@ -def f1[T][U](x: T, y: U): (T, U) = (x, y) +def f1[T]()[U](x: T, y: U): (T, U) = (x, y) def f2[T](x: T)[U](y: U): (T, U) = (x, y) -def f3[T, U][V](x: T): U = ??? -def f4[T](x: T)[U <: x.type](y: U): (T, U) = (x, y) \ No newline at end of file +def f3[T, U](using DummyImplicit)[V](x: T): U = ??? +def f4[T](x: T)[U <: x.type](y: U): (T, U) = (x, y) diff --git a/tests/pos/interleaving-functorInterleaving.scala b/tests/pos/interleaving-functor.scala similarity index 90% rename from tests/pos/interleaving-functorInterleaving.scala rename to tests/pos/interleaving-functor.scala index fb14c151387c..00c26151f5cb 100644 --- a/tests/pos/interleaving-functorInterleaving.scala +++ b/tests/pos/interleaving-functor.scala @@ -10,7 +10,7 @@ object functorInterweaving: def map[A](x: List[A])[B](f: A => B): List[B] = x.map(f) - def assertTransformation[F[_]: Functor][A](original: F[A])[B](expected: F[B])(mapping: A => B): Unit = + def assertTransformation[F[_]: Functor, A](original: F[A])[B](expected: F[B])(mapping: A => B): Unit = assert(expected == summon[Functor[F]].map(original)(mapping)) @main def testInterweaving = diff --git a/tests/pos/interleaving-functorCurrying.scala b/tests/pos/interleaving-functorCurrying.scala deleted file mode 100644 index 078d65233997..000000000000 --- a/tests/pos/interleaving-functorCurrying.scala +++ /dev/null @@ -1,17 +0,0 @@ -object functorCurrying: - //taken from https://dotty.epfl.ch/docs/reference/contextual/type-classes.html - //at version 3.1.1-RC1-bin-20210930-01f040b-NIGHTLY - //modified to have type currying - trait Functor[F[_]]: - def map[A][B](x: F[A], f: A => B): F[B] - - - given Functor[List] with - def map[A][B](x: List[A], f: A => B): List[B] = - x.map(f) - - def assertTransformation[F[_]: Functor][A][B](expected: F[B], original: F[A], mapping: A => B): Unit = - assert(expected == summon[Functor[F]].map(original, mapping)) - - @main def testCurrying = - assertTransformation(List("a1", "b1"), List("a", "b"), elt => s"${elt}1") diff --git a/tests/pos/interleaving-overload.scala b/tests/pos/interleaving-overload.scala index 3dcb0a911704..02074fd82914 100644 --- a/tests/pos/interleaving-overload.scala +++ b/tests/pos/interleaving-overload.scala @@ -1,8 +1,8 @@ class A{ - def f1[T][U](x: Any) = ??? - def f1[T][U](x: Int) = ??? + def f1[T](x: Any)[U] = ??? + def f1[T](x: Int)[U] = ??? f1(1) f1("hello") diff --git a/tests/pos/interleaving-params.scala b/tests/pos/interleaving-params.scala index fb55d451b2bd..dec9f6780370 100644 --- a/tests/pos/interleaving-params.scala +++ b/tests/pos/interleaving-params.scala @@ -1,6 +1,6 @@ class Params{ type U - def foo[T](x: T)[U >: x.type <: T][L <: List[U]](l: L): L = ??? + def foo[T](x: T)[U >: x.type <: T](using U)[L <: List[U]](l: L): L = ??? def aaa(x: U): U = ??? def bbb[T <: U](x: U)[U]: U = ??? } \ No newline at end of file diff --git a/tests/pos/interleaving-typeApply.scala b/tests/pos/interleaving-typeApply.scala index b637d59e2239..5b0be806bf33 100644 --- a/tests/pos/interleaving-typeApply.scala +++ b/tests/pos/interleaving-typeApply.scala @@ -1,13 +1,13 @@ object typeApply: def f0[T]: [U] => T => T = ??? - def f1[T][U]: T => T = ??? - def f2[T][U](): T => T = ??? - def f3[T <: Int][U <: String](): T => T = ??? - def f4[T <: Int][U <: String]: T => T = ??? - def f5[T <: Int][U <: String]: [X <: Unit] => X => X = ??? - def f6[T <: Int][U <: String](): [X <: Unit] => X => X = ??? - def f7[T <: Int][U <: String]()[X <: Unit]: X => X = ??? + def f1[T](using DummyImplicit)[U]: T => T = ??? + def f2[T](using DummyImplicit)[U](): T => T = ??? + def f3[T <: Int](using DummyImplicit)[U <: String](): T => T = ??? + def f4[T <: Int](using DummyImplicit)[U <: String]: T => T = ??? + def f5[T <: Int](using DummyImplicit)[U <: String]: [X <: Unit] => X => X = ??? + def f6[T <: Int](using DummyImplicit)[U <: String](): [X <: Unit] => X => X = ??? + def f7[T <: Int](using DummyImplicit)[U <: String]()[X <: Unit]: X => X = ??? @main def test = { f0[Int][String] diff --git a/tests/pos/namedTypeParams.scala b/tests/pos/namedTypeParams.scala index a8c38972838c..86f35bf3c596 100644 --- a/tests/pos/namedTypeParams.scala +++ b/tests/pos/namedTypeParams.scala @@ -9,4 +9,10 @@ object Test { f[Y = String](1, "") + def f2[X](using DummyImplicit)[Y](x: X, y: Y): Int = ??? + + f2[X = Int][Y = String](1, "") + f2[X = Int](1, "") + + } From 1a6c5c5552a15458c617cdbe09d4f8ce09c7462b Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Fri, 13 Jan 2023 14:45:17 +0100 Subject: [PATCH 14/19] Add and Fix tests --- .../src/tests/extensionParams.scala | 1 - .../src/tests/methodsAndConstructors.scala | 1 - tests/neg/extension-methods.scala | 2 +- tests/neg/interleaving-ab.scala | 14 +-- tests/neg/interleaving-nameCollision.scala | 3 - tests/neg/interleaving-params.scala | 2 +- .../neg/interleaving-signatureCollision.scala | 3 + tests/neg/interleaving-typeApply.check | 30 ++++++ tests/neg/interleaving-typeApply.scala | 2 +- tests/neg/interleaving-unmatched.scala | 2 +- tests/neg/namedTypeParams.check | 102 ++++++++++++++++++ tests/neg/namedTypeParams.scala | 5 +- tests/neg/overrides.scala | 2 - tests/pos/interleaving-ba.scala | 3 +- tests/pos/interleaving-chainedParams.scala | 24 ++--- tests/pos/interleaving-functor.scala | 4 +- tests/pos/interleaving-nameCollision.scala | 5 - tests/pos/interleaving-overload.scala | 2 +- tests/pos/interleaving-params.scala | 2 +- .../pos/interleaving-signatureCollision.scala | 5 + tests/pos/interleaving-typeApply.scala | 2 +- tests/pos/namedTypeParams.scala | 1 - tests/run/interleaving.scala | 101 +++++++++++++++++ 23 files changed, 273 insertions(+), 45 deletions(-) delete mode 100644 tests/neg/interleaving-nameCollision.scala create mode 100644 tests/neg/interleaving-signatureCollision.scala create mode 100644 tests/neg/interleaving-typeApply.check create mode 100644 tests/neg/namedTypeParams.check delete mode 100644 tests/pos/interleaving-nameCollision.scala create mode 100644 tests/pos/interleaving-signatureCollision.scala create mode 100644 tests/run/interleaving.scala diff --git a/scaladoc-testcases/src/tests/extensionParams.scala b/scaladoc-testcases/src/tests/extensionParams.scala index 58b95c5dac1f..e064ff6d56d7 100644 --- a/scaladoc-testcases/src/tests/extensionParams.scala +++ b/scaladoc-testcases/src/tests/extensionParams.scala @@ -56,4 +56,3 @@ extension (using String)(using Unit)(a: Animal)(using Int)(using Number) = ??? def f14[D](b: D)[T](c: T): T = ??? - diff --git a/scaladoc-testcases/src/tests/methodsAndConstructors.scala b/scaladoc-testcases/src/tests/methodsAndConstructors.scala index b4c354d174c4..5fc6af38888b 100644 --- a/scaladoc-testcases/src/tests/methodsAndConstructors.scala +++ b/scaladoc-testcases/src/tests/methodsAndConstructors.scala @@ -62,4 +62,3 @@ class Methods: def clauseInterleaving[T](x: T)[U](y: U)(using (T, U)): (T, U) = ??? - diff --git a/tests/neg/extension-methods.scala b/tests/neg/extension-methods.scala index e075105762f9..a11b2cca5add 100644 --- a/tests/neg/extension-methods.scala +++ b/tests/neg/extension-methods.scala @@ -15,4 +15,4 @@ object Test { 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 } -} \ No newline at end of file +} diff --git a/tests/neg/interleaving-ab.scala b/tests/neg/interleaving-ab.scala index 281002ba37fc..5516d9b9dd51 100644 --- a/tests/neg/interleaving-ab.scala +++ b/tests/neg/interleaving-ab.scala @@ -1,9 +1,9 @@ object Ab: - given String = "" - given Double = 0 + given String = "" + given Double = 0 - def illegal[A][B](x: A)(using B): B = summon[B] // error: Type parameter lists must be separated by a term or using parameter list - - def ab[A](x: A)[B](using B): B = summon[B] - def test = - ab[Int](0: Int) // error \ No newline at end of file + def illegal[A][B](x: A)(using B): B = summon[B] // error: Type parameter lists must be separated by a term or using parameter list + + def ab[A](x: A)[B](using B): B = summon[B] + def test = + ab[Int](0: Int) // error diff --git a/tests/neg/interleaving-nameCollision.scala b/tests/neg/interleaving-nameCollision.scala deleted file mode 100644 index 44aa1e169db4..000000000000 --- a/tests/neg/interleaving-nameCollision.scala +++ /dev/null @@ -1,3 +0,0 @@ -object nameCollision: - def f[T](x: T)[U](y: U) = (x,y) - def f[T](x: T, y: T) = (x,y) // error \ No newline at end of file diff --git a/tests/neg/interleaving-params.scala b/tests/neg/interleaving-params.scala index e8fa5361a9ab..97de1e6e2948 100644 --- a/tests/neg/interleaving-params.scala +++ b/tests/neg/interleaving-params.scala @@ -4,4 +4,4 @@ class Params{ def bbb[T <: U](x: U)[U]: U = ??? // error // error def f0[T](implicit x: T)[U](y: U) = (x,y) // error def f1[T](implicit x: T)[U] = (x,y) // error -} \ No newline at end of file +} diff --git a/tests/neg/interleaving-signatureCollision.scala b/tests/neg/interleaving-signatureCollision.scala new file mode 100644 index 000000000000..d2df384dc1e7 --- /dev/null +++ b/tests/neg/interleaving-signatureCollision.scala @@ -0,0 +1,3 @@ +object signatureCollision: + def f[T](x: T)[U](y: U) = (x,y) + def f[T](x: T, y: T) = (x,y) // error diff --git a/tests/neg/interleaving-typeApply.check b/tests/neg/interleaving-typeApply.check new file mode 100644 index 000000000000..c932a899dfe6 --- /dev/null +++ b/tests/neg/interleaving-typeApply.check @@ -0,0 +1,30 @@ +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:8:11 --------------------------------------------- +8 | f3[String]() // error + | ^ + | Type argument String does not conform to upper bound Int + | + | longer explanation available when compiling with `-explain` +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:9:16 --------------------------------------------- +9 | f5[Int][Unit] // error + | ^ + | Type argument Unit does not conform to upper bound String + | + | longer explanation available when compiling with `-explain` +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:10:19 -------------------------------------------- +10 | f5[String][Unit] // error // error + | ^ + | Type argument Unit does not conform to upper bound String + | + | longer explanation available when compiling with `-explain` +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:10:11 -------------------------------------------- +10 | f5[String][Unit] // error // error + | ^ + | Type argument String does not conform to upper bound Int + | + | longer explanation available when compiling with `-explain` +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:11:11 -------------------------------------------- +11 | f7[String]()[Unit] // error + | ^ + | Type argument String does not conform to upper bound Int + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/interleaving-typeApply.scala b/tests/neg/interleaving-typeApply.scala index 498873abae91..18a21b4984e1 100644 --- a/tests/neg/interleaving-typeApply.scala +++ b/tests/neg/interleaving-typeApply.scala @@ -9,4 +9,4 @@ object typeApply: f5[Int][Unit] // error f5[String][Unit] // error // error f7[String]()[Unit] // error - } \ No newline at end of file + } diff --git a/tests/neg/interleaving-unmatched.scala b/tests/neg/interleaving-unmatched.scala index b2afcf5b5d45..c433c0a7210a 100644 --- a/tests/neg/interleaving-unmatched.scala +++ b/tests/neg/interleaving-unmatched.scala @@ -1,3 +1,3 @@ object unmatched: def f1[T (x: T)] = ??? // error - def f2(x: Any[)T] = ??? // error // error \ No newline at end of file + def f2(x: Any[)T] = ??? // error // error diff --git a/tests/neg/namedTypeParams.check b/tests/neg/namedTypeParams.check new file mode 100644 index 000000000000..1252f29c4e9e --- /dev/null +++ b/tests/neg/namedTypeParams.check @@ -0,0 +1,102 @@ +-- [E040] Syntax Error: tests/neg/namedTypeParams.scala:2:8 ------------------------------------------------------------ +2 |class D[type T] // error: identifier expected, but `type` found + | ^^^^ + | an identifier expected, but 'type' found + | + | longer explanation available when compiling with `-explain` +-- [E040] Syntax Error: tests/neg/namedTypeParams.scala:11:13 ---------------------------------------------------------- +11 | val x: C[T = Int] = // error: ']' expected, but `=` found // error + | ^ + | ']' expected, but '=' found +-- [E040] Syntax Error: tests/neg/namedTypeParams.scala:12:12 ---------------------------------------------------------- +12 | new C[T = Int] // error: ']' expected, but `=` found // error + | ^ + | ']' expected, but '=' found +-- [E040] Syntax Error: tests/neg/namedTypeParams.scala:14:22 ---------------------------------------------------------- +14 | class E extends C[T = Int] // error: ']' expected, but `=` found // error + | ^ + | ']' expected, but '=' found +-- [E040] Syntax Error: tests/neg/namedTypeParams.scala:15:22 ---------------------------------------------------------- +15 | class F extends C[T = Int]() // error: ']' expected, but `=` found // error + | ^ + | ']' expected, but '=' found +-- [E040] Syntax Error: tests/neg/namedTypeParams.scala:19:19 ---------------------------------------------------------- +19 | f[X = Int, String](1, "") // error // error + | ^ + | '=' expected, but ']' found +-- Error: tests/neg/namedTypeParams.scala:6:8 -------------------------------------------------------------------------- +6 | f[X = Int, Y = Int](1, 2) // error: experimental // error: experimental + | ^^^ + | Named type arguments are experimental, + | they must be enabled with a `experimental.namedTypeArguments` language import or setting +-- Error: tests/neg/namedTypeParams.scala:6:17 ------------------------------------------------------------------------- +6 | f[X = Int, Y = Int](1, 2) // error: experimental // error: experimental + | ^^^ + | Named type arguments are experimental, + | they must be enabled with a `experimental.namedTypeArguments` language import or setting +-- [E006] Not Found Error: tests/neg/namedTypeParams.scala:11:11 ------------------------------------------------------- +11 | val x: C[T = Int] = // error: ']' expected, but `=` found // error + | ^ + | Not found: type T + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg/namedTypeParams.scala:12:10 ------------------------------------------------------- +12 | new C[T = Int] // error: ']' expected, but `=` found // error + | ^ + | Not found: type T + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg/namedTypeParams.scala:14:20 ------------------------------------------------------- +14 | class E extends C[T = Int] // error: ']' expected, but `=` found // error + | ^ + | Not found: type T + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg/namedTypeParams.scala:15:20 ------------------------------------------------------- +15 | class F extends C[T = Int]() // error: ']' expected, but `=` found // error + | ^ + | Not found: type T + | + | longer explanation available when compiling with `-explain` +-- [E102] Syntax Error: tests/neg/namedTypeParams.scala:19:18 ---------------------------------------------------------- +19 | f[X = Int, String](1, "") // error // error + | ^ + | Type parameter String is undefined. Expected one of X, Y. +-- Error: tests/neg/namedTypeParams.scala:20:12 ------------------------------------------------------------------------ +20 | f[X = Int][X = Int][Y = String](1, "") // error: illegal repeated type application + | ^^^^^^^^^^^^^^^^^^^ + | illegal repeated type application + | You might have meant something like: + | Test.f[X = Int, X = Int] +-- Error: tests/neg/namedTypeParams.scala:22:12 ------------------------------------------------------------------------ +22 | f[X = Int][Y = String](1, "") // error: illegal repeated type application + | ^^^^^^^^^^^^^^^^^^^^^^ + | illegal repeated type application + | You might have meant something like: + | Test.f[X = Int, Y = String] +-- Error: tests/neg/namedTypeParams.scala:23:12 ------------------------------------------------------------------------ +23 | f[X = Int][String](1, "") // error: illegal repeated type application + | ^^^^^^^^^^^^^^^^^^ + | illegal repeated type application + | You might have meant something like: + | Test.f[X = Int, String] +-- Error: tests/neg/namedTypeParams.scala:25:15 ------------------------------------------------------------------------ +25 | f[Y = String][X = Int](1, "") // error: illegal repeated type application + | ^^^^^^^^^^^^^^^^^^^^^^ + | illegal repeated type application + | You might have meant something like: + | Test.f[Y = String, X = Int] +-- Error: tests/neg/namedTypeParams.scala:26:15 ------------------------------------------------------------------------ +26 | f[Y = String][Int](1, "") // error: illegal repeated type application + | ^^^^^^^^^^^^^^^^^^ + | illegal repeated type application + | You might have meant something like: + | Test.f[Y = String, Int] +-- [E102] Syntax Error: tests/neg/namedTypeParams.scala:32:9 ----------------------------------------------------------- +32 | f2[Y = String][X = Int](1, "") // error: Y is undefined + | ^^^^^^ + | Type parameter Y is undefined. Expected one of X. +-- [E102] Syntax Error: tests/neg/namedTypeParams.scala:33:9 ----------------------------------------------------------- +33 | f2[Y = String](1, "") // error: Y is undefined + | ^^^^^^ + | Type parameter Y is undefined. Expected one of X. diff --git a/tests/neg/namedTypeParams.scala b/tests/neg/namedTypeParams.scala index 1b85ee6349f4..489ac1e8cdb6 100644 --- a/tests/neg/namedTypeParams.scala +++ b/tests/neg/namedTypeParams.scala @@ -5,7 +5,7 @@ object Test0: def f[X, Y](x: X, y: Y): Int = ??? f[X = Int, Y = Int](1, 2) // error: experimental // error: experimental -object Test { +object Test: import language.experimental.namedTypeArguments val x: C[T = Int] = // error: ']' expected, but `=` found // error @@ -25,8 +25,9 @@ object Test { f[Y = String][X = Int](1, "") // error: illegal repeated type application f[Y = String][Int](1, "") // error: illegal repeated type application +object TestInterleaving: + import language.experimental.namedTypeArguments def f2[X](using DummyImplicit)[Y](x: X, y: Y): Int = ??? f2[Y = String][X = Int](1, "") // error: Y is undefined f2[Y = String](1, "") // error: Y is undefined -} diff --git a/tests/neg/overrides.scala b/tests/neg/overrides.scala index ee7a8b73ba0d..48dbd41ab839 100644 --- a/tests/neg/overrides.scala +++ b/tests/neg/overrides.scala @@ -53,7 +53,6 @@ class B extends A[Int] { override def b[T <: Int](x: Int)(y: String) = true // error override def next(): Int = ??? // error: incompatible type - } class C extends A[String] { @@ -115,4 +114,3 @@ class C extends A { override def m: Int = 42 // error: has incompatible type } } - diff --git a/tests/pos/interleaving-ba.scala b/tests/pos/interleaving-ba.scala index dc2b36202891..4a7d721c804e 100644 --- a/tests/pos/interleaving-ba.scala +++ b/tests/pos/interleaving-ba.scala @@ -1,5 +1,4 @@ - object BA { given String = "" given Double = 0 @@ -8,4 +7,4 @@ object BA { def test = ba(0)[String] -} \ No newline at end of file +} diff --git a/tests/pos/interleaving-chainedParams.scala b/tests/pos/interleaving-chainedParams.scala index 6ed970a69458..8613120f581a 100644 --- a/tests/pos/interleaving-chainedParams.scala +++ b/tests/pos/interleaving-chainedParams.scala @@ -1,18 +1,18 @@ object chainedParams{ - trait Chain{ - type Tail <: Chain - } + trait Chain{ + type Tail <: Chain + } + + def f[C1 <: Chain](c1: C1)[C2 <: c1.Tail](c2: C2)[C3 <: c2.Tail](c3: C3): c3.Tail = ??? - def f[C1 <: Chain](c1: C1)[C2 <: c1.Tail](c2: C2)[C3 <: c2.Tail](c3: C3): c3.Tail = ??? + val self = new Chain{ type Tail = this.type } + val res: self.type = f(self)(self)(self) - val self = new Chain{ type Tail = this.type } - val res: self.type = f(self)(self)(self) + type C <: Chain - type C <: Chain - - val c3 = new Chain{ type Tail = C } - val c2 = new Chain{ type Tail = c3.type } - val c1 = new Chain{ type Tail = c2.type } - val u: C = f(c1)(c2)(c3) + val c3 = new Chain{ type Tail = C } + val c2 = new Chain{ type Tail = c3.type } + val c1 = new Chain{ type Tail = c2.type } + val u: C = f(c1)(c2)(c3) } diff --git a/tests/pos/interleaving-functor.scala b/tests/pos/interleaving-functor.scala index 00c26151f5cb..c3df1f869850 100644 --- a/tests/pos/interleaving-functor.scala +++ b/tests/pos/interleaving-functor.scala @@ -1,7 +1,7 @@ -object functorInterweaving: +object functorInterleaving: //taken from https://dotty.epfl.ch/docs/reference/contextual/type-classes.html //at version 3.1.1-RC1-bin-20210930-01f040b-NIGHTLY - //modified to have type interveawing + //modified to have type interleaving trait Functor[F[_]]: def map[A](x: F[A])[B](f: A => B): F[B] diff --git a/tests/pos/interleaving-nameCollision.scala b/tests/pos/interleaving-nameCollision.scala deleted file mode 100644 index b66f63b9f92c..000000000000 --- a/tests/pos/interleaving-nameCollision.scala +++ /dev/null @@ -1,5 +0,0 @@ -import scala.annotation.targetName - -object nameCollision: - def f[T](x: T)[U](y: U) = (x,y) - @targetName("g") def f[T](x: T, y: T) = (x,y) \ No newline at end of file diff --git a/tests/pos/interleaving-overload.scala b/tests/pos/interleaving-overload.scala index 02074fd82914..e1c3db1abe37 100644 --- a/tests/pos/interleaving-overload.scala +++ b/tests/pos/interleaving-overload.scala @@ -16,4 +16,4 @@ class A{ f2[Any](1) f2[Any][Any](1) -} \ No newline at end of file +} diff --git a/tests/pos/interleaving-params.scala b/tests/pos/interleaving-params.scala index dec9f6780370..e86f856a728d 100644 --- a/tests/pos/interleaving-params.scala +++ b/tests/pos/interleaving-params.scala @@ -3,4 +3,4 @@ class Params{ def foo[T](x: T)[U >: x.type <: T](using U)[L <: List[U]](l: L): L = ??? def aaa(x: U): U = ??? def bbb[T <: U](x: U)[U]: U = ??? -} \ No newline at end of file +} diff --git a/tests/pos/interleaving-signatureCollision.scala b/tests/pos/interleaving-signatureCollision.scala new file mode 100644 index 000000000000..be016e7bdbfe --- /dev/null +++ b/tests/pos/interleaving-signatureCollision.scala @@ -0,0 +1,5 @@ +import scala.annotation.targetName + +object signatureCollision: + def f[T](x: T)[U](y: U) = (x,y) + @targetName("g") def f[T](x: T, y: T) = (x,y) diff --git a/tests/pos/interleaving-typeApply.scala b/tests/pos/interleaving-typeApply.scala index 5b0be806bf33..ba9486408adb 100644 --- a/tests/pos/interleaving-typeApply.scala +++ b/tests/pos/interleaving-typeApply.scala @@ -19,4 +19,4 @@ object typeApply: f5[Int][String][Unit] f6[Int]()[Unit] f7[Int]()[Unit] - } \ No newline at end of file + } diff --git a/tests/pos/namedTypeParams.scala b/tests/pos/namedTypeParams.scala index 86f35bf3c596..160bfc11f079 100644 --- a/tests/pos/namedTypeParams.scala +++ b/tests/pos/namedTypeParams.scala @@ -8,7 +8,6 @@ object Test { f[X = Int](1, "") f[Y = String](1, "") - def f2[X](using DummyImplicit)[Y](x: X, y: Y): Int = ??? f2[X = Int][Y = String](1, "") diff --git a/tests/run/interleaving.scala b/tests/run/interleaving.scala new file mode 100644 index 000000000000..e84f2896e5af --- /dev/null +++ b/tests/run/interleaving.scala @@ -0,0 +1,101 @@ +object Test extends App { + trait Key { type Value } + trait DB { + def getOrElse(k: Key)[V >: k.Value](default: V): V // dependent type parameter + } + + val key1 = new Key{ type Value = Some[Int] } + val key2 = new Key{ type Value = Some[Int] } + val key3 = new Key{ type Value = Some[String] } + + val db1: DB = new DB{ + def getOrElse(k: Key)[V >: k.Value](default: V): V = if k == key1 then Some(4).asInstanceOf[k.Value] else default + } + + // Interleaved method with dependent type bound + val default1: None.type = None + assert(db1.getOrElse(key1)[Option[Int]](default1) == Some(4)) + assert(db1.getOrElse(key2)[Option[Int]](default1) == default1) + assert(db1.getOrElse(key3)[Option[String]](default1) == default1) + assert(db1.getOrElse(key1)(default1) == Some(4)) + assert(db1.getOrElse(key2)(default1) == default1) + assert(db1.getOrElse(key3)(default1) == default1) + + val default2: Any = 3 + assert(db1.getOrElse(key1)[Any](default2) == Some(4)) + assert(db1.getOrElse(key2)[Any](default2) == default2) + assert(db1.getOrElse(key3)[Any](default2) == default2) + assert(db1.getOrElse(key1)(default2) == Some(4)) + assert(db1.getOrElse(key2)(default2) == default2) + assert(db1.getOrElse(key3)(default2) == default2) + + // Extension method and using parameter + extension (k: Key) + def lookupOrElse(using db: DB)[V >: k.Value](default: V): V = db.getOrElse(k)(default) + + object Block1: + given DB = db1 + + assert(key1.lookupOrElse[Option[Int]](default1) == Some(4)) + assert(key2.lookupOrElse[Option[Int]](default1) == default1) + assert(key3.lookupOrElse[Option[String]](default1) == default1) + assert(key1.lookupOrElse(default1) == Some(4)) + assert(key2.lookupOrElse(default1) == default1) + assert(key3.lookupOrElse(default1) == default1) + + assert(key1.lookupOrElse[Any](default2) == Some(4)) + assert(key2.lookupOrElse[Any](default2) == default2) + assert(key3.lookupOrElse[Any](default2) == default2) + assert(key1.lookupOrElse(default2) == Some(4)) + assert(key2.lookupOrElse(default2) == default2) + assert(key3.lookupOrElse(default2) == default2) + end Block1 + + // Right associative extension method + extension (db: DB) + def ?:(k: Key)[V >: k.Value](default: V): V = db.getOrElse(k)(default) + + assert((db1 ?: (key1))[Option[Int]](default1) == Some(4)) + assert((db1 ?: (key2))[Option[Int]](default1) == default1) + assert((db1 ?: (key3))[Option[String]](default1) == default1) + assert((db1 ?: (key1))(default1) == Some(4)) + assert((db1 ?: (key2))(default1) == default1) + assert((db1 ?: (key3))(default1) == default1) + + assert((db1 ?: (key1))[Any](default2) == Some(4)) + assert((db1 ?: (key2))[Any](default2) == default2) + assert((db1 ?: (key3))[Any](default2) == default2) + assert((db1 ?: (key1))(default2) == Some(4)) + assert((db1 ?: (key2))(default2) == default2) + assert((db1 ?: (key3))(default2) == default2) + + + assert(key1.?:(db1)[Option[Int]](default1) == Some(4)) + assert(key2.?:(db1)[Option[Int]](default1) == default1) + assert(key3.?:(db1)[Option[String]](default1) == default1) + assert(key1.?:(db1)(default1) == Some(4)) + assert(key2.?:(db1)(default1) == default1) + assert(key3.?:(db1)(default1) == default1) + + assert(key1.?:(db1)[Any](default2) == Some(4)) + assert(key2.?:(db1)[Any](default2) == default2) + assert(key3.?:(db1)[Any](default2) == default2) + assert(key1.?:(db1)(default2) == Some(4)) + assert(key2.?:(db1)(default2) == default2) + assert(key3.?:(db1)(default2) == default2) + + + assert(?:(key1)(db1)[Option[Int]](default1) == Some(4)) + assert(?:(key2)(db1)[Option[Int]](default1) == default1) + assert(?:(key3)(db1)[Option[String]](default1) == default1) + assert(?:(key1)(db1)(default1) == Some(4)) + assert(?:(key2)(db1)(default1) == default1) + assert(?:(key3)(db1)(default1) == default1) + + assert(?:(key1)(db1)[Any](default2) == Some(4)) + assert(?:(key2)(db1)[Any](default2) == default2) + assert(?:(key3)(db1)[Any](default2) == default2) + assert(?:(key1)(db1)(default2) == Some(4)) + assert(?:(key2)(db1)(default2) == default2) + assert(?:(key3)(db1)(default2) == default2) +} From 35ed4295b2bcf3afca6e7f7bc4163aac6a44f742 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Thu, 2 Feb 2023 15:35:57 +0100 Subject: [PATCH 15/19] Fix right-associative-extension-methods.md --- .../right-associative-extension-methods.md | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/docs/_docs/reference/contextual/right-associative-extension-methods.md b/docs/_docs/reference/contextual/right-associative-extension-methods.md index 068123df8cd2..9d711c42ad3d 100644 --- a/docs/_docs/reference/contextual/right-associative-extension-methods.md +++ b/docs/_docs/reference/contextual/right-associative-extension-methods.md @@ -4,46 +4,54 @@ title: "Right-Associative Extension Methods: Details" nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/right-associative-extension-methods.html --- -The most general form of leading parameters of an extension method is as follows: - +The most general signature an extension method can have is as follows: + - An optional type clause `leadingTyParamss` - A possibly empty list of using clauses `leadingUsing` - - A single parameter `extensionParam` + - A single parameter `leftParamss` - A possibly empty list of using clauses `trailingUsing` + - A name (preceded by the `def` keyword) + - An optional type clause `rightTyParamss` + - An optional explicit term clause `rightParamss` + - Any number of any clauses `rest` -This is then followed by `def`, the method name, and possibly further parameters -`otherParams`. An example is: +For example: ```scala - extension (using a: A, b: B)(using c: C) // <-- leadingUsing - (x: X) // <-- extensionParam + extension [T] // <-- leadingTyParamss + (using a: A, b: B)(using c: C) // <-- leadingUsing + (x: X) // <-- leftParamss (using d: D) // <-- trailingUsing - def +:: (y: Y)(using e: E)(z: Z) // <-- otherParams + def +:: [U] // <-- rightTyParamss + (y: Y) // <-- rightParamss + [V](using e: E)[W](z: Z) // <-- rest ``` + An extension method is treated as a right-associative operator (as in [SLS §6.12.3](https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#infix-operations)) -if it has a name ending in `:` and is immediately followed by a -single parameter. In the example above, that parameter is `(y: Y)`. +if it has a name ending in `:`, and is immediately followed by a +single explicit term parameter (in other words, `rightParamss` is present). In the example above, that parameter is `(y: Y)`. The Scala compiler pre-processes a right-associative infix operation such as `x +: xs` to `xs.+:(x)` if `x` is a pure expression or a call-by-name parameter and to `val y = x; xs.+:(y)` otherwise. This is necessary since a regular right-associative infix method is defined in the class of its right operand. To make up for this swap, -the expansion of right-associative extension methods performs an analogous parameter swap. More precisely, if `otherParams` consists of a single parameter -`rightParam` followed by `remaining`, the total parameter sequence +the expansion of right-associative extension methods performs the inverse parameter swap. More precisely, if `rightParamss` is present, the total parameter sequence of the extension method's expansion is: ``` - leadingUsing rightParam trailingUsing extensionParam remaining + leadingTyParamss leadingUsing rightTyParamss rightParamss leftParamss trailingUsing rest ``` For instance, the `+::` method above would become ```scala - def +:: (using a: A, b: B)(using c: C) + def +:: [T] + (using a: A, b: B)(using c: C) + [U] (y: Y) - (using d: D) (x: X) - (using e: E)(z: Z) + (using d: D) + [V](using e: E)[W](z: Z) ``` This expansion has to be kept in mind when writing right-associative extension From 4ffe1dad952b7615f00e7457e4e1f0b87e035159 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Thu, 2 Feb 2023 16:18:16 +0100 Subject: [PATCH 16/19] Uniformise naming of clauses in right associative extension methods --- .../src/dotty/tools/dotc/ast/Desugar.scala | 12 ++++---- .../tools/dotc/printing/RefinedPrinter.scala | 29 ++++++++++--------- .../right-associative-extension-methods.md | 26 ++++++++++------- .../scaladoc/tasty/ClassLikeSupport.scala | 15 +++++----- 4 files changed, 45 insertions(+), 37 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 5326361ada98..2cfd292fd6dd 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -915,16 +915,16 @@ object desugar { name = normalizeName(mdef, mdef.tpt).asTermName, paramss = if mdef.name.isRightAssocOperatorName then - val (typaramss, paramss) = mdef.paramss.span(isTypeParamClause) // first extract type parameters + val (rightTyParams, paramss) = mdef.paramss.span(isTypeParamClause) // first extract type parameters paramss match - case params :: paramss1 => // `params` must have a single parameter and without `given` flag + case rightParam :: paramss1 => // `rightParam` must have a single parameter and without `given` flag def badRightAssoc(problem: String) = report.error(em"right-associative extension method $problem", mdef.srcPos) extParamss ++ mdef.paramss - params match + rightParam match case ValDefs(vparam :: Nil) => if !vparam.mods.is(Given) then // we merge the extension parameters with the method parameters, @@ -934,8 +934,10 @@ object desugar { // def %:[E](f: F)(g: G)(using H): Res = ??? // will be encoded as // def %:[A](using B)[E](f: F)(c: C)(using D)(g: G)(using H): Res = ??? - val (leadingUsing, otherExtParamss) = extParamss.span(isUsingOrTypeParamClause) - leadingUsing ::: typaramss ::: params :: otherExtParamss ::: paramss1 + // + // If you change the names of the clauses below, also change them in right-associative-extension-methods.md + val (leftTyParamsAndLeadingUsing, leftParamAndTrailingUsing) = extParamss.span(isUsingOrTypeParamClause) + leftTyParamsAndLeadingUsing ::: rightTyParams ::: rightParam :: leftParamAndTrailingUsing ::: paramss1 else badRightAssoc("cannot start with using clause") case _ => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index f94ff68d0698..6f258fdddbe9 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -896,30 +896,31 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if isExtension then val paramss = if tree.name.isRightAssocOperatorName then + // If you change the names of the clauses below, also change them in right-associative-extension-methods.md // we have the following encoding of tree.paramss: - // (leadingTyParamss ++ leadingUsing - // ++ rightTyParamss ++ rightParamss - // ++ leftParamss ++ trailingUsing ++ rest) + // (leftTyParams ++ leadingUsing + // ++ rightTyParams ++ rightParam + // ++ leftParam ++ trailingUsing ++ rest) // e.g. // extension [A](using B)(c: C)(using D) // def %:[E](f: F)(g: G)(using H): Res = ??? // will have the following values: - // - leadingTyParamss = List(`[A]`) + // - leftTyParams = List(`[A]`) // - leadingUsing = List(`(using B)`) - // - rightTyParamss = List(`[E]`) - // - rightParamss = List(`(f: F)`) - // - leftParamss = List(`(c: C)`) + // - rightTyParams = List(`[E]`) + // - rightParam = List(`(f: F)`) + // - leftParam = List(`(c: C)`) // - trailingUsing = List(`(using D)`) // - rest = List(`(g: G)`, `(using H)`) - // we need to swap (rightTyParams ++ rightParamss) with (leftParamss ++ trailingUsing) - val (leadingTyParamss, rest1) = tree.paramss.span(isTypeParamClause) + // we need to swap (rightTyParams ++ rightParam) with (leftParam ++ trailingUsing) + val (leftTyParams, rest1) = tree.paramss.span(isTypeParamClause) val (leadingUsing, rest2) = rest1.span(isUsingClause) - val (rightTyParamss, rest3) = rest2.span(isTypeParamClause) - val (rightParamss, rest4) = rest3.splitAt(1) - val (leftParamss, rest5) = rest4.splitAt(1) + val (rightTyParams, rest3) = rest2.span(isTypeParamClause) + val (rightParam, rest4) = rest3.splitAt(1) + val (leftParam, rest5) = rest4.splitAt(1) val (trailingUsing, rest6) = rest5.span(isUsingClause) - if leftParamss.nonEmpty then - leadingTyParamss ::: leadingUsing ::: leftParamss ::: trailingUsing ::: rightTyParamss ::: rightParamss ::: rest6 + if leftParam.nonEmpty then + leftTyParams ::: leadingUsing ::: leftParam ::: trailingUsing ::: rightTyParams ::: rightParam ::: rest6 else tree.paramss // it wasn't a binary operator, after all. else diff --git a/docs/_docs/reference/contextual/right-associative-extension-methods.md b/docs/_docs/reference/contextual/right-associative-extension-methods.md index 9d711c42ad3d..1b878ca2db22 100644 --- a/docs/_docs/reference/contextual/right-associative-extension-methods.md +++ b/docs/_docs/reference/contextual/right-associative-extension-methods.md @@ -4,25 +4,27 @@ title: "Right-Associative Extension Methods: Details" nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/right-associative-extension-methods.html --- + + The most general signature an extension method can have is as follows: - - An optional type clause `leadingTyParamss` + - An optional type clause `leftTyParams` - A possibly empty list of using clauses `leadingUsing` - - A single parameter `leftParamss` + - A single parameter `leftParam` (in an explicit term clause) - A possibly empty list of using clauses `trailingUsing` - A name (preceded by the `def` keyword) - - An optional type clause `rightTyParamss` - - An optional explicit term clause `rightParamss` + - An optional type clause `rightTyParams` + - An optional single parameter `rightParam` (in an explicit term clause) - Any number of any clauses `rest` For example: ```scala - extension [T] // <-- leadingTyParamss + extension [T] // <-- leftTyParams (using a: A, b: B)(using c: C) // <-- leadingUsing - (x: X) // <-- leftParamss + (x: X) // <-- leftParam (using d: D) // <-- trailingUsing - def +:: [U] // <-- rightTyParamss - (y: Y) // <-- rightParamss + def +:: [U] // <-- rightTyParams + (y: Y) // <-- rightParam [V](using e: E)[W](z: Z) // <-- rest ``` @@ -30,18 +32,20 @@ For example: An extension method is treated as a right-associative operator (as in [SLS §6.12.3](https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#infix-operations)) if it has a name ending in `:`, and is immediately followed by a -single explicit term parameter (in other words, `rightParamss` is present). In the example above, that parameter is `(y: Y)`. +single explicit term parameter (in other words, `rightParam` is present). In the example above, that parameter is `(y: Y)`. The Scala compiler pre-processes a right-associative infix operation such as `x +: xs` to `xs.+:(x)` if `x` is a pure expression or a call-by-name parameter and to `val y = x; xs.+:(y)` otherwise. This is necessary since a regular right-associative infix method is defined in the class of its right operand. To make up for this swap, -the expansion of right-associative extension methods performs the inverse parameter swap. More precisely, if `rightParamss` is present, the total parameter sequence +the expansion of right-associative extension methods performs the inverse parameter swap. More precisely, if `rightParam` is present, the total parameter sequence of the extension method's expansion is: ``` - leadingTyParamss leadingUsing rightTyParamss rightParamss leftParamss trailingUsing rest + leftTyParams leadingUsing rightTyParams rightParam leftParam trailingUsing rest ``` +In other words, we swap `leftParams trailingUsing` with `rightTyParam rightParam`. + For instance, the `+::` method above would become ```scala diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index c7178acac312..0c85dff0879f 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -350,16 +350,17 @@ trait ClassLikeSupport: val unshuffledMemberInfoParamLists = if methodSymbol.isExtensionMethod && methodSymbol.isRightAssoc then // Taken from RefinedPrinter.scala - val (leadingTyParamss, rest1) = memberInfo.paramLists.span(_.isType) + // If you change the names of the clauses below, also change them in right-associative-extension-methods.md + val (leftTyParams, rest1) = memberInfo.paramLists.span(_.isType) val (leadingUsing, rest2) = rest1.span(_.isUsing) - val (rightTyParamss, rest3) = rest2.span(_.isType) - val (rightParamss, rest4) = rest3.splitAt(1) - val (leftParamss, rest5) = rest4.splitAt(1) + val (rightTyParams, rest3) = rest2.span(_.isType) + val (rightParam, rest4) = rest3.splitAt(1) + val (leftParam, rest5) = rest4.splitAt(1) val (trailingUsing, rest6) = rest5.span(_.isUsing) - if leftParamss.nonEmpty then - // leadingTyParamss ::: leadingUsing ::: leftParamss ::: trailingUsing ::: rightTyParamss ::: rightParamss ::: rest6 + if leftParam.nonEmpty then + // leftTyParams ::: leadingUsing ::: leftParam ::: trailingUsing ::: rightTyParams ::: rightParam ::: rest6 // because of takeRight after, this is equivalent to the following: - rightTyParamss ::: rightParamss ::: rest6 + rightTyParams ::: rightParam ::: rest6 else memberInfo.paramLists // it wasn't a binary operator, after all. else From 6ace5b816a1845993b0f002067e65d98882667ee Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Thu, 25 Aug 2022 15:04:36 +0200 Subject: [PATCH 17/19] Make clause interleaving experimental --- compiler/src/dotty/tools/dotc/config/Feature.scala | 3 +++ compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 12 +++++++++++- .../right-associative-extension-methods.md | 4 ++-- .../generalized-method-syntax.md | 8 +++++++- docs/_docs/reference/syntax.md | 6 +----- .../src/scala/runtime/stdLibPatches/language.scala | 8 ++++++++ project/MiMaFilters.scala | 11 ++++++++--- 7 files changed, 40 insertions(+), 12 deletions(-) rename docs/_docs/reference/{other-new-features => experimental}/generalized-method-syntax.md (92%) diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index 188526bb094f..419ed5868cbf 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -28,6 +28,7 @@ object Feature: val symbolLiterals = deprecated("symbolLiterals") val fewerBraces = experimental("fewerBraces") val saferExceptions = experimental("saferExceptions") + val clauseInterleaving = experimental("clauseInterleaving") val pureFunctions = experimental("pureFunctions") val captureChecking = experimental("captureChecking") val into = experimental("into") @@ -76,6 +77,8 @@ object Feature: def namedTypeArgsEnabled(using Context) = enabled(namedTypeArguments) + def clauseInterleavingEnabled(using Context) = enabled(clauseInterleaving) + def genericNumberLiteralsEnabled(using Context) = enabled(genericNumberLiterals) def scala2ExperimentalMacroEnabled(using Context) = enabled(scala2macros) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index bac5d4e94d99..af4d0220162b 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3627,8 +3627,18 @@ object Parsers { val mods1 = addFlag(mods, Method) val ident = termIdent() var name = ident.name.asTermName - val paramss = typeOrTermParamClauses(ParamOwner.Def, numLeadParams = numLeadParams) + val paramss = + if in.featureEnabled(Feature.clauseInterleaving) then + // If you are making interleaving stable manually, please refer to the PR introducing it instead, section "How to make non-experimental" + typeOrTermParamClauses(ParamOwner.Def, numLeadParams = numLeadParams) + else + val tparams = typeParamClauseOpt(ParamOwner.Def) + val vparamss = termParamClauses(numLeadParams = numLeadParams) + + joinParams(tparams, vparamss) + var tpt = fromWithinReturnType { typedOpt() } + if (migrateTo3) newLineOptWhenFollowedBy(LBRACE) val rhs = if in.token == EQUALS then diff --git a/docs/_docs/reference/contextual/right-associative-extension-methods.md b/docs/_docs/reference/contextual/right-associative-extension-methods.md index 1b878ca2db22..61f0beece6ed 100644 --- a/docs/_docs/reference/contextual/right-associative-extension-methods.md +++ b/docs/_docs/reference/contextual/right-associative-extension-methods.md @@ -25,7 +25,7 @@ For example: (using d: D) // <-- trailingUsing def +:: [U] // <-- rightTyParams (y: Y) // <-- rightParam - [V](using e: E)[W](z: Z) // <-- rest + (using e: E)(z: Z) // <-- rest ``` @@ -55,7 +55,7 @@ For instance, the `+::` method above would become (y: Y) (x: X) (using d: D) - [V](using e: E)[W](z: Z) + (using e: E)(z: Z) ``` This expansion has to be kept in mind when writing right-associative extension diff --git a/docs/_docs/reference/other-new-features/generalized-method-syntax.md b/docs/_docs/reference/experimental/generalized-method-syntax.md similarity index 92% rename from docs/_docs/reference/other-new-features/generalized-method-syntax.md rename to docs/_docs/reference/experimental/generalized-method-syntax.md index c78a5df8a3f4..072052c1ae10 100644 --- a/docs/_docs/reference/other-new-features/generalized-method-syntax.md +++ b/docs/_docs/reference/experimental/generalized-method-syntax.md @@ -1,9 +1,15 @@ --- layout: doc-page title: "Generalized Method Syntax" -movedTo: https://docs.scala-lang.org/scala3/reference/other-new-features/generalized-method-syntax.html +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/generalized-method-syntax.html --- +This feature is not yet part of the Scala 3 language definition. It can be made available by a language import: + +```scala +import scala.language.experimental.clauseInterleaving +``` + The inclusion of using clauses is not the only way in which methods have been updated, type parameter clauses are now allowed in any number and at any position. ## Syntax Changes diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index dde70d2131ad..8da41c7e6d0c 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -350,10 +350,6 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param -DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent -DefParamClause ::= DefTypeParamClause - | DefTermParamClause - | UsingParamClause TypelessClauses ::= TypelessClause {TypelessClause} TypelessClause ::= DefTermParamClause | UsingParamClause @@ -417,7 +413,7 @@ Dcl ::= RefineDcl ValDcl ::= ids ‘:’ Type VarDcl ::= ids ‘:’ Type DefDcl ::= DefSig ‘:’ Type -DefSig ::= id [DefParamClauses] [DefImplicitClause] +DefSig ::= id [DefTypeParamClause] [TypelessClauses] [DefImplicitClause] TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds Def ::= ‘val’ PatDef diff --git a/library/src/scala/runtime/stdLibPatches/language.scala b/library/src/scala/runtime/stdLibPatches/language.scala index 401926dbab4d..d92495c6f5aa 100644 --- a/library/src/scala/runtime/stdLibPatches/language.scala +++ b/library/src/scala/runtime/stdLibPatches/language.scala @@ -61,6 +61,14 @@ object language: @compileTimeOnly("`saferExceptions` can only be used at compile time in import statements") object saferExceptions + /** Adds support for clause interleaving: + * Methods can now have as many type clauses as they like, this allows to have type bounds depend on terms: `def f(x: Int)[A <: x.type]: A` + * + * @see [[http://dotty.epfl.ch/docs/reference/other-new-features/explicit-nulls.html]] + */ + @compileTimeOnly("`clauseInterleaving` can only be used at compile time in import statements") + object clauseInterleaving + /** Experimental support for pure function type syntax * * @see [[https://dotty.epfl.ch/docs/reference/experimental/purefuns]] diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 63ce926355bb..0937777595ca 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -12,12 +12,17 @@ object MiMaFilters { ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language.3.3"), ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$3$u002E3$"), ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$3$u002E3$minusmigration$"), - ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.into"), - ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$into$"), ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary"), ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$"), ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$Break"), - ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$Label") + ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$Label"), + + // New experimental features in 3.3.X + ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.clauseInterleaving"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$clauseInterleaving$"), + ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.into"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$into$"), + // end of New experimental features in 3.3.X ) val TastyCore: Seq[ProblemFilter] = Seq( ProblemFilters.exclude[DirectMissingMethodProblem]("dotty.tools.tasty.TastyBuffer.reset"), From 1e402f3e44310dab4699fcbacbf6afc1eefb261c Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Thu, 25 Aug 2022 15:05:32 +0200 Subject: [PATCH 18/19] Add import to tests --- .../src/tests/extensionParams.scala | 5 +++ .../src/tests/methodsAndConstructors.scala | 3 ++ tests/neg/interleaving-ab.scala | 2 ++ tests/neg/interleaving-params.scala | 2 ++ .../neg/interleaving-signatureCollision.scala | 2 ++ tests/neg/interleaving-typeApply.check | 36 +++++++++---------- tests/neg/interleaving-typeApply.scala | 2 ++ tests/neg/interleaving-unmatched.scala | 2 ++ tests/neg/namedTypeParams.check | 8 ++--- tests/neg/namedTypeParams.scala | 1 + tests/neg/overrides.scala | 14 +++++--- tests/pos/interleaving-ba.scala | 1 + tests/pos/interleaving-chainedParams.scala | 2 ++ tests/pos/interleaving-classless.scala | 2 ++ tests/pos/interleaving-functor.scala | 2 ++ tests/pos/interleaving-newline.scala | 2 ++ tests/pos/interleaving-overload.scala | 1 + tests/pos/interleaving-params.scala | 2 ++ .../pos/interleaving-signatureCollision.scala | 1 + tests/pos/interleaving-typeApply.scala | 3 ++ tests/pos/namedTypeParams.scala | 4 +++ tests/pos/overrides.scala | 7 +++- tests/run/interleaving.scala | 1 + 23 files changed, 78 insertions(+), 27 deletions(-) diff --git a/scaladoc-testcases/src/tests/extensionParams.scala b/scaladoc-testcases/src/tests/extensionParams.scala index e064ff6d56d7..9276bf41f067 100644 --- a/scaladoc-testcases/src/tests/extensionParams.scala +++ b/scaladoc-testcases/src/tests/extensionParams.scala @@ -52,7 +52,12 @@ extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Num extension (using String)(using Unit)(a: Animal)(using Int)(using Number) def f11(b: Any)(c: Any): Any = ??? + +import scala.language.experimental.clauseInterleaving + +extension (using String)(using Unit)(a: Animal)(using Int)(using Number) def f13(b: Any)[T](c: T): T = ??? def f14[D](b: D)[T](c: T): T = ??? + diff --git a/scaladoc-testcases/src/tests/methodsAndConstructors.scala b/scaladoc-testcases/src/tests/methodsAndConstructors.scala index 5fc6af38888b..132d35035b30 100644 --- a/scaladoc-testcases/src/tests/methodsAndConstructors.scala +++ b/scaladoc-testcases/src/tests/methodsAndConstructors.scala @@ -60,5 +60,8 @@ class Methods: def withImplicitParam2(v: String)(implicit ab: Double, a: Int, b: String): String = ??? + import scala.language.experimental.clauseInterleaving + def clauseInterleaving[T](x: T)[U](y: U)(using (T, U)): (T, U) = ??? + diff --git a/tests/neg/interleaving-ab.scala b/tests/neg/interleaving-ab.scala index 5516d9b9dd51..e446626a2982 100644 --- a/tests/neg/interleaving-ab.scala +++ b/tests/neg/interleaving-ab.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object Ab: given String = "" given Double = 0 diff --git a/tests/neg/interleaving-params.scala b/tests/neg/interleaving-params.scala index 97de1e6e2948..dc6762cf0214 100644 --- a/tests/neg/interleaving-params.scala +++ b/tests/neg/interleaving-params.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + class Params{ def bar[T](x: T)[T]: String = ??? // error def zoo(x: Int)[T, U](x: U): T = ??? // error diff --git a/tests/neg/interleaving-signatureCollision.scala b/tests/neg/interleaving-signatureCollision.scala index d2df384dc1e7..a6a729ed3b62 100644 --- a/tests/neg/interleaving-signatureCollision.scala +++ b/tests/neg/interleaving-signatureCollision.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object signatureCollision: def f[T](x: T)[U](y: U) = (x,y) def f[T](x: T, y: T) = (x,y) // error diff --git a/tests/neg/interleaving-typeApply.check b/tests/neg/interleaving-typeApply.check index c932a899dfe6..a50c1455bfbb 100644 --- a/tests/neg/interleaving-typeApply.check +++ b/tests/neg/interleaving-typeApply.check @@ -1,29 +1,29 @@ --- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:8:11 --------------------------------------------- -8 | f3[String]() // error - | ^ - | Type argument String does not conform to upper bound Int - | - | longer explanation available when compiling with `-explain` --- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:9:16 --------------------------------------------- -9 | f5[Int][Unit] // error - | ^ - | Type argument Unit does not conform to upper bound String - | - | longer explanation available when compiling with `-explain` --- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:10:19 -------------------------------------------- -10 | f5[String][Unit] // error // error +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:10:11 -------------------------------------------- +10 | f3[String]() // error + | ^ + | Type argument String does not conform to upper bound Int + | + | longer explanation available when compiling with `-explain` +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:11:16 -------------------------------------------- +11 | f5[Int][Unit] // error + | ^ + | Type argument Unit does not conform to upper bound String + | + | longer explanation available when compiling with `-explain` +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:12:19 -------------------------------------------- +12 | f5[String][Unit] // error // error | ^ | Type argument Unit does not conform to upper bound String | | longer explanation available when compiling with `-explain` --- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:10:11 -------------------------------------------- -10 | f5[String][Unit] // error // error +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:12:11 -------------------------------------------- +12 | f5[String][Unit] // error // error | ^ | Type argument String does not conform to upper bound Int | | longer explanation available when compiling with `-explain` --- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:11:11 -------------------------------------------- -11 | f7[String]()[Unit] // error +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:13:11 -------------------------------------------- +13 | f7[String]()[Unit] // error | ^ | Type argument String does not conform to upper bound Int | diff --git a/tests/neg/interleaving-typeApply.scala b/tests/neg/interleaving-typeApply.scala index 18a21b4984e1..ad21fe2f0329 100644 --- a/tests/neg/interleaving-typeApply.scala +++ b/tests/neg/interleaving-typeApply.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object typeApply: def f3[T <: Int](using DummyImplicit)[U <: String](): T => T = ??? diff --git a/tests/neg/interleaving-unmatched.scala b/tests/neg/interleaving-unmatched.scala index c433c0a7210a..2ce3074d07fa 100644 --- a/tests/neg/interleaving-unmatched.scala +++ b/tests/neg/interleaving-unmatched.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object unmatched: def f1[T (x: T)] = ??? // error def f2(x: Any[)T] = ??? // error // error diff --git a/tests/neg/namedTypeParams.check b/tests/neg/namedTypeParams.check index 1252f29c4e9e..3f6f9f7913e8 100644 --- a/tests/neg/namedTypeParams.check +++ b/tests/neg/namedTypeParams.check @@ -92,11 +92,11 @@ | illegal repeated type application | You might have meant something like: | Test.f[Y = String, Int] --- [E102] Syntax Error: tests/neg/namedTypeParams.scala:32:9 ----------------------------------------------------------- -32 | f2[Y = String][X = Int](1, "") // error: Y is undefined +-- [E102] Syntax Error: tests/neg/namedTypeParams.scala:33:9 ----------------------------------------------------------- +33 | f2[Y = String][X = Int](1, "") // error: Y is undefined | ^^^^^^ | Type parameter Y is undefined. Expected one of X. --- [E102] Syntax Error: tests/neg/namedTypeParams.scala:33:9 ----------------------------------------------------------- -33 | f2[Y = String](1, "") // error: Y is undefined +-- [E102] Syntax Error: tests/neg/namedTypeParams.scala:34:9 ----------------------------------------------------------- +34 | f2[Y = String](1, "") // error: Y is undefined | ^^^^^^ | Type parameter Y is undefined. Expected one of X. diff --git a/tests/neg/namedTypeParams.scala b/tests/neg/namedTypeParams.scala index 489ac1e8cdb6..53ef14188e12 100644 --- a/tests/neg/namedTypeParams.scala +++ b/tests/neg/namedTypeParams.scala @@ -27,6 +27,7 @@ object Test: object TestInterleaving: import language.experimental.namedTypeArguments + import language.experimental.clauseInterleaving def f2[X](using DummyImplicit)[Y](x: X, y: Y): Int = ??? f2[Y = String][X = Int](1, "") // error: Y is undefined diff --git a/tests/neg/overrides.scala b/tests/neg/overrides.scala index 48dbd41ab839..c8fc8de97f7c 100644 --- a/tests/neg/overrides.scala +++ b/tests/neg/overrides.scala @@ -39,10 +39,12 @@ package p2 { // all being in the same package compiles fine class A[T] { def f(x: T)(y: T = x) = y - def b[U <: T](x: Int)[V >: T](y: String) = false def next: T = ??? + import scala.language.experimental.clauseInterleaving + + def b[U <: T](x: Int)[V >: T](y: String) = false } class B extends A[Int] { @@ -50,19 +52,23 @@ class B extends A[Int] { def f(x: Int)(y: Int) = y // error: needs `override' modifier f(2)() - override def b[T <: Int](x: Int)(y: String) = true // error override def next(): Int = ??? // error: incompatible type + + import scala.language.experimental.clauseInterleaving + + override def b[T <: Int](x: Int)(y: String) = true // error } class C extends A[String] { override def f(x: String) = x // error - override def b[T <: String](x: Int)[U >: Int](y: String) = true // error: incompatible type - override def next: Int = ??? // error: incompatible type + import scala.language.experimental.clauseInterleaving + + override def b[T <: String](x: Int)[U >: Int](y: String) = true // error: incompatible type } class X { diff --git a/tests/pos/interleaving-ba.scala b/tests/pos/interleaving-ba.scala index 4a7d721c804e..69fe2d9537a0 100644 --- a/tests/pos/interleaving-ba.scala +++ b/tests/pos/interleaving-ba.scala @@ -1,3 +1,4 @@ +import scala.language.experimental.clauseInterleaving object BA { given String = "" diff --git a/tests/pos/interleaving-chainedParams.scala b/tests/pos/interleaving-chainedParams.scala index 8613120f581a..e502888d97c8 100644 --- a/tests/pos/interleaving-chainedParams.scala +++ b/tests/pos/interleaving-chainedParams.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object chainedParams{ trait Chain{ diff --git a/tests/pos/interleaving-classless.scala b/tests/pos/interleaving-classless.scala index 924202673029..5aec92db3409 100644 --- a/tests/pos/interleaving-classless.scala +++ b/tests/pos/interleaving-classless.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + def f1[T]()[U](x: T, y: U): (T, U) = (x, y) def f2[T](x: T)[U](y: U): (T, U) = (x, y) def f3[T, U](using DummyImplicit)[V](x: T): U = ??? diff --git a/tests/pos/interleaving-functor.scala b/tests/pos/interleaving-functor.scala index c3df1f869850..35bed59f77f0 100644 --- a/tests/pos/interleaving-functor.scala +++ b/tests/pos/interleaving-functor.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object functorInterleaving: //taken from https://dotty.epfl.ch/docs/reference/contextual/type-classes.html //at version 3.1.1-RC1-bin-20210930-01f040b-NIGHTLY diff --git a/tests/pos/interleaving-newline.scala b/tests/pos/interleaving-newline.scala index dbd30bca4ef7..de8fb98a2f81 100644 --- a/tests/pos/interleaving-newline.scala +++ b/tests/pos/interleaving-newline.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object newline { def multipleLines [T] diff --git a/tests/pos/interleaving-overload.scala b/tests/pos/interleaving-overload.scala index e1c3db1abe37..1902551f9036 100644 --- a/tests/pos/interleaving-overload.scala +++ b/tests/pos/interleaving-overload.scala @@ -1,3 +1,4 @@ +import scala.language.experimental.clauseInterleaving class A{ diff --git a/tests/pos/interleaving-params.scala b/tests/pos/interleaving-params.scala index e86f856a728d..36963ff2e123 100644 --- a/tests/pos/interleaving-params.scala +++ b/tests/pos/interleaving-params.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + class Params{ type U def foo[T](x: T)[U >: x.type <: T](using U)[L <: List[U]](l: L): L = ??? diff --git a/tests/pos/interleaving-signatureCollision.scala b/tests/pos/interleaving-signatureCollision.scala index be016e7bdbfe..77190284ae6d 100644 --- a/tests/pos/interleaving-signatureCollision.scala +++ b/tests/pos/interleaving-signatureCollision.scala @@ -1,3 +1,4 @@ +import scala.language.experimental.clauseInterleaving import scala.annotation.targetName object signatureCollision: diff --git a/tests/pos/interleaving-typeApply.scala b/tests/pos/interleaving-typeApply.scala index ba9486408adb..3c669cc76bfc 100644 --- a/tests/pos/interleaving-typeApply.scala +++ b/tests/pos/interleaving-typeApply.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object typeApply: def f0[T]: [U] => T => T = ??? @@ -10,6 +12,7 @@ object typeApply: def f7[T <: Int](using DummyImplicit)[U <: String]()[X <: Unit]: X => X = ??? @main def test = { + import scala.language.experimental.namedTypeArguments f0[Int][String] f1[Int][String] f2[Int][String]() diff --git a/tests/pos/namedTypeParams.scala b/tests/pos/namedTypeParams.scala index 160bfc11f079..388bcfa98bef 100644 --- a/tests/pos/namedTypeParams.scala +++ b/tests/pos/namedTypeParams.scala @@ -1,4 +1,5 @@ import language.experimental.namedTypeArguments + object Test { def f[X, Y](x: X, y: Y): Int = ??? @@ -7,7 +8,10 @@ object Test { f[X = Int, Y = String](1, "") f[X = Int](1, "") f[Y = String](1, "") +} +object TestInterleaving{ + import language.experimental.clauseInterleaving def f2[X](using DummyImplicit)[Y](x: X, y: Y): Int = ??? f2[X = Int][Y = String](1, "") diff --git a/tests/pos/overrides.scala b/tests/pos/overrides.scala index 0f5d12367ca2..146dc06c76a9 100644 --- a/tests/pos/overrides.scala +++ b/tests/pos/overrides.scala @@ -1,16 +1,21 @@ class A[T] { def f(x: T)(y: T = x) = y + + import scala.language.experimental.clauseInterleaving + def b[U <: T](x: Int)[V >: T](y: String) = false } - class B extends A[Int] { override def f(x: Int)(y: Int) = y f(2)() + + import scala.language.experimental.clauseInterleaving + override def b[T <: Int](x: Int)[U >: Int](y: String) = true } diff --git a/tests/run/interleaving.scala b/tests/run/interleaving.scala index e84f2896e5af..557741032e8a 100644 --- a/tests/run/interleaving.scala +++ b/tests/run/interleaving.scala @@ -1,4 +1,5 @@ object Test extends App { + import scala.language.experimental.clauseInterleaving trait Key { type Value } trait DB { def getOrElse(k: Key)[V >: k.Value](default: V): V // dependent type parameter From 65091c31a625d9abb1d12942e2f870be8a8ae094 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 3 Jan 2023 14:21:44 +0100 Subject: [PATCH 19/19] Remove unused methods Not fixup'ed with another commit to keep a trace of the original version of these methods. --- .../dotty/tools/dotc/parsing/Parsers.scala | 31 ++----------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index af4d0220162b..479ae1fa9095 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3079,35 +3079,13 @@ object Parsers { /* -------- PARAMETERS ------------------------------------------- */ - /** DefParamClause ::= DefTypeParamClause - * | DefTermParamClause - * | UsingParamClause - */ - def typeOrTermParamClause( - ownerKind: ParamOwner, - nparams: Int, // number of parameters preceding this clause - ofClass: Boolean = false, // owner is a class - ofCaseClass: Boolean = false, // owner is a case class - prefix: Boolean = false, // clause precedes name of an extension method - givenOnly: Boolean = false, // only given parameters allowed - firstClause: Boolean = false // clause is the first in regular list of clauses - ): List[TypeDef] | List[ValDef] = - if (in.token == LPAREN) - termParamClause(nparams, ofClass, ofCaseClass, prefix, givenOnly, firstClause) - else if (in.token == LBRACKET) - typeParamClause(ownerKind) - else - Nil - - end typeOrTermParamClause - /** DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent + * DefParamClause ::= DefTypeParamClause + * | DefTermParamClause + * | UsingParamClause */ def typeOrTermParamClauses( ownerKind: ParamOwner, - ofClass: Boolean = false, - ofCaseClass: Boolean = false, - givenOnly: Boolean = false, numLeadParams: Int = 0 ): List[List[TypeDef] | List[ValDef]] = @@ -3118,9 +3096,6 @@ object Parsers { val paramsStart = in.offset val params = termParamClause( numLeadParams, - ofClass = ofClass, - ofCaseClass = ofCaseClass, - givenOnly = givenOnly, firstClause = firstClause) val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit) params :: (