schema compile BUGFIX freeing schemas being compiled as a dependency

As a reaction to a compile error, all the schemas previously compiled as
a dependency are supposed to be reverted back to the not-implemented
state. Freeing such schemas-compiled-by-dependency must be done
carefully since some of them could actually cause compilation of the
module which failed and is going to free the dependency modules. We use
current context's set id to identify which modules are safe to free and
which will be freed later when the call stack will return to them.
diff --git a/src/path.c b/src/path.c
index 5f30e94..2eeb7df 100644
--- a/src/path.c
+++ b/src/path.c
@@ -388,7 +388,7 @@
                 LOGVAL_P(ctx, cur_node, LYVE_XPATH, "Not implemented module \"%s\" in path.", (*mod)->name);
                 return LY_EVALID;
             }
-            lys_set_implemented_internal((struct lys_module *)*mod, 2);
+            lys_set_implemented_internal((struct lys_module *)*mod, ctx->module_set_id);
         }
     } else {
         switch (format) {
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 1497a35..fc73acf 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -1061,6 +1061,7 @@
 
     /* add into context */
     ly_set_add(&ctx->list, mod, LY_SET_OPT_USEASLIST);
+    ctx->module_set_id++;
 
 finish_parsing:
     /* resolve imports and includes */
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 32e5470..13164bc 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1861,7 +1861,7 @@
     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
-                                          2 - recently implemented module by dependency, it can be reverted in rollback procedure */
+                                          >1 - recently implemented module by dependency, it can be reverted in rollback procedure */
     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 b5fe792..71b2260 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -1342,9 +1342,11 @@
 {
     LY_ARRAY_COUNT_TYPE u, v;
 
-    /* keep the dis_features list until the complete lys_module is freed */
-    mod->dis_features = mod->compiled->features;
-    mod->compiled->features = NULL;
+    if (mod->compiled) {
+        /* keep the dis_features list until the complete lys_module is freed */
+        mod->dis_features = mod->compiled->features;
+        mod->compiled->features = NULL;
+    }
 
     /* in the dis_features list, remove all the parts (from finished compiling process)
      * which may points into the data being freed here */
@@ -7203,6 +7205,7 @@
     struct lys_module *m;
     LY_ARRAY_COUNT_TYPE u, v;
     uint32_t i;
+    uint16_t compile_id;
     LY_ERR ret = LY_SUCCESS;
 
     LY_CHECK_ARG_RET(NULL, mod, *mod, (*mod)->parsed, (*mod)->ctx, LY_EINVAL);
@@ -7212,6 +7215,7 @@
         return LY_SUCCESS;
     }
 
+    compile_id = ++(*mod)->ctx->module_set_id;
     sp = (*mod)->parsed;
 
     ctx.ctx = (*mod)->ctx;
@@ -7363,13 +7367,12 @@
         /* 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 == 2) {
+            if (m->implemented > 1) {
                 m->implemented = 1;
             }
         }
     }
 
-    (*mod)->compiled = mod_c;
     return LY_SUCCESS;
 
 error:
@@ -7383,10 +7386,12 @@
     lysc_module_free(mod_c, NULL);
     (*mod)->compiled = NULL;
 
-    /* revert compilation of modules implemented by dependency */
+    /* revert compilation of modules implemented by dependency, but only by (directly or indirectly) by dependency
+     * of this module, since this module can be also compiled from dependency, there can be some other modules being
+     * processed and we are going to get back to them via stack, so freeing them is not a good idea. */
     for (i = 0; i < ctx.ctx->list.count; ++i) {
         m = ctx.ctx->list.objs[i];
-        if ((m->implemented == 2) && m->compiled) {
+        if ((m->implemented >= compile_id) && m->compiled) {
             /* revert features list to the precompiled state */
             lys_feature_precompile_revert(&ctx, m);
             /* mark module as imported-only / not-implemented */
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 5b8c1a4..d807838 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -92,7 +92,7 @@
         }
         if (implement && !mod->implemented) {
             /* make the module implemented */
-            ret = lys_set_implemented_internal((struct lys_module*)mod, 2);
+            ret = lys_set_implemented_internal((struct lys_module*)mod, ctx->ctx->module_set_id);
             LY_CHECK_RET(ret);
         }
         if (context_node && (context_node->nodetype & (LYS_RPC | LYS_ACTION))) {