Skip to content

Commit

Permalink
Merge pull request #9594 from smarter/and-syntax
Browse files Browse the repository at this point in the history
Support writing `&` instead of `with` in types under `-Xsource:3`
  • Loading branch information
lrytz committed Apr 27, 2021
2 parents 8340614 + 98da259 commit ba2f3bb
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 3 deletions.
39 changes: 36 additions & 3 deletions src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
Expand Up @@ -1056,13 +1056,14 @@ self =>
else {
ts foreach checkNotByNameOrVarargs
val tuple = atPos(start) { makeSafeTupleType(ts) }
infixTypeRest(
val tpt = infixTypeRest(
compoundTypeRest(
annotTypeRest(
simpleTypeRest(
tuple))),
InfixMode.FirstOp
)
if (currentRun.isScala3) andType(tpt) else tpt
}
}
private def makeExistentialTypeTree(t: Tree) = {
Expand Down Expand Up @@ -1228,12 +1229,44 @@ self =>
else t
}

def andType(tpt: Tree): Tree = {
val parents = ListBuffer.empty[Tree]
var otherInfixOp: Tree = EmptyTree
def collect(tpt: Tree): Unit = tpt match {
case AppliedTypeTree(op @ Ident(tpnme.AND), List(left, right)) =>
collect(left)
collect(right)
case AppliedTypeTree(op, args) if args.exists(arg => arg.pos.start < op.pos.point) =>
otherInfixOp = op
parents += treeCopy.AppliedTypeTree(tpt, op, args.map(andType))
case _ =>
parents += tpt
}
collect(tpt)
if (parents.lengthCompare(1) > 0) {
if (!otherInfixOp.isEmpty) {
// TODO: Unlike Scala 3, we do not take precedence into account when
// parsing infix types, there's an unmerged PR that attempts to
// change that (#6147), but until that's merged we cannot accurately
// parse things like `A Map B & C`, so give up and emit an error
// rather than continuing with an incorrect parse tree.
syntaxError(otherInfixOp.pos.point,
s"Cannot parse infix type combining `&` and `$otherInfixOp`, please use `$otherInfixOp` as the head of a regular type application.")
}
atPos(tpt.pos.start)(CompoundTypeTree(Template(parents.toList, noSelfType, Nil)))
}
else
parents.head
}

/** {{{
* InfixType ::= CompoundType {id [nl] CompoundType}
* }}}
*/
def infixType(mode: InfixMode.Value): Tree =
placeholderTypeBoundary { infixTypeRest(compoundType(), mode) }
def infixType(mode: InfixMode.Value): Tree = {
val tpt = placeholderTypeBoundary { infixTypeRest(compoundType(), mode) }
if (currentRun.isScala3) andType(tpt) else tpt
}

/** {{{
* Types ::= Type {`,` Type}
Expand Down
3 changes: 3 additions & 0 deletions src/reflect/scala/reflect/internal/StdNames.scala
Expand Up @@ -322,6 +322,9 @@ trait StdNames {

final val scala_ : NameType = nameType("scala")

// Scala 3 special type
val AND: NameType = nme.AND.toTypeName

def dropSingletonName(name: Name): TypeName = (name dropRight SINGLETON_SUFFIX.length).toTypeName
def singletonName(name: Name): TypeName = (name append SINGLETON_SUFFIX).toTypeName
}
Expand Down
7 changes: 7 additions & 0 deletions test/files/neg/and-future.check
@@ -0,0 +1,7 @@
and-future.scala:9: error: Cannot parse infix type combining `&` and `Map`, please use `Map` as the head of a regular type application.
val b: Int Map X & Int Map Y = Map[Int, X & Y]() // error: unsupported
^
and-future.scala:13: error: Cannot parse infix type combining `&` and `Map`, please use `Map` as the head of a regular type application.
val c: (Int Map X) & (Int Map Y) = Map[Int, X & Y]() // error: unsupported
^
2 errors
14 changes: 14 additions & 0 deletions test/files/neg/and-future.scala
@@ -0,0 +1,14 @@
// scalac: -Xsource:3
//

trait X
trait Y

class Test {
val a: Map[Int, X] & Map[Int, Y] = Map[Int, X & Y]() // ok
val b: Int Map X & Int Map Y = Map[Int, X & Y]() // error: unsupported

// This one is unambiguous but it's hard to check whether parens were present
// from the parser output so we also emit an error there.
val c: (Int Map X) & (Int Map Y) = Map[Int, X & Y]() // error: unsupported
}
17 changes: 17 additions & 0 deletions test/files/pos/and-future.scala
@@ -0,0 +1,17 @@
// scalac: -Xsource:3
//

trait X
trait Y

class Test[A, B <: A & AnyRef] {
def foo[T >: A & Null <: A & AnyRef & Any](x: T & ""): "" & T = x

val a: X & Y & AnyRef = new X with Y {}
val b: (X & Y) & AnyRef = new X with Y {}
val c: X & (Y & AnyRef) = new X with Y {}

val d: X & Y = c match {
case xy: (X & Y) => xy
}
}

0 comments on commit ba2f3bb

Please sign in to comment.