Skip to content

skolemize singleton-typed existentials and improve error message #12208

Open
@adriaanm

Description

@adriaanm

(Discovered while pulling on the thread exposed by https://issues.scala-lang.org/browse/SI-9899)

Consider

class C {
  val p: (A, A => Unit) forSome {type A} = (1, println(_: Int))

//  def unpack[T](p: (T, T => Unit)): Unit = p._2(p._1)
//  unpack(p)
//
//  p match { case (a, f) => f(a) }

  p._2(p._1) // we get an error, but it could be clearer: 
// p._1 has type A&0 (obtained by skolemizing p's type and calling _1)
// p._2 has type A&1 => Unit (obtained by skolemizing p's type *again* and calling _2)
}

2.12.0-M5's error message reveals that we are not skolemizing p's existential type:

 found   : C.this.p._1.type (with underlying type A)
 required: A
  p._2(p._1)
         ^

The type checker does not skolemize p's type because it is the singleton type p.type.

The fix:

diff --git i/src/compiler/scala/tools/nsc/typechecker/Typers.scala w/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 9d273ce..a67ac2a 100644
--- i/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ w/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1168,8 +1168,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
           adapt(tree setType restpe, mode, pt, original)
         case TypeRef(_, ByNameParamClass, arg :: Nil) if mode.inExprMode => // (2)
           adapt(tree setType arg, mode, pt, original)
-        case tp if mode.typingExprNotLhs && isExistentialType(tp) && !isSyntheticAccessor(context.owner) =>
-          adapt(tree setType tp.dealias.skolemizeExistential(context.owner, tree), mode, pt, original)
+        case tp if mode.typingExprNotLhs && isExistentialType(tp.dealiasWiden) && !isSyntheticAccessor(context.owner) =>
+          adapt(tree setType tp.dealiasWiden.skolemizeExistential(context.owner, tree), mode, pt, original)
         case PolyType(tparams, restpe) if mode.inNone(TAPPmode | PATTERNmode) && !context.inTypeConstructorAllowed => // (3)
           // assert((mode & HKmode) == 0) //@M a PolyType in HKmode represents an anonymous type function,
           // we're in HKmode since a higher-kinded type is expected --> hence, don't implicitly apply it to type params!

We now get a more accurate, but more confusing error:

/Users/adriaan/Library/Preferences/IdeaIC2016.2/scratches/scratch_12.scala:9: error: type mismatch;
 found   : ((some other)A(in value <local C>), (some other)A(in value <local C>) => Unit)#_1.type (with underlying type (some other)A(in value <local C>)) where type (some other)A(in value <local C>)
 required: A(in value <local C>) where type A(in value <local C>)
  p._2(p._1)
         ^

It would read a lot better if:

/Users/adriaan/Library/Preferences/IdeaIC2016.2/scratches/scratch_12.scala:9: error: type mismatch;
 found   : (A&0, A&0 => Unit)#_1.type (with underlying type A&0) 
 required: A&1
(Note that A&0 and A&1 arose from skolemizing p's existential type in the expressions p._2 and p._1)
  p._2(p._1)
         ^

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions