data tree FEATURE lyd_new_implicit* functions

Separate functionality that was included
only in validation before.
diff --git a/src/parser_data.h b/src/parser_data.h
index f4ccf73..de9f992 100644
--- a/src/parser_data.h
+++ b/src/parser_data.h
@@ -49,7 +49,7 @@
  * - when statements on existing nodes are evaluated, if not satisfied, a validation error is raised,
  * - if-feature statements are evaluated,
  * - invalid multiple data instances/data from several cases cause a validation error,
- * - default values are added.
+ * - implicit nodes (NP containers and default values) are added.
  * @{
  */
 /* note: keep the lower 16bits free for use by LYD_VALIDATE_ flags. They are not supposed to be combined together,
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
index 2f6f8df..65fdb19 100644
--- a/src/parser_lyb.c
+++ b/src/parser_lyb.c
@@ -823,8 +823,9 @@
             LY_CHECK_GOTO(ret, cleanup);
 
             /* add any missing default children */
-            ret = lyd_validate_defaults_r(node, lyd_node_children_p(node), NULL, NULL, &lybctx->unres_node_type,
-                                          &lybctx->when_check, lybctx->validate_options, NULL);
+            ret = lyd_new_implicit_r(node, lyd_node_children_p(node), NULL, NULL, &lybctx->unres_node_type,
+                                     &lybctx->when_check, (lybctx->validate_options & LYD_VALIDATE_NO_STATE)
+                                        ? LYD_IMPLICIT_NO_STATE : 0, NULL);
             LY_CHECK_GOTO(ret, cleanup);
         }
 
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 0254a81..bc8b7e7 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -592,8 +592,9 @@
                 LY_CHECK_GOTO(ret, cleanup);
 
                 /* add any missing default children */
-                ret = lyd_validate_defaults_r(cur, lyd_node_children_p(cur), NULL, NULL, &lydctx->unres_node_type,
-                                              &lydctx->when_check, lydctx->options, NULL);
+                ret = lyd_new_implicit_r(cur, lyd_node_children_p(cur), NULL, NULL, &lydctx->unres_node_type,
+                                         &lydctx->when_check, (lydctx->options & LYD_VALIDATE_NO_STATE)
+                                            ? LYD_IMPLICIT_NO_STATE : 0, NULL);
                 LY_CHECK_GOTO(ret, cleanup);
             }
 
@@ -717,8 +718,8 @@
             LY_CHECK_GOTO(ret = lyd_validate_new(first2, NULL, mod, NULL), cleanup);
 
             /* add all top-level defaults for this module */
-            ret = lyd_validate_defaults_r(NULL, first2, NULL, mod, &lydctx.unres_node_type, &lydctx.when_check,
-                                          validate_options, NULL);
+            ret = lyd_new_implicit_r(NULL, first2, NULL, mod, &lydctx.unres_node_type, &lydctx.when_check,
+                                     validate_options & LYD_VALIDATE_NO_STATE ? LYD_IMPLICIT_NO_STATE : 0, NULL);
             LY_CHECK_GOTO(ret, cleanup);
 
             /* finish incompletely validated terminal values/attributes and when conditions */
diff --git a/src/tree_data.c b/src/tree_data.c
index f9887dd..df4ca43 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -32,6 +32,7 @@
 #include "config.h"
 #include "context.h"
 #include "dict.h"
+#include "diff.h"
 #include "hash_table.h"
 #include "log.h"
 #include "parser_data.h"
@@ -46,6 +47,7 @@
 #include "tree_data_internal.h"
 #include "tree_schema.h"
 #include "tree_schema_internal.h"
+#include "validation.h"
 #include "xml.h"
 #include "xpath.h"
 
@@ -1323,6 +1325,221 @@
     return ret;
 }
 
+LY_ERR
+lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
+                   const struct lys_module *mod, struct ly_set *node_types, struct ly_set *node_when, int impl_opts,
+                   struct lyd_node **diff)
+{
+    LY_ERR ret;
+    const struct lysc_node *iter = NULL;
+    struct lyd_node *node;
+    struct lyd_value **dflts;
+    LY_ARRAY_COUNT_TYPE u;
+
+    assert(first && (parent || sparent || mod));
+
+    if (!sparent && parent) {
+        sparent = parent->schema;
+    }
+
+    while ((iter = lys_getnext(iter, sparent, mod ? mod->compiled : NULL, LYS_GETNEXT_WITHCHOICE))) {
+        if ((impl_opts & LYD_IMPLICIT_NO_STATE) && (iter->flags & LYS_CONFIG_R)) {
+            continue;
+        }
+
+        switch (iter->nodetype) {
+        case LYS_CHOICE:
+            if (((struct lysc_node_choice *)iter)->dflt && !lys_getnext_data(NULL, *first, NULL, iter, NULL)) {
+                /* create default case data */
+                LY_CHECK_RET(lyd_new_implicit_r(parent, first, (struct lysc_node *)((struct lysc_node_choice *)iter)->dflt,
+                                                NULL, node_types, node_when, impl_opts, diff));
+            }
+            break;
+        case LYS_CONTAINER:
+            if (!(iter->flags & LYS_PRESENCE) && lyd_find_sibling_val(*first, iter, NULL, 0, NULL)) {
+                /* create default NP container */
+                LY_CHECK_RET(lyd_create_inner(iter, &node));
+                node->flags = LYD_DEFAULT;
+                lyd_insert_node(parent, first, node);
+
+                /* cannot be a NP container with when */
+                assert(!iter->when);
+
+                /* create any default children */
+                LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_children_p(node), NULL, NULL, node_types, node_when,
+                                                impl_opts, diff));
+            }
+            break;
+        case LYS_LEAF:
+            if (!(impl_opts & LYD_IMPLICIT_NO_DEFAULTS) && ((struct lysc_node_leaf *)iter)->dflt
+                    && lyd_find_sibling_val(*first, iter, NULL, 0, NULL)) {
+                /* create default leaf */
+                ret = lyd_create_term2(iter, ((struct lysc_node_leaf *)iter)->dflt, &node);
+                if (ret == LY_EINCOMPLETE) {
+                    if (node_types) {
+                        /* remember to resolve type */
+                        ly_set_add(node_types, node, LY_SET_OPT_USEASLIST);
+                    }
+                } else if (ret) {
+                    return ret;
+                }
+                node->flags = LYD_DEFAULT;
+                lyd_insert_node(parent, first, node);
+
+                if (iter->when && node_when) {
+                    /* remember to resolve when */
+                    ly_set_add(node_when, node, LY_SET_OPT_USEASLIST);
+                }
+                if (diff) {
+                    /* add into diff */
+                    LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
+                }
+            }
+            break;
+        case LYS_LEAFLIST:
+            if (!(impl_opts & LYD_IMPLICIT_NO_DEFAULTS) && ((struct lysc_node_leaflist *)iter)->dflts
+                    && lyd_find_sibling_val(*first, iter, NULL, 0, NULL)) {
+                /* create all default leaf-lists */
+                dflts = ((struct lysc_node_leaflist *)iter)->dflts;
+                LY_ARRAY_FOR(dflts, u) {
+                    ret = lyd_create_term2(iter, dflts[u], &node);
+                    if (ret == LY_EINCOMPLETE) {
+                        if (node_types) {
+                            /* remember to resolve type */
+                            ly_set_add(node_types, node, LY_SET_OPT_USEASLIST);
+                        }
+                    } else if (ret) {
+                        return ret;
+                    }
+                    node->flags = LYD_DEFAULT;
+                    lyd_insert_node(parent, first, node);
+
+                    if (iter->when && node_when) {
+                        /* remember to resolve when */
+                        ly_set_add(node_when, node, LY_SET_OPT_USEASLIST);
+                    }
+                    if (diff) {
+                        /* add into diff */
+                        LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
+                    }
+                }
+            }
+            break;
+        default:
+            /* without defaults */
+            break;
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+API LY_ERR
+lyd_new_implicit_tree(struct lyd_node *tree, int implicit_options, struct lyd_node **diff)
+{
+    struct lyd_node *next, *node;
+    LY_ERR ret = LY_SUCCESS;
+
+    LY_CHECK_ARG_RET(NULL, tree, LY_EINVAL);
+    if (diff) {
+        *diff = NULL;
+    }
+
+    LYD_TREE_DFS_BEGIN(tree, next, node) {
+        /* skip added default nodes */
+        if (((node->flags & (LYD_DEFAULT | LYD_NEW)) != (LYD_DEFAULT | LYD_NEW))
+                && (node->schema->nodetype & LYD_NODE_INNER)) {
+            LY_CHECK_GOTO(ret = lyd_new_implicit_r(node, lyd_node_children_p((struct lyd_node *)node), NULL, NULL, NULL,
+                                                   NULL, implicit_options, diff), cleanup);
+        }
+
+        LYD_TREE_DFS_END(tree, next, node);
+    }
+
+cleanup:
+    if (ret && diff) {
+        lyd_free_all(*diff);
+        *diff = NULL;
+    }
+    return ret;
+}
+
+API LY_ERR
+lyd_new_implicit_all(struct lyd_node **tree, const struct ly_ctx *ctx, int implicit_options, struct lyd_node **diff)
+{
+    const struct lys_module *mod;
+    struct lyd_node *d = NULL;
+    uint32_t i = 0;
+    LY_ERR ret = LY_SUCCESS;
+
+    LY_CHECK_ARG_RET(ctx, tree, *tree || ctx, LY_EINVAL);
+    if (diff) {
+        *diff = NULL;
+    }
+    if (!ctx) {
+        ctx = LYD_NODE_CTX(*tree);
+    }
+
+    /* add nodes for each module one-by-one */
+    while ((mod = ly_ctx_get_module_iter(ctx, &i))) {
+        if (!mod->implemented) {
+            continue;
+        }
+
+        LY_CHECK_GOTO(ret = lyd_new_implicit_module(tree, mod, implicit_options, diff ? &d : NULL), cleanup);
+        if (d) {
+            /* merge into one diff */
+            lyd_insert_sibling(*diff, d, diff);
+
+            d = NULL;
+        }
+    }
+
+cleanup:
+    if (ret && diff) {
+        lyd_free_all(*diff);
+        *diff = NULL;
+    }
+    return ret;
+}
+
+API LY_ERR
+lyd_new_implicit_module(struct lyd_node **tree, const struct lys_module *module, int implicit_options, struct lyd_node **diff)
+{
+    struct lyd_node *root, *d = NULL;
+    LY_ERR ret = LY_SUCCESS;
+
+    LY_CHECK_ARG_RET(NULL, tree, module, LY_EINVAL);
+    if (diff) {
+        *diff = NULL;
+    }
+
+    /* add all top-level defaults for this module */
+    LY_CHECK_GOTO(ret = lyd_new_implicit_r(NULL, tree, NULL, module, NULL, NULL, implicit_options, diff), cleanup);
+
+    /* process nested nodes */
+    LY_LIST_FOR(*tree, root) {
+        /* skip added default nodes */
+        if ((root->flags & (LYD_DEFAULT | LYD_NEW)) != (LYD_DEFAULT | LYD_NEW)) {
+            LY_CHECK_GOTO(ret = lyd_new_implicit_tree(root, implicit_options, diff ? &d : NULL), cleanup);
+
+            if (d) {
+                /* merge into one diff */
+                lyd_insert_sibling(*diff, d, diff);
+
+                d = NULL;
+            }
+        }
+    }
+
+cleanup:
+    if (ret && diff) {
+        lyd_free_all(*diff);
+        *diff = NULL;
+    }
+    return ret;
+}
+
 struct lyd_node *
 lyd_insert_get_next_anchor(const struct lyd_node *first_sibling, const struct lyd_node *new_node)
 {
diff --git a/src/tree_data.h b/src/tree_data.h
index fd4787d..16087c6 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -642,6 +642,57 @@
                      LYD_ANYDATA_VALUETYPE value_type, int options, struct lyd_node **new_parent, struct lyd_node **new_node);
 
 /**
+ * @defgroup implicitoptions Implicit node creation options
+ * @ingroup datatree
+ *
+ * Various options to change lyd_new_implicit*() behavior.
+ *
+ * Default behavior:
+ * - both configuration and state missing implicit nodes are added.
+ * - all implicit node types are added (non-presence containers, default leaves, and default leaf-lists).
+ * @{
+ */
+
+#define LYD_IMPLICIT_NO_STATE 0x01      /**< Do not add any implicit state nodes. */
+#define LYD_IMPLICIT_NO_DEFAULTS 0x02   /**< Do not add any default nodes (leaves/leaf-lists), only non-presence
+                                             containers. */
+
+/** @} implicitoptions */
+
+/**
+ * @brief Add any missing implicit nodes into a data subtree.
+ *
+ * @param[in] tree Tree to add implicit nodes into.
+ * @param[in] implicit_options Options for implicit node creation, see @ref implicitoptions.
+ * @param[out] diff Optional diff with any created nodes.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_new_implicit_tree(struct lyd_node *tree, int implicit_options, struct lyd_node **diff);
+
+/**
+ * @brief Add any missing implicit nodes.
+ *
+ * @param[in,out] tree Tree to add implicit nodes into.
+ * @param[in] ctx libyang context, must be set only if @p tree is an empty tree.
+ * @param[in] implicit_options Options for implicit node creation, see @ref implicitoptions.
+ * @param[out] diff Optional diff with any created nodes.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_new_implicit_all(struct lyd_node **tree, const struct ly_ctx *ctx, int implicit_options, struct lyd_node **diff);
+
+/**
+ * @brief Add any missing implicit nodes of one module.
+ *
+ * @param[in,out] tree Tree to add implicit nodes into.
+ * @param[in] module Module whose implicit nodes to create.
+ * @param[in] implicit_options Options for implicit node creation, see @ref implicitoptions.
+ * @param[out] diff Optional diff with any created nodes.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_new_implicit_module(struct lyd_node **tree, const struct lys_module *module, int implicit_options,
+                               struct lyd_node **diff);
+
+/**
  * @brief Change the value of a term (leaf or leaf-list) node.
  *
  * Node changed this way is always considered explicitly set, meaning its default flag
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index 28de6a1..0e8a17b 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -29,6 +29,38 @@
 #include "tree_data_internal.h"
 #include "tree_schema.h"
 
+struct lyd_node *
+lys_getnext_data(const struct lyd_node *last, const struct lyd_node *sibling, const struct lysc_node **slast,
+                 const struct lysc_node *parent, const struct lysc_module *module)
+{
+    const struct lysc_node *siter = NULL;
+    struct lyd_node *match = NULL;
+
+    assert(parent || module);
+    assert(!last || (slast && *slast));
+
+    if (slast) {
+        siter = *slast;
+    }
+
+    if (last && last->next && (last->next->schema == siter)) {
+        /* return next data instance */
+        return last->next;
+    }
+
+    /* find next schema node data instance */
+    while ((siter = lys_getnext(siter, parent, module, 0))) {
+        if (!lyd_find_sibling_val(sibling, siter, NULL, 0, &match)) {
+            break;
+        }
+    }
+
+    if (slast) {
+        *slast = siter;
+    }
+    return match;
+}
+
 struct lyd_node **
 lyd_node_children_p(struct lyd_node *node)
 {
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index 166cd12..be5ceca 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -23,6 +23,7 @@
 #include <stddef.h>
 
 struct ly_path_predicate;
+struct lysc_module;
 
 /**
  * @brief Internal data parser flags.
@@ -76,6 +77,23 @@
 struct lyd_node **lyd_node_children_p(struct lyd_node *node);
 
 /**
+ * @brief Just like lys_getnext() but iterates over all data instances of the schema nodes.
+ *
+ * @param[in] last Last returned data node.
+ * @param[in] sibling Data node sibling to search in.
+ * @param[in,out] slast Schema last node, set to NULL for first call and do not change afterwards.
+ * May not be set if the function is used only for any suitable node existence check (such as the existence
+ * of any choice case data).
+ * @param[in] parent Schema parent of the iterated children nodes.
+ * @param[in] module Schema module of the iterated top-level nodes.
+ * @return Next matching data node,
+ * @return NULL if last data node was already returned.
+ */
+struct lyd_node *lys_getnext_data(const struct lyd_node *last, const struct lyd_node *sibling,
+                                  const struct lysc_node **slast, const struct lysc_node *parent,
+                                  const struct lysc_module *module);
+
+/**
  * @brief Create a term (leaf/leaf-list) node from a string value.
  *
  * Hash is calculated and new node flag is set.
@@ -173,6 +191,23 @@
                        const char *ns, struct lyd_node **node);
 
 /**
+ * @brief Check the existence and create any non-existing implicit siblings, recursively for the created nodes.
+ *
+ * @param[in] parent Parent of the potential default values, NULL for top-level siblings.
+ * @param[in,out] first First sibling.
+ * @param[in] sparent Schema parent of the siblings, NULL if schema of @p parent can be used.
+ * @param[in] mod Module of the default values, NULL for nested siblings.
+ * @param[in] node_types Optional set to add nodes with unresolved types into.
+ * @param[in] node_when Optional set to add nodes with "when" conditions into.
+ * @param[in] impl_opts Implicit options (@ref implicitoptions).
+ * @param[in,out] diff Validation diff.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
+                          const struct lys_module *mod, struct ly_set *node_types, struct ly_set *node_when,
+                          int impl_opts, struct lyd_node **diff);
+
+/**
  * @brief Find the next node, before which to insert the new node.
  *
  * @param[in] first_sibling First sibling of the nodes to consider.
diff --git a/src/validation.c b/src/validation.c
index da9455f..9f556f2 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -33,60 +33,7 @@
 #include "tree_schema_internal.h"
 #include "xpath.h"
 
-/**
- * @brief Just like lys_getnext() but iterates over all data instances of the schema nodes.
- *
- * @param[in] last Last returned data node.
- * @param[in] sibling Data node sibling to search in.
- * @param[in,out] slast Schema last node, set to NULL for first call and do not change afterwards.
- * May not be set if the function is used only for any suitable node existence check (such as the existence
- * of any choice case data).
- * @param[in] parent Schema parent of the iterated children nodes.
- * @param[in] module Schema module of the iterated top-level nodes.
- * @return Next matching data node,
- * @return NULL if last data node was already returned.
- */
-static struct lyd_node *
-lys_getnext_data(const struct lyd_node *last, const struct lyd_node *sibling, const struct lysc_node **slast,
-                 const struct lysc_node *parent, const struct lysc_module *module)
-{
-    const struct lysc_node *siter = NULL;
-    struct lyd_node *match = NULL;
-
-    assert(parent || module);
-    assert(!last || (slast && *slast));
-
-    if (slast) {
-        siter = *slast;
-    }
-
-    if (last && last->next && (last->next->schema == siter)) {
-        /* return next data instance */
-        return last->next;
-    }
-
-    /* find next schema node data instance */
-    while ((siter = lys_getnext(siter, parent, module, 0))) {
-        if (!lyd_find_sibling_val(sibling, siter, NULL, 0, &match)) {
-            break;
-        }
-    }
-
-    if (slast) {
-        *slast = siter;
-    }
-    return match;
-}
-
-/**
- * @brief Add new changes into validation diff. They are always merged.
- *
- * @param[in] node Node/subtree to add.
- * @param[in] op Operation of the change.
- * @param[in,out] diff Validation diff.
- * @return LY_ERR value.
- */
-static LY_ERR
+LY_ERR
 lyd_val_diff_add(const struct lyd_node *node, enum lyd_diff_op op, struct lyd_node **diff)
 {
     LY_ERR ret = LY_SUCCESS;
@@ -1065,109 +1012,6 @@
     return LY_SUCCESS;
 }
 
-LY_ERR
-lyd_validate_defaults_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
-                        const struct lys_module *mod, struct ly_set *node_types, struct ly_set *node_when, int val_opts,
-                        struct lyd_node **diff)
-{
-    LY_ERR ret;
-    const struct lysc_node *iter = NULL;
-    struct lyd_node *node;
-    struct lyd_value **dflts;
-    LY_ARRAY_COUNT_TYPE u;
-
-    assert(first && (parent || sparent || mod) && node_types && node_when);
-
-    if (!sparent && parent) {
-        sparent = parent->schema;
-    }
-
-    while ((iter = lys_getnext(iter, sparent, mod ? mod->compiled : NULL, LYS_GETNEXT_WITHCHOICE))) {
-        if ((val_opts & LYD_VALIDATE_NO_STATE) && (iter->flags & LYS_CONFIG_R)) {
-            continue;
-        }
-
-        switch (iter->nodetype) {
-        case LYS_CHOICE:
-            if (((struct lysc_node_choice *)iter)->dflt && !lys_getnext_data(NULL, *first, NULL, iter, NULL)) {
-                /* create default case data */
-                LY_CHECK_RET(lyd_validate_defaults_r(parent, first, (struct lysc_node *)((struct lysc_node_choice *)iter)->dflt,
-                                                     NULL, node_types, node_when, val_opts, diff));
-            }
-            break;
-        case LYS_CONTAINER:
-            if (!(iter->flags & LYS_PRESENCE) && lyd_find_sibling_val(*first, iter, NULL, 0, NULL)) {
-                /* create default NP container */
-                LY_CHECK_RET(lyd_create_inner(iter, &node));
-                node->flags = LYD_DEFAULT;
-                lyd_insert_node(parent, first, node);
-
-                /* cannot be a NP container with when */
-                assert(!iter->when);
-
-                /* create any default children */
-                LY_CHECK_RET(lyd_validate_defaults_r(node, lyd_node_children_p(node), NULL, NULL, node_types, node_when,
-                                                     val_opts, diff));
-            }
-            break;
-        case LYS_LEAF:
-            if (((struct lysc_node_leaf *)iter)->dflt && lyd_find_sibling_val(*first, iter, NULL, 0, NULL)) {
-                /* create default leaf */
-                ret = lyd_create_term2(iter, ((struct lysc_node_leaf *)iter)->dflt, &node);
-                if (ret == LY_EINCOMPLETE) {
-                    /* remember to resolve type */
-                    ly_set_add(node_types, node, LY_SET_OPT_USEASLIST);
-                } else if (ret) {
-                    return ret;
-                }
-                node->flags = LYD_DEFAULT;
-                lyd_insert_node(parent, first, node);
-
-                if (iter->when) {
-                    /* remember to resolve when */
-                    ly_set_add(node_when, node, LY_SET_OPT_USEASLIST);
-                }
-                if (diff) {
-                    /* add into diff */
-                    LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
-                }
-            }
-            break;
-        case LYS_LEAFLIST:
-            if (((struct lysc_node_leaflist *)iter)->dflts && lyd_find_sibling_val(*first, iter, NULL, 0, NULL)) {
-                /* create all default leaf-lists */
-                dflts = ((struct lysc_node_leaflist *)iter)->dflts;
-                LY_ARRAY_FOR(dflts, u) {
-                    ret = lyd_create_term2(iter, dflts[u], &node);
-                    if (ret == LY_EINCOMPLETE) {
-                        /* remember to resolve type */
-                        ly_set_add(node_types, node, LY_SET_OPT_USEASLIST);
-                    } else if (ret) {
-                        return ret;
-                    }
-                    node->flags = LYD_DEFAULT;
-                    lyd_insert_node(parent, first, node);
-
-                    if (iter->when) {
-                        /* remember to resolve when */
-                        ly_set_add(node_when, node, LY_SET_OPT_USEASLIST);
-                    }
-                    if (diff) {
-                        /* add into diff */
-                        LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
-                    }
-                }
-            }
-            break;
-        default:
-            /* without defaults */
-            break;
-        }
-    }
-
-    return LY_SUCCESS;
-}
-
 /**
  * @brief Validate the whole data subtree.
  *
@@ -1202,8 +1046,9 @@
                 LY_CHECK_RET(lyd_validate_new(lyd_node_children_p((struct lyd_node *)node), node->schema, NULL, diff));
 
                 /* add nested defaults */
-                LY_CHECK_RET(lyd_validate_defaults_r(node, lyd_node_children_p((struct lyd_node *)node), NULL, NULL, type_check,
-                                                     when_check, val_opts, diff));
+                LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_children_p((struct lyd_node *)node), NULL, NULL, type_check,
+                                                when_check, val_opts & LYD_VALIDATE_NO_STATE ? LYD_IMPLICIT_NO_STATE : 0,
+                                                diff));
             }
 
             if (!(node->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && node->schema->when) {
@@ -1266,7 +1111,8 @@
         LY_CHECK_GOTO(ret, cleanup);
 
         /* add all top-level defaults for this module */
-        ret = lyd_validate_defaults_r(NULL, first2, NULL, mod, &type_check, &when_check, val_opts, diff);
+        ret = lyd_new_implicit_r(NULL, first2, NULL, mod, &type_check, &when_check, val_opts & LYD_VALIDATE_NO_STATE
+                                 ? LYD_IMPLICIT_NO_STATE : 0, diff);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* process nested nodes */
diff --git a/src/validation.h b/src/validation.h
index 4f1c4c1..41a47a5 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -20,6 +20,18 @@
 #include "plugins_types.h"
 #include "tree_data.h"
 
+enum lyd_diff_op;
+
+/**
+ * @brief Add new changes into a diff. They are always merged.
+ *
+ * @param[in] node Node/subtree to add.
+ * @param[in] op Operation of the change.
+ * @param[in,out] diff Diff to update.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_val_diff_add(const struct lyd_node *node, enum lyd_diff_op op, struct lyd_node **diff);
+
 /**
  * @brief Finish validation of nodes and attributes. Specifically, when (is processed first) and type validation.
  *
@@ -65,21 +77,4 @@
 LY_ERR lyd_validate_final_r(struct lyd_node *first, const struct lysc_node *sparent, const struct lys_module *mod,
                             int val_opts, LYD_VALIDATE_OP op);
 
-/**
- * @brief Check the existence and create any non-existing default siblings, recursively for the created nodes.
- *
- * @param[in] parent Parent of the potential default values, NULL for top-level siblings.
- * @param[in,out] first First sibling.
- * @param[in] sparent Schema parent of the siblings, NULL if schema of @p parent can be used.
- * @param[in] mod Module of the default values, NULL for nested siblings.
- * @param[in] node_types Set to add nodes with unresolved types into.
- * @param[in] node_when Set to add nodes with "when" conditions into.
- * @param[in] val_opts Validation options (@ref datavalidationoptions).
- * @param[in,out] diff Validation diff.
- * @return LY_ERR value.
- */
-LY_ERR lyd_validate_defaults_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
-                               const struct lys_module *mod, struct ly_set *node_types, struct ly_set *node_when,
-                               int val_opts, struct lyd_node **diff);
-
 #endif /* LY_VALIDATION_H_ */