diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index a7586f876435..31ba7ae10b95 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1114,9 +1114,7 @@ object desugar { Apply(Select(Apply(Ident(nme.StringContext), strs), id), elems) case InfixOp(l, op, r) => if (ctx.mode is Mode.Type) - if (!op.isBackquoted && op.name == tpnme.raw.AMP) AndTypeTree(l, r) // l & r - else if (!op.isBackquoted && op.name == tpnme.raw.BAR) OrTypeTree(l, r) // l | r - else AppliedTypeTree(op, l :: r :: Nil) // op[l, r] + AppliedTypeTree(op, l :: r :: Nil) // op[l, r] else { assert(ctx.mode is Mode.Pattern) // expressions are handled separately by `binop` Apply(op, l :: r :: Nil) // op(l, r) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index edaf6850d8cf..92a2a650d028 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -41,6 +41,25 @@ object Parsers { def nonePositive: Boolean = parCounts forall (_ <= 0) } + /** Checks whether `t` is a wildcard type. + * If it is, returns the [[Position]] where the wildcard occurs. + */ + @tailrec + final def findWildcardType(t: Tree): Option[Position] = t match { + case TypeBoundsTree(_, _) => Some(t.pos) + case Parens(t1) => findWildcardType(t1) + case Annotated(t1, _) => findWildcardType(t1) + case _ => None + } + + def rejectWildcard(t: Tree, report: Position => Unit, fallbackTree: Tree): Tree = + findWildcardType(t) match { + case Some(wildcardPos) => + report(wildcardPos) + fallbackTree + case None => t + } + @sharable object Location extends Enumeration { val InParens, InBlock, InPattern, ElseWhere = Value } @@ -452,7 +471,7 @@ object Parsers { if (isLeftAssoc(op1) != op2LeftAssoc) syntaxError(MixedLeftAndRightAssociativeOps(op1, op2, op2LeftAssoc), offset) - def reduceStack(base: List[OpInfo], top: Tree, prec: Int, leftAssoc: Boolean, op2: Name): Tree = { + def reduceStack(base: List[OpInfo], top: Tree, prec: Int, leftAssoc: Boolean, op2: Name, isType: Boolean): Tree = { if (opStack != base && precedence(opStack.head.operator.name) == prec) checkAssoc(opStack.head.offset, opStack.head.operator.name, op2, leftAssoc) def recur(top: Tree): Tree = { @@ -464,7 +483,15 @@ object Parsers { opStack = opStack.tail recur { atPos(opInfo.operator.pos union opInfo.operand.pos union top.pos) { - InfixOp(opInfo.operand, opInfo.operator, top) + val op = opInfo.operator + val l = opInfo.operand + val r = top + if (isType && !op.isBackquoted && op.name == tpnme.raw.BAR) { + OrTypeTree(checkWildcard(l), checkWildcard(r)) + } else if (isType && !op.isBackquoted && op.name == tpnme.raw.AMP) { + AndTypeTree(checkWildcard(l), checkWildcard(r)) + } else + InfixOp(l, op, r) } } } @@ -488,20 +515,20 @@ object Parsers { var top = first while (isIdent && isOperator) { val op = if (isType) typeIdent() else termIdent() - top = reduceStack(base, top, precedence(op.name), isLeftAssoc(op.name), op.name) + top = reduceStack(base, top, precedence(op.name), isLeftAssoc(op.name), op.name, isType) opStack = OpInfo(top, op, in.offset) :: opStack newLineOptWhenFollowing(canStartOperand) if (maybePostfix && !canStartOperand(in.token)) { val topInfo = opStack.head opStack = opStack.tail - val od = reduceStack(base, topInfo.operand, 0, true, in.name) + val od = reduceStack(base, topInfo.operand, 0, true, in.name, isType) return atPos(startOffset(od), topInfo.offset) { PostfixOp(od, topInfo.operator) } } top = operand() } - reduceStack(base, top, 0, true, in.name) + reduceStack(base, top, 0, true, in.name, isType) } /* -------- IDENTIFIERS AND LITERALS ------------------------------------------- */ @@ -709,15 +736,7 @@ object Parsers { /** Same as [[typ]], but if this results in a wildcard it emits a syntax error and * returns a tree for type `Any` instead. */ - def toplevelTyp(): Tree = { - val t = typ() - findWildcardType(t) match { - case Some(wildcardPos) => - syntaxError(UnboundWildcardType(), wildcardPos) - scalaAny - case None => t - } - } + def toplevelTyp(): Tree = checkWildcard(typ()) /** Type ::= [FunArgMods] FunArgTypes `=>' Type * | HkTypeParamClause `->' Type @@ -784,7 +803,7 @@ object Parsers { val start = in.offset val tparams = typeParamClause(ParamOwner.TypeParam) if (in.token == ARROW) - atPos(start, in.skipToken())(LambdaTypeTree(tparams, typ())) + atPos(start, in.skipToken())(LambdaTypeTree(tparams, toplevelTyp())) else { accept(ARROW); typ() } } else infixType() @@ -822,7 +841,7 @@ object Parsers { def refinedTypeRest(t: Tree): Tree = { newLineOptWhenFollowedBy(LBRACE) - if (in.token == LBRACE) refinedTypeRest(atPos(startOffset(t)) { RefinedTypeTree(t, refinement()) }) + if (in.token == LBRACE) refinedTypeRest(atPos(startOffset(t)) { RefinedTypeTree(checkWildcard(t), refinement()) }) else t } @@ -886,7 +905,7 @@ object Parsers { private def simpleTypeRest(t: Tree): Tree = in.token match { case HASH => simpleTypeRest(typeProjection(t)) case LBRACKET => simpleTypeRest(atPos(startOffset(t)) { - AppliedTypeTree(t, typeArgs(namedOK = false, wildOK = true)) }) + AppliedTypeTree(checkWildcard(t), typeArgs(namedOK = false, wildOK = true)) }) case _ => t } @@ -917,7 +936,7 @@ object Parsers { else Nil first :: rest } - def typParser() = if (wildOK) typ() else toplevelTyp() + def typParser() = checkWildcard(typ(), wildOK) if (namedOK && in.token == IDENTIFIER) typParser() match { case Ident(name) if in.token == EQUALS => @@ -1001,16 +1020,8 @@ object Parsers { else if (location == Location.InPattern) refinedType() else infixType() - /** Checks whether `t` is a wildcard type. - * If it is, returns the [[Position]] where the wildcard occurs. - */ - @tailrec - private final def findWildcardType(t: Tree): Option[Position] = t match { - case TypeBoundsTree(_, _) => Some(t.pos) - case Parens(t1) => findWildcardType(t1) - case Annotated(t1, _) => findWildcardType(t1) - case _ => None - } + def checkWildcard(t: Tree, wildOK: Boolean = false, fallbackTree: Tree = scalaAny): Tree = + if (wildOK) t else rejectWildcard(t, syntaxError(UnboundWildcardType(), _), fallbackTree) /* ----------- EXPRESSIONS ------------------------------------------------ */ @@ -2148,7 +2159,7 @@ object Parsers { in.token match { case EQUALS => in.nextToken() - TypeDef(name, lambdaAbstract(tparams, typ())).withMods(mods).setComment(in.getDocComment(start)) + TypeDef(name, lambdaAbstract(tparams, toplevelTyp())).withMods(mods).setComment(in.getDocComment(start)) case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE | EOF => TypeDef(name, lambdaAbstract(tparams, typeBounds())).withMods(mods).setComment(in.getDocComment(start)) case _ => @@ -2276,7 +2287,8 @@ object Parsers { /** ConstrApp ::= SimpleType {ParArgumentExprs} */ val constrApp = () => { - val t = annotType() + // Using Ident(nme.ERROR) to avoid causing cascade errors on non-user-written code + val t = checkWildcard(annotType(), fallbackTree = Ident(nme.ERROR)) if (in.token == LPAREN) parArgumentExprss(wrapNew(t)) else t } diff --git a/tests/neg/i4373.scala b/tests/neg/i4373.scala new file mode 100644 index 000000000000..349985521856 --- /dev/null +++ b/tests/neg/i4373.scala @@ -0,0 +1,23 @@ +trait Base +trait TypeConstr[X] + +class X1[A >: _ | X1[_]] // error +class X2[A >: _ & X2[_]] // error +class X3[A >: X3[_] | _] // error +class X4[A >: X4[_] & _] // error + +class A1 extends _ // error +class A2 extends _ with _ // error // error +class A3 extends Base with _ // error +class A4 extends _ with Base // error + +object Test { + type T1 = _ // error + type T2 = _[Int] // error + type T3 = _ { type S } // error + type T4 = [X] => _ // error + + // Open questions: + type T5 = TypeConstr[_ { type S }] // error + type T5 = TypeConstr[_[Int]] // error +}