nudojs 1.3.20 → 1.3.21
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/index.js +221 -36
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -19039,6 +19039,18 @@ function withExecPhi(p, body) {
|
|
|
19039
19039
|
phi = prev;
|
|
19040
19040
|
}
|
|
19041
19041
|
}
|
|
19042
|
+
function currentAssumePremises() {
|
|
19043
|
+
return assumePremises;
|
|
19044
|
+
}
|
|
19045
|
+
function withAssumePremises(p, body) {
|
|
19046
|
+
const prev = assumePremises;
|
|
19047
|
+
assumePremises = p;
|
|
19048
|
+
try {
|
|
19049
|
+
return body();
|
|
19050
|
+
} finally {
|
|
19051
|
+
assumePremises = prev;
|
|
19052
|
+
}
|
|
19053
|
+
}
|
|
19042
19054
|
function setHostGlobalFnCall(bridge) {
|
|
19043
19055
|
hostGlobalFnCall = bridge;
|
|
19044
19056
|
}
|
|
@@ -19075,6 +19087,12 @@ function $absVal(v2) {
|
|
|
19075
19087
|
return asAbsVal(v2);
|
|
19076
19088
|
}
|
|
19077
19089
|
function callAtFunctionBoundary(body) {
|
|
19090
|
+
if (assumePremises !== pTrue) {
|
|
19091
|
+
return withAssumePremises(pTrue, () => boundaryBody(body));
|
|
19092
|
+
}
|
|
19093
|
+
return boundaryBody(body);
|
|
19094
|
+
}
|
|
19095
|
+
function boundaryBody(body) {
|
|
19078
19096
|
let forwarded = [];
|
|
19079
19097
|
try {
|
|
19080
19098
|
return runWithLoopExits(() => {
|
|
@@ -19383,7 +19401,7 @@ function $isBreakTo(e, label) {
|
|
|
19383
19401
|
function $rethrowIfNudoReturn(e) {
|
|
19384
19402
|
if (isNudoReturn(e)) throw e;
|
|
19385
19403
|
}
|
|
19386
|
-
var phi, noBody2, hostGlobalFnCall, newTargetStack, loopExitsAls, throwExitsAls, tryMarksAls, softFrameActiveAls, NudoReturn, loopSignalFromAbstractArm, NudoLoopSignal;
|
|
19404
|
+
var phi, assumePremises, noBody2, hostGlobalFnCall, newTargetStack, loopExitsAls, throwExitsAls, tryMarksAls, softFrameActiveAls, NudoReturn, loopSignalFromAbstractArm, NudoLoopSignal;
|
|
19387
19405
|
var init_state = __esm({
|
|
19388
19406
|
"../core/src/algebra/exec/runtime/state.ts"() {
|
|
19389
19407
|
"use strict";
|
|
@@ -19396,6 +19414,7 @@ var init_state = __esm({
|
|
|
19396
19414
|
init_containers();
|
|
19397
19415
|
init_objects();
|
|
19398
19416
|
phi = pTrue;
|
|
19417
|
+
assumePremises = pTrue;
|
|
19399
19418
|
noBody2 = { type: "BlockStatement", body: [], directives: [] };
|
|
19400
19419
|
newTargetStack = [];
|
|
19401
19420
|
loopExitsAls = new AsyncLocalStorage3();
|
|
@@ -23800,7 +23819,12 @@ function emitArrMutatorRebinds(expr, opts, pad) {
|
|
|
23800
23819
|
if (node.type === "CallExpression" && node.callee?.type === "MemberExpression" && node.callee.computed !== true && node.callee.property?.type === "Identifier" && ARR_MUTATOR_NAMES.has(node.callee.property.name)) {
|
|
23801
23820
|
const methodName = node.callee.property.name;
|
|
23802
23821
|
const argSrcs = (node.arguments ?? []).map(
|
|
23803
|
-
(a) =>
|
|
23822
|
+
(a) => (
|
|
23823
|
+
// spread 实参展开(issue #153):$elems → JS Abs[],数组字面量 JS
|
|
23824
|
+
// spread 平铺(与 expr.ts 普通调用同口径);此前折 $unknown() 单参
|
|
23825
|
+
// 让重绑行以 unknown 实参执行真实变更,污染容器元素域
|
|
23826
|
+
a.type === "SpreadElement" ? `...$elems(${emitTranspileExpression(a.argument, opts)})` : isExpression(a) ? emitTranspileExpression(a, opts) : "$unknown()"
|
|
23827
|
+
)
|
|
23804
23828
|
).join(", ");
|
|
23805
23829
|
const objNode = node.callee.object;
|
|
23806
23830
|
if (objNode.type === "Identifier") {
|
|
@@ -24947,7 +24971,12 @@ ${pad}export default ${name};`;
|
|
|
24947
24971
|
const methodName = expr.callee.property.name;
|
|
24948
24972
|
if (["push", "unshift", "splice", "pop", "shift", "reverse", "sort"].includes(methodName)) {
|
|
24949
24973
|
const argSrcs = expr.arguments.map(
|
|
24950
|
-
(a) =>
|
|
24974
|
+
(a) => (
|
|
24975
|
+
// spread 实参展开(issue #153):$elems → JS Abs[],数组字面量 JS spread
|
|
24976
|
+
// 平铺(与普通调用 $callNamed/$invoke 同口径,DEC-006 K5);
|
|
24977
|
+
// 此前折 $unknown() 单参会污染容器元素域
|
|
24978
|
+
a.type === "SpreadElement" ? `...$elems(${emitTranspileExpression(a.argument, opts)})` : emitTranspileExpression(a, opts)
|
|
24979
|
+
)
|
|
24951
24980
|
).join(", ");
|
|
24952
24981
|
const objNode = expr.callee.object;
|
|
24953
24982
|
if (objNode.type === "Identifier") {
|
|
@@ -27251,7 +27280,7 @@ function $mul(a, b) {
|
|
|
27251
27280
|
return mul(a, b, currentExecPhi());
|
|
27252
27281
|
}
|
|
27253
27282
|
function $div(a, b) {
|
|
27254
|
-
return div(a, b, currentExecPhi());
|
|
27283
|
+
return div(a, b, currentExecPhi(), currentAssumePremises());
|
|
27255
27284
|
}
|
|
27256
27285
|
function $mod(a, b) {
|
|
27257
27286
|
return mod(a, b, currentExecPhi());
|
|
@@ -31822,6 +31851,7 @@ __export(runtime_exports, {
|
|
|
31822
31851
|
callAtFunctionBoundary: () => callAtFunctionBoundary,
|
|
31823
31852
|
clearStaleTermPred: () => clearStaleTermPred,
|
|
31824
31853
|
confPartial: () => confPartial,
|
|
31854
|
+
currentAssumePremises: () => currentAssumePremises,
|
|
31825
31855
|
currentExecPhi: () => currentExecPhi,
|
|
31826
31856
|
fillTuple: () => fillTuple,
|
|
31827
31857
|
isArrMutator: () => isArrMutator,
|
|
@@ -31839,6 +31869,7 @@ __export(runtime_exports, {
|
|
|
31839
31869
|
runWithLoopExits: () => runWithLoopExits,
|
|
31840
31870
|
takeLoopExits: () => takeLoopExits,
|
|
31841
31871
|
takeThrowExits: () => takeThrowExits,
|
|
31872
|
+
withAssumePremises: () => withAssumePremises,
|
|
31842
31873
|
withExecPhi: () => withExecPhi,
|
|
31843
31874
|
withNewTargetReset: () => withNewTargetReset
|
|
31844
31875
|
});
|
|
@@ -31932,6 +31963,15 @@ function isNudoConstraint(x) {
|
|
|
31932
31963
|
function isIntFlag(c) {
|
|
31933
31964
|
return c.int === true || intFlaggedBuilders.has(c);
|
|
31934
31965
|
}
|
|
31966
|
+
function finiteBoundArg(method, n) {
|
|
31967
|
+
if (typeof n !== "number" || !Number.isFinite(n)) {
|
|
31968
|
+
const shown = typeof n === "string" ? JSON.stringify(n) : String(n);
|
|
31969
|
+
throw new Error(
|
|
31970
|
+
`nudo ${method}(): finite numeric bound expected, got ${shown} \u2014 variable references are not supported; express cross-variable lemmas with @nudo:assume (issue #152)`
|
|
31971
|
+
);
|
|
31972
|
+
}
|
|
31973
|
+
return n;
|
|
31974
|
+
}
|
|
31935
31975
|
function makeBuilder(prim2, preds, extra) {
|
|
31936
31976
|
const fields = extra?.fields;
|
|
31937
31977
|
const element = extra?.element;
|
|
@@ -31956,17 +31996,16 @@ function makeBuilder(prim2, preds, extra) {
|
|
|
31956
31996
|
/* @__PURE__ */ Object.create(null),
|
|
31957
31997
|
base,
|
|
31958
31998
|
{
|
|
31959
|
-
gt: (n) => add2(gt(selfTerm(), lit(n))),
|
|
31960
|
-
ge: (n) => add2(ge(selfTerm(), lit(n))),
|
|
31961
|
-
lt: (n) => add2(lt(selfTerm(), lit(n))),
|
|
31962
|
-
le: (n) => add2(le(selfTerm(), lit(n))),
|
|
31999
|
+
gt: (n) => add2(gt(selfTerm(), lit(finiteBoundArg("gt", n)))),
|
|
32000
|
+
ge: (n) => add2(ge(selfTerm(), lit(finiteBoundArg("ge", n)))),
|
|
32001
|
+
lt: (n) => add2(lt(selfTerm(), lit(finiteBoundArg("lt", n)))),
|
|
32002
|
+
le: (n) => add2(le(selfTerm(), lit(finiteBoundArg("le", n)))),
|
|
31963
32003
|
int: () => makeBuilder(prim2 ?? "number", preds, { ...extra, int: true }),
|
|
31964
|
-
min: (n) => add2(ge(lenTerm(selfTerm()), lit(n))),
|
|
31965
|
-
max: (n) => add2(le(lenTerm(selfTerm()), lit(n))),
|
|
32004
|
+
min: (n) => add2(ge(lenTerm(selfTerm()), lit(finiteBoundArg("min", n)))),
|
|
32005
|
+
max: (n) => add2(le(lenTerm(selfTerm()), lit(finiteBoundArg("max", n)))),
|
|
31966
32006
|
length: (n) => add2(and(ge(lenTerm(selfTerm()), lit(n)), le(lenTerm(selfTerm()), lit(n)))),
|
|
31967
32007
|
shift: (n) => {
|
|
31968
|
-
|
|
31969
|
-
throw new Error("nudo shift(): offset must be a finite number");
|
|
32008
|
+
finiteBoundArg("shift", n);
|
|
31970
32009
|
if (fields || element || members || fnSlot || lazySlot)
|
|
31971
32010
|
throw new Error("nudo shift(): only numeric scalar constraint chains are allowed (shape/array/union/fn are not supported)");
|
|
31972
32011
|
if (prim2 !== void 0 && prim2 !== "number")
|
|
@@ -37078,7 +37117,9 @@ function callTranspiledExportFullInner(exports, name, args, opts) {
|
|
|
37078
37117
|
return runWithLoopExits(() => {
|
|
37079
37118
|
try {
|
|
37080
37119
|
const invoke = () => withNewTargetReset(() => fn2(...callArgs));
|
|
37081
|
-
const
|
|
37120
|
+
const ap = opts?.assumePremises;
|
|
37121
|
+
const seeded = ap !== void 0 ? () => withAssumePremises(ap, invoke) : invoke;
|
|
37122
|
+
const r = opts?.phi ? withExecPhi(opts.phi, seeded) : seeded();
|
|
37082
37123
|
drainPromiseMicros();
|
|
37083
37124
|
if (!isAbsVal(r)) return joinControlExits(asAbsVal(r));
|
|
37084
37125
|
return joinControlExits(r);
|
|
@@ -40816,7 +40857,50 @@ function mul(a, b, phi2 = pTrue) {
|
|
|
40816
40857
|
noteAbstractArithMayThrow(a, b);
|
|
40817
40858
|
return abs({ k: "unknown" }, void 0, void 0, "partial");
|
|
40818
40859
|
}
|
|
40819
|
-
function
|
|
40860
|
+
function assumePremiseList(assume) {
|
|
40861
|
+
if (!assume || assume.op === "true") return [];
|
|
40862
|
+
const out = [];
|
|
40863
|
+
const walk = (p) => {
|
|
40864
|
+
if (p.op === "and") {
|
|
40865
|
+
p.args.forEach(walk);
|
|
40866
|
+
return;
|
|
40867
|
+
}
|
|
40868
|
+
if (p.op !== "true") out.push(p);
|
|
40869
|
+
};
|
|
40870
|
+
walk(assume);
|
|
40871
|
+
return out;
|
|
40872
|
+
}
|
|
40873
|
+
function divAssumeBounds(a, b, resultTerm, phi2, assume) {
|
|
40874
|
+
const premises = assumePremiseList(assume);
|
|
40875
|
+
if (premises.length === 0) return void 0;
|
|
40876
|
+
if (!a.term || !b.term) return void 0;
|
|
40877
|
+
const bb = numericBounds(b, phi2);
|
|
40878
|
+
const posDen = bb?.lo !== void 0 && (bb.lo.value > 0 || bb.lo.value === 0 && bb.lo.strict);
|
|
40879
|
+
const negDen = bb?.hi !== void 0 && (bb.hi.value < 0 || bb.hi.value === 0 && bb.hi.strict);
|
|
40880
|
+
if (!posDen && !negDen) return void 0;
|
|
40881
|
+
let aLeB = false;
|
|
40882
|
+
let bLeA = false;
|
|
40883
|
+
for (const p of premises) {
|
|
40884
|
+
if (p.op !== "le") continue;
|
|
40885
|
+
if (termEquals(p.a, a.term) && termEquals(p.b, b.term)) aLeB = true;
|
|
40886
|
+
else if (termEquals(p.a, b.term) && termEquals(p.b, a.term)) bLeA = true;
|
|
40887
|
+
}
|
|
40888
|
+
if (!aLeB && !bLeA) return void 0;
|
|
40889
|
+
const facts = [];
|
|
40890
|
+
if (aLeB) facts.push(posDen ? le(resultTerm, lit(1)) : ge(resultTerm, lit(1)));
|
|
40891
|
+
if (bLeA) facts.push(posDen ? ge(resultTerm, lit(1)) : le(resultTerm, lit(1)));
|
|
40892
|
+
const ab = numericBounds(a, phi2);
|
|
40893
|
+
if (ab?.lo !== void 0 && (ab.lo.value > 0 || ab.lo.value === 0)) {
|
|
40894
|
+
if (posDen) facts.push(ab.lo.strict ? gt(resultTerm, lit(0)) : ge(resultTerm, lit(0)));
|
|
40895
|
+
else facts.push(ab.lo.strict ? lt(resultTerm, lit(0)) : le(resultTerm, lit(0)));
|
|
40896
|
+
}
|
|
40897
|
+
if (ab?.hi !== void 0 && (ab.hi.value < 0 || ab.hi.value === 0)) {
|
|
40898
|
+
if (posDen) facts.push(ab.hi.strict ? lt(resultTerm, lit(0)) : le(resultTerm, lit(0)));
|
|
40899
|
+
else facts.push(ab.hi.strict ? gt(resultTerm, lit(0)) : ge(resultTerm, lit(0)));
|
|
40900
|
+
}
|
|
40901
|
+
return facts.length ? and(...facts) : void 0;
|
|
40902
|
+
}
|
|
40903
|
+
function div(a, b, phi2 = pTrue, assume) {
|
|
40820
40904
|
{
|
|
40821
40905
|
const fa = builtinBrandToPrimitive(a, "number");
|
|
40822
40906
|
const fb = builtinBrandToPrimitive(b, "number");
|
|
@@ -40871,7 +40955,13 @@ function div(a, b, phi2 = pTrue) {
|
|
|
40871
40955
|
term.op === "lit" ? "exact" : confJoin(confJoin(a.conf, b.conf), "path")
|
|
40872
40956
|
);
|
|
40873
40957
|
}
|
|
40874
|
-
|
|
40958
|
+
const assumePred = divAssumeBounds(a, b, term, phi2, assume);
|
|
40959
|
+
return abs(
|
|
40960
|
+
num().shape,
|
|
40961
|
+
term,
|
|
40962
|
+
assumePred,
|
|
40963
|
+
confJoin(confJoin(a.conf, b.conf), "path")
|
|
40964
|
+
);
|
|
40875
40965
|
}
|
|
40876
40966
|
if (isNumericLike(a) && isNumericLike(b)) {
|
|
40877
40967
|
return abs(num().shape, void 0, void 0, "partial");
|
|
@@ -40882,7 +40972,7 @@ function div(a, b, phi2 = pTrue) {
|
|
|
40882
40972
|
if (isAnyLike(a) || isAnyLike(b)) {
|
|
40883
40973
|
return toNumberResult(a, b, "/");
|
|
40884
40974
|
}
|
|
40885
|
-
const distributed = distributeSumBinOp("/", a, b, phi2, div);
|
|
40975
|
+
const distributed = distributeSumBinOp("/", a, b, phi2, (x, y, p) => div(x, y, p, assume));
|
|
40886
40976
|
if (distributed) return distributed;
|
|
40887
40977
|
noteAbstractArithMayThrow(a, b);
|
|
40888
40978
|
return abs({ k: "unknown" }, void 0, void 0, "partial");
|
|
@@ -41692,6 +41782,14 @@ function scanContractSegments(lines) {
|
|
|
41692
41782
|
}
|
|
41693
41783
|
return reqs;
|
|
41694
41784
|
}
|
|
41785
|
+
function scanAssumeSegments(lines) {
|
|
41786
|
+
const out = [];
|
|
41787
|
+
for (const line of lines) {
|
|
41788
|
+
const m = line.match(/^@nudo:assume\s+(.+)$/);
|
|
41789
|
+
if (m) out.push(m[1].trim().replace(/\*\/$/, "").trim());
|
|
41790
|
+
}
|
|
41791
|
+
return out;
|
|
41792
|
+
}
|
|
41695
41793
|
function scanThrowsDecl(lines) {
|
|
41696
41794
|
const kinds = /* @__PURE__ */ new Set();
|
|
41697
41795
|
let any2 = false;
|
|
@@ -42566,6 +42664,24 @@ function extractRefineLines(source, fnName) {
|
|
|
42566
42664
|
const lines = fnDirectiveCommentLines(source, fnName);
|
|
42567
42665
|
return scanContractSegments(lines);
|
|
42568
42666
|
}
|
|
42667
|
+
function extractAssumeSegs(source, fnName) {
|
|
42668
|
+
if (!stripStringsKeepComments(source).includes("@nudo:assume")) return [];
|
|
42669
|
+
const out = [];
|
|
42670
|
+
for (const seg of scanAssumeSegments(fnDirectiveCommentLines(source, fnName))) {
|
|
42671
|
+
const geSeg = seg.match(/^([A-Za-z_$][\w$]*)\s*>=\s*([A-Za-z_$][\w$]*)$/);
|
|
42672
|
+
if (geSeg) {
|
|
42673
|
+
out.push({ kind: "ok", lhs: geSeg[2], rhs: geSeg[1] });
|
|
42674
|
+
continue;
|
|
42675
|
+
}
|
|
42676
|
+
const leSeg = seg.match(/^([A-Za-z_$][\w$]*)\s*<=\s*([A-Za-z_$][\w$]*)$/);
|
|
42677
|
+
if (leSeg) {
|
|
42678
|
+
out.push({ kind: "ok", lhs: leSeg[1], rhs: leSeg[2] });
|
|
42679
|
+
continue;
|
|
42680
|
+
}
|
|
42681
|
+
out.push({ kind: "bad", raw: seg });
|
|
42682
|
+
}
|
|
42683
|
+
return out;
|
|
42684
|
+
}
|
|
42569
42685
|
function extractRefinesFromSource(source, fnName, opts = {}) {
|
|
42570
42686
|
if (!stripStringsKeepComments(source).includes("@nudo:contract")) return [];
|
|
42571
42687
|
const constraints = collectConstraints(source, opts);
|
|
@@ -45369,7 +45485,7 @@ function flattenSumArms(ret) {
|
|
|
45369
45485
|
return out;
|
|
45370
45486
|
}
|
|
45371
45487
|
function isWidenedSumArm(m, parent) {
|
|
45372
|
-
return parent.conf === "partial" && m.shape.k === "prim" && !m.term && (!m.pred || m.pred.op === "true");
|
|
45488
|
+
return (parent.conf === "partial" || m.conf === "widened") && m.shape.k === "prim" && !m.term && (!m.pred || m.pred.op === "true");
|
|
45373
45489
|
}
|
|
45374
45490
|
function provesBarePrimDomain(arm, constraint) {
|
|
45375
45491
|
if (constraint.members) {
|
|
@@ -45396,7 +45512,7 @@ function assertImplies(ret, constraint, opts) {
|
|
|
45396
45512
|
const phi2 = opts?.phi ?? pTrue;
|
|
45397
45513
|
if (ret.shape.k === "never") return proved();
|
|
45398
45514
|
if (isAnyConstraint(constraint)) return proved();
|
|
45399
|
-
if (isValueSetUnknown(ret)) {
|
|
45515
|
+
if (isValueSetUnknown(ret) && ret.conf !== "widened") {
|
|
45400
45516
|
return unprovable(
|
|
45401
45517
|
ret.shape.k === "any" ? "any does not establish the return contract" : "inference failure (unknown/opaque) \u2014 cannot prove the return contract"
|
|
45402
45518
|
);
|
|
@@ -45452,6 +45568,9 @@ function assertImpliesSingle(arm, constraint, opts, phi2) {
|
|
|
45452
45568
|
return constraintAdmitsNullish(constraint) ? proved() : disproved(`nullish return \u22AD ${formatConstraint(constraint)}`);
|
|
45453
45569
|
}
|
|
45454
45570
|
if (isValueSetUnknown(arm)) {
|
|
45571
|
+
if (arm.conf === "widened" && arm.shape.k === "prim" && !arm.term && (!arm.pred || arm.pred.op === "true") && provesBarePrimDomain(arm, constraint)) {
|
|
45572
|
+
return proved();
|
|
45573
|
+
}
|
|
45455
45574
|
return unprovable(
|
|
45456
45575
|
arm.shape.k === "any" ? "any does not establish the return contract" : "inference failure (unknown/opaque) \u2014 cannot prove the return contract"
|
|
45457
45576
|
);
|
|
@@ -47509,6 +47628,7 @@ function numericBound(p, self) {
|
|
|
47509
47628
|
if (p.op !== "gt" && p.op !== "ge" && p.op !== "lt" && p.op !== "le") return void 0;
|
|
47510
47629
|
if (!self) return void 0;
|
|
47511
47630
|
if (termEquals(p.a, self) && p.b.op === "lit" && typeof p.b.value === "number") {
|
|
47631
|
+
if (!Number.isFinite(p.b.value)) return void 0;
|
|
47512
47632
|
return { op: p.op, n: p.b.value };
|
|
47513
47633
|
}
|
|
47514
47634
|
return void 0;
|
|
@@ -48578,6 +48698,49 @@ function sidecarDiagIssues(diags) {
|
|
|
48578
48698
|
}
|
|
48579
48699
|
return out;
|
|
48580
48700
|
}
|
|
48701
|
+
function assumePremisesPhiOf(name, source, file, issues) {
|
|
48702
|
+
const segs = extractAssumeSegs(source, name);
|
|
48703
|
+
if (segs.length === 0) return void 0;
|
|
48704
|
+
const meta = extractFn(source, name, file);
|
|
48705
|
+
const bindable = /* @__PURE__ */ new Set([
|
|
48706
|
+
...contractParamNameSet(meta?.formals ?? []),
|
|
48707
|
+
...meta?.params ?? []
|
|
48708
|
+
]);
|
|
48709
|
+
const surface = [...bindable].join(", ");
|
|
48710
|
+
const preds = [];
|
|
48711
|
+
const display = [];
|
|
48712
|
+
for (const seg of segs) {
|
|
48713
|
+
if (seg.kind === "bad") {
|
|
48714
|
+
issues.push({
|
|
48715
|
+
severity: "error",
|
|
48716
|
+
code: "nudo:assume-invalid",
|
|
48717
|
+
message: `${name}: malformed @nudo:assume '${seg.raw}' (expected <param> <= <param> or <param> >= <param>; constant bounds belong to sidecar contracts)`,
|
|
48718
|
+
actual: seg.raw,
|
|
48719
|
+
expected: "<param> <= <param>",
|
|
48720
|
+
suggestion: "cross-variable lemmas: @nudo:assume d <= max \u2014 constant bounds: sidecar number().le(10)",
|
|
48721
|
+
fn: name
|
|
48722
|
+
});
|
|
48723
|
+
continue;
|
|
48724
|
+
}
|
|
48725
|
+
const bad = [seg.lhs, seg.rhs].filter((n) => !bindable.has(n));
|
|
48726
|
+
if (bad.length > 0) {
|
|
48727
|
+
issues.push({
|
|
48728
|
+
severity: "error",
|
|
48729
|
+
code: "nudo:assume-invalid",
|
|
48730
|
+
message: `${name}: @nudo:assume parameter name(s) not in the formal parameter list (${bad.join(", ")})`,
|
|
48731
|
+
actual: bad.join(", "),
|
|
48732
|
+
expected: surface || "(no params)",
|
|
48733
|
+
suggestion: `reference one of: ${surface || "(function has no params)"}`,
|
|
48734
|
+
fn: name
|
|
48735
|
+
});
|
|
48736
|
+
continue;
|
|
48737
|
+
}
|
|
48738
|
+
preds.push(le(v(seg.lhs), v(seg.rhs)));
|
|
48739
|
+
display.push(`${seg.lhs} <= ${seg.rhs}`);
|
|
48740
|
+
}
|
|
48741
|
+
if (preds.length === 0) return void 0;
|
|
48742
|
+
return { phi: and(...preds), display };
|
|
48743
|
+
}
|
|
48581
48744
|
function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures, names2, truncated, identityOpts = opts, depsFp, sidecarFp) {
|
|
48582
48745
|
const hasRefineDirective = stripStringsKeepComments(source).includes("@nudo:contract");
|
|
48583
48746
|
const entryNames = localNamedExports(source);
|
|
@@ -48605,6 +48768,8 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
|
|
|
48605
48768
|
return eff;
|
|
48606
48769
|
};
|
|
48607
48770
|
for (const name of names2) {
|
|
48771
|
+
const assumePhi = assumePremisesPhiOf(name, source, file, issues);
|
|
48772
|
+
const runSeeded = (body) => assumePhi === void 0 ? body() : withAssumePremises(assumePhi.phi, body);
|
|
48608
48773
|
if (opts.skips?.has(name)) {
|
|
48609
48774
|
const declared = opts.skips.get(name) ?? null;
|
|
48610
48775
|
const meta = extractFn(source, name, file);
|
|
@@ -48649,19 +48814,21 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
|
|
|
48649
48814
|
}
|
|
48650
48815
|
continue;
|
|
48651
48816
|
}
|
|
48652
|
-
const g =
|
|
48653
|
-
|
|
48654
|
-
|
|
48655
|
-
|
|
48656
|
-
|
|
48657
|
-
|
|
48658
|
-
|
|
48659
|
-
|
|
48660
|
-
|
|
48661
|
-
|
|
48662
|
-
|
|
48663
|
-
|
|
48664
|
-
|
|
48817
|
+
const g = runSeeded(
|
|
48818
|
+
() => generalizeFromAst(name, source, {
|
|
48819
|
+
file,
|
|
48820
|
+
refine: {
|
|
48821
|
+
loadModule: refineLoad,
|
|
48822
|
+
fromFile: refineFrom,
|
|
48823
|
+
...autoBind !== void 0 ? { autoBind } : {},
|
|
48824
|
+
...projectDir !== void 0 ? { projectDir } : {}
|
|
48825
|
+
},
|
|
48826
|
+
depsFp,
|
|
48827
|
+
sidecarFp,
|
|
48828
|
+
modules: opts.modules,
|
|
48829
|
+
...opts.inject ? { inject: opts.inject } : {}
|
|
48830
|
+
})
|
|
48831
|
+
);
|
|
48665
48832
|
if (!g) {
|
|
48666
48833
|
const isEntryCandidate = entryNames.has(name) || name === "default" || entryNames.has("default") && isDefaultExportName(source, name);
|
|
48667
48834
|
if (isEntryCandidate && entryThrowsMode !== "off") {
|
|
@@ -48721,7 +48888,7 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
|
|
|
48721
48888
|
const isEntry = entryNames.has(name) || name === "default" || entryNames.has("default") && isDefaultExportName(source, name) || name.includes(".") && entryNames.has(name);
|
|
48722
48889
|
let throwsDisplay;
|
|
48723
48890
|
if (isEntry && entryThrowsMode !== "off") {
|
|
48724
|
-
const effects = collectEntryMayThrows(source, name, g, file, phi2, opts);
|
|
48891
|
+
const effects = runSeeded(() => collectEntryMayThrows(source, name, g, file, phi2, opts));
|
|
48725
48892
|
throwsDisplay = formatThrowsAbs(mayThrowEffectsToAbs(effects));
|
|
48726
48893
|
const declared = extractDeclaredThrows(source, name) ?? effectiveInterfaceCached(name)?.throws?.kinds;
|
|
48727
48894
|
const remaining = filterGateThrows(effects, declared, ignoreThrows);
|
|
@@ -48742,7 +48909,7 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
|
|
|
48742
48909
|
});
|
|
48743
48910
|
}
|
|
48744
48911
|
} else if (isEntry) {
|
|
48745
|
-
const effects = collectEntryMayThrows(source, name, g, file, phi2, opts);
|
|
48912
|
+
const effects = runSeeded(() => collectEntryMayThrows(source, name, g, file, phi2, opts));
|
|
48746
48913
|
throwsDisplay = formatThrowsAbs(mayThrowEffectsToAbs(effects));
|
|
48747
48914
|
}
|
|
48748
48915
|
const fmt = formatSigCached(g.symbolic, name);
|
|
@@ -48836,7 +49003,8 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
|
|
|
48836
49003
|
name,
|
|
48837
49004
|
display,
|
|
48838
49005
|
eff.returns.constraint,
|
|
48839
|
-
g.symbolic
|
|
49006
|
+
g.symbolic,
|
|
49007
|
+
assumePhi?.display
|
|
48840
49008
|
);
|
|
48841
49009
|
if (retIssues.some((i) => i.severity === "error")) returnViolated.add(name);
|
|
48842
49010
|
issues.push(...retIssues);
|
|
@@ -49032,9 +49200,24 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
|
|
|
49032
49200
|
}
|
|
49033
49201
|
};
|
|
49034
49202
|
}
|
|
49035
|
-
function checkReturnConstraint(fnName, cName, constraint, ret) {
|
|
49203
|
+
function checkReturnConstraint(fnName, cName, constraint, ret, assumeDisplay) {
|
|
49036
49204
|
const proof = assertImplies(ret, constraint);
|
|
49037
|
-
if (proof.status === "proved")
|
|
49205
|
+
if (proof.status === "proved") {
|
|
49206
|
+
if (assumeDisplay && assumeDisplay.length > 0) {
|
|
49207
|
+
return [
|
|
49208
|
+
{
|
|
49209
|
+
severity: "info",
|
|
49210
|
+
code: "nudo:assume-relied",
|
|
49211
|
+
message: `${fnName}: return contract proved via @nudo:assume ${assumeDisplay.join("; ")}`,
|
|
49212
|
+
actual: `proof relies on user lemma${assumeDisplay.length > 1 ? "s" : ""}`,
|
|
49213
|
+
expected: "engine verifies the obligation, not the lemma itself",
|
|
49214
|
+
suggestion: "@nudo:assume is a user-supplied axiom \u2014 falsify it with concrete @nudo:case runs (nudo test)",
|
|
49215
|
+
fn: fnName
|
|
49216
|
+
}
|
|
49217
|
+
];
|
|
49218
|
+
}
|
|
49219
|
+
return [];
|
|
49220
|
+
}
|
|
49038
49221
|
const push = (severity, actual, expected, suggestion) => [
|
|
49039
49222
|
{
|
|
49040
49223
|
severity,
|
|
@@ -49082,6 +49265,7 @@ var init_check = __esm({
|
|
|
49082
49265
|
init_abs();
|
|
49083
49266
|
init_abs();
|
|
49084
49267
|
init_pred();
|
|
49268
|
+
init_term();
|
|
49085
49269
|
init_format();
|
|
49086
49270
|
init_scan();
|
|
49087
49271
|
init_check_interface_drift();
|
|
@@ -49090,6 +49274,7 @@ var init_check = __esm({
|
|
|
49090
49274
|
init_check_assign();
|
|
49091
49275
|
init_check_case_scan();
|
|
49092
49276
|
init_run();
|
|
49277
|
+
init_runtime();
|
|
49093
49278
|
init_calls();
|
|
49094
49279
|
init_may_throw2();
|
|
49095
49280
|
init_check_memo();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nudojs",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.21",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=20"
|
|
6
6
|
},
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@babel/generator": "^8.0.6",
|
|
48
48
|
"@babel/types": "^8.0.6",
|
|
49
|
-
"@nudojs/core": "1.
|
|
50
|
-
"@nudojs/env": "0.4.
|
|
51
|
-
"@nudojs/harvester": "0.3.
|
|
52
|
-
"@nudojs/parser": "1.4.
|
|
53
|
-
"@nudojs/service": "1.6.
|
|
49
|
+
"@nudojs/core": "1.11.0",
|
|
50
|
+
"@nudojs/env": "0.4.35",
|
|
51
|
+
"@nudojs/harvester": "0.3.21",
|
|
52
|
+
"@nudojs/parser": "1.4.17",
|
|
53
|
+
"@nudojs/service": "1.6.20",
|
|
54
54
|
"commander": "^15.0.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|