Skip to content

Commit

Permalink
Merge pull request scala#1431 from cswinter/wip-unboundwildcard
Browse files Browse the repository at this point in the history
Fix scala#1396, scala#1403: Properly handle unbound wildcard types
  • Loading branch information
odersky authored Aug 1, 2016
2 parents 765aecb + a1d0f3e commit 01bd948
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 36 deletions.
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)
}

0 comments on commit 01bd948

Please sign in to comment.