Skip to content

Commit

Permalink
Parse +_ and -_ in types as identifiers to support Scala 3.2 plac…
Browse files Browse the repository at this point in the history
…eholder syntax

This change allows `kind-projector` plugin to rewrite `+_` and `-_` tokens to type lambdas,
in line with proposed syntax for Scala 3.2 in http://dotty.epfl.ch/docs/reference/changed-features/wildcards.html

When used in conjunction with `-Xsource:3` this will let the user use `?` for wildcards and `_` for placeholders, letting the user cross-compile the same sources with Scala 3 with `-source:3.2` flag.

This change is not source breaking since currently `+_` and `-_` fail to parse entirely,
this change also does not allow the user to declare types with these names without backticks,
they can only be used as part of a type tree.

Gate `-_`/`+_` parsing behind `-Xsource:3` to guarantee minimal disruption to existing code
  • Loading branch information
neko-kai committed May 8, 2021
1 parent 14c86df commit c60318f
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 1 deletion.
9 changes: 8 additions & 1 deletion src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1077,7 +1077,14 @@ self =>
simpleTypeRest(in.token match {
case LPAREN => atPos(start)(makeSafeTupleType(inParens(types()), start))
case _ =>
if (isWildcardType)
if (currentRun.isScala3 && (in.name == raw.PLUS || in.name == raw.MINUS) && lookingAhead(in.token == USCORE)) {
val start = in.offset
val identName = in.name.encode.append("_").toTypeName
in.nextToken()
in.nextToken()
atPos(start)(Ident(identName))
}
else if (isWildcardType)
wildcardType(in.skipToken())
else
path(thisOK = false, typeOK = true) match {
Expand Down
7 changes: 7 additions & 0 deletions test/files/neg/variant-placeholders-future.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
variant-placeholders-future.scala:4: error: `=', `>:', or `<:' expected
type -_ = Int // error -_ not allowed as a type def name without backticks
^
variant-placeholders-future.scala:5: error: `=', `>:', or `<:' expected
type +_ = Int // error +_ not allowed as a type def name without backticks
^
two errors found
6 changes: 6 additions & 0 deletions test/files/neg/variant-placeholders-future.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// scalac: -Xsource:3
//
object Test {
type -_ = Int // error -_ not allowed as a type def name without backticks
type +_ = Int // error +_ not allowed as a type def name without backticks
}
7 changes: 7 additions & 0 deletions test/files/neg/variant-placeholders-nofuture.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
variant-placeholders-nofuture.scala:5: error: ';' expected but '_' found.
val fnMinusPlus1: -_ => +_ = (_: Int).toLong // error -_/+_ won't parse without -Xsource:3
^
variant-placeholders-nofuture.scala:6: error: ')' expected but '_' found.
val fnMinusPlus2: (-_) => +_ = fnMinusPlus1 // error -_/+_ won't parse without -Xsource:3
^
two errors found
8 changes: 8 additions & 0 deletions test/files/neg/variant-placeholders-nofuture.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
object Test {
type `-_` = Int
type `+_` = Long

val fnMinusPlus1: -_ => +_ = (_: Int).toLong // error -_/+_ won't parse without -Xsource:3
val fnMinusPlus2: (-_) => +_ = fnMinusPlus1 // error -_/+_ won't parse without -Xsource:3
val fnMinusPlus3: -_ => (+_) = fnMinusPlus2 // error -_/+_ won't parse without -Xsource:3
}
35 changes: 35 additions & 0 deletions test/files/pos/variant-placeholders-future.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// scalac: -Xsource:3
//
object Test {
type `-_` = Int
type `+_` = Long

val fnMinusPlus1: -_ => +_ = (_: Int).toLong
val fnMinusPlus2: (-_) => +_ = fnMinusPlus1
val fnMinusPlus3: -_ => (+_) = fnMinusPlus2

val fnTupMinusPlus2: (=> -_, -_) => +_ = (a, b) => ((a: Int) + (b: Int)).toLong
def defMinusPlus2(byname: => -_, vararg: -_*): +_ = ((vararg.sum: Int) + (byname: -_)).toLong
val infixMinusPlus2: -_ Either +_ = Right[-_, +_](1L)

val optPlus: Option[+_] = Some[ + _ ](1L) // spaces allowed
optPlus match {
case opt: Option[ + _ ] =>
val opt1: + _ = opt.get
val opt2: Long = opt1
}

val optMinus: Option[-_] = Some[ - _ ](1) // spaces allowed
optMinus match {
case opt: Option[ - _ ] =>
val opt1: `-_` = opt.get
val optErr: - _ = opt.get
val opt2: Int = opt1
}

locally {
type `-_`[A] = A
type `+_`[A] = Option[A]
val optOpt: Option[ + _ [+_[-_[Int]]]] = Some(Some(Some(1)))
}
}

0 comments on commit c60318f

Please sign in to comment.