diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 8151f958aeb6..42767df41f78 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -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) = { @@ -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} diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 66dee512f7bd..3d944af6c26d 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -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 } diff --git a/test/files/neg/and-future.check b/test/files/neg/and-future.check new file mode 100644 index 000000000000..c7992b38964e --- /dev/null +++ b/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 diff --git a/test/files/neg/and-future.scala b/test/files/neg/and-future.scala new file mode 100644 index 000000000000..1092c013b186 --- /dev/null +++ b/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 +} diff --git a/test/files/pos/and-future.scala b/test/files/pos/and-future.scala new file mode 100644 index 000000000000..f7e15e822ecc --- /dev/null +++ b/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 + } +}