scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.83k stars 1.05k forks source link

Trees after typer losing information #15750

Closed tgodzik closed 2 years ago

tgodzik commented 2 years ago

Compiler version

3.1.3

Minimized code

object Main {
  def foo: ArrayBuffer[Int] = ???
}

ArrayBuffer is missing from imports.

Output

Parser tree

PackageDef(
  pid = Ident(name = <empty>),
  stats = List(
    ModuleDef(
      name = Main,
      impl = Template(
        constr = DefDef(
          name = <init>,
          paramss = List(),
          tpt = TypeTree,
          preRhs = Thicket(trees = List())
        ),
        parentsOrDerived = List(),
        self = ValDef(name = _, tpt = Thicket(trees = List()), preRhs = Thicket(trees = List())),
        preBody = List(
          DefDef(
            name = foo,
            paramss = List(),
            tpt = AppliedTypeTree(tpt = Ident(name = ArrayBuffer), args = List(Ident(name = Int))),
            preRhs = Ident(name = ???)
          )
        )
      )
    )
  )
)

Notice tpt = AppliedTypeTree(tpt = Ident(name = ArrayBuffer), args = List(Ident(name = Int))), <- AppliedTypeTree is available.

Typed tree though:

 tree = TypeDef(
        name = Main$,
        rhs = Template(
          constr = DefDef(
            name = <init>,
            paramss = List(List()),
            tpt = TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Unit)],
            preRhs = Thicket(trees = List())
          ),
          parentsOrDerived = List(
            Apply(
              fun = Select(
                qualifier = New(
                  tpt = TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class Object)]
                ),
                name = <init>
              ),
              args = List()
            )
          ),
          self = ValDef(
            name = _,
            tpt = SingletonTypeTree(ref = Ident(name = Main)),
            preRhs = Thicket(trees = List())
          ),
          preBody = List(
            DefDef(
              name = foo,
              paramss = List(),
              tpt = Ident(name = ArrayBuffer),
              preRhs = Ident(name = ???)
            )
          )
        )
      ),

Notice that the tpt of the DefDef is tpt = Ident(name = ArrayBuffer),

Seems that if the type is not available then we are missing TypeApply, which is useful for the IDE to figure out if we need to insert []

Expectation

TypeApply should still be available in tpt of DefDef. It might be an error type, but it should be available there.

dwijnand commented 2 years ago

Something like this should do the trick:

--- compiler/src/dotty/tools/dotc/typer/Typer.scala
+++ compiler/src/dotty/tools/dotc/typer/Typer.scala
@@ -1993,7 +1993,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
       typed(tree.tpt, AnyTypeConstructorProto)
     }
     val tparams = tpt1.tpe.typeParams
-    if (tparams.isEmpty) {
+    if tpt1.tpe.isError then
+      val args1 = tree.args.mapconserve(typedType(_))
+      assignType(cpy.AppliedTypeTree(tree)(tpt1, args1), tpt1, args1)
+    else if (tparams.isEmpty) {
       report.error(TypeDoesNotTakeParameters(tpt1.tpe, tree.args), tree.srcPos)
       tpt1
     }
tgodzik commented 2 years ago

Looks like this is exactly what I am looking for, thanks! I can take a look and try and raise a PR with fix or would you want to do it?

dwijnand commented 2 years ago

All yours - I got curious to try, but not curious enough to babysit the PR into main. 😄

vzmerr commented 2 years ago

I invite you to take a look at the problem this issue has caused in Metals, for the detection of existing type parameters, so that an extra type bracket is not inserted upon type completion. https://github.com/scalameta/metals/pull/4174#discussion_r930605632

tgodzik commented 2 years ago

@dwijnand Seems like that did the trick! Thanks! https://github.com/lampepfl/dotty/pull/15825