Skip to content
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

[WIP] Support a rational type #146

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
9 changes: 9 additions & 0 deletions src/main/scala/singleton/ops/OpIntercept.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package singleton.ops
import impl._

import scala.annotation.implicitNotFound
@implicitNotFound("Missing an `OpIntercept` implicit for the operation ${Op}")
trait OpIntercept[Op <: HasOut] extends HasOutValue
object OpIntercept {
type Aux[Op <: HasOut, Out0] = OpIntercept[Op]{type Out = Out0}
}
80 changes: 53 additions & 27 deletions src/main/scala/singleton/ops/impl/GeneralMacros.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package singleton.ops.impl
import singleton.twoface.impl.TwoFaceAny

import scala.reflect.macros.whitebox
import scala.reflect.macros.{TypecheckException, whitebox}

private object MacroCache {
import scala.collection.mutable
val cache = mutable.Map.empty[Any, Any]
def get(key : Any) : Option[Any] = cache.get(key)
def add[V <: Any](key : Any, value : V) : V = {cache += (key -> value); value}
var errorCache : String = ""
def clearErrorCache() : Unit = errorCache = ""
def setErrorCache(msg : String) : Unit = errorCache = msg
def getErrorMessage : String = errorCache
}
trait GeneralMacros {
val c: whitebox.Context
Expand Down Expand Up @@ -261,7 +265,7 @@ trait GeneralMacros {

def unapply(arg: CalcType): Option[Primitive] = Some(arg.primitive)
}
case class CalcUnknown(tpe : Type, treeOption : Option[Tree]) extends Calc {
case class CalcUnknown(tpe : Type, treeOption : Option[Tree], opIntercept : Boolean) extends Calc {
override val primitive: Primitive = Primitive.Unknown(tpe, "Unknown")
}
object NonLiteralCalc {
Expand Down Expand Up @@ -456,7 +460,7 @@ trait GeneralMacros {
def unapply(tp: Type): Option[Calc] = {
tp match {
case TypeRef(_, sym, ft :: tp :: _) if sym == opMacroSym && ft.typeSymbol == funcTypes.GetType =>
Some(CalcUnknown(tp, None))
Some(CalcUnknown(tp, None, opIntercept = false))
case TypeRef(_, sym, args) if sym == opMacroSym =>
VerboseTraversal(s"@@OpCalc@@\nTP: $tp\nRAW: ${showRaw(tp)}")
val funcType = args.head.typeSymbol.asType
Expand All @@ -473,7 +477,7 @@ trait GeneralMacros {
case (funcTypes.ImplicitFound, _) =>
setUncachingReason(1)
aValue match {
case CalcUnknown(t, _) => try {
case CalcUnknown(t, _, false) => try {
c.typecheck(q"implicitly[$t]")
Some(CalcLit(true))
} catch {
Expand All @@ -484,7 +488,7 @@ trait GeneralMacros {
}
case (funcTypes.EnumCount, _) =>
aValue match {
case CalcUnknown(t, _) => Some(CalcLit(t.typeSymbol.asClass.knownDirectSubclasses.size))
case CalcUnknown(t, _, false) => Some(CalcLit(t.typeSymbol.asClass.knownDirectSubclasses.size))
case _ => Some(CalcLit(0))
}
case (funcTypes.IsNat, _) =>
Expand Down Expand Up @@ -551,7 +555,8 @@ trait GeneralMacros {
case _ => //regular cases
opCalc(funcType, aValue, bValue, cValue) match {
case (res : CalcVal) => Some(res)
case u @ CalcUnknown(_,Some(_)) => Some(u) //Accept unknown values with a tree
case u @ CalcUnknown(_,Some(_), _) => Some(u) //Accept unknown values with a tree
case oi @ CalcUnknown(_,_, true) => Some(oi) //Accept unknown op interception
case _ => None
}
}
Expand All @@ -575,7 +580,7 @@ trait GeneralMacros {
case Some(t : CalcUnknown) => t
case _ =>
VerboseTraversal(s"@@Unknown@@\nTP: $tp\nRAW: ${showRaw(tp)}")
CalcUnknown(tp, None)
CalcUnknown(tp, None, opIntercept = false)
}
}

Expand Down Expand Up @@ -654,10 +659,11 @@ trait GeneralMacros {
}
////////////////////////////////////////////////////////////////////////

def abort(msg: String, annotatedSym : Option[TypeSymbol] = defaultAnnotatedSym): Nothing = {
def abort(msg: String, annotatedSym : Option[TypeSymbol] = defaultAnnotatedSym, position : Position = c.enclosingPosition): Nothing = {
VerboseTraversal(s"!!!!!!aborted with: $msg at $annotatedSym, $defaultAnnotatedSym")
if (annotatedSym.isDefined) setAnnotation(msg, annotatedSym.get)
c.abort(c.enclosingPosition, msg)
MacroCache.setErrorCache(msg) //propagating the error in case this is an inner implicit call for OpIntercept
c.abort(position, msg)
}

def buildWarningMsgLoc : String = s"${c.enclosingPosition.source.path}:${c.enclosingPosition.line}:${c.enclosingPosition.column}"
Expand Down Expand Up @@ -734,11 +740,11 @@ trait GeneralMacros {
case None =>
q"""
new $opTpe {
type OutWide = Option[$outTpe]
type Out = Option[$outTpe]
final val value: Option[$outTpe] = None
type OutWide = $outTpe
type Out = $outTpe
final lazy val value: $outTpe = throw new IllegalArgumentException("This operation does not produce a value.")
final val isLiteral = false
final val valueWide: Option[$outTpe] = None
final lazy val valueWide: $outTpe = throw new IllegalArgumentException("This operation does not produce a value.")
}
"""
}
Expand Down Expand Up @@ -771,11 +777,11 @@ trait GeneralMacros {
}

opTree match {
case q"""{
$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends ..$parents { $self => ..$opClsBlk }
$expr(...$exprss)
}""" => getOut(opClsBlk)
case _ => extractionFailed(opTree)
case q"""{
$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends ..$parents { $self => ..$opClsBlk }
$expr(...$exprss)
}""" => getOut(opClsBlk)
case _ => extractionFailed(opTree)
}
}

Expand Down Expand Up @@ -881,7 +887,7 @@ trait GeneralMacros {
val (typedTree, tpe) = GetArgTree(argIdx, lhs)
VerboseTraversal(s"@@extractFromArg@@\nTP: $tpe\nRAW: ${showRaw(tpe)}\nTree: $typedTree")
TypeCalc(tpe) match {
case _ : CalcUnknown => CalcUnknown(tpe, Some(c.untypecheck(typedTree)))
case _ : CalcUnknown => CalcUnknown(tpe, Some(c.untypecheck(typedTree)), opIntercept = false)
case t : CalcNLit => CalcNLit(t, typedTree)
case t => t
}
Expand All @@ -899,9 +905,28 @@ trait GeneralMacros {
lazy val b = bCalc
lazy val cArg = cCalc
def unsupported() : Calc = {
(a, b) match {
case (aArg : CalcVal, bArg : CalcVal) => abort(s"Unsupported $funcType[$a, $b, $cArg]")
case _ => CalcUnknown(funcType.toType, None)
val opMacroTpe = typeOf[OpMacro[_,_,_,_]].typeConstructor
val opTpe = appliedType(opMacroTpe, List(funcType.toType, a.tpe, b.tpe, cArg.tpe))
val interceptTpe = typeOf[singleton.ops.OpIntercept[_]].typeConstructor
MacroCache.clearErrorCache()
try {
val itree = c.inferImplicitValue (
appliedType(interceptTpe, List(opTpe)),
silent = false
)
TypeCalc(itree.tpe.decls.head.info) match {
case t : CalcUnknown => t.copy(treeOption = Some(c.untypecheck(q"$itree.value")),opIntercept = true) //the unknown result must be marked properly so we allow it later
case t => t
}
} catch {
case TypecheckException(_, _) =>
MacroCache.getErrorMessage match {
case m if m.nonEmpty => abort(m)
case _ => (a, b) match {
case (_ : CalcVal, _ : CalcVal) => abort(s"Unsupported operation $opTpe")
case _ => CalcUnknown(funcType.toType, None, opIntercept = false)
}
}
}
}

Expand Down Expand Up @@ -1055,7 +1080,7 @@ trait GeneralMacros {
}
//directly using the java lib `require` resulted in compiler crash, so we use wrapped require instead
case CalcNLit(Primitive.String, msg, _) => cArg match {
case CalcUnknown(t, _) if t.typeSymbol == symbolOf[Warn] =>
case CalcUnknown(t, _, false) if t.typeSymbol == symbolOf[Warn] =>
CalcNLit(Primitive.Boolean, q"""{println(${buildWarningMsg(msg)}); false}""")
case _ =>
CalcNLit(Primitive.Boolean, q"{_root_.singleton.ops.impl._require(false, $msg); false}")
Expand All @@ -1065,7 +1090,7 @@ trait GeneralMacros {
case CalcNLit(Primitive.Boolean, cond, _) => b match {
//directly using the java lib `require` resulted in compiler crash, so we use wrapped require instead
case CalcVal(msg : String, msgt) => cArg match {
case CalcUnknown(t, _) if t == symbolOf[Warn] =>
case CalcUnknown(t, _, false) if t == symbolOf[Warn] =>
CalcNLit(Primitive.Boolean,
q"""{
if ($cond) true
Expand Down Expand Up @@ -1366,7 +1391,7 @@ trait GeneralMacros {
case funcTypes.PrefixMatch => PrefixMatch
case funcTypes.ReplaceFirstMatch => ReplaceFirstMatch
case funcTypes.ReplaceAllMatches => ReplaceAllMatches
case _ => abort(s"Unsupported $funcType[$a, $b, $cArg]")
case _ => unsupported()
}
}

Expand All @@ -1381,6 +1406,7 @@ trait GeneralMacros {
else genOpTreeNat(opTpe, t)
case (_, CalcLit(_, t)) => genOpTreeLit(opTpe, t)
case (funcTypes.AcceptNonLiteral | funcTypes.GetArg, t : CalcNLit) => genOpTreeNLit(opTpe, t)
case (_, t @ CalcUnknown(_,_,true)) => genOpTreeUnknown(opTpe, t)
case (funcTypes.GetArg, t : CalcUnknown) => genOpTreeUnknown(opTpe, t)
case (_, t: CalcNLit) =>
abort("Calculation has returned a non-literal type/value.\nTo accept non-literal values, use `AcceptNonLiteral[T]`.")
Expand Down Expand Up @@ -1500,7 +1526,7 @@ trait GeneralMacros {
}
}

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

q"""
(new $chkSym[$condTpe, $msgTpe, $chkArgTpe]($outTree.asInstanceOf[$outTpe]))
Expand Down Expand Up @@ -1566,7 +1592,7 @@ trait GeneralMacros {
}
}

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

q"""
(new $chkSym[$condTpe, $msgTpe, $chkArgTpe, $paramFaceTpe, $paramTpe]($outTree.asInstanceOf[$outTpe]))
Expand Down
8 changes: 6 additions & 2 deletions src/main/scala/singleton/ops/impl/Op.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ trait HasOut extends Any with Serializable {
type Out
}

trait Op extends HasOut {
trait HasOutValue extends HasOut {
val value : Out
}

trait Op extends HasOutValue {
type OutWide
type Out
type OutNat <: Nat
Expand All @@ -29,7 +33,7 @@ protected[singleton] object OpGen {
implicit def getValue[O <: Op, Out](o : Aux[O, Out]) : Out = o.value
}

trait OpCast[T, O <: Op] extends HasOut {type Out <: T; val value : Out}
trait OpCast[T, O <: Op] extends HasOutValue {type Out <: T}


@scala.annotation.implicitNotFound(msg = "Unable to prove type argument is a Nat.")
Expand Down
Loading