/** * The two roles a class may declare with an annotation: `@dispose`, which makes * it a resource a `using` scope owns and releases, and `@iterate`, which makes * it a source a `for` loop can walk. * * D114 R1d: the role half of the class cluster. Both roles are the same shape — * an annotated member, a contract derived from it, an inherited answer, and a * guidance sentence for the refusals — so they are read together. */ import { type ClassDeclaration, type ClassDisposeBlock, type ClassIterateBlock, type Expression, type Statement, type UsingDeclaration } from "../../ast.ts"; import { type ClassField, type ClassInfo, type DisposalContract } from "../../contracts.ts"; import { type Diagnostic, type DiagnosticFix } from "../../diagnostic.ts"; import { type Span } from "../../source.ts"; import { type ValueType } from "../../types.ts"; import { type TypeReferences } from "../declarations/references.ts"; import { type LoweringRecorder } from "../lowering-recorder.ts"; import { type ReturnContext } from "../functions.ts"; import { type Binding, type BuiltinTypeNamePosition } from "../scopes.ts"; /** * Everything this half of the class cluster asks of the analyzer that hosts * it. The four halves share one host object; the union of their interfaces is * what the analyzer builds. */ export interface ClassRolesHost { analyzeStatements(statements: readonly Statement[]): void; readonly arrowOwnedCaptures: Map; readonly asynchronousFunctions: boolean[]; blockAlwaysReturns(statements: readonly Statement[]): boolean; classInfo(key: string): ClassInfo | undefined; readonly classes: Map; currentClass: string | null; declareBinding(name: string, mutable: boolean, type: ValueType, declarationSpan: Span, internal?: boolean, declaredType?: ValueType, importSource?: string, typeNamePosition?: BuiltinTypeNamePosition): void; readonly diagnostics: Diagnostic[]; enterScope(): void; exitScope(): void; expandAliases(type: ValueType, seen?: ReadonlySet): ValueType; readonly exportPositionCandidates: { readonly className: string; readonly member: string; readonly span: Span; }[]; extensionExpressionContainsDirectAwait(expression: Expression, contains: (expression: Expression) => boolean): boolean | undefined; extensionStatementContainsDirectAwait(statement: Statement, containsExpression: (expression: Expression) => boolean, containsBlock: (statements: readonly Statement[]) => boolean): boolean | undefined; fieldsOf(identity: string): ReadonlyMap | null; finallyLoopDepths: number[]; findField(className: string, name: string): ClassField | null; findMethod(className: string, name: string): { readonly owner: string; readonly type: ValueType; readonly abstract: boolean; } | null; flowFrameDepth: number; functionDepth: number; inferAnnotationFreeHead(expression: Expression): ValueType; inferCollectedFunctionResult(returned: readonly ValueType[], fallsThrough: boolean): ValueType; readonly inferredFunctionResultSeeds: ReadonlyMap; readonly inferredFunctionResultTypes: Map; lookup(name: string): Binding | null; loopDepth: number; readonly lowering: LoweringRecorder; ownershipScopeRejection(): string | null; readonly returnContexts: ReturnContext[]; readonly scopes: Map[]; selfClassType(className: string): ValueType; superMemberContext: "instance" | "static" | null; typeError(message: string, errorSpan: Span, fix?: DiagnosticFix): void; readonly typeReferences: TypeReferences; } export declare class ClassRoles { private readonly host; constructor(host: ClassRolesHost); /** * D43 item 69: `using name = expression` claims ownership of a resource for * the enclosing scope. The value's type must declare the release contract, * the scope must be able to run it, and the module top level — which lives * until the process ends — has no scope exit to release at. */ analyzeUsingDeclaration(statement: UsingDeclaration): void; /** * D51 rule 101: an owned resource may not leave the scope that releases it. * `using` means "this scope owns it and guarantees the release", so letting * the value out hands back a reference that is already known to be dead — * which is the construct's definition, not a restriction on top of it. The * judgement is *storage and return*, never use: passing the handle to a * function stays legal, because a callee borrows and must not assume * ownership. Returns the owned binding an expression carries, or null. */ carriedOwnedResource(expression: Expression | null): { readonly handle: string; readonly depth: number; } | null; /** The scope nesting level a name is declared at, or 0 when it is not a local. */ bindingScopeDepth(name: string): number; rejectOwnedResourceEscape(expression: Expression | null, action: string, errorSpan: Span): boolean; /** * The release contract of a value's type: a class's own `@dispose:` block, or * a standard capability handle, which delegates to the verb it already * publishes (`close()` or `stop()`) rather than being renamed for `using`. */ disposalContract(source: ValueType): DisposalContract | null; /** * D51 rule 102 + item NEW-D4. Rule 102 makes the compiler chain a derived * `@dispose:` into its base's, so the emitter is told which classes forward * and whether the forwarded release awaits. NEW-D4 is the soundness half: * `using` reads the release contract off the *static* type, so a subclass * that starts awaiting where its ancestors do not would be released without * an await through a base-typed binding — an unhandled rejection that kills * the process. Adding `await` downward is therefore rejected at the subclass; * an ancestor that already awaits carries every descendant with it. */ checkDisposalChain(statement: ClassDeclaration, baseName: string | null): void; /** Every `@dispose:` a class releases through, most derived first (D51 rule 102). */ disposalChain(className: string): readonly ("sync" | "async")[]; disposalGuidance(source: ValueType): string; /** * D43 item 69: the `@dispose:` body is a release contract, not a method. It * runs with `self` in scope and may `await`; whether it actually does is what * decides that a `using` of this class needs an async scope. */ analyzeClassDispose(statement: ClassDeclaration, block: ClassDisposeBlock): void; /** D68 rule 177: the convergence key of one `@iterate:` block. */ iterationResultKey(block: ClassIterateBlock): string; /** * D68 rule 177: `@iterate:` carries no result annotation — the block *is* the * answer — so the class shape pre-pass reads what the previous convergence * pass learned. Without the seed, a use written above the class would see an * unresolved placeholder, which is the same problem an omitted function * result has and gets the same solution. */ seededIterationSource(block: ClassIterateBlock): ValueType; /** * D90 R18: the seed routed to the field its form owns. An optional seed can * only have come from the asynchronous pull form — the synchronous form * never validates to `T?` — so the shape pre-pass reads the form off the * seed the previous convergence pass learned. */ seededIterationInfo(block: ClassIterateBlock): { readonly iterate: ValueType; } | { readonly iterateAsync: ValueType; }; /** * `@iterate:` answers the compiler's question "what does * iterating you mean?". It shares `@dispose:`'s compiler-name path, then * supplies its own role: it is a contract, not a method, and it produces a * value. D90 R18 gives it two forms, told apart by the answer's shape the * same way `@dispose:`'s async-ness is read off its own body: the * synchronous form answers a collection the language already iterates and * the eight plain consumers read it once; the asynchronous pull form * answers `T?` — `async for` drives it once per element, it may await, and * null is exhaustion. */ analyzeClassIterate(statement: ClassDeclaration, block: ClassIterateBlock, baseName: string | null): void; /** * AS-D1: an asynchronous `@iterate:` spends `null` on exhaustion, so a stream * whose *elements* may be null cannot be written — the first null element * ends the loop and every element behind it is dropped, with no report. The * charter already stated the ambiguity where it excludes the synchronous * form ("a sequence whose elements may be `null` could not be written at * all"); this is the report that says so where the author wrote it. * * The test is per return, because the block's merged answer is `T?` either * way and cannot say which return spent the null. `return null` is the * exhaustion answer and is always legal; any other return whose static type * is optional is carrying a null element. * * The returns are paired with the types the analysis collected, in source * order, and the check runs only when the two agree in length. An * unreachable return contributes no type and an extension statement's block * is not walked, so a disagreement means the pairing is not sound here — and * a report at the wrong return is worse than no report. */ private rejectNullStreamElements; /** * The answer space is the four collections plus `T?` (D90 R18): the * synchronous form says "iterating me is iterating this", and the language * already fixed what iterating a List, Set, Map, or Record means; the * asynchronous pull form answers one element per pull, null for exhaustion. * Anything else would be a second iteration semantics, which is the thing * charter section 19 keeps out. */ validatedIterationSource(statement: ClassDeclaration, block: ClassIterateBlock, answered: ValueType, baseName: string | null, awaits: boolean): { readonly form: "sync" | "async"; readonly source: ValueType; }; /** The `@iterate:` answer a class inherits, most derived ancestor first. */ inheritedIterationSource(className: string): ValueType | null; /** D90 R18: the asynchronous `@iterate:` element a class inherits, most derived ancestor first. */ inheritedAsyncIterationSource(className: string): ValueType | null; /** * D90 R18: what pulling this value under `async for` means. A class answers * through the asynchronous `@iterate:` form — its own, or the one it * inherits, mirroring the synchronous contract exactly. */ asyncIterationContract(type: ValueType): ValueType | null; /** * D68 rule 177: what iterating this value means. A class answers through * `@iterate:` — its own, or the one it inherits, because overriding replaces * a single answer instead of composing a chain the way `@dispose:` does. */ iterationContract(type: ValueType): ValueType | null; /** * Projects one consumer's operand through `@iterate:` and records the span so * the emitter projects it too. Every consumer of an iterable calls this, so * `for item in bag` and `item in bag` can never disagree about whether a * class participates — D68 names that split as the trap this design exists to * avoid. */ iterationSource(expression: Expression, type: ValueType): ValueType; /** * The one sentence that teaches the contract, appended wherever a consumer * refuses a class. A class that already declares `@iterate:` gets nothing: * its own block carries the precise diagnostic. */ iterationGuidance(type: ValueType): string; asyncPullElementType(source: ValueType, sourceSpan: Span, statementStart: number): ValueType; } //# sourceMappingURL=roles.d.ts.map