Skip to content

Fix #1396, #1403: Properly handle unbound wildcard types #1431

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

Merged
merged 4 commits into from
Aug 1, 2016
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions docs/SyntaxSummary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,17 +109,17 @@ grammar.
| StableId
| Path `.' `type' SingletonTypeTree(p)
| `(' ArgTypes ')' Tuple(ts)
| `_' TypeBounds
| Refinement RefinedTypeTree(EmptyTree, refinement)
| SimpleLiteral SingletonTypeTree(l)
ArgType ::= Type
| `_' TypeBounds
ArgTypes ::= ArgType {`,' ArgType}
FunArgType ::= ArgType
| `=>' ArgType PrefixOp(=>, t)
ArgTypes ::= Type {`,' Type}
| NamedTypeArg {`,' NamedTypeArg }
FunArgType ::= Type
| `=>' Type PrefixOp(=>, t)
ParamType ::= [`=>'] ParamValueType
ParamValueType ::= Type [`*'] PostfixOp(t, "*")
TypeArgs ::= `[' ArgTypes `]' ts
NamedTypeArg ::= id `=' ArgType NamedArg(id, t)
NamedTypeArg ::= id `=' Type NamedArg(id, t)
NamedTypeArgs ::= `[' NamedTypeArg {`,' NamedTypeArg} `]' nts
Refinement ::= `{' [Dcl] {semi [Dcl]} `}' ds
TypeBounds ::= [`>:' Type] [`<: Type] | INT TypeBoundsTree(lo, hi)
Expand Down
74 changes: 44 additions & 30 deletions src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import StdNames._
import util.Positions._
import Constants._
import ScriptParsers._
import annotation.switch
import scala.annotation.{tailrec, switch}
import util.DotClass
import rewrite.Rewrites.patch

Expand Down Expand Up @@ -648,12 +648,19 @@ object Parsers {
}

/* ------------- TYPES ------------------------------------------------------ */
/** Same as [[typ]], but emits a syntax error if it returns a wildcard.
*/
def toplevelTyp(): Tree = {
val t = typ()
for (wildcardPos <- findWildcardType(t)) syntaxError("unbound wildcard type", wildcardPos)
t
}

/** Type ::= FunArgTypes `=>' Type
/** Type ::= FunArgTypes `=>' Type
* | HkTypeParamClause `->' Type
* | InfixType
* | InfixType
* FunArgTypes ::= InfixType
* | `(' [ FunArgType {`,' FunArgType } ] `)'
* | `(' [ FunArgType {`,' FunArgType } ] `)'
*/
def typ(): Tree = {
val start = in.offset
Expand Down Expand Up @@ -737,6 +744,7 @@ object Parsers {
* | StableId
* | Path `.' type
* | `(' ArgTypes `)'
* | `_' TypeBounds
* | Refinement
* | Literal
*/
Expand All @@ -746,6 +754,10 @@ object Parsers {
else if (in.token == LBRACE)
atPos(in.offset) { RefinedTypeTree(EmptyTree, refinement()) }
else if (isSimpleLiteral) { SingletonTypeTree(literal()) }
else if (in.token == USCORE) {
val start = in.skipToken()
typeBounds().withPos(Position(start, in.offset, start))
}
else path(thisOK = false, handleSingletonType) match {
case r @ SingletonTypeTree(_) => r
case r => convertToTypeId(r)
Expand All @@ -770,25 +782,16 @@ object Parsers {
atPos(t.pos.start, id.pos.start) { SelectFromTypeTree(t, id.name) }
}

/** ArgType ::= Type | `_' TypeBounds
*/
val argType = () =>
if (in.token == USCORE) {
val start = in.skipToken()
typeBounds().withPos(Position(start, in.offset, start))
}
else typ()

/** NamedTypeArg ::= id `=' ArgType
/** NamedTypeArg ::= id `=' Type
*/
val namedTypeArg = () => {
val name = ident()
accept(EQUALS)
NamedArg(name.toTypeName, argType())
NamedArg(name.toTypeName, typ())
}

/** ArgTypes ::= ArgType {`,' ArgType}
* NamedTypeArg {`,' NamedTypeArg}
/** ArgTypes ::= Type {`,' Type}
* | NamedTypeArg {`,' NamedTypeArg}
*/
def argTypes(namedOK: Boolean = false) = {
def otherArgs(first: Tree, arg: () => Tree): List[Tree] = {
Expand All @@ -801,22 +804,22 @@ object Parsers {
first :: rest
}
if (namedOK && in.token == IDENTIFIER)
argType() match {
typ() match {
case Ident(name) if in.token == EQUALS =>
in.nextToken()
otherArgs(NamedArg(name, argType()), namedTypeArg)
otherArgs(NamedArg(name, typ()), namedTypeArg)
case firstArg =>
if (in.token == EQUALS) println(s"??? $firstArg")
otherArgs(firstArg, argType)
otherArgs(firstArg, typ)
}
else commaSeparated(argType)
else commaSeparated(typ)
}

/** FunArgType ::= ArgType | `=>' ArgType
/** FunArgType ::= Type | `=>' Type
*/
val funArgType = () =>
if (in.token == ARROW) atPos(in.skipToken()) { ByNameTypeTree(argType()) }
else argType()
if (in.token == ARROW) atPos(in.skipToken()) { ByNameTypeTree(typ()) }
else typ()

/** ParamType ::= [`=>'] ParamValueType
*/
Expand All @@ -827,14 +830,14 @@ object Parsers {
/** ParamValueType ::= Type [`*']
*/
def paramValueType(): Tree = {
val t = typ()
val t = toplevelTyp()
if (isIdent(nme.raw.STAR)) {
in.nextToken()
atPos(t.pos.start) { PostfixOp(t, nme.raw.STAR) }
} else t
}

/** TypeArgs ::= `[' ArgType {`,' ArgType} `]'
/** TypeArgs ::= `[' Type {`,' Type} `]'
* NamedTypeArgs ::= `[' NamedTypeArg {`,' NamedTypeArg} `]'
*/
def typeArgs(namedOK: Boolean = false): List[Tree] = inBrackets(argTypes(namedOK))
Expand All @@ -849,7 +852,7 @@ object Parsers {
atPos(in.offset) { TypeBoundsTree(bound(SUPERTYPE), bound(SUBTYPE)) }

private def bound(tok: Int): Tree =
if (in.token == tok) { in.nextToken(); typ() }
if (in.token == tok) { in.nextToken(); toplevelTyp() }
else EmptyTree

/** TypeParamBounds ::= TypeBounds {`<%' Type} {`:' Type}
Expand All @@ -863,26 +866,37 @@ object Parsers {
def contextBounds(pname: TypeName): List[Tree] = in.token match {
case COLON =>
atPos(in.skipToken) {
AppliedTypeTree(typ(), Ident(pname))
AppliedTypeTree(toplevelTyp(), Ident(pname))
} :: contextBounds(pname)
case VIEWBOUND =>
deprecationWarning("view bounds `<%' are deprecated, use a context bound `:' instead")
atPos(in.skipToken) {
Function(Ident(pname) :: Nil, typ())
Function(Ident(pname) :: Nil, toplevelTyp())
} :: contextBounds(pname)
case _ =>
Nil
}

def typedOpt(): Tree =
if (in.token == COLON) { in.nextToken(); typ() }
if (in.token == COLON) { in.nextToken(); toplevelTyp() }
else TypeTree()

def typeDependingOn(location: Location.Value): Tree =
if (location == Location.InParens) typ()
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
}

/* ----------- EXPRESSIONS ------------------------------------------------ */

/** EqualsExpr ::= `=' Expr
Expand Down
21 changes: 21 additions & 0 deletions tests/pending/neg/unboundWildcard.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
object unboundWildcard {

// TODO: move this to tests/neg once it doesn't crash the compiler anymore
val wildcardVal: _ = 0 // error: unbound wildcard type

val annotated: _ @unchecked = 0 // error: unbound wildcard type

def wildcardArg(x: _): Int = 0 // error: unbound wildcard type

def wildcardResult(x: Int): _ = 0 // error: unbound wildcard type

val singletonTuple: (((((((_))))))) = ??? // error: unbound wildcard type

val wildcardBoundedTypeArgL: List[_ <: _] = List(0) // error: unbound wildcard type
val wildcardBoundedTypeArgU: List[_ >: _] = List(0) // error: unbound wildcard type

def wildcardBoundedTypeParamL[T <: _](x: T): T = x // error: unbound wildcard type
def wildcardBoundedTypeParamU[T >: _](x: T): T = x // error: unbound wildcard type

val _1403: (_ <: Any) = 1 // error: unbound wildcard type
}
9 changes: 9 additions & 0 deletions tests/pos/wildcardInInfixType.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
object wildcardInInfixType {

val useless: _ => _ = (x: Int) => 1

val pointless: (_ <: Int) => _ = (x: Int) => 1

val answer: Int Either _ = Left(42)
}