Skip to content

Commit

Permalink
Disallow toplevel wildcard type param
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed Jul 29, 2021
1 parent 39c9d85 commit 3699aea
Show file tree
Hide file tree
Showing 14 changed files with 105 additions and 36 deletions.
29 changes: 22 additions & 7 deletions src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
Expand Up @@ -274,6 +274,14 @@ self =>
final val InBlock: Location = 1
final val InTemplate: Location = 2

type ParamOwner = Int
object ParamOwner {
final val Class = 0
final val Type = 1
final val TypeParam = 2 // unused
final val Def = 3
}

// These symbols may not yet be loaded (e.g. in the ide) so don't go
// through definitions to obtain the names.
lazy val ScalaValueClassNames = Seq(tpnme.AnyVal,
Expand Down Expand Up @@ -2552,8 +2560,9 @@ self =>
* TypeParam ::= Id TypeParamClauseOpt TypeBounds {`<%` Type} {`:` Type}
* }}}
*/
def typeParamClauseOpt(owner: Name, contextBoundBuf: ListBuffer[Tree]): List[TypeDef] = {
def typeParamClauseOpt(owner: Name, contextBoundBuf: ListBuffer[Tree], ownerKind: ParamOwner): List[TypeDef] = {
def typeParam(ms: Modifiers): TypeDef = {
val isAbstractOwner = ownerKind == ParamOwner.Type //|| ownerKind == ParamOwner.TypeParam
var mods = ms | Flags.PARAM
val start = in.offset
if (owner.isTypeName && isIdent) {
Expand All @@ -2567,10 +2576,16 @@ self =>
}
val nameOffset = in.offset
checkQMarkDefinition()
// TODO AM: freshTermName(o2p(in.skipToken()), "_$$"), will need to update test suite
val pname: TypeName = wildcardOrIdent().toTypeName
val pname: TypeName =
if (in.token == USCORE && (isAbstractOwner || !currentRun.isScala3)) {
if (!isAbstractOwner)
deprecationWarning(in.offset, "Top-level wildcard is not allowed and will error under -Xsource:3", "2.13.7")
in.nextToken()
freshTypeName("_$$")
}
else ident(skipIt = false).toTypeName
val param = atPos(start, nameOffset) {
val tparams = typeParamClauseOpt(pname, null) // @M TODO null --> no higher-order context bounds for now
val tparams = typeParamClauseOpt(pname, null, ParamOwner.Type) // @M TODO null --> no higher-order context bounds for now
TypeDef(mods, pname, tparams, typeBounds())
}
if (contextBoundBuf ne null) {
Expand Down Expand Up @@ -2889,7 +2904,7 @@ self =>
// [T : B] or [T : => B]; it contains the equivalent implicit parameter type,
// i.e. (B[T] or T => B)
val contextBoundBuf = new ListBuffer[Tree]
val tparams = typeParamClauseOpt(name, contextBoundBuf)
val tparams = typeParamClauseOpt(name, contextBoundBuf, ParamOwner.Def)
val vparamss = paramClauses(name, contextBoundBuf.toList, ofCaseClass = false)
newLineOptWhenFollowedBy(LBRACE)
var restype = fromWithinReturnType(typedOpt())
Expand Down Expand Up @@ -2990,7 +3005,7 @@ self =>
atPos(start, in.offset) {
val name = identForType()
// @M! a type alias as well as an abstract type may declare type parameters
val tparams = typeParamClauseOpt(name, null)
val tparams = typeParamClauseOpt(name, null, ParamOwner.Type)
in.token match {
case EQUALS =>
in.nextToken()
Expand Down Expand Up @@ -3054,7 +3069,7 @@ self =>
atPos(start, if (name == tpnme.ERROR) start else nameOffset) {
savingClassContextBounds {
val contextBoundBuf = new ListBuffer[Tree]
val tparams = typeParamClauseOpt(name, contextBoundBuf)
val tparams = typeParamClauseOpt(name, contextBoundBuf, ParamOwner.Class)
classContextBounds = contextBoundBuf.toList
val tstart = (in.offset :: classContextBounds.map(_.pos.start)).min
if (!classContextBounds.isEmpty && mods.isTrait) {
Expand Down
2 changes: 1 addition & 1 deletion src/partest/scala/tools/partest/ScaladocModelTest.scala
Expand Up @@ -72,7 +72,7 @@ abstract class ScaladocModelTest extends DirectTest {

try {
// 1 - compile with scaladoc and get the model out
val universe = model.getOrElse({sys.error("Scaladoc Model Test ERROR: No universe generated!")})
val universe = model.getOrElse { sys.error("Scaladoc Model Test ERROR: No universe generated!") }
// 2 - check the model generated
testModel(universe.rootPackage)
println("Done.")
Expand Down
2 changes: 2 additions & 0 deletions src/reflect/scala/reflect/internal/StdNames.scala
Expand Up @@ -464,6 +464,7 @@ trait StdNames {
def unexpandedName(name: Name): Name =
name.lastIndexOf("$$") match {
case 0 | -1 => name
case idx if idx > 0 && name.charAt(idx - 1) == '_' => raw.USCORE
case idx0 =>
// Sketchville - We've found $$ but if it's part of $$$ or $$$$
// or something we need to keep the bonus dollars, so e.g. foo$$$outer
Expand Down Expand Up @@ -974,6 +975,7 @@ trait StdNames {
final val STAR : NameType = nameType("*")
final val TILDE: NameType = nameType("~")
final val QMARK: NameType = nameType("?")
final val USCORE: NameType = nameType("_")

final val isUnary: Set[Name] = Set(MINUS, PLUS, TILDE, BANG)
}
Expand Down
2 changes: 1 addition & 1 deletion src/reflect/scala/reflect/internal/TypeDebugging.scala
Expand Up @@ -140,7 +140,7 @@ trait TypeDebugging {
def debugString(tp: Type) = debug(tp)
}
def paramString(tp: Type) = typeDebug.str params tp.params
def typeParamsString(tp: Type) = typeDebug.str brackets (tp.typeParams map (_.defString))
def typeParamsString(tp: Type) = typeDebug.str.brackets(tp.typeParams.map(_.defString))
def debugString(tp: Type) = typeDebug debugString tp
}

Expand Down
6 changes: 2 additions & 4 deletions src/reflect/scala/reflect/internal/Types.scala
Expand Up @@ -1559,18 +1559,16 @@ trait Types
/** Bounds notation used in Scala syntax.
* For example +This <: scala.collection.generic.Sorted[K,This].
*/
private[internal] def scalaNotation(typeString: Type => String): String = {
private[internal] def scalaNotation(typeString: Type => String): String =
(if (emptyLowerBound) "" else " >: " + typeString(lo)) +
(if (emptyUpperBound) "" else " <: " + typeString(hi))
}
/** Bounds notation used in https://adriaanm.github.com/files/higher.pdf.
* For example *(scala.collection.generic.Sorted[K,This]).
*/
private[internal] def starNotation(typeString: Type => String): String = {
private[internal] def starNotation(typeString: Type => String): String =
if (emptyLowerBound && emptyUpperBound) ""
else if (emptyLowerBound) s"(${typeString(hi)})"
else s"(${typeString(lo)}, ${typeString(hi)})"
}
override def kind = "TypeBoundsType"
override def mapOver(map: TypeMap): Type = {
val lo1 = map match {
Expand Down
Expand Up @@ -45,7 +45,7 @@ trait ModelFactoryTypeSupport {
appendType0(tp)
case tp :: tps =>
appendType0(tp)
nameBuffer append sep
nameBuffer.append(sep)
appendTypes0(tps, sep)
}

Expand Down Expand Up @@ -202,15 +202,16 @@ trait ModelFactoryTypeSupport {
/* Polymorphic types */
case PolyType(tparams, result) =>
assert(tparams.nonEmpty, "polymorphic type must have at least one type parameter")
def typeParamsToString(tps: List[Symbol]): String = if (tps.isEmpty) "" else
tps.map{tparam =>
tparam.varianceString + tparam.name + typeParamsToString(tparam.typeParams)
}.mkString("[", ", ", "]")
nameBuffer append typeParamsToString(tparams)
def typeParamsToString(tps: List[Symbol]): String =
if (tps.isEmpty) ""
else
tps.map { tparam =>
tparam.varianceString + tparam.unexpandedName + typeParamsToString(tparam.typeParams)
}.mkString("[", ", ", "]")
nameBuffer.append(typeParamsToString(tparams))
appendType0(result)

case et@ExistentialType(quantified, underlying) =>

def appendInfoStringReduced(sym: Symbol, tp: Type): Unit = {
if (sym.isType && !sym.isAliasType && !sym.isClass) {
tp match {
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/t2462c.check
Expand Up @@ -10,7 +10,7 @@ t2462c.scala:35: error: No C of Foo[Int]
t2462c.scala:38: error: I see no C[Foo[Int]]
h[Foo[Int]]
^
t2462c.scala:42: error: String List [?T0, ZZ] -> List[C[_]] Int Option[Long] -- .
t2462c.scala:42: error: String List [_$$2, ZZ] -> List[C[_]] Int Option[Long] -- .
i.m[Option[Long]]
^
5 errors
2 changes: 1 addition & 1 deletion test/files/neg/t2462c.scala
@@ -1,4 +1,4 @@
// scalac: -Xfatal-warnings
// scalac: -Werror
//

import annotation._
Expand Down
10 changes: 10 additions & 0 deletions test/files/neg/t5606.check
@@ -0,0 +1,10 @@
t5606.scala:3: error: identifier expected but '_' found.
case class CaseTest[_](someData: String)
^
t5606.scala:6: error: identifier expected but '_' found.
case class CaseTest2[_, _](someData: String)
^
t5606.scala:9: error: identifier expected but '_' found.
def f[_](x: Int) = ???
^
3 errors
23 changes: 23 additions & 0 deletions test/files/neg/t5606.scala
@@ -0,0 +1,23 @@
// scalac: -Xsource:3
// was: _ taken as ident of type param, but poor interactions below
case class CaseTest[_](someData: String)

// was: _ already defined
case class CaseTest2[_, _](someData: String)

class C {
def f[_](x: Int) = ???
}

object Test extends App {
def f0 = new CaseTest("X")
def f1: CaseTest[Int] = new CaseTest[Int]("X") // OK!
def f2: CaseTest[Int] = CaseTest[Int]("X") // CaseTest[Any]
def f3 = new CaseTest[Int]("X").copy() // CaseTest[Any]
def f4 = new CaseTest[Int]("X").copy[Int]() // CaseTest[Any]

def regress0[Option[_]] = 0
def regress1[Either[_, _]] = 1
//def regress0[Option[_$$1]] = 0;
//def regress1[Either[_$$2, _$$3]] = 1
}
15 changes: 15 additions & 0 deletions test/files/neg/t5606b.check
@@ -0,0 +1,15 @@
t5606b.scala:4: warning: Top-level wildcard is not allowed and will error under -Xsource:3
case class CaseTest[_](someData: String)
^
t5606b.scala:7: warning: Top-level wildcard is not allowed and will error under -Xsource:3
case class CaseTest2[_, _](someData: String)
^
t5606b.scala:7: warning: Top-level wildcard is not allowed and will error under -Xsource:3
case class CaseTest2[_, _](someData: String)
^
t5606b.scala:10: warning: Top-level wildcard is not allowed and will error under -Xsource:3
def f[_](x: Int) = ???
^
error: No warnings can be incurred under -Werror.
4 warnings
1 error
11 changes: 11 additions & 0 deletions test/files/neg/t5606b.scala
@@ -0,0 +1,11 @@
// scalac: -Xlint -Werror
//
// was: _ taken as ident of type param, now a fresh name
case class CaseTest[_](someData: String)

// was: _ already defined, now a fresh name
case class CaseTest2[_, _](someData: String)

class C {
def f[_](x: Int) = ???
}
8 changes: 1 addition & 7 deletions test/files/neg/trailing-commas.check
Expand Up @@ -61,15 +61,9 @@ trait TypeArgs { def f: C[Int, String, ] }
trailing-commas.scala:23: error: identifier expected but ']' found.
trait TypeParamClause { type C[A, B, ] }
^
trailing-commas.scala:23: error: ']' expected but '}' found.
trait TypeParamClause { type C[A, B, ] }
^
trailing-commas.scala:24: error: identifier expected but ']' found.
trait FunTypeParamClause { def f[A, B, ] }
^
trailing-commas.scala:24: error: ']' expected but '}' found.
trait FunTypeParamClause { def f[A, B, ] }
^
trailing-commas.scala:26: error: identifier expected but ')' found.
trait SimpleType { def f: (Int, String, ) }
^
Expand Down Expand Up @@ -127,4 +121,4 @@ trait SimpleType2 { def f: (Int, ) }
trailing-commas.scala:48: error: ')' expected but '}' found.
trait SimpleType2 { def f: (Int, ) }
^
43 errors
41 errors
14 changes: 7 additions & 7 deletions test/files/pos/t5606.scala
@@ -1,9 +1,9 @@
// was: _ taken as ident of type param, now a fresh name
case class CaseTest[_](someData: String)

// was: _ already defined, now a fresh name
case class CaseTest2[_, _](someData: String)







case class CaseTest[_](someData:String)
class C {
def f[_](x: Int) = ???
}

0 comments on commit 3699aea

Please sign in to comment.