@nudojs/core 1.10.2 → 1.11.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.
@@ -4391,6 +4391,19 @@ function withExecPhi(p, body) {
4391
4391
  phi = prev;
4392
4392
  }
4393
4393
  }
4394
+ var assumePremises = pTrue;
4395
+ function currentAssumePremises() {
4396
+ return assumePremises;
4397
+ }
4398
+ function withAssumePremises(p, body) {
4399
+ const prev = assumePremises;
4400
+ assumePremises = p;
4401
+ try {
4402
+ return body();
4403
+ } finally {
4404
+ assumePremises = prev;
4405
+ }
4406
+ }
4394
4407
  var noBody2 = { type: "BlockStatement", body: [], directives: [] };
4395
4408
  var hostGlobalFnCall;
4396
4409
  function setHostGlobalFnCall(bridge) {
@@ -4429,6 +4442,12 @@ function $absVal(v2) {
4429
4442
  return asAbsVal(v2);
4430
4443
  }
4431
4444
  function callAtFunctionBoundary(body) {
4445
+ if (assumePremises !== pTrue) {
4446
+ return withAssumePremises(pTrue, () => boundaryBody(body));
4447
+ }
4448
+ return boundaryBody(body);
4449
+ }
4450
+ function boundaryBody(body) {
4432
4451
  let forwarded = [];
4433
4452
  try {
4434
4453
  return runWithLoopExits(() => {
@@ -8679,7 +8698,12 @@ function emitArrMutatorRebinds(expr, opts, pad) {
8679
8698
  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)) {
8680
8699
  const methodName = node.callee.property.name;
8681
8700
  const argSrcs = (node.arguments ?? []).map(
8682
- (a) => a.type === "SpreadElement" ? "$unknown()" : isExpression(a) ? emitTranspileExpression(a, opts) : "$unknown()"
8701
+ (a) => (
8702
+ // spread 实参展开(issue #153):$elems → JS Abs[],数组字面量 JS
8703
+ // spread 平铺(与 expr.ts 普通调用同口径);此前折 $unknown() 单参
8704
+ // 让重绑行以 unknown 实参执行真实变更,污染容器元素域
8705
+ a.type === "SpreadElement" ? `...$elems(${emitTranspileExpression(a.argument, opts)})` : isExpression(a) ? emitTranspileExpression(a, opts) : "$unknown()"
8706
+ )
8683
8707
  ).join(", ");
8684
8708
  const objNode = node.callee.object;
8685
8709
  if (objNode.type === "Identifier") {
@@ -9809,7 +9833,12 @@ ${pad}export default ${name};`;
9809
9833
  const methodName = expr.callee.property.name;
9810
9834
  if (["push", "unshift", "splice", "pop", "shift", "reverse", "sort"].includes(methodName)) {
9811
9835
  const argSrcs = expr.arguments.map(
9812
- (a) => a.type === "SpreadElement" ? "$unknown()" : emitTranspileExpression(a, opts)
9836
+ (a) => (
9837
+ // spread 实参展开(issue #153):$elems → JS Abs[],数组字面量 JS spread
9838
+ // 平铺(与普通调用 $callNamed/$invoke 同口径,DEC-006 K5);
9839
+ // 此前折 $unknown() 单参会污染容器元素域
9840
+ a.type === "SpreadElement" ? `...$elems(${emitTranspileExpression(a.argument, opts)})` : emitTranspileExpression(a, opts)
9841
+ )
9813
9842
  ).join(", ");
9814
9843
  const objNode = expr.callee.object;
9815
9844
  if (objNode.type === "Identifier") {
@@ -12160,6 +12189,7 @@ __export(runtime_exports, {
12160
12189
  callAtFunctionBoundary: () => callAtFunctionBoundary,
12161
12190
  clearStaleTermPred: () => clearStaleTermPred,
12162
12191
  confPartial: () => confPartial,
12192
+ currentAssumePremises: () => currentAssumePremises,
12163
12193
  currentExecPhi: () => currentExecPhi,
12164
12194
  fillTuple: () => fillTuple,
12165
12195
  isArrMutator: () => isArrMutator,
@@ -12177,6 +12207,7 @@ __export(runtime_exports, {
12177
12207
  runWithLoopExits: () => runWithLoopExits,
12178
12208
  takeLoopExits: () => takeLoopExits,
12179
12209
  takeThrowExits: () => takeThrowExits,
12210
+ withAssumePremises: () => withAssumePremises,
12180
12211
  withExecPhi: () => withExecPhi,
12181
12212
  withNewTargetReset: () => withNewTargetReset
12182
12213
  });
@@ -12218,7 +12249,7 @@ function $mul(a, b) {
12218
12249
  return mul(a, b, currentExecPhi());
12219
12250
  }
12220
12251
  function $div(a, b) {
12221
- return div(a, b, currentExecPhi());
12252
+ return div(a, b, currentExecPhi(), currentAssumePremises());
12222
12253
  }
12223
12254
  function $mod(a, b) {
12224
12255
  return mod(a, b, currentExecPhi());
@@ -16649,6 +16680,15 @@ var intFlaggedBuilders = /* @__PURE__ */ new WeakSet();
16649
16680
  function isIntFlag(c) {
16650
16681
  return c.int === true || intFlaggedBuilders.has(c);
16651
16682
  }
16683
+ function finiteBoundArg(method, n) {
16684
+ if (typeof n !== "number" || !Number.isFinite(n)) {
16685
+ const shown = typeof n === "string" ? JSON.stringify(n) : String(n);
16686
+ throw new Error(
16687
+ `nudo ${method}(): finite numeric bound expected, got ${shown} \u2014 variable references are not supported; express cross-variable lemmas with @nudo:assume (issue #152)`
16688
+ );
16689
+ }
16690
+ return n;
16691
+ }
16652
16692
  function makeBuilder(prim, preds, extra) {
16653
16693
  const fields = extra?.fields;
16654
16694
  const element = extra?.element;
@@ -16673,17 +16713,16 @@ function makeBuilder(prim, preds, extra) {
16673
16713
  /* @__PURE__ */ Object.create(null),
16674
16714
  base,
16675
16715
  {
16676
- gt: (n) => add2(gt(selfTerm(), lit(n))),
16677
- ge: (n) => add2(ge(selfTerm(), lit(n))),
16678
- lt: (n) => add2(lt(selfTerm(), lit(n))),
16679
- le: (n) => add2(le(selfTerm(), lit(n))),
16716
+ gt: (n) => add2(gt(selfTerm(), lit(finiteBoundArg("gt", n)))),
16717
+ ge: (n) => add2(ge(selfTerm(), lit(finiteBoundArg("ge", n)))),
16718
+ lt: (n) => add2(lt(selfTerm(), lit(finiteBoundArg("lt", n)))),
16719
+ le: (n) => add2(le(selfTerm(), lit(finiteBoundArg("le", n)))),
16680
16720
  int: () => makeBuilder(prim ?? "number", preds, { ...extra, int: true }),
16681
- min: (n) => add2(ge(lenTerm(selfTerm()), lit(n))),
16682
- max: (n) => add2(le(lenTerm(selfTerm()), lit(n))),
16721
+ min: (n) => add2(ge(lenTerm(selfTerm()), lit(finiteBoundArg("min", n)))),
16722
+ max: (n) => add2(le(lenTerm(selfTerm()), lit(finiteBoundArg("max", n)))),
16683
16723
  length: (n) => add2(and(ge(lenTerm(selfTerm()), lit(n)), le(lenTerm(selfTerm()), lit(n)))),
16684
16724
  shift: (n) => {
16685
- if (!Number.isFinite(n))
16686
- throw new Error("nudo shift(): offset must be a finite number");
16725
+ finiteBoundArg("shift", n);
16687
16726
  if (fields || element || members || fnSlot || lazySlot)
16688
16727
  throw new Error("nudo shift(): only numeric scalar constraint chains are allowed (shape/array/union/fn are not supported)");
16689
16728
  if (prim !== void 0 && prim !== "number")
@@ -21594,7 +21633,9 @@ function callTranspiledExportFullInner(exports, name, args, opts) {
21594
21633
  return runWithLoopExits(() => {
21595
21634
  try {
21596
21635
  const invoke = () => withNewTargetReset(() => fn2(...callArgs));
21597
- const r = opts?.phi ? withExecPhi(opts.phi, invoke) : invoke();
21636
+ const ap = opts?.assumePremises;
21637
+ const seeded = ap !== void 0 ? () => withAssumePremises(ap, invoke) : invoke;
21638
+ const r = opts?.phi ? withExecPhi(opts.phi, seeded) : seeded();
21598
21639
  drainPromiseMicros();
21599
21640
  if (!isAbsVal(r)) return joinControlExits(asAbsVal(r));
21600
21641
  return joinControlExits(r);
@@ -25193,7 +25234,50 @@ function mul(a, b, phi2 = pTrue) {
25193
25234
  noteAbstractArithMayThrow(a, b);
25194
25235
  return abs({ k: "unknown" }, void 0, void 0, "partial");
25195
25236
  }
25196
- function div(a, b, phi2 = pTrue) {
25237
+ function assumePremiseList(assume) {
25238
+ if (!assume || assume.op === "true") return [];
25239
+ const out = [];
25240
+ const walk = (p) => {
25241
+ if (p.op === "and") {
25242
+ p.args.forEach(walk);
25243
+ return;
25244
+ }
25245
+ if (p.op !== "true") out.push(p);
25246
+ };
25247
+ walk(assume);
25248
+ return out;
25249
+ }
25250
+ function divAssumeBounds(a, b, resultTerm, phi2, assume) {
25251
+ const premises = assumePremiseList(assume);
25252
+ if (premises.length === 0) return void 0;
25253
+ if (!a.term || !b.term) return void 0;
25254
+ const bb = numericBounds(b, phi2);
25255
+ const posDen = bb?.lo !== void 0 && (bb.lo.value > 0 || bb.lo.value === 0 && bb.lo.strict);
25256
+ const negDen = bb?.hi !== void 0 && (bb.hi.value < 0 || bb.hi.value === 0 && bb.hi.strict);
25257
+ if (!posDen && !negDen) return void 0;
25258
+ let aLeB = false;
25259
+ let bLeA = false;
25260
+ for (const p of premises) {
25261
+ if (p.op !== "le") continue;
25262
+ if (termEquals(p.a, a.term) && termEquals(p.b, b.term)) aLeB = true;
25263
+ else if (termEquals(p.a, b.term) && termEquals(p.b, a.term)) bLeA = true;
25264
+ }
25265
+ if (!aLeB && !bLeA) return void 0;
25266
+ const facts = [];
25267
+ if (aLeB) facts.push(posDen ? le(resultTerm, lit(1)) : ge(resultTerm, lit(1)));
25268
+ if (bLeA) facts.push(posDen ? ge(resultTerm, lit(1)) : le(resultTerm, lit(1)));
25269
+ const ab = numericBounds(a, phi2);
25270
+ if (ab?.lo !== void 0 && (ab.lo.value > 0 || ab.lo.value === 0)) {
25271
+ if (posDen) facts.push(ab.lo.strict ? gt(resultTerm, lit(0)) : ge(resultTerm, lit(0)));
25272
+ else facts.push(ab.lo.strict ? lt(resultTerm, lit(0)) : le(resultTerm, lit(0)));
25273
+ }
25274
+ if (ab?.hi !== void 0 && (ab.hi.value < 0 || ab.hi.value === 0)) {
25275
+ if (posDen) facts.push(ab.hi.strict ? lt(resultTerm, lit(0)) : le(resultTerm, lit(0)));
25276
+ else facts.push(ab.hi.strict ? gt(resultTerm, lit(0)) : ge(resultTerm, lit(0)));
25277
+ }
25278
+ return facts.length ? and(...facts) : void 0;
25279
+ }
25280
+ function div(a, b, phi2 = pTrue, assume) {
25197
25281
  {
25198
25282
  const fa = builtinBrandToPrimitive(a, "number");
25199
25283
  const fb = builtinBrandToPrimitive(b, "number");
@@ -25248,7 +25332,13 @@ function div(a, b, phi2 = pTrue) {
25248
25332
  term.op === "lit" ? "exact" : confJoin(confJoin(a.conf, b.conf), "path")
25249
25333
  );
25250
25334
  }
25251
- return abs(num().shape, term, void 0, confJoin(confJoin(a.conf, b.conf), "path"));
25335
+ const assumePred = divAssumeBounds(a, b, term, phi2, assume);
25336
+ return abs(
25337
+ num().shape,
25338
+ term,
25339
+ assumePred,
25340
+ confJoin(confJoin(a.conf, b.conf), "path")
25341
+ );
25252
25342
  }
25253
25343
  if (isNumericLike(a) && isNumericLike(b)) {
25254
25344
  return abs(num().shape, void 0, void 0, "partial");
@@ -25259,7 +25349,7 @@ function div(a, b, phi2 = pTrue) {
25259
25349
  if (isAnyLike(a) || isAnyLike(b)) {
25260
25350
  return toNumberResult(a, b, "/");
25261
25351
  }
25262
- const distributed = distributeSumBinOp("/", a, b, phi2, div);
25352
+ const distributed = distributeSumBinOp("/", a, b, phi2, (x, y, p) => div(x, y, p, assume));
25263
25353
  if (distributed) return distributed;
25264
25354
  noteAbstractArithMayThrow(a, b);
25265
25355
  return abs({ k: "unknown" }, void 0, void 0, "partial");
@@ -25962,6 +26052,8 @@ export {
25962
26052
  clearStaleTermPred,
25963
26053
  currentExecPhi,
25964
26054
  withExecPhi,
26055
+ currentAssumePremises,
26056
+ withAssumePremises,
25965
26057
  asAbsVal,
25966
26058
  $absVal,
25967
26059
  callAtFunctionBoundary,
@@ -47,7 +47,7 @@ import {
47
47
  withEvalBudgetOverride,
48
48
  withExecPhi,
49
49
  withFnBudgetOverride
50
- } from "./chunk-VKSROGOF.js";
50
+ } from "./chunk-OYQMVCUV.js";
51
51
 
52
52
  // src/algebra/directive-scan.ts
53
53
  function cleanDirectiveLine(raw, kind) {
@@ -614,6 +614,14 @@ function scanContractSegments(lines) {
614
614
  }
615
615
  return reqs;
616
616
  }
617
+ function scanAssumeSegments(lines) {
618
+ const out = [];
619
+ for (const line of lines) {
620
+ const m = line.match(/^@nudo:assume\s+(.+)$/);
621
+ if (m) out.push(m[1].trim().replace(/\*\/$/, "").trim());
622
+ }
623
+ return out;
624
+ }
617
625
  function scanThrowsDecl(lines) {
618
626
  const kinds = /* @__PURE__ */ new Set();
619
627
  let any = false;
@@ -1490,6 +1498,24 @@ function extractRefineLines(source, fnName) {
1490
1498
  const lines = fnDirectiveCommentLines(source, fnName);
1491
1499
  return scanContractSegments(lines);
1492
1500
  }
1501
+ function extractAssumeSegs(source, fnName) {
1502
+ if (!stripStringsKeepComments(source).includes("@nudo:assume")) return [];
1503
+ const out = [];
1504
+ for (const seg of scanAssumeSegments(fnDirectiveCommentLines(source, fnName))) {
1505
+ const geSeg = seg.match(/^([A-Za-z_$][\w$]*)\s*>=\s*([A-Za-z_$][\w$]*)$/);
1506
+ if (geSeg) {
1507
+ out.push({ kind: "ok", lhs: geSeg[2], rhs: geSeg[1] });
1508
+ continue;
1509
+ }
1510
+ const leSeg = seg.match(/^([A-Za-z_$][\w$]*)\s*<=\s*([A-Za-z_$][\w$]*)$/);
1511
+ if (leSeg) {
1512
+ out.push({ kind: "ok", lhs: leSeg[1], rhs: leSeg[2] });
1513
+ continue;
1514
+ }
1515
+ out.push({ kind: "bad", raw: seg });
1516
+ }
1517
+ return out;
1518
+ }
1493
1519
  function extractRefinesFromSource(source, fnName, opts = {}) {
1494
1520
  if (!stripStringsKeepComments(source).includes("@nudo:contract")) return [];
1495
1521
  const constraints = collectConstraints(source, opts);
@@ -5525,6 +5551,7 @@ export {
5525
5551
  NudoSidecarError,
5526
5552
  execNudoModule,
5527
5553
  resetNudoModuleExecCache,
5554
+ extractAssumeSegs,
5528
5555
  extractRefinesFromSource,
5529
5556
  refineToIndexedFull,
5530
5557
  extractRefineReturnFromSource,
package/dist/exec.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as Abs, M as MayThrowEffect, b as Phi } from './run-Bgj2tCrv.js';
2
- export { W as $assignRecord, X as $call, Y as $callNamed, bI as $nodoPos, Z as $recordBinding, a5 as EvalAbsAssignRecord, a6 as EvalCallRecord, a7 as EvalFallback, g as EvalFallbackStats, a8 as EvalPosRecord, ab as MAX_EVAL_CALL_DEPTH, ac as MAX_EVAL_TOTAL_CALLS, ad as RUNTIME_IMPORT_RE, V as RunTranspiledOptions, af as TranspiledCallResult, at as bindingsOf, aw as callTranspiledExport, ax as callTranspiledExportApply, ay as callTranspiledExportFull, l as clearPureCallMemo, m as clearPureMemo, n as enterEvalCallBudgetSession, aD as evalExprAbs, p as exitEvalCallBudgetSession, v as getEvalCallBudgetState, aG as getEvalCallCollector, w as getEvalFallbackStats, bJ as getPosCollector, aO as isAbsVal, aQ as isCjsMainRun, b5 as noteEvalCallRecord, b6 as noteEvalFallback, bn as resetEvalCallBudget, H as resetEvalFallbackStats, bK as routeApplyThrows, bo as runTranspiled, bp as runTranspiledOptionsMemoKey, bq as setEvalAssignCollector, br as setEvalBindingSink, bs as setEvalCallCollector, bt as setEvalFallbackCollector, bv as setPosCollector, bF as tryRunTranspiled, O as withEvalBudgetOverride } from './run-Bgj2tCrv.js';
1
+ import { A as Abs, M as MayThrowEffect, b as Phi } from './run-CDBM1fJZ.js';
2
+ export { W as $assignRecord, X as $call, Y as $callNamed, bI as $nodoPos, Z as $recordBinding, a5 as EvalAbsAssignRecord, a6 as EvalCallRecord, a7 as EvalFallback, g as EvalFallbackStats, a8 as EvalPosRecord, ab as MAX_EVAL_CALL_DEPTH, ac as MAX_EVAL_TOTAL_CALLS, ad as RUNTIME_IMPORT_RE, V as RunTranspiledOptions, af as TranspiledCallResult, at as bindingsOf, aw as callTranspiledExport, ax as callTranspiledExportApply, ay as callTranspiledExportFull, l as clearPureCallMemo, m as clearPureMemo, n as enterEvalCallBudgetSession, aD as evalExprAbs, p as exitEvalCallBudgetSession, v as getEvalCallBudgetState, aG as getEvalCallCollector, w as getEvalFallbackStats, bJ as getPosCollector, aO as isAbsVal, aQ as isCjsMainRun, b5 as noteEvalCallRecord, b6 as noteEvalFallback, bn as resetEvalCallBudget, H as resetEvalFallbackStats, bK as routeApplyThrows, bo as runTranspiled, bp as runTranspiledOptionsMemoKey, bq as setEvalAssignCollector, br as setEvalBindingSink, bs as setEvalCallCollector, bt as setEvalFallbackCollector, bv as setPosCollector, bF as tryRunTranspiled, O as withEvalBudgetOverride } from './run-CDBM1fJZ.js';
3
3
  import { Node, Expression, File } from '@babel/types';
4
4
 
5
5
  /**
@@ -29,6 +29,8 @@ declare function isNudoThrow(e: unknown): e is NudoThrow;
29
29
  declare function clearStaleTermPred(v: Abs): void;
30
30
  declare function currentExecPhi(): Phi;
31
31
  declare function withExecPhi<T>(p: Phi, body: () => T): T;
32
+ declare function currentAssumePremises(): Phi;
33
+ declare function withAssumePremises<T>(p: Phi, body: () => T): T;
32
34
  /**
33
35
  * transpile 泄漏的 JS 函数值 → 一等 fn Abs。
34
36
  * 求值引擎把函数声明/表达式编译成真实 JS 函数;它们流进对象槽、
@@ -825,4 +827,4 @@ declare class NudoUnsupportedError extends Error {
825
827
  detail?: string);
826
828
  }
827
829
 
828
- export { $absVal, $add, $arguments, $arr, $arrMutContainer, $arrRest, $arrWithHoles, $async, $asyncReturn, $await, $bitand, $bitnot, $bitor, $bitxor, $catchVal, $class, $classExpr, $collectionForEach, $concat, $copy, $del, $delRes, $div, $dynamicImport, $elems, $eq, $eqLoose, $fnVal, $for, $forInKeys, $forIter, $forOf, $fork, $ge, $gen, $get, $getSuper, $getSuperObj, $gt, $idx, $idxSet, $importMeta, $in, $instanceof, $instanceofNonIdent, $invoke, $invokeSuper, $invokeSuperKey, $invokeSuperObj, $isBreakTo, $isForkExit, $iterCheck, $join, $le, $len, $lit, $loopBreak, $loopContinue, $loopReturn, $lt, $mod, $mul, $narrowMemberEq, $narrowTypeOf, $ne, $neLoose, $neg, $new, $newTarget, $not, $nullishTest, $obj, $objAccessor, $objAccessorKey, $objRest, $optionalGet, $optionalInvoke, $orDefault, $pow, $pushLoopExit, $rawThis, $reStateCall, $refineRel, $regex, $removeMemberNullish, $removeNull, $removeNullish, $removeUndefined, $restBind, $rethrowIfNudoReturn, $set, $setKey, $setProto, $shl, $shr, $spread, $staticInit, $staticInvoke, $sub, $super, $switch, $thisGet, $thisSet, $throw, $toNumber, $toNumeric, $tpl, $tryCurrentMark, $tryDetachSoftCatch, $tryDigestSoftCatch, $tryDiscardSoft, $tryMark, $tryOrphanSoft, $tryPopMark, $tryReleaseSoftCatch, $tryReleaseSoftOut, $tryTakeSince, $typeof, $unknown, $updateAdd, $updateSub, $ushr, $while, $whileSeq, $yield, $yieldStar, DEFAULT_MAX_LOOP_ITERS, type EvalClassSpec, NudoLoopSignal, NudoReturn, NudoThrow, NudoUnsupportedError, type TranspileOptions, asAbsVal, callAtFunctionBoundary, clearBClasses, clearStaleTermPred, currentExecPhi, fillTuple, foldRequireSpecArg, foldStaticStringExpr, getEvalClass, isArrMutator, isDefinitelyFalse, isDefinitelyTrue, isNudoBreak, isNudoContinue, isNudoReturn, isNudoThrow, litTruth, lookupObjAccessor, namespaceNameOf, pushLoopExit, pushThrowExit, registerEvalClass, runWithLoopExits, runtimeImportOf, takeLoopExits, takeThrowExits, transpile, transpileBodyNode, transpileExpression, transpileFile, transpileSource, withExecPhi };
830
+ export { $absVal, $add, $arguments, $arr, $arrMutContainer, $arrRest, $arrWithHoles, $async, $asyncReturn, $await, $bitand, $bitnot, $bitor, $bitxor, $catchVal, $class, $classExpr, $collectionForEach, $concat, $copy, $del, $delRes, $div, $dynamicImport, $elems, $eq, $eqLoose, $fnVal, $for, $forInKeys, $forIter, $forOf, $fork, $ge, $gen, $get, $getSuper, $getSuperObj, $gt, $idx, $idxSet, $importMeta, $in, $instanceof, $instanceofNonIdent, $invoke, $invokeSuper, $invokeSuperKey, $invokeSuperObj, $isBreakTo, $isForkExit, $iterCheck, $join, $le, $len, $lit, $loopBreak, $loopContinue, $loopReturn, $lt, $mod, $mul, $narrowMemberEq, $narrowTypeOf, $ne, $neLoose, $neg, $new, $newTarget, $not, $nullishTest, $obj, $objAccessor, $objAccessorKey, $objRest, $optionalGet, $optionalInvoke, $orDefault, $pow, $pushLoopExit, $rawThis, $reStateCall, $refineRel, $regex, $removeMemberNullish, $removeNull, $removeNullish, $removeUndefined, $restBind, $rethrowIfNudoReturn, $set, $setKey, $setProto, $shl, $shr, $spread, $staticInit, $staticInvoke, $sub, $super, $switch, $thisGet, $thisSet, $throw, $toNumber, $toNumeric, $tpl, $tryCurrentMark, $tryDetachSoftCatch, $tryDigestSoftCatch, $tryDiscardSoft, $tryMark, $tryOrphanSoft, $tryPopMark, $tryReleaseSoftCatch, $tryReleaseSoftOut, $tryTakeSince, $typeof, $unknown, $updateAdd, $updateSub, $ushr, $while, $whileSeq, $yield, $yieldStar, DEFAULT_MAX_LOOP_ITERS, type EvalClassSpec, NudoLoopSignal, NudoReturn, NudoThrow, NudoUnsupportedError, type TranspileOptions, asAbsVal, callAtFunctionBoundary, clearBClasses, clearStaleTermPred, currentAssumePremises, currentExecPhi, fillTuple, foldRequireSpecArg, foldStaticStringExpr, getEvalClass, isArrMutator, isDefinitelyFalse, isDefinitelyTrue, isNudoBreak, isNudoContinue, isNudoReturn, isNudoThrow, litTruth, lookupObjAccessor, namespaceNameOf, pushLoopExit, pushThrowExit, registerEvalClass, runWithLoopExits, runtimeImportOf, takeLoopExits, takeThrowExits, transpile, transpileBodyNode, transpileExpression, transpileFile, transpileSource, withAssumePremises, withExecPhi };
package/dist/exec.js CHANGED
@@ -149,6 +149,7 @@ import {
149
149
  clearPureCallMemo,
150
150
  clearPureMemo,
151
151
  clearStaleTermPred,
152
+ currentAssumePremises,
152
153
  currentExecPhi,
153
154
  enterEvalCallBudgetSession,
154
155
  evalExprAbs,
@@ -198,9 +199,10 @@ import {
198
199
  transpileFile,
199
200
  transpileSource,
200
201
  tryRunTranspiled,
202
+ withAssumePremises,
201
203
  withEvalBudgetOverride,
202
204
  withExecPhi
203
- } from "./chunk-VKSROGOF.js";
205
+ } from "./chunk-OYQMVCUV.js";
204
206
  export {
205
207
  $absVal,
206
208
  $add,
@@ -351,6 +353,7 @@ export {
351
353
  clearPureCallMemo,
352
354
  clearPureMemo,
353
355
  clearStaleTermPred,
356
+ currentAssumePremises,
354
357
  currentExecPhi,
355
358
  enterEvalCallBudgetSession,
356
359
  evalExprAbs,
@@ -400,6 +403,7 @@ export {
400
403
  transpileFile,
401
404
  transpileSource,
402
405
  tryRunTranspiled,
406
+ withAssumePremises,
403
407
  withEvalBudgetOverride,
404
408
  withExecPhi
405
409
  };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { d as AstEnv, A as Abs, b as Phi, T as Term, Q as HofCollectCtx, C as Confidence, S as Shape, a as Pred, R as RelSource, U as HofSite, c as AbsModuleExports, V as RunTranspiledOptions } from './run-Bgj2tCrv.js';
2
- export { W as $assignRecord, X as $call, Y as $callNamed, Z as $recordBinding, _ as AbsApplyNoThrow, a0 as AbsApplyNoThrowFn, a1 as AbsApplyResult, a2 as AbsApplyReturn, a3 as AbsFnImpl, a4 as AbsSigImpl, a5 as EvalAbsAssignRecord, a6 as EvalCallRecord, a7 as EvalFallback, a8 as EvalPosRecord, a9 as ImplicationOracle, aa as LitValueResult, L as LiteralValue, ab as MAX_EVAL_CALL_DEPTH, ac as MAX_EVAL_TOTAL_CALLS, P as PrimName, ad as RUNTIME_IMPORT_RE, ae as TYPEOF_NAMES, af as TranspiledCallResult, ag as TypeofName, ah as abs, ai as absFunction, aj as absOnly, ak as absToString, al as and, am as anyAbs, an as anyVar, ao as app, ap as assumeFinite, aq as attachFnImpl, ar as bigintLit, as as bindImports, at as bindingsOf, au as bool, av as boolLit, aw as callTranspiledExport, ax as callTranspiledExportApply, ay as callTranspiledExportFull, az as collectAbsExports, aA as confJoin, aB as emptyPhi, aC as eq, aD as evalExprAbs, aE as ge, aF as geNum, aG as getEvalCallCollector, aH as getFnImpl, aI as getImplicationOracle, aJ as gt, aK as gtNum, aL as hostFnCtorFacet, aM as implies, aN as isAbsApplyResult, aO as isAbsVal, aP as isBigPrim, aQ as isCjsMainRun, aR as isExactLit, aS as isNumPrim, aT as isStrPrim, aU as le, aV as leNum, aW as lit, aX as litValue, aY as lt, aZ as ltNum, a_ as makeAbsApplyResult, a$ as markPureFn, b0 as namespaceAbsOf, b1 as ne, b2 as negatePred, b3 as never, b4 as not, b5 as noteEvalCallRecord, b6 as noteEvalFallback, b7 as num, b8 as numLit, b9 as numVar, ba as obj, bb as or, bc as pFalse, bd as pTrue, be as phiAnd, bf as predEquals, bg as predToString, bh as predVars, bi as primToTypeof, bj as ptypeof, bk as pureFnNameOf, bl as relationFingerprint, bm as relationFn, bn as resetEvalCallBudget, bo as runTranspiled, bp as runTranspiledOptionsMemoKey, bq as setEvalAssignCollector, br as setEvalBindingSink, bs as setEvalCallCollector, bt as setEvalFallbackCollector, bu as setImplicationOracle, bv as setPosCollector, bw as shapeOfTerm, bx as shapeOnlyFn, by as shapeToString, bz as simplifyTerm, bA as str, bB as strLit, bC as substPred, bD as termEquals, bE as termToString, bF as tryRunTranspiled, bG as unknown, bH as v } from './run-Bgj2tCrv.js';
1
+ import { d as AstEnv, A as Abs, b as Phi, T as Term, Q as HofCollectCtx, C as Confidence, S as Shape, a as Pred, R as RelSource, U as HofSite, c as AbsModuleExports, V as RunTranspiledOptions } from './run-CDBM1fJZ.js';
2
+ export { W as $assignRecord, X as $call, Y as $callNamed, Z as $recordBinding, _ as AbsApplyNoThrow, a0 as AbsApplyNoThrowFn, a1 as AbsApplyResult, a2 as AbsApplyReturn, a3 as AbsFnImpl, a4 as AbsSigImpl, a5 as EvalAbsAssignRecord, a6 as EvalCallRecord, a7 as EvalFallback, a8 as EvalPosRecord, a9 as ImplicationOracle, aa as LitValueResult, L as LiteralValue, ab as MAX_EVAL_CALL_DEPTH, ac as MAX_EVAL_TOTAL_CALLS, P as PrimName, ad as RUNTIME_IMPORT_RE, ae as TYPEOF_NAMES, af as TranspiledCallResult, ag as TypeofName, ah as abs, ai as absFunction, aj as absOnly, ak as absToString, al as and, am as anyAbs, an as anyVar, ao as app, ap as assumeFinite, aq as attachFnImpl, ar as bigintLit, as as bindImports, at as bindingsOf, au as bool, av as boolLit, aw as callTranspiledExport, ax as callTranspiledExportApply, ay as callTranspiledExportFull, az as collectAbsExports, aA as confJoin, aB as emptyPhi, aC as eq, aD as evalExprAbs, aE as ge, aF as geNum, aG as getEvalCallCollector, aH as getFnImpl, aI as getImplicationOracle, aJ as gt, aK as gtNum, aL as hostFnCtorFacet, aM as implies, aN as isAbsApplyResult, aO as isAbsVal, aP as isBigPrim, aQ as isCjsMainRun, aR as isExactLit, aS as isNumPrim, aT as isStrPrim, aU as le, aV as leNum, aW as lit, aX as litValue, aY as lt, aZ as ltNum, a_ as makeAbsApplyResult, a$ as markPureFn, b0 as namespaceAbsOf, b1 as ne, b2 as negatePred, b3 as never, b4 as not, b5 as noteEvalCallRecord, b6 as noteEvalFallback, b7 as num, b8 as numLit, b9 as numVar, ba as obj, bb as or, bc as pFalse, bd as pTrue, be as phiAnd, bf as predEquals, bg as predToString, bh as predVars, bi as primToTypeof, bj as ptypeof, bk as pureFnNameOf, bl as relationFingerprint, bm as relationFn, bn as resetEvalCallBudget, bo as runTranspiled, bp as runTranspiledOptionsMemoKey, bq as setEvalAssignCollector, br as setEvalBindingSink, bs as setEvalCallCollector, bt as setEvalFallbackCollector, bu as setImplicationOracle, bv as setPosCollector, bw as shapeOfTerm, bx as shapeOnlyFn, by as shapeToString, bz as simplifyTerm, bA as str, bB as strLit, bC as substPred, bD as termEquals, bE as termToString, bF as tryRunTranspiled, bG as unknown, bH as v } from './run-CDBM1fJZ.js';
3
3
  import { Node, File } from '@babel/types';
4
- import { p as parseSource, L as LeakBudget, E as EffectiveInterfaceOpts, b as LoadDepsFingerprint, v as CheckReport, N as NudoConstraint, c as ProjectionBudget } from './projection-budget-Dey0IUJa.js';
5
- export { w as CONSTRAINT_BUILDERS, x as CONSTRAINT_BUILDER_NAMES, y as CONSTRAINT_EXPR_RE, z as CheckAction, C as CheckIssue, A as CheckJson, B as CheckJsonMulti, D as ConstraintBuilder, F as Diagnostic, G as EffectiveInterface, H as GitlabCodeQualityIssue, J as INTERFACE_SOURCE_ABBR, K as InterfaceDiag, I as InterfaceSource, O as InterfaceTierInfo, a as InterfaceTierOpts, Q as NamedImport, R as NudoField, S as NudoFnConstraint, T as NudoSidecarError, U as NudoSig, V as RefineDiag, W as RefineEntry, X as RefineResolveOpts, Y as SELF, Z as actionsForIssue, _ as andC, $ as any, a0 as array, a1 as boolean, a2 as checkArg, a3 as checkCall, a4 as constraintAdmitsNullish, a5 as constraintToEntryAbs, a6 as derefConstraint, a7 as drainPromiseMicros, a8 as effectiveInterface, a9 as enterPromiseExecutorScope, aa as evalPromiseCtor, ab as evalPromiseMethod, ac as evalPromiseStatic, ad as execNudoModule, ae as extractDeclaredThrows, af as extractFnBudget, ag as extractNudoImports, ah as extractRefineReturnFromSource, ai as extractRefinesFromSource, aj as fn, ak as fnConstraintToEntryReqs, al as formatCheckReport, am as formatConstraint, an as formatDiagnostics, ao as formatEffectiveInterfaceDisplay, ap as formatGithubAnnotations, aq as formatGitlabCodeQuality, ar as formatInterfaceTierLine, as as generatedExportNames, at as getParseSourceCacheSize, au as getTerm, av as instantiateConstraint, aw as interfaceDiagCount, ax as interfaceSourceOf, ay as interfaceTierOf, az as isIntFlag, aA as isNodeModulesPath, aB as isNudoConstraint, aC as lazy, aD as leavePromiseExecutorScope, aE as lenTerm, aF as litC, aG as localNamedExports, aH as notePromiseExecutorFork, aI as nullable, aJ as number, aK as omit, aL as partial, aM as pick, aN as queuePromiseMicro, aO as refineDiagCount, aP as refineToIndexedFull, aQ as resetNudoModuleExecCache, aR as resetParseSourceCache, aS as resetSidecarLoadFailureCache, aT as serializeCheckJson, aU as serializeCheckJsonMulti, aV as setInterfaceDiagCollector, aW as setRefineDiagCollector, aX as shape, aY as sidecarClosureFingerprint, aZ as sidecarPathOf, a_ as string, a$ as takeInterfaceDiags, b0 as takeInterfaceDiagsSince, b1 as takeRefineDiags, b2 as takeRefineDiagsSince, b3 as throwConstraintToKinds, b4 as union } from './projection-budget-Dey0IUJa.js';
4
+ import { p as parseSource, L as LeakBudget, E as EffectiveInterfaceOpts, b as LoadDepsFingerprint, v as CheckReport, N as NudoConstraint, c as ProjectionBudget } from './projection-budget-BB2U2rwC.js';
5
+ export { w as CONSTRAINT_BUILDERS, x as CONSTRAINT_BUILDER_NAMES, y as CONSTRAINT_EXPR_RE, z as CheckAction, C as CheckIssue, A as CheckJson, B as CheckJsonMulti, D as ConstraintBuilder, F as Diagnostic, G as EffectiveInterface, H as GitlabCodeQualityIssue, J as INTERFACE_SOURCE_ABBR, K as InterfaceDiag, I as InterfaceSource, O as InterfaceTierInfo, a as InterfaceTierOpts, Q as NamedImport, R as NudoField, S as NudoFnConstraint, T as NudoSidecarError, U as NudoSig, V as RefineDiag, W as RefineEntry, X as RefineResolveOpts, Y as SELF, Z as actionsForIssue, _ as andC, $ as any, a0 as array, a1 as boolean, a2 as checkArg, a3 as checkCall, a4 as constraintAdmitsNullish, a5 as constraintToEntryAbs, a6 as derefConstraint, a7 as drainPromiseMicros, a8 as effectiveInterface, a9 as enterPromiseExecutorScope, aa as evalPromiseCtor, ab as evalPromiseMethod, ac as evalPromiseStatic, ad as execNudoModule, ae as extractDeclaredThrows, af as extractFnBudget, ag as extractNudoImports, ah as extractRefineReturnFromSource, ai as extractRefinesFromSource, aj as fn, ak as fnConstraintToEntryReqs, al as formatCheckReport, am as formatConstraint, an as formatDiagnostics, ao as formatEffectiveInterfaceDisplay, ap as formatGithubAnnotations, aq as formatGitlabCodeQuality, ar as formatInterfaceTierLine, as as generatedExportNames, at as getParseSourceCacheSize, au as getTerm, av as instantiateConstraint, aw as interfaceDiagCount, ax as interfaceSourceOf, ay as interfaceTierOf, az as isIntFlag, aA as isNodeModulesPath, aB as isNudoConstraint, aC as lazy, aD as leavePromiseExecutorScope, aE as lenTerm, aF as litC, aG as localNamedExports, aH as notePromiseExecutorFork, aI as nullable, aJ as number, aK as omit, aL as partial, aM as pick, aN as queuePromiseMicro, aO as refineDiagCount, aP as refineToIndexedFull, aQ as resetNudoModuleExecCache, aR as resetParseSourceCache, aS as resetSidecarLoadFailureCache, aT as serializeCheckJson, aU as serializeCheckJsonMulti, aV as setInterfaceDiagCollector, aW as setRefineDiagCollector, aX as shape, aY as sidecarClosureFingerprint, aZ as sidecarPathOf, a_ as string, a$ as takeInterfaceDiags, b0 as takeInterfaceDiagsSince, b1 as takeRefineDiags, b2 as takeRefineDiagsSince, b3 as throwConstraintToKinds, b4 as union } from './projection-budget-BB2U2rwC.js';
6
6
  export { $add, $arguments, $arr, $arrMutContainer, $arrRest, $arrWithHoles, $async, $asyncReturn, $await, $bitand, $bitnot, $bitor, $bitxor, $catchVal, $class, $classExpr, $collectionForEach, $concat, $copy, $del, $delRes, $div, $dynamicImport, $elems, $eq, $eqLoose, $fnVal, $for, $forInKeys, $forIter, $forOf, $fork, $ge, $gen, $get, $gt, $idx, $idxSet, $importMeta, $in, $instanceof, $instanceofNonIdent, $invoke, $invokeSuper, $isBreakTo, $isForkExit, $join, $le, $len, $lit, $loopBreak, $loopContinue, $loopReturn, $lt, $mod, $mul, $ne, $neLoose, $neg, $new, $not, $nullishTest, $obj, $objAccessor, $objRest, $optionalGet, $optionalInvoke, $orDefault, $pow, $pushLoopExit, $rawThis, $reStateCall, $regex, $rethrowIfNudoReturn, $set, $setKey, $shl, $shr, $spread, $staticInvoke, $sub, $super, $switch, $thisGet, $thisSet, $throw, $toNumber, $toNumeric, $tryCurrentMark, $tryDetachSoftCatch, $tryDigestSoftCatch, $tryDiscardSoft, $tryMark, $tryOrphanSoft, $tryPopMark, $tryReleaseSoftCatch, $tryReleaseSoftOut, $tryTakeSince, $typeof, $unknown, $updateAdd, $updateSub, $ushr, $while, $whileSeq, $yield, DEFAULT_MAX_LOOP_ITERS, EvalClassSpec, NudoLoopSignal, NudoReturn, NudoThrow, NudoUnsupportedError, TranspileOptions, asAbsVal, callAtFunctionBoundary, clearBClasses, clearStaleTermPred, currentExecPhi, fillTuple, foldRequireSpecArg, foldStaticStringExpr, getEvalClass, isArrMutator, isDefinitelyFalse, isDefinitelyTrue, isNudoBreak, isNudoContinue, isNudoReturn, isNudoThrow, litTruth, lookupObjAccessor, namespaceNameOf, pushLoopExit, pushThrowExit, registerEvalClass, runWithLoopExits, runtimeImportOf, takeLoopExits, takeThrowExits, transpile, transpileBodyNode, transpileExpression, transpileFile, transpileSource, withExecPhi } from './exec.js';
7
7
 
8
8
  /**
@@ -262,7 +262,7 @@ declare function mul(a: Abs, b: Abs, phi?: Phi): Abs;
262
262
  * 除法:字面量折叠;除以正/负常数时按单调性推界。
263
263
  * 除以 0:JS 语义为 ±Infinity / NaN,shape 仍 number(不报违例)。
264
264
  */
265
- declare function div(a: Abs, b: Abs, phi?: Phi): Abs;
265
+ declare function div(a: Abs, b: Abs, phi?: Phi, assume?: Phi): Abs;
266
266
  /**
267
267
  * 取模:字面量折叠;`x % k`(k 为有限非零字面量)仅当被除数有限时
268
268
  * 结果界在 (−|k|, |k|)。整数模可收紧到 [0, k),此处先做保守实数界。
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  effectiveInterface,
9
9
  evictGeneralizeMemoForPaths,
10
10
  execNudoModule,
11
+ extractAssumeSegs,
11
12
  extractBalancedParens,
12
13
  extractDeclaredThrows,
13
14
  extractFileEnvNames,
@@ -64,7 +65,7 @@ import {
64
65
  takeInterfaceDiagsSince,
65
66
  takeRefineDiags,
66
67
  takeRefineDiagsSince
67
- } from "./chunk-GHFGZ6PZ.js";
68
+ } from "./chunk-UTRLGWJL.js";
68
69
  import {
69
70
  $add,
70
71
  $arguments,
@@ -520,9 +521,10 @@ import {
520
521
  unknown,
521
522
  ushrAbs,
522
523
  v,
524
+ withAssumePremises,
523
525
  withExecPhi,
524
526
  withVar
525
- } from "./chunk-VKSROGOF.js";
527
+ } from "./chunk-OYQMVCUV.js";
526
528
 
527
529
  // src/environment.ts
528
530
  function createEnvironment(parent, bindings = /* @__PURE__ */ new Map()) {
@@ -793,7 +795,7 @@ function flattenSumArms(ret) {
793
795
  return out;
794
796
  }
795
797
  function isWidenedSumArm(m, parent) {
796
- return parent.conf === "partial" && m.shape.k === "prim" && !m.term && (!m.pred || m.pred.op === "true");
798
+ return (parent.conf === "partial" || m.conf === "widened") && m.shape.k === "prim" && !m.term && (!m.pred || m.pred.op === "true");
797
799
  }
798
800
  function provesBarePrimDomain(arm, constraint) {
799
801
  if (constraint.members) {
@@ -820,7 +822,7 @@ function assertImplies(ret, constraint, opts) {
820
822
  const phi = opts?.phi ?? pTrue;
821
823
  if (ret.shape.k === "never") return proved();
822
824
  if (isAnyConstraint(constraint)) return proved();
823
- if (isValueSetUnknown(ret)) {
825
+ if (isValueSetUnknown(ret) && ret.conf !== "widened") {
824
826
  return unprovable(
825
827
  ret.shape.k === "any" ? "any does not establish the return contract" : "inference failure (unknown/opaque) \u2014 cannot prove the return contract"
826
828
  );
@@ -876,6 +878,9 @@ function assertImpliesSingle(arm, constraint, opts, phi) {
876
878
  return constraintAdmitsNullish(constraint) ? proved() : disproved(`nullish return \u22AD ${formatConstraint(constraint)}`);
877
879
  }
878
880
  if (isValueSetUnknown(arm)) {
881
+ if (arm.conf === "widened" && arm.shape.k === "prim" && !arm.term && (!arm.pred || arm.pred.op === "true") && provesBarePrimDomain(arm, constraint)) {
882
+ return proved();
883
+ }
879
884
  return unprovable(
880
885
  arm.shape.k === "any" ? "any does not establish the return contract" : "inference failure (unknown/opaque) \u2014 cannot prove the return contract"
881
886
  );
@@ -1372,6 +1377,7 @@ function numericBound(p, self) {
1372
1377
  if (p.op !== "gt" && p.op !== "ge" && p.op !== "lt" && p.op !== "le") return void 0;
1373
1378
  if (!self) return void 0;
1374
1379
  if (termEquals(p.a, self) && p.b.op === "lit" && typeof p.b.value === "number") {
1380
+ if (!Number.isFinite(p.b.value)) return void 0;
1375
1381
  return { op: p.op, n: p.b.value };
1376
1382
  }
1377
1383
  return void 0;
@@ -2340,6 +2346,49 @@ function sidecarDiagIssues(diags) {
2340
2346
  }
2341
2347
  return out;
2342
2348
  }
2349
+ function assumePremisesPhiOf(name, source, file, issues) {
2350
+ const segs = extractAssumeSegs(source, name);
2351
+ if (segs.length === 0) return void 0;
2352
+ const meta = extractFn(source, name, file);
2353
+ const bindable = /* @__PURE__ */ new Set([
2354
+ ...contractParamNameSet(meta?.formals ?? []),
2355
+ ...meta?.params ?? []
2356
+ ]);
2357
+ const surface = [...bindable].join(", ");
2358
+ const preds = [];
2359
+ const display = [];
2360
+ for (const seg of segs) {
2361
+ if (seg.kind === "bad") {
2362
+ issues.push({
2363
+ severity: "error",
2364
+ code: "nudo:assume-invalid",
2365
+ message: `${name}: malformed @nudo:assume '${seg.raw}' (expected <param> <= <param> or <param> >= <param>; constant bounds belong to sidecar contracts)`,
2366
+ actual: seg.raw,
2367
+ expected: "<param> <= <param>",
2368
+ suggestion: "cross-variable lemmas: @nudo:assume d <= max \u2014 constant bounds: sidecar number().le(10)",
2369
+ fn: name
2370
+ });
2371
+ continue;
2372
+ }
2373
+ const bad = [seg.lhs, seg.rhs].filter((n) => !bindable.has(n));
2374
+ if (bad.length > 0) {
2375
+ issues.push({
2376
+ severity: "error",
2377
+ code: "nudo:assume-invalid",
2378
+ message: `${name}: @nudo:assume parameter name(s) not in the formal parameter list (${bad.join(", ")})`,
2379
+ actual: bad.join(", "),
2380
+ expected: surface || "(no params)",
2381
+ suggestion: `reference one of: ${surface || "(function has no params)"}`,
2382
+ fn: name
2383
+ });
2384
+ continue;
2385
+ }
2386
+ preds.push(le(v(seg.lhs), v(seg.rhs)));
2387
+ display.push(`${seg.lhs} <= ${seg.rhs}`);
2388
+ }
2389
+ if (preds.length === 0) return void 0;
2390
+ return { phi: and(...preds), display };
2391
+ }
2343
2392
  function checkSourceInner(filePath, source, file, phi, opts, issues, signatures, names, truncated, identityOpts = opts, depsFp, sidecarFp) {
2344
2393
  const hasRefineDirective = stripStringsKeepComments(source).includes("@nudo:contract");
2345
2394
  const entryNames = localNamedExports(source);
@@ -2367,6 +2416,8 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2367
2416
  return eff;
2368
2417
  };
2369
2418
  for (const name of names) {
2419
+ const assumePhi = assumePremisesPhiOf(name, source, file, issues);
2420
+ const runSeeded = (body) => assumePhi === void 0 ? body() : withAssumePremises(assumePhi.phi, body);
2370
2421
  if (opts.skips?.has(name)) {
2371
2422
  const declared = opts.skips.get(name) ?? null;
2372
2423
  const meta = extractFn(source, name, file);
@@ -2411,19 +2462,21 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2411
2462
  }
2412
2463
  continue;
2413
2464
  }
2414
- const g = generalizeFromAst(name, source, {
2415
- file,
2416
- refine: {
2417
- loadModule: refineLoad,
2418
- fromFile: refineFrom,
2419
- ...autoBind !== void 0 ? { autoBind } : {},
2420
- ...projectDir !== void 0 ? { projectDir } : {}
2421
- },
2422
- depsFp,
2423
- sidecarFp,
2424
- modules: opts.modules,
2425
- ...opts.inject ? { inject: opts.inject } : {}
2426
- });
2465
+ const g = runSeeded(
2466
+ () => generalizeFromAst(name, source, {
2467
+ file,
2468
+ refine: {
2469
+ loadModule: refineLoad,
2470
+ fromFile: refineFrom,
2471
+ ...autoBind !== void 0 ? { autoBind } : {},
2472
+ ...projectDir !== void 0 ? { projectDir } : {}
2473
+ },
2474
+ depsFp,
2475
+ sidecarFp,
2476
+ modules: opts.modules,
2477
+ ...opts.inject ? { inject: opts.inject } : {}
2478
+ })
2479
+ );
2427
2480
  if (!g) {
2428
2481
  const isEntryCandidate = entryNames.has(name) || name === "default" || entryNames.has("default") && isDefaultExportName(source, name);
2429
2482
  if (isEntryCandidate && entryThrowsMode !== "off") {
@@ -2483,7 +2536,7 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2483
2536
  const isEntry = entryNames.has(name) || name === "default" || entryNames.has("default") && isDefaultExportName(source, name) || name.includes(".") && entryNames.has(name);
2484
2537
  let throwsDisplay;
2485
2538
  if (isEntry && entryThrowsMode !== "off") {
2486
- const effects = collectEntryMayThrows(source, name, g, file, phi, opts);
2539
+ const effects = runSeeded(() => collectEntryMayThrows(source, name, g, file, phi, opts));
2487
2540
  throwsDisplay = formatThrowsAbs(mayThrowEffectsToAbs(effects));
2488
2541
  const declared = extractDeclaredThrows(source, name) ?? effectiveInterfaceCached(name)?.throws?.kinds;
2489
2542
  const remaining = filterGateThrows(effects, declared, ignoreThrows);
@@ -2504,7 +2557,7 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2504
2557
  });
2505
2558
  }
2506
2559
  } else if (isEntry) {
2507
- const effects = collectEntryMayThrows(source, name, g, file, phi, opts);
2560
+ const effects = runSeeded(() => collectEntryMayThrows(source, name, g, file, phi, opts));
2508
2561
  throwsDisplay = formatThrowsAbs(mayThrowEffectsToAbs(effects));
2509
2562
  }
2510
2563
  const fmt = formatSigCached(g.symbolic, name);
@@ -2598,7 +2651,8 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2598
2651
  name,
2599
2652
  display,
2600
2653
  eff.returns.constraint,
2601
- g.symbolic
2654
+ g.symbolic,
2655
+ assumePhi?.display
2602
2656
  );
2603
2657
  if (retIssues.some((i) => i.severity === "error")) returnViolated.add(name);
2604
2658
  issues.push(...retIssues);
@@ -2794,9 +2848,24 @@ function checkSourceInner(filePath, source, file, phi, opts, issues, signatures,
2794
2848
  }
2795
2849
  };
2796
2850
  }
2797
- function checkReturnConstraint(fnName, cName, constraint, ret) {
2851
+ function checkReturnConstraint(fnName, cName, constraint, ret, assumeDisplay) {
2798
2852
  const proof = assertImplies(ret, constraint);
2799
- if (proof.status === "proved") return [];
2853
+ if (proof.status === "proved") {
2854
+ if (assumeDisplay && assumeDisplay.length > 0) {
2855
+ return [
2856
+ {
2857
+ severity: "info",
2858
+ code: "nudo:assume-relied",
2859
+ message: `${fnName}: return contract proved via @nudo:assume ${assumeDisplay.join("; ")}`,
2860
+ actual: `proof relies on user lemma${assumeDisplay.length > 1 ? "s" : ""}`,
2861
+ expected: "engine verifies the obligation, not the lemma itself",
2862
+ suggestion: "@nudo:assume is a user-supplied axiom \u2014 falsify it with concrete @nudo:case runs (nudo test)",
2863
+ fn: fnName
2864
+ }
2865
+ ];
2866
+ }
2867
+ return [];
2868
+ }
2800
2869
  const push = (severity, actual, expected, suggestion) => [
2801
2870
  {
2802
2871
  severity,
@@ -1,7 +1,7 @@
1
- import { I as InterfaceSource, a as InterfaceTierOpts, p as parseSource, C as CheckIssue } from './projection-budget-Dey0IUJa.js';
2
- export { L as LeakBudget, b as LoadDepsFingerprint, M as MAX_PROMISE_MICROS, P as PROJECTION_MAX_DEPTH, c as ProjectionBudget, d as ProjectionStopReason, e as defaultLeakBudget, f as exceedsBudget, g as exportedNameOfLocal, h as extractAllLoadSpecs, i as getPromiseMicrosLength, l as leakIfNeeded, j as loadModuleDepsFingerprint, m as maybeLeak, n as normPath, r as resetLeakCounter, k as resolveDepPath, s as sidecarSpecsOf, o as stablePathKey, q as stablePathKeyGraph, t as termDepth, u as termNodes } from './projection-budget-Dey0IUJa.js';
3
- import { A as Abs, T as Term, c as AbsModuleExports, d as AstEnv } from './run-Bgj2tCrv.js';
4
- export { $ as $tryDigestSoft, e as $tryMarkSoft, f as $tryReleaseSoft, E as ERROR_FAMILY, g as EvalFallbackStats, M as MayThrowEffect, h as absLit, i as absNumLit, j as absStrLit, k as allAbsStr, l as clearPureCallMemo, m as clearPureMemo, n as enterEvalCallBudgetSession, o as errorTypeAbs, p as exitEvalCallBudgetSession, q as filterDeclaredThrows, r as filterGateThrows, s as filterIgnoredThrows, t as flushMayThrowEffects, u as formatThrowsAbs, v as getEvalCallBudgetState, w as getEvalFallbackStats, x as getMayThrowCollector, y as isThrowsIgnored, z as mayThrowEffectsToAbs, B as orphanMayThrowEffects, D as popMayThrowFrame, F as pushMayThrowFrame, G as recordMayThrow, H as resetEvalFallbackStats, I as runWithMayThrowSession, J as setMayThrowCollector, K as throwAbsToKinds, N as throwsKindCovered, O as withEvalBudgetOverride } from './run-Bgj2tCrv.js';
1
+ import { I as InterfaceSource, a as InterfaceTierOpts, p as parseSource, C as CheckIssue } from './projection-budget-BB2U2rwC.js';
2
+ export { L as LeakBudget, b as LoadDepsFingerprint, M as MAX_PROMISE_MICROS, P as PROJECTION_MAX_DEPTH, c as ProjectionBudget, d as ProjectionStopReason, e as defaultLeakBudget, f as exceedsBudget, g as exportedNameOfLocal, h as extractAllLoadSpecs, i as getPromiseMicrosLength, l as leakIfNeeded, j as loadModuleDepsFingerprint, m as maybeLeak, n as normPath, r as resetLeakCounter, k as resolveDepPath, s as sidecarSpecsOf, o as stablePathKey, q as stablePathKeyGraph, t as termDepth, u as termNodes } from './projection-budget-BB2U2rwC.js';
3
+ import { A as Abs, T as Term, c as AbsModuleExports, d as AstEnv } from './run-CDBM1fJZ.js';
4
+ export { $ as $tryDigestSoft, e as $tryMarkSoft, f as $tryReleaseSoft, E as ERROR_FAMILY, g as EvalFallbackStats, M as MayThrowEffect, h as absLit, i as absNumLit, j as absStrLit, k as allAbsStr, l as clearPureCallMemo, m as clearPureMemo, n as enterEvalCallBudgetSession, o as errorTypeAbs, p as exitEvalCallBudgetSession, q as filterDeclaredThrows, r as filterGateThrows, s as filterIgnoredThrows, t as flushMayThrowEffects, u as formatThrowsAbs, v as getEvalCallBudgetState, w as getEvalFallbackStats, x as getMayThrowCollector, y as isThrowsIgnored, z as mayThrowEffectsToAbs, B as orphanMayThrowEffects, D as popMayThrowFrame, F as pushMayThrowFrame, G as recordMayThrow, H as resetEvalFallbackStats, I as runWithMayThrowSession, J as setMayThrowCollector, K as throwAbsToKinds, N as throwsKindCovered, O as withEvalBudgetOverride } from './run-CDBM1fJZ.js';
5
5
  import { File, Node } from '@babel/types';
6
6
 
7
7
  /**
package/dist/internal.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  stableAnalyzeKeySource,
22
22
  stablePathKey,
23
23
  stablePathKeyGraph
24
- } from "./chunk-GHFGZ6PZ.js";
24
+ } from "./chunk-UTRLGWJL.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-VKSROGOF.js";
172
+ } from "./chunk-OYQMVCUV.js";
173
173
 
174
174
  // src/algebra/inlay.ts
175
175
  function listFunctions(source) {
@@ -1,5 +1,5 @@
1
1
  import { File } from '@babel/types';
2
- import { A as Abs, T as Term, P as PrimName, a as Pred, L as LiteralValue, b as Phi, C as Confidence } from './run-Bgj2tCrv.js';
2
+ import { A as Abs, T as Term, P as PrimName, a as Pred, L as LiteralValue, b as Phi, C as Confidence } from './run-CDBM1fJZ.js';
3
3
 
4
4
  declare function resetParseSourceCache(): void;
5
5
  declare function getParseSourceCacheSize(): number;
@@ -840,9 +840,12 @@ type TranspiledCallResult = {
840
840
  declare function isAbsVal(v: unknown): v is Abs;
841
841
  /** 调用 runTranspiled 导出(捕获 $throw)。opts.phi:入口 Φ 种子
842
842
  * (instantiate/symbolic 的约束入口——eval 侧路径条件收窄)。
843
+ * opts.assumePremises(#152 用户引理):入口前提独立槽种子——与 phi 分槽
844
+ * (不随调用链继承),消费方经 currentAssumePremises() 读。
843
845
  * 嵌套进入(导出桥 apply)继承外层预算;仅最外层宿主入口重置。 */
844
846
  declare function callTranspiledExportFull(exports: Record<string, unknown>, name: string, args: Abs[], opts?: {
845
847
  phi?: Phi;
848
+ assumePremises?: Phi;
846
849
  }): TranspiledCallResult;
847
850
  /** 调用 runTranspiled 导出(仅结果) */
848
851
  declare function callTranspiledExport(exports: Record<string, unknown>, name: string, args: Abs[]): Abs;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nudojs/core",
3
- "version": "1.10.2",
3
+ "version": "1.11.0",
4
4
  "engines": {
5
5
  "node": ">=20"
6
6
  },