Skip to content

Disallow _ for wildcard arguments of types and use ? #18809

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

Closed
Closed
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ object Feature:
val dependent = experimental("dependent")
val erasedDefinitions = experimental("erasedDefinitions")
val symbolLiterals = deprecated("symbolLiterals")
val underscoreWildcards = deprecated("underscoreWildcards")
val fewerBraces = experimental("fewerBraces")
val saferExceptions = experimental("saferExceptions")
val clauseInterleaving = experimental("clauseInterleaving")
Expand Down
9 changes: 6 additions & 3 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1856,9 +1856,12 @@ object Parsers {
val start = in.skipToken()
Ident(tpnme.USCOREkw).withSpan(Span(start, in.lastOffset, start))
else
if sourceVersion.isAtLeast(future) then
deprecationWarning(em"`_` is deprecated for wildcard arguments of types: use `?` instead")
patch(source, Span(in.offset, in.offset + 1), "?")
if !in.featureEnabled(Feature.underscoreWildcards) then
report.errorOrMigrationWarning(
em"`_` is deprecated for wildcard arguments of types: use `?` instead${rewriteNotice(`3.4-migration`)}",
in.sourcePos(), from = `3.4`)
if sourceVersion == `3.4-migration` then
patch(source, Span(in.offset, in.offset + 1), "?")
val start = in.skipToken()
typeBounds().withSpan(Span(start, in.lastOffset, start))
// Allow symbols -_ and +_ through for compatibility with code written using kind-projector in Scala 3 underscore mode.
Expand Down
5 changes: 2 additions & 3 deletions compiler/test-resources/repl/i13208.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
//> using options -source:future -deprecation
//> using options -language:`3.4-migration` -deprecation
scala> type M[X] = X match { case Int => String case _ => Int }
scala> type N[X] = X match { case List[_] => Int }
1 warning found
-- Deprecation Warning: --------------------------------------------------------
-- Error: ----------------------------------------------------------------------
1 | type N[X] = X match { case List[_] => Int }
| ^
| `_` is deprecated for wildcard arguments of types: use `?` instead
2 changes: 1 addition & 1 deletion compiler/test-resources/repl/i6643
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ scala> import scala.collection._
scala>:type 1
Int

scala> object IterableTest { def g[CC[_] <: Iterable[_] with IterableOps[_, _, _]](from: CC[Int]): IterableFactory[CC] = ??? }
scala> object IterableTest { def g[CC[_] <: Iterable[?] with IterableOps[?, ?, ?]](from: CC[Int]): IterableFactory[CC] = ??? }
// defined object IterableTest
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ class DottyBytecodeTests extends DottyBytecodeTest {
| def test =
| try print("foo")
| catch {
| case _: scala.runtime.NonLocalReturnControl[_] => ()
| case _: scala.runtime.NonLocalReturnControl[?] => ()
| }
|}
""".stripMargin
Expand Down
12 changes: 6 additions & 6 deletions library/src/scala/Tuple.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,25 +97,25 @@ object Tuple {

/** Type of the head of a tuple */
type Head[X <: NonEmptyTuple] = X match {
case x *: _ => x
case x *: xs => x
}

/** Type of the initial part of the tuple without its last element */
type Init[X <: Tuple] <: Tuple = X match {
case _ *: EmptyTuple => EmptyTuple
case x *: EmptyTuple => EmptyTuple
case x *: xs =>
x *: Init[xs]
}

/** Type of the tail of a tuple */
type Tail[X <: NonEmptyTuple] <: Tuple = X match {
case _ *: xs => xs
case x *: xs => xs
}

/** Type of the last element of a tuple */
type Last[X <: Tuple] = X match {
case x *: EmptyTuple => x
case _ *: xs => Last[xs]
case x *: xs => Last[xs]
}

/** Type of the concatenation of two tuples */
Expand Down Expand Up @@ -182,8 +182,8 @@ object Tuple {
*/
type Zip[T1 <: Tuple, T2 <: Tuple] <: Tuple = (T1, T2) match {
case (h1 *: t1, h2 *: t2) => (h1, h2) *: Zip[t1, t2]
case (EmptyTuple, _) => EmptyTuple
case (_, EmptyTuple) => EmptyTuple
case (EmptyTuple, ?) => EmptyTuple
case (?, EmptyTuple) => EmptyTuple
case _ => Tuple
}

Expand Down
4 changes: 4 additions & 0 deletions library/src/scala/runtime/stdLibPatches/language.scala
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ object language:
*/
@compileTimeOnly("`symbolLiterals` can only be used at compile time in import statements")
object symbolLiterals

/** TODO */
@compileTimeOnly("`underscoreWildcards` can only be used at compile time in import statements")
object underscoreWildcards
end deprecated

/** Where imported, auto-tupling is disabled.
Expand Down
7 changes: 6 additions & 1 deletion project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1008,7 +1008,10 @@ object Build {
Seq("-sourcepath", ((Compile/sourceManaged).value / "scala-library-src").toString)
},
Compile / doc / scalacOptions += "-Ydocument-synthetic-types",
scalacOptions += "-Ycompile-scala2-library",
scalacOptions ++= Seq(
"-Ycompile-scala2-library",
"-language:deprecated.underscoreWildcards",
),
scalacOptions -= "-Xfatal-warnings",
ivyConfigurations += SourceDeps.hide,
transitiveClassifiers := Seq("sources"),
Expand Down Expand Up @@ -1292,6 +1295,7 @@ object Build {
mtagsSharedSources
} (Set(mtagsSharedSourceJar)).toSeq
}.taskValue,
scalacOptions += "-language:deprecated.underscoreWildcards",
)
}

Expand Down Expand Up @@ -1361,6 +1365,7 @@ object Build {
dependsOn(`scala3-library-bootstrappedJS`).
settings(
bspEnabled := false,
scalacOptions += "-language:deprecated.underscoreWildcards",
scalacOptions --= Seq("-Xfatal-warnings", "-deprecation"),

// Required to run Scala.js tests.
Expand Down
3 changes: 0 additions & 3 deletions sbt-test/compilerReporter/i14576/Test.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,3 @@ object Test:

// private[this] and = _ are deprecated under -source:future
private[this] var x: AnyRef = _

// under -source:future, `_` is deprecated for wildcard arguments of types: use `?` instead
val xs: List[_] = Nil
2 changes: 1 addition & 1 deletion sbt-test/compilerReporter/i14576/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ lazy val root = (project in file("."))
},
assertDeprecationSummary := {
assert {
FakePrintWriter.messages.exists(_.contains("there were 3 deprecation warnings; re-run with -deprecation for details"))
FakePrintWriter.messages.exists(_.contains("there were 2 deprecation warnings; re-run with -deprecation for details"))
}
},
assertNoDeprecationSummary := {
Expand Down
66 changes: 33 additions & 33 deletions sbt-test/scala2-compat/erasure/dottyApp/Api.scala
Original file line number Diff line number Diff line change
Expand Up @@ -156,41 +156,41 @@ class Z {
def int_63(x: AnyVal with Int): Unit = {}

def intARRAY_64(x: Array[Int with Singleton]): Unit = {}
def intARRAY_65(x: Array[_ <: Int]): Unit = {}
def intARRAY_66(x: Array[_ <: Int with Singleton]): Unit = {}
def intARRAY_67(x: Array[_ <: Singleton with Int]): Unit = {}
def intARRAY_68(x: Array[_ <: Int with Any]): Unit = {}
def intARRAY_69(x: Array[_ <: Any with Int]): Unit = {}
def intARRAY_70(x: Array[_ <: Int with AnyVal]): Unit = {}
def intARRAY_71(x: Array[_ <: AnyVal with Int]): Unit = {}
def intARRAY_71a(x: Array[_ <: Int | Int]): Unit = {}
def intARRAY_71b(x: Array[_ <: 1 | 2]): Unit = {}
def intARRAY_65(x: Array[? <: Int]): Unit = {}
def intARRAY_66(x: Array[? <: Int with Singleton]): Unit = {}
def intARRAY_67(x: Array[? <: Singleton with Int]): Unit = {}
def intARRAY_68(x: Array[? <: Int with Any]): Unit = {}
def intARRAY_69(x: Array[? <: Any with Int]): Unit = {}
def intARRAY_70(x: Array[? <: Int with AnyVal]): Unit = {}
def intARRAY_71(x: Array[? <: AnyVal with Int]): Unit = {}
def intARRAY_71a(x: Array[? <: Int | Int]): Unit = {}
def intARRAY_71b(x: Array[? <: 1 | 2]): Unit = {}

def stringARRAY_72(x: Array[String with Singleton]): Unit = {}
def stringARRAY_73(x: Array[_ <: String]): Unit = {}
def stringARRAY_74(x: Array[_ <: String with Singleton]): Unit = {}
def stringARRAY_75(x: Array[_ <: Singleton with String]): Unit = {}
def stringARRAY_76(x: Array[_ <: String with Any]): Unit = {}
def stringARRAY_77(x: Array[_ <: Any with String]): Unit = {}
def stringARRAY_78(x: Array[_ <: String with AnyRef]): Unit = {}
def stringARRAY_79(x: Array[_ <: AnyRef with String]): Unit = {}
def stringARRAY_79a(x: Array[_ <: String | String]): Unit = {}
def stringARRAY_79b(x: Array[_ <: "a" | "b"]): Unit = {}

def object_80(x: Array[_ <: Singleton]): Unit = {}
def object_81(x: Array[_ <: AnyVal]): Unit = {}
def objectARRAY_82(x: Array[_ <: AnyRef]): Unit = {}
def object_83(x: Array[_ <: Any]): Unit = {}
def object_83a(x: Array[_ <: Matchable]): Unit = {}
def object_83b(x: Array[_ <: Int | Double]): Unit = {}
def object_83c(x: Array[_ <: String | Int]): Unit = {}
def object_83d(x: Array[_ <: Int | Matchable]): Unit = {}
def object_83e(x: Array[_ <: AnyRef | AnyVal]): Unit = {}

def serializableARRAY_84(x: Array[_ <: Serializable]): Unit = {}
def univARRAY_85(x: Array[_ <: Univ]): Unit = {}
def aARRAY_86(x: Array[_ <: A]): Unit = {}
def aARRAY_87(x: Array[_ <: A with B]): Unit = {}
def stringARRAY_73(x: Array[? <: String]): Unit = {}
def stringARRAY_74(x: Array[? <: String with Singleton]): Unit = {}
def stringARRAY_75(x: Array[? <: Singleton with String]): Unit = {}
def stringARRAY_76(x: Array[? <: String with Any]): Unit = {}
def stringARRAY_77(x: Array[? <: Any with String]): Unit = {}
def stringARRAY_78(x: Array[? <: String with AnyRef]): Unit = {}
def stringARRAY_79(x: Array[? <: AnyRef with String]): Unit = {}
def stringARRAY_79a(x: Array[? <: String | String]): Unit = {}
def stringARRAY_79b(x: Array[? <: "a" | "b"]): Unit = {}

def object_80(x: Array[? <: Singleton]): Unit = {}
def object_81(x: Array[? <: AnyVal]): Unit = {}
def objectARRAY_82(x: Array[? <: AnyRef]): Unit = {}
def object_83(x: Array[? <: Any]): Unit = {}
def object_83a(x: Array[? <: Matchable]): Unit = {}
def object_83b(x: Array[? <: Int | Double]): Unit = {}
def object_83c(x: Array[? <: String | Int]): Unit = {}
def object_83d(x: Array[? <: Int | Matchable]): Unit = {}
def object_83e(x: Array[? <: AnyRef | AnyVal]): Unit = {}

def serializableARRAY_84(x: Array[? <: Serializable]): Unit = {}
def univARRAY_85(x: Array[? <: Univ]): Unit = {}
def aARRAY_86(x: Array[? <: A]): Unit = {}
def aARRAY_87(x: Array[? <: A with B]): Unit = {}

def objectARRAY_88(x: Array[Any]): Unit = {}
def objectARRAY_89(x: Array[AnyRef]): Unit = {}
Expand Down
2 changes: 1 addition & 1 deletion sbt-test/scala2-compat/i11173/app/App.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import i11173.Bar

def test(x: Bar[_]): Unit = ()
def test(x: Bar[?]): Unit = ()
2 changes: 1 addition & 1 deletion scaladoc-testcases/src/tests/exports1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class A: //unexpected
= 1
type HKT[T[_], X] //expected: final type HKT = [T[_], X] =>> a.HKT[T, X]
= T[X]
type SomeRandomType = (List[_] | Seq[_]) & String //expected: final type SomeRandomType = a.SomeRandomType
type SomeRandomType = (List[?] | Seq[?]) & String //expected: final type SomeRandomType = a.SomeRandomType
def x[T[_], X](x: X): HKT[T, X] //expected: def x[T[_], X](x: X): A.this.HKT[T, X]
= ???
def fn[T, U]: T => U
Expand Down
2 changes: 1 addition & 1 deletion scaladoc-testcases/src/tests/hkts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ trait Case14[C[_]]
class SomeClass extends Case14[List]


def method1[E, T](value: List[_ >: E]): Int = 0
def method1[E, T](value: List[? >: E]): Int = 0
def method2[F[+X] <: Option[X], A](fa: F[A]): A = fa.get

import scala.collection.immutable.ArraySeq
Expand Down
2 changes: 1 addition & 1 deletion scaladoc-testcases/src/tests/snippetTestcase2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package tests
package snippetTestcase2

trait Quotes2[A] {
val r1: r1Module[_] = ???
val r1: r1Module[?] = ???
trait r1Module[A] {
type X
object Y {
Expand Down
2 changes: 1 addition & 1 deletion scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -133,5 +133,5 @@ class ScaladocSettings extends SettingGroup with AllScalaSettings:
"List of quick links that is displayed in the header of documentation."
)

def scaladocSpecificSettings: Set[Setting[_]] =
def scaladocSpecificSettings: Set[Setting[?]] =
Set(sourceLinks, legacySourceLink, syntax, revision, externalDocumentationMappings, socialLinks, skipById, skipByRegex, deprecatedSkipPackages, docRootContent, snippetCompiler, generateInkuire, defaultTemplate, scastieConfiguration, quickLinks)
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class StaticSiteLoader(val root: File, val args: Scaladoc.Args)(using StaticSite
(("1900","01","01"), name)

def dateFrom(tf: TemplateFile, default: String = "1900-01-01"): String =
val pageSettings = tf.settings.get("page").collect{ case m: Map[String @unchecked, _] => m }
val pageSettings = tf.settings.get("page").collect{ case m: Map[String @unchecked, ?] => m }
pageSettings.flatMap(_.get("date").collect{ case s: String => s}).getOrElse(default) // blogs without date are last

val posts = List(rootPath.resolve("_posts"))
Expand Down
2 changes: 1 addition & 1 deletion scaladoc/src/dotty/tools/scaladoc/site/common.scala
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def loadTemplateFile(file: File, defaultTitle: Option[TemplateName] = None)(usin
}.map(_.stripPrefix("\"").stripSuffix("\""))

def listSetting(settings: Map[String, Object], name: String): Option[List[String]] = settings.get(name).map {
case elems: List[_] => elems.zipWithIndex.map {
case elems: List[?] => elems.zipWithIndex.map {
case (s: String, _) => s
case (other, index) =>
throw new RuntimeException(s"Expected a string at index $index for $name in $file but got $other")
Expand Down
4 changes: 2 additions & 2 deletions scaladoc/src/dotty/tools/scaladoc/site/templates.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ case class TemplateFile(
)

def asJavaElement(o: Object): Object = o match
case m: Map[_, _] => m.transform {
case m: Map[?, ?] => m.transform {
case (k: String, v: Object) => asJavaElement(v)
}.asJava
case l: List[_] => l.map(x => asJavaElement(x.asInstanceOf[Object])).asJava
case l: List[?] => l.map(x => asJavaElement(x.asInstanceOf[Object])).asJava
case other => other

// Library requires mutable maps..
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import dotty.tools.dotc.util.{ SourcePosition, NoSourcePosition, SourceFile, NoS
import scala.util.{ Try, Success, Failure }

class SnippetCompiler(
val snippetCompilerSettings: Seq[SnippetCompilerSetting[_]],
val snippetCompilerSettings: Seq[SnippetCompilerSetting[?]],
target: AbstractFile = new VirtualDirectory("(memory)")
):
object SnippetDriver extends Driver:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ case class DocFlexmarkRenderer(renderLink: (DocLink, String) => String)
html.raw(renderLink(node.target, node.body))

object Render extends NodeRenderer:
override def getNodeRenderingHandlers: JSet[NodeRenderingHandler[_]] =
override def getNodeRenderingHandlers: JSet[NodeRenderingHandler[?]] =
JSet(
new NodeRenderingHandler(classOf[DocLinkNode], Handler),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ object SectionRenderingExtension extends HtmlRenderer.HtmlRendererExtension:


object Render extends NodeRenderer:
override def getNodeRenderingHandlers: JSet[NodeRenderingHandler[_]] =
override def getNodeRenderingHandlers: JSet[NodeRenderingHandler[?]] =
JSet(
new NodeRenderingHandler(classOf[Section], SectionHandler),
new NodeRenderingHandler(classOf[AnchorLink], AnchorLinkHandler)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ object SnippetRenderingExtension extends HtmlRenderer.HtmlRendererExtension:
html.raw(SnippetRenderer.renderSnippet(node.getContentChars.toString, node.getInfo.toString.split(" ").headOption))

object Render extends NodeRenderer:
override def getNodeRenderingHandlers: JSet[NodeRenderingHandler[_]] =
override def getNodeRenderingHandlers: JSet[NodeRenderingHandler[?]] =
JSet(
new NodeRenderingHandler(classOf[ExtendedFencedCodeBlock], ExtendedFencedCodeBlockHandler),
new NodeRenderingHandler(classOf[FencedCodeBlock], FencedCodeBlockHandler)
Expand Down
2 changes: 1 addition & 1 deletion staging/src/scala/quoted/staging/ExprCompilationUnit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ import dotty.tools.dotc.CompilationUnit
import dotty.tools.dotc.util.NoSource

/** Compilation unit containing the contents of a quoted expression */
private class ExprCompilationUnit(val exprBuilder: Quotes => Expr[_]) extends CompilationUnit(NoSource)
private class ExprCompilationUnit(val exprBuilder: Quotes => Expr[?]) extends CompilationUnit(NoSource)
2 changes: 1 addition & 1 deletion staging/src/scala/quoted/staging/QuoteCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ private class QuoteCompiler extends Compiler:
/** Unpickle and optionally compile the expression.
* Returns either `Left` with name of the classfile generated or `Right` with the value contained in the expression.
*/
def compileExpr(exprBuilder: Quotes => Expr[_]): Either[String, Any] =
def compileExpr(exprBuilder: Quotes => Expr[?]): Either[String, Any] =
val units = new ExprCompilationUnit(exprBuilder) :: Nil
compileUnits(units)
result
Expand Down
2 changes: 1 addition & 1 deletion tests/bench/inductive-implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ package shapeless {
def ::[HH](h : HH) : HH :: H :: T = shapeless.::(h, this)

override def toString = head match {
case _: ::[_, _] => "("+head.toString+") :: "+tail.toString
case _: ::[?, ?] => "("+head.toString+") :: "+tail.toString
case _ => head.toString+" :: "+tail.toString
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/explicit-nulls/pos/array.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class Foo {

def test = {
// accept any array of string
def f(xs: Array[_ >: String <: String | Null] | Null): Unit = ???
def f(xs: Array[? >: String <: String | Null] | Null): Unit = ???

val a1: Array[String] = ???
val a2: Array[String] | Null = ???
Expand Down
4 changes: 2 additions & 2 deletions tests/explicit-nulls/pos/flow-inline.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ class TreeOps {
abstract class Tree[A, B](val key: A, val value: B)
class RedTree[A, B](override val key: A, override val value: B) extends Tree[A, B](key, value)

private transparent inline def isRedTree(tree: Tree[_, _] | Null) =
(tree != null) && tree.isInstanceOf[RedTree[_, _]]
private transparent inline def isRedTree(tree: Tree[?, ?] | Null) =
(tree != null) && tree.isInstanceOf[RedTree[?, ?]]

def foo[A, B](tree: Tree[A, B] | Null): Unit = {
if (isRedTree(tree)) {
Expand Down
8 changes: 4 additions & 4 deletions tests/generic-java-signatures/arrayBound.scala
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
class Foo[
T <: Array[_],
T <: Array[?],
U <: Array[T],
V <: java.util.List[Array[T]],
W <: java.util.List[_ <: java.util.Date],
X <: java.util.HashMap[Array[_], java.util.ArrayList[_ <: java.util.Date]],
W <: java.util.List[? <: java.util.Date],
X <: java.util.HashMap[Array[?], java.util.ArrayList[? <: java.util.Date]],
T1,
U1 <: Array[T1],
V1 <: Array[Array[T1]]
]
object Test {
def main(args: Array[String]): Unit = {
val tParams = classOf[Foo[_, _, _, _, _, _, _, _]].getTypeParameters()
val tParams = classOf[Foo[?, ?, ?, ?, ?, ?, ?, ?]].getTypeParameters()
tParams.foreach { tp =>
println(tp.getName + " <: " + tp.getBounds.map(_.getTypeName).mkString(", "))
}
Expand Down
Loading