@nudojs/core 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-V2XC3V72.js → chunk-N7AGFMSD.js} +39 -1
- package/dist/{chunk-A2PLG2QV.js → chunk-W3Z77Y2I.js} +234 -76
- package/dist/exec.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +77 -17
- package/dist/internal.js +2 -2
- package/package.json +1 -1
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
withEvalBudgetOverride,
|
|
48
48
|
withExecPhi,
|
|
49
49
|
withFnBudgetOverride
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-W3Z77Y2I.js";
|
|
51
51
|
|
|
52
52
|
// src/algebra/directive-scan.ts
|
|
53
53
|
function cleanDirectiveLine(raw, kind) {
|
|
@@ -622,6 +622,14 @@ function scanAssumeSegments(lines) {
|
|
|
622
622
|
}
|
|
623
623
|
return out;
|
|
624
624
|
}
|
|
625
|
+
function scanInvariantSegments(lines) {
|
|
626
|
+
const out = [];
|
|
627
|
+
for (const line of lines) {
|
|
628
|
+
const m = line.match(/^@nudo:invariant\s+(.+)$/);
|
|
629
|
+
if (m) out.push(m[1].trim().replace(/\*\/$/, "").trim());
|
|
630
|
+
}
|
|
631
|
+
return out;
|
|
632
|
+
}
|
|
625
633
|
function scanThrowsDecl(lines) {
|
|
626
634
|
const kinds = /* @__PURE__ */ new Set();
|
|
627
635
|
let any = false;
|
|
@@ -1516,6 +1524,35 @@ function extractAssumeSegs(source, fnName) {
|
|
|
1516
1524
|
}
|
|
1517
1525
|
return out;
|
|
1518
1526
|
}
|
|
1527
|
+
var INVARIANT_SEG_REGEX = /^([A-Za-z_$][\w$]*)((?:\[\*\]|\.[A-Za-z_$][\w$]*)*)\s*(>=|<=|>|<)\s*(-?\d+(?:\.\d+)?)$/;
|
|
1528
|
+
function extractInvariantSegs(source, fnName) {
|
|
1529
|
+
if (!stripStringsKeepComments(source).includes("@nudo:invariant")) return [];
|
|
1530
|
+
const out = [];
|
|
1531
|
+
for (const seg of scanInvariantSegments(fnDirectiveCommentLines(source, fnName))) {
|
|
1532
|
+
const m = seg.match(INVARIANT_SEG_REGEX);
|
|
1533
|
+
if (!m) {
|
|
1534
|
+
out.push({ kind: "bad", raw: seg });
|
|
1535
|
+
continue;
|
|
1536
|
+
}
|
|
1537
|
+
const pathStr = m[2] ?? "";
|
|
1538
|
+
const path = [];
|
|
1539
|
+
for (const pm of pathStr.matchAll(/\[\*\]|\.[A-Za-z_$][\w$]*/g)) {
|
|
1540
|
+
const t = pm[0];
|
|
1541
|
+
if (t === "[*]") path.push({ k: "idx" });
|
|
1542
|
+
else path.push({ k: "prop", name: t.slice(1) });
|
|
1543
|
+
}
|
|
1544
|
+
const relTxt = m[3];
|
|
1545
|
+
out.push({
|
|
1546
|
+
kind: "ok",
|
|
1547
|
+
name: m[1],
|
|
1548
|
+
path,
|
|
1549
|
+
rel: relTxt === ">=" ? "ge" : relTxt === "<=" ? "le" : relTxt === ">" ? "gt" : "lt",
|
|
1550
|
+
value: Number(m[4]),
|
|
1551
|
+
display: `${m[1]}${pathStr} ${relTxt} ${m[4]}`
|
|
1552
|
+
});
|
|
1553
|
+
}
|
|
1554
|
+
return out;
|
|
1555
|
+
}
|
|
1519
1556
|
function fnTopLevelLocalNames(body) {
|
|
1520
1557
|
const block = body;
|
|
1521
1558
|
if (!block || block.type !== "BlockStatement" || !Array.isArray(block.body)) return [];
|
|
@@ -5568,6 +5605,7 @@ export {
|
|
|
5568
5605
|
execNudoModule,
|
|
5569
5606
|
resetNudoModuleExecCache,
|
|
5570
5607
|
extractAssumeSegs,
|
|
5608
|
+
extractInvariantSegs,
|
|
5571
5609
|
fnTopLevelLocalNames,
|
|
5572
5610
|
extractRefinesFromSource,
|
|
5573
5611
|
refineToIndexedFull,
|
|
@@ -28,6 +28,27 @@ function termEquals(a, b) {
|
|
|
28
28
|
}
|
|
29
29
|
return false;
|
|
30
30
|
}
|
|
31
|
+
var DETERMINISTIC_APP_FNS = /* @__PURE__ */ new Set([
|
|
32
|
+
"Math.max",
|
|
33
|
+
"Math.min",
|
|
34
|
+
"Math.abs",
|
|
35
|
+
"Math.round",
|
|
36
|
+
"Math.floor",
|
|
37
|
+
"Math.ceil",
|
|
38
|
+
"Math.trunc",
|
|
39
|
+
"Math.sign",
|
|
40
|
+
"+",
|
|
41
|
+
"-",
|
|
42
|
+
"*",
|
|
43
|
+
"/",
|
|
44
|
+
"%"
|
|
45
|
+
]);
|
|
46
|
+
var LEN_TERM_ID_RE = /^len#\d+$/;
|
|
47
|
+
function isDeterministicTerm(t) {
|
|
48
|
+
if (t.op === "lit") return true;
|
|
49
|
+
if (t.op === "var") return LEN_TERM_ID_RE.test(t.id);
|
|
50
|
+
return DETERMINISTIC_APP_FNS.has(t.fn) && t.args.every(isDeterministicTerm);
|
|
51
|
+
}
|
|
31
52
|
function litKeyString(v2) {
|
|
32
53
|
if (typeof v2 === "number" && Object.is(v2, -0)) return "-0";
|
|
33
54
|
return String(v2);
|
|
@@ -4471,6 +4492,19 @@ function withAssumePremises(p, body) {
|
|
|
4471
4492
|
assumePremises = prev;
|
|
4472
4493
|
}
|
|
4473
4494
|
}
|
|
4495
|
+
var invariantSpecs = [];
|
|
4496
|
+
function currentInvariantSpecs() {
|
|
4497
|
+
return invariantSpecs;
|
|
4498
|
+
}
|
|
4499
|
+
function withInvariantSpecs(specs, body) {
|
|
4500
|
+
const prev = invariantSpecs;
|
|
4501
|
+
invariantSpecs = specs;
|
|
4502
|
+
try {
|
|
4503
|
+
return body();
|
|
4504
|
+
} finally {
|
|
4505
|
+
invariantSpecs = prev;
|
|
4506
|
+
}
|
|
4507
|
+
}
|
|
4474
4508
|
var noBody2 = { type: "BlockStatement", body: [], directives: [] };
|
|
4475
4509
|
var hostGlobalFnCall;
|
|
4476
4510
|
function setHostGlobalFnCall(bridge) {
|
|
@@ -4511,8 +4545,11 @@ function $absVal(v2) {
|
|
|
4511
4545
|
return asAbsVal(v2);
|
|
4512
4546
|
}
|
|
4513
4547
|
function callAtFunctionBoundary(body) {
|
|
4514
|
-
if (assumePremises !== pTrue) {
|
|
4515
|
-
return withAssumePremises(
|
|
4548
|
+
if (assumePremises !== pTrue || invariantSpecs.length > 0) {
|
|
4549
|
+
return withAssumePremises(
|
|
4550
|
+
pTrue,
|
|
4551
|
+
() => withInvariantSpecs([], () => boundaryBody(body))
|
|
4552
|
+
);
|
|
4516
4553
|
}
|
|
4517
4554
|
return boundaryBody(body);
|
|
4518
4555
|
}
|
|
@@ -5043,6 +5080,16 @@ function bumpEvalForkBudget() {
|
|
|
5043
5080
|
return false;
|
|
5044
5081
|
}
|
|
5045
5082
|
|
|
5083
|
+
// src/algebra/len-sharing.ts
|
|
5084
|
+
var domainShared = /* @__PURE__ */ new WeakSet();
|
|
5085
|
+
function markDomainShared(a) {
|
|
5086
|
+
domainShared.add(a);
|
|
5087
|
+
return a;
|
|
5088
|
+
}
|
|
5089
|
+
function isDomainShared(a) {
|
|
5090
|
+
return domainShared.has(a);
|
|
5091
|
+
}
|
|
5092
|
+
|
|
5046
5093
|
// src/algebra/exec/match-iter.ts
|
|
5047
5094
|
var tables = /* @__PURE__ */ new WeakMap();
|
|
5048
5095
|
function registerMatchIter(val, elements) {
|
|
@@ -6585,6 +6632,12 @@ function makeArrayCtorAbs(args) {
|
|
|
6585
6632
|
}
|
|
6586
6633
|
return abs({ k: "tuple", elements: [a0] }, void 0, void 0, "exact");
|
|
6587
6634
|
}
|
|
6635
|
+
var fromIdxSeq = 0;
|
|
6636
|
+
function fromIdxAbs(lenTerm2, lenNumeric = false) {
|
|
6637
|
+
const t = v(`fromIdx#${fromIdxSeq++}`);
|
|
6638
|
+
const pred = lenNumeric && lenTerm2 ? and(ge(t, lit(0)), lt(t, lenTerm2)) : ge(t, lit(0));
|
|
6639
|
+
return abs({ k: "prim", type: "number" }, t, pred, "path");
|
|
6640
|
+
}
|
|
6588
6641
|
function evalArrayFrom(args) {
|
|
6589
6642
|
if (args[0] && typeof args[0] === "object" && args[0].shape.k === "brand" && (args[0].shape.name === "Arguments" || args[0].shape.name === "Proxy")) {
|
|
6590
6643
|
args = [args[0].shape.shape, ...args.slice(1)];
|
|
@@ -6609,6 +6662,7 @@ function evalArrayFrom(args) {
|
|
|
6609
6662
|
if (!hasMapFn) return el;
|
|
6610
6663
|
return applyCallbackValue(mapFn, [el, i], emptyEnv(), pTrue, defaultLeakBudget);
|
|
6611
6664
|
};
|
|
6665
|
+
const idxOrDrop = (lenTerm2, lenNumeric = false) => hasMapFn ? fromIdxAbs(lenTerm2, lenNumeric) : unknown;
|
|
6612
6666
|
if (isSetAbs(a0) || isMapAbs(a0)) {
|
|
6613
6667
|
const els = isSetAbs(a0) ? setElementsAbs(a0) : mapEntriesAbs(a0);
|
|
6614
6668
|
if (els.length === 0) {
|
|
@@ -6643,7 +6697,7 @@ function evalArrayFrom(args) {
|
|
|
6643
6697
|
}
|
|
6644
6698
|
const k = a0.shape.k;
|
|
6645
6699
|
if (k === "arr") {
|
|
6646
|
-
const el = mapOne(a0.shape.element,
|
|
6700
|
+
const el = mapOne(markDomainShared(a0.shape.element), idxOrDrop());
|
|
6647
6701
|
return abs({ k: "arr", element: el }, void 0, void 0, "path");
|
|
6648
6702
|
}
|
|
6649
6703
|
if (k === "tuple") {
|
|
@@ -6717,16 +6771,19 @@ function evalArrayFrom(args) {
|
|
|
6717
6771
|
}
|
|
6718
6772
|
return abs({ k: "tuple", elements: els }, void 0, void 0, "exact");
|
|
6719
6773
|
}
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6774
|
+
const iAbs = idxOrDrop(lit(nLit), true);
|
|
6775
|
+
let el2 = mapOne(undefAbs(), iAbs);
|
|
6776
|
+
for (const vv of allIdxSlotValues()) el2 = joinAbs(el2, mapOne(vv, iAbs));
|
|
6777
|
+
if (open) el2 = joinAbs(el2, mapOne(unknown, iAbs));
|
|
6723
6778
|
return abs({ k: "arr", element: el2 }, void 0, void 0, "path");
|
|
6724
6779
|
}
|
|
6780
|
+
const lenT = lenAbs?.term;
|
|
6781
|
+
const lenNum = !!lenT && lenAbs.shape.k === "prim" && lenAbs.shape.type === "number";
|
|
6725
6782
|
const idxValues = allIdxSlotValues();
|
|
6726
6783
|
let el = idxValues.length > 0 ? idxValues.reduce((x, y) => joinAbs(x, y)) : undefAbs();
|
|
6727
6784
|
el = joinAbs(el, undefAbs());
|
|
6728
6785
|
if (open) el = joinAbs(el, unknown);
|
|
6729
|
-
el = mapOne(el,
|
|
6786
|
+
el = mapOne(markDomainShared(el), idxOrDrop(lenT, lenNum));
|
|
6730
6787
|
return abs({ k: "arr", element: el }, void 0, void 0, "path");
|
|
6731
6788
|
}
|
|
6732
6789
|
return unknown;
|
|
@@ -13700,7 +13757,7 @@ function $add(a, b) {
|
|
|
13700
13757
|
return add(a, b, currentExecPhi());
|
|
13701
13758
|
}
|
|
13702
13759
|
function $sub(a, b) {
|
|
13703
|
-
return sub(a, b, currentExecPhi());
|
|
13760
|
+
return sub(a, b, currentExecPhi(), currentAssumePremises());
|
|
13704
13761
|
}
|
|
13705
13762
|
function $mul(a, b) {
|
|
13706
13763
|
return mul(a, b, currentExecPhi());
|
|
@@ -15445,6 +15502,80 @@ function primOf(a) {
|
|
|
15445
15502
|
return void 0;
|
|
15446
15503
|
}
|
|
15447
15504
|
|
|
15505
|
+
// src/algebra/exec/runtime/invariant-apply.ts
|
|
15506
|
+
var mintSeq = 0;
|
|
15507
|
+
function strengthenLeaf(a, spec) {
|
|
15508
|
+
const s = a.shape;
|
|
15509
|
+
if (s.k !== "prim" || s.type !== "number") return a;
|
|
15510
|
+
if (a.term?.op === "lit") return a;
|
|
15511
|
+
const term = a.term ?? v(`inv#${mintSeq++}`);
|
|
15512
|
+
const bound = lit(spec.value);
|
|
15513
|
+
const rel = spec.rel === "ge" ? ge(term, bound) : spec.rel === "le" ? le(term, bound) : spec.rel === "gt" ? gt(term, bound) : lt(term, bound);
|
|
15514
|
+
return abs(s, term, a.pred ? and(a.pred, rel) : rel, "path");
|
|
15515
|
+
}
|
|
15516
|
+
function drill(v2, path, spec) {
|
|
15517
|
+
if (path.length === 0) return strengthenLeaf(v2, spec);
|
|
15518
|
+
const [step, ...rest] = path;
|
|
15519
|
+
const s = v2.shape;
|
|
15520
|
+
if (step.k === "idx") {
|
|
15521
|
+
if (s.k === "arr") {
|
|
15522
|
+
const element = drill(s.element, rest, spec);
|
|
15523
|
+
return element === s.element ? v2 : abs({ k: "arr", element }, void 0, void 0, v2.conf);
|
|
15524
|
+
}
|
|
15525
|
+
if (s.k === "tuple") {
|
|
15526
|
+
let changed = false;
|
|
15527
|
+
const elements = s.elements.map((e) => {
|
|
15528
|
+
const w = drill(e, rest, spec);
|
|
15529
|
+
if (w !== e) changed = true;
|
|
15530
|
+
return w;
|
|
15531
|
+
});
|
|
15532
|
+
return changed ? abs({ ...s, elements }, void 0, void 0, v2.conf) : v2;
|
|
15533
|
+
}
|
|
15534
|
+
if (s.k === "sum") {
|
|
15535
|
+
let changed = false;
|
|
15536
|
+
const members = s.members.map((m) => {
|
|
15537
|
+
const w = drill(m, rest, spec);
|
|
15538
|
+
if (w !== m) changed = true;
|
|
15539
|
+
return w;
|
|
15540
|
+
});
|
|
15541
|
+
return changed ? abs({ k: "sum", members }, void 0, void 0, v2.conf) : v2;
|
|
15542
|
+
}
|
|
15543
|
+
return v2;
|
|
15544
|
+
}
|
|
15545
|
+
if (s.k === "obj") {
|
|
15546
|
+
const slot = s.slots[step.name];
|
|
15547
|
+
if (!slot) return v2;
|
|
15548
|
+
const w = drill(slot.value, rest, spec);
|
|
15549
|
+
if (w === slot.value) return v2;
|
|
15550
|
+
return abs(
|
|
15551
|
+
{ ...s, slots: { ...s.slots, [step.name]: { ...slot, value: w } } },
|
|
15552
|
+
void 0,
|
|
15553
|
+
void 0,
|
|
15554
|
+
v2.conf
|
|
15555
|
+
);
|
|
15556
|
+
}
|
|
15557
|
+
return v2;
|
|
15558
|
+
}
|
|
15559
|
+
function applyInvariantsToPack(pack) {
|
|
15560
|
+
const specs = currentInvariantSpecs();
|
|
15561
|
+
if (specs.length === 0) return pack;
|
|
15562
|
+
const s = pack.shape;
|
|
15563
|
+
if (s.k !== "obj") return pack;
|
|
15564
|
+
let slots;
|
|
15565
|
+
let changed = false;
|
|
15566
|
+
for (const spec of specs) {
|
|
15567
|
+
const cur = (slots ?? s.slots)[spec.name];
|
|
15568
|
+
if (!cur) continue;
|
|
15569
|
+
const w = drill(cur.value, spec.path, spec);
|
|
15570
|
+
if (w === cur.value) continue;
|
|
15571
|
+
if (!slots) slots = { ...s.slots };
|
|
15572
|
+
slots[spec.name] = { ...cur, value: w };
|
|
15573
|
+
changed = true;
|
|
15574
|
+
}
|
|
15575
|
+
if (!changed) return pack;
|
|
15576
|
+
return abs({ ...s, slots }, void 0, void 0, pack.conf);
|
|
15577
|
+
}
|
|
15578
|
+
|
|
15448
15579
|
// src/algebra/exec/runtime/loop-widen.ts
|
|
15449
15580
|
function mergeGrowth(a, b) {
|
|
15450
15581
|
if (a === true || b === true) return true;
|
|
@@ -15730,9 +15861,9 @@ function makeLoopWidener() {
|
|
|
15730
15861
|
const base = acc ?? final;
|
|
15731
15862
|
const g = interrupted ? true : growth;
|
|
15732
15863
|
if (g === null) {
|
|
15733
|
-
return collapseArrSums(base);
|
|
15864
|
+
return applyInvariantsToPack(collapseArrSums(base));
|
|
15734
15865
|
}
|
|
15735
|
-
return widenWithPath(normalizeForWiden(base, g), g);
|
|
15866
|
+
return applyInvariantsToPack(widenWithPath(normalizeForWiden(base, g), g));
|
|
15736
15867
|
}
|
|
15737
15868
|
};
|
|
15738
15869
|
}
|
|
@@ -16364,7 +16495,7 @@ function $idx(a, i, opts) {
|
|
|
16364
16495
|
if (idx === void 0) {
|
|
16365
16496
|
return joinAbs(a.shape.element, oobUndef());
|
|
16366
16497
|
}
|
|
16367
|
-
return a.shape.element;
|
|
16498
|
+
return markDomainShared(a.shape.element);
|
|
16368
16499
|
}
|
|
16369
16500
|
if (a.shape.k === "brand") {
|
|
16370
16501
|
const elem = TYPED_ARRAY_ELEMENT[a.shape.name ?? ""];
|
|
@@ -16528,7 +16659,7 @@ function abstractLen(a) {
|
|
|
16528
16659
|
let t = lenTermMemo.get(a);
|
|
16529
16660
|
if (!t) {
|
|
16530
16661
|
t = { op: "var", id: `len#${lenVarSeq++}` };
|
|
16531
|
-
lenTermMemo.set(a, t);
|
|
16662
|
+
if (!isDomainShared(a)) lenTermMemo.set(a, t);
|
|
16532
16663
|
}
|
|
16533
16664
|
return abs({ k: "prim", type: "number" }, t, ge(t, lit(0)), "path");
|
|
16534
16665
|
}
|
|
@@ -16960,7 +17091,7 @@ function $tpl(cooked, raw) {
|
|
|
16960
17091
|
}
|
|
16961
17092
|
function elemsOf(a) {
|
|
16962
17093
|
if (a.shape.k === "tuple") return [...a.shape.elements];
|
|
16963
|
-
if (a.shape.k === "arr") return [a.shape.element];
|
|
17094
|
+
if (a.shape.k === "arr") return [markDomainShared(a.shape.element)];
|
|
16964
17095
|
if (isSetAbs(a)) return setElementsAbs(a);
|
|
16965
17096
|
if (isMapAbs(a)) return mapEntriesAbs(a);
|
|
16966
17097
|
const drained = drainGenElements(a);
|
|
@@ -18082,6 +18213,9 @@ function $for(init, test, step, body, maxIters = DEFAULT_MAX_LOOP_ITERS, opts) {
|
|
|
18082
18213
|
return exitJoin !== void 0 ? counterWiden.widenFinal(exitJoin) : state;
|
|
18083
18214
|
}
|
|
18084
18215
|
function widenLoopJoin(a) {
|
|
18216
|
+
return applyInvariantsToPack(widenLoopJoinInner(a));
|
|
18217
|
+
}
|
|
18218
|
+
function widenLoopJoinInner(a) {
|
|
18085
18219
|
const widenValue = (v2) => {
|
|
18086
18220
|
if (v2.shape.k !== "sum") return v2;
|
|
18087
18221
|
const members = v2.shape.members;
|
|
@@ -22030,7 +22164,7 @@ function invokeArrMethod(arr, method, args) {
|
|
|
22030
22164
|
"path"
|
|
22031
22165
|
);
|
|
22032
22166
|
}
|
|
22033
|
-
const out = callFn(args[0], args[1], shape2.element,
|
|
22167
|
+
const out = callFn(args[0], args[1], markDomainShared(shape2.element), fromIdxAbs(), arr);
|
|
22034
22168
|
const el = mapElementFallback(asAbs(args[0]), shape2.element, out);
|
|
22035
22169
|
const conf = el === out ? confJoin(arr.conf, out.conf) : "partial";
|
|
22036
22170
|
return abs({ k: "arr", element: el }, void 0, void 0, conf);
|
|
@@ -22067,7 +22201,7 @@ function invokeArrMethod(arr, method, args) {
|
|
|
22067
22201
|
}
|
|
22068
22202
|
return acc;
|
|
22069
22203
|
}
|
|
22070
|
-
return callFn(fn2, void 0, acc, shape2.element,
|
|
22204
|
+
return callFn(fn2, void 0, acc, markDomainShared(shape2.element), fromIdxAbs(), arr);
|
|
22071
22205
|
}
|
|
22072
22206
|
if (method === "reduceRight") {
|
|
22073
22207
|
validateCallableArg(args[0], "reduceRight callback may not be callable");
|
|
@@ -22103,7 +22237,7 @@ function invokeArrMethod(arr, method, args) {
|
|
|
22103
22237
|
cause: "reduceRight of possibly-empty array with no initial value"
|
|
22104
22238
|
});
|
|
22105
22239
|
}
|
|
22106
|
-
return callFn(fn2, void 0, acc, shape2.element,
|
|
22240
|
+
return callFn(fn2, void 0, acc, markDomainShared(shape2.element), fromIdxAbs(), arr);
|
|
22107
22241
|
}
|
|
22108
22242
|
if (method === "filter") {
|
|
22109
22243
|
validateCallableArg(args[0], "filter callback may not be callable");
|
|
@@ -22148,7 +22282,7 @@ function invokeArrMethod(arr, method, args) {
|
|
|
22148
22282
|
);
|
|
22149
22283
|
return projectFlatMapResult(arr.conf, mapped);
|
|
22150
22284
|
}
|
|
22151
|
-
const out = callFn(args[0], args[1], shape2.element,
|
|
22285
|
+
const out = callFn(args[0], args[1], markDomainShared(shape2.element), fromIdxAbs(), arr);
|
|
22152
22286
|
return projectFlatMapResult(arr.conf, [out]);
|
|
22153
22287
|
}
|
|
22154
22288
|
if (method === "forEach") {
|
|
@@ -22158,7 +22292,7 @@ function invokeArrMethod(arr, method, args) {
|
|
|
22158
22292
|
if (!isHole(i)) callFn(args[0], args[1], el, $lit(i), arr);
|
|
22159
22293
|
});
|
|
22160
22294
|
} else {
|
|
22161
|
-
callFn(args[0], args[1], shape2.element,
|
|
22295
|
+
callFn(args[0], args[1], markDomainShared(shape2.element), fromIdxAbs(), arr);
|
|
22162
22296
|
}
|
|
22163
22297
|
return undefAbs();
|
|
22164
22298
|
}
|
|
@@ -22177,7 +22311,7 @@ function invokeArrMethod(arr, method, args) {
|
|
|
22177
22311
|
if (method === "every" && !t) return boolLit(false);
|
|
22178
22312
|
}
|
|
22179
22313
|
} else {
|
|
22180
|
-
const t = callbackTruth(callFn(args[0], args[1], shape2.element,
|
|
22314
|
+
const t = callbackTruth(callFn(args[0], args[1], markDomainShared(shape2.element), fromIdxAbs(), arr));
|
|
22181
22315
|
if (t === void 0) return bool();
|
|
22182
22316
|
if (method === "some" && t) return boolLit(true);
|
|
22183
22317
|
if (method === "every" && !t) return boolLit(false);
|
|
@@ -22202,8 +22336,8 @@ function invokeArrMethod(arr, method, args) {
|
|
|
22202
22336
|
}
|
|
22203
22337
|
return undefAbs();
|
|
22204
22338
|
}
|
|
22205
|
-
const el = shape2.element;
|
|
22206
|
-
const t = callbackTruth(callFn(args[0], args[1], el,
|
|
22339
|
+
const el = markDomainShared(shape2.element);
|
|
22340
|
+
const t = callbackTruth(callFn(args[0], args[1], el, fromIdxAbs(), arr));
|
|
22207
22341
|
if (t === true) return el;
|
|
22208
22342
|
if (t === false) return undefAbs();
|
|
22209
22343
|
return joinAbs(el, undefAbs());
|
|
@@ -22220,7 +22354,7 @@ function invokeArrMethod(arr, method, args) {
|
|
|
22220
22354
|
if (undecided) return joinAbs(unknownIdx(), $lit(-1));
|
|
22221
22355
|
return $lit(-1);
|
|
22222
22356
|
}
|
|
22223
|
-
const t = callbackTruth(callFn(args[0], args[1], shape2.element,
|
|
22357
|
+
const t = callbackTruth(callFn(args[0], args[1], markDomainShared(shape2.element), fromIdxAbs(), arr));
|
|
22224
22358
|
if (t === true) return unknownIdx();
|
|
22225
22359
|
if (t === false) return $lit(-1);
|
|
22226
22360
|
return joinAbs(unknownIdx(), $lit(-1));
|
|
@@ -22241,7 +22375,7 @@ function invokeArrMethod(arr, method, args) {
|
|
|
22241
22375
|
}
|
|
22242
22376
|
return wantIndex ? $lit(-1) : undefAbs();
|
|
22243
22377
|
}
|
|
22244
|
-
const t = callbackTruth(callFn(args[0], args[1], shape2.element,
|
|
22378
|
+
const t = callbackTruth(callFn(args[0], args[1], markDomainShared(shape2.element), fromIdxAbs(), arr));
|
|
22245
22379
|
if (wantIndex) {
|
|
22246
22380
|
if (t === true) return unknownIdx();
|
|
22247
22381
|
if (t === false) return $lit(-1);
|
|
@@ -22291,7 +22425,7 @@ function invokeArrMethod(arr, method, args) {
|
|
|
22291
22425
|
}
|
|
22292
22426
|
return abstractSeqIteratorAbs(
|
|
22293
22427
|
abs(
|
|
22294
|
-
{ k: "tuple", elements: [
|
|
22428
|
+
{ k: "tuple", elements: [fromIdxAbs(), markDomainShared(shape2.element)] },
|
|
22295
22429
|
void 0,
|
|
22296
22430
|
void 0,
|
|
22297
22431
|
"partial"
|
|
@@ -28018,11 +28152,75 @@ function collectExclusionsFromPhi(phi2, id, acc) {
|
|
|
28018
28152
|
noteStrictExclusion(p, isTarget, acc);
|
|
28019
28153
|
}
|
|
28020
28154
|
}
|
|
28021
|
-
function
|
|
28155
|
+
function isNonNaN(a, phi2) {
|
|
28156
|
+
if (a.term?.op === "lit") {
|
|
28157
|
+
return typeof a.term.value === "number" && !Number.isNaN(a.term.value);
|
|
28158
|
+
}
|
|
28159
|
+
if (a.shape.k === "sum") {
|
|
28160
|
+
return a.shape.members.every(
|
|
28161
|
+
(m) => isNonNaN(m, phi2)
|
|
28162
|
+
);
|
|
28163
|
+
}
|
|
28164
|
+
const hasNonNaNFact = (p) => {
|
|
28165
|
+
if (!p) return false;
|
|
28166
|
+
if (p.op === "and") return p.args.some(hasNonNaNFact);
|
|
28167
|
+
if (p.op === "assumeFinite") {
|
|
28168
|
+
return !!a.term && termEquals(p.t, a.term);
|
|
28169
|
+
}
|
|
28170
|
+
if (p.op === "ne") {
|
|
28171
|
+
if (p.b.op === "lit" && typeof p.b.value === "number" && Number.isNaN(p.b.value) && a.term && termEquals(p.a, a.term)) {
|
|
28172
|
+
return true;
|
|
28173
|
+
}
|
|
28174
|
+
}
|
|
28175
|
+
if (p.op === "gt" || p.op === "ge" || p.op === "lt" || p.op === "le") {
|
|
28176
|
+
if (a.term && (termEquals(p.a, a.term) || termEquals(p.b, a.term))) return true;
|
|
28177
|
+
}
|
|
28178
|
+
return false;
|
|
28179
|
+
};
|
|
28180
|
+
if (hasNonNaNFact(a.pred)) return true;
|
|
28181
|
+
if (a.term?.op === "var") {
|
|
28182
|
+
const id = a.term.id;
|
|
28183
|
+
const conjs = phi2.op === "and" ? phi2.args : [phi2];
|
|
28184
|
+
for (const p of conjs) {
|
|
28185
|
+
if (p.op === "assumeFinite" && p.t.op === "var" && p.t.id === id) {
|
|
28186
|
+
return true;
|
|
28187
|
+
}
|
|
28188
|
+
if (p.op === "ne") {
|
|
28189
|
+
const b = p.b;
|
|
28190
|
+
if (b.op === "lit" && typeof b.value === "number" && Number.isNaN(b.value) && p.a.op === "var" && p.a.id === id) {
|
|
28191
|
+
return true;
|
|
28192
|
+
}
|
|
28193
|
+
}
|
|
28194
|
+
if (p.op === "gt" || p.op === "ge" || p.op === "lt" || p.op === "le") {
|
|
28195
|
+
const sides = [p.a, p.b];
|
|
28196
|
+
if (sides.some((t) => t.op === "var" && t.id === id) && sides.some((t) => t.op === "lit" && typeof t.value === "number")) {
|
|
28197
|
+
return true;
|
|
28198
|
+
}
|
|
28199
|
+
}
|
|
28200
|
+
}
|
|
28201
|
+
}
|
|
28202
|
+
return false;
|
|
28203
|
+
}
|
|
28204
|
+
function subAssumeBounds(a, b, resultTerm, assume) {
|
|
28205
|
+
const premises = assumePremiseList(assume);
|
|
28206
|
+
if (premises.length === 0) return void 0;
|
|
28207
|
+
if (!a.term || !b.term) return void 0;
|
|
28208
|
+
const facts = [];
|
|
28209
|
+
for (const p of premises) {
|
|
28210
|
+
if (p.op !== "le") continue;
|
|
28211
|
+
if (termEquals(p.a, a.term) && termEquals(p.b, b.term)) {
|
|
28212
|
+
facts.push(le(resultTerm, lit(0)));
|
|
28213
|
+
} else if (termEquals(p.a, b.term) && termEquals(p.b, a.term)) {
|
|
28214
|
+
facts.push(ge(resultTerm, lit(0)));
|
|
28215
|
+
}
|
|
28216
|
+
}
|
|
28217
|
+
return facts.length ? and(...facts) : void 0;
|
|
28218
|
+
}
|
|
28219
|
+
function sub(a, b, phi2 = pTrue, assume) {
|
|
28022
28220
|
{
|
|
28023
28221
|
const fa = builtinBrandToPrimitive(a, "number");
|
|
28024
28222
|
const fb = builtinBrandToPrimitive(b, "number");
|
|
28025
|
-
if (fa || fb) return sub(fa ?? a, fb ?? b, phi2);
|
|
28223
|
+
if (fa || fb) return sub(fa ?? a, fb ?? b, phi2, assume);
|
|
28026
28224
|
}
|
|
28027
28225
|
if (isSymbolAbs(a) || isSymbolAbs(b)) {
|
|
28028
28226
|
throw new NudoThrow(errorTypeAbs("TypeError"));
|
|
@@ -28041,6 +28239,12 @@ function sub(a, b, phi2 = pTrue) {
|
|
|
28041
28239
|
return numLit(Number(ra.value) - Number(rb.value));
|
|
28042
28240
|
}
|
|
28043
28241
|
if (isNumericLike(a) && isNumericLike(b) && a.term && b.term) {
|
|
28242
|
+
if (termEquals(a.term, b.term) && isDeterministicTerm(a.term) && // 域共享门(len-sharing.ts,第二类洞):同一带项 Abs 经元素域分发
|
|
28243
|
+
// 到两个下标位(xs.map(s=>s.length) 的 a[0] 与 a[1])——term 结构
|
|
28244
|
+
// 相同但代表不同具体值,$idx/回调实参分发位已标记,此处拒绝折叠。
|
|
28245
|
+
!isDomainShared(a) && !isDomainShared(b)) {
|
|
28246
|
+
return isNonNaN(a, phi2) || isNonNaN(b, phi2) ? numLit(0) : makeSum(numLit(NaN), numLit(0));
|
|
28247
|
+
}
|
|
28044
28248
|
const term = simplifyTerm(app("-", [a.term, b.term]));
|
|
28045
28249
|
const ab = numericBounds(a, phi2);
|
|
28046
28250
|
const bb = numericBounds(b, phi2);
|
|
@@ -28059,6 +28263,8 @@ function sub(a, b, phi2 = pTrue) {
|
|
|
28059
28263
|
facts.push(strict ? lt(term, lit(hi)) : le(term, lit(hi)));
|
|
28060
28264
|
}
|
|
28061
28265
|
}
|
|
28266
|
+
const assumePred = subAssumeBounds(a, b, term, assume);
|
|
28267
|
+
if (assumePred) facts.push(assumePred);
|
|
28062
28268
|
const conf = term.op === "lit" ? "exact" : confJoin(confJoin(a.conf, b.conf), facts.length ? "path" : "path");
|
|
28063
28269
|
return abs({ k: "prim", type: "number" }, term, facts.length ? and(...facts) : void 0, conf);
|
|
28064
28270
|
}
|
|
@@ -28071,7 +28277,7 @@ function sub(a, b, phi2 = pTrue) {
|
|
|
28071
28277
|
if (isAnyLike(a) || isAnyLike(b)) {
|
|
28072
28278
|
return toNumberResult(a, b, "-");
|
|
28073
28279
|
}
|
|
28074
|
-
const distributed = distributeSumBinOp("-", a, b, phi2, sub);
|
|
28280
|
+
const distributed = distributeSumBinOp("-", a, b, phi2, (x, y, p) => sub(x, y, p, assume));
|
|
28075
28281
|
if (distributed) return distributed;
|
|
28076
28282
|
noteAbstractArithMayThrow(a, b);
|
|
28077
28283
|
return abs({ k: "unknown" }, void 0, void 0, "partial");
|
|
@@ -28531,55 +28737,6 @@ function mathMethod(name) {
|
|
|
28531
28737
|
const impl = Math[name];
|
|
28532
28738
|
return typeof impl === "function" ? impl : void 0;
|
|
28533
28739
|
}
|
|
28534
|
-
function isNonNaN(a, phi2) {
|
|
28535
|
-
if (a.term?.op === "lit") {
|
|
28536
|
-
return typeof a.term.value === "number" && !Number.isNaN(a.term.value);
|
|
28537
|
-
}
|
|
28538
|
-
if (a.shape.k === "sum") {
|
|
28539
|
-
return a.shape.members.every(
|
|
28540
|
-
(m) => isNonNaN(m, phi2)
|
|
28541
|
-
);
|
|
28542
|
-
}
|
|
28543
|
-
const hasNonNaNFact = (p) => {
|
|
28544
|
-
if (!p) return false;
|
|
28545
|
-
if (p.op === "and") return p.args.some(hasNonNaNFact);
|
|
28546
|
-
if (p.op === "assumeFinite") {
|
|
28547
|
-
return !!a.term && termEquals(p.t, a.term);
|
|
28548
|
-
}
|
|
28549
|
-
if (p.op === "ne") {
|
|
28550
|
-
if (p.b.op === "lit" && typeof p.b.value === "number" && Number.isNaN(p.b.value) && a.term && termEquals(p.a, a.term)) {
|
|
28551
|
-
return true;
|
|
28552
|
-
}
|
|
28553
|
-
}
|
|
28554
|
-
if (p.op === "gt" || p.op === "ge" || p.op === "lt" || p.op === "le") {
|
|
28555
|
-
if (a.term && (termEquals(p.a, a.term) || termEquals(p.b, a.term))) return true;
|
|
28556
|
-
}
|
|
28557
|
-
return false;
|
|
28558
|
-
};
|
|
28559
|
-
if (hasNonNaNFact(a.pred)) return true;
|
|
28560
|
-
if (a.term?.op === "var") {
|
|
28561
|
-
const id = a.term.id;
|
|
28562
|
-
const conjs = phi2.op === "and" ? phi2.args : [phi2];
|
|
28563
|
-
for (const p of conjs) {
|
|
28564
|
-
if (p.op === "assumeFinite" && p.t.op === "var" && p.t.id === id) {
|
|
28565
|
-
return true;
|
|
28566
|
-
}
|
|
28567
|
-
if (p.op === "ne") {
|
|
28568
|
-
const b = p.b;
|
|
28569
|
-
if (b.op === "lit" && typeof b.value === "number" && Number.isNaN(b.value) && p.a.op === "var" && p.a.id === id) {
|
|
28570
|
-
return true;
|
|
28571
|
-
}
|
|
28572
|
-
}
|
|
28573
|
-
if (p.op === "gt" || p.op === "ge" || p.op === "lt" || p.op === "le") {
|
|
28574
|
-
const sides = [p.a, p.b];
|
|
28575
|
-
if (sides.some((t) => t.op === "var" && t.id === id) && sides.some((t) => t.op === "lit" && typeof t.value === "number")) {
|
|
28576
|
-
return true;
|
|
28577
|
-
}
|
|
28578
|
-
}
|
|
28579
|
-
}
|
|
28580
|
-
}
|
|
28581
|
-
return false;
|
|
28582
|
-
}
|
|
28583
28740
|
function ieeeBoundNum(n) {
|
|
28584
28741
|
return Number.isFinite(n) || n === Infinity || n === -Infinity ? n : void 0;
|
|
28585
28742
|
}
|
|
@@ -28984,6 +29141,7 @@ export {
|
|
|
28984
29141
|
withExecPhi,
|
|
28985
29142
|
currentAssumePremises,
|
|
28986
29143
|
withAssumePremises,
|
|
29144
|
+
withInvariantSpecs,
|
|
28987
29145
|
asAbsVal,
|
|
28988
29146
|
$absVal,
|
|
28989
29147
|
callAtFunctionBoundary,
|
package/dist/exec.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -255,7 +255,7 @@ declare function asAbs(v: unknown): Abs | undefined;
|
|
|
255
255
|
|
|
256
256
|
declare function add(a: Abs, b: Abs, phi?: Phi): Abs;
|
|
257
257
|
/** 减法:a - b = a + (-b),数值上做单调性 */
|
|
258
|
-
declare function sub(a: Abs, b: Abs, phi?: Phi): Abs;
|
|
258
|
+
declare function sub(a: Abs, b: Abs, phi?: Phi, assume?: Phi): Abs;
|
|
259
259
|
/** 乘法:字面量直接求值;×正数同向缩放;×负数翻转不等式;×0 归零 */
|
|
260
260
|
declare function mul(a: Abs, b: Abs, phi?: Phi): Abs;
|
|
261
261
|
/**
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
extractFileEnvNames,
|
|
15
15
|
extractFn,
|
|
16
16
|
extractFnBudget,
|
|
17
|
+
extractInvariantSegs,
|
|
17
18
|
extractMockModuleRecords,
|
|
18
19
|
extractNudoImportRecords,
|
|
19
20
|
extractNudoImports,
|
|
@@ -66,7 +67,7 @@ import {
|
|
|
66
67
|
takeInterfaceDiagsSince,
|
|
67
68
|
takeRefineDiags,
|
|
68
69
|
takeRefineDiagsSince
|
|
69
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-N7AGFMSD.js";
|
|
70
71
|
import {
|
|
71
72
|
$add,
|
|
72
73
|
$arguments,
|
|
@@ -525,8 +526,9 @@ import {
|
|
|
525
526
|
v,
|
|
526
527
|
withAssumePremises,
|
|
527
528
|
withExecPhi,
|
|
529
|
+
withInvariantSpecs,
|
|
528
530
|
withVar
|
|
529
|
-
} from "./chunk-
|
|
531
|
+
} from "./chunk-W3Z77Y2I.js";
|
|
530
532
|
|
|
531
533
|
// src/environment.ts
|
|
532
534
|
function createEnvironment(parent, bindings = /* @__PURE__ */ new Map()) {
|
|
@@ -2413,6 +2415,49 @@ function assumePremisesPhiOf(name, source, file, issues) {
|
|
|
2413
2415
|
if (preds.length === 0) return void 0;
|
|
2414
2416
|
return { phi: and(...preds), display };
|
|
2415
2417
|
}
|
|
2418
|
+
function invariantSpecsOf(name, source, file, issues) {
|
|
2419
|
+
const segs = extractInvariantSegs(source, name);
|
|
2420
|
+
if (segs.length === 0) return void 0;
|
|
2421
|
+
const meta = extractFn(source, name, file);
|
|
2422
|
+
const bindable = /* @__PURE__ */ new Set([
|
|
2423
|
+
...contractParamNameSet(meta?.formals ?? []),
|
|
2424
|
+
...meta?.params ?? [],
|
|
2425
|
+
...fnTopLevelLocalNames(meta?.body)
|
|
2426
|
+
]);
|
|
2427
|
+
const surface = [...bindable].join(", ");
|
|
2428
|
+
const specs = [];
|
|
2429
|
+
const display = [];
|
|
2430
|
+
for (const seg of segs) {
|
|
2431
|
+
if (seg.kind === "bad") {
|
|
2432
|
+
issues.push({
|
|
2433
|
+
severity: "warning",
|
|
2434
|
+
code: "nudo:invariant-invalid",
|
|
2435
|
+
message: `${name}: malformed @nudo:invariant '${seg.raw}' (expected <name><path> <rel> <number>; path = [*] or .member steps)`,
|
|
2436
|
+
actual: seg.raw,
|
|
2437
|
+
expected: "<name><path> <rel> <number>",
|
|
2438
|
+
suggestion: "loop-carried bounds: @nudo:invariant dp[*][*] >= 0 \u2014 scalar: @nudo:invariant last >= 1",
|
|
2439
|
+
fn: name
|
|
2440
|
+
});
|
|
2441
|
+
continue;
|
|
2442
|
+
}
|
|
2443
|
+
if (!bindable.has(seg.name)) {
|
|
2444
|
+
issues.push({
|
|
2445
|
+
severity: "warning",
|
|
2446
|
+
code: "nudo:invariant-invalid",
|
|
2447
|
+
message: `${name}: @nudo:invariant name not bound as a parameter or top-level local (${seg.name})`,
|
|
2448
|
+
actual: seg.name,
|
|
2449
|
+
expected: surface || "(no params/locals)",
|
|
2450
|
+
suggestion: `reference one of: ${surface || "(no params or top-level locals)"}`,
|
|
2451
|
+
fn: name
|
|
2452
|
+
});
|
|
2453
|
+
continue;
|
|
2454
|
+
}
|
|
2455
|
+
specs.push({ name: seg.name, path: seg.path, rel: seg.rel, value: seg.value });
|
|
2456
|
+
display.push(seg.display);
|
|
2457
|
+
}
|
|
2458
|
+
if (specs.length === 0) return void 0;
|
|
2459
|
+
return { specs, display };
|
|
2460
|
+
}
|
|
2416
2461
|
function checkSourceInner(filePath, source, file, phi, opts, issues, signatures, names, truncated, identityOpts = opts, depsFp, sidecarFp) {
|
|
2417
2462
|
const hasRefineDirective = stripStringsKeepComments(source).includes("@nudo:contract");
|
|
2418
2463
|
const entryNames = localNamedExports(source);
|
|
@@ -2441,7 +2486,11 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
|
|
|
2441
2486
|
};
|
|
2442
2487
|
for (const name of names) {
|
|
2443
2488
|
const assumePhi = assumePremisesPhiOf(name, source, file, issues);
|
|
2444
|
-
const
|
|
2489
|
+
const inv = invariantSpecsOf(name, source, file, issues);
|
|
2490
|
+
const runSeeded = (body) => {
|
|
2491
|
+
if (assumePhi === void 0 && inv === void 0) return body();
|
|
2492
|
+
return withAssumePremises(assumePhi?.phi ?? pTrue, () => withInvariantSpecs(inv?.specs ?? [], body));
|
|
2493
|
+
};
|
|
2445
2494
|
if (opts.skips?.has(name)) {
|
|
2446
2495
|
const declared = opts.skips.get(name) ?? null;
|
|
2447
2496
|
const meta = extractFn(source, name, file);
|
|
@@ -2676,7 +2725,8 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
|
|
|
2676
2725
|
display,
|
|
2677
2726
|
eff.returns.constraint,
|
|
2678
2727
|
g.symbolic,
|
|
2679
|
-
assumePhi?.display
|
|
2728
|
+
assumePhi?.display,
|
|
2729
|
+
inv?.display
|
|
2680
2730
|
);
|
|
2681
2731
|
if (retIssues.some((i) => i.severity === "error")) returnViolated.add(name);
|
|
2682
2732
|
issues.push(...retIssues);
|
|
@@ -2877,23 +2927,33 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
|
|
|
2877
2927
|
budget: { ...budget }
|
|
2878
2928
|
};
|
|
2879
2929
|
}
|
|
2880
|
-
function checkReturnConstraint(fnName, cName, constraint, ret, assumeDisplay) {
|
|
2930
|
+
function checkReturnConstraint(fnName, cName, constraint, ret, assumeDisplay, invariantDisplay) {
|
|
2881
2931
|
const proof = assertImplies(ret, constraint);
|
|
2882
2932
|
if (proof.status === "proved") {
|
|
2933
|
+
const relied = [];
|
|
2883
2934
|
if (assumeDisplay && assumeDisplay.length > 0) {
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2935
|
+
relied.push({
|
|
2936
|
+
severity: "info",
|
|
2937
|
+
code: "nudo:assume-relied",
|
|
2938
|
+
message: `${fnName}: return contract proved via @nudo:assume ${assumeDisplay.join("; ")}`,
|
|
2939
|
+
actual: `proof relies on user lemma${assumeDisplay.length > 1 ? "s" : ""}`,
|
|
2940
|
+
expected: "engine verifies the obligation, not the lemma itself",
|
|
2941
|
+
suggestion: "@nudo:assume is a user-supplied axiom \u2014 falsify it with concrete @nudo:case runs (nudo test)",
|
|
2942
|
+
fn: fnName
|
|
2943
|
+
});
|
|
2944
|
+
}
|
|
2945
|
+
if (invariantDisplay && invariantDisplay.length > 0) {
|
|
2946
|
+
relied.push({
|
|
2947
|
+
severity: "info",
|
|
2948
|
+
code: "nudo:invariant-relied",
|
|
2949
|
+
message: `${fnName}: return contract proved via @nudo:invariant ${invariantDisplay.join("; ")}`,
|
|
2950
|
+
actual: `proof relies on user invariant${invariantDisplay.length > 1 ? "s" : ""}`,
|
|
2951
|
+
expected: "engine verifies the obligation, not the loop invariant itself",
|
|
2952
|
+
suggestion: "@nudo:invariant is a user-supplied axiom \u2014 falsify it with concrete @nudo:case runs (nudo test)",
|
|
2953
|
+
fn: fnName
|
|
2954
|
+
});
|
|
2895
2955
|
}
|
|
2896
|
-
return
|
|
2956
|
+
return relied;
|
|
2897
2957
|
}
|
|
2898
2958
|
const push = (severity, actual, expected, suggestion) => [
|
|
2899
2959
|
{
|
package/dist/internal.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
stableAnalyzeKeySource,
|
|
22
22
|
stablePathKey,
|
|
23
23
|
stablePathKeyGraph
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-N7AGFMSD.js";
|
|
25
25
|
import {
|
|
26
26
|
$tryDigestSoft,
|
|
27
27
|
$tryMarkSoft,
|
|
@@ -169,7 +169,7 @@ import {
|
|
|
169
169
|
withEvalBudgetOverride,
|
|
170
170
|
withFnBudgetOverride,
|
|
171
171
|
wrapPromise
|
|
172
|
-
} from "./chunk-
|
|
172
|
+
} from "./chunk-W3Z77Y2I.js";
|
|
173
173
|
|
|
174
174
|
// src/algebra/inlay.ts
|
|
175
175
|
function listFunctions(source) {
|