Skip to content

Commit cc2fe64

Browse files
author
Oron Port
committed
DualSummon mechanism
1 parent 5bddd33 commit cc2fe64

File tree

8 files changed

+113
-116
lines changed

8 files changed

+113
-116
lines changed

core/src/main/scala/dfhdl/core/DFStruct.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ object DFStruct:
8787
|A valid field type is in the form of [DFType] <> VAL.
8888
|The following fields do not match this pattern:
8989
|$fieldTypesStr""".stripMargin
90-
IsGiven.controlledMacroError(msg)
90+
ControlledMacroError.report(msg)
9191
end if
9292
end dfTypeMacro
9393

@@ -148,7 +148,7 @@ object DFStruct:
148148
else
149149
val msg =
150150
s"Mismatch structure value type `${tpeR.showType}` for DFHDL receiver structure type `${tpeL.showType}`."
151-
IsGiven.controlledMacroError(msg)
151+
ControlledMacroError.report(msg)
152152
end sfMacro
153153

154154
object Val:

core/src/main/scala/dfhdl/core/DFType.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ object DFType:
181181
s"Type `$badTypeStr` is not a supported DFHDL type constructor.\nHint: Are you missing an argument in your DFHDL type constructor?"
182182
else
183183
s"Type `$badTypeStr` is not a supported product companion.\nHint: Did you forget to extends `Struct` or `Encoded`?"
184-
IsGiven.controlledMacroError(msg)
184+
ControlledMacroError.report(msg)
185185
end match
186186
end productMacro
187187

core/src/main/scala/dfhdl/core/DFVal.scala

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ object DFVal extends DFValLP:
324324
)(dfc: Expr[DFC])(using Quotes, Type[T], Type[M], Type[R], Type[Op]): Expr[DFValTP[DFBool, Any]] =
325325
import quotes.reflect.*
326326
if (TypeRepr.of[T].typeSymbol equals defn.NothingClass)
327-
return IsGiven.controlledMacroError("This is fake")
327+
return ControlledMacroError.report("This is fake")
328328
val exactInfo = arg.exactInfo
329329
val lpType = dfVal.asTerm.tpe.isConstTpe.asTypeOf[Any]
330330
val rpType = exactInfo.exactTpe.isConstTpe.asTypeOf[Any]
@@ -1358,7 +1358,7 @@ object VarsTuple:
13581358
println(tpe.widen.dealias.show)
13591359
Some(s"All tuple elements must be mutable but found an immutable type `${tpe.showType}`")
13601360
varsCheck(tTpe) match
1361-
case Some(err) => IsGiven.controlledMacroError(err)
1361+
case Some(err) => ControlledMacroError.report(err)
13621362
case None =>
13631363
import Width.calcValWidth
13641364
val widthType = tTpe.calcValWidth.asTypeOf[Int]
@@ -1555,39 +1555,31 @@ object ConnectOps:
15551555
def connect(consumer: Consumer, producer: Producer)(using DFC): Unit =
15561556
consumer.connect(tc(consumer.dfType, producer))
15571557

1558-
private[core] transparent inline def specialConnect[
1559-
L <: DFValAny,
1560-
R <: DFValAny
1561-
](
1558+
private def specialConnectRuntime[L <: DFValAny, R <: DFValAny](
1559+
lhs: L,
1560+
rhs: R,
1561+
dualSummon: DualSummonTrapError[TC_Connect[L, R], TC_Connect[R, L]]
1562+
)(using dfc: DFC): Unit = trydf {
1563+
(dualSummon.valueL, dualSummon.valueR) match
1564+
case (Some(tcL), Some(tcR)) =>
1565+
try tcL.connect(lhs, rhs)
1566+
catch
1567+
case eL: Throwable =>
1568+
try tcR.connect(rhs, lhs)
1569+
catch case eR: Throwable => throw eL
1570+
case (Some(tcL), None) => tcL.connect(lhs, rhs)
1571+
case (None, Some(tcR)) => tcR.connect(rhs, lhs)
1572+
case _ => // not possible because of the trap error
1573+
end match
1574+
}(using dfc, CTName("<>"))
1575+
1576+
private[core] transparent inline def specialConnect[L <: DFValAny, R <: DFValAny](
15621577
inline lhs: L,
15631578
inline rhs: R
1564-
)(using DFC): Unit =
1565-
inline val connectableL = inline compiletime.erasedValue[L] match
1566-
case _: DFVal[DFTypeAny, Modifier[Any, Modifier.Connectable, Any, Any]] => true
1567-
case _ => false
1568-
inline val connectableR = inline compiletime.erasedValue[R] match
1569-
case _: DFVal[DFTypeAny, Modifier[Any, Modifier.Connectable, Any, Any]] => true
1570-
case _ => false
1571-
inline if (connectableL || connectableR)
1572-
inline if (IsGiven[TC_Connect[L, R]])
1573-
val tcL = compiletime.summonInline[TC_Connect[L, R]]
1574-
inline if (IsGiven[TC_Connect[R, L]])
1575-
val tcR = compiletime.summonInline[TC_Connect[R, L]]
1576-
try tcL.connect(lhs, rhs)
1577-
catch
1578-
case eL: Throwable =>
1579-
try tcR.connect(rhs, lhs)
1580-
catch case eR: Throwable => throw eL
1581-
else tcL.connect(lhs, rhs)
1582-
else if (IsGiven[TC_Connect[R, L]])
1583-
compiletime.summonInline[TC_Connect[R, L]].connect(rhs, lhs)
1584-
else
1585-
// forcing the error message from the LHS case
1586-
compiletime.summonInline[TC_Connect[L, R]]
1587-
else compiletime.error(
1588-
"At least one of the connection arguments must be a connectable DFHDL value (var/port)."
1589-
)
1590-
end if
1579+
)(using dfc: DFC): Unit =
1580+
val dualSummon =
1581+
compiletime.summonInline[DualSummonTrapError[TC_Connect[L, R], TC_Connect[R, L]]]
1582+
specialConnectRuntime(lhs, rhs, dualSummon)
15911583
end specialConnect
15921584

15931585
given evConnectPort[

internals/src/main/scala/dfhdl/internals/Checked.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ private class MacroClass[Q <: Quotes](using val quotes: Q)(
113113
if (warn)
114114
report.warning(msg)
115115
'{}
116-
else IsGiven.controlledMacroError(msg)
116+
else ControlledMacroError.report(msg)
117117
case _ =>
118118
if (warn)
119119
'{ println($msgExpr) }
@@ -206,7 +206,7 @@ object Check1:
206206
object CheckOK extends Check[Any, Any, Nothing, Nothing, Boolean, String, Boolean]:
207207
def apply(arg: Any): Unit = {}
208208

209-
inline given [
209+
transparent inline given [
210210
Wide,
211211
T <: Wide,
212212
Cond[T <: Wide] <: Boolean,
@@ -344,7 +344,7 @@ object Check2:
344344
object CheckOK extends Check[Any, Any, Any, Any, Nothing, Nothing, Boolean, String, Boolean]:
345345
def apply(arg1: Any, arg2: Any): Unit = {}
346346

347-
inline given [
347+
transparent inline given [
348348
Wide1,
349349
Wide2,
350350
T1 <: Wide1,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package dfhdl.internals
2+
import scala.quoted.*
3+
4+
object ControlledMacroError:
5+
// if contains a key, it means to activate error control.
6+
// if the value is empty (by default), it means the implicit given is found.
7+
// if the value is not empty, it means the implicit given with the error message.
8+
private val positionError = collection.concurrent.TrieMap.empty[String, String]
9+
private def getKey(using Quotes): String =
10+
import quotes.reflect.*
11+
Position.ofMacroExpansion.toString
12+
def activate()(using Quotes): Unit = positionError += getKey -> ""
13+
def deactivate()(using Quotes): Unit = positionError.remove(getKey)
14+
def getLastError(using Quotes): String = positionError.getOrElse(getKey, "")
15+
def getLastErrorAndDeactivate(using Quotes): String = positionError.remove(getKey).getOrElse("")
16+
def report(msg: String)(using Quotes): Expr[Nothing] =
17+
import quotes.reflect.report as macroReport
18+
val key = getKey
19+
if (positionError.contains(key))
20+
positionError += key -> msg
21+
macroReport.errorAndAbort(msg)
22+
else
23+
'{ compiletime.error(${ Expr(msg) }) }
24+
end ControlledMacroError
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package dfhdl.internals
2+
import scala.quoted.*
3+
4+
trait DualSummonTrapError[L, R]:
5+
val valueL: Option[L]
6+
val valueR: Option[R]
7+
object DualSummonTrapError:
8+
def apply[L, R](valueL0: Option[L], valueR0: Option[R]): DualSummonTrapError[L, R] =
9+
new DualSummonTrapError[L, R]:
10+
val valueL: Option[L] = valueL0
11+
val valueR: Option[R] = valueR0
12+
end apply
13+
transparent inline given [L, R]: DualSummonTrapError[L, R] = ${ fromMacroImpl[L, R] }
14+
def fromMacroImpl[L, R](using Quotes, Type[L], Type[R]): Expr[DualSummonTrapError[L, R]] =
15+
import quotes.reflect.*
16+
ControlledMacroError.activate()
17+
enum Result[T] derives CanEqual:
18+
case Success[T](value: Expr[T]) extends Result[T]
19+
case PriorityError[T](msg: String) extends Result[T]
20+
case NotFoundError[T](msg: String) extends Result[T]
21+
import Result.*
22+
def getResult[T](using Type[T]): Result[T] =
23+
Implicits.search(TypeRepr.of[T]) match
24+
case iss: ImplicitSearchSuccess =>
25+
Success(iss.tree.asExprOf[T])
26+
case isf: ImplicitSearchFailure =>
27+
val lastError = ControlledMacroError.getLastError
28+
if (lastError.nonEmpty) PriorityError[T](lastError)
29+
else NotFoundError[T](isf.explanation)
30+
end match
31+
end getResult
32+
val resultL = getResult[L]
33+
val resultR = getResult[R]
34+
ControlledMacroError.deactivate()
35+
(resultL, resultR) match
36+
case (Success(valueL), Success(valueR)) => '{ apply[L, R](Some($valueL), Some($valueR)) }
37+
case (Success(valueL), _) => '{ apply[L, R](Some($valueL), None) }
38+
case (_, Success(valueR)) => '{ apply[L, R](None, Some($valueR)) }
39+
case (PriorityError(msgL), _) => '{ compiletime.error(${ Expr(msgL) }) }
40+
case (_, PriorityError(msgR)) => '{ compiletime.error(${ Expr(msgR) }) }
41+
case (NotFoundError(msgL), _) => '{ compiletime.error(${ Expr(msgL) }) }
42+
end match
43+
end fromMacroImpl
44+
end DualSummonTrapError

internals/src/main/scala/dfhdl/internals/Exact.scala

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -62,28 +62,6 @@ end extension
6262

6363
final class Exact[T](val value: T) extends AnyVal
6464
object Exact:
65-
// private val cacheMap = TrieMap.empty[String, (Quotes, Expr[Any])]
66-
// def cacheErrorExpr(msg: String)(using Quotes): Expr[Nothing] =
67-
// import quotes.reflect.*
68-
// val sourceFile = Position.ofMacroExpansion.sourceFile
69-
// val cached = cacheMap(sourceFile.path)
70-
// println(s"get: ${Position.ofMacroExpansion}")
71-
// val (start, end) =
72-
// given Quotes = cached._1
73-
// import quotes.reflect.*
74-
// val term = cached._2.asTerm
75-
// (term.pos.start, term.pos.end)
76-
// println(s"actual: <$start..$end>")
77-
// val msgExpr = Expr(msg)
78-
// val startExpr = Expr(start)
79-
// val endExpr = Expr(end)
80-
// '{ compiletimeErrorPos($msgExpr, $startExpr, $endExpr) }
81-
// end cacheErrorExpr
82-
83-
// transparent inline def cacheError(msg: String): Nothing = ${ cacheErrorMacro('msg) }
84-
// def cacheErrorMacro(msg: Expr[String])(using Quotes): Expr[Nothing] =
85-
// cacheErrorExpr(msg.value.get)
86-
8765
inline def apply[T](inline value: T): Exact[T] = new Exact(value)
8866
def strip(value: Any): Any =
8967
value match
@@ -96,10 +74,6 @@ object Exact:
9674
value: Expr[T]
9775
)(using Quotes, Type[T]): Expr[Exact[?]] =
9876
import quotes.reflect.*
99-
// val x = Position.ofMacroExpansion.sourceFile.path -> (quotes, value)
100-
// println("---------")
101-
// println(s"set: ${Position.ofMacroExpansion}")
102-
// cacheMap += x
10377
val exactInfo = value.exactInfo
10478
'{ Exact[exactInfo.Underlying](${ exactInfo.exactExpr }) }
10579
end fromValueMacro
@@ -287,7 +261,7 @@ private def exactOp1Macro[Op, Ctx, OutUB](lhs: Expr[Any])(ctx: Expr[Ctx])(using
287261
Expr.summon[ExactOp1[Op, OutUB, Ctx, lhsExactInfo.Underlying]] match
288262
case Some(expr) => '{ $expr(${ lhsExactInfo.exactExpr })(using $ctx) }
289263
case None =>
290-
IsGiven.controlledMacroError("Unsupported argument type for this operation.")
264+
ControlledMacroError.report("Unsupported argument type for this operation.")
291265
end match
292266
end exactOp1Macro
293267

@@ -331,7 +305,7 @@ private def exactOp2Macro[Op, Ctx, OutUB](
331305
$expr(${ lhsExactInfo.exactExpr }, ${ rhsExactInfo.exactExpr })(using $ctx)
332306
}
333307
case Left(msg) =>
334-
if (bothWays.value.get)
308+
if (bothWays.value.getOrElse(false))
335309
Expr.summonOrError[ExactOp2[
336310
Op,
337311
Ctx,
@@ -343,9 +317,9 @@ private def exactOp2Macro[Op, Ctx, OutUB](
343317
$expr(${ rhsExactInfo.exactExpr }, ${ lhsExactInfo.exactExpr })(using $ctx)
344318
}
345319
case Left(msg) =>
346-
IsGiven.controlledMacroError("Unsupported argument types for this operation.")
320+
ControlledMacroError.report("Unsupported argument types for this operation.")
347321
else
348-
IsGiven.controlledMacroError("Unsupported argument types for this operation.")
322+
ControlledMacroError.report("Unsupported argument types for this operation.")
349323
end match
350324
end exactOp2Macro
351325

@@ -393,6 +367,6 @@ private def exactOp3Macro[Op, Ctx, OutUB](
393367
)(using $ctx)
394368
}
395369
case None =>
396-
IsGiven.controlledMacroError("Unsupported argument types for this operation.")
370+
ControlledMacroError.report("Unsupported argument types for this operation.")
397371
end match
398372
end exactOp3Macro

internals/src/main/scala/dfhdl/internals/helpers.scala

Lines changed: 9 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ transparent inline def showTree[T](inline arg: T): Unit = ${
2626
}
2727

2828
def errorExpr(msg: String)(using Quotes): Expr[Nothing] =
29-
IsGiven.controlledMacroError(msg)
29+
ControlledMacroError.report(msg)
3030

3131
def showTreeMacro[T](arg: Expr[T])(using Quotes, Type[T]): Expr[Unit] =
3232
import quotes.reflect.*
@@ -133,7 +133,7 @@ object Error:
133133
case t => t.show
134134
}
135135
.mkString
136-
IsGiven.controlledMacroError(msg)
136+
ControlledMacroError.report(msg)
137137
end Error
138138

139139
transparent inline def summonInlineWithError[T]: T = ${ summonInlineWithErrorMacro[T] }
@@ -143,7 +143,7 @@ private def summonInlineWithErrorMacro[T: Type](using Quotes): Expr[T] =
143143
case iss: ImplicitSearchSuccess => iss.tree.asExprOf[T]
144144
case isf: NoMatchingImplicits => report.errorAndAbort(isf.explanation)
145145
case isf: ImplicitSearchFailure =>
146-
IsGiven.controlledMacroError(isf.explanation)
146+
ControlledMacroError.report(isf.explanation)
147147

148148
extension (using quotes: Quotes)(partsExprs: Seq[Expr[Any]])
149149
def scPartsWithArgs(argsExprs: Seq[Expr[Any]]): quotes.reflect.Term =
@@ -244,7 +244,7 @@ object CaseClass:
244244
case _ =>
245245
val msg =
246246
s"Type `${clsTpe.show}` is not a subtype of `${Type.show[UB]}`."
247-
IsGiven.controlledMacroError(msg)
247+
ControlledMacroError.report(msg)
248248
end match
249249
end macroImpl
250250
end CaseClass
@@ -272,49 +272,10 @@ object AssertGiven:
272272
if (recur(TypeRepr.of[G])) '{ Success.asInstanceOf[AssertGiven[G, M]] }
273273
else
274274
val ConstantType(StringConstant(msg)) = TypeRepr.of[M].dealias: @unchecked
275-
IsGiven.controlledMacroError(msg)
275+
ControlledMacroError.report(msg)
276276
end macroImpl
277277
end AssertGiven
278278

279-
object IsGiven:
280-
// if contains a key, it means to silence the error.
281-
// if the value is true (by default), it means the implicit given is found.
282-
// if the value is false, it means the implicit given is not found (there are errors).
283-
private val positionFlags = mutable.Map.empty[String, Boolean]
284-
private def getKey(using Quotes): String =
285-
import quotes.reflect.*
286-
Position.ofMacroExpansion.toString
287-
transparent inline def apply[G]: Boolean =
288-
silent
289-
compiletime.summonFrom {
290-
// given is found, but there could be silented errors
291-
case g: G => readAndWakeErrors
292-
// given is not found, there are errors
293-
case _ =>
294-
readAndWakeErrors
295-
false
296-
}
297-
298-
def controlledMacroError(msg: String)(using Quotes): Expr[Nothing] =
299-
import quotes.reflect.*
300-
val key = getKey
301-
if (positionFlags.contains(key))
302-
positionFlags += key -> false
303-
'{ ??? }
304-
else
305-
'{ compiletime.error(${ Expr(msg) }) }
306-
307-
private transparent inline def silent: Unit = ${ silentMacro }
308-
private def silentMacro(using Quotes): Expr[Unit] =
309-
import quotes.reflect.*
310-
positionFlags += getKey -> true
311-
'{}
312-
private transparent inline def readAndWakeErrors: Boolean = ${ readAndWakeErrorsMacro }
313-
private def readAndWakeErrorsMacro(using Quotes): Expr[Boolean] =
314-
import quotes.reflect.*
315-
Expr(positionFlags.remove(getKey).getOrElse(true))
316-
end IsGiven
317-
318279
trait OptionalGiven[T]:
319280
val value: Option[T]
320281
def get: T = value.get
@@ -341,7 +302,7 @@ object GivenOrError:
341302
case Some(t) => '{ apply[T, Msg]($t) }
342303
case None =>
343304
val ConstantType(StringConstant(msg)) = TypeRepr.of[Msg].dealias: @unchecked
344-
IsGiven.controlledMacroError(msg)
305+
ControlledMacroError.report(msg)
345306
end givenOrErrorMacro
346307
end GivenOrError
347308

@@ -592,7 +553,9 @@ end programFullPaths
592553
// checks if the program is accessible to the current shell
593554
def programIsAccessible(cmd: String): Boolean = programFullPaths(cmd).nonEmpty
594555

595-
def debugMacro(msg: => Any, fileName: String)(using Quotes): Unit =
556+
def debugMacro(msg: => Any, fileName: String = "lib\\src\\test\\scala\\Playground.scala")(using
557+
Quotes
558+
): Unit =
596559
import quotes.reflect.*
597560
if (Symbol.spliceOwner.pos.get.sourceFile.path.toString.endsWith(fileName))
598561
println(msg)

0 commit comments

Comments
 (0)