Skip to content

Commit ba2f3bb

Browse files
authored
Merge pull request scala#9594 from smarter/and-syntax
Support writing `&` instead of `with` in types under `-Xsource:3`
2 parents 8340614 + 98da259 commit ba2f3bb

File tree

5 files changed

+77
-3
lines changed

5 files changed

+77
-3
lines changed

src/compiler/scala/tools/nsc/ast/parser/Parsers.scala

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,13 +1056,14 @@ self =>
10561056
else {
10571057
ts foreach checkNotByNameOrVarargs
10581058
val tuple = atPos(start) { makeSafeTupleType(ts) }
1059-
infixTypeRest(
1059+
val tpt = infixTypeRest(
10601060
compoundTypeRest(
10611061
annotTypeRest(
10621062
simpleTypeRest(
10631063
tuple))),
10641064
InfixMode.FirstOp
10651065
)
1066+
if (currentRun.isScala3) andType(tpt) else tpt
10661067
}
10671068
}
10681069
private def makeExistentialTypeTree(t: Tree) = {
@@ -1228,12 +1229,44 @@ self =>
12281229
else t
12291230
}
12301231

1232+
def andType(tpt: Tree): Tree = {
1233+
val parents = ListBuffer.empty[Tree]
1234+
var otherInfixOp: Tree = EmptyTree
1235+
def collect(tpt: Tree): Unit = tpt match {
1236+
case AppliedTypeTree(op @ Ident(tpnme.AND), List(left, right)) =>
1237+
collect(left)
1238+
collect(right)
1239+
case AppliedTypeTree(op, args) if args.exists(arg => arg.pos.start < op.pos.point) =>
1240+
otherInfixOp = op
1241+
parents += treeCopy.AppliedTypeTree(tpt, op, args.map(andType))
1242+
case _ =>
1243+
parents += tpt
1244+
}
1245+
collect(tpt)
1246+
if (parents.lengthCompare(1) > 0) {
1247+
if (!otherInfixOp.isEmpty) {
1248+
// TODO: Unlike Scala 3, we do not take precedence into account when
1249+
// parsing infix types, there's an unmerged PR that attempts to
1250+
// change that (#6147), but until that's merged we cannot accurately
1251+
// parse things like `A Map B & C`, so give up and emit an error
1252+
// rather than continuing with an incorrect parse tree.
1253+
syntaxError(otherInfixOp.pos.point,
1254+
s"Cannot parse infix type combining `&` and `$otherInfixOp`, please use `$otherInfixOp` as the head of a regular type application.")
1255+
}
1256+
atPos(tpt.pos.start)(CompoundTypeTree(Template(parents.toList, noSelfType, Nil)))
1257+
}
1258+
else
1259+
parents.head
1260+
}
1261+
12311262
/** {{{
12321263
* InfixType ::= CompoundType {id [nl] CompoundType}
12331264
* }}}
12341265
*/
1235-
def infixType(mode: InfixMode.Value): Tree =
1236-
placeholderTypeBoundary { infixTypeRest(compoundType(), mode) }
1266+
def infixType(mode: InfixMode.Value): Tree = {
1267+
val tpt = placeholderTypeBoundary { infixTypeRest(compoundType(), mode) }
1268+
if (currentRun.isScala3) andType(tpt) else tpt
1269+
}
12371270

12381271
/** {{{
12391272
* Types ::= Type {`,` Type}

src/reflect/scala/reflect/internal/StdNames.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,9 @@ trait StdNames {
322322

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

325+
// Scala 3 special type
326+
val AND: NameType = nme.AND.toTypeName
327+
325328
def dropSingletonName(name: Name): TypeName = (name dropRight SINGLETON_SUFFIX.length).toTypeName
326329
def singletonName(name: Name): TypeName = (name append SINGLETON_SUFFIX).toTypeName
327330
}

test/files/neg/and-future.check

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
and-future.scala:9: error: Cannot parse infix type combining `&` and `Map`, please use `Map` as the head of a regular type application.
2+
val b: Int Map X & Int Map Y = Map[Int, X & Y]() // error: unsupported
3+
^
4+
and-future.scala:13: error: Cannot parse infix type combining `&` and `Map`, please use `Map` as the head of a regular type application.
5+
val c: (Int Map X) & (Int Map Y) = Map[Int, X & Y]() // error: unsupported
6+
^
7+
2 errors

test/files/neg/and-future.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// scalac: -Xsource:3
2+
//
3+
4+
trait X
5+
trait Y
6+
7+
class Test {
8+
val a: Map[Int, X] & Map[Int, Y] = Map[Int, X & Y]() // ok
9+
val b: Int Map X & Int Map Y = Map[Int, X & Y]() // error: unsupported
10+
11+
// This one is unambiguous but it's hard to check whether parens were present
12+
// from the parser output so we also emit an error there.
13+
val c: (Int Map X) & (Int Map Y) = Map[Int, X & Y]() // error: unsupported
14+
}

test/files/pos/and-future.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// scalac: -Xsource:3
2+
//
3+
4+
trait X
5+
trait Y
6+
7+
class Test[A, B <: A & AnyRef] {
8+
def foo[T >: A & Null <: A & AnyRef & Any](x: T & ""): "" & T = x
9+
10+
val a: X & Y & AnyRef = new X with Y {}
11+
val b: (X & Y) & AnyRef = new X with Y {}
12+
val c: X & (Y & AnyRef) = new X with Y {}
13+
14+
val d: X & Y = c match {
15+
case xy: (X & Y) => xy
16+
}
17+
}

0 commit comments

Comments
 (0)