@yabgu_gm/tensionscript 0.11.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -620,7 +620,8 @@ declare module "types:tensionscript/src/diagnosticMessages.generated" {
620
620
  _abi_align_0_the_alignment_must_be_a_power_of_two = 6407,
621
621
  _abi_align_0_is_less_than_the_field_type_s_natural_alignment_1_asking_for_less_is_a_lie_so_it_is_refused_rather_than_silently_ignored = 6408,
622
622
  _abi_align_0_is_too_large_the_maximum_is_1 = 6409,
623
- Alignment_is_a_property_of_the_field_not_its_type_Place_abi_align_N_on_the_field_declaration = 6410
623
+ Alignment_is_a_property_of_the_field_not_its_type_Place_abi_align_N_on_the_field_declaration = 6410,
624
+ A_view_parameter_unmanaged_0_over_an_inline_type_cannot_appear_in_a_host_visible_signature_an_external_or_exported_parameter_is_supplied_by_the_host_and_a_view_is_an_address_this_compiler_did_not_materialise = 6411
624
625
  }
625
626
  /** Translates a diagnostic code to its respective string. */
626
627
  export function diagnosticCodeToString(code: DiagnosticCode): string;
@@ -2706,6 +2707,21 @@ declare module "types:tensionscript/src/types" {
2706
2707
  * all of them.
2707
2708
  */
2708
2709
  static wasmParamCountOf(type: Type): number;
2710
+ /**
2711
+ * Appends the **leaf** types of an `@inline struct`, in declaration order,
2712
+ * depth first.
2713
+ *
2714
+ * A leaf is a field that occupies one WASM local: a scalar or a reference. A
2715
+ * field whose type is itself an `@inline struct` is not a leaf — it is several
2716
+ * locals behind a map — so the walk recurses into it. This is the type-level
2717
+ * twin of the `fieldLocals` tree the compiler builds
2718
+ * (`Compiler#fillSroaFieldLocals`), and the two must agree in **order** as
2719
+ * well as count: the callee binds WASM parameter *k* to the *k*th leaf, and
2720
+ * the caller pushes operands in the same walk.
2721
+ */
2722
+ static leafTypesOf(type: Type, out: Type[]): void;
2723
+ /** The number of leaf locals an `@inline struct` occupies. */
2724
+ static leafCountOf(type: Type): number;
2709
2725
  /**
2710
2726
  * How many WASM operands **this signature** spends on source parameter
2711
2727
  * `sourceIndex`.
@@ -2722,6 +2738,18 @@ declare module "types:tensionscript/src/types" {
2722
2738
  * the same source parameter.
2723
2739
  */
2724
2740
  wasmParamSlotCountOf(sourceIndex: number): number;
2741
+ /**
2742
+ * Whether source parameter `sourceIndex` is a **view**: `@unmanaged` at the
2743
+ * declaration over a type that is an `@inline struct`.
2744
+ *
2745
+ * The type still says *value* — every use of `MyStruct` shares one `Type`, and
2746
+ * that is what makes a member lookup work on the parameter — while the model
2747
+ * says this use is an address. One `i32` slot, no field locals, a load at the
2748
+ * field's offset: the same crossing an `@unmanaged struct` parameter already
2749
+ * takes, with the model recorded at the declaration instead of on a pointer
2750
+ * type.
2751
+ */
2752
+ isViewParam(sourceIndex: number): boolean;
2725
2753
  /**
2726
2754
  * Whether source parameter `sourceIndex` **crosses by value** — one `i32`
2727
2755
  * address instead of one WASM operand per field.
@@ -2735,8 +2763,12 @@ declare module "types:tensionscript/src/types" {
2735
2763
  * address, and the override asks for a copy of what is there — the same
2736
2764
  * copy, read from memory instead of from SROA locals.
2737
2765
  *
2738
- * The reverse is not a thing: `@unmanaged`/`@managed` over an `@inline`
2739
- * declaration is refused (`TS6395`), because a value has no address to lend.
2766
+ * The reverse is partly a thing now. `@unmanaged` over an `@inline`
2767
+ * declaration is legal where the use supplies an address — a parameter (a
2768
+ * view parameter, `isViewParam`) or `changetype`'s type argument — and a view
2769
+ * parameter crosses as a pointer, so it is asked before this predicate. Every
2770
+ * other cell still refuses (`TS6395`), and `@managed` over `@inline` refuses
2771
+ * at every position: a value has no GC header and no address to lend.
2740
2772
  */
2741
2773
  crossesByValueParam(sourceIndex: number): boolean;
2742
2774
  /**
@@ -2759,9 +2791,17 @@ declare module "types:tensionscript/src/types" {
2759
2791
  get byValPointerParams(): boolean;
2760
2792
  /** Whether `type` is an `@inline struct`. */
2761
2793
  static isInlineStructRef(type: Type): boolean;
2762
- /** The number of fields an `@inline struct` has (0 for a record with none).
2763
- * Distinct from `wasmParamCountOf`, which cannot express "one field" and
2764
- * "one operand" differently. */
2794
+ /**
2795
+ * How many **WASM slots** an `@inline struct` spends — its leaf count.
2796
+ *
2797
+ * Leaf count, not direct-field count: a nested inline field is several locals,
2798
+ * so `Outer1 { a: u32; inner: Inner{x,y} }` is 3, not 2. A record with no
2799
+ * members is 0.
2800
+ *
2801
+ * Callers that made sense of "direct fields" are listed in the Phase C audit
2802
+ * (`docs`); the one place that genuinely needs the direct count is layout, and
2803
+ * layout reads `members` itself rather than asking this.
2804
+ */
2765
2805
  static fieldCountOf(type: Type): number;
2766
2806
  /**
2767
2807
  * Whether a call to this signature passes a hidden destination pointer.
@@ -3095,6 +3135,60 @@ declare module "types:tensionscript/src/resolver" {
3095
3135
  currentElementExpression: Expression | null;
3096
3136
  /** Whether a new override has been discovered. */
3097
3137
  discoveredOverride: boolean;
3138
+ /**
3139
+ * Whether a use-site `@unmanaged` over an `@inline` declaration is being
3140
+ * resolved as the **type argument of `changetype`**.
3141
+ *
3142
+ * That position is the one where the operand can supply the address the value
3143
+ * itself does not have, so the refusal's premise does not hold there (see
3144
+ * `checkMemoryModelOverride`). Every other position — a parameter, a local, a
3145
+ * field, a return type — still refuses with `TS6395`, which is what keeps the
3146
+ * function-parameter form (`Phase 2`) out of this change.
3147
+ *
3148
+ * The compiler sets this around the one `resolveTypeArguments` call it makes
3149
+ * for a `changetype` builtin (`Compiler#compileCallExpressionBuiltin`), and
3150
+ * resets it immediately: the flag is a scope, not a mode.
3151
+ */
3152
+ allowInlineView: boolean;
3153
+ /**
3154
+ * Whether a use-site `@unmanaged` over an `@inline` declaration is being
3155
+ * resolved in a **parameter position** — the cell where the caller supplies
3156
+ * the address, so `f(a: @unmanaged T)` with an inline `T` is a view parameter.
3157
+ *
3158
+ * Distinct from {@link allowInlineView} (Phase 1, `changetype`'s type
3159
+ * argument, where the operand supplies the address at the point of use).
3160
+ *
3161
+ * **Saved and restored, never blindly cleared.** Resolving one parameter's
3162
+ * type can resolve another function (a default value's call, a nested
3163
+ * signature), and a nested window that reset the flag to `false` on the way
3164
+ * out closed the outer window — measured: the owner's pattern reported TS6395
3165
+ * with both flags reading `false` while inside `resolveFunction`'s own loop.
3166
+ */
3167
+ allowInlineViewParam: boolean;
3168
+ /**
3169
+ * Call expressions that are **views of caller bytes**: an inline-typed
3170
+ * `changetype<@unmanaged T>(ptr)`.
3171
+ *
3172
+ * Marked here, at resolution, because the compiler asks this question while
3173
+ * compiling the *field access* — which happens before the `changetype` it
3174
+ * wraps is ever compiled. Recording it at codegen time (the first attempt)
3175
+ * produced a field read that never saw the mark and compiled to
3176
+ * `unreachable`.
3177
+ *
3178
+ * Keyed on the AST node rather than on the type, because the type is the
3179
+ * declaration's (`T` is still an `@inline struct`) and the model is the
3180
+ * *use's*: there is one `Type` per class, shared by every use.
3181
+ */
3182
+ private viewCalls;
3183
+ /**
3184
+ * Records `node` as a view call. The resolver marks the ones it resolves;
3185
+ * `Compiler#compileCallExpressionBuiltin` marks the ones it resolves itself,
3186
+ * because a builtin's type arguments are resolved on the *codegen* path and
3187
+ * never pass through `maybeInferCall`.
3188
+ */
3189
+ markViewCall(node: Expression): void;
3190
+ /** Whether `node` is an inline-typed `changetype<@unmanaged T>(...)`. */
3191
+ isViewCall(node: Expression): boolean;
3098
3192
  /** Constructs the resolver for the specified program. */
3099
3193
  constructor(
3100
3194
  /** The program to construct a resolver for. */
@@ -3122,10 +3216,14 @@ declare module "types:tensionscript/src/resolver" {
3122
3216
  * - **`@managed` over an `@unmanaged` declaration** — the collector traces a
3123
3217
  * reference through a header the record does not have. No emitter can
3124
3218
  * invent the header, so a use cannot confer provenance. (`TS6394`.)
3125
- * - **`@unmanaged`/`@managed` over an `@inline` declaration** — a value is
3126
- * stored as one local per field and has **no address** at all, so there is
3127
- * nothing to lend as a pointer and nothing a header could sit in front of.
3128
- * (`TS6395`.)
3219
+ * - **`@managed` over an `@inline` declaration** — a value has neither a
3220
+ * header nor an address, at any position. (`TS6395`.)
3221
+ * - **`@unmanaged` over an `@inline` declaration**, *except* where the use
3222
+ * supplies an address: `changetype`'s type argument (the operand supplies
3223
+ * it) and a parameter (the caller supplies it). Those two windows are
3224
+ * `allowInlineView` and `allowInlineViewParam`, and they are the whole
3225
+ * difference between a view and a claim. Every other position — a return
3226
+ * type, a local, a field — still has nothing to lend. (`TS6395`.)
3129
3227
  *
3130
3228
  * Everything else is left to the emitters: `@unmanaged` over `@managed` is
3131
3229
  * the identity cell (10.0a), `@inline` over a pointer declaration is the
@@ -3763,6 +3861,14 @@ declare module "types:tensionscript/src/program" {
3763
3861
  function fromUnaryPostfixToken(token: Token): OperatorKind;
3764
3862
  }
3765
3863
  /** Represents an AssemblyScript program. */
3864
+ /** How many WASM slots a parameter binding consumed — see
3865
+ * `Program#bindSroaParameterLeaves`. A one-field wrapper because this compiler
3866
+ * reports the count and the caller advances past it; a bare `i32` return would
3867
+ * be the same information with no name at the call site. */
3868
+ export class BindResult {
3869
+ leafCount: number;
3870
+ constructor(leafCount: number);
3871
+ }
3766
3872
  export class Program extends DiagnosticEmitter {
3767
3873
  /** Compiler options. */
3768
3874
  options: Options;
@@ -4479,6 +4585,17 @@ declare module "types:tensionscript/src/program" {
4479
4585
  * therefore cannot alias.
4480
4586
  */
4481
4587
  fieldLocals: Map<string, Local> | null;
4588
+ /**
4589
+ * Whether this local is a **view parameter**: `a: @unmanaged T` where `T` is
4590
+ * an inline type.
4591
+ *
4592
+ * Its `type` is still the inline record, because that is what makes `a.field`
4593
+ * resolve — every use of `T` shares one `Type`. Its *storage* is the one `i32`
4594
+ * WASM parameter holding the caller's address, which is why a field read goes
4595
+ * through `load` at the field's offset rather than through `fieldLocals`
4596
+ * (there are none: a view has no per-field locals to copy into).
4597
+ */
4598
+ isViewParam: boolean;
4482
4599
  /** Constructs a new local variable. */
4483
4600
  constructor(
4484
4601
  /** Simple name. */
@@ -4590,6 +4707,20 @@ declare module "types:tensionscript/src/program" {
4590
4707
  nextInlineId: number;
4591
4708
  /** Counting id of anonymous inner functions. */
4592
4709
  nextAnonymousId: number;
4710
+ /**
4711
+ * Binds an `@inline struct` parameter's **leaves** to the WASM parameters that
4712
+ * carry them, building the `fieldLocals` tree a read path walks.
4713
+ *
4714
+ * `localIndex` is the index of the parameter's first WASM slot; the walk
4715
+ * consumes one slot per leaf in the same depth-first order
4716
+ * `Signature.leafTypesOf` declares and the caller pushes operands in. Returns
4717
+ * how many slots were consumed, so the caller advances past them.
4718
+ *
4719
+ * A nested field gets a placeholder `Local` (index `-1`) carrying its own map,
4720
+ * exactly as `fillSroaFieldLocals` builds one for a local — the tree shape is
4721
+ * one structure regardless of where the leaves' storage came from.
4722
+ */
4723
+ bindSroaParameterLeaves(type: Type, path: string, into: Map<string, Local>, localIndex: number, flow: Flow): BindResult;
4593
4724
  /** Constructs a new concrete function. */
4594
4725
  constructor(
4595
4726
  /** Name incl. type parameters, i.e. `foo<i32>`. */
@@ -5284,7 +5415,7 @@ declare module "types:tensionscript/src/compiler" {
5284
5415
  import { Range, DiagnosticEmitter } from "types:tensionscript/src/diagnostics";
5285
5416
  import { Module, MemorySegment, ExpressionRef, TypeRef, GlobalRef } from "types:tensionscript/src/module";
5286
5417
  import { Feature, Target, Runtime } from "types:tensionscript/src/common";
5287
- import { Program, Class, Element, DeclaredElement, Enum, Function, Global, Property, VariableLikeElement, File } from "types:tensionscript/src/program";
5418
+ import { Program, Class, Element, DeclaredElement, Enum, Function, Global, Local, Property, VariableLikeElement, File } from "types:tensionscript/src/program";
5288
5419
  import { Flow, ConditionKind } from "types:tensionscript/src/flow";
5289
5420
  import { Resolver } from "types:tensionscript/src/resolver";
5290
5421
  import { Node, FunctionTypeNode, Statement, Expression } from "types:tensionscript/src/ast";
@@ -5947,11 +6078,27 @@ declare module "types:tensionscript/src/compiler" {
5947
6078
  /**
5948
6079
  * The SROA local an expression names, or `null`.
5949
6080
  *
5950
- * Only a bare identifier can be one: `p.x.y` where `x` is itself an inline
5951
- * struct cannot happen this phase (a nested field is refused at declaration),
5952
- * so the receiver of a field access is either an SROA local or an ordinary
5953
- * expression that has an address.
6081
+ * The receiver is either an SROA local (base identifier) or a chain of field
6082
+ * accesses into one (`o.inner` in `o.inner.x`). The chain is walked from the
6083
+ * base outward, one hop per `fieldLocals` lookup: a nested inline field is
6084
+ * entered through the placeholder `Local` its creation installed
6085
+ * (`fillSroaFieldLocals`), so the walk and the tree are one structure rather
6086
+ * than two that could disagree.
6087
+ *
6088
+ * Returns the `Local` the final hop resolved to, or `null` when any step does
6089
+ * not hold — a non-local base, a field with no entry, or a leaf reached before
6090
+ * the chain ends. `null` is not an error here: the caller falls through to the
6091
+ * ordinary address paths, which refuse a value with no address.
6092
+ */
6093
+ /**
6094
+ * Pushes one operand per **leaf** of a `fieldLocals` tree, depth first.
6095
+ *
6096
+ * Shared by the call site (`expandSroaArgument`) so the operand order is the
6097
+ * one `Signature.leafTypesOf` declares and the binder indexes into — three
6098
+ * walks, one order. A nested field's entry is a placeholder (index `-1`)
6099
+ * carrying its own map, so the walk descends through it.
5954
6100
  */
6101
+ private pushSroaLeafOperands;
5955
6102
  private resolveSroaReceiver;
5956
6103
  /** Size of the static call-temp region, in bytes. 4 KB is 256 slots at the
5957
6104
  * 16-byte fixture record size. Overflow aborts (see `makeSretCallBlock`);
@@ -6041,6 +6188,15 @@ declare module "types:tensionscript/src/compiler" {
6041
6188
  /** `base + mark + extra`, freshly built. Callers must not share the node:
6042
6189
  * Binaryen's passes assume a single parent per expression. */
6043
6190
  private makeSretAddressPlus;
6191
+ /**
6192
+ * Stores a by-value argument's leaves into the static call-temp region.
6193
+ *
6194
+ * The same tree walk as `copyInto` (`emitSroaStores`) and `copySroaValue`:
6195
+ * leaves become stores at `tempAddress + field offset`, a nested field is
6196
+ * descended into. Keeping one recursion is what makes a nested value cross a
6197
+ * host boundary at the layout the C side expects.
6198
+ */
6199
+ emitTempArgumentLeaves(type: Type, fieldLocals: Map<string, Local>, base: number, markLocal: Local, baseOffset: number, stmts: ExpressionRef[]): void;
6044
6200
  /**
6045
6201
  * Materialises one `@inline struct` argument of an `@external` call on the
6046
6202
  * static call-temp stack and returns the address to pass.
@@ -6111,6 +6267,93 @@ declare module "types:tensionscript/src/compiler" {
6111
6267
  * Returns true when the declaration was handled here (the caller must not fall
6112
6268
  * through to the ordinary local path).
6113
6269
  */
6270
+ /**
6271
+ * Fills `into` with one entry per field of an `@inline struct` value.
6272
+ *
6273
+ * A **leaf** field (scalar, or a reference) gets one WASM local, named
6274
+ * `<path>$<field>` — a synthetic name keeps it out of user scope, so nothing
6275
+ * can look one up by accident.
6276
+ *
6277
+ * A field whose type is itself an `@inline struct` is **not a leaf**: it is
6278
+ * recursed into, and its entry is a placeholder `Local` (index `-1`) carrying
6279
+ * its own map. That makes `fieldLocals` a **tree** whose shape is the value's
6280
+ * nesting, which is what lets a read path walk `o.inner.x` a hop at a time
6281
+ * without a second representation. The placeholder is never `local.get`-ed:
6282
+ * the bare-read guard refuses a value read and the receiver walk descends to
6283
+ * a leaf before emitting anything.
6284
+ *
6285
+ * Returns `false` when a field was refused, having reported why. The refusal
6286
+ * is per **leaf**, so a nested value inherits exactly the rules the flat one
6287
+ * has — a `T[N]` field or a GC-reference field is refused at any depth.
6288
+ */
6289
+ private fillSroaFieldLocals;
6290
+ /**
6291
+ * Copies one SROA value's leaves into another's, following the tree.
6292
+ *
6293
+ * `from` and `to` are the two `fieldLocals` maps — the same shape
6294
+ * `fillSroaFieldLocals` builds, so a nested field is a placeholder on both
6295
+ * sides and the recursion descends through it rather than trying to move a
6296
+ * value that has no single representation.
6297
+ *
6298
+ * This is what makes `let q = p` and `q = p` **copies** rather than aliases:
6299
+ * one `local.set` per leaf, so a later write to `q` cannot be seen through
6300
+ * `p`. Both sides come from this compiler's own maps, so there is no aliasing
6301
+ * question to answer — the leaves are distinct locals by construction.
6302
+ */
6303
+ private copySroaValue;
6304
+ /**
6305
+ * Stores a `fieldLocals` tree's leaves into the hidden destination slot at
6306
+ * `destIndex`, one store per leaf.
6307
+ *
6308
+ * The third destination shape for the same recursion: `emitSroaStores` writes
6309
+ * to a compiled `usize`, `emitTempArgumentLeaves` to a call-temp address, and
6310
+ * this one to a **parameter** — the sret slot a `return p` fills. The address
6311
+ * is a fresh `local.get` per store, because Binaryen's passes assume one
6312
+ * parent per node.
6313
+ */
6314
+ emitSroaStoresToLocal(type: Type, fieldLocals: Map<string, Local>, destIndex: number, baseOffset: number, stmts: ExpressionRef[]): void;
6315
+ /**
6316
+ * Zeroes every **leaf** of an `@inline struct` in the destination slot.
6317
+ *
6318
+ * `return new P()` has no local to copy from — the value is all zeroes — but
6319
+ * the slot is stack space that a previous sret call may have filled, so the
6320
+ * bytes still have to be written. Walking the type rather than the direct
6321
+ * fields is what zeroes a nested field's leaves: measured before this,
6322
+ * `return new Outer1()` with `Inner{x,y}` stored only offset 0 and left
6323
+ * offsets 4 and 8 holding whatever was there.
6324
+ */
6325
+ emitSroaZerosToLocal(type: Type, destIndex: number, baseOffset: number, stmts: ExpressionRef[]): void;
6326
+ /**
6327
+ * Loads an `@inline struct`'s leaves out of the sret slot into a target's
6328
+ * field locals, one load per leaf.
6329
+ *
6330
+ * The mirror of {@link emitSroaStoresToLocal}: `let q = make()` reads the slot
6331
+ * back into the leaf locals a read path walks. A fresh slot address per load,
6332
+ * for the same one-parent-per-node reason.
6333
+ */
6334
+ emitSretLoadsInto(type: Type, fieldLocals: Map<string, Local>, base: number, markLocal: Local, baseOffset: number, stmts: ExpressionRef[]): void;
6335
+ /**
6336
+ * The `fieldLocals` tree behind an SROA-tracked expression, or `null`.
6337
+ *
6338
+ * The public face of {@link resolveSroaReceiver} for code outside this class —
6339
+ * today only the `copyInto` builtin, which needs the *tree* (to store its
6340
+ * leaves) rather than a single receiver.
6341
+ */
6342
+ sroaTreeOf(expression: Expression): Map<string, Local> | null;
6343
+ /**
6344
+ * Stores an inline value's leaves at `dest + offset`.
6345
+ *
6346
+ * The third leaf consumer, beside `copySroaValue` (local-to-local) and the
6347
+ * read path (local-to-stack): same tree, same recursion, an expression
6348
+ * reference instead of a destination map. The offsets come from the **type**
6349
+ * (`property.memoryOffset`, the layout the C side sees), so what lands in
6350
+ * memory is the wire layout, not a packing of the locals.
6351
+ *
6352
+ * Leaves whose local is missing are skipped rather than stored as `-1`: a
6353
+ * field refused at declaration was never part of the value (`isSroaFieldType`
6354
+ * has already reported why), and the caller has been told.
6355
+ */
6356
+ emitSroaStores(type: Type, fieldLocals: Map<string, Local>, dest: ExpressionRef, baseOffset: number, stmts: ExpressionRef[]): void;
6114
6357
  private compileSroaLocal;
6115
6358
  /**
6116
6359
  * The instance fields of an `@inline struct`, in declaration order, or `null`
@@ -6296,6 +6539,7 @@ declare module "types:tensionscript/src/builtins" {
6296
6539
  const select = "~lib/builtins/select";
6297
6540
  const unreachable = "~lib/builtins/unreachable";
6298
6541
  const changetype = "~lib/builtins/changetype";
6542
+ const copyInto = "~lib/builtins/copyInto";
6299
6543
  const assert = "~lib/builtins/assert";
6300
6544
  const call_indirect = "~lib/builtins/call_indirect";
6301
6545
  const unchecked = "~lib/builtins/unchecked";