Skip to content

Fix #2146: Make nested implicit function types work #2235

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

Merged
merged 4 commits into from
Apr 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/NameKinds.scala
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ object NameKinds {
val ProtectedAccessorName = new PrefixNameKind(PROTECTEDACCESSOR, "protected$")
val ProtectedSetterName = new PrefixNameKind(PROTECTEDSETTER, "protected$set") // dubious encoding, kept for Scala2 compatibility
val AvoidClashName = new SuffixNameKind(AVOIDCLASH, "$_avoid_name_clash_$")
val DirectName = new SuffixNameKind(DIRECT, "$direct")
val DirectMethodName = new SuffixNameKind(DIRECT, "$direct") { override def definesNewName = true }
val FieldName = new SuffixNameKind(FIELD, "$$local")
val ExtMethName = new SuffixNameKind(EXTMETH, "$extension")
val ModuleVarName = new SuffixNameKind(OBJECTVAR, "$module")
Expand Down
14 changes: 7 additions & 7 deletions compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import core.Decorators._
import core.StdNames.nme
import core.Names._
import core.NameOps._
import core.NameKinds.DirectName
import core.NameKinds.DirectMethodName
import ast.Trees._
import ast.tpd
import collection.mutable
Expand Down Expand Up @@ -77,6 +77,7 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTr
private def shouldBeSpecialized(sym: Symbol)(implicit ctx: Context) =
sym.is(Method, butNot = Accessor) &&
defn.isImplicitFunctionType(sym.info.finalResultType) &&
!sym.isAnonymousFunction &&
(specializeMonoTargets || !sym.isEffectivelyFinal || sym.allOverriddenSymbols.nonEmpty)

/** @pre The type's final result type is an implicit function type `implicit Ts => R`.
Expand All @@ -92,7 +93,7 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTr
/** A new `m$direct` method to accompany the given method `m` */
private def newDirectMethod(sym: Symbol)(implicit ctx: Context): Symbol = {
val direct = sym.copy(
name = DirectName(sym.name.asTermName).asInstanceOf[sym.ThisName],
name = DirectMethodName(sym.name.asTermName).asInstanceOf[sym.ThisName],
flags = sym.flags | Synthetic,
info = directInfo(sym.info))
if (direct.allOverriddenSymbols.isEmpty) direct.resetFlag(Override)
Expand All @@ -104,14 +105,13 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTr
*/
private def directMethod(sym: Symbol)(implicit ctx: Context): Symbol =
if (sym.owner.isClass) {
val direct = sym.owner.info.member(DirectName(sym.name.asTermName))
val direct = sym.owner.info.member(DirectMethodName(sym.name.asTermName))
.suchThat(_.info matches directInfo(sym.info)).symbol
if (direct.maybeOwner == sym.owner) direct
else newDirectMethod(sym).enteredAfter(thisTransform)
}
else directMeth.getOrElseUpdate(sym, newDirectMethod(sym))


/** Transform `qual.apply` occurrences according to rewrite rule (2) above */
override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) =
if (tree.name == nme.apply &&
Expand All @@ -122,7 +122,7 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTr
case TypeApply(fn, args) => cpy.TypeApply(tree)(directQual(fn), args)
case Block(stats, expr) => cpy.Block(tree)(stats, directQual(expr))
case tree: RefTree =>
cpy.Ref(tree)(DirectName(tree.name.asTermName))
cpy.Ref(tree)(DirectMethodName(tree.name.asTermName))
.withType(directMethod(tree.symbol).termRef)
}
directQual(tree.qualifier)
Expand Down Expand Up @@ -157,8 +157,8 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTr

val (remappedCore, fwdClosure) = splitClosure(mdef.rhs)
val originalDef = cpy.DefDef(mdef)(rhs = fwdClosure)
val directDef = polyDefDef(direct.asTerm, remappedCore)
Thicket(originalDef, directDef)
val directDef = transformDefDef(polyDefDef(direct.asTerm, remappedCore))
flatTree(List(originalDef, directDef))
}
else mdef
}
Expand Down
16 changes: 10 additions & 6 deletions compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -422,8 +422,8 @@ object ProtoTypes {

/** The normalized form of a type
* - unwraps polymorphic types, tracking their parameters in the current constraint
* - skips implicit parameters; if result type depends on implicit parameter,
* replace with Wildcard.
* - skips implicit parameters of methods and functions;
* if result type depends on implicit parameter, replace with fresh type dependent parameter.
* - converts non-dependent method types to the corresponding function types
* - dereferences parameterless method types
* - dereferences nullary method types provided the corresponding function type
Expand All @@ -437,9 +437,10 @@ object ProtoTypes {
*/
def normalize(tp: Type, pt: Type)(implicit ctx: Context): Type = Stats.track("normalize") {
tp.widenSingleton match {
case poly: PolyType => normalize(constrained(poly).resultType, pt)
case poly: PolyType =>
normalize(constrained(poly).resultType, pt)
case mt: MethodType =>
if (mt.isImplicit) resultTypeApprox(mt)
if (mt.isImplicit) normalize(resultTypeApprox(mt), pt)
else if (mt.isDependent) tp
else {
val rt = normalize(mt.resultType, pt)
Expand All @@ -451,8 +452,11 @@ object ProtoTypes {
if (mt.paramInfos.nonEmpty || ft <:< pt) ft else rt
}
}
case et: ExprType => et.resultType
case _ => tp
case et: ExprType =>
normalize(et.resultType, pt)
case wtp =>
if (defn.isImplicitFunctionType(wtp)) normalize(wtp.dealias.argInfos.last, pt)
else tp
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1582,7 +1582,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
val paramTypes = formals.map(fullyDefinedType(_, "implicit function parameter", tree.pos))
val ifun = desugar.makeImplicitFunction(paramTypes, tree)
typr.println(i"make implicit function $tree / $pt ---> $ifun")
typedUnadapted(ifun)
typed(ifun, pt)
}

def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = /*>|>*/ ctx.traceIndented (i"typing $tree", typr, show = true) /*<|<*/ {
Expand Down
8 changes: 8 additions & 0 deletions tests/neg/i2146.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
object Test {
case class A()
case class B()

def foo[A, B]: implicit A => implicit B => Int = { implicit b: B =>
42 // error: found Int, required: implicit A => implicit B => Int
}
}
9 changes: 9 additions & 0 deletions tests/pos/i2146.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(A(),B())
(A(),B())
(A(),B())
A()
(A(),B())
(A(),B())
(A(),B())
(A(),B())
(A(),B())
32 changes: 32 additions & 0 deletions tests/pos/i2146.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
object Test {
case class A()
case class B()

def simple[A]: implicit A => A = implicitly[A]

def foo[A, B]: implicit A => implicit B => (A, B) =
(implicitly[A], implicitly[B])

def bar[A, B]: implicit A => implicit B => (A, B) = { implicit a: A =>
(implicitly[A], implicitly[B])
}

implicit val a: A = A()
implicit val b: B = B()

def main(args: Array[String]) = {
println(foo[A, B])
println(foo[A, B](a))
println(foo(a)(b))
val s: implicit A => A = simple[A]
println(s)
val x0: implicit A => implicit B => (A, B) = foo[A, B]
println(x0)
val x1: implicit B => (A, B) = foo[A, B]
println(x1)

println(bar[A, B])
println(bar[A, B](a))
println(bar(a)(b))
}
}