schema tree REFACTOR overloaded implemented flag (#1223)

It no longer needs to be overloaded by
marking modules that are currently being
implemented. Instead, there is a set with
al such modules.
diff --git a/src/common.h b/src/common.h
index 559a4f9..2002c96 100644
--- a/src/common.h
+++ b/src/common.h
@@ -219,7 +219,9 @@
 struct ly_ctx {
     struct dict_table dict;           /**< dictionary to effectively store strings used in the context related structures */
     struct ly_set search_paths;       /**< set of directories where to search for schema's imports/includes */
-    struct ly_set list;               /**< set of YANG schemas */
+    struct ly_set list;               /**< set of loaded YANG schemas */
+    struct ly_set implementing;       /**< set of YANG schemas being atomically implemented (compiled); the first added
+                                           module is always the explcitly implemented module, the other ones are dependencies */
     ly_module_imp_clb imp_clb;        /**< Optional callback for retrieving missing included or imported models in a custom way. */
     void *imp_clb_data;               /**< Optional private data for imp_clb() */
     uint16_t module_set_id;           /**< ID of the current set of schemas */
diff --git a/src/path.c b/src/path.c
index fe6adeb..4b0ec07 100644
--- a/src/path.c
+++ b/src/path.c
@@ -398,7 +398,7 @@
                 LOGVAL_P(ctx, cur_node, LYVE_XPATH, "Not implemented module \"%s\" in path.", (*mod)->name);
                 return LY_EVALID;
             }
-            LY_CHECK_RET(lys_set_implemented_internal((struct lys_module *)*mod, ctx->module_set_id));
+            LY_CHECK_RET(lys_set_implemented((struct lys_module *)*mod));
         }
     } else {
         switch (format) {
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 49e2dd6..5f11d5b 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -811,44 +811,75 @@
     return LY_SUCCESS;
 }
 
-LY_ERR
-lys_set_implemented_internal(struct lys_module *mod, uint8_t value)
+API LY_ERR
+lys_set_implemented(struct lys_module *mod)
 {
+    LY_ERR ret = LY_SUCCESS, r;
     struct lys_module *m;
+    uint32_t i, idx;
 
     LY_CHECK_ARG_RET(NULL, mod, LY_EINVAL);
 
     if (mod->implemented) {
+        /* mod is already implemented */
         return LY_SUCCESS;
     }
 
     /* we have module from the current context */
     m = ly_ctx_get_module_implemented(mod->ctx, mod->name);
     if (m) {
-        if (m != mod) {
-            /* check collision with other implemented revision */
-            LOGERR(mod->ctx, LY_EDENIED, "Module \"%s\" is present in the context in other implemented revision (%s).",
-                   mod->name, mod->revision ? mod->revision : "module without revision");
-            return LY_EDENIED;
-        } else {
-            /* mod is already implemented */
-            return LY_SUCCESS;
-        }
+        assert(m != mod);
+
+        /* check collision with other implemented revision */
+        LOGERR(mod->ctx, LY_EDENIED, "Module \"%s%s%s\" is present in the context in other implemented revision (%s).",
+                mod->name, mod->revision ? "@" : "", mod->revision ? mod->revision : "", m->revision ? m->revision : "none");
+        return LY_EDENIED;
     }
 
+    /* add the module into newly implemented module set */
+    LY_CHECK_RET(ly_set_add(&mod->ctx->implementing, mod, LY_SET_OPT_USEASLIST, NULL));
+
     /* mark the module implemented, check for collision was already done */
-    mod->implemented = value;
+    mod->implemented = 1;
 
     /* compile the schema */
-    LY_CHECK_RET(lys_compile(mod, LYSC_OPT_INTERNAL));
+    ret = lys_compile(mod, 0);
 
-    return LY_SUCCESS;
-}
+    if (mod == mod->ctx->implementing.objs[0]) {
+        /* the first module being implemented, consolidate the set */
+        if (ret) {
+            /* failure, full compile revert */
+            for (i = 0; i < mod->ctx->list.count; ++i) {
+                m = mod->ctx->list.objs[i];
+                if (ly_set_contains(&mod->ctx->implementing, m, &idx)) {
+                    assert(m->implemented);
 
-API LY_ERR
-lys_set_implemented(struct lys_module *mod)
-{
-    return lys_set_implemented_internal(mod, 1);
+                    /* make the module correctly non-implemented again */
+                    m->implemented = 0;
+                    ly_set_rm_index(&mod->ctx->implementing, idx, NULL);
+                    lys_precompile_augments_deviations_revert(mod->ctx, m);
+                }
+
+                /* free the compiled version of the module, if any */
+                lysc_module_free(m->compiled, NULL);
+                m->compiled = NULL;
+
+                if (m->implemented) {
+                    /* recompile, must succeed because it was already compiled; hide messages because any
+                     * warnings were already printed, are not really relevant, and would hide the real error */
+                    uint32_t prev_lo = ly_log_options(0);
+                    r = lys_compile(m, 0);
+                    ly_log_options(prev_lo);
+                    if (r) {
+                        LOGERR(mod->ctx, r, "Recompilation of module \"%s\" failed.", m->name);
+                    }
+                }
+            }
+        }
+
+        ly_set_erase(&mod->ctx->implementing, NULL);
+    }
+    return ret;
 }
 
 static LY_ERR
@@ -1048,9 +1079,6 @@
             ret = LY_EDENIED;
             goto error;
         }
-
-        /* being implemented */
-        mod->implemented = ctx->module_set_id;
     }
 
     /* check for duplicity in the context */
@@ -1118,7 +1146,7 @@
 
     lys_parser_fill_filepath(ctx, in, &mod->filepath);
 
-    if (!mod->implemented) {
+    if (!implement) {
         /* pre-compile features and identities of the module */
         LY_CHECK_GOTO(ret = lys_feature_precompile(NULL, ctx, mod, mod->parsed->features, &mod->features), error);
         LY_CHECK_GOTO(ret = lys_identity_precompile(NULL, ctx, mod, mod->parsed->identities, &mod->identities), error);
@@ -1137,7 +1165,7 @@
     /* resolve imports and includes */
     LY_CHECK_GOTO(ret = lys_resolve_import_include(pctx, mod->parsed), error_ctx);
 
-    if (!mod->implemented) {
+    if (!implement) {
         /* pre-compile features and identities of any submodules */
         LY_ARRAY_FOR(mod->parsed->includes, u) {
             LY_CHECK_GOTO(ret = lys_feature_precompile(NULL, ctx, mod, mod->parsed->includes[u].submodule->features,
@@ -1150,10 +1178,9 @@
     /* check name collisions - typedefs and TODO groupings */
     LY_CHECK_GOTO(ret = lysp_check_typedefs(pctx, mod->parsed), error_ctx);
 
-    /* compile */
-    if (!mod->compiled) {
-        ret = lys_compile(mod, 0);
-        LY_CHECK_GOTO(ret, error_ctx);
+    if (implement) {
+        /* implement (compile) */
+        LY_CHECK_GOTO(ret = lys_set_implemented(mod), error_ctx);
     }
 
     if (format == LYS_IN_YANG) {
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 85bfc33..f27df91 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1124,10 +1124,9 @@
 #define LYSC_OPT_RPC_OUTPUT LYS_CONFIG_R       /**< Internal option when compiling schema tree of RPC/action output */
 #define LYSC_OPT_RPC_MASK   LYS_CONFIG_MASK    /**< mask for the internal RPC options */
 #define LYSC_OPT_FREE_SP    0x04               /**< Free the input printable schema */
-#define LYSC_OPT_INTERNAL   0x08               /**< Internal compilation caused by dependency */
-#define LYSC_OPT_NOTIFICATION 0x10             /**< Internal option when compiling schema tree of Notification */
+#define LYSC_OPT_NOTIFICATION 0x08             /**< Internal option when compiling schema tree of Notification */
 
-#define LYSC_OPT_GROUPING   0x20               /** Compiling (validation) of a non-instantiated grouping.
+#define LYSC_OPT_GROUPING   0x10               /** Compiling (validation) of a non-instantiated grouping.
                                                    In this case not all the restrictions are checked since they can be valid only
                                                    in the real placement of the grouping. TODO - what specifically is not done */
 /** @} scflags */
@@ -1857,10 +1856,7 @@
     struct lys_module **augmented_by;/**< List of modules that augment this module ([sized array](@ref sizedarrays)) */
     struct lys_module **deviated_by; /**< List of modules that deviate this module ([sized array](@ref sizedarrays)) */
 
-    uint8_t implemented;             /**< flag if the module is implemented, not just imported. The module is implemented if
-                                          the flag has non-zero value. Specific values are used internally:
-                                          1 - implemented module
-                                          >1 - recently implemented module by dependency, it can be reverted in rollback procedure */
+    ly_bool implemented;             /**< flag if the module is implemented, not just imported */
     uint8_t latest_revision;         /**< flag to mark the latest available revision:
                                           1 - the latest revision in searchdirs was not searched yet and this is the
                                           latest revision in the current context
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index b7d449e..7e23961 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -4667,7 +4667,7 @@
 
             /* all the modules must be implemented */
             if (!mod->implemented) {
-                ret = lys_set_implemented_internal(mod, ctx->ctx->module_set_id);
+                ret = lys_set_implemented(mod);
                 LY_CHECK_GOTO(ret, cleanup);
             }
         }
@@ -7143,7 +7143,8 @@
 
 /**
  * @brief Compile top-level augments and deviations defined in the current module.
- * Generally, just add the module refence to the target modules.
+ * Generally, just add the module refence to the target modules. But in case
+ * of foreign augments, they are directly applied.
  *
  * @param[in] ctx Compile context.
  * @return LY_ERR value.
@@ -7161,13 +7162,18 @@
     uint16_t flags;
     uint32_t idx, opt_prev = ctx->options;
 
-    mod_p = ctx->mod->parsed;
-
-    if (mod_p->mod->implemented == 1) {
+    for (idx = 0; idx < ctx->ctx->implementing.count; ++idx) {
+        if (ctx->mod == ctx->ctx->implementing.objs[idx]) {
+            break;
+        }
+    }
+    if (idx == ctx->ctx->implementing.count) {
         /* it was already implemented and all the augments and deviations fully applied */
         return LY_SUCCESS;
     }
 
+    mod_p = ctx->mod->parsed;
+
     LY_ARRAY_FOR(mod_p->augments, u) {
         lysc_update_path(ctx, NULL, "{augment}");
         lysc_update_path(ctx, NULL, mod_p->augments[u].nodeid);
@@ -7288,7 +7294,7 @@
 
         if (mod->implemented) {
             /* compile */
-            LY_CHECK_GOTO(ret = lys_compile(mod, LYSC_OPT_INTERNAL), cleanup);
+            LY_CHECK_GOTO(ret = lys_compile(mod, 0), cleanup);
         }
     }
 
@@ -8030,22 +8036,15 @@
     return LY_SUCCESS;
 }
 
-/**
- * @brief Revert precompilation of module augments and deviations. Meaning remove its reference from
- * all the target modules.
- *
- * @param[in] ctx Compile context.
- * @param[in] mod Mod whose precompilation to revert.
- */
-static void
-lys_precompile_augments_deviations_revert(struct lysc_ctx *ctx, const struct lys_module *mod)
+void
+lys_precompile_augments_deviations_revert(struct ly_ctx *ctx, const struct lys_module *mod)
 {
     uint32_t i;
     LY_ARRAY_COUNT_TYPE u, count;
     struct lys_module *m;
 
-    for (i = 0; i < ctx->ctx->list.count; ++i) {
-        m = ctx->ctx->list.objs[i];
+    for (i = 0; i < ctx->list.count; ++i) {
+        m = ctx->list.objs[i];
 
         if (m->augmented_by) {
             count = LY_ARRAY_COUNT(m->augmented_by);
@@ -8176,7 +8175,6 @@
     struct lysp_submodule *submod;
     struct lysp_node *pnode;
     struct lysp_grp *grps;
-    struct lys_module *m;
     LY_ARRAY_COUNT_TYPE u, v;
     uint32_t i;
     LY_ERR ret = LY_SUCCESS;
@@ -8323,20 +8321,10 @@
         mod->parsed = NULL;
     }
 
-    if (!(ctx.options & LYSC_OPT_INTERNAL)) {
-        /* remove flag of the modules implemented by dependency */
-        for (i = 0; i < ctx.ctx->list.count; ++i) {
-            m = ctx.ctx->list.objs[i];
-            if (m->implemented > 1) {
-                m->implemented = 1;
-            }
-        }
-    }
-
     return LY_SUCCESS;
 
 error:
-    lys_precompile_augments_deviations_revert(&ctx, mod);
+    lys_precompile_augments_deviations_revert(ctx.ctx, mod);
     lys_feature_precompile_revert(&ctx, mod);
     for (i = 0; i < ctx.dflts.count; ++i) {
         lysc_unres_dflt_free(ctx.ctx, ctx.dflts.objs[i]);
@@ -8365,31 +8353,5 @@
     lysc_module_free(mod_c, NULL);
     mod->compiled = NULL;
 
-    /* revert compilation of modules implemented by dependency */
-    if (!(ctx.options & LYSC_OPT_INTERNAL)) {
-        for (i = 0; i < ctx.ctx->list.count; ++i) {
-            m = ctx.ctx->list.objs[i];
-            if (m->implemented > 1) {
-                /* make the module non-implemented */
-                m->implemented = 0;
-            }
-
-            /* free the compiled version of the module, if any */
-            lysc_module_free(m->compiled, NULL);
-            m->compiled = NULL;
-
-            if (m->implemented) {
-                /* recompile, must succeed because it was already compiled; hide messages because any
-                 * warnings were already printed, are not really relevant, and would hide the real error */
-                uint32_t prev_lo = ly_log_options(0);
-                LY_ERR r = lys_compile(m, LYSC_OPT_INTERNAL);
-                ly_log_options(prev_lo);
-                if (r) {
-                    LOGERR(ctx.ctx, r, "Recompilation of module \"%s\" failed.", m->name);
-                }
-            }
-        }
-    }
-
     return ret;
 }
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 0a1d5ab..5d9bb9c 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -407,6 +407,15 @@
 void lysp_notif_free(struct ly_ctx *ctx, struct lysp_notif *notif);
 
 /**
+ * @brief Revert precompilation of module augments and deviations. Meaning remove its reference from
+ * all the target modules.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] mod Mod whose precompilation to revert.
+ */
+void lys_precompile_augments_deviations_revert(struct ly_ctx *ctx, const struct lys_module *mod);
+
+/**
  * @brief Compile printable schema into a validated schema linking all the references.
  *
  * @param[in] mod Pointer to the schema structure holding pointers to both schema structure types. The ::lys_module#parsed
@@ -834,16 +843,6 @@
 void lys_module_free(struct lys_module *module, void (*private_destructor)(const struct lysc_node *node, void *priv));
 
 /**
- * @brief Make the specific module implemented, use the provided value as flag.
- *
- * @param[in] mod Module to make implemented. It is not an error to provide already implemented module, it just does nothing.
- * @param[in] implemented Flag value for the ::lys_module::implemented item.
- * @return LY_SUCCESS or LY_EDENIED in case the context contains some other revision of the
- * same module which is already implemented.
- */
-LY_ERR lys_set_implemented_internal(struct lys_module *mod, uint8_t implemented);
-
-/**
  * @brief match yang keyword
  *
  * @param[in] ctx yang parser context for logging, can be NULL if keyword is from YIN data.