nudojs 1.3.19 → 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 +251 -52
- 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());
|
|
@@ -29873,14 +29902,32 @@ function $concat(a, b) {
|
|
|
29873
29902
|
if (isMapAbs(x)) return mapEntriesAbs(x);
|
|
29874
29903
|
const mi = matchIterElements(x);
|
|
29875
29904
|
if (mi) return mi;
|
|
29905
|
+
if (x.shape.k === "brand") return expand(x.shape.shape);
|
|
29906
|
+
if (x.shape.k === "tuple") return [...x.shape.elements];
|
|
29876
29907
|
return null;
|
|
29877
29908
|
};
|
|
29909
|
+
const sideEl = (x, expanded) => {
|
|
29910
|
+
if (expanded) {
|
|
29911
|
+
return expanded.length ? expanded.reduce((u, y) => joinAbs(u, y)) : abs({ k: "never" }, void 0, void 0, "exact");
|
|
29912
|
+
}
|
|
29913
|
+
if (x?.shape?.k === "any") return anyMemberResult();
|
|
29914
|
+
if (x?.shape?.k === "tuple") {
|
|
29915
|
+
const els = x.shape.elements;
|
|
29916
|
+
return els.length ? els.reduce((u, y) => joinAbs(u, y)) : abs({ k: "never" }, void 0, void 0, "exact");
|
|
29917
|
+
}
|
|
29918
|
+
if (x?.shape?.k === "prim" && x.shape.type === "string") {
|
|
29919
|
+
return abs({ k: "prim", type: "string" }, void 0, void 0, "path");
|
|
29920
|
+
}
|
|
29921
|
+
if (x?.shape?.k === "arr") return x.shape.element;
|
|
29922
|
+
if (x?.shape?.k === "brand") return sideEl(x.shape.shape, null);
|
|
29923
|
+
return unknown;
|
|
29924
|
+
};
|
|
29878
29925
|
if (as.k === "tuple") {
|
|
29879
29926
|
const be = expand(b);
|
|
29880
29927
|
if (be) {
|
|
29881
29928
|
return tupleOrWiden([...as.elements, ...be], confJoin(a.conf, b.conf));
|
|
29882
29929
|
}
|
|
29883
|
-
const bEl = b
|
|
29930
|
+
const bEl = sideEl(b, be);
|
|
29884
29931
|
return abs(
|
|
29885
29932
|
{ k: "arr", element: [...as.elements, bEl].reduce((x, y) => joinAbs(x, y)) },
|
|
29886
29933
|
void 0,
|
|
@@ -29892,20 +29939,6 @@ function $concat(a, b) {
|
|
|
29892
29939
|
if (ae && bs.k === "tuple") {
|
|
29893
29940
|
return tupleOrWiden([...ae, ...bs.elements], confJoin(a.conf, b.conf));
|
|
29894
29941
|
}
|
|
29895
|
-
const sideEl = (x, expanded) => {
|
|
29896
|
-
if (expanded) {
|
|
29897
|
-
return expanded.length ? expanded.reduce((u, y) => joinAbs(u, y)) : abs({ k: "never" }, void 0, void 0, "exact");
|
|
29898
|
-
}
|
|
29899
|
-
if (x?.shape?.k === "any") return anyMemberResult();
|
|
29900
|
-
if (x?.shape?.k === "tuple") {
|
|
29901
|
-
const els = x.shape.elements;
|
|
29902
|
-
return els.length ? els.reduce((u, y) => joinAbs(u, y)) : abs({ k: "never" }, void 0, void 0, "exact");
|
|
29903
|
-
}
|
|
29904
|
-
if (x?.shape?.k === "prim" && x.shape.type === "string") {
|
|
29905
|
-
return abs({ k: "prim", type: "string" }, void 0, void 0, "path");
|
|
29906
|
-
}
|
|
29907
|
-
return unknown;
|
|
29908
|
-
};
|
|
29909
29942
|
return abs(
|
|
29910
29943
|
{ k: "arr", element: joinAbs(sideEl(a, ae), sideEl(b, expand(b))) },
|
|
29911
29944
|
void 0,
|
|
@@ -29939,6 +29972,9 @@ function iterabilityKind(a) {
|
|
|
29939
29972
|
if (s.slots["@@iterator"]) return "total";
|
|
29940
29973
|
return s.open ? "may" : "definite";
|
|
29941
29974
|
}
|
|
29975
|
+
if (s.k === "brand") {
|
|
29976
|
+
return iterabilityKind(s.shape) === "total" ? "total" : "may";
|
|
29977
|
+
}
|
|
29942
29978
|
return "may";
|
|
29943
29979
|
}
|
|
29944
29980
|
function asyncIterabilityKind(a) {
|
|
@@ -29956,6 +29992,12 @@ function asyncIterabilityKind(a) {
|
|
|
29956
29992
|
if (s.k === "obj" && !s.slots["@@iterator"] && s.slots["@@asyncIterator"]) {
|
|
29957
29993
|
return "total";
|
|
29958
29994
|
}
|
|
29995
|
+
if (s.k === "brand") {
|
|
29996
|
+
const ish = s.shape.shape;
|
|
29997
|
+
if (ish.k === "obj" && !ish.slots["@@iterator"] && ish.slots["@@asyncIterator"]) {
|
|
29998
|
+
return "total";
|
|
29999
|
+
}
|
|
30000
|
+
}
|
|
29959
30001
|
return iterabilityKind(a);
|
|
29960
30002
|
}
|
|
29961
30003
|
function guardIterable(a, ctx2) {
|
|
@@ -30019,6 +30061,7 @@ function elemsOf(a) {
|
|
|
30019
30061
|
if (a.shape.k === "prim" && a.shape.type === "string") {
|
|
30020
30062
|
return [abs({ k: "prim", type: "string" }, void 0, void 0, "path")];
|
|
30021
30063
|
}
|
|
30064
|
+
if (a.shape.k === "brand") return elemsOf(a.shape.shape);
|
|
30022
30065
|
return [unknown];
|
|
30023
30066
|
}
|
|
30024
30067
|
function $forInKeys(o) {
|
|
@@ -31808,6 +31851,7 @@ __export(runtime_exports, {
|
|
|
31808
31851
|
callAtFunctionBoundary: () => callAtFunctionBoundary,
|
|
31809
31852
|
clearStaleTermPred: () => clearStaleTermPred,
|
|
31810
31853
|
confPartial: () => confPartial,
|
|
31854
|
+
currentAssumePremises: () => currentAssumePremises,
|
|
31811
31855
|
currentExecPhi: () => currentExecPhi,
|
|
31812
31856
|
fillTuple: () => fillTuple,
|
|
31813
31857
|
isArrMutator: () => isArrMutator,
|
|
@@ -31825,6 +31869,7 @@ __export(runtime_exports, {
|
|
|
31825
31869
|
runWithLoopExits: () => runWithLoopExits,
|
|
31826
31870
|
takeLoopExits: () => takeLoopExits,
|
|
31827
31871
|
takeThrowExits: () => takeThrowExits,
|
|
31872
|
+
withAssumePremises: () => withAssumePremises,
|
|
31828
31873
|
withExecPhi: () => withExecPhi,
|
|
31829
31874
|
withNewTargetReset: () => withNewTargetReset
|
|
31830
31875
|
});
|
|
@@ -31918,6 +31963,15 @@ function isNudoConstraint(x) {
|
|
|
31918
31963
|
function isIntFlag(c) {
|
|
31919
31964
|
return c.int === true || intFlaggedBuilders.has(c);
|
|
31920
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
|
+
}
|
|
31921
31975
|
function makeBuilder(prim2, preds, extra) {
|
|
31922
31976
|
const fields = extra?.fields;
|
|
31923
31977
|
const element = extra?.element;
|
|
@@ -31942,17 +31996,16 @@ function makeBuilder(prim2, preds, extra) {
|
|
|
31942
31996
|
/* @__PURE__ */ Object.create(null),
|
|
31943
31997
|
base,
|
|
31944
31998
|
{
|
|
31945
|
-
gt: (n) => add2(gt(selfTerm(), lit(n))),
|
|
31946
|
-
ge: (n) => add2(ge(selfTerm(), lit(n))),
|
|
31947
|
-
lt: (n) => add2(lt(selfTerm(), lit(n))),
|
|
31948
|
-
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)))),
|
|
31949
32003
|
int: () => makeBuilder(prim2 ?? "number", preds, { ...extra, int: true }),
|
|
31950
|
-
min: (n) => add2(ge(lenTerm(selfTerm()), lit(n))),
|
|
31951
|
-
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)))),
|
|
31952
32006
|
length: (n) => add2(and(ge(lenTerm(selfTerm()), lit(n)), le(lenTerm(selfTerm()), lit(n)))),
|
|
31953
32007
|
shift: (n) => {
|
|
31954
|
-
|
|
31955
|
-
throw new Error("nudo shift(): offset must be a finite number");
|
|
32008
|
+
finiteBoundArg("shift", n);
|
|
31956
32009
|
if (fields || element || members || fnSlot || lazySlot)
|
|
31957
32010
|
throw new Error("nudo shift(): only numeric scalar constraint chains are allowed (shape/array/union/fn are not supported)");
|
|
31958
32011
|
if (prim2 !== void 0 && prim2 !== "number")
|
|
@@ -36948,7 +37001,7 @@ ${js}`;
|
|
|
36948
37001
|
(k) => !ENV_SHADOW_SKIP.has(k)
|
|
36949
37002
|
);
|
|
36950
37003
|
if (envInjectNames.length > 0) {
|
|
36951
|
-
const envBinds = envInjectNames.map((k) => `
|
|
37004
|
+
const envBinds = envInjectNames.map((k) => `let ${k} = __nudoEnv[${JSON.stringify(k)}];`).join("\n");
|
|
36952
37005
|
js = `${envBinds}
|
|
36953
37006
|
${js}`;
|
|
36954
37007
|
}
|
|
@@ -37064,7 +37117,9 @@ function callTranspiledExportFullInner(exports, name, args, opts) {
|
|
|
37064
37117
|
return runWithLoopExits(() => {
|
|
37065
37118
|
try {
|
|
37066
37119
|
const invoke = () => withNewTargetReset(() => fn2(...callArgs));
|
|
37067
|
-
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();
|
|
37068
37123
|
drainPromiseMicros();
|
|
37069
37124
|
if (!isAbsVal(r)) return joinControlExits(asAbsVal(r));
|
|
37070
37125
|
return joinControlExits(r);
|
|
@@ -40802,7 +40857,50 @@ function mul(a, b, phi2 = pTrue) {
|
|
|
40802
40857
|
noteAbstractArithMayThrow(a, b);
|
|
40803
40858
|
return abs({ k: "unknown" }, void 0, void 0, "partial");
|
|
40804
40859
|
}
|
|
40805
|
-
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) {
|
|
40806
40904
|
{
|
|
40807
40905
|
const fa = builtinBrandToPrimitive(a, "number");
|
|
40808
40906
|
const fb = builtinBrandToPrimitive(b, "number");
|
|
@@ -40857,7 +40955,13 @@ function div(a, b, phi2 = pTrue) {
|
|
|
40857
40955
|
term.op === "lit" ? "exact" : confJoin(confJoin(a.conf, b.conf), "path")
|
|
40858
40956
|
);
|
|
40859
40957
|
}
|
|
40860
|
-
|
|
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
|
+
);
|
|
40861
40965
|
}
|
|
40862
40966
|
if (isNumericLike(a) && isNumericLike(b)) {
|
|
40863
40967
|
return abs(num().shape, void 0, void 0, "partial");
|
|
@@ -40868,7 +40972,7 @@ function div(a, b, phi2 = pTrue) {
|
|
|
40868
40972
|
if (isAnyLike(a) || isAnyLike(b)) {
|
|
40869
40973
|
return toNumberResult(a, b, "/");
|
|
40870
40974
|
}
|
|
40871
|
-
const distributed = distributeSumBinOp("/", a, b, phi2, div);
|
|
40975
|
+
const distributed = distributeSumBinOp("/", a, b, phi2, (x, y, p) => div(x, y, p, assume));
|
|
40872
40976
|
if (distributed) return distributed;
|
|
40873
40977
|
noteAbstractArithMayThrow(a, b);
|
|
40874
40978
|
return abs({ k: "unknown" }, void 0, void 0, "partial");
|
|
@@ -41678,6 +41782,14 @@ function scanContractSegments(lines) {
|
|
|
41678
41782
|
}
|
|
41679
41783
|
return reqs;
|
|
41680
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
|
+
}
|
|
41681
41793
|
function scanThrowsDecl(lines) {
|
|
41682
41794
|
const kinds = /* @__PURE__ */ new Set();
|
|
41683
41795
|
let any2 = false;
|
|
@@ -42552,6 +42664,24 @@ function extractRefineLines(source, fnName) {
|
|
|
42552
42664
|
const lines = fnDirectiveCommentLines(source, fnName);
|
|
42553
42665
|
return scanContractSegments(lines);
|
|
42554
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
|
+
}
|
|
42555
42685
|
function extractRefinesFromSource(source, fnName, opts = {}) {
|
|
42556
42686
|
if (!stripStringsKeepComments(source).includes("@nudo:contract")) return [];
|
|
42557
42687
|
const constraints = collectConstraints(source, opts);
|
|
@@ -45355,7 +45485,7 @@ function flattenSumArms(ret) {
|
|
|
45355
45485
|
return out;
|
|
45356
45486
|
}
|
|
45357
45487
|
function isWidenedSumArm(m, parent) {
|
|
45358
|
-
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");
|
|
45359
45489
|
}
|
|
45360
45490
|
function provesBarePrimDomain(arm, constraint) {
|
|
45361
45491
|
if (constraint.members) {
|
|
@@ -45382,7 +45512,7 @@ function assertImplies(ret, constraint, opts) {
|
|
|
45382
45512
|
const phi2 = opts?.phi ?? pTrue;
|
|
45383
45513
|
if (ret.shape.k === "never") return proved();
|
|
45384
45514
|
if (isAnyConstraint(constraint)) return proved();
|
|
45385
|
-
if (isValueSetUnknown(ret)) {
|
|
45515
|
+
if (isValueSetUnknown(ret) && ret.conf !== "widened") {
|
|
45386
45516
|
return unprovable(
|
|
45387
45517
|
ret.shape.k === "any" ? "any does not establish the return contract" : "inference failure (unknown/opaque) \u2014 cannot prove the return contract"
|
|
45388
45518
|
);
|
|
@@ -45438,6 +45568,9 @@ function assertImpliesSingle(arm, constraint, opts, phi2) {
|
|
|
45438
45568
|
return constraintAdmitsNullish(constraint) ? proved() : disproved(`nullish return \u22AD ${formatConstraint(constraint)}`);
|
|
45439
45569
|
}
|
|
45440
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
|
+
}
|
|
45441
45574
|
return unprovable(
|
|
45442
45575
|
arm.shape.k === "any" ? "any does not establish the return contract" : "inference failure (unknown/opaque) \u2014 cannot prove the return contract"
|
|
45443
45576
|
);
|
|
@@ -47495,6 +47628,7 @@ function numericBound(p, self) {
|
|
|
47495
47628
|
if (p.op !== "gt" && p.op !== "ge" && p.op !== "lt" && p.op !== "le") return void 0;
|
|
47496
47629
|
if (!self) return void 0;
|
|
47497
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;
|
|
47498
47632
|
return { op: p.op, n: p.b.value };
|
|
47499
47633
|
}
|
|
47500
47634
|
return void 0;
|
|
@@ -48564,6 +48698,49 @@ function sidecarDiagIssues(diags) {
|
|
|
48564
48698
|
}
|
|
48565
48699
|
return out;
|
|
48566
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
|
+
}
|
|
48567
48744
|
function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures, names2, truncated, identityOpts = opts, depsFp, sidecarFp) {
|
|
48568
48745
|
const hasRefineDirective = stripStringsKeepComments(source).includes("@nudo:contract");
|
|
48569
48746
|
const entryNames = localNamedExports(source);
|
|
@@ -48591,6 +48768,8 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
|
|
|
48591
48768
|
return eff;
|
|
48592
48769
|
};
|
|
48593
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);
|
|
48594
48773
|
if (opts.skips?.has(name)) {
|
|
48595
48774
|
const declared = opts.skips.get(name) ?? null;
|
|
48596
48775
|
const meta = extractFn(source, name, file);
|
|
@@ -48635,19 +48814,21 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
|
|
|
48635
48814
|
}
|
|
48636
48815
|
continue;
|
|
48637
48816
|
}
|
|
48638
|
-
const g =
|
|
48639
|
-
|
|
48640
|
-
|
|
48641
|
-
|
|
48642
|
-
|
|
48643
|
-
|
|
48644
|
-
|
|
48645
|
-
|
|
48646
|
-
|
|
48647
|
-
|
|
48648
|
-
|
|
48649
|
-
|
|
48650
|
-
|
|
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
|
+
);
|
|
48651
48832
|
if (!g) {
|
|
48652
48833
|
const isEntryCandidate = entryNames.has(name) || name === "default" || entryNames.has("default") && isDefaultExportName(source, name);
|
|
48653
48834
|
if (isEntryCandidate && entryThrowsMode !== "off") {
|
|
@@ -48707,7 +48888,7 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
|
|
|
48707
48888
|
const isEntry = entryNames.has(name) || name === "default" || entryNames.has("default") && isDefaultExportName(source, name) || name.includes(".") && entryNames.has(name);
|
|
48708
48889
|
let throwsDisplay;
|
|
48709
48890
|
if (isEntry && entryThrowsMode !== "off") {
|
|
48710
|
-
const effects = collectEntryMayThrows(source, name, g, file, phi2, opts);
|
|
48891
|
+
const effects = runSeeded(() => collectEntryMayThrows(source, name, g, file, phi2, opts));
|
|
48711
48892
|
throwsDisplay = formatThrowsAbs(mayThrowEffectsToAbs(effects));
|
|
48712
48893
|
const declared = extractDeclaredThrows(source, name) ?? effectiveInterfaceCached(name)?.throws?.kinds;
|
|
48713
48894
|
const remaining = filterGateThrows(effects, declared, ignoreThrows);
|
|
@@ -48728,7 +48909,7 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
|
|
|
48728
48909
|
});
|
|
48729
48910
|
}
|
|
48730
48911
|
} else if (isEntry) {
|
|
48731
|
-
const effects = collectEntryMayThrows(source, name, g, file, phi2, opts);
|
|
48912
|
+
const effects = runSeeded(() => collectEntryMayThrows(source, name, g, file, phi2, opts));
|
|
48732
48913
|
throwsDisplay = formatThrowsAbs(mayThrowEffectsToAbs(effects));
|
|
48733
48914
|
}
|
|
48734
48915
|
const fmt = formatSigCached(g.symbolic, name);
|
|
@@ -48822,7 +49003,8 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
|
|
|
48822
49003
|
name,
|
|
48823
49004
|
display,
|
|
48824
49005
|
eff.returns.constraint,
|
|
48825
|
-
g.symbolic
|
|
49006
|
+
g.symbolic,
|
|
49007
|
+
assumePhi?.display
|
|
48826
49008
|
);
|
|
48827
49009
|
if (retIssues.some((i) => i.severity === "error")) returnViolated.add(name);
|
|
48828
49010
|
issues.push(...retIssues);
|
|
@@ -49018,9 +49200,24 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
|
|
|
49018
49200
|
}
|
|
49019
49201
|
};
|
|
49020
49202
|
}
|
|
49021
|
-
function checkReturnConstraint(fnName, cName, constraint, ret) {
|
|
49203
|
+
function checkReturnConstraint(fnName, cName, constraint, ret, assumeDisplay) {
|
|
49022
49204
|
const proof = assertImplies(ret, constraint);
|
|
49023
|
-
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
|
+
}
|
|
49024
49221
|
const push = (severity, actual, expected, suggestion) => [
|
|
49025
49222
|
{
|
|
49026
49223
|
severity,
|
|
@@ -49068,6 +49265,7 @@ var init_check = __esm({
|
|
|
49068
49265
|
init_abs();
|
|
49069
49266
|
init_abs();
|
|
49070
49267
|
init_pred();
|
|
49268
|
+
init_term();
|
|
49071
49269
|
init_format();
|
|
49072
49270
|
init_scan();
|
|
49073
49271
|
init_check_interface_drift();
|
|
@@ -49076,6 +49274,7 @@ var init_check = __esm({
|
|
|
49076
49274
|
init_check_assign();
|
|
49077
49275
|
init_check_case_scan();
|
|
49078
49276
|
init_run();
|
|
49277
|
+
init_runtime();
|
|
49079
49278
|
init_calls();
|
|
49080
49279
|
init_may_throw2();
|
|
49081
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": {
|