You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by bt...@apache.org on 2019/07/03 02:18:11 UTC

[incubator-daffodil] branch master updated: Emit SDE when a type calculator attempts to use an undefined type.

This is an automated email from the ASF dual-hosted git repository.

btsloane pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-daffodil.git


The following commit(s) were added to refs/heads/master by this push:
     new a7c8901  Emit SDE when a type calculator attempts to use an undefined type.
a7c8901 is described below

commit a7c890167f7b5d38cfbab33a4c47d902757293ea
Author: Brandon Sloane <bs...@tresys.com>
AuthorDate: Fri Jun 7 14:00:41 2019 -0400

    Emit SDE when a type calculator attempts to use an undefined type.
    
    This lays down the infrastructure which allows the DPath
    interperator to lookup type calculators at compile time,
    which will allow us to make their return type dependent on
    the calculator they are using, instead of their functin name.
    
    DAFFODIL-2148
---
 .../daffodil/dpath/DFDLExpressionParser.scala      | 20 ++++--
 .../org/apache/daffodil/dsom/ElementBase.scala     |  3 +-
 .../org/apache/daffodil/dsom/SchemaComponent.scala |  5 +-
 .../scala/org/apache/daffodil/dsom/SchemaSet.scala |  4 +-
 .../org/apache/daffodil/dsom/SimpleTypes.scala     | 24 +++++--
 .../daffodil/dpath/DFDLXTypeCalcFunctions.scala    |  7 +-
 .../org/apache/daffodil/dpath/DPathRuntime.scala   |  6 +-
 .../scala/org/apache/daffodil/dpath/DState.scala   | 54 ++++++++------
 .../apache/daffodil/dsom/CompiledExpression1.scala | 51 ++++++++------
 .../apache/daffodil/dsom/ExpressionCompiler.scala  |  1 +
 .../daffodil/processors/SchemaSetRuntimeData.scala |  3 +-
 .../daffodil/processors/TypeCalculator.scala       | 82 ++++++++++++++--------
 .../daffodil/extensions/lookAhead/lookAhead.tdml   |  4 +-
 .../type_calc/typeCalcFunctionErrors.tdml          | 71 ++++++++++++++++---
 .../extensions/TestInputTypeValueCalc.scala        |  2 +
 15 files changed, 230 insertions(+), 107 deletions(-)

diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dpath/DFDLExpressionParser.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dpath/DFDLExpressionParser.scala
index 494d8b1..323e1ed 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dpath/DFDLExpressionParser.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dpath/DFDLExpressionParser.scala
@@ -17,14 +17,20 @@
 
 package org.apache.daffodil.dpath
 
-import org.apache.daffodil.exceptions._
-import org.apache.daffodil.dsom._
-import scala.xml.NamespaceBinding
-import org.apache.daffodil.xml._
-import scala.util.parsing.input.CharSequenceReader
+import java.math.{ BigDecimal => JBigDecimal }
+import java.math.{ BigInteger => JBigInt }
+
 import scala.util.parsing.combinator.RegexParsers
-import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInt }
-import org.apache.daffodil.oolag.OOLAG._
+import scala.util.parsing.input.CharSequenceReader
+import scala.xml.NamespaceBinding
+
+import org.apache.daffodil.dsom.CompiledExpression
+import org.apache.daffodil.dsom.ConstantExpression
+import org.apache.daffodil.dsom.DPathCompileInfo
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.oolag.OOLAG.OOLAGHost
+import org.apache.daffodil.xml.NamedQName
+import org.apache.daffodil.xml.QNameRegex
 
 /**
  * Parses DPath expressions. Most real analysis is done later. This is
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
index 417434e..787020d 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
@@ -300,7 +300,8 @@ trait ElementBase
       namedQName,
       optPrimType,
       schemaFileLocation,
-      tunable)
+      tunable,
+      schemaSet.typeCalcMap)
     eci
   }
 
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaComponent.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaComponent.scala
index bf30bd5..1d454b4 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaComponent.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaComponent.scala
@@ -62,7 +62,7 @@ trait SchemaComponent
   override def oolagContextViaArgs = optLexicalParent
 
   lazy val tunable: DaffodilTunables = optLexicalParent.get.tunable
-
+  
   lazy val dpathCompileInfo: DPathCompileInfo =
     new DPathCompileInfo(
       enclosingComponent.map { _.dpathCompileInfo },
@@ -70,7 +70,8 @@ trait SchemaComponent
       namespaces,
       path,
       schemaFileLocation,
-      tunable)
+      tunable,
+      schemaSet.typeCalcMap)
 
   /**
    * ALl non-terms get runtimeData from this definition. All Terms
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaSet.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaSet.scala
index dcab2e0..9c1292a 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaSet.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaSet.scala
@@ -40,6 +40,7 @@ import org.apache.daffodil.processors.TypeCalculator
 import scala.collection.immutable.Map
 import scala.collection.immutable.HashMap
 import org.apache.daffodil.compiler.ProcessorFactory
+import org.apache.daffodil.processors.TypeCalculatorCompiler.TypeCalcMap
 
 /**
  * A schema set is exactly that, a set of schemas. Each schema has
@@ -173,11 +174,10 @@ final class SchemaSet(
 
   lazy val globalSimpleTypeDefs: Seq[GlobalSimpleTypeDef] = schemas.flatMap(_.globalSimpleTypeDefs)
 
-  lazy val typeCalcMap: Map[GlobalQName, TypeCalculator[AnyRef, AnyRef]] = {
+  lazy val typeCalcMap: TypeCalcMap = {
     val factories = globalSimpleTypeDefs
     val withCalc = factories.filter(_.optTypeCalculator.isDefined)
     val mappings = withCalc.map(st => (st.globalQName, st.optTypeCalculator.get))
-
     mappings.toMap
   }
 
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SimpleTypes.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SimpleTypes.scala
index c22bb7a..9d11e17 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SimpleTypes.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SimpleTypes.scala
@@ -339,16 +339,25 @@ abstract class SimpleTypeDefBase(xml: Node, lexicalParent: SchemaComponent)
         TypeCalculatorCompiler.compileUnion(subCalculators)
       })
       val fromExpression: Option[TypeCalculator[AnyRef, AnyRef]] = {
-        val optInputCompiled = optInputTypeCalc.toOption.map(sExpr => {
+        /*
+         * This is a minefield for circular dependencies.
+         * In order to compile expressions involve many typeCalc functions,
+         * the DPath compiler needs to look up the typeCalculator involved
+         * (to determine the src/dst type of the calculator)
+         * However, a fromExpression typeCalculator needs to compile DPath expressions,
+         * which may make use of typeCalc functions, and therefore need for the expressions
+         * to have been already compiled.
+         */
+        lazy val optInputCompiled = optInputTypeCalc.toOption.map(sExpr => {
           val prop = optInputTypeCalc.asInstanceOf[Found]
-          val qn = GlobalQName(Some("daf"), "inputTypeCalc", XMLUtils.dafintURI)
+          val qn = GlobalQName(Some("dfdlx"), "inputTypeCalc", XMLUtils.dafintURI)
           val exprNamespaces = prop.location.namespaces
           val exprComponent = prop.location.asInstanceOf[SchemaComponent]
           ExpressionCompilers.AnyRef.compileExpression(
             qn,
             dstType, sExpr, exprNamespaces, exprComponent.dpathCompileInfo, false, this, dpathCompileInfo)
         })
-        val optOutputCompiled = optOutputTypeCalc.toOption.map(sExpr => {
+        lazy val optOutputCompiled = optOutputTypeCalc.toOption.map(sExpr => {
           val prop = optOutputTypeCalc.asInstanceOf[Found]
           val qn = GlobalQName(Some("daf"), "outputTypeCalc", XMLUtils.dafintURI)
           val exprNamespaces = prop.location.namespaces
@@ -357,9 +366,12 @@ abstract class SimpleTypeDefBase(xml: Node, lexicalParent: SchemaComponent)
             qn,
             srcType, sExpr, exprNamespaces, exprComponent.dpathCompileInfo, false, this, dpathCompileInfo)
         })
-        (optInputCompiled, optOutputCompiled) match {
-          case (None, None) => None
-          case _ => Some(TypeCalculatorCompiler.compileExpression(optInputCompiled, optOutputCompiled, srcType, dstType))
+        val supportsParse = optInputTypeCalc.isDefined
+        val supportsUnparse = optOutputTypeCalc.isDefined
+        if (supportsParse || supportsUnparse) {
+          Some(TypeCalculatorCompiler.compileTypeCalculatorFromExpression(optInputCompiled, optOutputCompiled, srcType, dstType, supportsParse, supportsUnparse))
+        } else {
+          None
         }
       }
 
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLXTypeCalcFunctions.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLXTypeCalcFunctions.scala
index a4e7046..0e85725 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLXTypeCalcFunctions.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLXTypeCalcFunctions.scala
@@ -44,11 +44,10 @@ trait TypeCalculatorNamedDispatch { self: FNTwoArgs =>
    */
   def getCalculator(arg1: AnyRef, dstate: DState, expectedInputType: NodeInfo.Kind, expectedOutputType: NodeInfo.Kind, requireInputCalc: Boolean = false, requireOutputCalc: Boolean = false): TypeCalculator[AnyRef, AnyRef] = {
     val qn = arg1.asInstanceOf[String]
-    val runtimeData = dstate.runtimeData.get
-    val refType = QName.resolveRef(qn, runtimeData.namespaces, runtimeData.tunable).get.toGlobalQName
-    val maybeCalc = dstate.maybeSsrd.get.typeCalculators.get(refType)
+    val refType = QName.resolveRef(qn, dstate.compileInfo.namespaces, dstate.compileInfo.tunable).get.toGlobalQName
+    val maybeCalc = dstate.typeCalculators.get(refType)
     if (!maybeCalc.isDefined) {
-      throw FNErrorFunctionException(Maybe(runtimeData.schemaFileLocation), dstate.contextLocation, s"Simple type ${refType} does not exist or does not have a repType")
+      dstate.SDE("Simple type %s does not exist or does not have a repType", refType)
     }
     val calc = maybeCalc.get
     if (!expectedInputType.isSubtypeOf(calc.srcType)) {
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala
index 3a4c870..362ea6a 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala
@@ -36,6 +36,8 @@ import org.apache.daffodil.processors.VariableRuntimeData
 import org.apache.daffodil.util.Misc
 import org.apache.daffodil.dpath.NodeInfo.PrimType
 import org.apache.daffodil.dsom.DPathCompileInfo
+import org.apache.daffodil.xml.GlobalQName
+import org.apache.daffodil.processors.TypeCalculator
 
 class CompiledDPath(val ops: RecipeOp*) extends Serializable {
 
@@ -72,7 +74,9 @@ class CompiledDPath(val ops: RecipeOp*) extends Serializable {
    * TODO: constant folding really should operate on sub-expressions of expressions
    * so that part of an expression can be constant, not necessarily the whole thing.
    */
-  def runExpressionForConstant(sfl: SchemaFileLocation, compileInfo: DPathCompileInfo): Option[AnyRef] = {
+  def runExpressionForConstant(
+      sfl: SchemaFileLocation, 
+      compileInfo: DPathCompileInfo ): Option[AnyRef] = {
 
     //
     // we use a special dummy dstate here that errors out via throw
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DState.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DState.scala
index 14c49b3..73290de 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DState.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DState.scala
@@ -17,7 +17,7 @@
 
 package org.apache.daffodil.dpath
 
-import org.apache.daffodil.processors._ ; import org.apache.daffodil.infoset._
+import org.apache.daffodil.processors._; import org.apache.daffodil.infoset._
 import org.apache.daffodil.exceptions._
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.util.Maybe._
@@ -27,6 +27,8 @@ import org.apache.daffodil.api.DataLocation
 import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInt }
 import org.apache.daffodil.api.WarnID
 import org.apache.daffodil.dsom.DPathCompileInfo
+import org.apache.daffodil.xml.GlobalQName
+import org.apache.daffodil.processors.TypeCalculatorCompiler.TypeCalcMap
 
 /**
  * Modes for expression evaluation.
@@ -96,7 +98,7 @@ case object UnparserNonBlocking extends EvalMode
  * if used in a forward referencing expression the expression can block until the information
  * becomes available.
  */
-case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
+case class DState(val maybeSsrd: Maybe[SchemaSetRuntimeData]) {
   import org.apache.daffodil.util.Numbers._
 
   var isCompile = false
@@ -111,10 +113,10 @@ case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
    * (eg. to the runtime stack), and replaces the below fields.
    * At the end of the computation, it should restore the below fields.
    */
-  var logicalValue:Maybe[(AnyRef, NodeInfo.Kind)] = Maybe.Nope
-  
-  var repValue:Maybe[(AnyRef, NodeInfo.Kind)] = Maybe.Nope
-  
+  var logicalValue: Maybe[(AnyRef, NodeInfo.Kind)] = Maybe.Nope
+
+  var repValue: Maybe[(AnyRef, NodeInfo.Kind)] = Maybe.Nope
+
   /**
    * The currentValue is used when we have a value that is not
    * associated with an element of simple type. E.g., If I have
@@ -193,15 +195,15 @@ case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
   def setCurrentValue(v: AnyRef) {
     _currentValue = v
     _currentNode = null
-  }  
+  }
   def setCurrentValue(v: Long) {
     _currentValue = asAnyRef(v)
     _currentNode = null
-  }  
+  }
   def setCurrentValue(v: Boolean) {
     _currentValue = asAnyRef(v)
     _currentNode = null
-  }  
+  }
 
   def booleanValue: Boolean = currentValue.asInstanceOf[Boolean]
 
@@ -280,16 +282,16 @@ case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
   def currentElement = currentNode.asInstanceOf[DIElement]
   def currentArray = currentNode.asInstanceOf[DIArray]
   def currentComplex = currentNode.asComplex
-  
+
   def nextSibling = {
     val contents = currentElement.parent.asInstanceOf[DIComplex].contents
-       
+
     //TOOD, currentNode should really know this
     val i = contents.indexOf(currentNode)
-    if(i == contents.length-1){
+    if (i == contents.length - 1) {
       throw new InfosetNoNextSiblingException(currentNode.asSimple, currentNode.erd.dpathElementCompileInfo)
-    }else{
-      contents(i+1)
+    } else {
+      contents(i + 1)
     }
   }
 
@@ -325,12 +327,12 @@ case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
   def runtimeData = {
     if (contextNode.isDefined) One(contextNode.get.erd)
     else Nope
-  } 
-  
+  }
+
   // Overwritten in DStateForConstantFolding, so it should
   // be safe to assume we have runtime data
   def compileInfo = runtimeData.get.dpathCompileInfo
-  
+
   private var _contextNode: Maybe[DINode] = Nope
   def contextNode = _contextNode
   def setContextNode(node: DINode) {
@@ -357,6 +359,8 @@ case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
     Nope
   }
 
+  def typeCalculators = maybeSsrd.get.typeCalculators
+
   private var _savesErrorsAndWarnings: Maybe[SavesErrorsAndWarnings] = Nope
   def errorOrWarn = _savesErrorsAndWarnings
   def setErrorOrWarn(s: SavesErrorsAndWarnings) {
@@ -368,7 +372,7 @@ case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
   def setArrayPos(arrayPos1b: Long) {
     _arrayPos = arrayPos1b
   }
-  
+
   private var _parseOrUnparseState: Maybe[ParseOrUnparseState] = Nope
   def parseOrUnparseState = _parseOrUnparseState
   def setParseOrUnparseState(state: ParseOrUnparseState) {
@@ -376,8 +380,12 @@ case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
   }
 
   def SDE(formatString: String, args: Any*) = {
-    Assert.usage(runtimeData.isDefined)
-    errorOrWarn.get.SDE(formatString, args: _*)
+    if (isCompile) {
+      compileInfo.SDE(formatString, args: _*)
+    } else {
+      Assert.usage(runtimeData.isDefined)
+      errorOrWarn.get.SDE(formatString, args: _*)
+    }
   }
 
   // These exists so we can override it in our fake DState we use when
@@ -430,7 +438,8 @@ object DState {
   }
 }
 
-class DStateForConstantFolding(override val compileInfo: DPathCompileInfo) extends DState(Nope) {
+class DStateForConstantFolding(
+  override val compileInfo: DPathCompileInfo) extends DState(Nope) {
   private def die = throw new java.lang.IllegalStateException("No infoset at compile time.")
 
   override def currentSimple = currentNode.asInstanceOf[DISimple]
@@ -444,6 +453,7 @@ class DStateForConstantFolding(override val compileInfo: DPathCompileInfo) exten
   override def fnExists() = die
   override def arrayPos = die
   override def arrayLength = die
-  
+  override val typeCalculators = compileInfo.typeCalcMap
+
   isCompile = true
 }
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/CompiledExpression1.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/CompiledExpression1.scala
index 25c58d3..0b3d065 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/CompiledExpression1.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/CompiledExpression1.scala
@@ -17,25 +17,29 @@
 
 package org.apache.daffodil.dsom
 
-import org.apache.daffodil.exceptions._
-import org.apache.daffodil.processors.VariableMap
-import org.apache.daffodil.processors.Suspension
-import org.apache.daffodil.util._
-import org.apache.daffodil.xml._
-import org.apache.daffodil.dpath._
-import org.apache.daffodil.xml.NamedQName
-import org.apache.daffodil.util.PreSerialization
-import org.apache.daffodil.dpath.NodeInfo.PrimType
-import org.apache.daffodil.processors.ParseOrUnparseState
-import org.apache.daffodil.util.TransientParam
-import org.apache.daffodil.util.Maybe
-import scala.runtime.ScalaRunTime.stringOf // for printing arrays properly.
-import org.apache.daffodil.exceptions.SchemaFileLocation
-import org.apache.daffodil.exceptions.HasSchemaFileLocation
-import org.apache.daffodil.processors.ParseOrUnparseState
+import scala.runtime.ScalaRunTime.stringOf
+
 import org.apache.daffodil.api.DaffodilTunables
 import org.apache.daffodil.api.UnqualifiedPathStepPolicy
 import org.apache.daffodil.api.WarnID
+import org.apache.daffodil.dpath.DState
+import org.apache.daffodil.dpath.NodeInfo
+import org.apache.daffodil.dpath.NodeInfo.PrimType
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.exceptions.HasSchemaFileLocation
+import org.apache.daffodil.exceptions.SchemaFileLocation
+import org.apache.daffodil.processors.ParseOrUnparseState
+import org.apache.daffodil.processors.Suspension
+import org.apache.daffodil.processors.TypeCalculatorCompiler.TypeCalcMap
+import org.apache.daffodil.processors.VariableMap
+import org.apache.daffodil.util.Maybe
+import org.apache.daffodil.util.MaybeULong
+import org.apache.daffodil.util.PreSerialization
+import org.apache.daffodil.util.TransientParam
+import org.apache.daffodil.xml.NS
+import org.apache.daffodil.xml.NamedQName
+import org.apache.daffodil.xml.NoNamespace
+import org.apache.daffodil.xml.StepQName
 
 trait ContentValueReferencedElementInfoMixin {
 
@@ -200,14 +204,20 @@ class DPathCompileInfo(
   val namespaces: scala.xml.NamespaceBinding,
   val path: String,
   override val schemaFileLocation: SchemaFileLocation,
-  val tunable: DaffodilTunables)
+  val tunable: DaffodilTunables,
+  @TransientParam typeCalcMapArg: => TypeCalcMap)
   extends ImplementsThrowsSDE with PreSerialization
   with HasSchemaFileLocation {
 
   lazy val parent = parentArg
   lazy val variableMap =
     variableMapArg
-
+    
+  /*
+   * This map(identity) pattern appears to work around an unidentified bug with serialization.
+   */
+  lazy val typeCalcMap: TypeCalcMap = typeCalcMapArg.map(identity)
+    
   override def preSerialization: Any = {
     parent
     variableMap
@@ -305,8 +315,9 @@ class DPathElementCompileInfo(
   val namedQName: NamedQName,
   val optPrimType: Option[PrimType],
   sfl: SchemaFileLocation,
-  override val tunable: DaffodilTunables)
-  extends DPathCompileInfo(parentArg, variableMap, namespaces, path, sfl, tunable)
+  override val tunable: DaffodilTunables,
+  typeCalcMap: TypeCalcMap)
+  extends DPathCompileInfo(parentArg, variableMap, namespaces, path, sfl, tunable, typeCalcMap)
   with HasSchemaFileLocation {
 
   lazy val elementChildrenCompileInfo = elementChildrenCompileInfoArg
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/ExpressionCompiler.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/ExpressionCompiler.scala
index 3638203..410690a 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/ExpressionCompiler.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/ExpressionCompiler.scala
@@ -22,6 +22,7 @@ import org.apache.daffodil.xml.NamedQName
 import scala.xml.NamespaceBinding
 import java.lang.{ Long => JLong, Boolean => JBoolean }
 import org.apache.daffodil.oolag.OOLAG._
+import org.apache.daffodil.processors.TypeCalculatorCompiler.TypeCalcMap
 
 trait ExpressionCompilerBase[T <: AnyRef] {
 
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/SchemaSetRuntimeData.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/SchemaSetRuntimeData.scala
index 08175f2..fc47d77 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/SchemaSetRuntimeData.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/SchemaSetRuntimeData.scala
@@ -23,6 +23,7 @@ import org.apache.daffodil.api.Diagnostic
 import org.apache.daffodil.processors.unparsers.Unparser
 import org.apache.daffodil.processors.parsers.Parser
 import org.apache.daffodil.xml.GlobalQName
+import org.apache.daffodil.processors.TypeCalculatorCompiler.TypeCalcMap
 
 final class SchemaSetRuntimeData(
   val parser: Parser,
@@ -31,7 +32,7 @@ final class SchemaSetRuntimeData(
   val elementRuntimeData: ElementRuntimeData,
   var variables: VariableMap,
   var validationMode: ValidationMode.Type,
-  val typeCalculators: Map[GlobalQName, TypeCalculator[AnyRef, AnyRef]])
+  val typeCalculators: TypeCalcMap)
   extends Serializable with ThrowsSDE {
 
   def tunable = elementRuntimeData.tunable
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/TypeCalculator.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/TypeCalculator.scala
index 7bc2759..332da69 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/TypeCalculator.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/TypeCalculator.scala
@@ -17,31 +17,25 @@
 
 package org.apache.daffodil.processors
 
-import scala.math.BigInt
+import scala.collection.immutable.HashMap
 import scala.collection.immutable.HashSet
+
+import org.apache.daffodil.dpath.DState
+import org.apache.daffodil.dpath.NodeInfo
 import org.apache.daffodil.dsom.CompiledExpression
-import org.apache.daffodil.util.Maybe
-import org.apache.daffodil.oolag.OOLAG.OOLAGHost
-import org.apache.daffodil.dsom.CompiledExpression
-import org.apache.daffodil.dsom.CompiledExpression
-import scala.collection.immutable.HashMap
+import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.processors.parsers.PState
 import org.apache.daffodil.processors.parsers.ParseError
-import org.apache.daffodil.processors.parsers.GeneralParseFailure
-import org.apache.daffodil.exceptions.Assert
-import java.lang.{ Long => JLong }
 import org.apache.daffodil.processors.unparsers.UState
+import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.util.Maybe.One
-import org.apache.daffodil.dsom.CompiledExpression
-import org.apache.daffodil.dpath.DState
-import org.apache.daffodil.processors.parsers.ParseError
-import org.apache.daffodil.util.Maybe.One
-import org.apache.daffodil.dpath.NodeInfo.Kind
-import org.apache.daffodil.dpath.NodeInfo
+import org.apache.daffodil.util.PreSerialization
 import org.apache.daffodil.util.RangeBound
+import org.apache.daffodil.util.TransientParam
+import org.apache.daffodil.xml.GlobalQName
 
 abstract class TypeCalculator[A <: AnyRef, B <: AnyRef](val srcType: NodeInfo.Kind, val dstType: NodeInfo.Kind)
-  extends Serializable {
+  extends PreSerialization {
   type Error = String
 
   /*
@@ -118,10 +112,13 @@ abstract class TypeCalculator[A <: AnyRef, B <: AnyRef](val srcType: NodeInfo.Ki
 
   def supportsParse: Boolean = true
   def supportsUnparse: Boolean = true
+
+  @throws(classOf[java.io.IOException])
+  final private def writeObject(out: java.io.ObjectOutputStream): Unit = serializeObject(out)
 }
 
 class KeysetValueTypeCalculatorOrdered[A <: AnyRef, B <: AnyRef](valueMap: HashMap[A, B], rangeTable: Seq[(RangeBound[A], RangeBound[A], B)], unparseMap: HashMap[B, A],
-                                                                 srcType: NodeInfo.Kind, dstType: NodeInfo.Kind)
+  srcType: NodeInfo.Kind, dstType: NodeInfo.Kind)
   extends TypeCalculator[A, B](srcType, dstType) {
 
   override def inputTypeCalc(x: A, xType: NodeInfo.Kind): (Maybe[B], Maybe[Error]) = {
@@ -145,7 +142,7 @@ class KeysetValueTypeCalculatorOrdered[A <: AnyRef, B <: AnyRef](valueMap: HashM
   override def outputTypeCalc(x: B, xType: NodeInfo.Kind): (Maybe[A], Maybe[Error]) = {
     unparseMap.get(x) match {
       case Some(v) => (Maybe(v), Maybe.Nope)
-      case None    => (Maybe.Nope, One(s"Value ${x} not found in keyset-value mapping"))
+      case None => (Maybe.Nope, One(s"Value ${x} not found in keyset-value mapping"))
     }
   }
 
@@ -158,7 +155,7 @@ class KeysetValueTypeCalculatorUnordered[A <: AnyRef, B <: AnyRef](valueMap: Has
     if (valueMap.contains(x)) {
       valueMap.get(x) match {
         case Some(a) => (Maybe(a), Maybe.Nope)
-        case None    => (Maybe.Nope, One(s"Value ${x} not found in keyset-value mapping"))
+        case None => (Maybe.Nope, One(s"Value ${x} not found in keyset-value mapping"))
       }
     } else {
       (Maybe.Nope, One(s"Key ${x} not found in keyset-value mapping"))
@@ -168,17 +165,34 @@ class KeysetValueTypeCalculatorUnordered[A <: AnyRef, B <: AnyRef](valueMap: Has
   override def outputTypeCalc(x: B, xType: NodeInfo.Kind): (Maybe[A], Maybe[Error]) = {
     unparseMap.get(x) match {
       case Some(v) => (Maybe(v), Maybe.Nope)
-      case None    => (Maybe.Nope, One(s"Value ${x} not found in keyset-value mapping"))
+      case None => (Maybe.Nope, One(s"Value ${x} not found in keyset-value mapping"))
     }
   }
 
 }
 
-class ExpressionTypeCalculator[A <: AnyRef, B <: AnyRef](maybeInputTypeCalc: Maybe[CompiledExpression[B]], maybeOutputTypeCalc: Maybe[CompiledExpression[A]],
-                                                         srcType: NodeInfo.Kind, dstType: NodeInfo.Kind)
+class ExpressionTypeCalculator[A <: AnyRef, B <: AnyRef](
+  @TransientParam maybeInputTypeCalcArg: => Maybe[CompiledExpression[B]],
+  @TransientParam maybeOutputTypeCalcArg: => Maybe[CompiledExpression[A]],
+  srcType: NodeInfo.Kind, dstType: NodeInfo.Kind,
+  override val supportsParse: Boolean, override val supportsUnparse: Boolean)
   extends TypeCalculator[A, B](srcType, dstType) {
-  override def supportsParse = maybeInputTypeCalc.isDefined
-  override def supportsUnparse = maybeOutputTypeCalc.isDefined
+
+  /*
+   * Compiling DPath expressions may need to evaluate typeCalculators in order to lookup their srcType and dstType.
+   * To prevent circular dependencies, this means that the underlying expressions must be lazy.
+   *
+   * Since these fields must be lazy, we cannot use them to determine supportsParse or supportUnparse
+   */
+
+  lazy val maybeInputTypeCalc = maybeInputTypeCalcArg
+  lazy val maybeOutputTypeCalc = maybeOutputTypeCalcArg
+
+  override protected def preSerialization: Any = {
+    super.preSerialization
+    maybeInputTypeCalc
+    maybeOutputTypeCalc
+  }
 
   //The class TypeValueCalc will verify that supports(Un)Parse is true when nessasary
   //Therefore, if we ever call the below functions, we know that the relevent Maybe object is defined.
@@ -315,6 +329,9 @@ object RepValueSetCompiler {
 }
 
 object TypeCalculatorCompiler {
+
+  type TypeCalcMap = Map[GlobalQName, TypeCalculator[AnyRef, AnyRef]]
+
   // mappings: [(keySet, canonicalKey, value)]
   def compileKeysetValue[A <: AnyRef, B <: AnyRef](mappings: Seq[(RepValueSet[A], A, B)], srcType: NodeInfo.Kind, dstType: NodeInfo.Kind): TypeCalculator[A, B] = {
     Assert.invariant(!mappings.isEmpty)
@@ -349,10 +366,19 @@ object TypeCalculatorCompiler {
     }
   }
 
-  def compileExpression[A <: AnyRef, B <: AnyRef](optInputTypeCalc: Option[CompiledExpression[B]], optOutputTypeCalc: Option[CompiledExpression[A]], srcType: NodeInfo.Kind, dstType: NodeInfo.Kind): ExpressionTypeCalculator[A, B] = {
-    val maybeInputType: Maybe[CompiledExpression[B]] = optInputTypeCalc.map(Maybe(_)).getOrElse(Maybe.Nope)
-    val maybeOutputType: Maybe[CompiledExpression[A]] = optOutputTypeCalc.map(Maybe(_)).getOrElse(Maybe.Nope)
-    new ExpressionTypeCalculator(maybeInputType, maybeOutputType, srcType, dstType)
+  /*
+   * compileExpression is already a highly overloaded name from the DPath expression compiler.
+   * While this technically overload that function, to avoid confusion, we are giving it a different
+   * name entirely.
+   */
+  def compileTypeCalculatorFromExpression[A <: AnyRef, B <: AnyRef](
+    optInputTypeCalc: => Option[CompiledExpression[B]],
+    optOutputTypeCalc: => Option[CompiledExpression[A]],
+    srcType: NodeInfo.Kind, dstType: NodeInfo.Kind,
+    supportsParse: Boolean, supportsUnparse: Boolean): ExpressionTypeCalculator[A, B] = {
+    lazy val maybeInputType: Maybe[CompiledExpression[B]] = optInputTypeCalc.map(Maybe(_)).getOrElse(Maybe.Nope)
+    lazy val maybeOutputType: Maybe[CompiledExpression[A]] = optOutputTypeCalc.map(Maybe(_)).getOrElse(Maybe.Nope)
+    new ExpressionTypeCalculator(maybeInputType, maybeOutputType, srcType, dstType, supportsParse, supportsUnparse)
   }
   def compileIdentity[A <: AnyRef](srcType: NodeInfo.Kind): TypeCalculator[A, A] = new IdentifyTypeCalculator(srcType)
 
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/extensions/lookAhead/lookAhead.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/extensions/lookAhead/lookAhead.tdml
index ab86e81..c0e92dd 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/extensions/lookAhead/lookAhead.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/extensions/lookAhead/lookAhead.tdml
@@ -299,7 +299,7 @@
     </tdml:documentPart>
     </tdml:document>
     <tdml:errors>
-      <tdml:error>Runtime Schema Definition Error</tdml:error>
+      <tdml:error>Schema Definition Error</tdml:error>
       <tdml:error>Look-ahead distance of 520 bits exceeds implementation defined limit of 512 bits</tdml:error>
     </tdml:errors>
   </tdml:parserTestCase>
@@ -321,7 +321,7 @@
     </tdml:documentPart>
     </tdml:document>
     <tdml:errors>
-      <tdml:error>Runtime Schema Definition Error</tdml:error>
+      <tdml:error>Schema Definition Error</tdml:error>
       <tdml:error>Look-ahead distance of 513 bits exceeds implementation defined limit of 512 bits</tdml:error>
     </tdml:errors>
   </tdml:parserTestCase>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/extensions/type_calc/typeCalcFunctionErrors.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/extensions/type_calc/typeCalcFunctionErrors.tdml
index 729877d..5aa2a7e 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/extensions/type_calc/typeCalcFunctionErrors.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/extensions/type_calc/typeCalcFunctionErrors.tdml
@@ -136,7 +136,8 @@
     </tdml:documentPart>
     </tdml:document>
     <tdml:errors>
-      <tdml:error>Parse Error</tdml:error>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>Cannot convert 'a' from String type to Long</tdml:error>
     </tdml:errors>
     <tdml:warnings>
       <tdml:warning>Schema Definition Warning</tdml:warning>
@@ -175,7 +176,8 @@
     </tdml:documentPart>
     </tdml:document>
     <tdml:errors>
-      <tdml:error>Parse Error</tdml:error>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>Cannot convert 'a' from String type to Long</tdml:error>
     </tdml:errors>
     <tdml:warnings>
       <tdml:warning>Schema Definition Warning</tdml:warning>
@@ -246,12 +248,10 @@
     <tdml:documentPart type="text">a</tdml:documentPart>
     </tdml:document>
     <tdml:errors>
-      <tdml:error>parse Error</tdml:error>
+      <tdml:error>Parse Error</tdml:error>
+      <tdml:error>Cannot convert</tdml:error>
+      <tdml:error>from String type to Long</tdml:error>
     </tdml:errors>
-    <tdml:warnings>
-      <tdml:warning>Schema Definition Warning</tdml:warning>
-      <tdml:warning>Expression result type (String) should be manually cast to the expected type (Int)</tdml:warning>
-    </tdml:warnings>
   </tdml:parserTestCase>
 
   <tdml:parserTestCase name="typeCalcDispatch_typeError_10"
@@ -285,10 +285,6 @@
         <typeCalcDispatch_typeError_12>a</typeCalcDispatch_typeError_12>
       </tdml:dfdlInfoset>
     </tdml:infoset>
-    <tdml:warnings>
-      <tdml:warning>Schema Definition Warning</tdml:warning>
-      <tdml:warning>Expression result type (String) should be manually cast to the expected type (Int)</tdml:warning>
-    </tdml:warnings>
     <tdml:errors>
       <tdml:error>Unparse Error</tdml:error>
       <tdml:error>Cannot convert 'a' from String type to Long</tdml:error>
@@ -372,4 +368,57 @@
     </tdml:errors>
   </tdml:parserTestCase>
 
+  <tdml:defineSchema name="nonexistantTypeCalcType_01.dfdl.xsd">
+    <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+    <dfdl:format ref="ex:GeneralFormat" lengthKind="implicit"
+      lengthUnits="bytes" encoding="UTF-8" separator="" initiator=""
+      terminator="" occursCountKind="parsed" ignoreCase="no"
+      textNumberRep="standard" representation="binary" 
+      dfdlx:parseUnparsePolicy="parseOnly"
+      />
+
+     <xs:element name="root" type="xs:int" dfdl:inputValueCalc="{ dfdlx:inputTypeCalcInt('tns:nonExistant', 0) }"/>
+
+  </tdml:defineSchema>
+
+  <tdml:parserTestCase name="nonexistantTypeCalcType_01"
+    root="root" model="nonexistantTypeCalcType_01.dfdl.xsd" description="Extensions - inputTypeCalc errors">
+    <tdml:document>
+    <tdml:documentPart type="byte"></tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>tns:nonExistant</tdml:error>
+      <tdml:error>does not exist</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <tdml:defineSchema name="nonexistantTypeCalcType_02.dfdl.xsd">
+    <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+    <dfdl:format ref="ex:GeneralFormat" lengthKind="implicit"
+      lengthUnits="bytes" encoding="UTF-8" separator="" initiator=""
+      terminator="" occursCountKind="parsed" ignoreCase="no"
+      textNumberRep="standard" representation="binary" 
+      dfdlx:parseUnparsePolicy="parseOnly"
+      />
+
+     <xs:simpleType name="nonExistantTypeCalc">
+       <xs:restriction base="xs:int"/>
+     </xs:simpleType>
+     <xs:element name="root" type="xs:int" dfdl:inputValueCalc="{ dfdlx:inputTypeCalcInt('tns:nonExistantTypeCalc', 0) }"/>
+
+  </tdml:defineSchema>
+
+  <tdml:parserTestCase name="nonexistantTypeCalcType_02"
+    root="root" model="nonexistantTypeCalcType_02.dfdl.xsd" description="Extensions - inputTypeCalc errors">
+    <tdml:document>
+    <tdml:documentPart type="byte"></tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>tns:nonExistant</tdml:error>
+      <tdml:error>does not have a repType</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
 </tdml:testSuite>
diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/extensions/TestInputTypeValueCalc.scala b/daffodil-test/src/test/scala/org/apache/daffodil/extensions/TestInputTypeValueCalc.scala
index f4b7262..11b1e60 100644
--- a/daffodil-test/src/test/scala/org/apache/daffodil/extensions/TestInputTypeValueCalc.scala
+++ b/daffodil-test/src/test/scala/org/apache/daffodil/extensions/TestInputTypeValueCalc.scala
@@ -96,5 +96,7 @@ class TestInputTypeValueCalc {
   @Test def test_nonexistant_reptype_01() { fnErrRunner.runOneTest("nonexistant_reptype_01") }
   @Test def test_nonexistantOutputTypeCalc_01() { fnErrRunner.runOneTest("nonexistantOutputTypeCalc_01") }
   @Test def test_nonexistantInputTypeCalc_01() { fnErrRunner.runOneTest("nonexistantInputTypeCalc_01") }
+  @Test def test_nonexistantTypeCalcType_01() { fnErrRunner.runOneTest("nonexistantTypeCalcType_01") }
+  @Test def test_nonexistantTypeCalcType_02() { fnErrRunner.runOneTest("nonexistantTypeCalcType_02") }
 
 }