Skip to content

Well-typed expression in quote fails to re-typecheck during inlining #23423

Closed
@smarter

Description

@smarter

Compiler version

Regressed in 3.6.4-RC1 (with 26ecda540b9), present until today (3.7.2-RC1).

Minimized code

A_1.scala:

package pkg

import scala.quoted.*

trait HasElem {
  type Elem
  type Alias = Elem
}

object Macro:
  inline def foo: Unit = ${fooImpl}
  def fooImpl(using Quotes): Expr[Unit] =
    '{
      val lll: (he: HasElem) => he.Alias =
        (hx: HasElem) => ???
    }

B_2.scala:

object Test:
  def test: Unit = pkg.Macro.foo

Output

[error] ./try/qb/B_2.scala:2:20
[error] Found:    (hx: pkg.HasElem) => hx.Elem
[error] Required: (he: pkg.HasElem) => he.Elem
[error]   def test: Unit = pkg.Macro.foo
[error]                    ^^^^^^^^^^^^^

When compiling with -Ycheck:all:

checking /home/smarter/opt/dotty/try/qb/A_1.scala after phase staging
*** error while checking /home/smarter/opt/dotty/try/qb/A_1.scala after phase staging ***

  unhandled exception while running Ycheck on /home/smarter/opt/dotty/try/qb/A_1.scala

  An unhandled exception was thrown in the compiler.
  Please file a crash report here:
  https://github.com/scala/scala3/issues/new/choose
  For non-enriched exceptions, compile with -Xno-enrich-error-messages.


     while compiling: /home/smarter/opt/dotty/try/qb/A_1.scala
        during phase: Ycheck
                mode: Mode(ImplicitsEnabled)
     library version: version 2.13.16
    compiler version: version 3.7.2-RC1
            settings: -Ycheck List(all) -bootclasspath /home/smarter/.cache/coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.7.2-RC1/scala3-library_3-3.7.2-RC1.jar:/home/smarter/.cache/coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.16/scala-library-2.13.16.jar -classpath /home/smarter/opt/dotty/try/qb/.scala-build/.bloop/qb_602a3656b0-ef55e4b6ab/bloop-internal-classes/main-irbvWweBRRmK1j4mRX8ZSg==:/home/smarter/opt/dotty/try/qb/.scala-build/qb_602a3656b0-ef55e4b6ab/classes/classes-empty-qb_602a3656b0-ef55e4b6ab -d /home/smarter/opt/dotty/try/qb/.scala-build/.bloop/qb_602a3656b0-ef55e4b6ab/bloop-internal-classes/main-irbvWweBRRmK1j4mRX8ZSg== -sourceroot /home/smarter/opt/dotty/try/qb


Error compiling project (Scala 3.7.2-RC1, JVM (17))
Error: java.lang.AssertionError: assertion failed: Type Mismatch (while checking typedUnadapted):
Found:    (hx: pkg.HasElem) => hx.Elem
Required: (hx: pkg.HasElem) => hx.Alias
I tried to show that
  (hx: pkg.HasElem) => hx.Elem
conforms to
  (hx: pkg.HasElem) => hx.Alias
but none of the attempts shown below succeeded:

  ==> (hx: pkg.HasElem) => hx.Elem  <:  (hx: pkg.HasElem) => hx.Alias
    ==> (hx: pkg.HasElem) => hx.Elem  <:  pkg.HasElem => pkg.HasElem#Alias
      ==> pkg.HasElem => pkg.HasElem#Elem  <:  pkg.HasElem => pkg.HasElem#Alias
        ==> pkg.HasElem#Elem  <:  pkg.HasElem#Alias
          ==> Any  <:  pkg.HasElem#Alias  = false

The tests were made under the empty constraint
tree = closure($anonfun) Closure

Expectation

Code that typechecks should not break in a later phase. This regressed after 26ecda540b9 which made the following change impact when asSeenFrom decides to approximate a type:

diff --git compiler/src/dotty/tools/dotc/core/TypeOps.scala compiler/src/dotty/tools/dotc/core/TypeOps.scala
index 2403a6e22b..c27b3c3978 100644
--- compiler/src/dotty/tools/dotc/core/TypeOps.scala
+++ compiler/src/dotty/tools/dotc/core/TypeOps.scala
@@ -124,7 +124,7 @@ object TypeOps:
   }

   def isLegalPrefix(pre: Type)(using Context): Boolean =
-    pre.isStable || !ctx.phase.isTyper
+    pre.isStable

   /** Implementation of Types#simplified */
   def simplify(tp: Type, theMap: SimplifyMap | Null)(using Context): Type = {

Reverting that change fixes this issue, but breaks the test cases from 26ecda540b9 like tests/pos/i17222.8.scala From the original PR I found #22653 which shows it was reverted in 3.6.4 final (and indeed my test case works in 3.6.4 final), but this was only done in the 3.6 branch and I haven't understood why, do you remember @WojciechMazur @dwijnand ?

I can also fix this issue by removing an unnecessary de-aliasing in the staging phase:

diff --git compiler/src/dotty/tools/dotc/staging/HealType.scala compiler/src/dotty/tools/dotc/staging/HealType.scala
index a73f884fba..fc7c56b7b5 100644
--- compiler/src/dotty/tools/dotc/staging/HealType.scala
+++ compiler/src/dotty/tools/dotc/staging/HealType.scala
@@ -32,7 +32,10 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap {
    */
   def apply(tp: Type): Type =
     tp match
-      case NonSpliceAlias(aliased) => this.apply(aliased)
+      case NonSpliceAlias(aliased) =>
+        val aliased1 = this.apply(aliased)
+        if aliased1 ne aliased then aliased1
+        else tp
       case tp: TypeRef => healTypeRef(tp)
       case tp: TermRef =>
         val inconsistentRoot = levelInconsistentRootOfPath(tp)

Which is not a bad change by itself, but I'm afraid we'll find other problematic and hard-to-debug cases due to this recent behavior of isLegalPrefix.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions