8000 Add support for multiple trailing implicit parameter lists. by milessabin · Pull Request #5108 · scala/scala · GitHub
[go: up one dir, main page]

Skip to content

Add support for multiple trailing implicit parameter lists. #5108

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

Closed
wants to merge 1 commit into from
Closed
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
4 changes: 4 additions & 0 deletions bincompat-forward.whitelist.conf
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,10 @@ filter {
{
matchName="scala.concurrent.impl.Promise$DefaultPromise"
problemName=MissingTypesProblem
},
{
matchName="scala.reflect.runtime.Settings.YmultiImplicitParams"
problemName=MissingMethodProblem
}
]
}
2 changes: 1 addition & 1 deletion project/ScalaOptionParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ object ScalaOptionParser {
"-Xno-forwarders", "-Xno-patmat-analysis", "-Xno-uescape", "-Xnojline", "-Xprint-pos", "-Xprint-types", "-Xprompt", "-Xresident", "-Xshow-phases", "-Xstrict-inference", "-Xverify", "-Y",
"-Ybreak-cycles", "-Ydebug", "-Ycompact-trees", "-YdisableFlatCpCaching", "-Ydoc-debug",
"-Yeta-expand-keeps-star", "-Yide-debug", "-Yinfer-argument-types", "-Yinfer-by-name",
"-Yissue-debug", "-Ylog-classpath", "-Ymacro-debug-lite", "-Ymacro-debug-verbose", "-Ymacro-no-expand",
"-Yissue-debug", "-Ylog-classpath", "-Ymacro-debug-lite", "-Ymacro-debug-verbose", "-Ymacro-no-expand", "-Ymulti-implicit-params",
"-Yno-completion", "-Yno-generic-signatures", "-Yno-imports", "-Yno-predef",
"-Yoverride-objects", "-Yoverride-vars", "-Ypatmat-debug", "-Yno-adapted-args", "-Ypos-debug", "-Ypresentation-debug",
"-Ypresentation-strict", "-Ypresentation-verbose", "-Yquasiquote-debug", "-Yrangepos", "-Yreify-copypaste", "-Yreify-debug", "-Yrepl-class-based",
Expand Down
6 changes: 4 additions & 2 deletions src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2225,7 +2225,9 @@ self =>
if (in.token == IMPLICIT) {
in.nextToken()
implicitmod = Flags.IMPLICIT
}
} else if (implicitmod != 0)
syntaxError(in.lastOffset, "parameter lists following an implicit parameter list must also be implicit")

commaSeparated(param(owner, implicitmod, caseParam ))
}
val vds = new ListBuffer[List[ValDef]]
Expand All @@ -2234,7 +2236,7 @@ self =>
if (ofCaseClass && in.token != LPAREN)
syntaxError(in.lastOffset, "case classes without a parameter list are not allowed;\n"+
"use either case objects or case classes with an explicit `()' as a parameter list.")
while (implicitmod == 0 && in.token == LPAREN) {
while ((implicitmod == 0 || settings.YmultiImplicitParams) && in.token == LPAREN) {
in.nextToken()
vds += paramClause()
accept(RPAREN)
Expand Down
10 changes: 5 additions & 5 deletions src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,11 @@ abstract class TreeBuilder {
def makeEvidenceParam(tpt: Tree) = ValDef(mods | IMPLICIT | SYNTHETIC, freshTermName(nme.EVIDENCE_PARAM_PREFIX), tpt, EmptyTree)
val evidenceParams = contextBounds map makeEvidenceParam

val vparamssLast = if(vparamss.nonEmpty) vparamss.last else Nil
if(vparamssLast.nonEmpty && vparamssLast.head.mods.hasFlag(IMPLICIT))
vparamss.init ::: List(evidenceParams ::: vparamssLast)
else
vparamss ::: List(evidenceParams)
val (prefix, suffix) = vparamss.span(_.headOption.map(!_.mods.hasFlag(IMPLICIT)).getOrElse(true))
prefix ::: (suffix match {
case is :: iss => List(evidenceParams ::: is) ::: iss
case Nil => List(evidenceParams)
})
}
}

Expand Down
1 change: 1 addition & 0 deletions src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ trait ScalaSettings extends AbsScalaSettings
val inferByName = BooleanSetting ("-Yinfer-by-name", "Allow inference of by-name types. This is a temporary option to ease transition. See SI-7899.").withDeprecationMessage(removalIn212)
val YclasspathImpl = ChoiceSetting ("-YclasspathImpl", "implementation", "Choose classpath scanning method.", List(ClassPathRepresentationType.Recursive, ClassPathRepresentationType.Flat), ClassPathRepresentationType.Flat)
val YdisableFlatCpCaching = BooleanSetting ("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.")
val YmultiImplicitParams = BooleanSetting ("-Ymulti-implicit-params", "Allow methods to have multiple trailing implicit parameter lists.")

val exposeEmptyPackage = BooleanSetting ("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly()
val Ydelambdafy = ChoiceSetting ("-Ydelambdafy", "strategy", "Strategy used for translating lambdas into JVM code.", List("inline", "method"), "method")
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/scala/tools/nsc/typechecker/Infer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -821,11 +821,11 @@ trait Infer extends Checkable {
case OverloadedType(pre, alts) => alts exists (alt => isAsSpecific(pre memberType alt, ftpe2))
case et: ExistentialType => isAsSpecific(et.skolemizeExistential, ftpe2)
case NullaryMethodType(restpe) => isAsSpecific(restpe, ftpe2)
case mt @ MethodType(_, restpe) if mt.isImplicit => isAsSpecific(restpe, ftpe2)
case mt @ MethodType(_, _) if mt.isImplicit => isAsSpecific(skipImplicit(mt), ftpe2)
case mt @ MethodType(_, _) if bothAreVarargs => checkIsApplicable(mt.paramTypes mapConserve repeatedToSingle)
case mt @ MethodType(params, _) if params.nonEmpty => checkIsApplicable(mt.paramTypes)
case PolyType(tparams, NullaryMethodType(restpe)) => isAsSpecific(PolyType(tparams, restpe), ftpe2)
case PolyType(tparams, mt @ MethodType(_, restpe)) if mt.isImplicit => isAsSpecific(PolyType(tparams, restpe), ftpe2)
case PolyType(tparams, mt @ MethodType(_, _)) if mt.isImplicit => isAsSpecific(PolyType(tparams, skipImplicit(mt)), ftpe2)
case PolyType(_, mt @ MethodType(params, _)) if params.nonEmpty => checkIsApplicable(mt.paramTypes)
case ErrorType => true
case _ => onRight
Expand Down Expand Up @@ -1065,7 +1065,7 @@ trait Infer extends Checkable {
if (isFullyDefined(pt)) {
inferFor(pt.instantiateTypeParams(ptparams, ptparams map (x => WildcardType))) flatMap { targs =>
val ctorTpInst = tree.tpe.instantiateTypeParams(undetparams, targs)
val resTpInst = skipImplicit(ctorTpInst.finalResultType)
val resTpInst = ctorTpInst.finalResultType
val ptvars =
ptparams map {
// since instantiateTypeVar wants to modify the skolem that corresponds to the method's type parameter,
Expand Down
7 changes: 7 additions & 0 deletions test/files/neg/multi-implicits.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
multi-implicits.scala:30: error: '=' expected but '(' found.
def run[T: Baz](t: T)(implicit foo: Foo[T])(implicit bar: Bar[foo.A]): bar.B = bar.value
^
multi-implicits.scala:32: error: illegal start of simple expression
val value = run(23)
^
two errors found
61 changes: 61 additions & 0 deletions test/files/neg/multi-implicits.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package test

object Test {
// A couple of type classes with type members ...
trait Foo[T] {
type A
}

object Foo {
implicit val fooIS = new Foo[Int] { type A = String }
}

trait Bar[T] {
type B
val value: B
}

object Bar {
implicit val barSB = new Bar[String] {
type B = Boolean
val value = true
}
}

trait Baz[T]
object Baz {
implicit def baz[T]: Baz[T] = new Baz[T] {}
}

def run[T: Baz](t: T)(implicit foo: Foo[T])(implicit bar: Bar[foo.A]): bar.B = bar.value

val value = run(23)
assert(value: Boolean)

val value2 = run(23)(Baz.baz, Foo.fooIS)(Bar.barSB)
assert(value: Boolean)

def boundNullary[T: Baz] = ()
boundNullary[Int]
boundNullary[Int](Baz.baz)

def boundEmpty[T: Baz]() = ()
boundEmpty[Int]()
boundEmpty[Int]()(Baz.baz)

def boundExplicit[T: Baz](i: T) = ()
boundExplicit(23)
boundExplicit[Int](23)(Baz.baz)

def boundImplicit[T: Baz](implicit fooT: Foo[T]) = ()
boundImplicit[Int]
boundImplicit[Int](Baz.baz, Foo.fooIS)

def boundExplicitImplicit[T: Baz](i: T)(implicit fooT: Foo[T]) = ()
boundExplicitImplicit(23)
boundExplicitImplicit[Int](23)(Baz.baz, Foo.fooIS)

def boundEmptyImplicit[T: Baz]()(implicit fooT: Foo[T]) = ()
boundEmptyImplicit[Int]()
boundEmptyImplicit[Int]()(Baz.baz, Foo.fooIS)
}
1 change: 1 addition & 0 deletions test/files/pos/multi-implicits.flags
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-Ymulti-implicit-params
106 changes: 106 additions & 0 deletions test/files/pos/multi-implicits.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package test

object Test {
// A couple of type classes with type members ...
trait Foo[T] {
type A
}

object Foo {
implicit val fooIS = new Foo[Int] { type A = String }
}

trait Bar[T] {
type B
val value: B
}

object Bar {
implicit val barSB = new Bar[String] {
type B = Boolean
val value = true
}
}

trait Baz[T]
object Baz {
implicit def baz[T]: Baz[T] = new Baz[T] {}
}

def run[T: Baz](t: T)(implicit foo: Foo[T])(implicit bar: Bar[foo.A]): bar.B = bar.value

val value = run(23)
assert(value: Boolean)

val value2 = run(23)(Baz.baz, Foo.fooIS)(Bar.barSB)
assert(value: Boolean)

def boundNullary[T: Baz] = ()
boundNullary[Int]
boundNullary[Int](Baz.baz)

def boundEmpty[T: Baz]() = ()
boundEmpty[Int]()
boundEmpty[Int]()(Baz.baz)

def boundExplicit[T: Baz](i: T) = ()
boundExplicit(23)
boundExplicit[Int](23)(Baz.baz)

def boundImplicit[T: Baz](implicit fooT: Foo[T]) = ()
boundImplicit[Int]
boundImplicit[Int](Baz.baz, Foo.fooIS)

def boundExplicitImplicit[T: Baz](i: T)(implicit fooT: Foo[T]) = ()
boundExplicitImplicit(23)
boundExplicitImplicit[Int](23)(Baz.baz, Foo.fooIS)

def boundEmptyImplicit[T: Baz]()(implicit fooT: Foo[T]) = ()
boundEmptyImplicit[Int]()
boundEmptyImplicit[Int]()(Baz.baz, Foo.fooIS)

{
def bar(a: Int)(implicit b: DummyImplicit)(implicit c: DummyImplicit) = 0
val res = bar _
(res: Int => Int)
}

{
class A
implicit def bar(implicit b: DummyImplicit)(implicit c: DummyImplicit): A = new A
val res = implicitly[A]
(res: A)
}

object Overload1 {
def over(i: Int)(implicit d: DummyImplicit): Int = i
def over(b: Boolean)(implicit d: DummyImplicit): Boolean = b

(over(23): Int)
(over(true): Boolean)
}

object Overload2 {
def over(i: Int)(implicit d: DummyImplicit)(implicit e: DummyImplicit): Int = i
def over(b: Boolean)(implicit d: DummyImplicit)(implicit e: DummyImplicit): Boolean = b

(over(23): Int)
(over(true): Boolean)
}

{
object O {
def a(implicit I: DummyImplicit) = 42
def a_=(a: Any) = ()
}
O.a = 0
}

{
object O {
def a(implicit I: DummyImplicit)(implicit J: DummyImplicit) = 42
def a_=(a: Any) = ()
}
O.a = 0
}
}
0