From b5a19b2a65c6e1b6223a5f4dd6ded7353d961188 Mon Sep 17 00:00:00 2001 From: Tom Wiseman Date: Mon, 1 Apr 2024 14:16:58 -0400 Subject: [PATCH 01/12] a potential solution --- .../graph/expression/TypeEvaluator.scala | 2 +- .../types/BiscayneTypeEvaluators.scala | 34 ++-- .../linking/expression/types/types.scala | 154 +++++++++--------- .../types/CascadesTypeEvaluators.scala | 34 ++-- .../linking/expression/types/types.scala | 154 +++++++++--------- .../linking/expression/types/package.scala | 128 +++++++-------- .../base/linking/expression/package.scala | 2 +- .../types/BinaryOperatorEvaluators.scala | 4 +- .../types/EngineFunctionEvaluators.scala | 129 ++++++++++----- .../expression/types/LiteralEvaluators.scala | 34 ++-- .../expression/types/LookupEvaluators.scala | 14 +- .../expression/types/TernaryIfEvaluator.scala | 8 +- .../types/UnaryOperatorEvaluators.scala | 4 +- .../expression/WdlomWomExpression.scala | 9 +- .../graph/CallElementToGraphNode.scala | 3 +- .../graph/IfElementToGraphNode.scala | 6 +- .../graph/ScatterElementToGraphNode.scala | 6 +- 17 files changed, 390 insertions(+), 335 deletions(-) diff --git a/wdl/model/draft3/src/main/scala/wdl/model/draft3/graph/expression/TypeEvaluator.scala b/wdl/model/draft3/src/main/scala/wdl/model/draft3/graph/expression/TypeEvaluator.scala index b7910478d83..98eae9b7ab5 100644 --- a/wdl/model/draft3/src/main/scala/wdl/model/draft3/graph/expression/TypeEvaluator.scala +++ b/wdl/model/draft3/src/main/scala/wdl/model/draft3/graph/expression/TypeEvaluator.scala @@ -8,7 +8,7 @@ import wom.types.WomType @typeclass trait TypeEvaluator[A] { - def evaluateType(a: A, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + def evaluateType(a: A, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] } diff --git a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala index 75c6cc297ae..cda71c6f572 100644 --- a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala +++ b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala @@ -12,7 +12,7 @@ import wom.types._ object BiscayneTypeEvaluators { implicit val keysFunctionEvaluator: TypeEvaluator[Keys] = new TypeEvaluator[Keys] { - override def evaluateType(a: Keys, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Keys, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType)) flatMap { @@ -22,7 +22,7 @@ object BiscayneTypeEvaluators { } implicit val asMapFunctionEvaluator: TypeEvaluator[AsMap] = new TypeEvaluator[AsMap] { - override def evaluateType(a: AsMap, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: AsMap, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { @@ -34,7 +34,7 @@ object BiscayneTypeEvaluators { } implicit val asPairsFunctionEvaluator: TypeEvaluator[AsPairs] = new TypeEvaluator[AsPairs] { - override def evaluateType(a: AsPairs, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: AsPairs, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType)) flatMap { @@ -44,7 +44,7 @@ object BiscayneTypeEvaluators { } implicit val collectByKeyFunctionEvaluator: TypeEvaluator[CollectByKey] = new TypeEvaluator[CollectByKey] { - override def evaluateType(a: CollectByKey, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: CollectByKey, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { @@ -67,29 +67,29 @@ object BiscayneTypeEvaluators { } implicit val minFunctionEvaluator: TypeEvaluator[Min] = new TypeEvaluator[Min] { - override def evaluateType(a: Min, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Min, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { - val type1 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues) - val type2 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues) + val type1 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues, typeAliases) + val type2 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues, typeAliases) (type1, type2) flatMapN resultTypeOfIntVsFloat("min") } } implicit val maxFunctionEvaluator: TypeEvaluator[Max] = new TypeEvaluator[Max] { - override def evaluateType(a: Max, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Max, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { - val type1 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues) - val type2 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues) + val type1 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues, typeAliases) + val type2 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues, typeAliases) (type1, type2) flatMapN resultTypeOfIntVsFloat("max") } } implicit val sepFunctionEvaluator: TypeEvaluator[Sep] = new TypeEvaluator[Sep] { - override def evaluateType(a: Sep, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Sep, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.arg2, linkedValues, WomArrayType(WomAnyType)) flatMap { @@ -103,7 +103,7 @@ object BiscayneTypeEvaluators { } implicit val subPosixFunctionEvaluator: TypeEvaluator[SubPosix] = new TypeEvaluator[SubPosix] { - override def evaluateType(a: SubPosix, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: SubPosix, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = (validateParamType(a.input, linkedValues, WomSingleFileType), @@ -113,7 +113,7 @@ object BiscayneTypeEvaluators { } implicit val suffixFunctionEvaluator: TypeEvaluator[Suffix] = new TypeEvaluator[Suffix] { - override def evaluateType(a: Suffix, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Suffix, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = (validateParamType(a.suffix, linkedValues, WomStringType), @@ -122,7 +122,7 @@ object BiscayneTypeEvaluators { } implicit val quoteFunctionEvaluator: TypeEvaluator[Quote] = new TypeEvaluator[Quote] { - override def evaluateType(a: Quote, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Quote, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomAnyType)) flatMap { @@ -136,7 +136,7 @@ object BiscayneTypeEvaluators { } implicit val sQuoteFunctionEvaluator: TypeEvaluator[SQuote] = new TypeEvaluator[SQuote] { - override def evaluateType(a: SQuote, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: SQuote, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomAnyType)) flatMap { @@ -150,7 +150,7 @@ object BiscayneTypeEvaluators { } implicit val unzipFunctionEvaluator: TypeEvaluator[Unzip] = new TypeEvaluator[Unzip] { - override def evaluateType(a: Unzip, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Unzip, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { @@ -161,7 +161,7 @@ object BiscayneTypeEvaluators { } implicit val structLiteralTypeEvaluator: TypeEvaluator[StructLiteral] = new TypeEvaluator[StructLiteral] { - override def evaluateType(a: StructLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: StructLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = // This works fine, but is not yet a strong enough type check for the WDL 1.1 spec diff --git a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/types.scala b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/types.scala index 7352c9a1376..ba477c146f8 100644 --- a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/types.scala +++ b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/types.scala @@ -18,102 +18,102 @@ import wdl.transforms.biscayne.linking.expression.types.BiscayneTypeEvaluators._ package object types { implicit val expressionTypeEvaluator: TypeEvaluator[ExpressionElement] = new TypeEvaluator[ExpressionElement] { - override def evaluateType(a: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( implicit typeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = a match { // Literals: - case a: PrimitiveLiteralExpressionElement => a.evaluateType(linkedValues)(typeEvaluator) - case a: NoneLiteralElement.type => a.evaluateType(linkedValues)(typeEvaluator) + case a: PrimitiveLiteralExpressionElement => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: NoneLiteralElement.type => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: StringLiteral => a.evaluateType(linkedValues)(typeEvaluator) - case a: StringExpression => a.evaluateType(linkedValues)(typeEvaluator) - case a: ObjectLiteral => a.evaluateType(linkedValues)(typeEvaluator) - case a: StructLiteral => a.evaluateType(linkedValues)(typeEvaluator) - case a: MapLiteral => a.evaluateType(linkedValues)(typeEvaluator) - case a: ArrayLiteral => a.evaluateType(linkedValues)(typeEvaluator) - case a: PairLiteral => a.evaluateType(linkedValues)(typeEvaluator) + case a: StringLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: StringExpression => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ObjectLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: StructLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: MapLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ArrayLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: PairLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) // Lookups and member accesses: - case a: IdentifierLookup => a.evaluateType(linkedValues)(typeEvaluator) - case a: ExpressionMemberAccess => a.evaluateType(linkedValues)(typeEvaluator) - case a: IdentifierMemberAccess => a.evaluateType(linkedValues)(typeEvaluator) - case a: IndexAccess => a.evaluateType(linkedValues)(typeEvaluator) + case a: IdentifierLookup => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ExpressionMemberAccess => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: IdentifierMemberAccess => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: IndexAccess => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) // Unary operators: - case a: UnaryNegation => a.evaluateType(linkedValues)(typeEvaluator) - case a: UnaryPlus => a.evaluateType(linkedValues)(typeEvaluator) - case a: LogicalNot => a.evaluateType(linkedValues)(typeEvaluator) + case a: UnaryNegation => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: UnaryPlus => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: LogicalNot => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) // Binary operators (at some point we might want to split these into separate cases): - case a: LogicalOr => a.evaluateType(linkedValues)(typeEvaluator) - case a: LogicalAnd => a.evaluateType(linkedValues)(typeEvaluator) - case a: Equals => a.evaluateType(linkedValues)(typeEvaluator) - case a: NotEquals => a.evaluateType(linkedValues)(typeEvaluator) - case a: LessThan => a.evaluateType(linkedValues)(typeEvaluator) - case a: LessThanOrEquals => a.evaluateType(linkedValues)(typeEvaluator) - case a: GreaterThan => a.evaluateType(linkedValues)(typeEvaluator) - case a: GreaterThanOrEquals => a.evaluateType(linkedValues)(typeEvaluator) - case a: Add => a.evaluateType(linkedValues)(typeEvaluator) - case a: Subtract => a.evaluateType(linkedValues)(typeEvaluator) - case a: Multiply => a.evaluateType(linkedValues)(typeEvaluator) - case a: Divide => a.evaluateType(linkedValues)(typeEvaluator) - case a: Remainder => a.evaluateType(linkedValues)(typeEvaluator) + case a: LogicalOr => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: LogicalAnd => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Equals => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: NotEquals => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: LessThan => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: LessThanOrEquals => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: GreaterThan => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: GreaterThanOrEquals => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Add => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Subtract => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Multiply => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Divide => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Remainder => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: TernaryIf => a.evaluateType(linkedValues)(typeEvaluator) + case a: TernaryIf => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) // Engine functions: - case a: ReadLines => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadTsv => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadMap => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadObject => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadObjects => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadJson => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadInt => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadString => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadFloat => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadBoolean => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteLines => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteTsv => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteMap => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteObject => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteObjects => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteJson => a.evaluateType(linkedValues)(typeEvaluator) - case a: Range => a.evaluateType(linkedValues)(typeEvaluator) - case a: Transpose => a.evaluateType(linkedValues)(typeEvaluator) - case a: Length => a.evaluateType(linkedValues)(typeEvaluator) - case a: Flatten => a.evaluateType(linkedValues)(typeEvaluator) - case a: Prefix => a.evaluateType(linkedValues)(typeEvaluator) - case a: Suffix => a.evaluateType(linkedValues)(typeEvaluator) - case a: SelectFirst => a.evaluateType(linkedValues)(typeEvaluator) - case a: SelectAll => a.evaluateType(linkedValues)(typeEvaluator) - case a: Defined => a.evaluateType(linkedValues)(typeEvaluator) - case a: Floor => a.evaluateType(linkedValues)(typeEvaluator) - case a: Ceil => a.evaluateType(linkedValues)(typeEvaluator) - case a: Round => a.evaluateType(linkedValues)(typeEvaluator) - case a: Glob => a.evaluateType(linkedValues)(typeEvaluator) - case a: Quote => a.evaluateType(linkedValues)(typeEvaluator) - case a: SQuote => a.evaluateType(linkedValues)(typeEvaluator) - case a: Size => a.evaluateType(linkedValues)(typeEvaluator) - case a: Basename => a.evaluateType(linkedValues)(typeEvaluator) + case a: ReadLines => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadTsv => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadMap => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadObject => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadObjects => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadJson => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadInt => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadString => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadFloat => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadBoolean => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteLines => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteTsv => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteMap => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteObject => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteObjects => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteJson => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Range => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Transpose => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Length => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Flatten => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Prefix => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Suffix => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: SelectFirst => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: SelectAll => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Defined => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Floor => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Ceil => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Round => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Glob => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Quote => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: SQuote => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Size => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Basename => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: Zip => a.evaluateType(linkedValues)(typeEvaluator) - case a: Cross => a.evaluateType(linkedValues)(typeEvaluator) - case a: Unzip => a.evaluateType(linkedValues)(typeEvaluator) + case a: Zip => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Cross => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Unzip => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: SubPosix => a.evaluateType(linkedValues)(typeEvaluator) + case a: SubPosix => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: StdoutElement.type => a.evaluateType(linkedValues)(typeEvaluator) - case a: StderrElement.type => a.evaluateType(linkedValues)(typeEvaluator) + case a: StdoutElement.type => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: StderrElement.type => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: Keys => a.evaluateType(linkedValues)(typeEvaluator) - case a: AsMap => a.evaluateType(linkedValues)(typeEvaluator) - case a: AsPairs => a.evaluateType(linkedValues)(typeEvaluator) - case a: CollectByKey => a.evaluateType(linkedValues)(typeEvaluator) - case a: Sep => a.evaluateType(linkedValues)(typeEvaluator) + case a: Keys => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: AsMap => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: AsPairs => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: CollectByKey => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Sep => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: Min => a.evaluateType(linkedValues)(typeEvaluator) - case a: Max => a.evaluateType(linkedValues)(typeEvaluator) + case a: Min => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Max => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) case other => s"Unable to process ${other.getClass.getSimpleName}: No evaluateType exists for that type.".invalidNel diff --git a/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluators.scala b/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluators.scala index 137b42128e2..0bcea02b782 100644 --- a/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluators.scala +++ b/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluators.scala @@ -12,7 +12,7 @@ import wom.types._ object cascadesTypeEvaluators { implicit val keysFunctionEvaluator: TypeEvaluator[Keys] = new TypeEvaluator[Keys] { - override def evaluateType(a: Keys, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Keys, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType)) flatMap { @@ -22,7 +22,7 @@ object cascadesTypeEvaluators { } implicit val asMapFunctionEvaluator: TypeEvaluator[AsMap] = new TypeEvaluator[AsMap] { - override def evaluateType(a: AsMap, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: AsMap, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { @@ -34,7 +34,7 @@ object cascadesTypeEvaluators { } implicit val asPairsFunctionEvaluator: TypeEvaluator[AsPairs] = new TypeEvaluator[AsPairs] { - override def evaluateType(a: AsPairs, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: AsPairs, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType)) flatMap { @@ -44,7 +44,7 @@ object cascadesTypeEvaluators { } implicit val collectByKeyFunctionEvaluator: TypeEvaluator[CollectByKey] = new TypeEvaluator[CollectByKey] { - override def evaluateType(a: CollectByKey, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: CollectByKey, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { @@ -67,29 +67,29 @@ object cascadesTypeEvaluators { } implicit val minFunctionEvaluator: TypeEvaluator[Min] = new TypeEvaluator[Min] { - override def evaluateType(a: Min, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Min, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { - val type1 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues) - val type2 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues) + val type1 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues, typeAliases) + val type2 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues, typeAliases) (type1, type2) flatMapN resultTypeOfIntVsFloat("min") } } implicit val maxFunctionEvaluator: TypeEvaluator[Max] = new TypeEvaluator[Max] { - override def evaluateType(a: Max, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Max, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { - val type1 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues) - val type2 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues) + val type1 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues, typeAliases) + val type2 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues, typeAliases) (type1, type2) flatMapN resultTypeOfIntVsFloat("max") } } implicit val sepFunctionEvaluator: TypeEvaluator[Sep] = new TypeEvaluator[Sep] { - override def evaluateType(a: Sep, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Sep, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.arg2, linkedValues, WomArrayType(WomAnyType)) flatMap { @@ -103,7 +103,7 @@ object cascadesTypeEvaluators { } implicit val subPosixFunctionEvaluator: TypeEvaluator[SubPosix] = new TypeEvaluator[SubPosix] { - override def evaluateType(a: SubPosix, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: SubPosix, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = (validateParamType(a.input, linkedValues, WomSingleFileType), @@ -113,7 +113,7 @@ object cascadesTypeEvaluators { } implicit val suffixFunctionEvaluator: TypeEvaluator[Suffix] = new TypeEvaluator[Suffix] { - override def evaluateType(a: Suffix, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Suffix, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = (validateParamType(a.suffix, linkedValues, WomStringType), @@ -122,7 +122,7 @@ object cascadesTypeEvaluators { } implicit val quoteFunctionEvaluator: TypeEvaluator[Quote] = new TypeEvaluator[Quote] { - override def evaluateType(a: Quote, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Quote, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomAnyType)) flatMap { @@ -136,7 +136,7 @@ object cascadesTypeEvaluators { } implicit val sQuoteFunctionEvaluator: TypeEvaluator[SQuote] = new TypeEvaluator[SQuote] { - override def evaluateType(a: SQuote, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: SQuote, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomAnyType)) flatMap { @@ -150,7 +150,7 @@ object cascadesTypeEvaluators { } implicit val unzipFunctionEvaluator: TypeEvaluator[Unzip] = new TypeEvaluator[Unzip] { - override def evaluateType(a: Unzip, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Unzip, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { @@ -161,7 +161,7 @@ object cascadesTypeEvaluators { } implicit val structLiteralTypeEvaluator: TypeEvaluator[StructLiteral] = new TypeEvaluator[StructLiteral] { - override def evaluateType(a: StructLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: StructLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = // This works fine, but is not yet a strong enough type check for the WDL 1.1 spec diff --git a/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/types.scala b/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/types.scala index 58e1fe888d7..62b59865a86 100644 --- a/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/types.scala +++ b/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/types.scala @@ -18,103 +18,103 @@ import wdl.transforms.biscayne.linking.expression.types.cascadesTypeEvaluators._ package object types { implicit val expressionTypeEvaluator: TypeEvaluator[ExpressionElement] = new TypeEvaluator[ExpressionElement] { - override def evaluateType(a: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType] )( implicit typeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = a match { // Literals: - case a: PrimitiveLiteralExpressionElement => a.evaluateType(linkedValues)(typeEvaluator) - case a: NoneLiteralElement.type => a.evaluateType(linkedValues)(typeEvaluator) + case a: PrimitiveLiteralExpressionElement => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: NoneLiteralElement.type => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: StringLiteral => a.evaluateType(linkedValues)(typeEvaluator) - case a: StringExpression => a.evaluateType(linkedValues)(typeEvaluator) - case a: ObjectLiteral => a.evaluateType(linkedValues)(typeEvaluator) - case a: StructLiteral => a.evaluateType(linkedValues)(typeEvaluator) - case a: MapLiteral => a.evaluateType(linkedValues)(typeEvaluator) - case a: ArrayLiteral => a.evaluateType(linkedValues)(typeEvaluator) - case a: PairLiteral => a.evaluateType(linkedValues)(typeEvaluator) + case a: StringLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: StringExpression => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ObjectLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: StructLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: MapLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ArrayLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: PairLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) // Lookups and member accesses: - case a: IdentifierLookup => a.evaluateType(linkedValues)(typeEvaluator) - case a: ExpressionMemberAccess => a.evaluateType(linkedValues)(typeEvaluator) - case a: IdentifierMemberAccess => a.evaluateType(linkedValues)(typeEvaluator) - case a: IndexAccess => a.evaluateType(linkedValues)(typeEvaluator) + case a: IdentifierLookup => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ExpressionMemberAccess => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: IdentifierMemberAccess => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: IndexAccess => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) // Unary operators: - case a: UnaryNegation => a.evaluateType(linkedValues)(typeEvaluator) - case a: UnaryPlus => a.evaluateType(linkedValues)(typeEvaluator) - case a: LogicalNot => a.evaluateType(linkedValues)(typeEvaluator) + case a: UnaryNegation => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: UnaryPlus => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: LogicalNot => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) // Binary operators (at some point we might want to split these into separate cases): - case a: LogicalOr => a.evaluateType(linkedValues)(typeEvaluator) - case a: LogicalAnd => a.evaluateType(linkedValues)(typeEvaluator) - case a: Equals => a.evaluateType(linkedValues)(typeEvaluator) - case a: NotEquals => a.evaluateType(linkedValues)(typeEvaluator) - case a: LessThan => a.evaluateType(linkedValues)(typeEvaluator) - case a: LessThanOrEquals => a.evaluateType(linkedValues)(typeEvaluator) - case a: GreaterThan => a.evaluateType(linkedValues)(typeEvaluator) - case a: GreaterThanOrEquals => a.evaluateType(linkedValues)(typeEvaluator) - case a: Add => a.evaluateType(linkedValues)(typeEvaluator) - case a: Subtract => a.evaluateType(linkedValues)(typeEvaluator) - case a: Multiply => a.evaluateType(linkedValues)(typeEvaluator) - case a: Divide => a.evaluateType(linkedValues)(typeEvaluator) - case a: Remainder => a.evaluateType(linkedValues)(typeEvaluator) + case a: LogicalOr => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: LogicalAnd => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Equals => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: NotEquals => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: LessThan => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: LessThanOrEquals => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: GreaterThan => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: GreaterThanOrEquals => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Add => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Subtract => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Multiply => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Divide => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Remainder => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: TernaryIf => a.evaluateType(linkedValues)(typeEvaluator) + case a: TernaryIf => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) // Engine functions: - case a: ReadLines => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadTsv => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadMap => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadObject => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadObjects => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadJson => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadInt => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadString => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadFloat => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadBoolean => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteLines => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteTsv => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteMap => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteObject => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteObjects => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteJson => a.evaluateType(linkedValues)(typeEvaluator) - case a: Range => a.evaluateType(linkedValues)(typeEvaluator) - case a: Transpose => a.evaluateType(linkedValues)(typeEvaluator) - case a: Length => a.evaluateType(linkedValues)(typeEvaluator) - case a: Flatten => a.evaluateType(linkedValues)(typeEvaluator) - case a: Prefix => a.evaluateType(linkedValues)(typeEvaluator) - case a: Suffix => a.evaluateType(linkedValues)(typeEvaluator) - case a: Quote => a.evaluateType(linkedValues)(typeEvaluator) - case a: SQuote => a.evaluateType(linkedValues)(typeEvaluator) - case a: SelectFirst => a.evaluateType(linkedValues)(typeEvaluator) - case a: SelectAll => a.evaluateType(linkedValues)(typeEvaluator) - case a: Defined => a.evaluateType(linkedValues)(typeEvaluator) - case a: Floor => a.evaluateType(linkedValues)(typeEvaluator) - case a: Ceil => a.evaluateType(linkedValues)(typeEvaluator) - case a: Round => a.evaluateType(linkedValues)(typeEvaluator) - case a: Glob => a.evaluateType(linkedValues)(typeEvaluator) + case a: ReadLines => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadTsv => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadMap => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadObject => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadObjects => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadJson => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadInt => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadString => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadFloat => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadBoolean => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteLines => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteTsv => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteMap => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteObject => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteObjects => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteJson => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Range => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Transpose => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Length => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Flatten => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Prefix => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Suffix => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Quote => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: SQuote => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: SelectFirst => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: SelectAll => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Defined => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Floor => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Ceil => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Round => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Glob => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: Size => a.evaluateType(linkedValues)(typeEvaluator) - case a: Basename => a.evaluateType(linkedValues)(typeEvaluator) + case a: Size => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Basename => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: Zip => a.evaluateType(linkedValues)(typeEvaluator) - case a: Cross => a.evaluateType(linkedValues)(typeEvaluator) - case a: Unzip => a.evaluateType(linkedValues)(typeEvaluator) + case a: Zip => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Cross => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Unzip => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: SubPosix => a.evaluateType(linkedValues)(typeEvaluator) + case a: SubPosix => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: StdoutElement.type => a.evaluateType(linkedValues)(typeEvaluator) - case a: StderrElement.type => a.evaluateType(linkedValues)(typeEvaluator) + case a: StdoutElement.type => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: StderrElement.type => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: Keys => a.evaluateType(linkedValues)(typeEvaluator) - case a: AsMap => a.evaluateType(linkedValues)(typeEvaluator) - case a: AsPairs => a.evaluateType(linkedValues)(typeEvaluator) - case a: CollectByKey => a.evaluateType(linkedValues)(typeEvaluator) - case a: Sep => a.evaluateType(linkedValues)(typeEvaluator) + case a: Keys => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: AsMap => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: AsPairs => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: CollectByKey => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Sep => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: Min => a.evaluateType(linkedValues)(typeEvaluator) - case a: Max => a.evaluateType(linkedValues)(typeEvaluator) + case a: Min => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Max => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) case other => s"Unable to process ${other.getClass.getSimpleName}: No evaluateType exists for that type.".invalidNel diff --git a/wdl/transforms/draft3/src/main/scala/wdl/draft3/transforms/linking/expression/types/package.scala b/wdl/transforms/draft3/src/main/scala/wdl/draft3/transforms/linking/expression/types/package.scala index 4651a0d4c18..7956057fc63 100644 --- a/wdl/transforms/draft3/src/main/scala/wdl/draft3/transforms/linking/expression/types/package.scala +++ b/wdl/transforms/draft3/src/main/scala/wdl/draft3/transforms/linking/expression/types/package.scala @@ -17,87 +17,87 @@ import wom.types.WomType package object types { implicit val expressionTypeEvaluator: TypeEvaluator[ExpressionElement] = new TypeEvaluator[ExpressionElement] { - override def evaluateType(a: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( implicit typeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = a match { // Literals: - case a: PrimitiveLiteralExpressionElement => a.evaluateType(linkedValues)(typeEvaluator) - case a: StringLiteral => a.evaluateType(linkedValues)(typeEvaluator) - case a: StringExpression => a.evaluateType(linkedValues)(typeEvaluator) - case a: ObjectLiteral => a.evaluateType(linkedValues)(typeEvaluator) - case a: MapLiteral => a.evaluateType(linkedValues)(typeEvaluator) - case a: ArrayLiteral => a.evaluateType(linkedValues)(typeEvaluator) - case a: PairLiteral => a.evaluateType(linkedValues)(typeEvaluator) + case a: PrimitiveLiteralExpressionElement => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: StringLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: StringExpression => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ObjectLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: MapLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ArrayLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: PairLiteral => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) // Lookups and member accesses: - case a: IdentifierLookup => a.evaluateType(linkedValues)(typeEvaluator) - case a: ExpressionMemberAccess => a.evaluateType(linkedValues)(typeEvaluator) - case a: IdentifierMemberAccess => a.evaluateType(linkedValues)(typeEvaluator) - case a: IndexAccess => a.evaluateType(linkedValues)(typeEvaluator) + case a: IdentifierLookup => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ExpressionMemberAccess => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: IdentifierMemberAccess => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: IndexAccess => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) // Unary operators: - case a: UnaryNegation => a.evaluateType(linkedValues)(typeEvaluator) - case a: UnaryPlus => a.evaluateType(linkedValues)(typeEvaluator) - case a: LogicalNot => a.evaluateType(linkedValues)(typeEvaluator) + case a: UnaryNegation => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: UnaryPlus => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: LogicalNot => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) // Binary operators (at some point we might want to split these into separate cases): - case a: LogicalOr => a.evaluateType(linkedValues)(typeEvaluator) - case a: LogicalAnd => a.evaluateType(linkedValues)(typeEvaluator) - case a: Equals => a.evaluateType(linkedValues)(typeEvaluator) - case a: NotEquals => a.evaluateType(linkedValues)(typeEvaluator) - case a: LessThan => a.evaluateType(linkedValues)(typeEvaluator) - case a: LessThanOrEquals => a.evaluateType(linkedValues)(typeEvaluator) - case a: GreaterThan => a.evaluateType(linkedValues)(typeEvaluator) - case a: GreaterThanOrEquals => a.evaluateType(linkedValues)(typeEvaluator) - case a: Add => a.evaluateType(linkedValues)(typeEvaluator) - case a: Subtract => a.evaluateType(linkedValues)(typeEvaluator) - case a: Multiply => a.evaluateType(linkedValues)(typeEvaluator) - case a: Divide => a.evaluateType(linkedValues)(typeEvaluator) - case a: Remainder => a.evaluateType(linkedValues)(typeEvaluator) + case a: LogicalOr => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: LogicalAnd => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Equals => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: NotEquals => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: LessThan => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: LessThanOrEquals => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: GreaterThan => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: GreaterThanOrEquals => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Add => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Subtract => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Multiply => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Divide => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Remainder => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: TernaryIf => a.evaluateType(linkedValues)(typeEvaluator) + case a: TernaryIf => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) // Engine functions: - case a: ReadLines => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadTsv => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadMap => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadObject => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadObjects => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadJson => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadInt => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadString => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadFloat => a.evaluateType(linkedValues)(typeEvaluator) - case a: ReadBoolean => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteLines => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteTsv => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteMap => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteObject => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteObjects => a.evaluateType(linkedValues)(typeEvaluator) - case a: WriteJson => a.evaluateType(linkedValues)(typeEvaluator) - case a: Range => a.evaluateType(linkedValues)(typeEvaluator) - case a: Transpose => a.evaluateType(linkedValues)(typeEvaluator) - case a: Length => a.evaluateType(linkedValues)(typeEvaluator) - case a: Flatten => a.evaluateType(linkedValues)(typeEvaluator) - case a: Prefix => a.evaluateType(linkedValues)(typeEvaluator) - case a: SelectFirst => a.evaluateType(linkedValues)(typeEvaluator) - case a: SelectAll => a.evaluateType(linkedValues)(typeEvaluator) - case a: Defined => a.evaluateType(linkedValues)(typeEvaluator) - case a: Floor => a.evaluateType(linkedValues)(typeEvaluator) - case a: Ceil => a.evaluateType(linkedValues)(typeEvaluator) - case a: Round => a.evaluateType(linkedValues)(typeEvaluator) - case a: Glob => a.evaluateType(linkedValues)(typeEvaluator) + case a: ReadLines => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadTsv => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadMap => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadObject => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadObjects => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadJson => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadInt => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadString => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadFloat => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: ReadBoolean => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteLines => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteTsv => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteMap => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteObject => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteObjects => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: WriteJson => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Range => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Transpose => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Length => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Flatten => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Prefix => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: SelectFirst => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: SelectAll => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Defined => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Floor => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Ceil => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Round => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Glob => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: Size => a.evaluateType(linkedValues)(typeEvaluator) - case a: Basename => a.evaluateType(linkedValues)(typeEvaluator) + case a: Size => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Basename => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: Zip => a.evaluateType(linkedValues)(typeEvaluator) - case a: Cross => a.evaluateType(linkedValues)(typeEvaluator) + case a: Zip => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: Cross => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: Sub => a.evaluateType(linkedValues)(typeEvaluator) + case a: Sub => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) - case a: StdoutElement.type => a.evaluateType(linkedValues)(typeEvaluator) - case a: StderrElement.type => a.evaluateType(linkedValues)(typeEvaluator) + case a: StdoutElement.type => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) + case a: StderrElement.type => a.evaluateType(linkedValues, typeAliases)(typeEvaluator) case other => s"Unable to process ${other.getClass.getSimpleName}: No evaluateType exists for that type.".invalidNel diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/package.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/package.scala index 59f98ee63b5..45d12abd8b2 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/package.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/package.scala @@ -32,7 +32,7 @@ package object expression { s"Could not create WOM expression for '$a': Found no generated value for consumed value $missing".invalidNel } - neededLinkedValues flatMap { lookup => WdlomWomExpression.make(a, lookup.toMap) } + neededLinkedValues flatMap { lookup => WdlomWomExpression.make(a, lookup.toMap, typeAliases) } } } diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/BinaryOperatorEvaluators.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/BinaryOperatorEvaluators.scala index c18a0aadc84..6fe735ffeef 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/BinaryOperatorEvaluators.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/BinaryOperatorEvaluators.scala @@ -28,10 +28,10 @@ object BinaryOperatorEvaluators { implicit val remainderEvaluator: TypeEvaluator[Remainder] = forOperation(_.mod(_)) private def forOperation[A <: BinaryOperation](op: (WomType, WomType) => Try[WomType]) = new TypeEvaluator[A] { - override def evaluateType(a: A, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: A, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - (a.left.evaluateType(linkedValues), a.right.evaluateType(linkedValues)) flatMapN { (left, right) => + (a.left.evaluateType(linkedValues, typeAliases), a.right.evaluateType(linkedValues, typeAliases)) flatMapN { (left, right) => op(left, right).toErrorOr } } diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/EngineFunctionEvaluators.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/EngineFunctionEvaluators.scala index 8268cdaac72..0b0013b87a8 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/EngineFunctionEvaluators.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/EngineFunctionEvaluators.scala @@ -20,7 +20,8 @@ object EngineFunctionEvaluators { implicit val stdoutFunctionEvaluator: TypeEvaluator[StdoutElement.type] = new TypeEvaluator[ExpressionElement.StdoutElement.type] { override def evaluateType(a: ExpressionElement.StdoutElement.type, - linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle] + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = WomSingleFileType.validNel } @@ -28,48 +29,55 @@ object EngineFunctionEvaluators { implicit val stderrFunctionEvaluator: TypeEvaluator[StderrElement.type] = new TypeEvaluator[ExpressionElement.StderrElement.type] { override def evaluateType(a: ExpressionElement.StderrElement.type, - linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle] + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = WomSingleFileType.validNel } implicit val readLinesFunctionEvaluator: TypeEvaluator[ReadLines] = new TypeEvaluator[ReadLines] { - override def evaluateType(a: ReadLines, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: ReadLines, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomArrayType(WomStringType)) } implicit val readTsvFunctionEvaluator: TypeEvaluator[ReadTsv] = new TypeEvaluator[ReadTsv] { - override def evaluateType(a: ReadTsv, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: ReadTsv, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomArrayType(WomArrayType(WomStringType))) } implicit val readMapFunctionEvaluator: TypeEvaluator[ReadMap] = new TypeEvaluator[ReadMap] { - override def evaluateType(a: ReadMap, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: ReadMap, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomMapType(WomStringType, WomStringType)) } implicit val readObjectFunctionEvaluator: TypeEvaluator[ReadObject] = new TypeEvaluator[ReadObject] { - override def evaluateType(a: ReadObject, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: ReadObject, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomObjectType) } implicit val readObjectsFunctionEvaluator: TypeEvaluator[ReadObjects] = new TypeEvaluator[ReadObjects] { - override def evaluateType(a: ReadObjects, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: ReadObjects, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomArrayType(WomObjectType)) } implicit val readJsonFunctionEvaluator: TypeEvaluator[ReadJson] = new TypeEvaluator[ReadJson] { - override def evaluateType(a: ReadJson, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: ReadJson, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = // we can't figure out the WomType of data without reading the file hence evaluate it to `WomAnyType` @@ -77,73 +85,83 @@ object EngineFunctionEvaluators { } implicit val readIntFunctionEvaluator: TypeEvaluator[ReadInt] = new TypeEvaluator[ReadInt] { - override def evaluateType(a: ReadInt, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: ReadInt, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomIntegerType) } implicit val readStringFunctionEvaluator: TypeEvaluator[ReadString] = new TypeEvaluator[ReadString] { - override def evaluateType(a: ReadString, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: ReadString, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomStringType) } implicit val readFloatFunctionEvaluator: TypeEvaluator[ReadFloat] = new TypeEvaluator[ReadFloat] { - override def evaluateType(a: ReadFloat, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: ReadFloat, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomFloatType) } implicit val readBooleanFunctionEvaluator: TypeEvaluator[ReadBoolean] = new TypeEvaluator[ReadBoolean] { - override def evaluateType(a: ReadBoolean, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: ReadBoolean, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomBooleanType) } implicit val writeLinesFunctionEvaluator: TypeEvaluator[WriteLines] = new TypeEvaluator[WriteLines] { - override def evaluateType(a: WriteLines, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: WriteLines, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomStringType)).map(_ => WomSingleFileType) } implicit val writeTsvFunctionEvaluator: TypeEvaluator[WriteTsv] = new TypeEvaluator[WriteTsv] { - override def evaluateType(a: WriteTsv, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: WriteTsv, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomArrayType(WomStringType))).map(_ => WomSingleFileType) } implicit val writeMapFunctionEvaluator: TypeEvaluator[WriteMap] = new TypeEvaluator[WriteMap] { - override def evaluateType(a: WriteMap, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: WriteMap, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType)).map(_ => WomSingleFileType) } implicit val writeObjectFunctionEvaluator: TypeEvaluator[WriteObject] = new TypeEvaluator[WriteObject] { - override def evaluateType(a: WriteObject, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: WriteObject, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomObjectType).map(_ => WomSingleFileType) } implicit val writeObjectsFunctionEvaluator: TypeEvaluator[WriteObjects] = new TypeEvaluator[WriteObjects] { - override def evaluateType(a: WriteObjects, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: WriteObjects, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomObjectType)).map(_ => WomSingleFileType) } implicit val writeJsonFunctionEvaluator: TypeEvaluator[WriteJson] = new TypeEvaluator[WriteJson] { - override def evaluateType(a: WriteJson, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: WriteJson, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - a.param.evaluateType(linkedValues).flatMap { + a.param.evaluateType(linkedValues, typeAliases).flatMap { case v if WomBooleanType.isCoerceableFrom(v) || WomIntegerType.isCoerceableFrom(v) || @@ -160,17 +178,19 @@ object EngineFunctionEvaluators { } implicit val rangeFunctionEvaluator: TypeEvaluator[Range] = new TypeEvaluator[Range] { - override def evaluateType(a: Range, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Range, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomIntegerType).map(_ => WomArrayType(WomIntegerType)) } implicit val transposeFunctionEvaluator: TypeEvaluator[Transpose] = new TypeEvaluator[Transpose] { - override def evaluateType(a: Transpose, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Transpose, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - a.param.evaluateType(linkedValues).flatMap { + a.param.evaluateType(linkedValues, typeAliases).flatMap { case a @ WomArrayType(WomArrayType(_)) => a.validNel case foundType => s"Invalid parameter '${a.param}'. Expected 'Array[Array[_]]' but got '${foundType.stableName}'".invalidNel @@ -178,17 +198,19 @@ object EngineFunctionEvaluators { } implicit val lengthFunctionEvaluator: TypeEvaluator[Length] = new TypeEvaluator[Length] { - override def evaluateType(a: Length, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Length, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomAnyType)).map(_ => WomIntegerType) } implicit val flattenFunctionEvaluator: TypeEvaluator[Flatten] = new TypeEvaluator[Flatten] { - override def evaluateType(a: Flatten, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Flatten, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - a.param.evaluateType(linkedValues).flatMap { + a.param.evaluateType(linkedValues, typeAliases).flatMap { case WomArrayType(inner @ WomArrayType(_)) => inner.validNel case foundType => s"Invalid parameter '${a.param}'. Expected 'Array[Array[_]]' but got '${foundType.stableName}'".invalidNel @@ -196,10 +218,11 @@ object EngineFunctionEvaluators { } implicit val selectFirstFunctionEvaluator: TypeEvaluator[SelectFirst] = new TypeEvaluator[SelectFirst] { - override def evaluateType(a: SelectFirst, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: SelectFirst, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - a.param.evaluateType(linkedValues).flatMap { + a.param.evaluateType(linkedValues, typeAliases).flatMap { case WomArrayType(WomOptionalType(inner)) => inner.validNel case WomArrayType(alreadyNonOptional) => alreadyNonOptional.validNel case foundType => @@ -208,10 +231,11 @@ object EngineFunctionEvaluators { } implicit val selectAllFunctionEvaluator: TypeEvaluator[SelectAll] = new TypeEvaluator[SelectAll] { - override def evaluateType(a: SelectAll, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: SelectAll, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - a.param.evaluateType(linkedValues).flatMap { + a.param.evaluateType(linkedValues, typeAliases).flatMap { case WomArrayType(WomOptionalType(inner)) => WomArrayType(inner).validNel case alreadyNonOptional: WomArrayType => alreadyNonOptional.validNel case foundType => @@ -220,35 +244,40 @@ object EngineFunctionEvaluators { } implicit val definedFunctionEvaluator: TypeEvaluator[Defined] = new TypeEvaluator[Defined] { - override def evaluateType(a: Defined, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Defined, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomOptionalType(WomAnyType)).map(_ => WomBooleanType) } implicit val floorFunctionEvaluator: TypeEvaluator[Floor] = new TypeEvaluator[Floor] { - override def evaluateType(a: Floor, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Floor, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomFloatType).map(_ => WomIntegerType) } implicit val ceilFunctionEvaluator: TypeEvaluator[Ceil] = new TypeEvaluator[Ceil] { - override def evaluateType(a: Ceil, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Ceil, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomFloatType).map(_ => WomIntegerType) } implicit val roundFunctionEvaluator: TypeEvaluator[Round] = new TypeEvaluator[Round] { - override def evaluateType(a: Round, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Round, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomFloatType).map(_ => WomIntegerType) } implicit val globFunctionTypeEvaluator: TypeEvaluator[Glob] = new TypeEvaluator[Glob] { - override def evaluateType(a: Glob, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Glob, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomStringType).map(_ => WomArrayType(WomSingleFileType)) @@ -262,14 +291,15 @@ object EngineFunctionEvaluators { case _ => false } - override def evaluateType(a: Size, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Size, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { val validatedSecondArg: ErrorOr[Unit] = a.secondParam match { case None => ().validNel case Some(arg) => validateParamType(arg, linkedValues, WomStringType).void } - val validatedFirstArg: ErrorOr[Unit] = a.firstParam.evaluateType(linkedValues).flatMap { + val validatedFirstArg: ErrorOr[Unit] = a.firstParam.evaluateType(linkedValues, typeAliases).flatMap { case t if suitableSizeType(t) => ().validNel case other => s"Invalid first 'size' parameter. Expected File, File? Array[File] or Array[File?] but got ${other.stableName}".invalidNel @@ -279,7 +309,8 @@ object EngineFunctionEvaluators { } implicit val basenameFunctionEvaluator: TypeEvaluator[Basename] = new TypeEvaluator[Basename] { - override def evaluateType(a: Basename, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Basename, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { val validatedSecondArg: ErrorOr[Unit] = a.secondParam match { @@ -296,8 +327,9 @@ object EngineFunctionEvaluators { private def crossOrZipType(arg1: ExpressionElement, arg2: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle] - )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = - (arg1.evaluateType(linkedValues), arg2.evaluateType(linkedValues)) match { + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = { + //TODO: can/should we pipe type aliases through here? + (arg1.evaluateType(linkedValues, Map()), arg2.evaluateType(linkedValues, Map())) match { case (Valid(WomArrayType(left)), Valid(WomArrayType(right))) => WomArrayType(WomPairType(left, right)).validNel case (Valid(otherLeft), Valid(WomArrayType(_))) => @@ -309,22 +341,26 @@ object EngineFunctionEvaluators { // One or more are invalid, so mapN function won't actually ever run: case (otherLeft, otherRight) => (otherLeft, otherRight) mapN { (_, _) => WomNothingType } } + } implicit val zipFunctionEvaluator: TypeEvaluator[Zip] = new TypeEvaluator[Zip] { - override def evaluateType(a: Zip, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Zip, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = crossOrZipType(a.arg1, a.arg2, linkedValues) } implicit val crossFunctionEvaluator: TypeEvaluator[Cross] = new TypeEvaluator[Cross] { - override def evaluateType(a: Cross, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Cross, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = crossOrZipType(a.arg1, a.arg2, linkedValues) } implicit val prefixFunctionEvaluator: TypeEvaluator[Prefix] = new TypeEvaluator[Prefix] { - override def evaluateType(a: Prefix, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Prefix, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = (validateParamType(a.prefix, linkedValues, WomStringType), @@ -333,7 +369,8 @@ object EngineFunctionEvaluators { } implicit val subFunctionEvaluator: TypeEvaluator[Sub] = new TypeEvaluator[Sub] { - override def evaluateType(a: Sub, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: Sub, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = (validateParamType(a.input, linkedValues, WomSingleFileType), @@ -345,11 +382,13 @@ object EngineFunctionEvaluators { def validateParamType(param: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], expectedType: WomType - )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = - param.evaluateType(linkedValues).flatMap { foundType => + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = { + //TODO: pipe type aliases through here + param.evaluateType(linkedValues, Map()).flatMap { foundType => if (expectedType.isCoerceableFrom(foundType)) { foundType.validNel } else { s"Invalid parameter '$param'. Expected '${expectedType.stableName}' but got '${foundType.stableName}'".invalidNel } } + } } diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LiteralEvaluators.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LiteralEvaluators.scala index 9e74672f5bc..48ad93a8e2a 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LiteralEvaluators.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LiteralEvaluators.scala @@ -16,7 +16,8 @@ object LiteralEvaluators { implicit val primitiveTypeEvaluator: TypeEvaluator[PrimitiveLiteralExpressionElement] = new TypeEvaluator[PrimitiveLiteralExpressionElement] { override def evaluateType(a: PrimitiveLiteralExpressionElement, - linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle] + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = a.value.womType.validNel } @@ -24,38 +25,43 @@ object LiteralEvaluators { implicit val noneLiteralTypeEvaluator: TypeEvaluator[NoneLiteralElement.type] = new TypeEvaluator[NoneLiteralElement.type] { override def evaluateType(a: NoneLiteralElement.type, - linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle] + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = WomOptionalType(WomNothingType).validNel } implicit val objectLiteralTypeEvaluator: TypeEvaluator[ObjectLiteral] = new TypeEvaluator[ObjectLiteral] { - override def evaluateType(a: ObjectLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: ObjectLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = WomObjectType.validNel } implicit val stringLiteralTypeEvaluator: TypeEvaluator[StringLiteral] = new TypeEvaluator[StringLiteral] { - override def evaluateType(a: StringLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: StringLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = WomStringType.validNel } implicit val stringExpressionTypeEvaluator: TypeEvaluator[StringExpression] = new TypeEvaluator[StringExpression] { - override def evaluateType(a: StringExpression, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: StringExpression, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = WomStringType.validNel } implicit val mapLiteralTypeEvaluator: TypeEvaluator[MapLiteral] = new TypeEvaluator[MapLiteral] { - override def evaluateType(a: MapLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: MapLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { - val keyTypes = a.elements.keySet.toList.traverse { x: ExpressionElement => x.evaluateType(linkedValues) } + val keyTypes = a.elements.keySet.toList.traverse { x: ExpressionElement => x.evaluateType(linkedValues, typeAliases) } val commonKeyType: ErrorOr[WomType] = keyTypes.map(WomType.homogeneousTypeFromTypes) - val valueTypes = a.elements.values.toList.traverse { y: ExpressionElement => y.evaluateType(linkedValues) } + val valueTypes = a.elements.values.toList.traverse { y: ExpressionElement => y.evaluateType(linkedValues, typeAliases) } val commonValueType: ErrorOr[WomType] = valueTypes.map(WomType.homogeneousTypeFromTypes) (commonKeyType, commonValueType) mapN { (k, v) => WomMapType(k, v) } @@ -63,11 +69,12 @@ object LiteralEvaluators { } implicit val arrayLiteralTypeEvaluator: TypeEvaluator[ArrayLiteral] = new TypeEvaluator[ArrayLiteral] { - override def evaluateType(a: ArrayLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: ArrayLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { - val types = a.elements.toList.traverse { x: ExpressionElement => x.evaluateType(linkedValues) } + val types = a.elements.toList.traverse { x: ExpressionElement => x.evaluateType(linkedValues, typeAliases) } val commonType: ErrorOr[WomType] = types.map(WomType.homogeneousTypeFromTypes) commonType.map(WomArrayType.apply(_, guaranteedNonEmpty = a.elements.nonEmpty)) @@ -75,12 +82,13 @@ object LiteralEvaluators { } implicit val pairLiteralTypeEvaluator: TypeEvaluator[PairLiteral] = new TypeEvaluator[PairLiteral] { - override def evaluateType(a: PairLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: PairLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { - val leftType = a.left.evaluateType(linkedValues) - val rightType = a.right.evaluateType(linkedValues) + val leftType = a.left.evaluateType(linkedValues,typeAliases: Map[String, WomType]) + val rightType = a.right.evaluateType(linkedValues,typeAliases: Map[String, WomType]) (leftType, rightType) mapN { (l, r) => WomPairType(l, r) } } diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LookupEvaluators.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LookupEvaluators.scala index 0221390e8ca..6e11eb02eb4 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LookupEvaluators.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LookupEvaluators.scala @@ -15,7 +15,7 @@ import wom.types._ object LookupEvaluators { implicit val identifierLookupTypeEvaluator: TypeEvaluator[IdentifierLookup] = new TypeEvaluator[IdentifierLookup] { - override def evaluateType(a: IdentifierLookup, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: IdentifierLookup, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = linkedValues.collectFirst { @@ -32,9 +32,10 @@ object LookupEvaluators { implicit val expressionMemberAccessEvaluator: TypeEvaluator[ExpressionMemberAccess] = new TypeEvaluator[ExpressionMemberAccess] { override def evaluateType(a: ExpressionMemberAccess, - linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle] + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = { - val baseType = a.expression.evaluateType(linkedValues) + val baseType = a.expression.evaluateType(linkedValues, typeAliases) baseType flatMap { doLookup(_, a.memberAccessTail) } } } @@ -42,7 +43,8 @@ object LookupEvaluators { implicit val identifierMemberAccessEvaluator: TypeEvaluator[IdentifierMemberAccess] = new TypeEvaluator[IdentifierMemberAccess] { override def evaluateType(a: IdentifierMemberAccess, - linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle] + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = { val generatedValueHandle = linkedValues.get(UnlinkedCallOutputOrIdentifierAndMemberAccessHook(a.first, a.second)) @@ -64,10 +66,10 @@ object LookupEvaluators { } implicit val indexAccessTypeEvaluator: TypeEvaluator[IndexAccess] = new TypeEvaluator[IndexAccess] { - override def evaluateType(a: IndexAccess, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + override def evaluateType(a: IndexAccess, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - (a.expressionElement.evaluateType(linkedValues), a.index.evaluateType(linkedValues), a.index.validNel) flatMapN { + (a.expressionElement.evaluateType(linkedValues, typeAliases), a.index.evaluateType(linkedValues, typeAliases), a.index.validNel) flatMapN { case (a: WomArrayType, WomIntegerType, _) => a.memberType.validNel case (WomMapType(keyType, valueType), lookupType, _) if keyType.isCoerceableFrom(lookupType) => valueType.validNel diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/TernaryIfEvaluator.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/TernaryIfEvaluator.scala index ca4b1d61a7f..126c48aff72 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/TernaryIfEvaluator.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/TernaryIfEvaluator.scala @@ -13,13 +13,13 @@ import wom.types.{WomBooleanType, WomType} object TernaryIfEvaluator { implicit val ternaryIfEvaluator: TypeEvaluator[TernaryIf] = new TypeEvaluator[TernaryIf] { - override def evaluateType(a: TernaryIf, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: TernaryIf, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - a.condition.evaluateType(linkedValues) flatMap { + a.condition.evaluateType(linkedValues, typeAliases) flatMap { case WomBooleanType => - (a.ifTrue.evaluateType(linkedValues): ErrorOr[WomType], - a.ifFalse.evaluateType(linkedValues): ErrorOr[WomType] + (a.ifTrue.evaluateType(linkedValues, typeAliases): ErrorOr[WomType], + a.ifFalse.evaluateType(linkedValues, typeAliases): ErrorOr[WomType] ) mapN { (tType, fType) => WomType.homogeneousTypeFromTypes(Seq(tType, fType)) } case other => s"Condition should have evaluated to a Boolean but instead got ${other.stableName}".invalidNel } diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/UnaryOperatorEvaluators.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/UnaryOperatorEvaluators.scala index 3c2cb62721e..9bbcf5b5b77 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/UnaryOperatorEvaluators.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/UnaryOperatorEvaluators.scala @@ -19,9 +19,9 @@ object UnaryOperatorEvaluators { implicit val logicalNotEvaluator: TypeEvaluator[LogicalNot] = forOperation(_.not) private def forOperation[A <: UnaryOperation](op: WomType => Try[WomType]) = new TypeEvaluator[A] { - override def evaluateType(a: A, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])(implicit + override def evaluateType(a: A, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - a.argument.evaluateType(linkedValues) flatMap { op(_).toErrorOr } + a.argument.evaluateType(linkedValues, typeAliases) flatMap { op(_).toErrorOr } } } diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/expression/WdlomWomExpression.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/expression/WdlomWomExpression.scala index f94c15aa7b7..9e6a1630523 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/expression/WdlomWomExpression.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/expression/WdlomWomExpression.scala @@ -15,7 +15,8 @@ import wom.types._ import wom.values.WomValue final case class WdlomWomExpression private (expressionElement: ExpressionElement, - linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle] + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] )(implicit expressionValueConsumer: ExpressionValueConsumer[ExpressionElement], fileEvaluator: FileEvaluator[ExpressionElement], @@ -36,7 +37,7 @@ final case class WdlomWomExpression private (expressionElement: ExpressionElemen override def evaluateValue(inputValues: Map[String, WomValue], ioFunctionSet: IoFunctionSet): ErrorOr[WomValue] = expressionElement.evaluateValue(inputValues, ioFunctionSet, None) map { _.value } - private lazy val evaluatedType = expressionElement.evaluateType(linkedValues) + private lazy val evaluatedType = expressionElement.evaluateType(linkedValues, typeAliases) // NB types can be determined using the linked values, so we don't need the inputMap: override def evaluateType(inputMap: Map[String, WomType]): ErrorOr[WomType] = evaluatedType @@ -52,14 +53,14 @@ final case class WdlomWomExpression private (expressionElement: ExpressionElemen } object WdlomWomExpression { - def make(expressionElement: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle])( + def make(expressionElement: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], typeAliases: Map[String, WomType])( implicit expressionValueConsumer: ExpressionValueConsumer[ExpressionElement], fileEvaluator: FileEvaluator[ExpressionElement], typeEvaluator: TypeEvaluator[ExpressionElement], valueEvaluator: ValueEvaluator[ExpressionElement] ): ErrorOr[WdlomWomExpression] = { - val candidate = WdlomWomExpression(expressionElement, linkedValues) + val candidate = WdlomWomExpression(expressionElement, linkedValues, typeAliases) candidate.evaluatedType.contextualizeErrors(s"process expression '${candidate.sourceString}'") map { _ => candidate } diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/CallElementToGraphNode.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/CallElementToGraphNode.scala index 6aa36184aef..cca46bee462 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/CallElementToGraphNode.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/CallElementToGraphNode.scala @@ -101,7 +101,8 @@ object CallElementToGraphNode { case _: CallableTaskDefinition => TaskCallInputExpressionNode.apply _ case _ => PlainAnonymousExpressionNode.apply _ } - WdlomWomExpression.make(expression, a.linkableValues) flatMap { wdlomWomExpression => + //TODO: pipe type aliases through here + WdlomWomExpression.make(expression, a.linkableValues, Map()) flatMap { wdlomWomExpression => val requiredInputType = i match { case _: OverridableInputDefinitionWithDefault => WomOptionalType(i.womType).flatOptionalType case _ => i.womType diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/IfElementToGraphNode.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/IfElementToGraphNode.scala index 7c53c5c0cd6..6843dd0e6f3 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/IfElementToGraphNode.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/IfElementToGraphNode.scala @@ -32,8 +32,9 @@ object IfElementToGraphNode { val conditionExpression = a.node.conditionExpression val graphElements = a.node.graphElements + //TODO: pipe type aliases through here val conditionWomExpressionV: ErrorOr[WdlomWomExpression] = - WdlomWomExpression.make(conditionExpression, a.linkableValues) + WdlomWomExpression.make(conditionExpression, a.linkableValues, Map()) val conditionExpressionNodeValidation: ErrorOr[AnonymousExpressionNode] = conditionWomExpressionV flatMap { conditionWomExpression => AnonymousExpressionNode.fromInputMapping(WomIdentifier("if_condition"), @@ -43,7 +44,8 @@ object IfElementToGraphNode { ) } - val conditionVariableTypeValidation: ErrorOr[Unit] = conditionExpression.evaluateType(a.linkableValues) flatMap { + //TODO: Can/should we pipe type aliases through here? + val conditionVariableTypeValidation: ErrorOr[Unit] = conditionExpression.evaluateType(a.linkableValues, Map[String,WomType]()) flatMap { case WomBooleanType | WomAnyType => ().validNel case other => s"Invalid type for condition variable: ${other.stableName}".invalidNel } diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/ScatterElementToGraphNode.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/ScatterElementToGraphNode.scala index a1a3d5c4d76..93aceb74d26 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/ScatterElementToGraphNode.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/ScatterElementToGraphNode.scala @@ -60,8 +60,9 @@ object ScatterElementToGraphNode { val scatterVariableName = a.node.scatterVariableName val graphElements = a.node.graphElements + //TODO: pipe type aliases through here val scatterWomExpressionV: ErrorOr[WdlomWomExpression] = - WdlomWomExpression.make(scatterExpression, a.linkableValues) + WdlomWomExpression.make(scatterExpression, a.linkableValues, Map()) val scatterExpressionNodeValidation: ErrorOr[AnonymousExpressionNode] = scatterWomExpressionV flatMap { scatterWomExpression => AnonymousExpressionNode.fromInputMapping(WomIdentifier(scatterVariableName), @@ -71,7 +72,8 @@ object ScatterElementToGraphNode { ) } - val scatterVariableTypeValidation: ErrorOr[WomType] = scatterExpression.evaluateType(a.linkableValues) flatMap { + //TODO: pipe type aliases through here. + val scatterVariableTypeValidation: ErrorOr[WomType] = scatterExpression.evaluateType(a.linkableValues, Map() ) flatMap { case a: WomArrayType => a.memberType.validNel case WomAnyType => WomAnyType.validNel case other => s"Invalid type for scatter variable '$scatterVariableName': ${other.stableName}".invalidNel From f135ba8489b80e5d1e431d4b983c64da1f7a424a Mon Sep 17 00:00:00 2001 From: Tom Wiseman Date: Wed, 3 Apr 2024 10:57:16 -0400 Subject: [PATCH 02/12] stash changes before demo --- .../languages/util/LanguageFactoryUtil.scala | 6 ++- .../biscayne/WdlBiscayneLanguageFactory.scala | 18 ++++++++- .../call-a/execution/script | 38 +++++++++++++++++++ .../call-a/execution/script | 38 +++++++++++++++++++ .../types/BiscayneTypeEvaluators.scala | 26 ++++++++++++- .../types/CascadesTypeEvaluatorSpec.scala | 2 +- 6 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/script create mode 100644 struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/script diff --git a/languageFactories/language-factory-core/src/main/scala/cromwell/languages/util/LanguageFactoryUtil.scala b/languageFactories/language-factory-core/src/main/scala/cromwell/languages/util/LanguageFactoryUtil.scala index 8fabb445bac..d212e1727d9 100644 --- a/languageFactories/language-factory-core/src/main/scala/cromwell/languages/util/LanguageFactoryUtil.scala +++ b/languageFactories/language-factory-core/src/main/scala/cromwell/languages/util/LanguageFactoryUtil.scala @@ -51,12 +51,16 @@ object LanguageFactoryUtil { } } - def validateWomNamespace(womExecutable: Executable, ioFunctions: IoFunctionSet): Checked[ValidatedWomNamespace] = + def validateWomNamespace(womExecutable: Executable, ioFunctions: IoFunctionSet): Checked[ValidatedWomNamespace] = { + val allNodes = womExecutable.graph.allNodes + println(allNodes) for { evaluatedInputs <- validateExecutableInputs(womExecutable.resolvedExecutableInputs, ioFunctions).toEither validatedWomNamespace = ValidatedWomNamespace(womExecutable, evaluatedInputs, Map.empty) _ <- validateWdlFiles(validatedWomNamespace.womValueInputs) } yield validatedWomNamespace + } + /* * At this point input values are either a WomValue (if it was provided through the input file) diff --git a/languageFactories/wdl-biscayne/src/main/scala/languages/wdl/biscayne/WdlBiscayneLanguageFactory.scala b/languageFactories/wdl-biscayne/src/main/scala/languages/wdl/biscayne/WdlBiscayneLanguageFactory.scala index eace0c91f9b..9e2b220f92a 100644 --- a/languageFactories/wdl-biscayne/src/main/scala/languages/wdl/biscayne/WdlBiscayneLanguageFactory.scala +++ b/languageFactories/wdl-biscayne/src/main/scala/languages/wdl/biscayne/WdlBiscayneLanguageFactory.scala @@ -66,7 +66,22 @@ class WdlBiscayneLanguageFactory(override val config: Config) extends LanguageFa convertNestedScatterToSubworkflow: Boolean = true ): Checked[WomBundle] = { - val converter: CheckedAtoB[FileStringParserInput, WomBundle] = + val converter: CheckedAtoB[FileStringParserInput, WomBundle] = { + val ast = stringToAst + val wrapped = ast andThen wrapAst + val elems = wrapped andThen astToFileElement.map( + FileElementToWomBundleInputs( + _, + workflowOptionsJson, + convertNestedScatterToSubworkflow, + importResolvers, + languageFactories, + workflowDefinitionElementToWomWorkflowDefinition, + taskDefinitionElementToWomTaskDefinition + ) + ) andThen fileElementToWomBundle + + println(elems) stringToAst andThen wrapAst andThen astToFileElement.map( FileElementToWomBundleInputs( _, @@ -78,6 +93,7 @@ class WdlBiscayneLanguageFactory(override val config: Config) extends LanguageFa taskDefinitionElementToWomTaskDefinition ) ) andThen fileElementToWomBundle + } lazy val validationCallable = new Callable[ErrorOr[WomBundle]] { def call: ErrorOr[WomBundle] = converter diff --git a/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/script b/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/script new file mode 100644 index 00000000000..f70da48eceb --- /dev/null +++ b/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/script @@ -0,0 +1,38 @@ +#!/bin/bash + +cd /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution +tmpDir=$(mktemp -d "$PWD"/tmp.XXXXXX) +chmod 777 "$tmpDir" +export _JAVA_OPTIONS=-Djava.io.tmpdir="$tmpDir" +export TMPDIR="$tmpDir" + + +cd /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution + + + +out19f376d1="${tmpDir}/out.$$" err19f376d1="${tmpDir}/err.$$" +mkfifo "$out19f376d1" "$err19f376d1" +trap 'rm "$out19f376d1" "$err19f376d1"' EXIT +touch '/cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/stdout' '/cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/stderr' +tee '/cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/stdout' < "$out19f376d1" & +tee '/cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/stderr' < "$err19f376d1" >&2 & +( +cd /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution + + +echo "44" +) > "$out19f376d1" 2> "$err19f376d1" +echo $? > /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/rc.tmp +( +# add a .file in every empty directory to facilitate directory delocalization on the cloud +cd /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution +find . -type d -exec sh -c '[ -z "$(ls -A '"'"'{}'"'"')" ] && touch '"'"'{}'"'"'/.file' \; +) +( +cd /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution +sync + + +) +mv /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/rc.tmp /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/rc diff --git a/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/script b/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/script new file mode 100644 index 00000000000..b0b7d20e221 --- /dev/null +++ b/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/script @@ -0,0 +1,38 @@ +#!/bin/bash + +cd /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution +tmpDir=$(mktemp -d "$PWD"/tmp.XXXXXX) +chmod 777 "$tmpDir" +export _JAVA_OPTIONS=-Djava.io.tmpdir="$tmpDir" +export TMPDIR="$tmpDir" + + +cd /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution + + + +outd341ca12="${tmpDir}/out.$$" errd341ca12="${tmpDir}/err.$$" +mkfifo "$outd341ca12" "$errd341ca12" +trap 'rm "$outd341ca12" "$errd341ca12"' EXIT +touch '/cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/stdout' '/cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/stderr' +tee '/cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/stdout' < "$outd341ca12" & +tee '/cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/stderr' < "$errd341ca12" >&2 & +( +cd /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution + + +echo "44" +) > "$outd341ca12" 2> "$errd341ca12" +echo $? > /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/rc.tmp +( +# add a .file in every empty directory to facilitate directory delocalization on the cloud +cd /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution +find . -type d -exec sh -c '[ -z "$(ls -A '"'"'{}'"'"')" ] && touch '"'"'{}'"'"'/.file' \; +) +( +cd /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution +sync + + +) +mv /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/rc.tmp /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/rc diff --git a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala index cda71c6f572..2591b2ef01e 100644 --- a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala +++ b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala @@ -8,7 +8,7 @@ import wdl.model.draft3.elements.ExpressionElement._ import wdl.model.draft3.graph.expression.TypeEvaluator import wdl.model.draft3.graph.{GeneratedValueHandle, UnlinkedConsumedValueHook} import wdl.transforms.base.linking.expression.types.EngineFunctionEvaluators.validateParamType -import wom.types._ +import wom.types.{WomCompositeType, WomType, _} object BiscayneTypeEvaluators { implicit val keysFunctionEvaluator: TypeEvaluator[Keys] = new TypeEvaluator[Keys] { @@ -163,12 +163,34 @@ object BiscayneTypeEvaluators { implicit val structLiteralTypeEvaluator: TypeEvaluator[StructLiteral] = new TypeEvaluator[StructLiteral] { override def evaluateType(a: StructLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] - ): ErrorOr[WomType] = + ): ErrorOr[WomType] = { + typeAliases.get(a.structTypeName) map { definition => + definition match { + case compositeType: WomCompositeType => { + a.elements.map { case (memberKey, memberExpressionElement) => { + val evaluatedType = expressionTypeEvaluator.evaluateType(memberExpressionElement, linkedValues, typeAliases) + val expectedType = compositeType.typeMap.get(memberKey) + } + "".invalidNel + }.invalidNel + } + case _ => s"Could not find type definition for struct literal ${a.structTypeName}".invalidNel + } + } + + + val element = a.elements.head._2 + val evaluatedType = expressionTypeEvaluator.evaluateType(element, linkedValues, typeAliases) + println(evaluatedType) + //a.elements.get("id").asInstanceOf[Some].value.asInstanceOf[PrimitiveLiteralExpressionElement].value.asWomExpression.value.womType + //val expressionElementMap = a.elements + //val expressionElement = expressionElementMap.head._2 // This works fine, but is not yet a strong enough type check for the WDL 1.1 spec // (i.e. users are able to instantiate struct literals with k/v pairs that aren't actually in the struct definition, without an error being thrown.) // We want to add extra validation here, and return a WomCompositeType that matches the struct definition of everything is OK. // Note that users are allowed to omit optional k/v pairs in their literal. // This requires some extra work to be done in a subsequent PR. WomObjectType.validNel + } } } diff --git a/wdl/transforms/cascades/src/test/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluatorSpec.scala b/wdl/transforms/cascades/src/test/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluatorSpec.scala index 0bef2d7a6ef..66c8ff00134 100644 --- a/wdl/transforms/cascades/src/test/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluatorSpec.scala +++ b/wdl/transforms/cascades/src/test/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluatorSpec.scala @@ -16,7 +16,7 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomIntegerType + e.evaluateType(Map.empty, Map.empty) shouldBeValid WomIntegerType } } From 262df280caca16dbe3869273e69893bed5a41af4 Mon Sep 17 00:00:00 2001 From: Tom Wiseman Date: Mon, 8 Apr 2024 11:58:06 -0400 Subject: [PATCH 03/12] working PoC --- .../types/BiscayneTypeEvaluators.scala | 34 +++++-------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala index 2591b2ef01e..08bf58200f3 100644 --- a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala +++ b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala @@ -164,33 +164,17 @@ object BiscayneTypeEvaluators { override def evaluateType(a: StructLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { - typeAliases.get(a.structTypeName) map { definition => - definition match { - case compositeType: WomCompositeType => { - a.elements.map { case (memberKey, memberExpressionElement) => { - val evaluatedType = expressionTypeEvaluator.evaluateType(memberExpressionElement, linkedValues, typeAliases) - val expectedType = compositeType.typeMap.get(memberKey) - } - "".invalidNel - }.invalidNel + typeAliases.get(a.structTypeName) map { + case compositeType: WomCompositeType => + val errors = a.elements.flatMap { case (memberKey, memberExpressionElement) => + val evaluatedType = expressionTypeEvaluator.evaluateType(memberExpressionElement, linkedValues, typeAliases) + val expectedType = compositeType.typeMap.get(memberKey) + if (evaluatedType.toOption != expectedType) Some(s"${memberKey} of type ${evaluatedType.toString} found, expected ${expectedType.toString}\n") else None } - case _ => s"Could not find type definition for struct literal ${a.structTypeName}".invalidNel - } + if (errors.isEmpty) compositeType.validNel else errors.toString.invalidNel + case _ => s"Could not find struct definition for struct literal ${a.structTypeName}".invalidNel } - - - val element = a.elements.head._2 - val evaluatedType = expressionTypeEvaluator.evaluateType(element, linkedValues, typeAliases) - println(evaluatedType) - //a.elements.get("id").asInstanceOf[Some].value.asInstanceOf[PrimitiveLiteralExpressionElement].value.asWomExpression.value.womType - //val expressionElementMap = a.elements - //val expressionElement = expressionElementMap.head._2 - // This works fine, but is not yet a strong enough type check for the WDL 1.1 spec - // (i.e. users are able to instantiate struct literals with k/v pairs that aren't actually in the struct definition, without an error being thrown.) - // We want to add extra validation here, and return a WomCompositeType that matches the struct definition of everything is OK. - // Note that users are allowed to omit optional k/v pairs in their literal. - // This requires some extra work to be done in a subsequent PR. - WomObjectType.validNel + s"Could not find struct definition for struct literal ${a.structTypeName}".invalidNel } } } From ee782523ee3f9a09ffbe70171c5f78774158ce51 Mon Sep 17 00:00:00 2001 From: Tom Wiseman Date: Mon, 8 Apr 2024 17:00:43 -0400 Subject: [PATCH 04/12] works with inputs now, too --- .../types/BiscayneTypeEvaluators.scala | 104 ++++++++++++++---- .../graph/CallElementToGraphNode.scala | 8 +- 2 files changed, 87 insertions(+), 25 deletions(-) diff --git a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala index 08bf58200f3..06660194f61 100644 --- a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala +++ b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala @@ -8,11 +8,14 @@ import wdl.model.draft3.elements.ExpressionElement._ import wdl.model.draft3.graph.expression.TypeEvaluator import wdl.model.draft3.graph.{GeneratedValueHandle, UnlinkedConsumedValueHook} import wdl.transforms.base.linking.expression.types.EngineFunctionEvaluators.validateParamType -import wom.types.{WomCompositeType, WomType, _} +import wom.types._ object BiscayneTypeEvaluators { implicit val keysFunctionEvaluator: TypeEvaluator[Keys] = new TypeEvaluator[Keys] { - override def evaluateType(a: Keys, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Keys, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType)) flatMap { @@ -22,7 +25,10 @@ object BiscayneTypeEvaluators { } implicit val asMapFunctionEvaluator: TypeEvaluator[AsMap] = new TypeEvaluator[AsMap] { - override def evaluateType(a: AsMap, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: AsMap, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { @@ -34,7 +40,10 @@ object BiscayneTypeEvaluators { } implicit val asPairsFunctionEvaluator: TypeEvaluator[AsPairs] = new TypeEvaluator[AsPairs] { - override def evaluateType(a: AsPairs, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: AsPairs, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType)) flatMap { @@ -44,8 +53,11 @@ object BiscayneTypeEvaluators { } implicit val collectByKeyFunctionEvaluator: TypeEvaluator[CollectByKey] = new TypeEvaluator[CollectByKey] { - override def evaluateType(a: CollectByKey, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: CollectByKey, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { case WomArrayType(WomPairType(x: WomPrimitiveType, y)) => WomMapType(x, WomArrayType(y)).validNel @@ -67,7 +79,10 @@ object BiscayneTypeEvaluators { } implicit val minFunctionEvaluator: TypeEvaluator[Min] = new TypeEvaluator[Min] { - override def evaluateType(a: Min, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Min, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { val type1 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues, typeAliases) @@ -78,7 +93,10 @@ object BiscayneTypeEvaluators { } implicit val maxFunctionEvaluator: TypeEvaluator[Max] = new TypeEvaluator[Max] { - override def evaluateType(a: Max, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Max, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { val type1 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues, typeAliases) @@ -89,7 +107,10 @@ object BiscayneTypeEvaluators { } implicit val sepFunctionEvaluator: TypeEvaluator[Sep] = new TypeEvaluator[Sep] { - override def evaluateType(a: Sep, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Sep, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.arg2, linkedValues, WomArrayType(WomAnyType)) flatMap { @@ -103,7 +124,10 @@ object BiscayneTypeEvaluators { } implicit val subPosixFunctionEvaluator: TypeEvaluator[SubPosix] = new TypeEvaluator[SubPosix] { - override def evaluateType(a: SubPosix, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: SubPosix, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = (validateParamType(a.input, linkedValues, WomSingleFileType), @@ -113,7 +137,10 @@ object BiscayneTypeEvaluators { } implicit val suffixFunctionEvaluator: TypeEvaluator[Suffix] = new TypeEvaluator[Suffix] { - override def evaluateType(a: Suffix, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Suffix, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = (validateParamType(a.suffix, linkedValues, WomStringType), @@ -122,7 +149,10 @@ object BiscayneTypeEvaluators { } implicit val quoteFunctionEvaluator: TypeEvaluator[Quote] = new TypeEvaluator[Quote] { - override def evaluateType(a: Quote, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Quote, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomAnyType)) flatMap { @@ -136,7 +166,10 @@ object BiscayneTypeEvaluators { } implicit val sQuoteFunctionEvaluator: TypeEvaluator[SQuote] = new TypeEvaluator[SQuote] { - override def evaluateType(a: SQuote, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: SQuote, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomAnyType)) flatMap { @@ -150,7 +183,10 @@ object BiscayneTypeEvaluators { } implicit val unzipFunctionEvaluator: TypeEvaluator[Unzip] = new TypeEvaluator[Unzip] { - override def evaluateType(a: Unzip, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Unzip, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { @@ -161,20 +197,46 @@ object BiscayneTypeEvaluators { } implicit val structLiteralTypeEvaluator: TypeEvaluator[StructLiteral] = new TypeEvaluator[StructLiteral] { - override def evaluateType(a: StructLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + + def typeCheckHelper(typeName: String, + memberName: String, + evaluatedType: Option[WomType], + expectedType: Option[WomType] + ): Option[String] = + evaluatedType match { + case Some(evaluated) => + expectedType match { + case Some(expected) => + if (expected.isCoerceableFrom(evaluated)) None + else + Some(s"$typeName.$memberName expected to be ${expected.friendlyName}. Found ${evaluated.friendlyName}") + case None => Some(s"Type $typeName does not have a member called $memberName") + } + case None => Some(s"Error evaluating the type of ${typeName}.${memberName}") + } + override def evaluateType(a: StructLiteral, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { - typeAliases.get(a.structTypeName) map { + val myType = typeAliases.get(a.structTypeName) + val myEvaluatedType = myType map { case compositeType: WomCompositeType => val errors = a.elements.flatMap { case (memberKey, memberExpressionElement) => - val evaluatedType = expressionTypeEvaluator.evaluateType(memberExpressionElement, linkedValues, typeAliases) + val evaluatedType = + expressionTypeEvaluator.evaluateType(memberExpressionElement, linkedValues, typeAliases).toOption val expectedType = compositeType.typeMap.get(memberKey) - if (evaluatedType.toOption != expectedType) Some(s"${memberKey} of type ${evaluatedType.toString} found, expected ${expectedType.toString}\n") else None + typeCheckHelper(a.structTypeName, memberKey, evaluatedType, expectedType) + } + if (errors.isEmpty) { + WomCompositeType(compositeType.typeMap, Some(a.structTypeName)).validNel + } else { + errors.toString.invalidNel } - if (errors.isEmpty) compositeType.validNel else errors.toString.invalidNel case _ => s"Could not find struct definition for struct literal ${a.structTypeName}".invalidNel } - s"Could not find struct definition for struct literal ${a.structTypeName}".invalidNel + myEvaluatedType.getOrElse(s"Type map does not include definition for struct literal ${a.structTypeName}".invalidNel) } } } diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/CallElementToGraphNode.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/CallElementToGraphNode.scala index cca46bee462..1e2a3bff562 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/CallElementToGraphNode.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/CallElementToGraphNode.scala @@ -82,7 +82,7 @@ object CallElementToGraphNode { * * @return ErrorOr of LocalName(key) mapped to ExpressionNode(value). */ - def expressionNodeMappings(callable: Callable): ErrorOr[Map[LocalName, AnonymousExpressionNode]] = { + def expressionNodeMappings(callable: Callable, typeAliases: Map[String, WomType]): ErrorOr[Map[LocalName, AnonymousExpressionNode]] = { def hasDeclaration(callable: Callable, name: String): Boolean = callable match { case t: TaskDefinition => @@ -101,8 +101,8 @@ object CallElementToGraphNode { case _: CallableTaskDefinition => TaskCallInputExpressionNode.apply _ case _ => PlainAnonymousExpressionNode.apply _ } - //TODO: pipe type aliases through here - WdlomWomExpression.make(expression, a.linkableValues, Map()) flatMap { wdlomWomExpression => + + WdlomWomExpression.make(expression, a.linkableValues, typeAliases) flatMap { wdlomWomExpression => val requiredInputType = i match { case _: OverridableInputDefinitionWithDefault => WomOptionalType(i.womType).flatOptionalType case _ => i.womType @@ -239,7 +239,7 @@ object CallElementToGraphNode { val result = for { callable <- callableValidation - mappings <- expressionNodeMappings(callable) + mappings <- expressionNodeMappings(callable, a.availableTypeAliases) identifier = WomIdentifier(localName = callName, fullyQualifiedName = a.workflowName + "." + callName) upstream <- findUpstreamCalls(a.node.afters.toList) result = callNodeBuilder.build(identifier, From a388526229cf536f60624259730f7266ac3e5f68 Mon Sep 17 00:00:00 2001 From: Tom Wiseman Date: Mon, 8 Apr 2024 18:44:02 -0400 Subject: [PATCH 05/12] add some tests, need a few more --- .../types/BiscayneTypeEvaluators.scala | 39 ++++++--- .../types/BiscayneTypeEvaluatorSpec.scala | 85 +++++++++++++++---- 2 files changed, 96 insertions(+), 28 deletions(-) diff --git a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala index 06660194f61..49c363abbd3 100644 --- a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala +++ b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala @@ -198,6 +198,24 @@ object BiscayneTypeEvaluators { implicit val structLiteralTypeEvaluator: TypeEvaluator[StructLiteral] = new TypeEvaluator[StructLiteral] { + /* + def isMissingMembers(foundMembers: Map[String, WomType], expectedMembers: Map[String, WomType]): Option[String] = { + val errors: Iterable[String] = expectedMembers flatMap { case (memberName, memberType) => + memberType match { + case WomOptionalType(_) => None // It's OK if an Optional type is not found. + case _ => if (!foundMembers.contains(memberName)) Some(s"Expected member ${memberName} not found.") else None + } + } + errors match { + case Nil => None + case _ => Some(errors.mkString) + } + } + */ + def areTypesAssignable(a: WomType, b: WomType): Boolean = + !a.equalsType(b).isFailure + + def typeCheckHelper(typeName: String, memberName: String, evaluatedType: Option[WomType], @@ -207,21 +225,21 @@ object BiscayneTypeEvaluators { case Some(evaluated) => expectedType match { case Some(expected) => - if (expected.isCoerceableFrom(evaluated)) None + if (areTypesAssignable(evaluated, expected)) None else - Some(s"$typeName.$memberName expected to be ${expected.friendlyName}. Found ${evaluated.friendlyName}") - case None => Some(s"Type $typeName does not have a member called $memberName") + Some(s"$typeName.$memberName expected to be ${expected.friendlyName}. Found ${evaluated.friendlyName}.") + case None => Some(s"Type $typeName does not have a member called $memberName.") } - case None => Some(s"Error evaluating the type of ${typeName}.${memberName}") + case None => Some(s"Error evaluating the type of ${typeName}.${memberName}.") } + override def evaluateType(a: StructLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] - ): ErrorOr[WomType] = { - val myType = typeAliases.get(a.structTypeName) - val myEvaluatedType = myType map { + ): ErrorOr[WomType] = + typeAliases.get(a.structTypeName) map { case compositeType: WomCompositeType => val errors = a.elements.flatMap { case (memberKey, memberExpressionElement) => val evaluatedType = @@ -232,11 +250,10 @@ object BiscayneTypeEvaluators { if (errors.isEmpty) { WomCompositeType(compositeType.typeMap, Some(a.structTypeName)).validNel } else { - errors.toString.invalidNel + errors.mkString(",").invalidNel } case _ => s"Could not find struct definition for struct literal ${a.structTypeName}".invalidNel - } - myEvaluatedType.getOrElse(s"Type map does not include definition for struct literal ${a.structTypeName}".invalidNel) - } + } getOrElse s"Type map does not include definition for struct literal ${a.structTypeName}".invalidNel + } } diff --git a/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala b/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala index 0c5c158b542..4edb1303b18 100644 --- a/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala +++ b/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala @@ -11,12 +11,28 @@ import wdl.transforms.biscayne.ast2wdlom._ import wom.types._ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec with Matchers { + + val plantTypeMap: Map[String, WomType] = Map( + "isTasty" -> WomBooleanType, + "count" -> WomIntegerType + ) + + val animalTypeMap: Map[String, WomType] = Map( + "isMaybeGood" -> WomOptionalType(WomBooleanType), + "hat" -> WomCompositeType(plantTypeMap, Some("Plant")) + ) + + val typeAliases: Map[String, WomType] = Map( + "Plant" -> WomCompositeType(plantTypeMap, Some("Plant")), + "Animal" -> WomCompositeType(plantTypeMap, Some("Animal")) + ) + it should "return nothing from static integer addition" in { val str = "3 + 3" val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomIntegerType + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomIntegerType } } @@ -25,7 +41,7 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomMapType(WomIntegerType, WomIntegerType) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomMapType(WomIntegerType, WomIntegerType) } } @@ -34,7 +50,7 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomArrayType(WomPairType(WomStringType, WomIntegerType)) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomArrayType(WomPairType(WomStringType, WomIntegerType)) } } @@ -43,7 +59,7 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomStringType + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomStringType } } @@ -52,7 +68,7 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomStringType + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomStringType } } @@ -61,7 +77,7 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomStringType + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomStringType } } @@ -70,7 +86,7 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomArrayType(WomStringType) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomArrayType(WomStringType) } } @@ -79,7 +95,7 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomArrayType(WomStringType) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomArrayType(WomStringType) } } @@ -88,7 +104,7 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomArrayType(WomNothingType) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomArrayType(WomNothingType) } } @@ -97,7 +113,7 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomArrayType(WomStringType) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomArrayType(WomStringType) } } @@ -106,7 +122,7 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomArrayType(WomNothingType) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomArrayType(WomNothingType) } } @@ -114,13 +130,17 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val string_and_int = """ unzip([("one", 1),("two", 2),("three", 3)]) """ val string_and_int_expr = fromString[ExpressionElement](string_and_int, parser.parse_e) string_and_int_expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomPairType(WomArrayType(WomStringType), WomArrayType(WomIntegerType)) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomPairType(WomArrayType(WomStringType), + WomArrayType(WomIntegerType) + ) } val int_and_int = """ unzip([(1,2),(3,4),(5,6)]) """ val int_and_int_expr = fromString[ExpressionElement](int_and_int, parser.parse_e) int_and_int_expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomPairType(WomArrayType(WomIntegerType), WomArrayType(WomIntegerType)) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomPairType(WomArrayType(WomIntegerType), + WomArrayType(WomIntegerType) + ) } } @@ -128,17 +148,48 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val empty = """ unzip([]) """ val empty_unzip_expr = fromString[ExpressionElement](empty, parser.parse_e) empty_unzip_expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomPairType(WomArrayType(WomAnyType), WomArrayType(WomAnyType)) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomPairType(WomArrayType(WomAnyType), + WomArrayType(WomAnyType) + ) } } it should "evaluate the type of a struct literal" in { - // NB: This is not yet strict enough type checking for the WDL 1.1 spec. - // In a subsequent branch, we will make this be a WomCompositeType that matches the struct definition. + val structLiteral = """ Plant{isTasty: true, count: 42} """ + val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) + structExpr.shouldBeValidPF { case e => + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomCompositeType(plantTypeMap, Some("Plant")) + } + } + + it should "fail to evaluate the type of a struct literal with incorrect members" in { val structLiteral = """ Animal{fur: "fuzzy", isGood: true} """ val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) structExpr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomObjectType + e.evaluateType(Map.empty, + typeAliases + ) shouldBeInvalid "Type Animal does not have a member called fur.,Type Animal does not have a member called isGood." + } + } + + it should "fail to evaluate the type of a struct literal with members that are the wrong type" in { + val structLiteral = """ Plant{isTasty: true, count: "four"} """ + val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) + structExpr.shouldBeValidPF { case e => + e.evaluateType(Map.empty, + typeAliases + ) shouldBeInvalid "Plant.count expected to be Int. Found String." + } + } + + it should "fail if a member is missing" in { + val structLiteral = """ Plant{count: 4} """ + val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) + structExpr.shouldBeValidPF { case e => + e.evaluateType(Map.empty, + typeAliases + ) shouldBeInvalid "Plant.count expected to be Int. Found String." } } + } From c4b6521274ad81f572fbe2b4cc0a29c066bc8e8c Mon Sep 17 00:00:00 2001 From: Tom Wiseman Date: Tue, 9 Apr 2024 09:24:06 -0400 Subject: [PATCH 06/12] add the final tests --- .../types/BiscayneTypeEvaluators.scala | 118 ++++++++++++------ .../types/BiscayneTypeEvaluatorSpec.scala | 21 +++- 2 files changed, 96 insertions(+), 43 deletions(-) diff --git a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala index 49c363abbd3..eb615eb00ec 100644 --- a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala +++ b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala @@ -1,5 +1,6 @@ package wdl.transforms.biscayne.linking.expression.types +import cats.data.Validated.{Invalid, Valid} import cats.implicits.{catsSyntaxTuple2Semigroupal, catsSyntaxTuple3Semigroupal} import cats.syntax.validated._ import common.validation.ErrorOr._ @@ -198,62 +199,99 @@ object BiscayneTypeEvaluators { implicit val structLiteralTypeEvaluator: TypeEvaluator[StructLiteral] = new TypeEvaluator[StructLiteral] { - /* - def isMissingMembers(foundMembers: Map[String, WomType], expectedMembers: Map[String, WomType]): Option[String] = { - val errors: Iterable[String] = expectedMembers flatMap { case (memberName, memberType) => - memberType match { - case WomOptionalType(_) => None // It's OK if an Optional type is not found. - case _ => if (!foundMembers.contains(memberName)) Some(s"Expected member ${memberName} not found.") else None - } - } - errors match { - case Nil => None - case _ => Some(errors.mkString) - } - } - */ + // does it make sense that someone would assign type b to type a? def areTypesAssignable(a: WomType, b: WomType): Boolean = !a.equalsType(b).isFailure - - def typeCheckHelper(typeName: String, - memberName: String, - evaluatedType: Option[WomType], - expectedType: Option[WomType] - ): Option[String] = + // Helper method to check something (maybe) found in the struct literal to something (maybe) found in the struct definition. + def checkIfMemberIsValid(typeName: String, + memberName: String, + evaluatedType: Option[WomType], + expectedType: Option[WomType] + ): ErrorOr[WomType] = evaluatedType match { case Some(evaluated) => expectedType match { case Some(expected) => - if (areTypesAssignable(evaluated, expected)) None + if (areTypesAssignable(evaluated, expected)) evaluated.validNel else - Some(s"$typeName.$memberName expected to be ${expected.friendlyName}. Found ${evaluated.friendlyName}.") - case None => Some(s"Type $typeName does not have a member called $memberName.") + s"$typeName.$memberName expected to be ${expected.friendlyName}. Found ${evaluated.friendlyName}.".invalidNel + case None => s"Type $typeName does not have a member called $memberName.".invalidNel + } + case None => s"Error evaluating the type of ${typeName}.${memberName}.".invalidNel + } + + // For each member in the literal, check that it exists in the struct definition and is the expected type. + def checkMembersAgainstDefinition(a: StructLiteral, + structDefinition: WomCompositeType, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + ): ErrorOr[WomCompositeType] = { + val checkedMembers: Map[String, ErrorOr[WomType]] = a.elements.map { case (memberKey, memberExpressionElement) => + val evaluatedType = + expressionTypeEvaluator.evaluateType(memberExpressionElement, linkedValues, typeAliases).toOption + val expectedType = structDefinition.typeMap.get(memberKey) + (memberKey, checkIfMemberIsValid(a.structTypeName, memberKey, evaluatedType, expectedType)) + } + + val errors: Iterable[String] = checkedMembers.flatMap { case (_, errorOr) => + errorOr match { + case Invalid(e) => Some(e.toList.mkString) + case _ => None + } + } + + if (errors.nonEmpty) { + errors.mkString(",").invalidNel + } else { + val validatedTypes: Map[String, WomType] = checkedMembers.flatMap { case (key, errorOr) => + errorOr match { + case Valid(v) => Some((key, v)) + case _ => None } - case None => Some(s"Error evaluating the type of ${typeName}.${memberName}.") + } + WomCompositeType(validatedTypes, Some(a.structTypeName)).validNel } + } + // For every member in the definition, if that member isn't optional, confirm that it is also in the struct literal. + def checkForMissingMembers(foundMembers: Map[String, WomType], + structDefinition: WomCompositeType + ): Option[String] = { + val errors: Iterable[String] = structDefinition.typeMap flatMap { case (memberName, memberType) => + memberType match { + case WomOptionalType(_) => None + case _ => + if (!foundMembers.contains(memberName)) Some(s"Expected member ${memberName} not found. ") + else None + } + } + errors match { + case Nil => None + case _ => Some(errors.mkString) + } + } override def evaluateType(a: StructLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] - ): ErrorOr[WomType] = - typeAliases.get(a.structTypeName) map { - case compositeType: WomCompositeType => - val errors = a.elements.flatMap { case (memberKey, memberExpressionElement) => - val evaluatedType = - expressionTypeEvaluator.evaluateType(memberExpressionElement, linkedValues, typeAliases).toOption - val expectedType = compositeType.typeMap.get(memberKey) - typeCheckHelper(a.structTypeName, memberKey, evaluatedType, expectedType) - } - if (errors.isEmpty) { - WomCompositeType(compositeType.typeMap, Some(a.structTypeName)).validNel - } else { - errors.mkString(",").invalidNel + ): ErrorOr[WomType] = { + val structDefinition = typeAliases.get(a.structTypeName) + structDefinition match { + case Some(definition) => + definition match { + case compositeType: WomCompositeType => + checkMembersAgainstDefinition(a, compositeType, linkedValues, typeAliases).flatMap { foundMembers => + checkForMissingMembers(foundMembers.typeMap, compositeType) match { + case Some(error) => error.invalidNel + case _ => compositeType.validNel + } + } + case _ => s"Unexpected error while parsing ${a.structTypeName}".invalidNel } - case _ => s"Could not find struct definition for struct literal ${a.structTypeName}".invalidNel - } getOrElse s"Type map does not include definition for struct literal ${a.structTypeName}".invalidNel - + case None => s"Could not find Struct Definition for type ${a.structTypeName}".invalidNel + } + } } } diff --git a/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala b/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala index 4edb1303b18..8428660faad 100644 --- a/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala +++ b/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala @@ -24,7 +24,7 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val typeAliases: Map[String, WomType] = Map( "Plant" -> WomCompositeType(plantTypeMap, Some("Plant")), - "Animal" -> WomCompositeType(plantTypeMap, Some("Animal")) + "Animal" -> WomCompositeType(animalTypeMap, Some("Animal")) ) it should "return nothing from static integer addition" in { @@ -162,6 +162,14 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit } } + it should "evaluate the type of a struct literal with a nested struct literal" in { + val structLiteral = """ Animal{isMaybeGood: true, hat: Plant{isTasty: true, count: 42}} """ + val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) + structExpr.shouldBeValidPF { case e => + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomCompositeType(animalTypeMap, Some("Animal")) + } + } + it should "fail to evaluate the type of a struct literal with incorrect members" in { val structLiteral = """ Animal{fur: "fuzzy", isGood: true} """ val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) @@ -182,14 +190,21 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit } } - it should "fail if a member is missing" in { + it should "fail if a struct literal member is missing" in { val structLiteral = """ Plant{count: 4} """ val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) structExpr.shouldBeValidPF { case e => e.evaluateType(Map.empty, typeAliases - ) shouldBeInvalid "Plant.count expected to be Int. Found String." + ) shouldBeInvalid "Expected member isTasty not found. " } } + it should "tolerate a missing struct literal optional member" in { + val structLiteral = """ Animal{hat: Plant{isTasty: true, count: 42}} """ + val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) + structExpr.shouldBeValidPF { case e => + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomCompositeType(animalTypeMap, Some("Animal")) + } + } } From a212ea890ebed37a75b9010dcb95b3cb29e2b2fe Mon Sep 17 00:00:00 2001 From: Tom Wiseman Date: Tue, 9 Apr 2024 09:33:17 -0400 Subject: [PATCH 07/12] remove whoops-commit files --- .../call-a/execution/script | 38 ------------------- .../call-a/execution/script | 38 ------------------- 2 files changed, 76 deletions(-) delete mode 100644 struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/script delete mode 100644 struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/script diff --git a/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/script b/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/script deleted file mode 100644 index f70da48eceb..00000000000 --- a/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/script +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -cd /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution -tmpDir=$(mktemp -d "$PWD"/tmp.XXXXXX) -chmod 777 "$tmpDir" -export _JAVA_OPTIONS=-Djava.io.tmpdir="$tmpDir" -export TMPDIR="$tmpDir" - - -cd /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution - - - -out19f376d1="${tmpDir}/out.$$" err19f376d1="${tmpDir}/err.$$" -mkfifo "$out19f376d1" "$err19f376d1" -trap 'rm "$out19f376d1" "$err19f376d1"' EXIT -touch '/cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/stdout' '/cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/stderr' -tee '/cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/stdout' < "$out19f376d1" & -tee '/cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/stderr' < "$err19f376d1" >&2 & -( -cd /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution - - -echo "44" -) > "$out19f376d1" 2> "$err19f376d1" -echo $? > /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/rc.tmp -( -# add a .file in every empty directory to facilitate directory delocalization on the cloud -cd /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution -find . -type d -exec sh -c '[ -z "$(ls -A '"'"'{}'"'"')" ] && touch '"'"'{}'"'"'/.file' \; -) -( -cd /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution -sync - - -) -mv /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/rc.tmp /cromwell-executions/struct_literal/19f376d1-c4aa-454f-bcdd-0cc93b7946f5/call-a/execution/rc diff --git a/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/script b/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/script deleted file mode 100644 index b0b7d20e221..00000000000 --- a/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/script +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -cd /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution -tmpDir=$(mktemp -d "$PWD"/tmp.XXXXXX) -chmod 777 "$tmpDir" -export _JAVA_OPTIONS=-Djava.io.tmpdir="$tmpDir" -export TMPDIR="$tmpDir" - - -cd /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution - - - -outd341ca12="${tmpDir}/out.$$" errd341ca12="${tmpDir}/err.$$" -mkfifo "$outd341ca12" "$errd341ca12" -trap 'rm "$outd341ca12" "$errd341ca12"' EXIT -touch '/cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/stdout' '/cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/stderr' -tee '/cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/stdout' < "$outd341ca12" & -tee '/cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/stderr' < "$errd341ca12" >&2 & -( -cd /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution - - -echo "44" -) > "$outd341ca12" 2> "$errd341ca12" -echo $? > /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/rc.tmp -( -# add a .file in every empty directory to facilitate directory delocalization on the cloud -cd /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution -find . -type d -exec sh -c '[ -z "$(ls -A '"'"'{}'"'"')" ] && touch '"'"'{}'"'"'/.file' \; -) -( -cd /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution -sync - - -) -mv /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/rc.tmp /cromwell-executions/struct_literal/d341ca12-c956-4372-bc9b-a0c4f26d2b43/call-a/execution/rc From 16dc9766e40210803a37e77b574783ddf2f62308 Mon Sep 17 00:00:00 2001 From: Tom Wiseman Date: Tue, 9 Apr 2024 09:53:35 -0400 Subject: [PATCH 08/12] finish //todos, fix tests, run scalafmt --- .../languages/util/LanguageFactoryUtil.scala | 1 - .../graph/expression/TypeEvaluator.scala | 5 +- .../linking/expression/types/types.scala | 7 +- .../types/BiscayneTypeEvaluatorSpec.scala | 8 +- .../types/CascadesTypeEvaluators.scala | 69 ++++-- .../linking/expression/types/types.scala | 7 +- .../types/CascadesTypeEvaluatorSpec.scala | 52 ++-- .../linking/expression/types/package.scala | 7 +- .../MemberAccessTypeEvaluatorSpec.scala | 14 +- .../expression/TernaryIfEvaluatorSpec.scala | 2 +- ...UnaryAndBinaryOperatorsEvaluatorSpec.scala | 2 +- .../types/BinaryOperatorEvaluators.scala | 10 +- .../types/EngineFunctionEvaluators.scala | 228 +++++++++++------- .../expression/types/LiteralEvaluators.scala | 64 +++-- .../expression/types/LookupEvaluators.scala | 23 +- .../expression/types/TernaryIfEvaluator.scala | 5 +- .../types/UnaryOperatorEvaluators.scala | 5 +- .../expression/WdlomWomExpression.scala | 8 +- .../graph/CallElementToGraphNode.scala | 4 +- .../graph/IfElementToGraphNode.scala | 13 +- .../graph/ScatterElementToGraphNode.scala | 15 +- 21 files changed, 359 insertions(+), 190 deletions(-) diff --git a/languageFactories/language-factory-core/src/main/scala/cromwell/languages/util/LanguageFactoryUtil.scala b/languageFactories/language-factory-core/src/main/scala/cromwell/languages/util/LanguageFactoryUtil.scala index d212e1727d9..da5d2d737f3 100644 --- a/languageFactories/language-factory-core/src/main/scala/cromwell/languages/util/LanguageFactoryUtil.scala +++ b/languageFactories/language-factory-core/src/main/scala/cromwell/languages/util/LanguageFactoryUtil.scala @@ -61,7 +61,6 @@ object LanguageFactoryUtil { } yield validatedWomNamespace } - /* * At this point input values are either a WomValue (if it was provided through the input file) * or a WomExpression (if we fell back to the default). diff --git a/wdl/model/draft3/src/main/scala/wdl/model/draft3/graph/expression/TypeEvaluator.scala b/wdl/model/draft3/src/main/scala/wdl/model/draft3/graph/expression/TypeEvaluator.scala index 98eae9b7ab5..4fc44b7e771 100644 --- a/wdl/model/draft3/src/main/scala/wdl/model/draft3/graph/expression/TypeEvaluator.scala +++ b/wdl/model/draft3/src/main/scala/wdl/model/draft3/graph/expression/TypeEvaluator.scala @@ -8,7 +8,10 @@ import wom.types.WomType @typeclass trait TypeEvaluator[A] { - def evaluateType(a: A, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], typeAliases: Map[String, WomType])(implicit + def evaluateType(a: A, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] } diff --git a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/types.scala b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/types.scala index ba477c146f8..ab2f114440a 100644 --- a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/types.scala +++ b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/types.scala @@ -18,8 +18,11 @@ import wdl.transforms.biscayne.linking.expression.types.BiscayneTypeEvaluators._ package object types { implicit val expressionTypeEvaluator: TypeEvaluator[ExpressionElement] = new TypeEvaluator[ExpressionElement] { - override def evaluateType(a: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( - implicit typeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: ExpressionElement, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + typeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = a match { // Literals: diff --git a/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala b/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala index 8428660faad..6ba9e99e0e4 100644 --- a/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala +++ b/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala @@ -184,9 +184,7 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val structLiteral = """ Plant{isTasty: true, count: "four"} """ val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) structExpr.shouldBeValidPF { case e => - e.evaluateType(Map.empty, - typeAliases - ) shouldBeInvalid "Plant.count expected to be Int. Found String." + e.evaluateType(Map.empty, typeAliases) shouldBeInvalid "Plant.count expected to be Int. Found String." } } @@ -194,9 +192,7 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val structLiteral = """ Plant{count: 4} """ val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) structExpr.shouldBeValidPF { case e => - e.evaluateType(Map.empty, - typeAliases - ) shouldBeInvalid "Expected member isTasty not found. " + e.evaluateType(Map.empty, typeAliases) shouldBeInvalid "Expected member isTasty not found. " } } diff --git a/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluators.scala b/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluators.scala index 0bcea02b782..369cc8ba918 100644 --- a/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluators.scala +++ b/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluators.scala @@ -12,7 +12,10 @@ import wom.types._ object cascadesTypeEvaluators { implicit val keysFunctionEvaluator: TypeEvaluator[Keys] = new TypeEvaluator[Keys] { - override def evaluateType(a: Keys, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Keys, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType)) flatMap { @@ -22,7 +25,10 @@ object cascadesTypeEvaluators { } implicit val asMapFunctionEvaluator: TypeEvaluator[AsMap] = new TypeEvaluator[AsMap] { - override def evaluateType(a: AsMap, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: AsMap, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { @@ -34,7 +40,10 @@ object cascadesTypeEvaluators { } implicit val asPairsFunctionEvaluator: TypeEvaluator[AsPairs] = new TypeEvaluator[AsPairs] { - override def evaluateType(a: AsPairs, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: AsPairs, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType)) flatMap { @@ -44,8 +53,11 @@ object cascadesTypeEvaluators { } implicit val collectByKeyFunctionEvaluator: TypeEvaluator[CollectByKey] = new TypeEvaluator[CollectByKey] { - override def evaluateType(a: CollectByKey, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: CollectByKey, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { case WomArrayType(WomPairType(x: WomPrimitiveType, y)) => WomMapType(x, WomArrayType(y)).validNel @@ -67,7 +79,10 @@ object cascadesTypeEvaluators { } implicit val minFunctionEvaluator: TypeEvaluator[Min] = new TypeEvaluator[Min] { - override def evaluateType(a: Min, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Min, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { val type1 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues, typeAliases) @@ -78,7 +93,10 @@ object cascadesTypeEvaluators { } implicit val maxFunctionEvaluator: TypeEvaluator[Max] = new TypeEvaluator[Max] { - override def evaluateType(a: Max, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Max, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { val type1 = expressionTypeEvaluator.evaluateType(a.arg1, linkedValues, typeAliases) @@ -89,7 +107,10 @@ object cascadesTypeEvaluators { } implicit val sepFunctionEvaluator: TypeEvaluator[Sep] = new TypeEvaluator[Sep] { - override def evaluateType(a: Sep, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Sep, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.arg2, linkedValues, WomArrayType(WomAnyType)) flatMap { @@ -103,7 +124,10 @@ object cascadesTypeEvaluators { } implicit val subPosixFunctionEvaluator: TypeEvaluator[SubPosix] = new TypeEvaluator[SubPosix] { - override def evaluateType(a: SubPosix, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: SubPosix, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = (validateParamType(a.input, linkedValues, WomSingleFileType), @@ -113,7 +137,10 @@ object cascadesTypeEvaluators { } implicit val suffixFunctionEvaluator: TypeEvaluator[Suffix] = new TypeEvaluator[Suffix] { - override def evaluateType(a: Suffix, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Suffix, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = (validateParamType(a.suffix, linkedValues, WomStringType), @@ -122,7 +149,10 @@ object cascadesTypeEvaluators { } implicit val quoteFunctionEvaluator: TypeEvaluator[Quote] = new TypeEvaluator[Quote] { - override def evaluateType(a: Quote, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Quote, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomAnyType)) flatMap { @@ -136,7 +166,10 @@ object cascadesTypeEvaluators { } implicit val sQuoteFunctionEvaluator: TypeEvaluator[SQuote] = new TypeEvaluator[SQuote] { - override def evaluateType(a: SQuote, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: SQuote, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomAnyType)) flatMap { @@ -150,7 +183,10 @@ object cascadesTypeEvaluators { } implicit val unzipFunctionEvaluator: TypeEvaluator[Unzip] = new TypeEvaluator[Unzip] { - override def evaluateType(a: Unzip, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Unzip, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { @@ -161,8 +197,11 @@ object cascadesTypeEvaluators { } implicit val structLiteralTypeEvaluator: TypeEvaluator[StructLiteral] = new TypeEvaluator[StructLiteral] { - override def evaluateType(a: StructLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: StructLiteral, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = // This works fine, but is not yet a strong enough type check for the WDL 1.1 spec // (i.e. users are able to instantiate struct literals with k/v pairs that aren't actually in the struct definition, without an error being thrown.) diff --git a/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/types.scala b/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/types.scala index 62b59865a86..45603ff54e9 100644 --- a/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/types.scala +++ b/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/types.scala @@ -18,8 +18,11 @@ import wdl.transforms.biscayne.linking.expression.types.cascadesTypeEvaluators._ package object types { implicit val expressionTypeEvaluator: TypeEvaluator[ExpressionElement] = new TypeEvaluator[ExpressionElement] { - override def evaluateType(a: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType] )( - implicit typeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: ExpressionElement, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + typeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = a match { // Literals: diff --git a/wdl/transforms/cascades/src/test/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluatorSpec.scala b/wdl/transforms/cascades/src/test/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluatorSpec.scala index 66c8ff00134..ebc85e82696 100644 --- a/wdl/transforms/cascades/src/test/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluatorSpec.scala +++ b/wdl/transforms/cascades/src/test/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluatorSpec.scala @@ -11,12 +11,28 @@ import wdl.transforms.cascades.ast2wdlom._ import wom.types._ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec with Matchers { + + val plantTypeMap: Map[String, WomType] = Map( + "isTasty" -> WomBooleanType, + "count" -> WomIntegerType + ) + + val animalTypeMap: Map[String, WomType] = Map( + "isMaybeGood" -> WomOptionalType(WomBooleanType), + "hat" -> WomCompositeType(plantTypeMap, Some("Plant")) + ) + + val typeAliases: Map[String, WomType] = Map( + "Plant" -> WomCompositeType(plantTypeMap, Some("Plant")), + "Animal" -> WomCompositeType(animalTypeMap, Some("Animal")) + ) + it should "return nothing from static integer addition" in { val str = "3 + 3" val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty, Map.empty) shouldBeValid WomIntegerType + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomIntegerType } } @@ -25,7 +41,7 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomMapType(WomIntegerType, WomIntegerType) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomMapType(WomIntegerType, WomIntegerType) } } @@ -34,7 +50,7 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomArrayType(WomPairType(WomStringType, WomIntegerType)) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomArrayType(WomPairType(WomStringType, WomIntegerType)) } } @@ -43,7 +59,7 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomStringType + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomStringType } } @@ -52,7 +68,7 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomStringType + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomStringType } } @@ -61,7 +77,7 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomStringType + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomStringType } } @@ -70,7 +86,7 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomArrayType(WomStringType) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomArrayType(WomStringType) } } @@ -79,7 +95,7 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomArrayType(WomStringType) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomArrayType(WomStringType) } } @@ -88,7 +104,7 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomArrayType(WomNothingType) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomArrayType(WomNothingType) } } @@ -97,7 +113,7 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomArrayType(WomStringType) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomArrayType(WomStringType) } } @@ -106,7 +122,7 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val expr = fromString[ExpressionElement](str, parser.parse_e) expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomArrayType(WomNothingType) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomArrayType(WomNothingType) } } @@ -114,13 +130,17 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val string_and_int = """ unzip([("one", 1),("two", 2),("three", 3)]) """ val string_and_int_expr = fromString[ExpressionElement](string_and_int, parser.parse_e) string_and_int_expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomPairType(WomArrayType(WomStringType), WomArrayType(WomIntegerType)) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomPairType(WomArrayType(WomStringType), + WomArrayType(WomIntegerType) + ) } val int_and_int = """ unzip([(1,2),(3,4),(5,6)]) """ val int_and_int_expr = fromString[ExpressionElement](int_and_int, parser.parse_e) int_and_int_expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomPairType(WomArrayType(WomIntegerType), WomArrayType(WomIntegerType)) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomPairType(WomArrayType(WomIntegerType), + WomArrayType(WomIntegerType) + ) } } @@ -128,7 +148,9 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val empty = """ unzip([]) """ val empty_unzip_expr = fromString[ExpressionElement](empty, parser.parse_e) empty_unzip_expr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomPairType(WomArrayType(WomAnyType), WomArrayType(WomAnyType)) + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomPairType(WomArrayType(WomAnyType), + WomArrayType(WomAnyType) + ) } } @@ -138,7 +160,7 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val structLiteral = """ Animal{fur: "fuzzy", isGood: true} """ val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) structExpr.shouldBeValidPF { case e => - e.evaluateType(Map.empty) shouldBeValid WomObjectType + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomObjectType } } } diff --git a/wdl/transforms/draft3/src/main/scala/wdl/draft3/transforms/linking/expression/types/package.scala b/wdl/transforms/draft3/src/main/scala/wdl/draft3/transforms/linking/expression/types/package.scala index 7956057fc63..d35685146ac 100644 --- a/wdl/transforms/draft3/src/main/scala/wdl/draft3/transforms/linking/expression/types/package.scala +++ b/wdl/transforms/draft3/src/main/scala/wdl/draft3/transforms/linking/expression/types/package.scala @@ -17,8 +17,11 @@ import wom.types.WomType package object types { implicit val expressionTypeEvaluator: TypeEvaluator[ExpressionElement] = new TypeEvaluator[ExpressionElement] { - override def evaluateType(a: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( - implicit typeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: ExpressionElement, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + typeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = a match { // Literals: diff --git a/wdl/transforms/draft3/src/test/scala/wdl/draft3/transforms/expression/MemberAccessTypeEvaluatorSpec.scala b/wdl/transforms/draft3/src/test/scala/wdl/draft3/transforms/expression/MemberAccessTypeEvaluatorSpec.scala index 06bbbc57725..90bd7cea585 100644 --- a/wdl/transforms/draft3/src/test/scala/wdl/draft3/transforms/expression/MemberAccessTypeEvaluatorSpec.scala +++ b/wdl/transforms/draft3/src/test/scala/wdl/draft3/transforms/expression/MemberAccessTypeEvaluatorSpec.scala @@ -23,10 +23,10 @@ class MemberAccessTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec it should "find the left and right hand sides of a pair expression" in { val pair = PairLiteral(fiveLiteral, sixLiteral) val leftExpression: ExpressionElement = ExpressionMemberAccess(pair, NonEmptyList("left", List.empty)) - leftExpression.evaluateType(Map.empty) shouldBeValid WomIntegerType + leftExpression.evaluateType(Map.empty, Map.empty) shouldBeValid WomIntegerType val rightExpression: ExpressionElement = ExpressionMemberAccess(pair, NonEmptyList("right", List.empty)) - rightExpression.evaluateType(Map.empty) shouldBeValid WomIntegerType + rightExpression.evaluateType(Map.empty, Map.empty) shouldBeValid WomIntegerType } it should "find the appropriate value in a deeply nested Pair" in { @@ -46,7 +46,7 @@ class MemberAccessTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec ), memberAccessTail = NonEmptyList("left", List("right", "right", "left")) ) - nestedPairLookup.evaluateType(Map.empty) shouldBeValid WomPairType(WomIntegerType, WomIntegerType) + nestedPairLookup.evaluateType(Map.empty, Map.empty) shouldBeValid WomPairType(WomIntegerType, WomIntegerType) } it should "evaluate a nested member access on a call output" in { @@ -59,7 +59,7 @@ class MemberAccessTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec ) ) - callOutputLookup.evaluateType(linkedValues) shouldBeValid WomPairType(WomIntegerType, WomIntegerType) + callOutputLookup.evaluateType(linkedValues, Map.empty) shouldBeValid WomPairType(WomIntegerType, WomIntegerType) } it should "evaluate a nested member access on a struct" in { @@ -77,7 +77,7 @@ class MemberAccessTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec ) ) - objectLookup.evaluateType(linkedValues) shouldBeValid WomPairType(WomIntegerType, WomIntegerType) + objectLookup.evaluateType(linkedValues, Map.empty) shouldBeValid WomPairType(WomIntegerType, WomIntegerType) } it should "evaluate a nested member access type on an object" in { @@ -89,7 +89,7 @@ class MemberAccessTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec ) ) - objectLookup.evaluateType(linkedValues) shouldBeValid WomAnyType + objectLookup.evaluateType(linkedValues, Map.empty) shouldBeValid WomAnyType } it should "evaluate an identifier lookup" in { @@ -101,7 +101,7 @@ class MemberAccessTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec ) ) - identifierLookup.evaluateType(linkedValues) shouldBeValid WomPairType(WomStringType, WomStringType) + identifierLookup.evaluateType(linkedValues, Map.empty) shouldBeValid WomPairType(WomStringType, WomStringType) } } diff --git a/wdl/transforms/draft3/src/test/scala/wdl/draft3/transforms/expression/TernaryIfEvaluatorSpec.scala b/wdl/transforms/draft3/src/test/scala/wdl/draft3/transforms/expression/TernaryIfEvaluatorSpec.scala index 0c9a4323dcc..f73f824904f 100644 --- a/wdl/transforms/draft3/src/test/scala/wdl/draft3/transforms/expression/TernaryIfEvaluatorSpec.scala +++ b/wdl/transforms/draft3/src/test/scala/wdl/draft3/transforms/expression/TernaryIfEvaluatorSpec.scala @@ -56,7 +56,7 @@ class TernaryIfEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec with M typeTests foreach { case (name, expression, expected) => it should s"evaluate the expression '$name'" in { - expression.evaluateType(Map.empty) shouldBeValid expected + expression.evaluateType(Map.empty, Map.empty) shouldBeValid expected } } } diff --git a/wdl/transforms/draft3/src/test/scala/wdl/draft3/transforms/expression/UnaryAndBinaryOperatorsEvaluatorSpec.scala b/wdl/transforms/draft3/src/test/scala/wdl/draft3/transforms/expression/UnaryAndBinaryOperatorsEvaluatorSpec.scala index 656e6ae21be..23c149a6061 100644 --- a/wdl/transforms/draft3/src/test/scala/wdl/draft3/transforms/expression/UnaryAndBinaryOperatorsEvaluatorSpec.scala +++ b/wdl/transforms/draft3/src/test/scala/wdl/draft3/transforms/expression/UnaryAndBinaryOperatorsEvaluatorSpec.scala @@ -65,7 +65,7 @@ class UnaryAndBinaryOperatorsEvaluatorSpec extends AnyFlatSpec with CromwellTime } it should s"evaluate the type of the expression '$name'" in { - expression.evaluateType(Map.empty) shouldBeValid expectedType + expression.evaluateType(Map.empty, Map.empty) shouldBeValid expectedType } } } diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/BinaryOperatorEvaluators.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/BinaryOperatorEvaluators.scala index 6fe735ffeef..11e9e4b53c5 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/BinaryOperatorEvaluators.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/BinaryOperatorEvaluators.scala @@ -28,11 +28,15 @@ object BinaryOperatorEvaluators { implicit val remainderEvaluator: TypeEvaluator[Remainder] = forOperation(_.mod(_)) private def forOperation[A <: BinaryOperation](op: (WomType, WomType) => Try[WomType]) = new TypeEvaluator[A] { - override def evaluateType(a: A, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: A, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - (a.left.evaluateType(linkedValues, typeAliases), a.right.evaluateType(linkedValues, typeAliases)) flatMapN { (left, right) => - op(left, right).toErrorOr + (a.left.evaluateType(linkedValues, typeAliases), a.right.evaluateType(linkedValues, typeAliases)) flatMapN { + (left, right) => + op(left, right).toErrorOr } } } diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/EngineFunctionEvaluators.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/EngineFunctionEvaluators.scala index 0b0013b87a8..a1ad06230b8 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/EngineFunctionEvaluators.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/EngineFunctionEvaluators.scala @@ -21,7 +21,7 @@ object EngineFunctionEvaluators { new TypeEvaluator[ExpressionElement.StdoutElement.type] { override def evaluateType(a: ExpressionElement.StdoutElement.type, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType] + typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = WomSingleFileType.validNel } @@ -30,54 +30,66 @@ object EngineFunctionEvaluators { new TypeEvaluator[ExpressionElement.StderrElement.type] { override def evaluateType(a: ExpressionElement.StderrElement.type, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType] + typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = WomSingleFileType.validNel } implicit val readLinesFunctionEvaluator: TypeEvaluator[ReadLines] = new TypeEvaluator[ReadLines] { - override def evaluateType(a: ReadLines, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: ReadLines, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomArrayType(WomStringType)) } implicit val readTsvFunctionEvaluator: TypeEvaluator[ReadTsv] = new TypeEvaluator[ReadTsv] { - override def evaluateType(a: ReadTsv, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: ReadTsv, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomArrayType(WomArrayType(WomStringType))) } implicit val readMapFunctionEvaluator: TypeEvaluator[ReadMap] = new TypeEvaluator[ReadMap] { - override def evaluateType(a: ReadMap, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: ReadMap, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomMapType(WomStringType, WomStringType)) } implicit val readObjectFunctionEvaluator: TypeEvaluator[ReadObject] = new TypeEvaluator[ReadObject] { - override def evaluateType(a: ReadObject, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: ReadObject, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomObjectType) } implicit val readObjectsFunctionEvaluator: TypeEvaluator[ReadObjects] = new TypeEvaluator[ReadObjects] { - override def evaluateType(a: ReadObjects, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: ReadObjects, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomArrayType(WomObjectType)) } implicit val readJsonFunctionEvaluator: TypeEvaluator[ReadJson] = new TypeEvaluator[ReadJson] { - override def evaluateType(a: ReadJson, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: ReadJson, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = // we can't figure out the WomType of data without reading the file hence evaluate it to `WomAnyType` @@ -85,80 +97,100 @@ object EngineFunctionEvaluators { } implicit val readIntFunctionEvaluator: TypeEvaluator[ReadInt] = new TypeEvaluator[ReadInt] { - override def evaluateType(a: ReadInt, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: ReadInt, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomIntegerType) } implicit val readStringFunctionEvaluator: TypeEvaluator[ReadString] = new TypeEvaluator[ReadString] { - override def evaluateType(a: ReadString, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: ReadString, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomStringType) } implicit val readFloatFunctionEvaluator: TypeEvaluator[ReadFloat] = new TypeEvaluator[ReadFloat] { - override def evaluateType(a: ReadFloat, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: ReadFloat, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomFloatType) } implicit val readBooleanFunctionEvaluator: TypeEvaluator[ReadBoolean] = new TypeEvaluator[ReadBoolean] { - override def evaluateType(a: ReadBoolean, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: ReadBoolean, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomBooleanType) } implicit val writeLinesFunctionEvaluator: TypeEvaluator[WriteLines] = new TypeEvaluator[WriteLines] { - override def evaluateType(a: WriteLines, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: WriteLines, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomStringType)).map(_ => WomSingleFileType) } implicit val writeTsvFunctionEvaluator: TypeEvaluator[WriteTsv] = new TypeEvaluator[WriteTsv] { - override def evaluateType(a: WriteTsv, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: WriteTsv, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomArrayType(WomStringType))).map(_ => WomSingleFileType) } implicit val writeMapFunctionEvaluator: TypeEvaluator[WriteMap] = new TypeEvaluator[WriteMap] { - override def evaluateType(a: WriteMap, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: WriteMap, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType)).map(_ => WomSingleFileType) } implicit val writeObjectFunctionEvaluator: TypeEvaluator[WriteObject] = new TypeEvaluator[WriteObject] { - override def evaluateType(a: WriteObject, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: WriteObject, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomObjectType).map(_ => WomSingleFileType) } implicit val writeObjectsFunctionEvaluator: TypeEvaluator[WriteObjects] = new TypeEvaluator[WriteObjects] { - override def evaluateType(a: WriteObjects, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: WriteObjects, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomObjectType)).map(_ => WomSingleFileType) } implicit val writeJsonFunctionEvaluator: TypeEvaluator[WriteJson] = new TypeEvaluator[WriteJson] { - override def evaluateType(a: WriteJson, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: WriteJson, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = a.param.evaluateType(linkedValues, typeAliases).flatMap { @@ -178,16 +210,20 @@ object EngineFunctionEvaluators { } implicit val rangeFunctionEvaluator: TypeEvaluator[Range] = new TypeEvaluator[Range] { - override def evaluateType(a: Range, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Range, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomIntegerType).map(_ => WomArrayType(WomIntegerType)) } implicit val transposeFunctionEvaluator: TypeEvaluator[Transpose] = new TypeEvaluator[Transpose] { - override def evaluateType(a: Transpose, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Transpose, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = a.param.evaluateType(linkedValues, typeAliases).flatMap { @@ -198,16 +234,20 @@ object EngineFunctionEvaluators { } implicit val lengthFunctionEvaluator: TypeEvaluator[Length] = new TypeEvaluator[Length] { - override def evaluateType(a: Length, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Length, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomArrayType(WomAnyType)).map(_ => WomIntegerType) } implicit val flattenFunctionEvaluator: TypeEvaluator[Flatten] = new TypeEvaluator[Flatten] { - override def evaluateType(a: Flatten, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Flatten, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = a.param.evaluateType(linkedValues, typeAliases).flatMap { @@ -218,9 +258,11 @@ object EngineFunctionEvaluators { } implicit val selectFirstFunctionEvaluator: TypeEvaluator[SelectFirst] = new TypeEvaluator[SelectFirst] { - override def evaluateType(a: SelectFirst, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: SelectFirst, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = a.param.evaluateType(linkedValues, typeAliases).flatMap { case WomArrayType(WomOptionalType(inner)) => inner.validNel @@ -231,8 +273,10 @@ object EngineFunctionEvaluators { } implicit val selectAllFunctionEvaluator: TypeEvaluator[SelectAll] = new TypeEvaluator[SelectAll] { - override def evaluateType(a: SelectAll, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: SelectAll, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = a.param.evaluateType(linkedValues, typeAliases).flatMap { @@ -244,40 +288,50 @@ object EngineFunctionEvaluators { } implicit val definedFunctionEvaluator: TypeEvaluator[Defined] = new TypeEvaluator[Defined] { - override def evaluateType(a: Defined, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Defined, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomOptionalType(WomAnyType)).map(_ => WomBooleanType) } implicit val floorFunctionEvaluator: TypeEvaluator[Floor] = new TypeEvaluator[Floor] { - override def evaluateType(a: Floor, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Floor, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomFloatType).map(_ => WomIntegerType) } implicit val ceilFunctionEvaluator: TypeEvaluator[Ceil] = new TypeEvaluator[Ceil] { - override def evaluateType(a: Ceil, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Ceil, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomFloatType).map(_ => WomIntegerType) } implicit val roundFunctionEvaluator: TypeEvaluator[Round] = new TypeEvaluator[Round] { - override def evaluateType(a: Round, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Round, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomFloatType).map(_ => WomIntegerType) } implicit val globFunctionTypeEvaluator: TypeEvaluator[Glob] = new TypeEvaluator[Glob] { - override def evaluateType(a: Glob, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Glob, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = validateParamType(a.param, linkedValues, WomStringType).map(_ => WomArrayType(WomSingleFileType)) @@ -291,8 +345,10 @@ object EngineFunctionEvaluators { case _ => false } - override def evaluateType(a: Size, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Size, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { val validatedSecondArg: ErrorOr[Unit] = a.secondParam match { @@ -309,8 +365,10 @@ object EngineFunctionEvaluators { } implicit val basenameFunctionEvaluator: TypeEvaluator[Basename] = new TypeEvaluator[Basename] { - override def evaluateType(a: Basename, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Basename, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { val validatedSecondArg: ErrorOr[Unit] = a.secondParam match { @@ -327,8 +385,8 @@ object EngineFunctionEvaluators { private def crossOrZipType(arg1: ExpressionElement, arg2: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle] - )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = { - //TODO: can/should we pipe type aliases through here? + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = + // TODO: can/should we pipe type aliases through here? (arg1.evaluateType(linkedValues, Map()), arg2.evaluateType(linkedValues, Map())) match { case (Valid(WomArrayType(left)), Valid(WomArrayType(right))) => WomArrayType(WomPairType(left, right)).validNel @@ -341,26 +399,31 @@ object EngineFunctionEvaluators { // One or more are invalid, so mapN function won't actually ever run: case (otherLeft, otherRight) => (otherLeft, otherRight) mapN { (_, _) => WomNothingType } } - } implicit val zipFunctionEvaluator: TypeEvaluator[Zip] = new TypeEvaluator[Zip] { - override def evaluateType(a: Zip, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Zip, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = crossOrZipType(a.arg1, a.arg2, linkedValues) } implicit val crossFunctionEvaluator: TypeEvaluator[Cross] = new TypeEvaluator[Cross] { - override def evaluateType(a: Cross, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Cross, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = crossOrZipType(a.arg1, a.arg2, linkedValues) } implicit val prefixFunctionEvaluator: TypeEvaluator[Prefix] = new TypeEvaluator[Prefix] { - override def evaluateType(a: Prefix, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Prefix, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = (validateParamType(a.prefix, linkedValues, WomStringType), @@ -369,8 +432,10 @@ object EngineFunctionEvaluators { } implicit val subFunctionEvaluator: TypeEvaluator[Sub] = new TypeEvaluator[Sub] { - override def evaluateType(a: Sub, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: Sub, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = (validateParamType(a.input, linkedValues, WomSingleFileType), @@ -382,13 +447,12 @@ object EngineFunctionEvaluators { def validateParamType(param: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], expectedType: WomType - )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = { - //TODO: pipe type aliases through here + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = + // TODO: pipe type aliases through here param.evaluateType(linkedValues, Map()).flatMap { foundType => if (expectedType.isCoerceableFrom(foundType)) { foundType.validNel } else { s"Invalid parameter '$param'. Expected '${expectedType.stableName}' but got '${foundType.stableName}'".invalidNel } } - } } diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LiteralEvaluators.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LiteralEvaluators.scala index 48ad93a8e2a..ff04850866d 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LiteralEvaluators.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LiteralEvaluators.scala @@ -17,7 +17,7 @@ object LiteralEvaluators { new TypeEvaluator[PrimitiveLiteralExpressionElement] { override def evaluateType(a: PrimitiveLiteralExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType] + typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = a.value.womType.validNel } @@ -26,42 +26,54 @@ object LiteralEvaluators { new TypeEvaluator[NoneLiteralElement.type] { override def evaluateType(a: NoneLiteralElement.type, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType] + typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = WomOptionalType(WomNothingType).validNel } implicit val objectLiteralTypeEvaluator: TypeEvaluator[ObjectLiteral] = new TypeEvaluator[ObjectLiteral] { - override def evaluateType(a: ObjectLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: ObjectLiteral, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = WomObjectType.validNel } implicit val stringLiteralTypeEvaluator: TypeEvaluator[StringLiteral] = new TypeEvaluator[StringLiteral] { - override def evaluateType(a: StringLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: StringLiteral, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = WomStringType.validNel } implicit val stringExpressionTypeEvaluator: TypeEvaluator[StringExpression] = new TypeEvaluator[StringExpression] { - override def evaluateType(a: StringExpression, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: StringExpression, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = WomStringType.validNel } implicit val mapLiteralTypeEvaluator: TypeEvaluator[MapLiteral] = new TypeEvaluator[MapLiteral] { - override def evaluateType(a: MapLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: MapLiteral, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { - val keyTypes = a.elements.keySet.toList.traverse { x: ExpressionElement => x.evaluateType(linkedValues, typeAliases) } + val keyTypes = a.elements.keySet.toList.traverse { x: ExpressionElement => + x.evaluateType(linkedValues, typeAliases) + } val commonKeyType: ErrorOr[WomType] = keyTypes.map(WomType.homogeneousTypeFromTypes) - val valueTypes = a.elements.values.toList.traverse { y: ExpressionElement => y.evaluateType(linkedValues, typeAliases) } + val valueTypes = a.elements.values.toList.traverse { y: ExpressionElement => + y.evaluateType(linkedValues, typeAliases) + } val commonValueType: ErrorOr[WomType] = valueTypes.map(WomType.homogeneousTypeFromTypes) (commonKeyType, commonValueType) mapN { (k, v) => WomMapType(k, v) } @@ -69,9 +81,11 @@ object LiteralEvaluators { } implicit val arrayLiteralTypeEvaluator: TypeEvaluator[ArrayLiteral] = new TypeEvaluator[ArrayLiteral] { - override def evaluateType(a: ArrayLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: ArrayLiteral, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { val types = a.elements.toList.traverse { x: ExpressionElement => x.evaluateType(linkedValues, typeAliases) } @@ -82,13 +96,15 @@ object LiteralEvaluators { } implicit val pairLiteralTypeEvaluator: TypeEvaluator[PairLiteral] = new TypeEvaluator[PairLiteral] { - override def evaluateType(a: PairLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: PairLiteral, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = { - val leftType = a.left.evaluateType(linkedValues,typeAliases: Map[String, WomType]) - val rightType = a.right.evaluateType(linkedValues,typeAliases: Map[String, WomType]) + val leftType = a.left.evaluateType(linkedValues, typeAliases: Map[String, WomType]) + val rightType = a.right.evaluateType(linkedValues, typeAliases: Map[String, WomType]) (leftType, rightType) mapN { (l, r) => WomPairType(l, r) } } diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LookupEvaluators.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LookupEvaluators.scala index 6e11eb02eb4..1d08616de47 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LookupEvaluators.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/LookupEvaluators.scala @@ -15,8 +15,11 @@ import wom.types._ object LookupEvaluators { implicit val identifierLookupTypeEvaluator: TypeEvaluator[IdentifierLookup] = new TypeEvaluator[IdentifierLookup] { - override def evaluateType(a: IdentifierLookup, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: IdentifierLookup, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = linkedValues.collectFirst { case (UnlinkedIdentifierHook(id), gen) if a.identifier == id => gen.womType @@ -33,7 +36,7 @@ object LookupEvaluators { new TypeEvaluator[ExpressionMemberAccess] { override def evaluateType(a: ExpressionMemberAccess, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType] + typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = { val baseType = a.expression.evaluateType(linkedValues, typeAliases) baseType flatMap { doLookup(_, a.memberAccessTail) } @@ -44,7 +47,7 @@ object LookupEvaluators { new TypeEvaluator[IdentifierMemberAccess] { override def evaluateType(a: IdentifierMemberAccess, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType] + typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = { val generatedValueHandle = linkedValues.get(UnlinkedCallOutputOrIdentifierAndMemberAccessHook(a.first, a.second)) @@ -66,10 +69,16 @@ object LookupEvaluators { } implicit val indexAccessTypeEvaluator: TypeEvaluator[IndexAccess] = new TypeEvaluator[IndexAccess] { - override def evaluateType(a: IndexAccess, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])( - implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + override def evaluateType(a: IndexAccess, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - (a.expressionElement.evaluateType(linkedValues, typeAliases), a.index.evaluateType(linkedValues, typeAliases), a.index.validNel) flatMapN { + (a.expressionElement.evaluateType(linkedValues, typeAliases), + a.index.evaluateType(linkedValues, typeAliases), + a.index.validNel + ) flatMapN { case (a: WomArrayType, WomIntegerType, _) => a.memberType.validNel case (WomMapType(keyType, valueType), lookupType, _) if keyType.isCoerceableFrom(lookupType) => valueType.validNel diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/TernaryIfEvaluator.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/TernaryIfEvaluator.scala index 126c48aff72..3d53d4bc528 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/TernaryIfEvaluator.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/TernaryIfEvaluator.scala @@ -13,7 +13,10 @@ import wom.types.{WomBooleanType, WomType} object TernaryIfEvaluator { implicit val ternaryIfEvaluator: TypeEvaluator[TernaryIf] = new TypeEvaluator[TernaryIf] { - override def evaluateType(a: TernaryIf, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: TernaryIf, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = a.condition.evaluateType(linkedValues, typeAliases) flatMap { diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/UnaryOperatorEvaluators.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/UnaryOperatorEvaluators.scala index 9bbcf5b5b77..23766b48270 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/UnaryOperatorEvaluators.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/UnaryOperatorEvaluators.scala @@ -19,7 +19,10 @@ object UnaryOperatorEvaluators { implicit val logicalNotEvaluator: TypeEvaluator[LogicalNot] = forOperation(_.not) private def forOperation[A <: UnaryOperation](op: WomType => Try[WomType]) = new TypeEvaluator[A] { - override def evaluateType(a: A, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle],typeAliases: Map[String, WomType])(implicit + override def evaluateType(a: A, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = a.argument.evaluateType(linkedValues, typeAliases) flatMap { op(_).toErrorOr } diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/expression/WdlomWomExpression.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/expression/WdlomWomExpression.scala index 9e6a1630523..7bfc61907a9 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/expression/WdlomWomExpression.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/expression/WdlomWomExpression.scala @@ -16,7 +16,7 @@ import wom.values.WomValue final case class WdlomWomExpression private (expressionElement: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - typeAliases: Map[String, WomType] + typeAliases: Map[String, WomType] )(implicit expressionValueConsumer: ExpressionValueConsumer[ExpressionElement], fileEvaluator: FileEvaluator[ExpressionElement], @@ -53,8 +53,10 @@ final case class WdlomWomExpression private (expressionElement: ExpressionElemen } object WdlomWomExpression { - def make(expressionElement: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], typeAliases: Map[String, WomType])( - implicit + def make(expressionElement: ExpressionElement, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit expressionValueConsumer: ExpressionValueConsumer[ExpressionElement], fileEvaluator: FileEvaluator[ExpressionElement], typeEvaluator: TypeEvaluator[ExpressionElement], diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/CallElementToGraphNode.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/CallElementToGraphNode.scala index 1e2a3bff562..7e255f27cc9 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/CallElementToGraphNode.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/CallElementToGraphNode.scala @@ -82,7 +82,9 @@ object CallElementToGraphNode { * * @return ErrorOr of LocalName(key) mapped to ExpressionNode(value). */ - def expressionNodeMappings(callable: Callable, typeAliases: Map[String, WomType]): ErrorOr[Map[LocalName, AnonymousExpressionNode]] = { + def expressionNodeMappings(callable: Callable, + typeAliases: Map[String, WomType] + ): ErrorOr[Map[LocalName, AnonymousExpressionNode]] = { def hasDeclaration(callable: Callable, name: String): Boolean = callable match { case t: TaskDefinition => diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/IfElementToGraphNode.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/IfElementToGraphNode.scala index 6843dd0e6f3..43f18f8754f 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/IfElementToGraphNode.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/IfElementToGraphNode.scala @@ -32,9 +32,8 @@ object IfElementToGraphNode { val conditionExpression = a.node.conditionExpression val graphElements = a.node.graphElements - //TODO: pipe type aliases through here val conditionWomExpressionV: ErrorOr[WdlomWomExpression] = - WdlomWomExpression.make(conditionExpression, a.linkableValues, Map()) + WdlomWomExpression.make(conditionExpression, a.linkableValues, a.availableTypeAliases) val conditionExpressionNodeValidation: ErrorOr[AnonymousExpressionNode] = conditionWomExpressionV flatMap { conditionWomExpression => AnonymousExpressionNode.fromInputMapping(WomIdentifier("if_condition"), @@ -44,11 +43,11 @@ object IfElementToGraphNode { ) } - //TODO: Can/should we pipe type aliases through here? - val conditionVariableTypeValidation: ErrorOr[Unit] = conditionExpression.evaluateType(a.linkableValues, Map[String,WomType]()) flatMap { - case WomBooleanType | WomAnyType => ().validNel - case other => s"Invalid type for condition variable: ${other.stableName}".invalidNel - } + val conditionVariableTypeValidation: ErrorOr[Unit] = + conditionExpression.evaluateType(a.linkableValues, a.availableTypeAliases) flatMap { + case WomBooleanType | WomAnyType => ().validNel + case other => s"Invalid type for condition variable: ${other.stableName}".invalidNel + } final case class RequiredOuterPorts(valueGeneratorPorts: Map[String, OutputPort], completionPorts: Map[String, CallNode] diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/ScatterElementToGraphNode.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/ScatterElementToGraphNode.scala index 93aceb74d26..126f2b99345 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/ScatterElementToGraphNode.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/wdlom2wom/graph/ScatterElementToGraphNode.scala @@ -60,9 +60,8 @@ object ScatterElementToGraphNode { val scatterVariableName = a.node.scatterVariableName val graphElements = a.node.graphElements - //TODO: pipe type aliases through here val scatterWomExpressionV: ErrorOr[WdlomWomExpression] = - WdlomWomExpression.make(scatterExpression, a.linkableValues, Map()) + WdlomWomExpression.make(scatterExpression, a.linkableValues, a.availableTypeAliases) val scatterExpressionNodeValidation: ErrorOr[AnonymousExpressionNode] = scatterWomExpressionV flatMap { scatterWomExpression => AnonymousExpressionNode.fromInputMapping(WomIdentifier(scatterVariableName), @@ -72,12 +71,12 @@ object ScatterElementToGraphNode { ) } - //TODO: pipe type aliases through here. - val scatterVariableTypeValidation: ErrorOr[WomType] = scatterExpression.evaluateType(a.linkableValues, Map() ) flatMap { - case a: WomArrayType => a.memberType.validNel - case WomAnyType => WomAnyType.validNel - case other => s"Invalid type for scatter variable '$scatterVariableName': ${other.stableName}".invalidNel - } + val scatterVariableTypeValidation: ErrorOr[WomType] = + scatterExpression.evaluateType(a.linkableValues, a.availableTypeAliases) flatMap { + case a: WomArrayType => a.memberType.validNel + case WomAnyType => WomAnyType.validNel + case other => s"Invalid type for scatter variable '$scatterVariableName': ${other.stableName}".invalidNel + } final case class RequiredOuterPorts(valueGeneratorPorts: Map[String, OutputPort], completionPorts: Map[String, CallNode] From fd0018b49fb0597ba1f7ef4d1f9fdfb9ab915670 Mon Sep 17 00:00:00 2001 From: Tom Wiseman Date: Tue, 9 Apr 2024 10:09:43 -0400 Subject: [PATCH 09/12] cleanup pass --- .../languages/util/LanguageFactoryUtil.scala | 5 +---- .../biscayne/WdlBiscayneLanguageFactory.scala | 18 +----------------- .../types/BiscayneTypeEvaluators.scala | 3 ++- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/languageFactories/language-factory-core/src/main/scala/cromwell/languages/util/LanguageFactoryUtil.scala b/languageFactories/language-factory-core/src/main/scala/cromwell/languages/util/LanguageFactoryUtil.scala index da5d2d737f3..8fabb445bac 100644 --- a/languageFactories/language-factory-core/src/main/scala/cromwell/languages/util/LanguageFactoryUtil.scala +++ b/languageFactories/language-factory-core/src/main/scala/cromwell/languages/util/LanguageFactoryUtil.scala @@ -51,15 +51,12 @@ object LanguageFactoryUtil { } } - def validateWomNamespace(womExecutable: Executable, ioFunctions: IoFunctionSet): Checked[ValidatedWomNamespace] = { - val allNodes = womExecutable.graph.allNodes - println(allNodes) + def validateWomNamespace(womExecutable: Executable, ioFunctions: IoFunctionSet): Checked[ValidatedWomNamespace] = for { evaluatedInputs <- validateExecutableInputs(womExecutable.resolvedExecutableInputs, ioFunctions).toEither validatedWomNamespace = ValidatedWomNamespace(womExecutable, evaluatedInputs, Map.empty) _ <- validateWdlFiles(validatedWomNamespace.womValueInputs) } yield validatedWomNamespace - } /* * At this point input values are either a WomValue (if it was provided through the input file) diff --git a/languageFactories/wdl-biscayne/src/main/scala/languages/wdl/biscayne/WdlBiscayneLanguageFactory.scala b/languageFactories/wdl-biscayne/src/main/scala/languages/wdl/biscayne/WdlBiscayneLanguageFactory.scala index 9e2b220f92a..eace0c91f9b 100644 --- a/languageFactories/wdl-biscayne/src/main/scala/languages/wdl/biscayne/WdlBiscayneLanguageFactory.scala +++ b/languageFactories/wdl-biscayne/src/main/scala/languages/wdl/biscayne/WdlBiscayneLanguageFactory.scala @@ -66,22 +66,7 @@ class WdlBiscayneLanguageFactory(override val config: Config) extends LanguageFa convertNestedScatterToSubworkflow: Boolean = true ): Checked[WomBundle] = { - val converter: CheckedAtoB[FileStringParserInput, WomBundle] = { - val ast = stringToAst - val wrapped = ast andThen wrapAst - val elems = wrapped andThen astToFileElement.map( - FileElementToWomBundleInputs( - _, - workflowOptionsJson, - convertNestedScatterToSubworkflow, - importResolvers, - languageFactories, - workflowDefinitionElementToWomWorkflowDefinition, - taskDefinitionElementToWomTaskDefinition - ) - ) andThen fileElementToWomBundle - - println(elems) + val converter: CheckedAtoB[FileStringParserInput, WomBundle] = stringToAst andThen wrapAst andThen astToFileElement.map( FileElementToWomBundleInputs( _, @@ -93,7 +78,6 @@ class WdlBiscayneLanguageFactory(override val config: Config) extends LanguageFa taskDefinitionElementToWomTaskDefinition ) ) andThen fileElementToWomBundle - } lazy val validationCallable = new Callable[ErrorOr[WomBundle]] { def call: ErrorOr[WomBundle] = converter diff --git a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala index eb615eb00ec..8ee8ce0f112 100644 --- a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala +++ b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala @@ -199,7 +199,8 @@ object BiscayneTypeEvaluators { implicit val structLiteralTypeEvaluator: TypeEvaluator[StructLiteral] = new TypeEvaluator[StructLiteral] { - // does it make sense that someone would assign type b to type a? + // Does it make sense that someone would assign type b to type a? + // Using WomType equality for strict, coercion-less checking. def areTypesAssignable(a: WomType, b: WomType): Boolean = !a.equalsType(b).isFailure From 4b08b93cce42f57be2b347e7b625abf0df0bee39 Mon Sep 17 00:00:00 2001 From: Tom Wiseman Date: Tue, 9 Apr 2024 15:02:16 -0400 Subject: [PATCH 10/12] add test, use partition, call out nuance --- .../types/BiscayneTypeEvaluators.scala | 34 +++++++++---------- .../types/BiscayneTypeEvaluatorSpec.scala | 19 +++++++++-- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala index 8ee8ce0f112..34b664ad32a 100644 --- a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala +++ b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala @@ -199,10 +199,14 @@ object BiscayneTypeEvaluators { implicit val structLiteralTypeEvaluator: TypeEvaluator[StructLiteral] = new TypeEvaluator[StructLiteral] { - // Does it make sense that someone would assign type b to type a? - // Using WomType equality for strict, coercion-less checking. - def areTypesAssignable(a: WomType, b: WomType): Boolean = - !a.equalsType(b).isFailure + // Is the evaluated type allowed to be assigned to the expectedType? + def areTypesAssignable(evaluatedType: WomType, expectedType: WomType): Boolean = + // NB: This check is a little looser than we'd like it to be. + // For example, String is coercible to Int (Int i = "1" is OK) + // It's not until we actually evaluate the value of the string that we can know if that coercion succeeded or not. (Int i = "orange" will fail) + // We don't know whether the user has provided "1" or "orange" at this stage. + // This is OK as-is because the value evaluators do the coercing and throw meaningful errors if the coercion fails. + expectedType.isCoerceableFrom(evaluatedType) // Helper method to check something (maybe) found in the struct literal to something (maybe) found in the struct definition. def checkIfMemberIsValid(typeName: String, @@ -235,23 +239,19 @@ object BiscayneTypeEvaluators { (memberKey, checkIfMemberIsValid(a.structTypeName, memberKey, evaluatedType, expectedType)) } - val errors: Iterable[String] = checkedMembers.flatMap { case (_, errorOr) => + val (errors, validatedTypes) = checkedMembers.partition { case (_, errorOr) => errorOr match { - case Invalid(e) => Some(e.toList.mkString) - case _ => None + case Invalid(_) => true + case Valid(_) => false } } - if (errors.nonEmpty) { - errors.mkString(",").invalidNel - } else { - val validatedTypes: Map[String, WomType] = checkedMembers.flatMap { case (key, errorOr) => - errorOr match { - case Valid(v) => Some((key, v)) - case _ => None - } - } - WomCompositeType(validatedTypes, Some(a.structTypeName)).validNel + errors.headOption match { + case Some((_, Invalid(_))) => + errors.collect { case (_, Invalid(e)) => e.toList.mkString }.toList.mkString(",").invalidNel + case _ => + val types = validatedTypes.collect { case (key, Valid(v)) => (key, v) } + WomCompositeType(types, Some(a.structTypeName)).validNel } } diff --git a/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala b/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala index 6ba9e99e0e4..7595abfdd54 100644 --- a/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala +++ b/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala @@ -22,9 +22,14 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit "hat" -> WomCompositeType(plantTypeMap, Some("Plant")) ) + val bacteriumTypeMap: Map[String, WomType] = Map( + "myFile" -> WomSingleFileType + ) + val typeAliases: Map[String, WomType] = Map( "Plant" -> WomCompositeType(plantTypeMap, Some("Plant")), - "Animal" -> WomCompositeType(animalTypeMap, Some("Animal")) + "Animal" -> WomCompositeType(animalTypeMap, Some("Animal")), + "Bacterium" -> WomCompositeType(bacteriumTypeMap, Some("Bacterium")) ) it should "return nothing from static integer addition" in { @@ -181,10 +186,10 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit } it should "fail to evaluate the type of a struct literal with members that are the wrong type" in { - val structLiteral = """ Plant{isTasty: true, count: "four"} """ + val structLiteral = """ Plant{isTasty: true, count: (0, 1)} """ val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) structExpr.shouldBeValidPF { case e => - e.evaluateType(Map.empty, typeAliases) shouldBeInvalid "Plant.count expected to be Int. Found String." + e.evaluateType(Map.empty, typeAliases) shouldBeInvalid "Plant.count expected to be Int. Found Pair[Int, Int]." } } @@ -203,4 +208,12 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit e.evaluateType(Map.empty, typeAliases) shouldBeValid WomCompositeType(animalTypeMap, Some("Animal")) } } + + it should "work with file paths in struct literal" in { + val structLiteral = """ Bacterium{myFile: "/my/file/path"} """ + val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) + structExpr.shouldBeValidPF { case e => + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomCompositeType(bacteriumTypeMap, Some("Bacterium")) + } + } } From 58fb27d84cabb95dfa650b20310a7b1fdb34eb90 Mon Sep 17 00:00:00 2001 From: Tom Wiseman Date: Wed, 10 Apr 2024 15:17:46 -0400 Subject: [PATCH 11/12] address PR feedback --- .../types/BiscayneTypeEvaluators.scala | 62 ++++++++++++------- .../types/BiscayneTypeEvaluatorSpec.scala | 4 +- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala index 34b664ad32a..0fa041637d1 100644 --- a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala +++ b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala @@ -199,7 +199,9 @@ object BiscayneTypeEvaluators { implicit val structLiteralTypeEvaluator: TypeEvaluator[StructLiteral] = new TypeEvaluator[StructLiteral] { - // Is the evaluated type allowed to be assigned to the expectedType? + /** + * Is the evaluated type allowed to be assigned to the expectedType? + */ def areTypesAssignable(evaluatedType: WomType, expectedType: WomType): Boolean = // NB: This check is a little looser than we'd like it to be. // For example, String is coercible to Int (Int i = "1" is OK) @@ -208,7 +210,10 @@ object BiscayneTypeEvaluators { // This is OK as-is because the value evaluators do the coercing and throw meaningful errors if the coercion fails. expectedType.isCoerceableFrom(evaluatedType) - // Helper method to check something (maybe) found in the struct literal to something (maybe) found in the struct definition. + /** + * Helper method to check if something (maybe) found in the struct literal against something (maybe) found in the struct definition. + * @return The WomType of the evaluated member. Error if either is not present, or if the types aren't compatible. + */ def checkIfMemberIsValid(typeName: String, memberName: String, evaluatedType: Option[WomType], @@ -226,7 +231,11 @@ object BiscayneTypeEvaluators { case None => s"Error evaluating the type of ${typeName}.${memberName}.".invalidNel } - // For each member in the literal, check that it exists in the struct definition and is the expected type. + /** + * For each member in the literal, check that it exists in the struct definition and is the expected type. + * @return The WomCompositeType of the struct literal, as determined from evaluating each member. + * This might not *exactly* match the struct definition due to permitted type coercions. + */ def checkMembersAgainstDefinition(a: StructLiteral, structDefinition: WomCompositeType, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], @@ -246,19 +255,20 @@ object BiscayneTypeEvaluators { } } - errors.headOption match { - case Some((_, Invalid(_))) => - errors.collect { case (_, Invalid(e)) => e.toList.mkString }.toList.mkString(",").invalidNel - case _ => - val types = validatedTypes.collect { case (key, Valid(v)) => (key, v) } - WomCompositeType(types, Some(a.structTypeName)).validNel + if (errors.nonEmpty) { + errors.collect { case (_, Invalid(e)) => e.toList.mkString(", ") }.toList.mkString("[ ", ", ", " ]").invalidNel + } else { + val types = validatedTypes.collect { case (key, Valid(v)) => (key, v) } + WomCompositeType(types, Some(a.structTypeName)).validNel } } - // For every member in the definition, if that member isn't optional, confirm that it is also in the struct literal. + /** + * For each member in the struct definition, if that member isn't optional, confirm that it is also in the struct literal. + */ def checkForMissingMembers(foundMembers: Map[String, WomType], structDefinition: WomCompositeType - ): Option[String] = { + ): ErrorOr[Unit] = { val errors: Iterable[String] = structDefinition.typeMap flatMap { case (memberName, memberType) => memberType match { case WomOptionalType(_) => None @@ -267,32 +277,42 @@ object BiscayneTypeEvaluators { else None } } - errors match { - case Nil => None - case _ => Some(errors.mkString) - } + if (errors.nonEmpty) errors.mkString.invalidNel else ().validNel } + + /** + * Returns the type of a struct literal, assuming it is compatible with an existing struct definition. + * It is required that: + * - a struct definition exist for the literal (looked up the via name of the struct type). This is defined elsewhere in the WDL and is not part of the literal. + * - the struct definition be a WomCompositeType (it's programmer error if it's not) + * - each member provided in the literal is also in the definition, and is coercible to the defined type. + * - all non-optional members of the definition are present in the literal. + * @param a The literal to evaluate + * @param linkedValues Used by the expression type evaluator. + * @param typeAliases A map containing available struct definitions + * @param expressionTypeEvaluator An object capable of evaluating the types of ExpressionElements. Used to evaluate the type of each member provided in the literal. + * @return The type of the struct definition (as found in typeAliases) if all goes well. A list of errors otherwise. + */ override def evaluateType(a: StructLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] - ): ErrorOr[WomType] = { - val structDefinition = typeAliases.get(a.structTypeName) - structDefinition match { + ): ErrorOr[WomType] = + typeAliases.get(a.structTypeName) match { case Some(definition) => definition match { case compositeType: WomCompositeType => checkMembersAgainstDefinition(a, compositeType, linkedValues, typeAliases).flatMap { foundMembers => checkForMissingMembers(foundMembers.typeMap, compositeType) match { - case Some(error) => error.invalidNel + case Invalid(error) => error.invalid case _ => compositeType.validNel } } - case _ => s"Unexpected error while parsing ${a.structTypeName}".invalidNel + case _ => + s"Programmer error: Expected the struct definition of ${a.structTypeName} to be a WomCompositeType".invalidNel } case None => s"Could not find Struct Definition for type ${a.structTypeName}".invalidNel } - } } } diff --git a/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala b/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala index 7595abfdd54..8c1e95f5523 100644 --- a/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala +++ b/wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluatorSpec.scala @@ -181,7 +181,7 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit structExpr.shouldBeValidPF { case e => e.evaluateType(Map.empty, typeAliases - ) shouldBeInvalid "Type Animal does not have a member called fur.,Type Animal does not have a member called isGood." + ) shouldBeInvalid "[ Type Animal does not have a member called fur., Type Animal does not have a member called isGood. ]" } } @@ -189,7 +189,7 @@ class BiscayneTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit val structLiteral = """ Plant{isTasty: true, count: (0, 1)} """ val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) structExpr.shouldBeValidPF { case e => - e.evaluateType(Map.empty, typeAliases) shouldBeInvalid "Plant.count expected to be Int. Found Pair[Int, Int]." + e.evaluateType(Map.empty, typeAliases) shouldBeInvalid "[ Plant.count expected to be Int. Found Pair[Int, Int]. ]" } } From 9ffff90bcb6d95a0e19462c129d8612ed7c9e12f Mon Sep 17 00:00:00 2001 From: Tom Wiseman Date: Wed, 10 Apr 2024 15:42:39 -0400 Subject: [PATCH 12/12] finish //todos and copy to cascades --- .../types/BiscayneTypeEvaluators.scala | 28 ++-- .../types/CascadesTypeEvaluators.scala | 145 +++++++++++++++--- .../types/CascadesTypeEvaluatorSpec.scala | 61 +++++++- .../types/EngineFunctionEvaluators.scala | 85 +++++----- 4 files changed, 245 insertions(+), 74 deletions(-) diff --git a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala index 0fa041637d1..34ea1090d50 100644 --- a/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala +++ b/wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/linking/expression/types/BiscayneTypeEvaluators.scala @@ -19,7 +19,7 @@ object BiscayneTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType)) flatMap { + validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType), typeAliases) flatMap { case WomMapType(keyType, _) => WomArrayType(keyType).validNel case other => s"Cannot invoke 'keys' on type '${other.stableName}'. Expected a map".invalidNel } @@ -32,7 +32,7 @@ object BiscayneTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { + validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType)), typeAliases) flatMap { case WomArrayType(WomPairType(x: WomPrimitiveType, y)) => WomMapType(x, y).validNel case other @ WomArrayType(WomPairType(x, _)) => s"Cannot invoke 'as_map' on type ${other.stableName}. Map keys must be primitive but got '${x.stableName}'".invalidNel @@ -47,7 +47,7 @@ object BiscayneTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType)) flatMap { + validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType), typeAliases) flatMap { case WomMapType(x, y) => WomArrayType(WomPairType(x, y)).validNel case other => s"Cannot invoke 'as_pairs' on type '${other.stableName}'. Expected a map".invalidNel } @@ -60,7 +60,7 @@ object BiscayneTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { + validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType)), typeAliases) flatMap { case WomArrayType(WomPairType(x: WomPrimitiveType, y)) => WomMapType(x, WomArrayType(y)).validNel case other @ WomArrayType(WomPairType(x, _)) => s"Cannot invoke 'collect_by_key' on type ${other.stableName}. Map keys must be primitive but got '${x.stableName}'".invalidNel @@ -114,7 +114,7 @@ object BiscayneTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.arg2, linkedValues, WomArrayType(WomAnyType)) flatMap { + validateParamType(a.arg2, linkedValues, WomArrayType(WomAnyType), typeAliases) flatMap { case WomArrayType(WomArrayType(_)) => s"Cannot invoke 'sep' on type 'Array[Array[_]]'. Expected an Array[String].".invalidNel case WomArrayType(_) => WomStringType.validNel @@ -131,9 +131,9 @@ object BiscayneTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - (validateParamType(a.input, linkedValues, WomSingleFileType), - validateParamType(a.pattern, linkedValues, WomSingleFileType), - validateParamType(a.replace, linkedValues, WomSingleFileType) + (validateParamType(a.input, linkedValues, WomSingleFileType, typeAliases), + validateParamType(a.pattern, linkedValues, WomSingleFileType, typeAliases), + validateParamType(a.replace, linkedValues, WomSingleFileType, typeAliases) ) mapN { (_, _, _) => WomStringType } } @@ -144,8 +144,8 @@ object BiscayneTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - (validateParamType(a.suffix, linkedValues, WomStringType), - validateParamType(a.array, linkedValues, WomArrayType(WomStringType)) + (validateParamType(a.suffix, linkedValues, WomStringType, typeAliases), + validateParamType(a.array, linkedValues, WomArrayType(WomStringType), typeAliases) ) mapN { (_, _) => WomArrayType(WomStringType) } } @@ -156,7 +156,7 @@ object BiscayneTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomArrayType(WomAnyType)) flatMap { + validateParamType(a.param, linkedValues, WomArrayType(WomAnyType), typeAliases) flatMap { case WomArrayType(WomNothingType) => WomArrayType(WomNothingType).validNel case WomArrayType(_: WomPrimitiveType) => WomArrayType(WomStringType).validNel case other @ WomArrayType(_) => @@ -173,7 +173,7 @@ object BiscayneTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomArrayType(WomAnyType)) flatMap { + validateParamType(a.param, linkedValues, WomArrayType(WomAnyType), typeAliases) flatMap { case WomArrayType(WomNothingType) => WomArrayType(WomNothingType).validNel case WomArrayType(_: WomPrimitiveType) => WomArrayType(WomStringType).validNel case other @ WomArrayType(_) => @@ -190,7 +190,7 @@ object BiscayneTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { + validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType)), typeAliases) flatMap { case WomArrayType(WomNothingType) => WomPairType(WomArrayType(WomAnyType), WomArrayType(WomAnyType)).validNel case WomArrayType(WomPairType(x, y)) => WomPairType(WomArrayType(x), WomArrayType(y)).validNel case other => s"Cannot invoke 'unzip' on type '${other.stableName}'. Expected an array of pairs".invalidNel @@ -240,6 +240,8 @@ object BiscayneTypeEvaluators { structDefinition: WomCompositeType, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomCompositeType] = { val checkedMembers: Map[String, ErrorOr[WomType]] = a.elements.map { case (memberKey, memberExpressionElement) => val evaluatedType = diff --git a/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluators.scala b/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluators.scala index 369cc8ba918..7a49804abb6 100644 --- a/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluators.scala +++ b/wdl/transforms/cascades/src/main/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluators.scala @@ -1,5 +1,6 @@ package wdl.transforms.biscayne.linking.expression.types +import cats.data.Validated.{Invalid, Valid} import cats.implicits.{catsSyntaxTuple2Semigroupal, catsSyntaxTuple3Semigroupal} import cats.syntax.validated._ import common.validation.ErrorOr._ @@ -18,7 +19,7 @@ object cascadesTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType)) flatMap { + validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType), typeAliases) flatMap { case WomMapType(keyType, _) => WomArrayType(keyType).validNel case other => s"Cannot invoke 'keys' on type '${other.stableName}'. Expected a map".invalidNel } @@ -31,7 +32,7 @@ object cascadesTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { + validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType)), typeAliases) flatMap { case WomArrayType(WomPairType(x: WomPrimitiveType, y)) => WomMapType(x, y).validNel case other @ WomArrayType(WomPairType(x, _)) => s"Cannot invoke 'as_map' on type ${other.stableName}. Map keys must be primitive but got '${x.stableName}'".invalidNel @@ -46,7 +47,7 @@ object cascadesTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType)) flatMap { + validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType), typeAliases) flatMap { case WomMapType(x, y) => WomArrayType(WomPairType(x, y)).validNel case other => s"Cannot invoke 'as_pairs' on type '${other.stableName}'. Expected a map".invalidNel } @@ -59,7 +60,7 @@ object cascadesTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { + validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType)), typeAliases) flatMap { case WomArrayType(WomPairType(x: WomPrimitiveType, y)) => WomMapType(x, WomArrayType(y)).validNel case other @ WomArrayType(WomPairType(x, _)) => s"Cannot invoke 'collect_by_key' on type ${other.stableName}. Map keys must be primitive but got '${x.stableName}'".invalidNel @@ -113,7 +114,7 @@ object cascadesTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.arg2, linkedValues, WomArrayType(WomAnyType)) flatMap { + validateParamType(a.arg2, linkedValues, WomArrayType(WomAnyType), typeAliases) flatMap { case WomArrayType(WomArrayType(_)) => s"Cannot invoke 'sep' on type 'Array[Array[_]]'. Expected an Array[String].".invalidNel case WomArrayType(_) => WomStringType.validNel @@ -130,9 +131,9 @@ object cascadesTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - (validateParamType(a.input, linkedValues, WomSingleFileType), - validateParamType(a.pattern, linkedValues, WomSingleFileType), - validateParamType(a.replace, linkedValues, WomSingleFileType) + (validateParamType(a.input, linkedValues, WomSingleFileType, typeAliases), + validateParamType(a.pattern, linkedValues, WomSingleFileType, typeAliases), + validateParamType(a.replace, linkedValues, WomSingleFileType, typeAliases) ) mapN { (_, _, _) => WomStringType } } @@ -143,8 +144,8 @@ object cascadesTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - (validateParamType(a.suffix, linkedValues, WomStringType), - validateParamType(a.array, linkedValues, WomArrayType(WomStringType)) + (validateParamType(a.suffix, linkedValues, WomStringType, typeAliases), + validateParamType(a.array, linkedValues, WomArrayType(WomStringType), typeAliases) ) mapN { (_, _) => WomArrayType(WomStringType) } } @@ -155,7 +156,7 @@ object cascadesTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomArrayType(WomAnyType)) flatMap { + validateParamType(a.param, linkedValues, WomArrayType(WomAnyType), typeAliases) flatMap { case WomArrayType(WomNothingType) => WomArrayType(WomNothingType).validNel case WomArrayType(_: WomPrimitiveType) => WomArrayType(WomStringType).validNel case other @ WomArrayType(_) => @@ -172,7 +173,7 @@ object cascadesTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomArrayType(WomAnyType)) flatMap { + validateParamType(a.param, linkedValues, WomArrayType(WomAnyType), typeAliases) flatMap { case WomArrayType(WomNothingType) => WomArrayType(WomNothingType).validNel case WomArrayType(_: WomPrimitiveType) => WomArrayType(WomStringType).validNel case other @ WomArrayType(_) => @@ -189,7 +190,7 @@ object cascadesTypeEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType))) flatMap { + validateParamType(a.param, linkedValues, WomArrayType(WomPairType(WomAnyType, WomAnyType)), typeAliases) flatMap { case WomArrayType(WomNothingType) => WomPairType(WomArrayType(WomAnyType), WomArrayType(WomAnyType)).validNel case WomArrayType(WomPairType(x, y)) => WomPairType(WomArrayType(x), WomArrayType(y)).validNel case other => s"Cannot invoke 'unzip' on type '${other.stableName}'. Expected an array of pairs".invalidNel @@ -197,17 +198,123 @@ object cascadesTypeEvaluators { } implicit val structLiteralTypeEvaluator: TypeEvaluator[StructLiteral] = new TypeEvaluator[StructLiteral] { + + /** + * Is the evaluated type allowed to be assigned to the expectedType? + */ + def areTypesAssignable(evaluatedType: WomType, expectedType: WomType): Boolean = + // NB: This check is a little looser than we'd like it to be. + // For example, String is coercible to Int (Int i = "1" is OK) + // It's not until we actually evaluate the value of the string that we can know if that coercion succeeded or not. (Int i = "orange" will fail) + // We don't know whether the user has provided "1" or "orange" at this stage. + // This is OK as-is because the value evaluators do the coercing and throw meaningful errors if the coercion fails. + expectedType.isCoerceableFrom(evaluatedType) + + /** + * Helper method to check if something (maybe) found in the struct literal against something (maybe) found in the struct definition. + * @return The WomType of the evaluated member. Error if either is not present, or if the types aren't compatible. + */ + def checkIfMemberIsValid(typeName: String, + memberName: String, + evaluatedType: Option[WomType], + expectedType: Option[WomType] + ): ErrorOr[WomType] = + evaluatedType match { + case Some(evaluated) => + expectedType match { + case Some(expected) => + if (areTypesAssignable(evaluated, expected)) evaluated.validNel + else + s"$typeName.$memberName expected to be ${expected.friendlyName}. Found ${evaluated.friendlyName}.".invalidNel + case None => s"Type $typeName does not have a member called $memberName.".invalidNel + } + case None => s"Error evaluating the type of ${typeName}.${memberName}.".invalidNel + } + + /** + * For each member in the literal, check that it exists in the struct definition and is the expected type. + * @return The WomCompositeType of the struct literal, as determined from evaluating each member. + * This might not *exactly* match the struct definition due to permitted type coercions. + */ + def checkMembersAgainstDefinition(a: StructLiteral, + structDefinition: WomCompositeType, + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] + )(implicit + expressionTypeEvaluator: TypeEvaluator[ExpressionElement] + ): ErrorOr[WomCompositeType] = { + val checkedMembers: Map[String, ErrorOr[WomType]] = a.elements.map { case (memberKey, memberExpressionElement) => + val evaluatedType = + expressionTypeEvaluator.evaluateType(memberExpressionElement, linkedValues, typeAliases).toOption + val expectedType = structDefinition.typeMap.get(memberKey) + (memberKey, checkIfMemberIsValid(a.structTypeName, memberKey, evaluatedType, expectedType)) + } + + val (errors, validatedTypes) = checkedMembers.partition { case (_, errorOr) => + errorOr match { + case Invalid(_) => true + case Valid(_) => false + } + } + + if (errors.nonEmpty) { + errors.collect { case (_, Invalid(e)) => e.toList.mkString(", ") }.toList.mkString("[ ", ", ", " ]").invalidNel + } else { + val types = validatedTypes.collect { case (key, Valid(v)) => (key, v) } + WomCompositeType(types, Some(a.structTypeName)).validNel + } + } + + /** + * For each member in the struct definition, if that member isn't optional, confirm that it is also in the struct literal. + */ + def checkForMissingMembers(foundMembers: Map[String, WomType], + structDefinition: WomCompositeType + ): ErrorOr[Unit] = { + val errors: Iterable[String] = structDefinition.typeMap flatMap { case (memberName, memberType) => + memberType match { + case WomOptionalType(_) => None + case _ => + if (!foundMembers.contains(memberName)) Some(s"Expected member ${memberName} not found. ") + else None + } + } + if (errors.nonEmpty) errors.mkString.invalidNel else ().validNel + } + + /** + * Returns the type of a struct literal, assuming it is compatible with an existing struct definition. + * It is required that: + * - a struct definition exist for the literal (looked up the via name of the struct type). This is defined elsewhere in the WDL and is not part of the literal. + * - the struct definition be a WomCompositeType (it's programmer error if it's not) + * - each member provided in the literal is also in the definition, and is coercible to the defined type. + * - all non-optional members of the definition are present in the literal. + * @param a The literal to evaluate + * @param linkedValues Used by the expression type evaluator. + * @param typeAliases A map containing available struct definitions + * @param expressionTypeEvaluator An object capable of evaluating the types of ExpressionElements. Used to evaluate the type of each member provided in the literal. + * @return The type of the struct definition (as found in typeAliases) if all goes well. A list of errors otherwise. + */ override def evaluateType(a: StructLiteral, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - // This works fine, but is not yet a strong enough type check for the WDL 1.1 spec - // (i.e. users are able to instantiate struct literals with k/v pairs that aren't actually in the struct definition, without an error being thrown.) - // We want to add extra validation here, and return a WomCompositeType that matches the struct definition of everything is OK. - // Note that users are allowed to omit optional k/v pairs in their literal. - // This requires some extra work to be done in a subsequent PR. - WomObjectType.validNel + typeAliases.get(a.structTypeName) match { + case Some(definition) => + definition match { + case compositeType: WomCompositeType => + checkMembersAgainstDefinition(a, compositeType, linkedValues, typeAliases).flatMap { foundMembers => + checkForMissingMembers(foundMembers.typeMap, compositeType) match { + case Invalid(error) => error.invalid + case _ => compositeType.validNel + } + } + case _ => + s"Programmer error: Expected the struct definition of ${a.structTypeName} to be a WomCompositeType".invalidNel + } + case None => s"Could not find Struct Definition for type ${a.structTypeName}".invalidNel + } } } diff --git a/wdl/transforms/cascades/src/test/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluatorSpec.scala b/wdl/transforms/cascades/src/test/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluatorSpec.scala index ebc85e82696..b9274d7e866 100644 --- a/wdl/transforms/cascades/src/test/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluatorSpec.scala +++ b/wdl/transforms/cascades/src/test/scala/wdl/transforms/cascades/linking/expression/types/CascadesTypeEvaluatorSpec.scala @@ -22,9 +22,14 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit "hat" -> WomCompositeType(plantTypeMap, Some("Plant")) ) + val bacteriumTypeMap: Map[String, WomType] = Map( + "myFile" -> WomSingleFileType + ) + val typeAliases: Map[String, WomType] = Map( "Plant" -> WomCompositeType(plantTypeMap, Some("Plant")), - "Animal" -> WomCompositeType(animalTypeMap, Some("Animal")) + "Animal" -> WomCompositeType(animalTypeMap, Some("Animal")), + "Bacterium" -> WomCompositeType(bacteriumTypeMap, Some("Bacterium")) ) it should "return nothing from static integer addition" in { @@ -155,12 +160,60 @@ class CascadesTypeEvaluatorSpec extends AnyFlatSpec with CromwellTimeoutSpec wit } it should "evaluate the type of a struct literal" in { - // NB: This is not yet strict enough type checking for the WDL 1.1 spec. - // In a subsequent branch, we will make this be a WomCompositeType that matches the struct definition. + val structLiteral = """ Plant{isTasty: true, count: 42} """ + val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) + structExpr.shouldBeValidPF { case e => + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomCompositeType(plantTypeMap, Some("Plant")) + } + } + + it should "evaluate the type of a struct literal with a nested struct literal" in { + val structLiteral = """ Animal{isMaybeGood: true, hat: Plant{isTasty: true, count: 42}} """ + val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) + structExpr.shouldBeValidPF { case e => + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomCompositeType(animalTypeMap, Some("Animal")) + } + } + + it should "fail to evaluate the type of a struct literal with incorrect members" in { val structLiteral = """ Animal{fur: "fuzzy", isGood: true} """ val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) structExpr.shouldBeValidPF { case e => - e.evaluateType(Map.empty, typeAliases) shouldBeValid WomObjectType + e.evaluateType(Map.empty, + typeAliases + ) shouldBeInvalid "[ Type Animal does not have a member called fur., Type Animal does not have a member called isGood. ]" + } + } + + it should "fail to evaluate the type of a struct literal with members that are the wrong type" in { + val structLiteral = """ Plant{isTasty: true, count: (0, 1)} """ + val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) + structExpr.shouldBeValidPF { case e => + e.evaluateType(Map.empty, typeAliases) shouldBeInvalid "[ Plant.count expected to be Int. Found Pair[Int, Int]. ]" + } + } + + it should "fail if a struct literal member is missing" in { + val structLiteral = """ Plant{count: 4} """ + val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) + structExpr.shouldBeValidPF { case e => + e.evaluateType(Map.empty, typeAliases) shouldBeInvalid "Expected member isTasty not found. " + } + } + + it should "tolerate a missing struct literal optional member" in { + val structLiteral = """ Animal{hat: Plant{isTasty: true, count: 42}} """ + val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) + structExpr.shouldBeValidPF { case e => + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomCompositeType(animalTypeMap, Some("Animal")) + } + } + + it should "work with file paths in struct literal" in { + val structLiteral = """ Bacterium{myFile: "/my/file/path"} """ + val structExpr = fromString[ExpressionElement](structLiteral, parser.parse_e) + structExpr.shouldBeValidPF { case e => + e.evaluateType(Map.empty, typeAliases) shouldBeValid WomCompositeType(bacteriumTypeMap, Some("Bacterium")) } } } diff --git a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/EngineFunctionEvaluators.scala b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/EngineFunctionEvaluators.scala index a1ad06230b8..56d9c59ff72 100644 --- a/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/EngineFunctionEvaluators.scala +++ b/wdl/transforms/new-base/src/main/scala/wdl/transforms/base/linking/expression/types/EngineFunctionEvaluators.scala @@ -42,7 +42,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomArrayType(WomStringType)) + validateParamType(a.param, linkedValues, WomSingleFileType, typeAliases).map(_ => WomArrayType(WomStringType)) } implicit val readTsvFunctionEvaluator: TypeEvaluator[ReadTsv] = new TypeEvaluator[ReadTsv] { @@ -52,7 +52,9 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomArrayType(WomArrayType(WomStringType))) + validateParamType(a.param, linkedValues, WomSingleFileType, typeAliases).map(_ => + WomArrayType(WomArrayType(WomStringType)) + ) } implicit val readMapFunctionEvaluator: TypeEvaluator[ReadMap] = new TypeEvaluator[ReadMap] { @@ -62,7 +64,9 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomMapType(WomStringType, WomStringType)) + validateParamType(a.param, linkedValues, WomSingleFileType, typeAliases).map(_ => + WomMapType(WomStringType, WomStringType) + ) } implicit val readObjectFunctionEvaluator: TypeEvaluator[ReadObject] = new TypeEvaluator[ReadObject] { @@ -72,7 +76,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomObjectType) + validateParamType(a.param, linkedValues, WomSingleFileType, typeAliases).map(_ => WomObjectType) } implicit val readObjectsFunctionEvaluator: TypeEvaluator[ReadObjects] = new TypeEvaluator[ReadObjects] { @@ -82,7 +86,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomArrayType(WomObjectType)) + validateParamType(a.param, linkedValues, WomSingleFileType, typeAliases).map(_ => WomArrayType(WomObjectType)) } implicit val readJsonFunctionEvaluator: TypeEvaluator[ReadJson] = new TypeEvaluator[ReadJson] { @@ -93,7 +97,7 @@ object EngineFunctionEvaluators { expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = // we can't figure out the WomType of data without reading the file hence evaluate it to `WomAnyType` - validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomAnyType) + validateParamType(a.param, linkedValues, WomSingleFileType, typeAliases).map(_ => WomAnyType) } implicit val readIntFunctionEvaluator: TypeEvaluator[ReadInt] = new TypeEvaluator[ReadInt] { @@ -103,7 +107,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomIntegerType) + validateParamType(a.param, linkedValues, WomSingleFileType, typeAliases).map(_ => WomIntegerType) } implicit val readStringFunctionEvaluator: TypeEvaluator[ReadString] = new TypeEvaluator[ReadString] { @@ -113,7 +117,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomStringType) + validateParamType(a.param, linkedValues, WomSingleFileType, typeAliases).map(_ => WomStringType) } implicit val readFloatFunctionEvaluator: TypeEvaluator[ReadFloat] = new TypeEvaluator[ReadFloat] { @@ -123,7 +127,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomFloatType) + validateParamType(a.param, linkedValues, WomSingleFileType, typeAliases).map(_ => WomFloatType) } implicit val readBooleanFunctionEvaluator: TypeEvaluator[ReadBoolean] = new TypeEvaluator[ReadBoolean] { @@ -133,7 +137,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomSingleFileType).map(_ => WomBooleanType) + validateParamType(a.param, linkedValues, WomSingleFileType, typeAliases).map(_ => WomBooleanType) } implicit val writeLinesFunctionEvaluator: TypeEvaluator[WriteLines] = new TypeEvaluator[WriteLines] { @@ -143,7 +147,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomArrayType(WomStringType)).map(_ => WomSingleFileType) + validateParamType(a.param, linkedValues, WomArrayType(WomStringType), typeAliases).map(_ => WomSingleFileType) } implicit val writeTsvFunctionEvaluator: TypeEvaluator[WriteTsv] = new TypeEvaluator[WriteTsv] { @@ -153,7 +157,9 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomArrayType(WomArrayType(WomStringType))).map(_ => WomSingleFileType) + validateParamType(a.param, linkedValues, WomArrayType(WomArrayType(WomStringType)), typeAliases).map(_ => + WomSingleFileType + ) } implicit val writeMapFunctionEvaluator: TypeEvaluator[WriteMap] = new TypeEvaluator[WriteMap] { @@ -163,7 +169,9 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType)).map(_ => WomSingleFileType) + validateParamType(a.param, linkedValues, WomMapType(WomAnyType, WomAnyType), typeAliases).map(_ => + WomSingleFileType + ) } implicit val writeObjectFunctionEvaluator: TypeEvaluator[WriteObject] = new TypeEvaluator[WriteObject] { @@ -173,7 +181,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomObjectType).map(_ => WomSingleFileType) + validateParamType(a.param, linkedValues, WomObjectType, typeAliases).map(_ => WomSingleFileType) } implicit val writeObjectsFunctionEvaluator: TypeEvaluator[WriteObjects] = new TypeEvaluator[WriteObjects] { @@ -183,7 +191,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomArrayType(WomObjectType)).map(_ => WomSingleFileType) + validateParamType(a.param, linkedValues, WomArrayType(WomObjectType), typeAliases).map(_ => WomSingleFileType) } implicit val writeJsonFunctionEvaluator: TypeEvaluator[WriteJson] = new TypeEvaluator[WriteJson] { @@ -216,7 +224,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomIntegerType).map(_ => WomArrayType(WomIntegerType)) + validateParamType(a.param, linkedValues, WomIntegerType, typeAliases).map(_ => WomArrayType(WomIntegerType)) } implicit val transposeFunctionEvaluator: TypeEvaluator[Transpose] = new TypeEvaluator[Transpose] { @@ -240,7 +248,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomArrayType(WomAnyType)).map(_ => WomIntegerType) + validateParamType(a.param, linkedValues, WomArrayType(WomAnyType), typeAliases).map(_ => WomIntegerType) } implicit val flattenFunctionEvaluator: TypeEvaluator[Flatten] = new TypeEvaluator[Flatten] { @@ -294,7 +302,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomOptionalType(WomAnyType)).map(_ => WomBooleanType) + validateParamType(a.param, linkedValues, WomOptionalType(WomAnyType), typeAliases).map(_ => WomBooleanType) } implicit val floorFunctionEvaluator: TypeEvaluator[Floor] = new TypeEvaluator[Floor] { @@ -304,7 +312,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomFloatType).map(_ => WomIntegerType) + validateParamType(a.param, linkedValues, WomFloatType, typeAliases).map(_ => WomIntegerType) } implicit val ceilFunctionEvaluator: TypeEvaluator[Ceil] = new TypeEvaluator[Ceil] { @@ -314,7 +322,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomFloatType).map(_ => WomIntegerType) + validateParamType(a.param, linkedValues, WomFloatType, typeAliases).map(_ => WomIntegerType) } implicit val roundFunctionEvaluator: TypeEvaluator[Round] = new TypeEvaluator[Round] { @@ -324,7 +332,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomFloatType).map(_ => WomIntegerType) + validateParamType(a.param, linkedValues, WomFloatType, typeAliases).map(_ => WomIntegerType) } implicit val globFunctionTypeEvaluator: TypeEvaluator[Glob] = new TypeEvaluator[Glob] { @@ -334,7 +342,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - validateParamType(a.param, linkedValues, WomStringType).map(_ => WomArrayType(WomSingleFileType)) + validateParamType(a.param, linkedValues, WomStringType, typeAliases).map(_ => WomArrayType(WomSingleFileType)) } implicit val sizeFunctionEvaluator: TypeEvaluator[Size] = new TypeEvaluator[Size] { @@ -353,7 +361,7 @@ object EngineFunctionEvaluators { ): ErrorOr[WomType] = { val validatedSecondArg: ErrorOr[Unit] = a.secondParam match { case None => ().validNel - case Some(arg) => validateParamType(arg, linkedValues, WomStringType).void + case Some(arg) => validateParamType(arg, linkedValues, WomStringType, typeAliases).void } val validatedFirstArg: ErrorOr[Unit] = a.firstParam.evaluateType(linkedValues, typeAliases).flatMap { case t if suitableSizeType(t) => ().validNel @@ -373,21 +381,22 @@ object EngineFunctionEvaluators { ): ErrorOr[WomType] = { val validatedSecondArg: ErrorOr[Unit] = a.secondParam match { case None => ().validNel - case Some(arg) => validateParamType(arg, linkedValues, WomStringType).void + case Some(arg) => validateParamType(arg, linkedValues, WomStringType, typeAliases).void } - (validateParamType(a.firstParam, linkedValues, WomSingleFileType), validatedSecondArg) mapN { (_, _) => - WomStringType + (validateParamType(a.firstParam, linkedValues, WomSingleFileType, typeAliases), validatedSecondArg) mapN { + (_, _) => + WomStringType } } } private def crossOrZipType(arg1: ExpressionElement, arg2: ExpressionElement, - linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle] + linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], + typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = - // TODO: can/should we pipe type aliases through here? - (arg1.evaluateType(linkedValues, Map()), arg2.evaluateType(linkedValues, Map())) match { + (arg1.evaluateType(linkedValues, typeAliases), arg2.evaluateType(linkedValues, typeAliases)) match { case (Valid(WomArrayType(left)), Valid(WomArrayType(right))) => WomArrayType(WomPairType(left, right)).validNel case (Valid(otherLeft), Valid(WomArrayType(_))) => @@ -407,7 +416,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - crossOrZipType(a.arg1, a.arg2, linkedValues) + crossOrZipType(a.arg1, a.arg2, linkedValues, typeAliases) } implicit val crossFunctionEvaluator: TypeEvaluator[Cross] = new TypeEvaluator[Cross] { override def evaluateType(a: Cross, @@ -416,7 +425,7 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - crossOrZipType(a.arg1, a.arg2, linkedValues) + crossOrZipType(a.arg1, a.arg2, linkedValues, typeAliases) } implicit val prefixFunctionEvaluator: TypeEvaluator[Prefix] = new TypeEvaluator[Prefix] { @@ -426,8 +435,8 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - (validateParamType(a.prefix, linkedValues, WomStringType), - validateParamType(a.array, linkedValues, WomArrayType(WomStringType)) + (validateParamType(a.prefix, linkedValues, WomStringType, typeAliases), + validateParamType(a.array, linkedValues, WomArrayType(WomStringType), typeAliases) ) mapN { (_, _) => WomArrayType(WomStringType) } } @@ -438,17 +447,17 @@ object EngineFunctionEvaluators { )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement] ): ErrorOr[WomType] = - (validateParamType(a.input, linkedValues, WomSingleFileType), - validateParamType(a.pattern, linkedValues, WomSingleFileType), - validateParamType(a.replace, linkedValues, WomSingleFileType) + (validateParamType(a.input, linkedValues, WomSingleFileType, typeAliases), + validateParamType(a.pattern, linkedValues, WomSingleFileType, typeAliases), + validateParamType(a.replace, linkedValues, WomSingleFileType, typeAliases) ) mapN { (_, _, _) => WomStringType } } def validateParamType(param: ExpressionElement, linkedValues: Map[UnlinkedConsumedValueHook, GeneratedValueHandle], - expectedType: WomType + expectedType: WomType, + typeAliases: Map[String, WomType] )(implicit expressionTypeEvaluator: TypeEvaluator[ExpressionElement]): ErrorOr[WomType] = - // TODO: pipe type aliases through here param.evaluateType(linkedValues, Map()).flatMap { foundType => if (expectedType.isCoerceableFrom(foundType)) { foundType.validNel } else {