* Check a candidate bound against the existing bounds for the same subject * (design §4.3 — bounds-level consistency only, per subject). * * Returns `'tautology'` if the new bound is already implied, * `'contradiction'` if it is incompatible, `undefined` otherwise (store it).
( existing: IntervalBounds, candidate: IntervalBounds )
| 1068 | cause?: Expression |
| 1069 | ): AssumeResult { |
| 1070 | if (!hasDef(ce, symbol)) { |
| 1071 | ce.declare(symbol, type); |
| 1072 | recordDeclaredByAssumption(ce, symbol, cause); |
| 1073 | return 'ok'; |
| 1074 | } |
| 1075 | |
| 1076 | // Shadow a parent-scope declaration in the current scope so the |
| 1077 | // assumption is reverted when the scope is popped. The shadow's creation |
| 1078 | // is recorded here — the write below then only appends when it actually |
| 1079 | // changes the shadow's type (append-on-change), so the creation entry is |
| 1080 | // the shadow's one guaranteed provenance record. |
| 1081 | if (!ce.context?.lexicalScope?.bindings.has(symbol)) { |
| 1082 | ce.declare(symbol, type); |
| 1083 | recordDeclaredByAssumption(ce, symbol, cause); |
| 1084 | } |
| 1085 | |
| 1086 | const def = ce.lookupDefinition(symbol); |
| 1087 | if (isValueDef(def)) { |
| 1088 | // An explicitly declared, known type is *narrowed* to the meet of the |
| 1089 | // declared type and the assumed type. It is a contradiction only when |
| 1090 | // that meet is empty. The old check (`!isSubtype(assumed, declared)`) |
| 1091 | // misfired whenever the assumed type was not a *subtype* of the declared |
| 1092 | // one even though they overlapped, e.g. `assume(q ∈ ℤ)` on a |
| 1093 | // `finite_number`-declared `q` (`integer ⊄ finite_number` only because |
| 1094 | // `integer` admits ±∞ — the meet is the satisfiable `finite_integer`). |
| 1095 | if ( |
| 1096 | def.value.type && |
| 1097 | !def.value.type.isUnknown && |
| 1098 | !def.value.inferredType |
| 1099 | ) { |
| 1100 | const meet = reduceType({ |
| 1101 | kind: 'intersection', |
| 1102 | types: [type, def.value.type.type], |
| 1103 | }); |
| 1104 | if (isEmptyType(meet)) return 'contradiction'; |
| 1105 | const previousMeet = def.value.type; |
| 1106 | def.value.type = new BoxedType(meet, ce._typeResolver); |
| 1107 | def.value.inferredType = false; |
| 1108 | recordAssumedType(ce, def.value, cause, previousMeet); |
| 1109 | return 'ok'; |
| 1110 | } |
| 1111 | const previous = def.value.type; |
| 1112 | def.value.type = new BoxedType(type, ce._typeResolver); |
| 1113 | // The type was explicitly asserted: it is no longer an inferred type |
| 1114 | // (so a subsequent bare-symbol inequality won't widen it to 'real') |
no test coverage detected