11package edg .compiler
22
3- import scala .collection .mutable
4- import scala .collection .Set
3+ import edg .ExprBuilder
4+ import edg .compiler .CompilerError .ExprError
5+ import edg .util .DependencyGraph
6+ import edg .wir ._
57import edgir .expr .expr
68import edgir .init .init
7- import edg .wir ._
8- import edg .util .{DependencyGraph , MutableBiMap }
9- import edg .ExprBuilder
109import edgir .ref .ref .LocalPath
1110
12- case class AssignRecord ( target : IndirectDesignPath , root : DesignPath , value : expr. ValueExpr )
11+ import scala . collection .{ Set , mutable }
1312
14- case class OverassignRecord ( assigns : mutable. Set [( DesignPath , String , expr.ValueExpr )] = mutable. Set () )
13+ case class AssignRecord ( target : IndirectDesignPath , root : DesignPath , value : expr.ValueExpr )
1514
1615sealed trait ConnectedLinkRecord // a record in the connected link directed graph
1716object ConnectedLinkRecord {
@@ -43,6 +42,7 @@ class ConstProp() {
4342 // Assign statements are added to the dependency graph only when arrays are ready
4443 // This is the authoritative source for the state of any param - in the graph (and its dependencies), or value solved
4544 // CONNECTED_LINK has an empty value but indicates that the path was resolved in that data structure
45+ // NAME has an empty value but indicates declaration (existence in paramTypes)
4646 private val params = DependencyGraph [IndirectDesignPath , ExprValue ]()
4747 // Parameter types are used to track declared parameters
4848 // Undeclared parameters cannot have values set, but can be forced (though the value is not effective until declared)
@@ -53,21 +53,20 @@ class ConstProp() {
5353 // Params that have a forced/override value, so they arent over-assigned.
5454 private val forcedParams = mutable.Set [IndirectDesignPath ]()
5555
56- // Overassigns, for error tracking
57- // This only tracks overassigns that were discarded, not including assigns that took effect.
58- // Additional analysis is needed to get the full set of conflicting assigns.
59- private val discardOverassigns = mutable.HashMap [IndirectDesignPath , OverassignRecord ]()
56+ // Errors that were generated during the process of resolving parameters, including overassigns
57+ // A value may or may not exist (and may or not have been propagated) in the param dependency graph
58+ private val paramErrors = mutable.HashMap [IndirectDesignPath , mutable.ListBuffer [ErrorValue ]]()
6059
6160 def initFrom (that : ConstProp ): Unit = {
6261 require(paramAssign.isEmpty && paramSource.isEmpty && paramTypes.isEmpty && forcedParams.isEmpty
63- && discardOverassigns .isEmpty)
62+ && paramErrors .isEmpty)
6463 paramAssign.addAll(that.paramAssign)
6564 paramSource.addAll(that.paramSource)
6665 params.initFrom(that.params)
6766 paramTypes.addAll(that.paramTypes)
6867 connectedLink.initFrom(that.connectedLink)
6968 forcedParams.addAll(that.forcedParams)
70- discardOverassigns .addAll(that.discardOverassigns )
69+ paramErrors .addAll(that.paramErrors )
7170 }
7271
7372 //
@@ -108,21 +107,20 @@ class ConstProp() {
108107 connectedLink.setValue(ready, DesignPath ())
109108 }
110109
111- var readyList = Set [IndirectDesignPath ]()
110+ var readyList = Iterable [IndirectDesignPath ]()
112111 do {
113- // ignore params where we haven't seen the decl yet, to allow forced-assign when the block is expanded
114- // TODO support this for all params, including indirect ones (eg, name)
115- readyList = params.getReady.filter { elt =>
116- DesignPath .fromIndirectOption(elt) match {
117- case Some (elt) => paramTypes.keySet.contains(elt.asIndirect)
118- case None => true
119- }
120- }
112+ readyList = params.getReady
121113 readyList.foreach { constrTarget =>
122114 val assign = paramAssign(constrTarget)
123- new ExprEvaluatePartial (this , assign.root).map(assign.value) match {
115+ new ExprEvaluatePartial (getValue , assign.root).map(assign.value) match {
124116 case ExprResult .Result (result) =>
125- params.setValue(constrTarget, result)
117+ result match {
118+ case result @ ErrorValue (_) =>
119+ paramErrors.getOrElseUpdate(constrTarget, mutable.ListBuffer ()).append(result)
120+ params.clearReadyNode(constrTarget)
121+ case result => params.setValue(constrTarget, result)
122+ }
123+
126124 onParamSolved(constrTarget, result)
127125 case ExprResult .Missing (missing) => // account for CONNECTED_LINK prefix
128126 val missingCorrected = missing.map { path =>
@@ -161,6 +159,7 @@ class ConstProp() {
161159 case _ => throw new NotImplementedError (s " Unknown param declaration / init $decl" )
162160 }
163161 paramTypes.put(target.asIndirect, paramType)
162+ params.setValue(target.asIndirect + IndirectStep .Name , BooleanValue (false )) // dummy value
164163 update()
165164 }
166165
@@ -192,22 +191,29 @@ class ConstProp() {
192191 require(target.splitConnectedLink.isEmpty, " cannot set CONNECTED_LINK" )
193192 val paramSourceRecord = (root, constrName, targetExpr)
194193
194+ // ignore params where we haven't seen the decl yet, to allow forced-assign when the block is expanded
195+ val paramTypesDep = DesignPath .fromIndirectOption(target) match {
196+ case Some (path) => Seq (path.asIndirect + IndirectStep .Name )
197+ case None => Seq () // has indirect step, no direct decl
198+ }
195199 if (forced) {
196200 require(! forcedParams.contains(target), s " attempt to re-force $target" )
197201 forcedParams.add(target)
198202 require(
199203 ! params.valueDefinedAt(target),
200204 s " forced value must be set before value is resolved, prior ${paramSource(target)}"
201205 )
202- params.addNode(target, Seq () , overwrite = true ) // forced can overwrite other records
206+ params.addNode(target, paramTypesDep , overwrite = true ) // forced can overwrite other records
203207 } else {
204208 if (! forcedParams.contains(target)) {
205- if (params.nodeDefinedAt(target)) {
206- val record = discardOverassigns.getOrElseUpdate(target, OverassignRecord ())
207- record.assigns.add(paramSourceRecord)
209+ if (params.nodeDefinedAt(target)) { // TODO add propagated assign
210+ val (prevRoot, prevConstr, _) = paramSource.get(target).getOrElse(" ?" , " ?" , " " )
211+ paramErrors.getOrElseUpdate(target, mutable.ListBuffer ()).append(
212+ ErrorValue (s " over-assign from $root. $constrName, prev assigned from $prevRoot. $prevConstr" )
213+ )
208214 return // first set "wins"
209215 }
210- params.addNode(target, Seq () )
216+ params.addNode(target, paramTypesDep )
211217 } else {
212218 return // ignored - param was forced, discard the new assign
213219 }
@@ -254,7 +260,7 @@ class ConstProp() {
254260 }
255261
256262 /** Returns the value of a parameter, or None if it does not have a value (yet?). Can be used to check if parameters
257- * are resolved yet by testing against None.
263+ * are resolved yet by testing against None. Cannot return an ErrorValue
258264 */
259265 def getValue (param : IndirectDesignPath ): Option [ExprValue ] = {
260266 resolveConnectedLink(param) match {
@@ -264,7 +270,6 @@ class ConstProp() {
264270
265271 }
266272 def getValue (param : DesignPath ): Option [ExprValue ] = {
267- // TODO should this be an implicit conversion?
268273 getValue(param.asIndirect)
269274 }
270275
@@ -282,20 +287,14 @@ class ConstProp() {
282287 * references.
283288 */
284289 def getUnsolved : Set [IndirectDesignPath ] = {
285- paramTypes.keySet.toSet -- params.knownValueKeys
290+ paramTypes.keySet.toSet -- params.knownValueKeys -- paramErrors.keys
286291 }
287292
288293 def getAllSolved : Map [IndirectDesignPath , ExprValue ] = params.toMap
289294
290- def getErrors : Seq [CompilerError ] = {
291- discardOverassigns.map { case (target, record) =>
292- val propagatedAssign = paramSource.get(target).map { case (root, constrName, value) =>
293- CompilerError .OverAssignCause .Assign (target, root, constrName, value)
294- }.toSeq
295- val discardedAssigns = record.assigns.map { case (root, constrName, value) =>
296- CompilerError .OverAssignCause .Assign (target, root, constrName, value)
297- }
298- CompilerError .OverAssign (target, propagatedAssign ++ discardedAssigns)
295+ def getErrors : Seq [ExprError ] = {
296+ paramErrors.flatMap { case (target, errors) =>
297+ errors.map(error => ExprError (target, error.msg))
299298 }.toSeq
300299 }
301300}
0 commit comments