diff --git a/akka-channels/src/main/scala/akka/channels/Ops.scala b/akka-channels/src/main/scala/akka/channels/Ops.scala index feb25c8daf..b8a3019af4 100644 --- a/akka-channels/src/main/scala/akka/channels/Ops.scala +++ b/akka-channels/src/main/scala/akka/channels/Ops.scala @@ -14,6 +14,14 @@ sealed trait TNil extends ChannelList sealed trait :+:[A <: (_, _), B <: ChannelList] extends ChannelList sealed trait ReplyChannels[T <: ChannelList] extends ChannelList +/** + * This type is used to stand in for the unknown reply types of the fabricated + * sender references; users don’t need to write it down, and if they do, they + * know that they’re cheating (since these ref types must not escape their + * defining actor context). + */ +sealed trait UnknownDoNotWriteMeDown + class ActorRefOps(val ref: ActorRef) extends AnyVal { import macros.Helpers._ def narrow[C <: ChannelList](implicit timeout: Timeout, ec: ExecutionContext, tt: ru.TypeTag[C]): Future[ChannelRef[C]] = { diff --git a/akka-channels/src/main/scala/akka/channels/macros/Ask.scala b/akka-channels/src/main/scala/akka/channels/macros/Ask.scala index e33127b78e..f4dbf475ce 100644 --- a/akka-channels/src/main/scala/akka/channels/macros/Ask.scala +++ b/akka-channels/src/main/scala/akka/channels/macros/Ask.scala @@ -31,7 +31,7 @@ object Ask { Tell.verify(c)(null, unwrapped, typeOf[(Any, Nothing) :+: TNil], tpeChannel) - implicit val ttReturnChannels = c.TypeTag[ReturnChannels](toChannels(c.universe)(out)) + implicit val ttReturnChannels = c.TypeTag[ReturnChannels](toChannels(c.universe)(out, weakTypeOf[Nothing])) implicit val ttReturnLUB = c.TypeTag[ReturnLUB](c.universe.lub(out)) reify(askOps[WrappedMessage[ReturnChannels, ReturnLUB]]( c.prefix.splice.actorRef, toMsg(c)(msg, tpeMsg).splice)(imp[Timeout](c).splice)) @@ -54,7 +54,7 @@ object Ask { Tell.verify(c)(null, unwrapped, typeOf[(Any, Nothing) :+: TNil], tpeChannel) - implicit val ttReturnChannels = c.TypeTag[ReturnChannels](toChannels(c.universe)(out)) + implicit val ttReturnChannels = c.TypeTag[ReturnChannels](toChannels(c.universe)(out, weakTypeOf[Nothing])) implicit val ttReturnLUB = c.TypeTag[ReturnLUB](c.universe.lub(out)) val msg = reify(c.prefix.splice.value) reify(askOps[WrappedMessage[ReturnChannels, ReturnLUB]]( @@ -78,7 +78,7 @@ object Ask { Tell.verify(c)(null, unwrapped, typeOf[(Any, Nothing) :+: TNil], tpeChannel) - implicit val ttReturnChannels = c.TypeTag[ReturnChannels](toChannels(c.universe)(out)) + implicit val ttReturnChannels = c.TypeTag[ReturnChannels](toChannels(c.universe)(out, weakTypeOf[Nothing])) implicit val ttReturnLUB = c.TypeTag[ReturnLUB](c.universe.lub(out)) if (tpeMsg <:< typeOf[ChannelList]) reify(askFutureWrapped[WrappedMessage[ReturnChannels, ReturnLUB]]( diff --git a/akka-channels/src/main/scala/akka/channels/macros/Channel.scala b/akka-channels/src/main/scala/akka/channels/macros/Channel.scala index 8c93577c74..d39f5ce201 100644 --- a/akka-channels/src/main/scala/akka/channels/macros/Channel.scala +++ b/akka-channels/src/main/scala/akka/channels/macros/Channel.scala @@ -34,7 +34,7 @@ object Channel { checkUnique(c.universe)(tpeMsgT, tpeMyChannels) foreach (c.error(c.enclosingPosition, _)) // need to calculate the intersection of the reply channel sets for all input channels val intersection = inputChannels(c.universe)(tpeMsgT) map (replyChannels(c.universe)(tpeMyChannels, _).toSet) reduce (_ intersect _) - val channels = toChannels(c.universe)(intersection.toList) + val channels = toChannels(c.universe)(intersection.toList, weakTypeOf[UnknownDoNotWriteMeDown]) implicit val ttMyChannels = c.TypeTag[MyChannels](tpeMyChannels) implicit val ttReplyChannels = c.TypeTag[ReplyChannels](channels) implicit val ttMsgT = c.TypeTag[MsgT](tpeMsgT) diff --git a/akka-channels/src/main/scala/akka/channels/macros/Helpers.scala b/akka-channels/src/main/scala/akka/channels/macros/Helpers.scala index 008a08ca30..9e61e5f297 100644 --- a/akka-channels/src/main/scala/akka/channels/macros/Helpers.scala +++ b/akka-channels/src/main/scala/akka/channels/macros/Helpers.scala @@ -117,7 +117,7 @@ object Helpers { * convert a list of types List(, , ...) into a ChannelList * ( Channel[, Nothing] :=: Channel[, Nothing] :=: ... :=: TNil ) */ - final def toChannels(u: Universe)(list: List[u.Type]): u.Type = { + final def toChannels(u: Universe)(list: List[u.Type], out: u.Type): u.Type = { import u._ def rec(l: List[Type], acc: Type): Type = l match { case head :: (tail: List[Type]) ⇒ @@ -127,7 +127,7 @@ object Helpers { appliedType(weakTypeOf[:+:[_, _]].typeConstructor, List( appliedType(weakTypeOf[Tuple2[_, _]].typeConstructor, List( head, - weakTypeOf[Nothing])), + out)), acc))) case _ ⇒ acc } diff --git a/akka-channels/src/main/scala/akka/channels/macros/Tell.scala b/akka-channels/src/main/scala/akka/channels/macros/Tell.scala index 1686a9b782..c86f7e5d3c 100644 --- a/akka-channels/src/main/scala/akka/channels/macros/Tell.scala +++ b/akka-channels/src/main/scala/akka/channels/macros/Tell.scala @@ -70,15 +70,18 @@ object Tell { } def verify(c: Context)(sender: c.universe.Tree, msgT: c.universe.Type, sndT: c.universe.Type, chT: c.universe.Type)(): Unit = { + val unknown = c.universe.weakTypeOf[UnknownDoNotWriteMeDown] + val nothing = c.universe.weakTypeOf[Nothing] + def ignoreUnknown(in: c.universe.Type): c.universe.Type = if (in =:= unknown) nothing else in def rec(msg: Set[c.universe.Type], checked: Set[c.universe.Type], depth: Int): Unit = if (msg.nonEmpty) { val u: c.universe.type = c.universe - val replies = msg map (m ⇒ m -> replyChannels(u)(chT, m)) + val replies = msg map (m ⇒ m -> (replyChannels(u)(chT, m) map (t => ignoreUnknown(t)))) val missing = replies collect { case (k, v) if v.size == 0 ⇒ k } if (missing.nonEmpty) error(c, s"target ChannelRef does not support messages of types ${missing mkString ", "} (at depth $depth)") else { - val nextSend = replies.map(_._2).flatten map (m ⇒ m -> replyChannels(u)(sndT, m)) + val nextSend = replies.map(_._2).flatten map (m ⇒ m -> (replyChannels(u)(sndT, m) map (t => ignoreUnknown(t)))) val nextMissing = nextSend collect { case (k, v) if v.size == 0 ⇒ k } if (nextMissing.nonEmpty) error(c, s"implicit sender `$sender` does not support messages of the reply types ${nextMissing mkString ", "} (at depth $depth)")