Skip to content

Commit 06e423a

Browse files
authored
Add explicit fallback to array reduction; add error value (#485)
Changes the IR and compiler to define the empty array output of array reduction operations in the frontend, allowing better type propagation (especially disambiguating float 0 and range 0 for sums). Changes the error handling to generate an error value instead of crashing the compiler, allowing a partial compile instead of a unhelpful stack trace. In the compiler, moves the param errors into the param value, while tracking overassigns separately because of the immutability of the param value system. Also tightens up what reduction operations are allowed in the array subtypes. Resolves #223
1 parent 51fedf4 commit 06e423a

33 files changed

Lines changed: 315 additions & 169 deletions

compiler/src/main/scala/edg/ExprBuilder.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,11 @@ object ExprBuilder {
4949
expr = expr.ValueExpr.Expr.Unary(expr.UnaryExpr(op = op, `val` = Some(vals)))
5050
)
5151

52-
def UnarySetOp(op: expr.UnarySetExpr.Op, vals: expr.ValueExpr): expr.ValueExpr = expr.ValueExpr(
53-
expr = expr.ValueExpr.Expr.UnarySet(expr.UnarySetExpr(op = op, vals = Some(vals)))
54-
)
52+
def UnarySetOp(op: expr.UnarySetExpr.Op, vals: expr.ValueExpr, emptyValue: expr.ValueExpr): expr.ValueExpr =
53+
expr.ValueExpr(
54+
expr =
55+
expr.ValueExpr.Expr.UnarySet(expr.UnarySetExpr(op = op, vals = Some(vals), emptyValue = Some(emptyValue)))
56+
)
5557

5658
def IfThenElse(cond: expr.ValueExpr, tru: expr.ValueExpr, fal: expr.ValueExpr): expr.ValueExpr = expr.ValueExpr(
5759
expr = expr.ValueExpr.Expr.IfThenElse(expr.IfThenElseExpr(cond = Some(cond), tru = Some(tru), fal = Some(fal)))
@@ -113,6 +115,9 @@ object ExprBuilder {
113115
}
114116

115117
object Literal {
118+
def Error(msg: Option[String]): lit.ValueLit =
119+
lit.ValueLit(`type` = lit.ValueLit.Type.Error(lit.ErrorLit(msg.getOrElse(""))))
120+
116121
def Floating(value: Double): lit.ValueLit = Floating(value.toFloat)
117122
def Floating(value: Float): lit.ValueLit = lit.ValueLit(`type` = lit.ValueLit.Type.Floating(lit.FloatLit(value)))
118123

compiler/src/main/scala/edg/compiler/Compiler.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class AssignNamer() {
117117
}
118118

119119
object Compiler {
120-
final val kExpectedProtoVersion = 10
120+
final val kExpectedProtoVersion = 11
121121
}
122122

123123
/** Compiler for a particular design, with an associated library to elaborate references from.
@@ -919,7 +919,8 @@ class Compiler private (
919919
path.asIndirect ++ portPostfix + IndirectStep.Allocated,
920920
ValueExpr.UnarySetOp(
921921
expr.UnarySetExpr.Op.FLATTEN,
922-
ValueExpr.Array(Seq(connectTerms) ++ arrayConnectTermss)
922+
ValueExpr.Array(Seq(connectTerms) ++ arrayConnectTermss),
923+
ValueExpr.Array(Seq())
923924
),
924925
path,
925926
""
@@ -1235,7 +1236,8 @@ class Compiler private (
12351236
path.asIndirect ++ portPostfix + IndirectStep.Allocated,
12361237
ValueExpr.UnarySetOp(
12371238
expr.UnarySetExpr.Op.FLATTEN,
1238-
ValueExpr.Array(Seq(connectTerms) ++ arrayConnectTermss)
1239+
ValueExpr.Array(Seq(connectTerms) ++ arrayConnectTermss),
1240+
ValueExpr.Array(Seq())
12391241
),
12401242
path,
12411243
""

compiler/src/main/scala/edg/compiler/ConstProp.scala

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -51,25 +51,25 @@ class ConstProp() {
5151
// Undeclared parameters cannot have values set, but can be forced (though the value is not effective until declared)
5252
private val paramTypes = mutable.HashMap[IndirectDesignPath, Class[_ <: ExprValue]]()
5353

54+
// This tracks overassign errors and their sources, which otherwise breaks the immutability of the dependency graph
55+
private val overassigns = mutable.HashMap[IndirectDesignPath, mutable.HashSet[IndirectDesignPath]]()
56+
5457
private val connectedLink = DependencyGraph[ConnectedLinkRecord, ConnectedLinkPort]() // tracks the port -> link paths
5558

5659
// Params that have a forced/override value, so they arent over-assigned.
5760
private val forcedParams = mutable.Set[IndirectDesignPath]()
5861

59-
// Errors that were generated during the process of resolving parameters, including overassigns
60-
// A value may or may not exist (and may or not have been propagated) in the param dependency graph
61-
private val paramErrors = mutable.HashMap[IndirectDesignPath, mutable.ListBuffer[ErrorValue]]()
62-
6362
def initFrom(that: ConstProp): Unit = {
64-
require(paramAssign.isEmpty && paramSource.isEmpty && paramTypes.isEmpty && forcedParams.isEmpty
65-
&& paramErrors.isEmpty)
63+
require(
64+
paramAssign.isEmpty && paramSource.isEmpty && paramTypes.isEmpty && forcedParams.isEmpty && overassigns.isEmpty
65+
)
6666
paramAssign.addAll(that.paramAssign)
6767
paramSource.addAll(that.paramSource)
6868
params.initFrom(that.params)
6969
paramTypes.addAll(that.paramTypes)
7070
connectedLink.initFrom(that.connectedLink)
7171
forcedParams.addAll(that.forcedParams)
72-
paramErrors.addAll(that.paramErrors)
72+
overassigns.addAll(that.overassigns)
7373
}
7474

7575
//
@@ -116,14 +116,11 @@ class ConstProp() {
116116
readyList.foreach { constrTarget =>
117117
val assign = paramAssign(constrTarget)
118118
new ExprEvaluatePartial(getValue, assign.root).map(assign.value) match {
119+
case ExprResult.Result(result @ ErrorValue(_)) =>
120+
params.setValue(constrTarget, result, stop = true)
121+
onParamSolved(constrTarget, result)
119122
case ExprResult.Result(result) =>
120-
result match {
121-
case result @ ErrorValue(_) =>
122-
paramErrors.getOrElseUpdate(constrTarget, mutable.ListBuffer()).append(result)
123-
params.clearReadyNode(constrTarget)
124-
case result => params.setValue(constrTarget, result)
125-
}
126-
123+
params.setValue(constrTarget, result)
127124
onParamSolved(constrTarget, result)
128125
case ExprResult.Missing(missing) => // account for CONNECTED_LINK prefix
129126
val missingCorrected = missing.map { path =>
@@ -211,10 +208,9 @@ class ConstProp() {
211208
} else {
212209
if (!forcedParams.contains(target)) {
213210
if (params.nodeDefinedAt(target)) { // TODO add propagated assign
214-
val (prevRoot, prevConstr, _) = paramSource.get(target).getOrElse("?", "?", "")
215-
paramErrors.getOrElseUpdate(target, mutable.ListBuffer()).append(
216-
ErrorValue(s"over-assign from $root.$constrName, prev assigned from $prevRoot.$prevConstr")
217-
)
211+
val (prevRoot, prevConstr, _) = paramSource(target)
212+
overassigns.getOrElseUpdate(target, mutable.HashSet()).add(root.asIndirect + constrName)
213+
overassigns.getOrElseUpdate(target, mutable.HashSet()).add(prevRoot.asIndirect + prevConstr)
218214
return // first set "wins"
219215
}
220216
params.addNode(target, paramTypesDep)
@@ -264,7 +260,7 @@ class ConstProp() {
264260
}
265261

266262
/** Returns the value of a parameter, or None if it does not have a value (yet?). Can be used to check if parameters
267-
* are resolved yet by testing against None. Cannot return an ErrorValue
263+
* are resolved yet by testing against None. Overassigns return their first value for consistency.
268264
*/
269265
def getValue(param: IndirectDesignPath): Option[ExprValue] = {
270266
resolveConnectedLink(param) match {
@@ -291,10 +287,16 @@ class ConstProp() {
291287
* references.
292288
*/
293289
def getUnsolved: Set[IndirectDesignPath] = {
294-
paramTypes.keySet.toSet -- params.knownValueKeys -- paramErrors.keys
290+
paramTypes.keySet.toSet -- params.knownValueKeys
291+
}
292+
293+
private def getOverassignValues: Map[IndirectDesignPath, ErrorValue] = {
294+
overassigns.map { case (target, sources) =>
295+
target -> ErrorValue(Some(s"overassign from " + sources.map(_.toString).mkString(", ")))
296+
}.toMap
295297
}
296298

297-
def getAllSolved: Map[IndirectDesignPath, ExprValue] = params.toMap
299+
def getAllSolved: Map[IndirectDesignPath, ExprValue] = params.toMap ++ getOverassignValues
298300

299301
def getAllConnections: Map[DesignPath, DesignPath] = connectedLink.toMap.collect {
300302
case (ConnectedLinkRecord.ConnectedLink(towardsBlockPort), ConnectedLinkPort(linkPath, linkPortSuffix))
@@ -303,8 +305,10 @@ class ConstProp() {
303305
}
304306

305307
def getErrors: Seq[ExprError] = {
306-
paramErrors.flatMap { case (target, errors) =>
307-
errors.map(error => ExprError(target, error.msg))
308-
}.toSeq
308+
(params.toMap.collect { case (target, ErrorValue(Some(msg))) =>
309+
ExprError(target, msg)
310+
} ++ getOverassignValues.map {
311+
case (target, error) => ExprError(target, error.msg.get)
312+
}).toSeq
309313
}
310314
}

compiler/src/main/scala/edg/compiler/DesignRefsValidate.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,12 @@ object CollectExprRefs extends ValueExprMap[Seq[ref.LocalPath]] {
3434
override def mapUnary(unary: expr.UnaryExpr, `val`: Seq[ref.LocalPath]): Seq[ref.LocalPath] = {
3535
`val`
3636
}
37-
override def mapUnarySet(unarySet: expr.UnarySetExpr, vals: Seq[ref.LocalPath]): Seq[ref.LocalPath] = {
38-
vals
37+
override def mapUnarySet(
38+
unarySet: expr.UnarySetExpr,
39+
vals: Seq[ref.LocalPath],
40+
emptyValue: Seq[ref.LocalPath]
41+
): Seq[ref.LocalPath] = {
42+
vals ++ emptyValue
3943
}
4044
override def mapArray(array: expr.ArrayExpr, vals: Seq[Seq[ref.LocalPath]]): Seq[ref.LocalPath] = {
4145
vals.flatten

compiler/src/main/scala/edg/compiler/ExprEvaluate.scala

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ object ExprEvaluate {
5959
val lower = contribMax * targetMin
6060
val upper = contribMin * targetMax
6161
if (lower > upper) {
62-
ErrorValue(s"shrink_mult($lhs, $rhs) produces empty range, target $lhs tighter tol than contrib $rhs")
62+
ErrorValue(
63+
Some(s"shrink_mult($lhs, $rhs) produces empty range, target $lhs tighter tol than contrib $rhs")
64+
)
6365
} else {
6466
RangeValue(lower, upper)
6567
}
@@ -293,8 +295,8 @@ object ExprEvaluate {
293295
case (Op.MIN, RangeValue(valMin, _)) => FloatValue(valMin)
294296
case (Op.MAX, RangeValue(_, valMax)) => FloatValue(valMax)
295297

296-
case (Op.MAX, RangeEmpty) => ErrorValue("max(RangeEmpty) is undefined")
297-
case (Op.MIN, RangeEmpty) => ErrorValue("min(RangeEmpty) is undefined")
298+
case (Op.MAX, RangeEmpty) => ErrorValue(Some("max(RangeEmpty) is undefined"))
299+
case (Op.MIN, RangeEmpty) => ErrorValue(Some("min(RangeEmpty) is undefined"))
298300

299301
case (Op.CENTER, RangeValue(valMin, valMax)) => FloatValue((valMin + valMax) / 2)
300302
case (Op.WIDTH, RangeValue(valMin, valMax)) => FloatValue(math.abs(valMax - valMin))
@@ -303,25 +305,23 @@ object ExprEvaluate {
303305
}
304306
}
305307

306-
def evalUnarySet(unarySet: expr.UnarySetExpr, vals: ExprValue): ExprValue = {
308+
def evalUnarySet(unarySet: expr.UnarySetExpr, vals: ExprValue, emptyValue: ExprValue): ExprValue = {
307309
import expr.UnarySetExpr.Op
308310
(unarySet.op, vals) match {
311+
case (_, ArrayValue.Empty(_)) => emptyValue
309312
// In this case we don't do numeric promotion
310-
case (Op.SUM, ArrayValue.Empty(_)) => FloatValue(0) // TODO type needs to be dynamic
311313
case (Op.SUM, ArrayValue.ExtractFloat(vals)) => FloatValue(vals.sum)
312314
case (Op.SUM, ArrayValue.ExtractInt(vals)) => IntValue(vals.sum)
313315
case (Op.SUM, ArrayValue.ExtractBoolean(vals)) => IntValue(vals.count(_ == true))
314316
case (Op.SUM, ArrayValue.UnpackRange(extracted)) => extracted match {
315317
case ArrayValue.UnpackRange.FullRange(valMins, valMaxs) => RangeValue(valMins.sum, valMaxs.sum)
316318
case ArrayValue.UnpackRange.RangeWithEmpty(_, _) => RangeEmpty
317319
case ArrayValue.UnpackRange.EmptyRange() => RangeEmpty
318-
case ArrayValue.UnpackRange.EmptyArray() =>
319-
RangeValue(0, 0) // unreachable in practice, superseded by float 0 case
320+
case ArrayValue.UnpackRange.EmptyArray() => // empty array case handled above
321+
throw new AssertionError("compiler internal error, shouldn't happen")
320322
}
321323

322-
case (Op.ALL_TRUE, ArrayValue.Empty(_)) => BooleanValue(true)
323324
case (Op.ALL_TRUE, ArrayValue.ExtractBoolean(vals)) => BooleanValue(vals.forall(_ == true))
324-
case (Op.ANY_TRUE, ArrayValue.Empty(_)) => BooleanValue(false)
325325
case (Op.ANY_TRUE, ArrayValue.ExtractBoolean(vals)) => BooleanValue(vals.contains(true))
326326

327327
// TODO better support for empty arrays?
@@ -335,11 +335,10 @@ object ExprEvaluate {
335335
case (Op.MINIMUM, ArrayValue.ExtractFloat(vals)) => FloatValue(vals.min)
336336
case (Op.MINIMUM, ArrayValue.ExtractInt(vals)) => IntValue(vals.min)
337337

338-
case (Op.SET_EXTRACT, ArrayValue.Empty(_)) => ErrorValue("set_extract(empty) is undefined")
339338
case (Op.SET_EXTRACT, ArrayValue(vals)) => if (vals.forall(_ == vals.head)) {
340339
vals.head
341340
} else {
342-
ErrorValue(f"set_extract($vals) with non-equal values")
341+
ErrorValue(Some(f"set_extract($vals) with non-equal values"))
343342
}
344343

345344
// Any empty value means the expression result is empty
@@ -349,21 +348,20 @@ object ExprEvaluate {
349348
if (maxMin <= minMax) {
350349
RangeValue(maxMin, minMax)
351350
} else { // does not intersect, null set
352-
ErrorValue(f"intersection($extracted) produces empty set")
351+
ErrorValue(Some(f"intersection($extracted) produces empty set"))
353352
}
354353
case ArrayValue.UnpackRange.RangeWithEmpty(_, _) => RangeEmpty
355354
case ArrayValue.UnpackRange.EmptyRange() => RangeEmpty
356-
// The implicit initial value of intersect is the full range
357-
case ArrayValue.UnpackRange.EmptyArray() => RangeValue(Float.NegativeInfinity, Float.PositiveInfinity)
358-
case _ => ErrorValue(f"intersection($vals) is undefined")
355+
case _ => ErrorValue(Some(f"intersection($vals) is undefined"))
359356
}
360357

361358
// Any value is used (empty effectively discarded)
362359
case (Op.HULL, ArrayValue.UnpackRange(extracted)) => extracted match {
363360
case ArrayValue.UnpackRange.FullRange(valMins, valMaxs) => RangeValue(valMins.min, valMaxs.max)
364361
case ArrayValue.UnpackRange.RangeWithEmpty(valMins, valMaxs) => RangeValue(valMins.min, valMaxs.max)
365362
case ArrayValue.UnpackRange.EmptyRange() => RangeEmpty
366-
case ArrayValue.UnpackRange.EmptyArray() => RangeEmpty // TODO: should this be an error?
363+
case ArrayValue.UnpackRange.EmptyArray() => // empty array case handled above
364+
throw new AssertionError("compiler internal error, shouldn't happen")
367365
}
368366
case (Op.HULL, ArrayValue.ExtractFloat(vals)) => RangeValue(vals.min, vals.max)
369367

@@ -408,7 +406,7 @@ object ExprEvaluate {
408406
case (FloatPromotable(lhs), FloatPromotable(rhs)) => if (lhs <= rhs) {
409407
RangeValue(lhs, rhs)
410408
} else {
411-
ErrorValue(s"range($minimum, $maximum) is malformed, $minimum > $maximum")
409+
ErrorValue(Some(s"range($minimum, $maximum) is malformed, $minimum > $maximum"))
412410
}
413411
case _ => throw new ExprEvaluateException(s"Unknown range operands types $minimum $maximum from $range")
414412
}
@@ -442,8 +440,8 @@ class ExprEvaluate(refs: ConstProp, root: DesignPath) extends ValueExprMap[ExprV
442440
override def mapUnary(unary: expr.UnaryExpr, `val`: ExprValue): ExprValue =
443441
ExprEvaluate.evalUnary(unary, `val`)
444442

445-
override def mapUnarySet(unarySet: expr.UnarySetExpr, vals: ExprValue): ExprValue =
446-
ExprEvaluate.evalUnarySet(unarySet, vals)
443+
override def mapUnarySet(unarySet: expr.UnarySetExpr, vals: ExprValue, emptyValue: ExprValue): ExprValue =
444+
ExprEvaluate.evalUnarySet(unarySet, vals, emptyValue)
447445

448446
override def mapArray(array: expr.ArrayExpr, vals: Seq[ExprValue]): ExprValue =
449447
ExprEvaluate.evalArray(array, vals)

compiler/src/main/scala/edg/compiler/ExprEvaluatePartial.scala

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,14 @@ class ExprEvaluatePartial(refResolver: IndirectDesignPath => Option[ExprValue],
7070
case ExprResult.Result(resVal) => ExprResult.Result(ExprEvaluate.evalUnary(unary, resVal))
7171
}
7272

73-
override def mapUnarySet(unarySet: expr.UnarySetExpr, vals: ExprResult): ExprResult = vals match {
74-
case vals @ ExprResult.Missing(_) => vals
75-
case ExprResult.Result(vals) => ExprResult.Result(ExprEvaluate.evalUnarySet(unarySet, vals))
76-
}
73+
override def mapUnarySet(unarySet: expr.UnarySetExpr, vals: ExprResult, emptyValue: ExprResult): ExprResult =
74+
(vals, emptyValue) match {
75+
case (ExprResult.Missing(vals), ExprResult.Missing(emptyValue)) => ExprResult.Missing(vals ++ emptyValue)
76+
case (vals @ ExprResult.Missing(_), _) => vals
77+
case (_, emptyValue @ ExprResult.Missing(_)) => emptyValue
78+
case (ExprResult.Result(vals), ExprResult.Result(emptyValue)) =>
79+
ExprResult.Result(ExprEvaluate.evalUnarySet(unarySet, vals, emptyValue))
80+
}
7781

7882
override def mapArray(array: expr.ArrayExpr, vals: Seq[ExprResult]): ExprResult = {
7983
val missing = vals.collect {

compiler/src/main/scala/edg/compiler/ExprToString.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class ExprToString() extends ValueExprMap[String] {
3030
val arrayElts = array.elts.map(mapLiteral)
3131
s"[${arrayElts.mkString(", ")}]"
3232
case lit.ValueLit.Type.Struct(_) => "unsupported struct"
33+
case lit.ValueLit.Type.Error(literal) => s"error(${literal.message})"
3334
case lit.ValueLit.Type.Empty => "(empty)"
3435
}
3536

@@ -143,7 +144,7 @@ class ExprToString() extends ValueExprMap[String] {
143144
}
144145
}
145146

146-
override def mapUnarySet(unarySet: expr.UnarySetExpr, vals: String): String = unarySet.op match {
147+
override def mapUnarySet(unarySet: expr.UnarySetExpr, vals: String, emptyValue: String): String = unarySet.op match {
147148
case UnarySetExprOp(op) => s"$op(${vals})"
148149
case op => s"unknown[$op](${vals})"
149150
}

compiler/src/main/scala/edg/compiler/ExprValue.scala

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ object ExprValue {
2525
}
2626

2727
def fromValueLit(literal: lit.ValueLit): ExprValue = literal.`type` match {
28+
case lit.ValueLit.Type.Error(literal) => ErrorValue(literal.message match {
29+
case "" => None
30+
case msg => Some(msg)
31+
})
2832
case lit.ValueLit.Type.Floating(literal) => FloatValue(literal.`val`.toFloat)
2933
case lit.ValueLit.Type.Integer(literal) => IntValue(literal.`val`)
3034
case lit.ValueLit.Type.Boolean(literal) => BooleanValue(literal.`val`)
@@ -47,6 +51,11 @@ object ExprValue {
4751
}
4852
}
4953

54+
case class ErrorValue(msg: Option[String]) extends ExprValue {
55+
def toLit: lit.ValueLit = Literal.Error(msg)
56+
def toStringValue: String = s"error(${msg.getOrElse("")})"
57+
}
58+
5059
// These should be consistent with what is in init.proto
5160
object FloatPromotable {
5261
def unapply(floatPromotable: FloatPromotable): Option[Float] = {
@@ -207,9 +216,3 @@ case class ArrayValue[T <: ExprValue](values: Seq[T]) extends ExprValue {
207216
s"[$valuesString]"
208217
}
209218
}
210-
211-
// a special value not in the IR that indicates and error
212-
case class ErrorValue(msg: String) extends ExprValue {
213-
override def toLit: lit.ValueLit = throw new IllegalArgumentException("cannot convert error value to literal")
214-
override def toStringValue: String = msg
215-
}

compiler/src/main/scala/edg/compiler/ValueExprMap.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ trait ValueExprMap[OutputType] {
4343
throw new NotImplementedError(s"Undefined mapBinarySet for $binarySet")
4444
def mapUnary(unary: expr.UnaryExpr, `val`: OutputType): OutputType =
4545
throw new NotImplementedError(s"Undefined mapUnary for $unary")
46-
def mapUnarySet(unarySet: expr.UnarySetExpr, vals: OutputType): OutputType =
47-
throw new NotImplementedError(s"Undefined mapBinarySet for $unarySet")
46+
def mapUnarySet(unarySet: expr.UnarySetExpr, vals: OutputType, emptyValue: OutputType): OutputType =
47+
throw new NotImplementedError(s"Undefined mapUnarySet for $unarySet")
4848
def mapArray(array: expr.ArrayExpr, vals: Seq[OutputType]): OutputType =
4949
throw new NotImplementedError(s"Undefined mapArray for $array")
5050
def mapStruct(struct: expr.StructExpr, vals: Map[String, OutputType]): OutputType =
@@ -117,7 +117,7 @@ trait ValueExprMap[OutputType] {
117117
mapUnary(unary, map(unary.`val`.get))
118118
}
119119
def wrapUnarySet(unarySet: expr.UnarySetExpr): OutputType = {
120-
mapUnarySet(unarySet, map(unarySet.vals.get))
120+
mapUnarySet(unarySet, map(unarySet.vals.get), map(unarySet.emptyValue.get))
121121
}
122122
def wrapArray(array: expr.ArrayExpr): OutputType = {
123123
mapArray(array, array.vals.map(value => map(value)))

0 commit comments

Comments
 (0)