/** * Which locations can carry a fact at all, and what a write takes away. * * A narrowing is filed under a *location* — a binding name, or a dotted path * of stored fields rooted at one. This module owns both halves of that: the * rules deciding whether a path is stable enough to file under (a getter is * recomputed on every read, an index is not a name, so neither qualifies), and * the invalidation family that retracts the facts an assignment, a mutating * collection call, or an aliasing member write just falsified. * * D114 R1d: split out of `./narrowing.ts` during the move, which came to 930 * lines as one file — over the 800-line budget of D115 §一.1. The two halves * answer different questions ("what does this condition prove" against "where * may a fact live, and what kills it"), and the dependency runs one way: * `Narrowing` calls in here, and nothing here calls back. */ import { type AssignmentStatement, type Expression } from "../../ast.ts"; import { type ClassField } from "../../contracts.ts"; import { type Span } from "../../source.ts"; import { type ValueType } from "../../types.ts"; import { type Binding, type MemberNarrowing } from "../scopes.ts"; /** An assignment statement's target, the one shape `invalidateAssignmentNarrowings` accepts. */ type AssignmentTarget = AssignmentStatement["target"]; /** * Everything the location half asks of the analyzer that hosts it, and nothing * more. */ export interface MemberLocationsHost { conditionSubjectText(condition: Expression): string | null; readonly currentClass: string | null; equalityTypesIntersect(leftSource: ValueType, rightSource: ValueType): boolean; expandAliases(type: ValueType, seen?: ReadonlySet): ValueType; fieldsOf(identity: string): ReadonlyMap | null; findField(className: string, name: string): ClassField | null; findGetter(className: string, name: string): { readonly owner: string; readonly type: ValueType; readonly abstract: boolean; } | null; findStaticField(className: string, name: string): ClassField | null; findStaticGetter(className: string, name: string): ValueType | null; readonly flowFrameDepth: number; inferredExpressionType(expression: Expression): ValueType; readonly inferredExpressionTypes: Map; lookup(name: string): Binding | null; readonly memberNarrowings: Map[]; privateFieldForAccess(className: string, name: string, staticMember: boolean): ClassField | null; readonly privateGetters: Map>; readonly privateStaticGetters: Map>; readonlyDataViewOf(type: ValueType): ValueType; readonlyFieldsOf(identity: string): ReadonlySet | null; recordFlowFactOrigin(binding: Binding): void; readonly scopes: Map[]; typeError(message: string, errorSpan: Span): void; } export declare class MemberLocations { private readonly host; constructor(host: MemberLocationsHost); /** * D90 R16: a getter is recomputed on every read, so a check against one * narrows nothing and silently does nothing. Say so where it is written, * and name the one spelling that works: bind the getter to a `const` and * check that. Following `?.` instead would compute the getter twice. */ checkGetterNarrowingTest(condition: Expression): void; /** The location a condition would narrow, for the forms that narrow one. */ private narrowingSubjectExpression; /** The property name when an expression reads a getter rather than a stored field. */ getterAccessProperty(expression: Expression): string | null; /** * A check that narrows a location installs a shadow of the binding in the * scope it entered, so nested checks leave one shadow per enclosing scope. * Clearing only the innermost shadow lets an outer scope keep a fact this * write just falsified — visible after a `while` whose condition narrows * the same name its body assigns, where the body's shadow is discarded with * the body scope and the loop's own shadow never learns of the write. */ invalidateShadowedNarrowings(name: string, target: Binding | null): void; stableMemberAccessPath(expression: Expression): string | null; stableOptionalMemberAccessPath(expression: Expression): string | null; /** * A getter is recomputed on every read and an index is not a name, so * neither is a stable location a fact can be filed under. What is left is a * stored field of a record, a named type, or a class. */ stableDataMember(objectExpression: Expression, property: string): boolean; discriminatedDataField(original: ValueType, property: string): ValueType | null; dataFieldIsReadonly(original: ValueType, property: string): boolean; lookupMemberNarrowing(path: string): ValueType | null; lookupMemberNarrowingEntry(path: string): MemberNarrowing | null; invalidateAssignmentNarrowings(target: AssignmentTarget, binding: Binding | null): void; invalidateMemberNarrowings(path: string): void; private invalidateMemberDescendantNarrowings; invalidateMutableCollectionCallReceiver(callee: Extract): void; invalidateCurrentMemberNarrowings(): void; invalidateAliasableMemberNarrowings(target: Extract): void; /** The current type of the binding a member-narrowing path is rooted at, or null when it cannot be resolved. */ private memberNarrowingRootType; } export {}; //# sourceMappingURL=locations.d.ts.map