Skip to content

Commit 6a278b7

Browse files
author
Oron Port
committed
changed method of procuring the Out for OpIntercept
1 parent b87d834 commit 6a278b7

File tree

3 files changed

+42
-101
lines changed

3 files changed

+42
-101
lines changed
Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,9 @@
11
package singleton.ops
2-
import scala.reflect.macros.whitebox
32
import impl._
43

54
import scala.annotation.implicitNotFound
6-
7-
trait OpIntercept[Op <: HasOut]
5+
@implicitNotFound("Missing an `OpIntercept` implicit for the operation ${Op}")
6+
trait OpIntercept[Op <: HasOut] extends HasOut
87
object OpIntercept {
9-
@implicitNotFound("Failed to cache with result ${Out}")
10-
trait CacheResult[Out]
11-
object CacheResult {
12-
implicit def call[Out] : CacheResult[Out] = macro Macro.materializeCacheResult[Out]
13-
final class Macro(val c: whitebox.Context) extends GeneralMacros {
14-
def materializeCacheResult[
15-
Out: c.WeakTypeTag
16-
]: c.Tree = cacheOpInterceptResult[Out]
17-
}
18-
19-
}
8+
type Aux[Op <: HasOut, Out0] = OpIntercept[Op]{type Out = Out0}
209
}

src/main/scala/singleton/ops/impl/GeneralMacros.scala

Lines changed: 27 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ private object MacroCache {
88
val cache = mutable.Map.empty[Any, Any]
99
def get(key : Any) : Option[Any] = cache.get(key)
1010
def add[V <: Any](key : Any, value : V) : V = {cache += (key -> value); value}
11-
private var opInterceptValue : Option[Any] = None
12-
def setOpInterceptValue(value : Any) : Unit = opInterceptValue = Some(value)
13-
def getOpInterceptValue : Any = opInterceptValue
14-
def clearOpInterceptValue() : Unit = opInterceptValue = None
11+
var errorCache : String = ""
12+
def clearErrorCache() : Unit = errorCache = ""
13+
def setErrorCache(msg : String) : Unit = errorCache = msg
14+
def getErrorMessage : String = errorCache
1515
}
1616
trait GeneralMacros {
1717
val c: whitebox.Context
@@ -332,19 +332,6 @@ trait GeneralMacros {
332332
VerboseTraversal(s"${GREEN}${BOLD}caching${RESET} $k -> $value")
333333
value
334334
}
335-
def setOpInterceptCalc(calc : Calc) : Unit = MacroCache.setOpInterceptValue(Left(calc))
336-
def setOpInterceptError(msg : String) : Unit = MacroCache.setOpInterceptValue(Right(msg))
337-
def clearOpInterceptCalc() : Unit = MacroCache.clearOpInterceptValue()
338-
def getOpInterceptCalc : Option[Either[Calc, String]] = {
339-
MacroCache.getOpInterceptValue.asInstanceOf[Option[Either[Calc, String]]] match {
340-
case Some(Left(v)) => Some(Left(v match {
341-
case lit : CalcLit => CalcLit(lit.value) //reconstruct internal literal tree
342-
case nlit : CalcNLit => CalcNLit(nlit.primitive, deepCopyTree(nlit.tree))
343-
case c => c
344-
}))
345-
case v => v
346-
}
347-
}
348335
}
349336
////////////////////////////////////////////////////////////////////
350337

@@ -566,7 +553,7 @@ trait GeneralMacros {
566553
}
567554

568555
case _ => //regular cases
569-
opCalc(Some(tp), funcType, aValue, bValue, cValue) match {
556+
opCalc(funcType, aValue, bValue, cValue) match {
570557
case (res : CalcVal) => Some(res)
571558
case u @ CalcUnknown(_,Some(_), _) => Some(u) //Accept unknown values with a tree
572559
case oi @ CalcUnknown(_,_, true) => Some(oi) //Accept unknown op interception
@@ -675,7 +662,7 @@ trait GeneralMacros {
675662
def abort(msg: String, annotatedSym : Option[TypeSymbol] = defaultAnnotatedSym, position : Position = c.enclosingPosition): Nothing = {
676663
VerboseTraversal(s"!!!!!!aborted with: $msg at $annotatedSym, $defaultAnnotatedSym")
677664
if (annotatedSym.isDefined) setAnnotation(msg, annotatedSym.get)
678-
CalcCache.setOpInterceptError(msg) //propagating the error in case this is an inner implicit call for OpIntercept
665+
MacroCache.setErrorCache(msg) //propagating the error in case this is an inner implicit call for OpIntercept
679666
c.abort(position, msg)
680667
}
681668

@@ -907,56 +894,38 @@ trait GeneralMacros {
907894
}
908895
///////////////////////////////////////////////////////////////////////////////////////////
909896

910-
///////////////////////////////////////////////////////////////////////////////////////////
911-
// OpInterept Result Caching
912-
///////////////////////////////////////////////////////////////////////////////////////////
913-
def cacheOpInterceptResult[Out](implicit ev0: c.WeakTypeTag[Out]) : Tree = {
914-
val outTpe = weakTypeOf[Out]
915-
val outCalc = TypeCalc(outTpe)
916-
CalcCache.setOpInterceptCalc(outCalc)
917-
q"new _root_.singleton.ops.OpIntercept.CacheResult[$outTpe]{}"
918-
}
919-
///////////////////////////////////////////////////////////////////////////////////////////
920-
921897
///////////////////////////////////////////////////////////////////////////////////////////
922898
// Three operands (Generic)
923899
///////////////////////////////////////////////////////////////////////////////////////////
924900
def materializeOpGen[F](implicit ev0: c.WeakTypeTag[F]): MaterializeOpAuxGen =
925901
new MaterializeOpAuxGen(weakTypeOf[F])
926902

927-
def opCalc(opTpe : Option[Type], funcType : TypeSymbol, aCalc : => Calc, bCalc : => Calc, cCalc : => Calc) : Calc = {
903+
def opCalc(funcType : TypeSymbol, aCalc : => Calc, bCalc : => Calc, cCalc : => Calc) : Calc = {
928904
lazy val a = aCalc
929905
lazy val b = bCalc
930906
lazy val cArg = cCalc
931907
def unsupported() : Calc = {
932-
val cachedTpe = opTpe.get match {
933-
case TypeRef(pre, sym, args) => c.internal.typeRef(pre, sym, List(funcType.toType, a.tpe, b.tpe, cArg.tpe))
934-
}
935-
//calling OpIntercept for the operation should cache the expected result if executed correctly
936-
CalcCache.clearOpInterceptCalc()
937-
val implicitlyTree = q"implicitly[_root_.singleton.ops.OpIntercept[$cachedTpe]]"
908+
val opMacroTpe = typeOf[OpMacro[_,_,_,_]].typeConstructor
909+
val opTpe = appliedType(opMacroTpe, List(funcType.toType, a.tpe, b.tpe, cArg.tpe))
910+
val interceptTpe = typeOf[singleton.ops.OpIntercept[_]].typeConstructor
911+
MacroCache.clearErrorCache()
938912
try {
939-
c.typecheck(implicitlyTree, silent = false)
940-
val cachedCalc = CalcCache.getOpInterceptCalc match {
941-
case Some(calc) => calc
942-
case None => abort("Missing a result cache for OpIntercept. Make sure you set `OpIntercept.CacheResult`")
943-
}
944-
CalcCache.clearOpInterceptCalc()
945-
cachedCalc match {
946-
case Left(t : CalcUnknown) =>
947-
t.copy(opIntercept = true) //the unknown result must be marked properly so we allow it later
948-
case Left(t) => t
949-
case Right(msg) => abort(msg)
913+
val itree = c.inferImplicitValue (
914+
appliedType(interceptTpe, List(opTpe)),
915+
silent = false
916+
)
917+
TypeCalc(itree.tpe.decls.head.info) match {
918+
case t : CalcUnknown => t.copy(opIntercept = true) //the unknown result must be marked properly so we allow it later
919+
case t => t
950920
}
951921
} catch {
952-
case TypecheckException(pos, msg) =>
953-
CalcCache.getOpInterceptCalc match {
954-
case Some(Right(msg)) => abort(msg)
955-
case _ =>
956-
(a, b) match {
957-
case (_ : CalcVal, _ : CalcVal) => abort(s"Unsupported operation $cachedTpe")
958-
case _ => CalcUnknown(funcType.toType, None, opIntercept = false)
959-
}
922+
case TypecheckException(_, _) =>
923+
MacroCache.getErrorMessage match {
924+
case m if m.nonEmpty => abort(m)
925+
case _ => (a, b) match {
926+
case (_ : CalcVal, _ : CalcVal) => abort(s"Unsupported operation $opTpe")
927+
case _ => CalcUnknown(funcType.toType, None, opIntercept = false)
928+
}
960929
}
961930
}
962931
}
@@ -1557,7 +1526,7 @@ trait GeneralMacros {
15571526
}
15581527
}
15591528

1560-
val reqCalc = opCalc(None, funcTypes.Require, condCalc, msgCalc, CalcUnknown(typeOf[NoSym], None, opIntercept = false))
1529+
val reqCalc = opCalc(funcTypes.Require, condCalc, msgCalc, CalcUnknown(typeOf[NoSym], None, opIntercept = false))
15611530

15621531
q"""
15631532
(new $chkSym[$condTpe, $msgTpe, $chkArgTpe]($outTree.asInstanceOf[$outTpe]))
@@ -1623,7 +1592,7 @@ trait GeneralMacros {
16231592
}
16241593
}
16251594

1626-
val reqCalc = opCalc(None, funcTypes.Require, condCalc, msgCalc, CalcUnknown(typeOf[NoSym], None, opIntercept = false))
1595+
val reqCalc = opCalc(funcTypes.Require, condCalc, msgCalc, CalcUnknown(typeOf[NoSym], None, opIntercept = false))
16271596

16281597
q"""
16291598
(new $chkSym[$condTpe, $msgTpe, $chkArgTpe, $paramFaceTpe, $paramTpe]($outTree.asInstanceOf[$outTpe]))

src/test/scala/singleton/ops/OpInterceptSpec.scala

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,16 @@ class OpInterceptSpec extends Properties("OpInterceptSpec") {
88

99
trait Vec[A0, A1]
1010

11-
implicit def `Vec+`[VL0, VL1, VR0, VR1, VO0, VO1](
11+
implicit def `Vec+`[VL0, VL1, VR0, VR1](
1212
implicit
13-
opL : OpAuxGen[VL0 + VR0, VO0],
14-
opR : OpAuxGen[VL1 + VR1, VO1],
15-
result : OpIntercept.CacheResult[Vec[VO0, VO1]]
16-
) : OpIntercept[Vec[VL0, VL1] + Vec[VR0, VR1]] = ???
13+
opL : VL0 + VR0,
14+
opR : VL1 + VR1
15+
) : OpIntercept.Aux[Vec[VL0, VL1] + Vec[VR0, VR1], Vec[opL.Out, opR.Out]] = ???
1716

18-
implicit def `Vec==`[VL0, VL1, VR0, VR1, EqOut](
17+
implicit def `Vec==`[VL0, VL1, VR0, VR1](
1918
implicit
20-
op : OpAuxGen[(VL0 == VR0) && (VL1 == VR1), EqOut],
21-
result : OpIntercept.CacheResult[EqOut]
22-
) : OpIntercept[Vec[VL0, VL1] == Vec[VR0, VR1]] = ???
23-
19+
op : (VL0 == VR0) && (VL1 == VR1)
20+
) : OpIntercept.Aux[Vec[VL0, VL1] == Vec[VR0, VR1], op.Out] = ???
2421

2522
property("Custom Vec Equality OK") = wellTyped {
2623
val eq1 = shapeless.the[Vec[W.`1`.T, W.`2`.T] == Vec[W.`1`.T, W.`2`.T]]
@@ -38,14 +35,13 @@ class OpInterceptSpec extends Properties("OpInterceptSpec") {
3835
implicitly[add23.Out =:= Vec[W.`28`.T, W.`40`.T]]
3936
}
4037

38+
4139
trait FibId
4240
type Fib[P] = impl.OpMacro[FibId, P, W.`0`.T, W.`0`.T]
43-
implicit def doFib[P, Out](
41+
implicit def doFib[P](
4442
implicit
45-
op : OpAuxGen[ITE[P == W.`0`.T, W.`0`.T, ITE[P == W.`1`.T, W.`1`.T, Fib[P - W.`1`.T] + Fib[P - W.`2`.T]]], Out],
46-
result : OpIntercept.CacheResult[Out]
47-
) : OpIntercept[Fib[P]] = ???
48-
43+
op : ITE[P == W.`0`.T, W.`0`.T, ITE[P == W.`1`.T, W.`1`.T, Fib[P - W.`1`.T] + Fib[P - W.`2`.T]]]
44+
) : OpIntercept.Aux[Fib[P], op.Out] = ???
4945

5046
property("Custom Fibonacci Op OK") = wellTyped {
5147
val fib4 = shapeless.the[Fib[W.`4`.T]]
@@ -59,23 +55,10 @@ class OpInterceptSpec extends Properties("OpInterceptSpec") {
5955
type FooOp[C, M] = impl.OpMacro[FooOpId, C, M, W.`0`.T]
6056
implicit def FooOp[C, M](
6157
implicit
62-
r : RequireMsg[C, M],
63-
result : OpIntercept.CacheResult[W.`true`.T]
58+
r : RequireMsg[C, M]
6459
) : OpIntercept[FooOp[C, M]] = ???
6560

6661
property("Error Message Propagation") = wellTyped {
6762
illTyped("""shapeless.the[FooOp[W.`false`.T, W.`"this is a test"`.T]]""", "this is a test")
6863
}
69-
70-
trait BarOpId
71-
type BarOp[C, M] = impl.OpMacro[BarOpId, C, M, W.`0`.T]
72-
implicit def BarOp[C, M](
73-
implicit
74-
op : C + M
75-
) : OpIntercept[BarOp[C, M]] = ???
76-
77-
property("Missing Caching Error") = wellTyped {
78-
illTyped("""shapeless.the[BarOp[W.`1`.T, W.`2`.T]]""", "Missing a result cache for OpIntercept. Make sure you set `OpIntercept.CacheResult`")
79-
}
80-
8164
}

0 commit comments

Comments
 (0)