schema compile CHANGE check that derived range is more limiting than its base
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 56b4c4c..ee169b4 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -35,6 +35,8 @@
 #define FREE_STRING(CTX, STRING) if (STRING) {lydict_remove(CTX, STRING);}
 #define FREE_STRINGS(CTX, ARRAY) {uint64_t c__; LY_ARRAY_FOR(ARRAY, c__){FREE_STRING(CTX, ARRAY[c__]);}LY_ARRAY_FREE(ARRAY);}
 
+#define DUP_STRING(CTX, DUP, ORIG) if (ORIG) {DUP = lydict_insert(CTX, ORIG, 0);}
+
 #define COMPILE_ARRAY_GOTO(CTX, ARRAY_P, ARRAY_C, OPTIONS, ITER, FUNC, RET, GOTO) \
     if (ARRAY_P) { \
         LY_ARRAY_CREATE_GOTO((CTX)->ctx, ARRAY_C, LY_ARRAY_SIZE(ARRAY_P), RET, GOTO); \
@@ -182,6 +184,7 @@
     FREE_ARRAY(ctx, type->exts, lysp_ext_instance_free);
 }
 
+static void lysc_type_free(struct ly_ctx *ctx, struct lysc_type *type);
 static void
 lysp_tpdf_free(struct ly_ctx *ctx, struct lysp_tpdf *tpdf)
 {
@@ -191,7 +194,9 @@
     FREE_STRING(ctx, tpdf->dsc);
     FREE_STRING(ctx, tpdf->ref);
     FREE_ARRAY(ctx, tpdf->exts, lysp_ext_instance_free);
+
     lysp_type_free(ctx, &tpdf->type);
+    FREE_MEMBER(ctx, tpdf->type.compiled, lysc_type_free);
 }
 
 static void
@@ -457,6 +462,15 @@
     free(module);
 }
 
+static struct lysc_ext_instance *
+lysc_ext_instance_dup(struct ly_ctx *ctx, struct lysc_ext_instance *orig)
+{
+    /* TODO */
+    (void) ctx;
+    (void) orig;
+    return NULL;
+}
+
 static void
 lysc_ext_instance_free(struct ly_ctx *ctx, struct lysc_ext_instance *ext)
 {
@@ -497,6 +511,30 @@
     FREE_ARRAY(ctx, feat->exts, lysc_ext_instance_free);
 }
 
+struct lysc_range*
+lysc_range_dup(struct ly_ctx *ctx, const struct lysc_range *orig)
+{
+    struct lysc_range *dup;
+    LY_ERR ret;
+
+    dup = calloc(1, sizeof *dup);
+    LY_CHECK_ERR_RET(!dup, LOGMEM(ctx), NULL);
+    if (orig->parts) {
+        LY_ARRAY_CREATE_GOTO(ctx, dup->parts, LY_ARRAY_SIZE(orig->parts), ret, cleanup);
+        LY_ARRAY_SIZE(dup->parts) = LY_ARRAY_SIZE(orig->parts);
+        memcpy(dup->parts, orig->parts, LY_ARRAY_SIZE(dup->parts) * sizeof *dup->parts);
+    }
+    DUP_STRING(ctx, dup->eapptag, orig->eapptag);
+    DUP_STRING(ctx, dup->emsg, orig->emsg);
+    dup->exts = lysc_ext_instance_dup(ctx, orig->exts);
+
+    return dup;
+cleanup:
+    free(dup);
+    (void) ret; /* set but not used due to the return type */
+    return NULL;
+}
+
 static void
 lysc_range_free(struct ly_ctx *ctx, struct lysc_range *range)
 {
@@ -1539,38 +1577,33 @@
 
 static LY_ERR
 lys_compile_type_range(struct lysc_ctx *ctx, struct lysp_restr *range_p, LY_DATA_TYPE basetype, int length_restr,
-                       struct lysc_range **range)
+                       struct lysc_range *base_range, struct lysc_range **range)
 {
     LY_ERR ret = LY_EVALID;
     const char *expr;
     struct lysc_range_part *parts = NULL, *part;
-    size_t parts_done = 0;
-    int range_expected = 0;
+    int range_expected = 0, uns;
+    unsigned int parts_done = 0, u, v;
 
     assert(range);
     assert(range_p);
 
-    if (!(*range)) {
-        *range = calloc(1, sizeof **range);
-        LY_CHECK_ERR_RET(!(*range), LOGMEM(ctx->ctx), LY_EMEM);
-    }
-
-    if (range_p->eapptag) {
-        lydict_remove(ctx->ctx, (*range)->eapptag);
-        (*range)->eapptag = lydict_insert(ctx->ctx, range_p->eapptag, 0);
-    }
-    if (range_p->emsg) {
-        lydict_remove(ctx->ctx, (*range)->emsg);
-        (*range)->emsg = lydict_insert(ctx->ctx, range_p->emsg, 0);
-    }
-    /* extensions are taken only from the last range by the caller */
-
     expr = range_p->arg;
     while(1) {
         if (isspace(*expr)) {
             ++expr;
-            continue;
         } else if (*expr == '\0') {
+            if (range_expected) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid %s restriction - unexpected end of the expression after \"..\" (%s).",
+                       length_restr ? "length" : "range", range_p->arg);
+                goto cleanup;
+            } else if (!parts || parts_done == LY_ARRAY_SIZE(parts)) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid %s restriction - unexpected end of the expression (%s).",
+                       length_restr ? "length" : "range", range_p->arg);
+                goto cleanup;
+            }
             parts_done++;
             break;
         } else if (!strncmp(expr, "min", 3)) {
@@ -1595,17 +1628,12 @@
             expr++;
             parts_done++;
             /* process next part of the expression */
-            continue;
         } else if (!strncmp(expr, "..", 2)) {
             expr += 2;
             while (isspace(*expr)) {
                 expr++;
             }
-            if (*expr == '\0') {
-                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
-                       "Invalid %s restriction - unexpected end of the expression after \"..\".", length_restr ? "length" : "range");
-                goto cleanup;
-            } else if (!parts || LY_ARRAY_SIZE(parts) == parts_done) {
+            if (!parts || LY_ARRAY_SIZE(parts) == parts_done) {
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
                        "Invalid %s restriction - unexpected \"..\" without a lower bound.", length_restr ? "length" : "range");
                 goto cleanup;
@@ -1617,6 +1645,7 @@
             if (range_expected) {
                 part = &parts[LY_ARRAY_SIZE(parts) - 1];
                 LY_CHECK_GOTO(range_part_minmax(ctx, part, 1, part->min_64, basetype, 0, length_restr, &expr), cleanup);
+                range_expected = 0;
             } else {
                 LY_ARRAY_NEW_GOTO(ctx->ctx, parts, part, ret, cleanup);
                 LY_CHECK_GOTO(range_part_minmax(ctx, part, 0, parts_done ? parts[LY_ARRAY_SIZE(parts) - 2].max_64 : 0,
@@ -1625,8 +1654,6 @@
             }
 
             /* continue with possible another expression part */
-            range_expected = 0;
-            continue;
         } else if (!strncmp(expr, "max", 3)) {
             expr += 3;
             while (isspace(*expr)) {
@@ -1640,15 +1667,13 @@
             if (range_expected) {
                 part = &parts[LY_ARRAY_SIZE(parts) - 1];
                 LY_CHECK_GOTO(range_part_minmax(ctx, part, 1, part->min_64, basetype, 0, length_restr, NULL), cleanup);
-                parts_done++;
+                range_expected = 0;
             } else {
                 LY_ARRAY_NEW_GOTO(ctx->ctx, parts, part, ret, cleanup);
                 LY_CHECK_GOTO(range_part_minmax(ctx, part, 1, parts_done ? parts[LY_ARRAY_SIZE(parts) - 2].max_64 : 0,
                                                 basetype, parts_done ? 0 : 1, length_restr, NULL), cleanup);
                 part->min_64 = part->max_64;
             }
-            /* done */
-            break;
         } else {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Invalid %s restriction - unexpected data (%s).",
                    length_restr ? "length" : "range", expr);
@@ -1656,6 +1681,110 @@
         }
     }
 
+    /* check with the previous range/length restriction */
+    if (base_range) {
+        switch (basetype) {
+        case LY_TYPE_BINARY:
+        case LY_TYPE_UINT8:
+        case LY_TYPE_UINT16:
+        case LY_TYPE_UINT32:
+        case LY_TYPE_UINT64:
+        case LY_TYPE_STRING:
+            uns = 1;
+            break;
+        case LY_TYPE_DEC64:
+        case LY_TYPE_INT8:
+        case LY_TYPE_INT16:
+        case LY_TYPE_INT32:
+        case LY_TYPE_INT64:
+            uns = 0;
+            break;
+        default:
+            LOGINT(ctx->ctx);
+            ret = LY_EINT;
+            goto cleanup;
+        }
+        for (u = v = 0; u < parts_done && v < LY_ARRAY_SIZE(base_range->parts); ++u) {
+            if ((uns && parts[u].min_u64 < base_range->parts[v].min_u64) || (!uns && parts[u].min_64 < base_range->parts[v].min_64)) {
+                goto baseerror;
+            }
+            /* current lower bound is not lower than the base */
+            if (base_range->parts[v].min_64 == base_range->parts[v].max_64) {
+                /* base has single value */
+                if (base_range->parts[v].min_64 == parts[u].min_64) {
+                    /* both lower bounds are the same */
+                    if (parts[u].min_64 != parts[u].max_64) {
+                        /* current continues with a range */
+                        goto baseerror;
+                    } else {
+                        /* equal single values, move both forward */
+                        ++v;
+                        continue;
+                    }
+                } else {
+                    /* base is single value lower than current range, so the
+                     * value from base range is removed in the current,
+                     * move only base and repeat checking */
+                    ++v;
+                    --u;
+                    continue;
+                }
+            } else {
+                /* base is the range */
+                if (parts[u].min_64 == parts[u].max_64) {
+                    /* current is a single value */
+                    if ((uns && parts[u].max_u64 > base_range->parts[v].max_u64) || (!uns && parts[u].max_64 > base_range->parts[v].max_64)) {
+                        /* current is behind the base range, so base range is omitted,
+                         * move the base and keep the current for further check */
+                        ++v;
+                        --u;
+                    } /* else it is within the base range, so move the current, but keep the base */
+                    continue;
+                } else {
+                    /* both are ranges - check the higher bound, the lower was already checked */
+                    if ((uns && parts[u].max_u64 > base_range->parts[v].max_u64) || (!uns && parts[u].max_64 > base_range->parts[v].max_64)) {
+                        /* higher bound is higher than the current higher bound */
+                        if ((uns && parts[u].min_u64 > base_range->parts[v].max_u64) || (!uns && parts[u].min_64 > base_range->parts[v].max_64)) {
+                            /* but the current lower bound is also higher, so the base range is omitted,
+                             * continue with the same current, but move the base */
+                            --u;
+                            ++v;
+                            continue;
+                        }
+                        /* current range starts within the base range but end behind it */
+                        goto baseerror;
+                    } else {
+                        /* current range is smaller than the base,
+                         * move current, but stay with the base */
+                        continue;
+                    }
+                }
+            }
+        }
+        if (u != parts_done) {
+baseerror:
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                   "Invalid %s restriction - the derived restriction (%s) is not equally or more limiting.",
+                   length_restr ? "length" : "range", range_p->arg);
+            goto cleanup;
+        }
+    }
+
+    if (!(*range)) {
+        *range = calloc(1, sizeof **range);
+        LY_CHECK_ERR_RET(!(*range), LOGMEM(ctx->ctx), LY_EMEM);
+    }
+
+    if (range_p->eapptag) {
+        lydict_remove(ctx->ctx, (*range)->eapptag);
+        (*range)->eapptag = lydict_insert(ctx->ctx, range_p->eapptag, 0);
+    }
+    if (range_p->emsg) {
+        lydict_remove(ctx->ctx, (*range)->emsg);
+        (*range)->emsg = lydict_insert(ctx->ctx, range_p->emsg, 0);
+    }
+    /* extensions are taken only from the last range by the caller */
+
     (*range)->parts = parts;
     parts = NULL;
     ret = LY_SUCCESS;
@@ -1678,6 +1807,7 @@
         struct lysp_module *mod;
     } *tctx, *tctx_prev = NULL;
     LY_DATA_TYPE basetype = LY_TYPE_UNKNOWN;
+    struct lysc_type *base = NULL;
     struct ly_set tpdf_chain = {0};
     struct lysc_type_bin* bin;
 
@@ -1686,7 +1816,7 @@
     LY_CHECK_ERR_RET(!tctx, LOGMEM(ctx->ctx), LY_EMEM);
     for (ret = lysp_type_find(type_p->name, (struct lysp_node*)leaf_p, ctx->mod->parsed,
                              &basetype, &tctx->tpdf, &tctx->node, &tctx->mod);
-            ret;
+            ret == LY_SUCCESS;
             ret = lysp_type_find(tctx_prev->tpdf->type.name, tctx_prev->node, tctx_prev->mod,
                                          &basetype, &tctx->tpdf, &tctx->node, &tctx->mod)) {
         if (basetype) {
@@ -1700,7 +1830,13 @@
             goto cleanup;
         }
 
-        if (!(tctx->tpdf->flags & LYS_TYPE_MODIFIED)) {
+        if (tctx->tpdf->type.compiled) {
+            /* it is not necessary to continue, the rest of the chain was already compiled */
+            basetype = tctx->tpdf->type.compiled->basetype;
+            ly_set_add(&tpdf_chain, tctx, LY_SET_OPT_USEASLIST);
+            tctx = NULL;
+            break;
+        } else if (!(tctx->tpdf->type.flags & LYS_TYPE_MODIFIED)) {
             /* no change in comparison to the following (actually preceding in the chain of type derivations) type */
             memset(tctx, 0, sizeof *tctx);
             continue;
@@ -1710,6 +1846,7 @@
         ly_set_add(&tpdf_chain, tctx, LY_SET_OPT_USEASLIST);
 
         /* prepare next loop */
+        tctx_prev = tctx;
         tctx = calloc(1, sizeof *tctx);
         LY_CHECK_ERR_RET(!tctx, LOGMEM(ctx->ctx), LY_EMEM);
     }
@@ -1759,41 +1896,66 @@
     case LY_TYPE_UINT64:
         *type = calloc(1, sizeof(struct lysc_type_num));
         break;
-    default:
-        LOGINT(ctx->ctx);
+    case LY_TYPE_UNKNOWN:
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+               "Referenced type \"%s\" not found.", tctx_prev ? tctx_prev->tpdf->type.name : type_p->name);
+        goto cleanup;
     }
     LY_CHECK_ERR_GOTO(!(*type), LOGMEM(ctx->ctx), cleanup);
-    (*type)->basetype = basetype;
-    COMPILE_ARRAY_GOTO(ctx, type_p->exts, (*type)->exts, options, u, lys_compile_ext, ret, cleanup);
 
 
     /* get restrictions from the referred typedefs */
     for (u = tpdf_chain.count - 1; u + 1 > 0; --u) {
         tctx = (struct type_context*)tpdf_chain.objs[u];
+        if (tctx->tpdf->type.compiled) {
+            base = tctx->tpdf->type.compiled;
+            continue;
+        } else if ((u != tpdf_chain.count - 1) && (tctx->tpdf->type.flags & LYS_TYPE_MODIFIED)) {
+            base = ((struct lysp_tpdf*)tctx->tpdf)->type.compiled = ((struct type_context*)tpdf_chain.objs[u + 1])->tpdf->type.compiled;
+            ++base->refcount;
+            continue;
+        }
 
+        ++(*type)->refcount;
+        (*type)->basetype = basetype;
         switch (basetype) {
         case LY_TYPE_BINARY:
             /* RFC 6020 9.8.1, 9.4.4 - length, number of octets it contains */
             if (tctx->tpdf->type.length) {
-                ret = lys_compile_type_range(ctx, tctx->tpdf->type.length, basetype, 1, &bin->length);
+                ret = lys_compile_type_range(ctx, tctx->tpdf->type.length, basetype, 1,
+                                             base ? ((struct lysc_type_bin*)base)->length : NULL, &bin->length);
                 LY_CHECK_GOTO(ret, cleanup);
+            } else if (base && ((struct lysc_type_bin*)base)->length) {
+                bin->length = lysc_range_dup(ctx->ctx, ((struct lysc_type_bin*)base)->length);
             }
+
+            base = ((struct lysp_tpdf*)tctx->tpdf)->type.compiled = *type;
+            *type = calloc(1, sizeof(struct lysc_type_bin));
+            bin = (struct lysc_type_bin*)(*type);
             break;
         }
+        LY_CHECK_ERR_GOTO(!(*type), LOGMEM(ctx->ctx), cleanup);
+
     }
 
     /* get restrictions from the node itself, finalize the type structure */
-    switch ((*type)->basetype) {
+    (*type)->basetype = basetype;
+    switch (basetype) {
     case LY_TYPE_BINARY:
         if (leaf_p->type.length) {
-            ret = lys_compile_type_range(ctx, leaf_p->type.length, basetype, 1, &bin->length);
+            ret = lys_compile_type_range(ctx, leaf_p->type.length, basetype, 1,
+                                         base ? ((struct lysc_type_bin*)base)->length : NULL, &bin->length);
             LY_CHECK_GOTO(ret, cleanup);
             COMPILE_ARRAY_GOTO(ctx, leaf_p->type.length->exts, bin->length->exts,
                                options, u, lys_compile_ext, ret, cleanup);
+        } else if (base && ((struct lysc_type_bin*)base)->length) {
+            bin->length = lysc_range_dup(ctx->ctx, ((struct lysc_type_bin*)base)->length);
         }
         break;
     }
 
+    COMPILE_ARRAY_GOTO(ctx, type_p->exts, (*type)->exts, options, u, lys_compile_ext, ret, cleanup);
+
 cleanup:
     ly_set_erase(&tpdf_chain, free);