Skip to content
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

Regression in Scala 2.13.7 - cannot use wildcard for F-bounded type when using OptManifest #12481

Closed
Sciss opened this issue Nov 3, 2021 · 13 comments · Fixed by scala/scala#9806
Assignees
Milestone

Comments

@Sciss
Copy link

Sciss commented Nov 3, 2021

reproduction steps

using Scala 2.13.6 (and all before), the following works ( https://scastie.scala-lang.org/NkHsooECRGqIgttwtvkgMg )

trait Txn[T <: Txn[T]]

trait Universe[T <: Txn[T]]

object Ref {
  def apply[A](init: A)(implicit om: OptManifest[A]): Ref[A] = ???
}
trait Ref[A]

trait Handler {
  def all: Ref[Vector[Universe[_]]] = Ref(Vector.empty)   // here
}

In Scala 2.13.7, the marked line produces an error

type arguments [_$1] do not conform to trait Universe's type parameter bounds [T <: Txn[T]]
      def all: Ref[Vector[Universe[_]]] = Ref(Vector.empty)

This is somehow caused by the OptManifest argument to the Ref constructor. If I remove that implicit argument, it compiles again.

@Sciss
Copy link
Author

Sciss commented Nov 3, 2021

Note that I can write in this case def all: Ref[Vector[Universe[_]]] = Ref(Vector.empty)(NoManifest) as a work-around, since the class manifest is not strictly needed here.

@som-snytt
Copy link

I'll take a look. Compare scalacenter/scalafix#1493

@som-snytt
Copy link

I guess it involves scala/scala#9776 and not wildcard representation. An extra check is introduced.

@SethTisue
Copy link
Member

attn @lrytz

@som-snytt
Copy link

som-snytt commented Nov 3, 2021

I'll let lrytz say if it is simply correct, which I guess it is. The manifest seeks to summon Class[Vector[Universe[_]]]. I've misplaced my debug output. The test shows the correctly conforming case.

test at scala/scala#9802

@lrytz lrytz added this to the 2.13.8 milestone Nov 4, 2021
@lrytz
Copy link
Member

lrytz commented Nov 4, 2021

Thank you @som-snytt - I think you're right

  def m1[T <: Txn[T]] = optManifest[Universe[T]] // ok
  def m2 = optManifest[Universe[_]] // type arguments [_$2] do not conform to trait Universe's type parameter bounds [T <: Txn[T]]

@lrytz
Copy link
Member

lrytz commented Nov 4, 2021

Though i'm not all that sure how wildcards are working / supposed to work. def t: Universe[_] = ??? is ok. Writing out the code after -Vprint:typer explicitly type checks.

def t: Ref[Vector[Universe[_]]] = Ref.apply[Vector[Universe[_]]](scala.Vector.empty[Nothing])(scala.reflect.ClassManifestFactory.classType[Vector[Universe[_]]](classOf[scala.collection.immutable.Vector[_]], scala.reflect.ClassManifestFactory.classType[Universe[_]](classOf[Universe[_]], scala.reflect.NoManifest)))

@Jasper-M
Copy link
Member

Jasper-M commented Nov 4, 2021

Works in Scala 3, by the way. But their wildcards are generally better at ignoring stuff, as demonstrated in the possibly somewhat related #8039.

@som-snytt
Copy link

Yes, I noticed that dotty is nicer with special wildcard name handling. I expected to need some name-based extraction somewhere. But per Lukas's comment, I tried to induce explicit failure, but it was something else leaking when summoning the manifest, which is not merely implicit but "internal" or "magical". Also, paulp's "rocket science" dig is motivational, I don't remember why it didn't come up on the underscore ticket.

BTW both PRs landed about the same time, which is why I assumed the underscore ticket was causative. I didn't actually bisect.

@joroKr21
Copy link
Member

joroKr21 commented Nov 6, 2021

The main code path where checkTypeRef is used takes care of converting existential types to wildcards:
https://github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala#L1758-L1780

            val existentialParams = new ListBuffer[Symbol]
            var skipBounds = false
            // check all bounds, except those that are existential type parameters
            // or those within typed annotated with @uncheckedBounds
            if (!inPattern) tree.tpe foreach {
              case tp @ ExistentialType(tparams, tpe) =>
                existentialParams ++= tparams
              case ann: AnnotatedType if ann.hasAnnotation(UncheckedBoundsClass) =>
                // scala/bug#7694 Allow code synthesizers to disable checking of bounds for TypeTrees based on inferred LUBs
                // which might not conform to the constraints.
                skipBounds = true
              case tp: TypeRef =>
                val tpWithWildcards = deriveTypeWithWildcards(existentialParams.toList)(tp)
                checkTypeRef(tpWithWildcards, tree, skipBounds)
              case _ =>
            }
            if (skipBounds) {
              tree.setType(tree.tpe.map {
                _.filterAnnotations(_.symbol != UncheckedBoundsClass)
              })
            }

            tree

@joroKr21
Copy link
Member

joroKr21 commented Nov 6, 2021

TIL that this doesn't complain 🤔

  trait Foo[A <: Int]
  class bar[A] extends StaticAnnotation
  @bar[Foo[String]]
  val x = 42

@joroKr21
Copy link
Member

joroKr21 commented Nov 6, 2021

Also this:

  trait Foo[A <: Int]
  trait Bar[A, B]
  val x: Bar[Foo[String] @uncheckedBounds, Foo[String]] = null

@SethTisue
Copy link
Member

SethTisue commented Nov 11, 2021

(leaving it on the 2.13.8 milestone, because no released version of 2.12 had the bug)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants