aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/SyntaxSummary.txt12
-rw-r--r--src/dotty/tools/dotc/parsing/Parsers.scala74
-rw-r--r--tests/pending/neg/unboundWildcard.scala21
-rw-r--r--tests/pos/wildcardInInfixType.scala9
4 files changed, 80 insertions, 36 deletions
diff --git a/docs/SyntaxSummary.txt b/docs/SyntaxSummary.txt
index 6751c90e2..f07335d1d 100644
--- a/docs/SyntaxSummary.txt
+++ b/docs/SyntaxSummary.txt
@@ -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)
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala
index 82add4c14..12f629c57 100644
--- a/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -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
@@ -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
@@ -737,6 +744,7 @@ object Parsers {
* | StableId
* | Path `.' type
* | `(' ArgTypes `)'
+ * | `_' TypeBounds
* | Refinement
* | Literal
*/
@@ -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)
@@ -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] = {
@@ -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
*/
@@ -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))
@@ -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}
@@ -863,19 +866,19 @@ 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 =
@@ -883,6 +886,17 @@ 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
+ }
+
/* ----------- EXPRESSIONS ------------------------------------------------ */
/** EqualsExpr ::= `=' Expr
diff --git a/tests/pending/neg/unboundWildcard.scala b/tests/pending/neg/unboundWildcard.scala
new file mode 100644
index 000000000..eeee04fde
--- /dev/null
+++ b/tests/pending/neg/unboundWildcard.scala
@@ -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
+}
diff --git a/tests/pos/wildcardInInfixType.scala b/tests/pos/wildcardInInfixType.scala
new file mode 100644
index 000000000..9905762c5
--- /dev/null
+++ b/tests/pos/wildcardInInfixType.scala
@@ -0,0 +1,9 @@
+object wildcardInInfixType {
+
+ val useless: _ => _ = (x: Int) => 1
+
+ val pointless: (_ <: Int) => _ = (x: Int) => 1
+
+ val answer: Int Either _ = Left(42)
+}
+