Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support writing & instead of with in types under -Xsource:3 #9594

Merged
merged 1 commit into from Apr 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
}
}