You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by ji...@apache.org on 2023/12/09 00:46:53 UTC

(daffodil) branch main updated: Give codegen-c somewhat limited dfdl:assert support

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

jinterrante pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil.git


The following commit(s) were added to refs/heads/main by this push:
     new b4eaa46c0 Give codegen-c somewhat limited dfdl:assert support
b4eaa46c0 is described below

commit b4eaa46c0c6b17b9be7f94772a2e37ae817801f0
Author: John Interrante <in...@research.ge.com>
AuthorDate: Fri Dec 8 09:28:41 2023 -0500

    Give codegen-c somewhat limited dfdl:assert support
    
    On the basis that a little support is better than none, allow
    codegen-c to generate C code for a limited subset of dfdl:assert
    expressions.  The use case is to allow generated code to validate or
    reject messages using dfdl:assert expressions as well as ranges and
    enumerations.  The dfdl:assert support is limited to replacing
    "../foo/x" and "."  with the appropriate pointers and replacing DFDL
    named operators ("eq") with C-compatible operators ("==") using find
    and replace operators.  In the long term, a better approach would be
    to add methods to the compiled expression classes to generate C code
    directly from the compiled expressions with fuller understanding of
    what the parts of the expressions represent.
    
    DAFFODIL-2865
    
    errors.[ch]: Add new ERR_DFDL_ASSERT error code for assertion
    expressions which fail.
    
    validators.[ch]: Add new validate_dfdl_assert function for checking
    assertion expressions, making sure to handle both recoverable and
    non-recoverable assertion failures appropriately.
    
    DaffodilCCodeGenerator.scala: Call AssertStatementGenerateCode to
    generate C code for assertion expressions in both empty sequences and
    simple elements.
    
    AssertStatementGenerateCode.scala: Generate C code for two types of
    assertion expressions: "{ ../../foo/x eq 2 }" as used in empty
    sequences and "{ . eq 2 }" as used in simple elements.  Anything else
    likely won't produce valid C code at this time.
    
    CodeGeneratorState.scala: Make the cExpression function find and
    replace even more DFDL named operators with C-compatible operators.
    
    NestedUnion/generated_code.c: Regenerate to show new C code for
    assertion expressions in simple elements.
    
    data/nested.struct.{diag{1,2},err{1,2}}.dat: Add new data files which
    will deliberately fail assertion expression checks.
    
    infosets/nested.struct.{diag{1,2},err{1,2}}.dat.xml: Add new infoset
    files which will deliberately fail assertion expression checks.
    
    {data,infosets}/nested.union.bar.dat{,.xml}: Modify to pass assertion
    expression checks.
    
    nested.dfdl.xsd: Add both kinds of assertion expression checks (empty
    sequences and simple elements) to test the new codegen-c support.
    
    nested.tdml: Add new validation test cases to check both failure types
    of assertion expression checks.
    
    TestNested.scala: Call the new validation test cases.
---
 .../daffodil/codegen/c/files/libruntime/errors.c   |   1 +
 .../daffodil/codegen/c/files/libruntime/errors.h   |   1 +
 .../codegen/c/files/libruntime/validators.c        |  25 ++++++++
 .../codegen/c/files/libruntime/validators.h        |   5 ++
 .../codegen/c/DaffodilCCodeGenerator.scala         |  27 +++++++-
 .../c/generators/AssertStatementGenerateCode.scala |  56 +++++++++++++++++
 .../codegen/c/generators/CodeGeneratorState.scala  |  13 +++-
 .../src/test/examples/NestedUnion/generated_code.c |   8 +++
 .../codegen/c/data/nested.struct.diag1.dat         | Bin 0 -> 36 bytes
 .../codegen/c/data/nested.struct.diag2.dat         | Bin 0 -> 36 bytes
 .../daffodil/codegen/c/data/nested.struct.err1.dat | Bin 0 -> 36 bytes
 .../daffodil/codegen/c/data/nested.struct.err2.dat | Bin 0 -> 36 bytes
 .../daffodil/codegen/c/data/nested.union.bar.dat   | Bin 28 -> 28 bytes
 ...ion.bar.dat.xml => nested.struct.diag1.dat.xml} |  16 +++--
 ...ion.bar.dat.xml => nested.struct.diag2.dat.xml} |  16 +++--
 ...nion.bar.dat.xml => nested.struct.err1.dat.xml} |  16 +++--
 ...nion.bar.dat.xml => nested.struct.err2.dat.xml} |  16 +++--
 .../codegen/c/infosets/nested.union.bar.dat.xml    |   6 +-
 .../org/apache/daffodil/codegen/c/nested.dfdl.xsd  |  36 ++++++++++-
 .../org/apache/daffodil/codegen/c/nested.tdml      |  69 ++++++++++++++++++++-
 .../org/apache/daffodil/codegen/c/TestNested.scala |  10 ++-
 21 files changed, 283 insertions(+), 38 deletions(-)

diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.c b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.c
index 550e1a575..e076e8948 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.c
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.c
@@ -81,6 +81,7 @@ error_lookup(uint8_t code)
     static const ErrorLookup table[ERR__NUM_CODES] = {
         {ERR_ARRAY_BOUNDS, "%s count out of bounds\n", FIELD_S},
         {ERR_CHOICE_KEY, "no match between choice dispatch key %" PRId64 " and any branch key\n", FIELD_D64},
+        {ERR_DFDL_ASSERT, "Assertion expression failed: %s", FIELD_S},
         {ERR_HEXBINARY_ALLOC, "error allocating hexBinary memory -- %" PRId64 " bytes\n", FIELD_D64},
         {ERR_LEFTOVER_DATA, "Left over data, at least %i bit(s) remaining after end of parse\n", FIELD_C},
         {ERR_PARSE_BOOL, "error parsing binary value %" PRId64 " as either true or false\n", FIELD_D64},
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.h b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.h
index 62b52c51b..1fe7a26fd 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.h
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.h
@@ -32,6 +32,7 @@ enum ErrorCode
 {
     ERR_ARRAY_BOUNDS,
     ERR_CHOICE_KEY,
+    ERR_DFDL_ASSERT,
     ERR_HEXBINARY_ALLOC,
     ERR_LEFTOVER_DATA,
     ERR_PARSE_BOOL,
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/validators.c b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/validators.c
index 69f388741..32782b211 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/validators.c
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/validators.c
@@ -38,6 +38,31 @@ validate_array_bounds(const char *name, size_t count, size_t minOccurs, size_t m
     }
 }
 
+// Validate element or sequence's dfdl:assert expression returns true
+
+void
+validate_dfdl_assert(bool assertion, const char *expression, bool recoverable, ParserOrUnparserState *pu)
+{
+    // The boolean expression that determines if the assertion is true
+    // is evaluated in generated code and the result passed here, as
+    // well as failure_type which determines whether to issue a diagnostic
+    // which is printed later or an error which terminates processing now.
+    if (!assertion && recoverable)
+    {
+        Diagnostics *diagnostics = get_diagnostics();
+        const Error error = {ERR_DFDL_ASSERT, {.s = expression}};
+
+        add_diagnostic(diagnostics, &error);
+        pu->diagnostics = diagnostics;
+    }
+    else if (!assertion)
+    {
+        static Error error = {ERR_DFDL_ASSERT, {0}};
+        error.arg.s = expression;
+        pu->error = &error;
+    }
+}
+
 // Validate element's value is same as its fixed attribute
 
 void
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/validators.h b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/validators.h
index fb9cca8d6..27133957c 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/validators.h
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/validators.h
@@ -31,6 +31,11 @@
 extern void validate_array_bounds(const char *name, size_t count, size_t minOccurs, size_t maxOccurs,
                                   ParserOrUnparserState *pu);
 
+// Validate element or sequence's dfdl:assert expression returns true
+
+extern void validate_dfdl_assert(bool assertion, const char *expression, bool recoverable,
+                                 ParserOrUnparserState *pu);
+
 // Validate element's value is same as its fixed attribute
 
 extern void validate_fixed_attribute(bool same, const char *element, ParserOrUnparserState *pu);
diff --git a/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/DaffodilCCodeGenerator.scala b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/DaffodilCCodeGenerator.scala
index 7bedd2fa5..b8dc511d8 100644
--- a/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/DaffodilCCodeGenerator.scala
+++ b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/DaffodilCCodeGenerator.scala
@@ -25,6 +25,7 @@ import scala.collection.JavaConverters._
 import scala.util.Properties.isWin
 
 import org.apache.daffodil.codegen.c.generators.AlignmentFillCodeGenerator
+import org.apache.daffodil.codegen.c.generators.AssertStatementGenerateCode
 import org.apache.daffodil.codegen.c.generators.BinaryBooleanCodeGenerator
 import org.apache.daffodil.codegen.c.generators.BinaryFloatCodeGenerator
 import org.apache.daffodil.codegen.c.generators.BinaryIntegerKnownLengthCodeGenerator
@@ -60,6 +61,8 @@ import org.apache.daffodil.core.grammar.primitives.SpecifiedLengthExplicit
 import org.apache.daffodil.core.grammar.primitives.SpecifiedLengthImplicit
 import org.apache.daffodil.lib.api.Diagnostic
 import org.apache.daffodil.lib.api.WarnID
+import org.apache.daffodil.lib.schema.annotation.props.gen.FailureType
+import org.apache.daffodil.lib.schema.annotation.props.gen.TestKind
 import org.apache.daffodil.lib.util.Misc
 import org.apache.daffodil.runtime1.api.DFDL
 import org.apache.daffodil.runtime1.dsom.SchemaDefinitionError
@@ -247,6 +250,7 @@ class DaffodilCCodeGenerator(root: Root) extends DFDL.CodeGenerator {
  */
 object DaffodilCCodeGenerator
   extends AlignmentFillCodeGenerator
+  with AssertStatementGenerateCode
   with BinaryBooleanCodeGenerator
   with BinaryIntegerKnownLengthCodeGenerator
   with BinaryFloatCodeGenerator
@@ -263,7 +267,13 @@ object DaffodilCCodeGenerator
       case g: Gram if g.isEmpty => noop(g)
       // Handle non-empty grams
       case g: AlignmentFill => alignmentFillGenerateCode(g, cgState)
-      case g: AssertBooleanPrim => noop(g)
+      case g: AssertBooleanPrim =>
+        assertStatementGenerateCode(
+          g.name,
+          g.exprText,
+          g.stmt.failureType == FailureType.RecoverableError,
+          cgState,
+        )
       case g: BinaryBoolean => binaryBooleanGenerateCode(g.e, cgState)
       case g: BinaryDouble => binaryFloatGenerateCode(g.e, lengthInBits = 64, cgState)
       case g: BinaryFloat => binaryFloatGenerateCode(g.e, lengthInBits = 32, cgState)
@@ -306,6 +316,21 @@ object DaffodilCCodeGenerator
   private def elementCombinator(g: ElementCombinator, cgState: CodeGeneratorState): Unit = {
     cgState.pushElement(g.context)
     DaffodilCCodeGenerator.generateCode(g.subComb, cgState)
+    // Also generate code for the element's assert statements
+    g.context.assertStatements.foreach { assert =>
+      assert.testKind match {
+        case TestKind.Pattern =>
+          g.SDW(
+            WarnID.IgnoreDFDLProperty,
+            "Code generation not supported for dfdl:assert pattern stmts",
+          )
+        case TestKind.Expression =>
+          val name = g.name
+          val exprText = assert.testTxt
+          val recoverable = assert.failureType == FailureType.RecoverableError
+          assertStatementGenerateCode(name, exprText, recoverable, cgState)
+      }
+    }
     cgState.popElement(g.context)
   }
 
diff --git a/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/AssertStatementGenerateCode.scala b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/AssertStatementGenerateCode.scala
new file mode 100644
index 000000000..1fa5b4399
--- /dev/null
+++ b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/AssertStatementGenerateCode.scala
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.daffodil.codegen.c.generators
+
+// Generate C code for two types of assertion expressions:
+//
+//   { ../../foo/x eq 2 } as used in empty sequences
+//   { . eq 2 } as used in simple elements
+//
+// Anything else likely won't produce valid C code at this time.
+
+trait AssertStatementGenerateCode {
+
+  def assertStatementGenerateCode(
+    name: String,
+    exprText: String,
+    recoverable: Boolean,
+    cgState: CodeGeneratorState,
+  ): Unit = {
+    val indent = if (cgState.hasChoice) INDENT else NO_INDENT
+    val exprWOBraces = exprText.stripPrefix("{").stripSuffix("}").trim()
+    // Handle assertion expressions with "../" as used in empty sequences
+    val modifiedExpr =
+      if (exprWOBraces.startsWith("../"))
+        s"../$exprWOBraces"
+      else
+        exprWOBraces
+    // Handle assertion expressions with "." as used in simple elements
+    val cExpr = cgState.cExpression(modifiedExpr).replaceFirst("^\\. ", s"instance->$name ")
+
+    val initERDStatement = ""
+    val parseStatement =
+      s"""$indent    validate_dfdl_assert($cExpr, "$exprText", $recoverable, &pstate->pu);
+         |$indent    if (pstate->pu.error) return;""".stripMargin
+    val unparseStatement =
+      s"""$indent    validate_dfdl_assert($cExpr, "$exprText", $recoverable, &ustate->pu);
+         |$indent    if (ustate->pu.error) return;""".stripMargin
+    cgState.addSimpleTypeStatements(initERDStatement, parseStatement, unparseStatement)
+  }
+
+}
diff --git a/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/CodeGeneratorState.scala b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/CodeGeneratorState.scala
index 70ac0ca21..743a96786 100644
--- a/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/CodeGeneratorState.scala
+++ b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/CodeGeneratorState.scala
@@ -445,15 +445,24 @@ class CodeGeneratorState(private val root: ElementBase) {
       },
     )
     // Match each named operator and replace it with a C operator
-    val operator = """(\bidiv\b|\bdiv\b|\bmod\b)""".r
+    val operator =
+      """(\band\b|\bdiv\b|\beq\b|\bge\b|\bgt\b|\bidiv\b|\ble\b|\blt\b|\bmod\b|\bne\b|\bor\b)""".r
     val exprWithOperators = operator.replaceAllIn(
       exprWithFields,
       m => {
         val operatorName = m.group(1)
         val cOperatorSymbol = operatorName match {
-          case "idiv" => "/"
+          case "and" => "&&"
           case "div" => "/"
+          case "eq" => "=="
+          case "ge" => ">="
+          case "gt" => ">"
+          case "idiv" => "/"
+          case "le" => "<="
+          case "lt" => "<"
           case "mod" => "%"
+          case "ne" => "!="
+          case "or" => "||"
         }
         cOperatorSymbol
       },
diff --git a/daffodil-codegen-c/src/test/examples/NestedUnion/generated_code.c b/daffodil-codegen-c/src/test/examples/NestedUnion/generated_code.c
index 3633e1696..589d2be04 100644
--- a/daffodil-codegen-c/src/test/examples/NestedUnion/generated_code.c
+++ b/daffodil-codegen-c/src/test/examples/NestedUnion/generated_code.c
@@ -236,6 +236,8 @@ foo_data_NestedUnionType__parseSelf(foo_data_NestedUnionType_ *instance, PState
 {
     parse_be_int32(&instance->a, 32, pstate);
     if (pstate->pu.error) return;
+    validate_dfdl_assert(instance->a == 1, "{ . eq 1 }", true, &pstate->pu);
+    if (pstate->pu.error) return;
     parse_be_int32(&instance->b, 32, pstate);
     if (pstate->pu.error) return;
     parse_be_int32(&instance->c, 32, pstate);
@@ -247,6 +249,8 @@ foo_data_NestedUnionType__unparseSelf(const foo_data_NestedUnionType_ *instance,
 {
     unparse_be_int32(instance->a, 32, ustate);
     if (ustate->pu.error) return;
+    validate_dfdl_assert(instance->a == 1, "{ . eq 1 }", true, &ustate->pu);
+    if (ustate->pu.error) return;
     unparse_be_int32(instance->b, 32, ustate);
     if (ustate->pu.error) return;
     unparse_be_int32(instance->c, 32, ustate);
@@ -267,6 +271,8 @@ bar_data_NestedUnionType__parseSelf(bar_data_NestedUnionType_ *instance, PState
     if (pstate->pu.error) return;
     parse_be_double(&instance->y, 64, pstate);
     if (pstate->pu.error) return;
+    validate_dfdl_assert(instance->y == 5.5, "{ . eq 5.5 }", false, &pstate->pu);
+    if (pstate->pu.error) return;
     parse_be_double(&instance->z, 64, pstate);
     if (pstate->pu.error) return;
 }
@@ -278,6 +284,8 @@ bar_data_NestedUnionType__unparseSelf(const bar_data_NestedUnionType_ *instance,
     if (ustate->pu.error) return;
     unparse_be_double(instance->y, 64, ustate);
     if (ustate->pu.error) return;
+    validate_dfdl_assert(instance->y == 5.5, "{ . eq 5.5 }", false, &ustate->pu);
+    if (ustate->pu.error) return;
     unparse_be_double(instance->z, 64, ustate);
     if (ustate->pu.error) return;
 }
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.struct.diag1.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.struct.diag1.dat
new file mode 100644
index 000000000..f821f075f
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.struct.diag1.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.struct.diag2.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.struct.diag2.dat
new file mode 100644
index 000000000..3cd1e8543
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.struct.diag2.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.struct.err1.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.struct.err1.dat
new file mode 100644
index 000000000..c345a9f69
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.struct.err1.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.struct.err2.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.struct.err2.dat
new file mode 100644
index 000000000..07f3de48c
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.struct.err2.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.union.bar.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.union.bar.dat
index a8d472cd4..b1d7dcb15 100644
Binary files a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.union.bar.dat and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/nested.union.bar.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.struct.diag1.dat.xml
similarity index 82%
copy from daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml
copy to daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.struct.diag1.dat.xml
index 6688d57f6..87f86b0b0 100644
--- a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.struct.diag1.dat.xml
@@ -16,13 +16,17 @@
   limitations under the License.
 -->
 
-<idl:NestedUnion xmlns:idl="urn:idl:1.0">
-  <tag>4</tag>
+<idl:NestedStruct xmlns:idl="urn:idl:1.0">
   <data>
+    <foo>
+      <a>0</a>
+      <b>2</b>
+      <c>3</c>
+    </foo>
     <bar>
-      <x>1.5</x>
-      <y>2.5</y>
-      <z>3.5</z>
+      <x>4.5</x>
+      <y>5.5</y>
+      <z>6.5</z>
     </bar>
   </data>
-</idl:NestedUnion>
+</idl:NestedStruct>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.struct.diag2.dat.xml
similarity index 82%
copy from daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml
copy to daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.struct.diag2.dat.xml
index 6688d57f6..aca4664c5 100644
--- a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.struct.diag2.dat.xml
@@ -16,13 +16,17 @@
   limitations under the License.
 -->
 
-<idl:NestedUnion xmlns:idl="urn:idl:1.0">
-  <tag>4</tag>
+<idl:NestedStruct xmlns:idl="urn:idl:1.0">
   <data>
+    <foo>
+      <a>1</a>
+      <b>0</b>
+      <c>3</c>
+    </foo>
     <bar>
-      <x>1.5</x>
-      <y>2.5</y>
-      <z>3.5</z>
+      <x>4.5</x>
+      <y>5.5</y>
+      <z>6.5</z>
     </bar>
   </data>
-</idl:NestedUnion>
+</idl:NestedStruct>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.struct.err1.dat.xml
similarity index 82%
copy from daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml
copy to daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.struct.err1.dat.xml
index 6688d57f6..c34397a41 100644
--- a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.struct.err1.dat.xml
@@ -16,13 +16,17 @@
   limitations under the License.
 -->
 
-<idl:NestedUnion xmlns:idl="urn:idl:1.0">
-  <tag>4</tag>
+<idl:NestedStruct xmlns:idl="urn:idl:1.0">
   <data>
+    <foo>
+      <a>1</a>
+      <b>2</b>
+      <c>3</c>
+    </foo>
     <bar>
-      <x>1.5</x>
-      <y>2.5</y>
-      <z>3.5</z>
+      <x>0.5</x>
+      <y>5.5</y>
+      <z>6.5</z>
     </bar>
   </data>
-</idl:NestedUnion>
+</idl:NestedStruct>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.struct.err2.dat.xml
similarity index 82%
copy from daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml
copy to daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.struct.err2.dat.xml
index 6688d57f6..684b74aac 100644
--- a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.struct.err2.dat.xml
@@ -16,13 +16,17 @@
   limitations under the License.
 -->
 
-<idl:NestedUnion xmlns:idl="urn:idl:1.0">
-  <tag>4</tag>
+<idl:NestedStruct xmlns:idl="urn:idl:1.0">
   <data>
+    <foo>
+      <a>1</a>
+      <b>2</b>
+      <c>3</c>
+    </foo>
     <bar>
-      <x>1.5</x>
-      <y>2.5</y>
-      <z>3.5</z>
+      <x>4.5</x>
+      <y>0.5</y>
+      <z>6.5</z>
     </bar>
   </data>
-</idl:NestedUnion>
+</idl:NestedStruct>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml
index 6688d57f6..0ae9223d4 100644
--- a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/nested.union.bar.dat.xml
@@ -20,9 +20,9 @@
   <tag>4</tag>
   <data>
     <bar>
-      <x>1.5</x>
-      <y>2.5</y>
-      <z>3.5</z>
+      <x>4.5</x>
+      <y>5.5</y>
+      <z>6.5</z>
     </bar>
   </data>
 </idl:NestedUnion>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/nested.dfdl.xsd b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/nested.dfdl.xsd
index a5536ae37..c37f07a61 100644
--- a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/nested.dfdl.xsd
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/nested.dfdl.xsd
@@ -44,7 +44,15 @@
 
   <xs:complexType name="FooType">
     <xs:sequence>
-      <xs:element name="a" type="idl:int32"/>
+      <xs:element name="a" type="idl:int32">
+        <!-- Validate field a; recoverable error if fails -->
+        <xs:annotation>
+          <xs:appinfo source="http://www.ogf.org/dfdl/">
+            <dfdl:assert test="{ . eq 1 }"
+                         failureType="recoverableError"/>
+          </xs:appinfo>
+        </xs:annotation>
+      </xs:element>
       <xs:element name="b" type="idl:int32"/>
       <xs:element name="c" type="idl:int32"/>
     </xs:sequence>
@@ -53,7 +61,14 @@
   <xs:complexType name="BarType">
     <xs:sequence>
       <xs:element name="x" type="idl:double"/>
-      <xs:element name="y" type="idl:double"/>
+      <xs:element name="y" type="idl:double">
+        <!-- Validate field y; processing error if fails -->
+        <xs:annotation>
+          <xs:appinfo source="http://www.ogf.org/dfdl/">
+            <dfdl:assert test="{ . eq 5.5 }"/>
+          </xs:appinfo>
+        </xs:annotation>
+      </xs:element>
       <xs:element name="z" type="idl:double"/>
     </xs:sequence>
   </xs:complexType>
@@ -64,7 +79,24 @@
         <xs:complexType>
           <xs:sequence>
             <xs:element name="foo" type="idl:FooType"/>
+            <!-- Validate field b in foo; recoverable error if fails -->
+            <xs:sequence>
+              <xs:annotation>
+                <xs:appinfo source="http://www.ogf.org/dfdl/">
+                  <dfdl:assert test="{ ../data/foo/b eq 2 }"
+                               failureType="recoverableError"/>
+                </xs:appinfo>
+              </xs:annotation>
+            </xs:sequence>
             <xs:element name="bar" type="idl:BarType"/>
+            <!-- Validate field x in bar; processing error if fails -->
+            <xs:sequence>
+              <xs:annotation>
+                <xs:appinfo source="http://www.ogf.org/dfdl/">
+                  <dfdl:assert test="{ ../data/bar/x eq 4.5 }"/>
+                </xs:appinfo>
+              </xs:annotation>
+            </xs:sequence>
           </xs:sequence>
         </xs:complexType>
       </xs:element>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/nested.tdml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/nested.tdml
index bb8032915..3c1124c01 100644
--- a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/nested.tdml
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/nested.tdml
@@ -28,9 +28,18 @@
 
       daffodil test -i -I daffodil nested.tdml
       daffodil test -i -I daffodilC nested.tdml
+
+      Or you can debug specific steps in isolation:
+
+      daffodil parse -s nested.dfdl.xsd -o infosets/nested.struct.dat.xml data/nested.struct.dat
+      daffodil unparse -s nested.dfdl.xsd -o data/nested.struct.dat infosets/nested.struct.dat.xml
+
+      daffodil generate c -s nested.dfdl.xsd && make -C c
+      c/daffodil parse -o c/nested.struct.dat.xml data/nested.struct.dat
+      c/daffodil unparse -o c/nested.struct.dat infosets/nested.struct.dat.xml
   -->
 
-  <tdml:parserTestCase model="nested.dfdl.xsd" name="nested_struct">
+  <tdml:parserTestCase model="nested.dfdl.xsd" name="nested.struct">
     <tdml:document>
       <tdml:documentPart type="file">data/nested.struct.dat</tdml:documentPart>
     </tdml:document>
@@ -39,7 +48,61 @@
     </tdml:infoset>
   </tdml:parserTestCase>
 
-  <tdml:parserTestCase model="nested.dfdl.xsd" name="nested_union_bar">
+  <tdml:parserTestCase model="nested.dfdl.xsd" name="nested.struct.diag1">
+    <tdml:document>
+      <tdml:documentPart type="file">data/nested.struct.diag1.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/nested.struct.diag1.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:validationErrors>
+      <tdml:error>expression</tdml:error>
+      <tdml:error>failed</tdml:error>
+      <tdml:error>. eq 1</tdml:error>
+    </tdml:validationErrors>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase model="nested.dfdl.xsd" name="nested.struct.diag2">
+    <tdml:document>
+      <tdml:documentPart type="file">data/nested.struct.diag2.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/nested.struct.diag2.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:validationErrors>
+      <tdml:error>expression</tdml:error>
+      <tdml:error>failed</tdml:error>
+      <tdml:error>b eq 2</tdml:error>
+    </tdml:validationErrors>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase model="nested.dfdl.xsd"
+                       name="nested.struct.err1"
+                       root="NestedStruct">
+    <tdml:document>
+      <tdml:documentPart type="file">data/nested.struct.err1.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>expression</tdml:error>
+      <tdml:error>failed</tdml:error>
+      <tdml:error>x eq 4.5</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase model="nested.dfdl.xsd"
+                       name="nested.struct.err2"
+                       root="NestedStruct">
+    <tdml:document>
+      <tdml:documentPart type="file">data/nested.struct.err2.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>expression</tdml:error>
+      <tdml:error>failed</tdml:error>
+      <tdml:error>. eq 5.5</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase model="nested.dfdl.xsd" name="nested.union.bar">
     <tdml:document>
       <tdml:documentPart type="file">data/nested.union.bar.dat</tdml:documentPart>
     </tdml:document>
@@ -48,7 +111,7 @@
     </tdml:infoset>
   </tdml:parserTestCase>
 
-  <tdml:parserTestCase model="nested.dfdl.xsd" name="nested_union_foo">
+  <tdml:parserTestCase model="nested.dfdl.xsd" name="nested.union.foo">
     <tdml:document>
       <tdml:documentPart type="file">data/nested.union.foo.dat</tdml:documentPart>
     </tdml:document>
diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/codegen/c/TestNested.scala b/daffodil-test/src/test/scala/org/apache/daffodil/codegen/c/TestNested.scala
index 2744c06d0..1177449ff 100644
--- a/daffodil-test/src/test/scala/org/apache/daffodil/codegen/c/TestNested.scala
+++ b/daffodil-test/src/test/scala/org/apache/daffodil/codegen/c/TestNested.scala
@@ -33,7 +33,11 @@ object TestNested {
 class TestNested {
   import TestNested._
 
-  @Test def test_nested_struct(): Unit = { runner.runOneTest("nested_struct") }
-  @Test def test_nested_union_bar(): Unit = { runner.runOneTest("nested_union_bar") }
-  @Test def test_nested_union_foo(): Unit = { runner.runOneTest("nested_union_foo") }
+  @Test def test_nested_struct(): Unit = { runner.runOneTest("nested.struct") }
+  @Test def test_nested_struct_diag1(): Unit = { runner.runOneTest("nested.struct.diag1") }
+  @Test def test_nested_struct_diag2(): Unit = { runner.runOneTest("nested.struct.diag2") }
+  @Test def test_nested_struct_err1(): Unit = { runner.runOneTest("nested.struct.err1") }
+  @Test def test_nested_struct_err2(): Unit = { runner.runOneTest("nested.struct.err2") }
+  @Test def test_nested_union_bar(): Unit = { runner.runOneTest("nested.union.bar") }
+  @Test def test_nested_union_foo(): Unit = { runner.runOneTest("nested.union.foo") }
 }