diff --git a/TypesEverywhere.scala b/TypesEverywhere.scala new file mode 100644 index 000000000000..ad39b0166780 --- /dev/null +++ b/TypesEverywhere.scala @@ -0,0 +1,13 @@ +import scala.annotation.targetName +class TypesEverywhere{ + def f1[T](x: T): T = x + def f2[T][U](x: T, y: U): (T, U) = (x, y) + def f3[T](x: T)[U <: x.type](y: U): (T, U) = (x, y) + + + + def f4[T](x: T)(y: T) = (x,y) + @targetName("f5") def f4[T](x: T, y: T) = (x,y) +} + + diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 27c1dead4482..96ff73059da6 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2890,6 +2890,55 @@ object Parsers { /* -------- PARAMETERS ------------------------------------------- */ + 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.Value + ): 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 + + def typeOrTermParamClauses( + ownerKind: ParamOwner.Value, + 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) //I have doubts this works //TODO: test this + 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 @@ -3348,8 +3397,9 @@ 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) + //val tparams = typeParamClauseOpt(ParamOwner.Def) + //val vparamss = paramClauses(numLeadParams = numLeadParams) var tpt = fromWithinReturnType { typedOpt() } if (migrateTo3) newLineOptWhenFollowedBy(LBRACE) val rhs = @@ -3367,7 +3417,8 @@ object Parsers { accept(EQUALS) expr() - val ddef = DefDef(name, joinParams(tparams, vparamss), tpt, rhs) + //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) } @@ -3396,7 +3447,7 @@ object Parsers { argumentExprss(mkApply(Ident(nme.CONSTRUCTOR), argumentExprs())) } - /** TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds [‘=’ Type] + /** TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds [‘=’ Type] //TODO: change to {ParamClauses} ? */ def typeDefOrDcl(start: Offset, mods: Modifiers): Tree = { newLinesOpt() @@ -3489,7 +3540,7 @@ object Parsers { val tparams = typeParamClauseOpt(ParamOwner.Class) val cmods = fromWithinClassConstr(constrModsOpt()) val vparamss = paramClauses(ofClass = true, ofCaseClass = isCaseClass) - makeConstructor(tparams, vparamss).withMods(cmods) + makeConstructor(tparams, vparamss).withMods(cmods) //TODO: Reafactor to take List[List[ValDef] | List[TypeDef]] } /** ConstrMods ::= {Annotation} [AccessModifier] @@ -3578,7 +3629,7 @@ object Parsers { } /** GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance) - * GivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘:’ + * GivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘:’ //TODO: Change to {Params} */ def givenDef(start: Offset, mods: Modifiers, givenMod: Mod) = atSpan(start, nameStart) { var mods1 = addMod(mods, givenMod) @@ -3590,7 +3641,7 @@ object Parsers { newLineOpt() val vparamss = if in.token == LPAREN && in.lookahead.isIdent(nme.using) - then paramClauses(givenOnly = true) + then paramClauses(givenOnly = true) else Nil newLinesOpt() val noParams = tparams.isEmpty && vparamss.isEmpty @@ -3626,7 +3677,7 @@ object Parsers { } /** Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} ‘(’ DefParam ‘)’ - * {UsingParamClause} ExtMethods + * {UsingParamClause} ExtMethods //TODO: Change to {Params} ? */ def extension(): ExtMethods = val start = in.skipToken() diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index ab7187f6f4d4..84f8501273c3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1085,8 +1085,8 @@ 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, "illegal repeated type application") + /* case _: TypeApply if !ctx.isAfterTyper => //TODO: assess removing this is okay + errorTree(tree, "illegal repeated type application") */ case typedFn => typedFn.tpe.widen match { case pt: PolyType => diff --git a/tests/neg/_TypeInterweaving/ab.scala b/tests/neg/_TypeInterweaving/ab.scala new file mode 100644 index 000000000000..fa4335e4b858 --- /dev/null +++ b/tests/neg/_TypeInterweaving/ab.scala @@ -0,0 +1,8 @@ + +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/_TypeInterweaving/nameCollision.scala b/tests/neg/_TypeInterweaving/nameCollision.scala new file mode 100644 index 000000000000..c85bca1d9c78 --- /dev/null +++ b/tests/neg/_TypeInterweaving/nameCollision.scala @@ -0,0 +1,2 @@ +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/_TypeInterweaving/params.scala b/tests/neg/_TypeInterweaving/params.scala new file mode 100644 index 000000000000..4ad3a8e2a255 --- /dev/null +++ b/tests/neg/_TypeInterweaving/params.scala @@ -0,0 +1,5 @@ +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 +} \ No newline at end of file diff --git a/tests/neg/_TypeInterweaving/unmatched1.scala b/tests/neg/_TypeInterweaving/unmatched1.scala new file mode 100644 index 000000000000..a7fa95595a72 --- /dev/null +++ b/tests/neg/_TypeInterweaving/unmatched1.scala @@ -0,0 +1,2 @@ + +def f1[T (x: T)] = ??? // error diff --git a/tests/neg/_TypeInterweaving/unmatched2.scala b/tests/neg/_TypeInterweaving/unmatched2.scala new file mode 100644 index 000000000000..ca47f7ffbac0 --- /dev/null +++ b/tests/neg/_TypeInterweaving/unmatched2.scala @@ -0,0 +1,5 @@ + + + + +def f2[T(x: T) = ??? // error diff --git a/tests/neg/_TypeInterweaving/unmatched3.scala b/tests/neg/_TypeInterweaving/unmatched3.scala new file mode 100644 index 000000000000..4449ea0b7d48 --- /dev/null +++ b/tests/neg/_TypeInterweaving/unmatched3.scala @@ -0,0 +1,3 @@ + + +def f3(x: Any[)T] = ??? // error diff --git a/tests/pos/_typeInterweaving/ba.scala b/tests/pos/_typeInterweaving/ba.scala new file mode 100644 index 000000000000..58bffb42bcff --- /dev/null +++ b/tests/pos/_typeInterweaving/ba.scala @@ -0,0 +1,8 @@ + +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/_typeInterweaving/chainedParams.scala b/tests/pos/_typeInterweaving/chainedParams.scala new file mode 100644 index 000000000000..acbc0d259725 --- /dev/null +++ b/tests/pos/_typeInterweaving/chainedParams.scala @@ -0,0 +1,6 @@ + +class Chain{ + type Tail <: Chain +} + +def f[C1 <: Chain](c1: C1)[C2 <: c1.Tail](c2: C2)[C3 <: c2.Tail](c3: C3): c3.Tail = ??? \ No newline at end of file diff --git a/tests/pos/_typeInterweaving/class.scala b/tests/pos/_typeInterweaving/class.scala new file mode 100644 index 000000000000..84d0a4f3d712 --- /dev/null +++ b/tests/pos/_typeInterweaving/class.scala @@ -0,0 +1,6 @@ + +class C[T](x: T)[U](y: U){ + def pair: (T,U) = (x,y) + def first = x + def second = y +} \ No newline at end of file diff --git a/tests/pos/_typeInterweaving/classless.scala b/tests/pos/_typeInterweaving/classless.scala new file mode 100644 index 000000000000..48f1a8e4d788 --- /dev/null +++ b/tests/pos/_typeInterweaving/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) + +@main def test = f2(0)[String]("Hello") \ No newline at end of file diff --git a/tests/pos/_typeInterweaving/functorCurrying.scala b/tests/pos/_typeInterweaving/functorCurrying.scala new file mode 100644 index 000000000000..09867ca79009 --- /dev/null +++ b/tests/pos/_typeInterweaving/functorCurrying.scala @@ -0,0 +1,16 @@ +//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 test = + assertTransformation(List("a1", "b1"), List("a", "b"), elt => s"${elt}1") diff --git a/tests/pos/_typeInterweaving/functorInterweaving.scala b/tests/pos/_typeInterweaving/functorInterweaving.scala new file mode 100644 index 000000000000..b3daee73d4f7 --- /dev/null +++ b/tests/pos/_typeInterweaving/functorInterweaving.scala @@ -0,0 +1,16 @@ +//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 test = + assertTransformation(List("a", "b"))(List("a1", "b1")){elt => s"${elt}1"} diff --git a/tests/pos/_typeInterweaving/higherKindedReturn.scala b/tests/pos/_typeInterweaving/higherKindedReturn.scala new file mode 100644 index 000000000000..3b86cc7ea2d8 --- /dev/null +++ b/tests/pos/_typeInterweaving/higherKindedReturn.scala @@ -0,0 +1,21 @@ +def f_4[T]: [U] => T => T = ??? +def f_3[T][U]: T => T = ??? +def f_2[T][U](): T => T = ??? +def f_1[T <: Int][U <: String](): T => T = ??? +def f0[T <: Int][U <: String]: T => T = ??? +def f1[T <: Int][U <: String]: [X <: Unit] => X => X = ??? +def f2[T <: Int][U <: String](): [X <: Unit] => X => X = ??? +def f3[T <: Int][U <: String]()[X <: Unit]: X => X = ??? + +@main def test = { + f_4[Int][String] //only one that works when lines 1088 to 1089 of Applications.scala are uncommented + f_3[Int][String] + f_2[Int][String]() + f_1[Int][String]() + f0[Int][String] + f1[Int][String] + f1[Int][Unit] + f1[Int][String][Unit] + f2[Int]()[Unit] + f3[Int]()[Unit] +} \ No newline at end of file diff --git a/tests/pos/_typeInterweaving/nameCollision.scala b/tests/pos/_typeInterweaving/nameCollision.scala new file mode 100644 index 000000000000..3724d681b8e2 --- /dev/null +++ b/tests/pos/_typeInterweaving/nameCollision.scala @@ -0,0 +1,3 @@ +import scala.annotation.targetName +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/_typeInterweaving/newline.scala b/tests/pos/_typeInterweaving/newline.scala new file mode 100644 index 000000000000..baddb246fb1e --- /dev/null +++ b/tests/pos/_typeInterweaving/newline.scala @@ -0,0 +1,9 @@ +class Newline { + def multipleLines + [T] + (x: T) + [U] + (using (T,U)) + (y: U) + = ??? +} diff --git a/tests/pos/_typeInterweaving/overload.scala b/tests/pos/_typeInterweaving/overload.scala new file mode 100644 index 000000000000..567ff8b6d9d6 --- /dev/null +++ b/tests/pos/_typeInterweaving/overload.scala @@ -0,0 +1,25 @@ + +class A{ + /* + def f0[T](x: Any) = ??? + def f0[T](x: Int) = ??? + */ + + def f1[T][U](x: Any) = ??? + def f1[T][U](x: Int) = ??? + + //f0(1) + 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/_typeInterweaving/params.scala b/tests/pos/_typeInterweaving/params.scala new file mode 100644 index 000000000000..fb55d451b2bd --- /dev/null +++ b/tests/pos/_typeInterweaving/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/_typeInterweaving/todo.scala b/tests/pos/_typeInterweaving/todo.scala new file mode 100644 index 000000000000..102caa096b4a --- /dev/null +++ b/tests/pos/_typeInterweaving/todo.scala @@ -0,0 +1,157 @@ + + +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 foo[T, U][V](x: T): U = ??? + +@main def test = foo[Int] // should probably not work (and doesn't) + +// check ce qui se passe quand appelée +// def f3[T][U]: [X] => F[X] +// done: voir higherKindedReturn.scala + +// reflechir quoi changer dans la spe pour rendre le currying/interweaving dans la doc +// https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html +// +// Only need to change definitions, as applications can also be of that form, see higherKindedReturn.scala:11 +/* + * in https://www.scala-lang.org/files/archive/spec/2.13/04-basic-declarations-and-definitions.html#function-declarations-and-definitions + * Change: + * + * FunSig ::= id ParamClauses + * ParamClauses ::= ParamClause {ParamClause} + * ParamClause ::= TermParamClause | TypeParamClause | UsingParamClause + * TermParamClause ::= [nl] ‘(’ [TermParams] ‘)’ //note: allows () + * TypeParamClause ::= [nl] ‘[’ TypeParams ‘]’ + * UsingParamClause ::= [nl] ‘(’ ‘using’ TermParams ‘)’ + * + * (slightly simpler but should be equivalent to changes made in Parser.scla) + */ +// same for classes, je suis un peu confus par la syntaxe ci-dessous, notament Annotation et AccessModifiers, ils sont pas là en scala 3, non ? +// https://www.scala-lang.org/files/archive/spec/2.13/05-classes-and-objects.html#class-definitions +// +// change text to explain application notably when only passed one clause of type params when multiple type clauses expected + +// impl param de classe, déjà Parser: Done + + +//add function call to chainedParams + +/* Endroits à changer: (toujour creation et application ?) + function definition Done + function application Already works + class definition Standby + primary constr Standby + auxiliary constr Standby + class instantiation Standby (Might already work // see new) + given definition Todo //yes if given alias, Standby otherwise given name[](using ...)[](using ...) : Classe[Params] + given application(?) Already works ?/standby Doesn't exist ? or add multiple params to usings ? (using name: Class[T]{val x}[U]) + type declarations Standby: because no transformation from F[X,Y] <-> F[X][Y] //is [X,Y] =>> F[X][Y] valid ? + type instantiation Doesn't exist ? + extension definition Todo // probably only on right side, since classes do not keep info about params + extension application Todo: tester + */ + + + +// étapes pour "convaincre" +// 1) exemples simples, et exemples d'utilité TODO +// 2) expliquer intuitivement TODO +// 3) update la doc Done +// 4) PR et tout le tralala TODO + + +//potentiellement cette feature en experimental + +// eta expension: +val f = foo //should work and doesn't preserve type params yet + + + + + +//new: +// mettre de coté la partie sur les classes: Done +// fork le repo de la doc est faire changements appropriés: Done: https://github.com/scala/scala/pull/9792 +// check overloading resolution +// Applications.scala resolveMapped //What to do here? +// methType +// ProtoTypes.scala + +// check ne passer aucun arguments à une fonction // Je me rappelle pas ce que c'était ... +// voir fichier overload dans pos/interweaving_ // Done +// voir methType // Done ? +// check occurances de PolyType et PolyProto pour être sûr que ça fonctionne bien avec les [T][U] +// PolyType: Done in Application.scala +// PolyProto: Done in Application.scala +// testCompilation +// Pas eu le temps, mais la PR a lancer des tests, donc 5 qui ont ratés dans une phase + +// check def foo[T][T] +// +// is def foo[T](using T)(x: Int) valid ? +/* + * check: done see newline.scala + * def foo + * [T] + * (x: T) + * [U] + * + * and similar + * +*/ + + +// should we allow fun[L <: List[T]][T] as alias for something like fun[F[_] <: List[]][T][L <: F[T]] +// I'm not sure it's useful + + + +// dans docu check "method type" et "poly(morphic) type/method" pour voir si suppositions fausses +// + +// tryApply: +// si f pas fonction, essaie f.apply(...) + + + +// next: +// Relire toute docu: Done +// voir eta expension dans la doc, pour voir comment changer pour polymorphique, ajouter texte qui explique que passe parfois param de type si besoin +// mettre un peu au propre texte PR -> trouver exemples +// essayer given def: Paused + + +/** + * A few comments: + Should handle and test for extension definitions as well. + Should test of overriding and signature check and proper report when signature does not match. + I suggest opening a thread on https://contributors.scala-lang.org/ to discuss this language change. + * + * + * + * + */ + + +// type Z = X =>> F[X] +// def name: [X] => (X, X) => (U) + + +// def foo[T](x: T): T +// val bar = foo + + +// check overload: (should fail) +// def foo(x: Int)(y: String) +// def foo(x: Int)[T](y: String) +// def foo[T](x: Int)(y: String) + + + +// next +// adaptations faitent dans Typer.scala adapt (regarder la fin ou il y a le "vrai" body de la fonction) +// pour rajouter le cas valId3 qui typecheck (les autres cas ce sera pour après) +// https://github.com/lampepfl/dotty/pull/4672