diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index d77520c778cb..d2cc7dc6e99f 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -465,6 +465,14 @@ trait Checking { case _ => } + /** Check that result type does not refer any of the parameters in `vparams`. + */ + def checkNotDependent(resTpt: Tree, vparams: List[Symbol])(implicit ctx: Context): Unit = + for (vparam <- vparams) + if (!vparam.is(Implicit) && vparam.termRef.occursIn(resTpt.tpe)) + ctx.errorOrMigrationWarning( + em"implicit method's result type may not depend on parameter ${vparam.name}", resTpt.pos) + /** Check that any top-level type arguments in this type are feasible, i.e. that * their lower bound conforms to their upper bound. If a type argument is * infeasible, issue and error and continue with upper bound. @@ -544,6 +552,7 @@ trait NoChecking extends Checking { override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = () override def checkClassType(tp: Type, pos: Position, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type = tp override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = () + override def checkNotDependent(resTpt: Tree, vparams: List[Symbol])(implicit ctx: Context): Unit = () override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = () override def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = () diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 52470ba879ec..035c570a4f69 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1100,7 +1100,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit completeAnnotations(ddef, sym) val tparams1 = tparams mapconserve (typed(_).asInstanceOf[TypeDef]) val vparamss1 = vparamss nestedMapconserve (typed(_).asInstanceOf[ValDef]) - if (sym is Implicit) checkImplicitParamsNotSingletons(vparamss1) var tpt1 = checkSimpleKinded(typedType(tpt)) var rhsCtx = ctx @@ -1113,12 +1112,18 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit rhsCtx.gadt.setBounds(tdef.symbol, TypeAlias(tparam.typeRef))) } val rhs1 = typedExpr(ddef.rhs, tpt1.tpe)(rhsCtx) + def vparamSyms = vparamss1.flatMap(_.map(_.symbol)) if (sym.isAnonymousFunction) { // If we define an anonymous function, make sure the return type does not // refer to parameters. This is necessary because closure types are // function types so no dependencies on parameters are allowed. - tpt1 = tpt1.withType(avoid(tpt1.tpe, vparamss1.flatMap(_.map(_.symbol)))) + tpt1 = tpt1.withType(avoid(tpt1.tpe, vparamSyms)) } + else if (sym is Implicit) { + checkImplicitParamsNotSingletons(vparamss1) + checkNotDependent(tpt1, vparamSyms) + } + assignType(cpy.DefDef(ddef)(name, tparams1, vparamss1, tpt1, rhs1), sym) //todo: make sure dependent method types do not depend on implicits or by-name params } diff --git a/tests/pending/pos/protected-t1010.scala b/tests/disabled/not-representable/protected-t1010.scala similarity index 100% rename from tests/pending/pos/protected-t1010.scala rename to tests/disabled/not-representable/protected-t1010.scala diff --git a/tests/pending/pos/depmet_implicit_oopsla_zipwith.scala b/tests/disabled/structural-type/pos/depmeth_implicit_oopsla_zipwith.scala similarity index 100% rename from tests/pending/pos/depmet_implicit_oopsla_zipwith.scala rename to tests/disabled/structural-type/pos/depmeth_implicit_oopsla_zipwith.scala diff --git a/tests/neg/dependent-implicits.scala b/tests/neg/dependent-implicits.scala new file mode 100644 index 000000000000..dd123ea13940 --- /dev/null +++ b/tests/neg/dependent-implicits.scala @@ -0,0 +1,7 @@ +object Test { + trait T { type X; val x: X } + implicit def f(x: T): x.X = x.x // error + val t = new T { type X = String; val x = "" } + val x: String = t + val uy: String = f(t) +} diff --git a/tests/pending/pos/depmet_implicit_norm_ret.scala b/tests/neg/depmet_implicit_norm_ret.scala similarity index 94% rename from tests/pending/pos/depmet_implicit_norm_ret.scala rename to tests/neg/depmet_implicit_norm_ret.scala index 85be750b4296..e0f5ad285e83 100644 --- a/tests/pending/pos/depmet_implicit_norm_ret.scala +++ b/tests/neg/depmet_implicit_norm_ret.scala @@ -12,7 +12,7 @@ object Test{ // def apply[S: ZipWith](s : S) = ?[ZipWith[S]].zipWith(s) // TODO: bug return type should be inferred def apply[S](s : S)(implicit zw: ZipWith[S]): zw.T = zw.zipWith(s) - implicit def SuccZipWith[S,R](implicit zWith : ZipWith[R]): Test.ZipWith[S => R]{type T = Stream[S] => zWith.T} = new ZipWith[S => R] { + implicit def SuccZipWith[S,R](implicit zWith : ZipWith[R]): Test.ZipWith[S => R]{type T = Stream[S] => zWith.T} = new ZipWith[S => R] { // error: may not be dependent type T = Stream[S] => zWith.T // dependent types replace the associated types functionality } } @@ -25,5 +25,5 @@ object Test{ // bug: inferred return type = (Stream[A]) => java.lang.Object with Test.ZipWith[B]{type T = Stream[B]}#T // this seems incompatible with vvvvvvvvvvvvvvvvvvvvvv -- #3731 def map[A,B](f : A => B) /* : Stream[A] => Stream[B]*/ = ZipWith(f) - val tst: Stream[Int] = map{x: String => x.length}(Stream("a")) + val tst: Stream[Int] = map{x: String => x.length}(Stream("a")) // error // error } diff --git a/tests/pending/pos/depmet_implicit_oopsla_session.scala b/tests/pending/pos/depmeth_implicit_oopsla_session.scala similarity index 72% rename from tests/pending/pos/depmet_implicit_oopsla_session.scala rename to tests/pending/pos/depmeth_implicit_oopsla_session.scala index a9c8e56ce3d9..eee5bb7390a0 100644 --- a/tests/pending/pos/depmet_implicit_oopsla_session.scala +++ b/tests/pending/pos/depmeth_implicit_oopsla_session.scala @@ -46,18 +46,5 @@ object Sessions { def runSession[S, D: Session[S]#HasDual](p: S, dp: D) = implicitly[Session[S]#HasDual[D]].run(p, dp) - // def runSession[S, D](p: S, dp: D)(implicit s: Session[S]#HasDual[D]) = - // s.run(p, dp) - // - // def runSession[S, D](p: S, dp: D)(implicit s: Session[S]{type Dual=D}) = - // s.run(p, dp) - - // TODO: can we relax the ordering restrictions on dependencies so that we can use - // def runSession[S](p: S, dp: s.Dual)(implicit s: Session[S]) = - // s.run(p, dp) - // to emphasise similarity of type parameters and implicit arguments: - // def runSession[S][val s: Session[S]](p: S, dp: s.Dual) = - // s.run(p, dp) - def myRun = runSession(addServer, addClient) } diff --git a/tests/pending/pos/depmet_implicit_oopsla_session_2.scala b/tests/pending/pos/depmeth_implicit_oopsla_session_2.scala similarity index 100% rename from tests/pending/pos/depmet_implicit_oopsla_session_2.scala rename to tests/pending/pos/depmeth_implicit_oopsla_session_2.scala diff --git a/tests/pending/pos/depmet_implicit_oopsla_session_simpler.scala b/tests/pending/pos/depmeth_implicit_oopsla_session_simpler.scala similarity index 100% rename from tests/pending/pos/depmet_implicit_oopsla_session_simpler.scala rename to tests/pending/pos/depmeth_implicit_oopsla_session_simpler.scala diff --git a/tests/pending/pos/depsel.scala b/tests/pending/pos/depsel.scala deleted file mode 100644 index 2cec4349e18d..000000000000 --- a/tests/pending/pos/depsel.scala +++ /dev/null @@ -1,14 +0,0 @@ -// demonstrates selection on non-path types. Needs to be fleshed out to -// become a real test. -object Test { - - class C { - type T - val f: T => T = ??? - } - - var x = new C - val y = x.f - - -} diff --git a/tests/pos/depmet_implicit_chaining_zw.scala b/tests/pos-scala2/depmet_implicit_chaining_zw.scala similarity index 100% rename from tests/pos/depmet_implicit_chaining_zw.scala rename to tests/pos-scala2/depmet_implicit_chaining_zw.scala diff --git a/tests/pos/t5070.scala b/tests/pos/t5070.scala new file mode 100644 index 000000000000..95a20a570ab1 --- /dev/null +++ b/tests/pos/t5070.scala @@ -0,0 +1,15 @@ +trait A { + type T +} + +object O { + implicit def b(implicit x: A): x.T = error("") +} + +class Test { + import O._ + implicit val a: A = new A {} + implicitly[a.T] // works in scalac, not in dotty + + implicitly[a.T](b(a)) // works +}