Skip to content

Commit 01bd948

Browse files
authored
Merge pull request #1431 from cswinter/wip-unboundwildcard
Fix #1396, #1403: Properly handle unbound wildcard types
2 parents 765aecb + a1d0f3e commit 01bd948

File tree

4 files changed

+80
-36
lines changed

4 files changed

+80
-36
lines changed

docs/SyntaxSummary.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,17 +109,17 @@ grammar.
109109
| StableId
110110
| Path `.' `type' SingletonTypeTree(p)
111111
| `(' ArgTypes ')' Tuple(ts)
112+
| `_' TypeBounds
112113
| Refinement RefinedTypeTree(EmptyTree, refinement)
113114
| SimpleLiteral SingletonTypeTree(l)
114-
ArgType ::= Type
115-
| `_' TypeBounds
116-
ArgTypes ::= ArgType {`,' ArgType}
117-
FunArgType ::= ArgType
118-
| `=>' ArgType PrefixOp(=>, t)
115+
ArgTypes ::= Type {`,' Type}
116+
| NamedTypeArg {`,' NamedTypeArg }
117+
FunArgType ::= Type
118+
| `=>' Type PrefixOp(=>, t)
119119
ParamType ::= [`=>'] ParamValueType
120120
ParamValueType ::= Type [`*'] PostfixOp(t, "*")
121121
TypeArgs ::= `[' ArgTypes `]' ts
122-
NamedTypeArg ::= id `=' ArgType NamedArg(id, t)
122+
NamedTypeArg ::= id `=' Type NamedArg(id, t)
123123
NamedTypeArgs ::= `[' NamedTypeArg {`,' NamedTypeArg} `]' nts
124124
Refinement ::= `{' [Dcl] {semi [Dcl]} `}' ds
125125
TypeBounds ::= [`>:' Type] [`<: Type] | INT TypeBoundsTree(lo, hi)

src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import StdNames._
1919
import util.Positions._
2020
import Constants._
2121
import ScriptParsers._
22-
import annotation.switch
22+
import scala.annotation.{tailrec, switch}
2323
import util.DotClass
2424
import rewrite.Rewrites.patch
2525

@@ -648,12 +648,19 @@ object Parsers {
648648
}
649649

650650
/* ------------- TYPES ------------------------------------------------------ */
651+
/** Same as [[typ]], but emits a syntax error if it returns a wildcard.
652+
*/
653+
def toplevelTyp(): Tree = {
654+
val t = typ()
655+
for (wildcardPos <- findWildcardType(t)) syntaxError("unbound wildcard type", wildcardPos)
656+
t
657+
}
651658

652-
/** Type ::= FunArgTypes `=>' Type
659+
/** Type ::= FunArgTypes `=>' Type
653660
* | HkTypeParamClause `->' Type
654-
* | InfixType
661+
* | InfixType
655662
* FunArgTypes ::= InfixType
656-
* | `(' [ FunArgType {`,' FunArgType } ] `)'
663+
* | `(' [ FunArgType {`,' FunArgType } ] `)'
657664
*/
658665
def typ(): Tree = {
659666
val start = in.offset
@@ -737,6 +744,7 @@ object Parsers {
737744
* | StableId
738745
* | Path `.' type
739746
* | `(' ArgTypes `)'
747+
* | `_' TypeBounds
740748
* | Refinement
741749
* | Literal
742750
*/
@@ -746,6 +754,10 @@ object Parsers {
746754
else if (in.token == LBRACE)
747755
atPos(in.offset) { RefinedTypeTree(EmptyTree, refinement()) }
748756
else if (isSimpleLiteral) { SingletonTypeTree(literal()) }
757+
else if (in.token == USCORE) {
758+
val start = in.skipToken()
759+
typeBounds().withPos(Position(start, in.offset, start))
760+
}
749761
else path(thisOK = false, handleSingletonType) match {
750762
case r @ SingletonTypeTree(_) => r
751763
case r => convertToTypeId(r)
@@ -770,25 +782,16 @@ object Parsers {
770782
atPos(t.pos.start, id.pos.start) { SelectFromTypeTree(t, id.name) }
771783
}
772784

773-
/** ArgType ::= Type | `_' TypeBounds
774-
*/
775-
val argType = () =>
776-
if (in.token == USCORE) {
777-
val start = in.skipToken()
778-
typeBounds().withPos(Position(start, in.offset, start))
779-
}
780-
else typ()
781-
782-
/** NamedTypeArg ::= id `=' ArgType
785+
/** NamedTypeArg ::= id `=' Type
783786
*/
784787
val namedTypeArg = () => {
785788
val name = ident()
786789
accept(EQUALS)
787-
NamedArg(name.toTypeName, argType())
790+
NamedArg(name.toTypeName, typ())
788791
}
789792

790-
/** ArgTypes ::= ArgType {`,' ArgType}
791-
* NamedTypeArg {`,' NamedTypeArg}
793+
/** ArgTypes ::= Type {`,' Type}
794+
* | NamedTypeArg {`,' NamedTypeArg}
792795
*/
793796
def argTypes(namedOK: Boolean = false) = {
794797
def otherArgs(first: Tree, arg: () => Tree): List[Tree] = {
@@ -801,22 +804,22 @@ object Parsers {
801804
first :: rest
802805
}
803806
if (namedOK && in.token == IDENTIFIER)
804-
argType() match {
807+
typ() match {
805808
case Ident(name) if in.token == EQUALS =>
806809
in.nextToken()
807-
otherArgs(NamedArg(name, argType()), namedTypeArg)
810+
otherArgs(NamedArg(name, typ()), namedTypeArg)
808811
case firstArg =>
809812
if (in.token == EQUALS) println(s"??? $firstArg")
810-
otherArgs(firstArg, argType)
813+
otherArgs(firstArg, typ)
811814
}
812-
else commaSeparated(argType)
815+
else commaSeparated(typ)
813816
}
814817

815-
/** FunArgType ::= ArgType | `=>' ArgType
818+
/** FunArgType ::= Type | `=>' Type
816819
*/
817820
val funArgType = () =>
818-
if (in.token == ARROW) atPos(in.skipToken()) { ByNameTypeTree(argType()) }
819-
else argType()
821+
if (in.token == ARROW) atPos(in.skipToken()) { ByNameTypeTree(typ()) }
822+
else typ()
820823

821824
/** ParamType ::= [`=>'] ParamValueType
822825
*/
@@ -827,14 +830,14 @@ object Parsers {
827830
/** ParamValueType ::= Type [`*']
828831
*/
829832
def paramValueType(): Tree = {
830-
val t = typ()
833+
val t = toplevelTyp()
831834
if (isIdent(nme.raw.STAR)) {
832835
in.nextToken()
833836
atPos(t.pos.start) { PostfixOp(t, nme.raw.STAR) }
834837
} else t
835838
}
836839

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

851854
private def bound(tok: Int): Tree =
852-
if (in.token == tok) { in.nextToken(); typ() }
855+
if (in.token == tok) { in.nextToken(); toplevelTyp() }
853856
else EmptyTree
854857

855858
/** TypeParamBounds ::= TypeBounds {`<%' Type} {`:' Type}
@@ -863,26 +866,37 @@ object Parsers {
863866
def contextBounds(pname: TypeName): List[Tree] = in.token match {
864867
case COLON =>
865868
atPos(in.skipToken) {
866-
AppliedTypeTree(typ(), Ident(pname))
869+
AppliedTypeTree(toplevelTyp(), Ident(pname))
867870
} :: contextBounds(pname)
868871
case VIEWBOUND =>
869872
deprecationWarning("view bounds `<%' are deprecated, use a context bound `:' instead")
870873
atPos(in.skipToken) {
871-
Function(Ident(pname) :: Nil, typ())
874+
Function(Ident(pname) :: Nil, toplevelTyp())
872875
} :: contextBounds(pname)
873876
case _ =>
874877
Nil
875878
}
876879

877880
def typedOpt(): Tree =
878-
if (in.token == COLON) { in.nextToken(); typ() }
881+
if (in.token == COLON) { in.nextToken(); toplevelTyp() }
879882
else TypeTree()
880883

881884
def typeDependingOn(location: Location.Value): Tree =
882885
if (location == Location.InParens) typ()
883886
else if (location == Location.InPattern) refinedType()
884887
else infixType()
885888

889+
/** Checks whether `t` is a wildcard type.
890+
* If it is, returns the [[Position]] where the wildcard occurs.
891+
*/
892+
@tailrec
893+
private final def findWildcardType(t: Tree): Option[Position] = t match {
894+
case TypeBoundsTree(_, _) => Some(t.pos)
895+
case Parens(t1) => findWildcardType(t1)
896+
case Annotated(_, t1) => findWildcardType(t1)
897+
case _ => None
898+
}
899+
886900
/* ----------- EXPRESSIONS ------------------------------------------------ */
887901

888902
/** EqualsExpr ::= `=' Expr
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
object unboundWildcard {
2+
3+
// TODO: move this to tests/neg once it doesn't crash the compiler anymore
4+
val wildcardVal: _ = 0 // error: unbound wildcard type
5+
6+
val annotated: _ @unchecked = 0 // error: unbound wildcard type
7+
8+
def wildcardArg(x: _): Int = 0 // error: unbound wildcard type
9+
10+
def wildcardResult(x: Int): _ = 0 // error: unbound wildcard type
11+
12+
val singletonTuple: (((((((_))))))) = ??? // error: unbound wildcard type
13+
14+
val wildcardBoundedTypeArgL: List[_ <: _] = List(0) // error: unbound wildcard type
15+
val wildcardBoundedTypeArgU: List[_ >: _] = List(0) // error: unbound wildcard type
16+
17+
def wildcardBoundedTypeParamL[T <: _](x: T): T = x // error: unbound wildcard type
18+
def wildcardBoundedTypeParamU[T >: _](x: T): T = x // error: unbound wildcard type
19+
20+
val _1403: (_ <: Any) = 1 // error: unbound wildcard type
21+
}

tests/pos/wildcardInInfixType.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
object wildcardInInfixType {
2+
3+
val useless: _ => _ = (x: Int) => 1
4+
5+
val pointless: (_ <: Int) => _ = (x: Int) => 1
6+
7+
val answer: Int Either _ = Left(42)
8+
}
9+

0 commit comments

Comments
 (0)