(ce: ComputeEngine, value: NumericValue)
| 967 | |
| 968 | // Not a recognized MathJSON object (no 'fn'/'str'/'sym'/'num'/'dict' |
| 969 | // key). Return an Error expression rather than throwing so callers |
| 970 | // boxing untrusted input never have to special-case a JS exception. |
| 971 | return ce.error('unexpected-mathjson', stringifyForError(expr)); |
| 972 | } |
| 973 | |
| 974 | return ce.symbol('Undefined'); |
| 975 | } |
| 976 | |
| 977 | /** |
| 978 | * True when every declared parameter of a signature (required, optional and |
| 979 | * variadic) is a numeric type (a subtype of `number`). Used to restrict the |
| 980 | * post-canonical argument re-validation in `makeCanonicalFunction` to the |
| 981 | * pure-numeric operators (`Sin`, `Factorial`, …) whose custom canonical |
| 982 | * handlers historically only checked arity. A signature with no parameters, or |
| 983 | * any non-numeric parameter, returns `false` so structural/higher-order |
| 984 | * operators are left untouched. |
| 985 | */ |
| 986 | function allParamsNumeric(signature: Type): boolean { |
| 987 | if (typeof signature === 'string') return false; |
| 988 | if (signature.kind !== 'signature') return false; |
| 989 | const params: Type[] = [ |
| 990 | ...(signature.args?.map((x) => x.type) ?? []), |
| 991 | ...(signature.optArgs?.map((x) => x.type) ?? []), |
| 992 | ...(signature.variadicArg ? [signature.variadicArg.type] : []), |
| 993 | ]; |
| 994 | if (params.length === 0) return false; |
| 995 | return params.every((t) => isSubtype(t, 'number')); |
| 996 | } |
| 997 | |
| 998 | /** |
| 999 | * The parameter types an operand at position `idx` could be bound to by a |
| 1000 | * value definition's declared signature — one entry per overload arm (a |
| 1001 | * single entry for a plain signature). Empty when the position is beyond |
| 1002 | * every arm's parameters (an arity error, diagnosed elsewhere). |
| 1003 | * |
| 1004 | * Each type is returned GROUND, because the only consumer asks |
| 1005 | * `provablyDisjoint` whether the operand refutes the parameter, and that |
| 1006 | * predicate requires ground inputs: a quantified signature (`(T) -> list<T> |
| 1007 | * where T: number`) stores its parameters with the variable still free, so |
| 1008 | * handing them over raw walked an open `T` into |
| 1009 | * `provablyDisjoint`/`typeCategory`, which the type-variable design forbids |
| 1010 | * (the dev-time `console.assert` in `assertGroundType`, |
| 1011 | * `common/type/subtype.ts`, is the tripwire for it). |
| 1012 | * |
| 1013 | * Grounding happens on the WHOLE arm, not on the extracted parameter, because |
| 1014 | * a variable's bound lives in the arm's `where` clause (`typeParams`) and is |
| 1015 | * out of reach once the parameter is detached. `readTypeVariablesAsBounds` |
| 1016 | * reads each variable as its declared bound — so `T: number` becomes `number` |
| 1017 | * and a `string` operand provably refutes it, which is the whole point of the |
| 1018 | * check — and an unbounded variable as `unknown`, which nothing refutes. |
| 1019 | * `groundSkeleton` then covers any variable the arm does not itself quantify |
| 1020 | * (one bound by a nested arrow, say), reading it as the widest type admissible |
| 1021 | * at its position so no refutation is ever claimed that the bound does not |
| 1022 | * support. |
| 1023 | */ |
| 1024 | function candidateParamsAt(valueType: Type, idx: number): Type[] { |
| 1025 | const arms: ReadonlyArray<FunctionSignature> = |
| 1026 | typeof valueType !== 'string' && valueType.kind === 'signature' |
no test coverage detected