Skip to content

dropExistential insufficient when an existential quantifier is bounded by another #11363

Open
@retronym

Description

@retronym

The typer substitutes bounded wildcard types for the quantifiers of existential expected types (in non-pattern mode.)

Currently, dropExistential is implemented in terms of SubstWildcardMap.

However, as we see below, this seems too shallow, and does not descend into the info the the quantifiers themselves.

scala> class X { def foo = { class C; new { def c: C = ??? } } }
warning: there was one feature warning; re-run with -feature for details
defined class X

scala> val et @ ExistentialType(qs, ul) = typeOf[X].decls.last.info.finalResultType
et: $r.intp.global.ExistentialType = $anon forSome { type $anon <: AnyRef{def c: C}; type C <: AnyRef }
qs: List[$r.intp.global.Symbol] = List(type $anon, type C)
ul: $r.intp.global.Type = $anon

scala> new SubstWildcardMap(qs).apply(et)
res11: $r.intp.global.Type = ? <: AnyRef{def c: C} forSome { type $anon <: AnyRef{def c: C}; type C <: AnyRef }

I believe something like this is needed:

  def dropExistential(tp: Type): Type = tp match {
    case ExistentialType(tparams, tpe) =>
      val tparams1 = cloneSymbols(tparams)
      val subst1 = new SubstWildcardMap(tparams1)
      tparams1 foreach (_.modifyInfo(subst1.apply))
      val result = ExistentialType(tparams1, subst1.apply(tpe.substituteSymbols(tparams, tparams1)))
      result
    case TypeRef(_, sym, _) if sym.isAliasType =>
      val tp0 = tp.dealias
      if (tp eq tp0) {
        devWarning(s"dropExistential did not progress dealiasing $tp, see SI-7126")
        tp
      } else {
        val tp1 = dropExistential(tp0)
        if (tp1 eq tp0) tp else tp1
      }
    case _ => tp
  }

I believe this will be the correct fix for pos/existentials-harmful.scala, rather than the workaround in scala/scala@7a6fa80.

Here's a current manifestation of the bug in the REPL:

scala> { class C; new { def c: C = ??? } }
warning: there was one feature warning; re-run with -feature for details
<console>:5: error: type mismatch;
 found   : $anon(in lazy value $result) where type $anon(in lazy value $result) <: AnyRef{def c: C}
 required: (some other)$anon(in lazy value $result) forSome { type (some other)$anon(in lazy value $result) <: AnyRef{def c: C}; type C <: AnyRef }
  lazy val $result = res0
                                          ^
<console>:5: error: type mismatch;
 found   : $anon(in value $result) where type $anon(in value $result) <: AnyRef{def c: C}
 required: $anon(in lazy value $result) forSome { type $anon(in lazy value $result) <: AnyRef{def c: C}; type C <: AnyRef }
  lazy val $result = res0
           ^

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions