Skip to content

Create {And,Or}TypeTree in Parsers, not desugar #4424

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

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
72 changes: 42 additions & 30 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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 = {
Expand All @@ -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)
}
}
}
Expand All @@ -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 ------------------------------------------- */
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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 =>
Expand Down Expand Up @@ -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 ------------------------------------------------ */

Expand Down Expand Up @@ -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 _ =>
Expand Down Expand Up @@ -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
}
Expand Down
23 changes: 23 additions & 0 deletions tests/neg/i4373.scala
Original file line number Diff line number Diff line change
@@ -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
}