* Determine whether the operands of a `Set` expression describe a * set-builder (comprehension) rather than a literal set. * * A `Set` is a comprehension iff it has exactly two operands and the second * operand is an indexing-set form: * * - `["Set", body, ["Element", v, domain, cond?]]` — the
( ops: ReadonlyArray<Expression> )
| 2827 | // ── Property stores: `p.age = 43` ──────────────────────────────────────────── |
| 2828 | // |
| 2829 | // Assignment through a `Field` target is a **store into a mutable object**, |
| 2830 | // and it is legal on nothing else. Spec: `docs/TYPE_SYSTEM_ROADMAP.md` |
| 2831 | // Appendix B, "Assigning to a property" and "A store writes the evaluated |
| 2832 | // value". The two functions below are the canonical-time and evaluate-time |
| 2833 | // halves of one decision, and `Assign` (`library/core.ts`) is their only |
| 2834 | // caller. |
| 2835 | // |
| 2836 | |
| 2837 | /** |
| 2838 | * Does the receiver's own object layout declare this field? |
| 2839 | * |
| 2840 | * This is the question that has to be asked BEFORE the protocol-property |
| 2841 | * route, and it is asked of the STATIC type because the canonical handler has |
| 2842 | * no value in hand. An object's stored fields belong to the object: when a |
| 2843 | * `readwrite` protocol happens to declare a property of the same name, the |
| 2844 | * store still wins. |
| 2845 | * |
| 2846 | * A `true` here means "this is certainly a store, do not refuse it statically", |
| 2847 | * and it earns its place by reading a bare-symbol receiver off its DEFINITION: |
| 2848 | * {@link fieldAssignmentVerdict} canonicalizes the receiver instead, which |
| 2849 | * folds a single-letter target into the library constant of that name (`i` |
| 2850 | * becomes `ImaginaryUnit`) and would report a settled non-object type for a |
| 2851 | * binding that in fact holds an object. |
| 2852 | * |
| 2853 | * Answers `false` whenever the layout cannot be read — an unsettled receiver, |
| 2854 | * a bare `object` annotation (which promises fields without naming them), a |
| 2855 | * union. Those defer to the runtime route, where `objectFieldStore` puts the |
| 2856 | * instance's own layout back in charge. |
| 2857 | * |
| 2858 | * `isObjectFieldStore()` in `boxed-expression/effects-of.ts` resolves a |
| 2859 | * receiver by the same two rules, for a different purpose: labelling the |
| 2860 | * assignment's EFFECT. It deliberately parts company on the union case, where |
| 2861 | * it claims the store rather than declining — an effect label cannot be |
| 2862 | * deferred to evaluation the way this precedence decision can, and the store |
| 2863 | * does happen. Keep the receiver resolution in step; that one arm is meant to |
| 2864 | * differ. |
| 2865 | */ |
| 2866 | export function objectLayoutOwnsField( |
| 2867 | ce: ComputeEngine, |
| 2868 | lhs: Expression |
| 2869 | ): boolean { |
| 2870 | if (!isFunction(lhs, 'Field')) return false; |
| 2871 | if (!isString(lhs.ops[1])) return false; |
| 2872 | const name = lhs.ops[1].string; |
| 2873 | const base = lhs.ops[0]; |
| 2874 | if (base === undefined) return false; |
| 2875 | |
| 2876 | // A bare-symbol receiver is read off its DEFINITION, never by canonicalizing |
| 2877 | // it: canonicalizing a single-letter target folds it into the constant of |
| 2878 | // that name (`i` becomes `ImaginaryUnit`), which is why `Assign` keeps its |
| 2879 | // left operand raw at all. |
| 2880 | const rootName = sym(base); |
| 2881 | if (rootName !== undefined) { |
| 2882 | const def = ce.lookupDefinition(rootName); |
| 2883 | const t = |
| 2884 | def !== undefined && isValueDef(def) ? def.value.type.type : undefined; |
| 2885 | return ( |
| 2886 | t !== undefined && objectLayoutOfType(t)?.elements[name] !== undefined |