@neocompose/cli 0.89.0 → 0.89.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.
@@ -22918,8 +22918,12 @@ var FunctionTypeInference = class {
22918
22918
  const value = actual.parameterTypes[index];
22919
22919
  if (value !== void 0) this.add(value, expected, "exact");
22920
22920
  });
22921
- } else if (pattern.kind === "reference" && actual.kind === "reference") {
22922
- this.add(actual.target, pattern.target, "exact");
22921
+ } else if (pattern.kind === "reference") {
22922
+ this.add(
22923
+ actual.kind === "reference" ? actual.target : actual,
22924
+ pattern.target,
22925
+ "exact"
22926
+ );
22923
22927
  }
22924
22928
  }
22925
22929
  fix(index) {
@@ -33327,11 +33331,14 @@ function resolveMemberForValueRow(row, ctx, visited) {
33327
33331
  ctx.__valueOverlay
33328
33332
  );
33329
33333
  if (parent === null) continue;
33330
- const parentMember = resolveMemberForValueRow(
33331
- parent,
33332
- ctx,
33333
- new Set(visited)
33334
- );
33334
+ let parentMember = resolveMemberForValueRow(parent, ctx, new Set(visited));
33335
+ if (parentMember?.kind === 21 /* Generic */) {
33336
+ parentMember = substituteMember(
33337
+ parentMember,
33338
+ runtimeParentGenericEnv(parent, ctx, /* @__PURE__ */ new Set()),
33339
+ ctx.vm.members
33340
+ );
33341
+ }
33335
33342
  if (parentMember !== null && isMemberTupleBase(parentMember)) {
33336
33343
  const memberId = parentMember.elementMemberIds[Number(link.key)];
33337
33344
  return memberId === void 0 ? null : evalMemberById(ctx.vm, memberId);
@@ -50834,7 +50841,9 @@ function indexCalls(tokens, pairs, lexed) {
50834
50841
  const name = tokens[index];
50835
50842
  const openIndex = callOpenParenIndex(tokens, index);
50836
50843
  const open = tokens[openIndex];
50837
- if (!name || name.kind !== "identifier") continue;
50844
+ if (!name) continue;
50845
+ const expression = [")", "]", "!"].includes(name.text) && openIndex === index + 1;
50846
+ if (name.kind !== "identifier" && !expression) continue;
50838
50847
  if (!open || open.kind !== "punctuation" || open.text !== "(") continue;
50839
50848
  const previous = tokens[index - 1];
50840
50849
  const targetTyped = isContextualNew(name, previous, open);
@@ -50863,14 +50872,19 @@ function indexCalls(tokens, pairs, lexed) {
50863
50872
  segmentStart = token.end;
50864
50873
  segmentStartIndex = cursor + 1;
50865
50874
  }
50875
+ if (tokens[cursor + 1]?.text === "<") {
50876
+ const genericOpen = callOpenParenIndex(tokens, cursor);
50877
+ if (genericOpen >= 0) cursor = genericOpen - 1;
50878
+ }
50866
50879
  }
50867
50880
  if (segmentStart < (close?.start ?? lexed.end)) {
50868
50881
  ranges.push(lexed.source.range(segmentStart, close?.start ?? lexed.end));
50869
50882
  names2.push(callArgumentLabel(tokens, segmentStartIndex, limit));
50870
50883
  }
50884
+ const namedKind = contextualKeyword ? "constructor" : "function";
50871
50885
  calls.push({
50872
- kind: contextualKeyword ? "constructor" : "function",
50873
- name: name.text,
50886
+ kind: expression ? "expression" : namedKind,
50887
+ name: expression ? "delegate" : name.text,
50874
50888
  nameRange: name.range,
50875
50889
  ...contextualKeyword ? { contextualKeywordRange: contextualKeyword.range } : {},
50876
50890
  ...targetTyped ? { targetTyped: true } : {},
@@ -52690,6 +52704,10 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
52690
52704
  if (expected === void 0) return;
52691
52705
  this.requireAssignable(resolved.type, expected, expression.pos, "value");
52692
52706
  }
52707
+ /** Type a source expression with the same call binding rules as executable code. */
52708
+ resolveExpressionType(expression) {
52709
+ return this.resolveExpression(expression, this.initialScope()).type;
52710
+ }
52693
52711
  persistedListenerCompletions(receiver, expected) {
52694
52712
  const scope = this.initialScope();
52695
52713
  let receiverType = this.context.thisClass;
@@ -54956,8 +54974,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
54956
54974
  );
54957
54975
  }
54958
54976
  }
54959
- resolveMemberRowOf(place, scope) {
54960
- const optional2 = place.optional === true;
54977
+ resolveMemberRowOf(place, scope, resolved) {
54961
54978
  if ((this.context.kind === "initializer" || this.context.kind === "initializerExpression") && this.delegateCaptureContexts.length === 0 && referencePlaceRoot(place) === "this") {
54962
54979
  const text = referencePlaceText(place) ?? "this";
54963
54980
  throw new CompileError(
@@ -54965,12 +54982,12 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
54965
54982
  place.pos
54966
54983
  );
54967
54984
  }
54968
- const slot = this.resolveMemberNatural(
54985
+ const slot = resolved ?? this.resolveMemberNatural(
54969
54986
  place.receiver,
54970
54987
  place.name,
54971
54988
  scope,
54972
54989
  place.pos,
54973
- optional2
54990
+ place.optional === true
54974
54991
  );
54975
54992
  const member = slot.symbol;
54976
54993
  if (member === void 0) {
@@ -54988,19 +55005,55 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
54988
55005
  place.pos
54989
55006
  );
54990
55007
  }
54991
- const read = slot.optionalChainType ?? slot.type;
54992
- const stored = member.lookup !== void 0 && member.lookup.multiselect !== true && read.kind !== "reference" ? {
54993
- kind: "reference",
54994
- target: { ...read, nullable: false },
54995
- ...isNullable(read) ? { nullable: true } : {}
54996
- } : read;
54997
- const type2 = this.referenceType(stored, optional2);
55008
+ const type2 = this.memberReferenceType(slot, place.optional === true);
54998
55009
  if (type2.kind !== "reference") return slot;
54999
55010
  return {
55000
55011
  pointer: { type: "rowOf" /* RowOf */, place: slot.pointer },
55001
55012
  type: type2
55002
55013
  };
55003
55014
  }
55015
+ memberReferenceType(slot, optional2) {
55016
+ const read = slot.storedType ?? slot.optionalChainType ?? slot.type;
55017
+ const stored = slot.symbol?.lookup !== void 0 && slot.symbol.lookup.multiselect !== true && read.kind !== "reference" ? {
55018
+ kind: "reference",
55019
+ target: { ...read, nullable: false },
55020
+ ...isNullable(read) ? { nullable: true } : {}
55021
+ } : read;
55022
+ return this.referenceType(
55023
+ stored,
55024
+ optional2 || slot.optionalChainType !== void 0
55025
+ );
55026
+ }
55027
+ /** Only stored member slots can acquire a reference from an expected type. */
55028
+ implicitReferenceType(expression, value) {
55029
+ if (expression.kind !== "member") return void 0;
55030
+ if (value.symbol === void 0 || value.symbol.computed === true)
55031
+ return void 0;
55032
+ return this.memberReferenceType(value, expression.optional === true);
55033
+ }
55034
+ referenceInferenceType(expression, value, pattern) {
55035
+ const read = this.readReferences(value, pattern).type;
55036
+ if (pattern.kind !== "reference" || read.kind === "reference") return read;
55037
+ return this.implicitReferenceType(expression, value) ?? read;
55038
+ }
55039
+ contextualReferenceType(expression, value, expected) {
55040
+ const read = this.readReferences(value, expected).type;
55041
+ if (expected.kind !== "reference" || isNeoScriptTypeAssignable(read, expected, this.project))
55042
+ return read;
55043
+ const reference = this.implicitReferenceType(expression, value);
55044
+ return reference !== void 0 && isNeoScriptTypeAssignable(reference, expected, this.project) ? reference : read;
55045
+ }
55046
+ /** Use the same slot identity for explicit and expected-type references. */
55047
+ contextualReference(expression, value, scope, expected) {
55048
+ const read = this.readReferences(value, expected);
55049
+ if (expected?.kind !== "reference" || isNeoScriptTypeAssignable(read.type, expected, this.project))
55050
+ return read;
55051
+ const reference = this.implicitReferenceType(expression, value);
55052
+ if (expression.kind === "member" && reference !== void 0 && isNeoScriptTypeAssignable(reference, expected, this.project)) {
55053
+ return this.resolveMemberRowOf(expression, scope, value);
55054
+ }
55055
+ return read;
55056
+ }
55004
55057
  /** Whether assigning `target` rebinds a reference to a handle. */
55005
55058
  holdsHandleReference(target) {
55006
55059
  if (target.type.kind === "reference") {
@@ -55609,13 +55662,15 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
55609
55662
  });
55610
55663
  }
55611
55664
  resolveExpression(expression, scope, expected, convertSequence = true) {
55612
- const value = this.readReferences(
55665
+ const value = this.contextualReference(
55666
+ expression,
55613
55667
  this.resolveReferenceExpression(
55614
55668
  expression,
55615
55669
  scope,
55616
55670
  expected,
55617
55671
  convertSequence
55618
55672
  ),
55673
+ scope,
55619
55674
  expected
55620
55675
  );
55621
55676
  return convertSequence ? this.convertSequence(value, expected) : value;
@@ -57137,7 +57192,8 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
57137
57192
  }
57138
57193
  entryType = common;
57139
57194
  }
57140
- if (expected?.kind === "set" && this.context.kind === "initializer") {
57195
+ const referenceSelection = expected?.kind === "set" && this.context.kind === "initializer" && (expected.elementType.kind === "reference" || hasReferenceEquality(expected.elementType, this.project));
57196
+ if (expected?.kind === "set" && this.context.kind === "initializer" && !referenceSelection) {
57141
57197
  const element = expected.elementType;
57142
57198
  const spelling = element.kind === "named" && this.project.typeById.get(element.typeId)?.kind === "enum" ? `.Option or ${this.describe({ ...element, nullable: false })}.Option values` : "literal values";
57143
57199
  throw new CompileError(
@@ -57155,7 +57211,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
57155
57211
  "NeoScript strict compiler produced a non-list wire type for a list literal."
57156
57212
  );
57157
57213
  }
57158
- return {
57214
+ const list = {
57159
57215
  pointer: {
57160
57216
  type: "listLiteral" /* ListLiteral */,
57161
57217
  typeInfo: wire,
@@ -57164,6 +57220,18 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
57164
57220
  type: type2,
57165
57221
  entries: resolved
57166
57222
  };
57223
+ return referenceSelection ? this.resolveCollectionQuery(
57224
+ list,
57225
+ {
57226
+ kind: "list",
57227
+ keyType: { kind: "primitive", name: "int" },
57228
+ valueType: entryType
57229
+ },
57230
+ "ToSet",
57231
+ [],
57232
+ scope,
57233
+ pos
57234
+ ) : list;
57167
57235
  }
57168
57236
  resolveDictionaryLiteral(entries, scope, expected, pos) {
57169
57237
  const expectedDictionary = expected?.kind === "dictionary" ? expected : void 0;
@@ -57874,7 +57942,12 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
57874
57942
  const rootEntry = scope.lookupFlowRoot(pathRoot(path));
57875
57943
  if (rootEntry) {
57876
57944
  const narrowed = scope.lookupNarrowedType(path, rootEntry);
57877
- if (narrowed) resolved = { ...resolved, type: narrowed };
57945
+ if (narrowed)
57946
+ resolved = {
57947
+ ...resolved,
57948
+ storedType: resolved.type,
57949
+ type: narrowed
57950
+ };
57878
57951
  }
57879
57952
  }
57880
57953
  }
@@ -60593,17 +60666,28 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
60593
60666
  args,
60594
60667
  scope
60595
60668
  );
60596
- return resolveNeoScriptDeclaredConstructorOverload(
60669
+ const resolve = (implicit) => resolveNeoScriptDeclaredConstructorOverload(
60597
60670
  declared,
60598
60671
  named.map((name, index) => {
60599
60672
  const inferred = inferredTypes?.[index];
60600
- if (!inferred) return { name };
60673
+ const argument2 = args[index];
60674
+ if (!inferred || !argument2) return { name };
60601
60675
  return {
60602
60676
  name,
60603
- matchesParameter: (parameter3) => isNeoScriptTypeAssignable(inferred, parameter3.type, this.project)
60677
+ matchesParameter: (parameter3) => implicit && parameter3.type.kind === "reference" && isContextualReferenceExpression(argument2) ? this.targetTypedArgumentFits(argument2, parameter3.type, scope) : isNeoScriptTypeAssignable(
60678
+ implicit ? this.contextualReferenceType(
60679
+ argument2,
60680
+ inferred,
60681
+ parameter3.type
60682
+ ) : inferred.type,
60683
+ parameter3.type,
60684
+ this.project
60685
+ )
60604
60686
  };
60605
60687
  })
60606
60688
  );
60689
+ const direct = resolve(false);
60690
+ return direct.kind === "ambiguous" ? resolve(true) : direct;
60607
60691
  }
60608
60692
  /**
60609
60693
  * Types for the §6.1.1 step-2 tie-break, computed only when several
@@ -60624,7 +60708,9 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
60624
60708
  const argument2 = args[index];
60625
60709
  if (!argument2) return null;
60626
60710
  try {
60627
- return this.resolveExpression(argument2, scope).type;
60711
+ if (isContextualReferenceExpression(argument2))
60712
+ this.typeOverloadEntries(argument2, scope);
60713
+ return this.resolveReferenceExpression(argument2, scope);
60628
60714
  } catch (error) {
60629
60715
  if (error instanceof CompileError) return null;
60630
60716
  throw error;
@@ -60994,7 +61080,8 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
60994
61080
  (argument2) => this.overloadArgumentKind(argument2, scope)
60995
61081
  );
60996
61082
  argumentsList2.forEach((argument2, index) => {
60997
- if (kinds[index] !== "typed") this.typeOverloadEntries(argument2, scope);
61083
+ if (kinds[index] !== "typed" || isContextualReferenceExpression(argument2))
61084
+ this.typeOverloadEntries(argument2, scope);
60998
61085
  });
60999
61086
  const typed = argumentsList2.map(
61000
61087
  (argument2, index) => kinds[index] === "targetTyped" ? void 0 : this.resolveReferenceExpression(argument2, scope)
@@ -61042,11 +61129,11 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
61042
61129
  const fits = argumentsList2.every((argument2, index) => {
61043
61130
  const parameter3 = requiredAt(parameters, index).type;
61044
61131
  const resolved = typed[index];
61045
- if (resolved === void 0 || argument2.kind === "tuple" || kinds[index] === "numeric" && parameter3.kind === "tuple") {
61132
+ if (resolved === void 0 || parameter3.kind === "reference" && isContextualReferenceExpression(argument2) || argument2.kind === "tuple" || kinds[index] === "numeric" && parameter3.kind === "tuple") {
61046
61133
  return this.targetTypedArgumentFits(argument2, parameter3, scope);
61047
61134
  }
61048
61135
  return this.typedArgumentFits(
61049
- this.readReferences(resolved, parameter3).type,
61136
+ this.contextualReferenceType(argument2, resolved, parameter3),
61050
61137
  requiredAt(kinds, index),
61051
61138
  parameter3
61052
61139
  );
@@ -61218,6 +61305,12 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
61218
61305
  }
61219
61306
  case "annotated":
61220
61307
  return this.entryFits(argument2.expression, parameter3, scope);
61308
+ case "coalesce":
61309
+ return this.entryFits(
61310
+ argument2.left,
61311
+ { ...parameter3, nullable: true },
61312
+ scope
61313
+ ) && this.entryFits(argument2.right, parameter3, scope);
61221
61314
  case "conditional": {
61222
61315
  const { trueScope, falseScope } = this.conditionalBranchScopes(
61223
61316
  argument2,
@@ -61297,12 +61390,12 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
61297
61390
  */
61298
61391
  entryFits(argument2, parameter3, scope) {
61299
61392
  const kind = this.overloadArgumentKind(argument2, scope);
61300
- if (kind === "targetTyped" || argument2.kind === "tuple" || kind === "numeric" && parameter3.kind === "tuple") {
61393
+ if (kind === "targetTyped" || parameter3.kind === "reference" && isContextualReferenceExpression(argument2) || argument2.kind === "tuple" || kind === "numeric" && parameter3.kind === "tuple") {
61301
61394
  return this.targetTypedArgumentFits(argument2, parameter3, scope);
61302
61395
  }
61303
- const resolved = isPureArgument(argument2) ? this.resolveExpression(argument2, scope) : this.overloadEntries.get(argument2) ?? this.resolveReferenceExpression(argument2, scope);
61396
+ const resolved = isPureArgument(argument2) ? this.resolveReferenceExpression(argument2, scope) : this.overloadEntries.get(argument2) ?? this.resolveReferenceExpression(argument2, scope);
61304
61397
  return this.typedArgumentFits(
61305
- this.readReferences(resolved, parameter3).type,
61398
+ this.contextualReferenceType(argument2, resolved, parameter3),
61306
61399
  kind,
61307
61400
  parameter3
61308
61401
  );
@@ -61345,6 +61438,10 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
61345
61438
  case "annotated":
61346
61439
  visit(argument2.expression, scope);
61347
61440
  return;
61441
+ case "coalesce":
61442
+ visit(argument2.left, scope);
61443
+ visit(argument2.right, scope);
61444
+ return;
61348
61445
  case "conditional": {
61349
61446
  visit(argument2.condition, scope);
61350
61447
  const { trueScope, falseScope } = this.conditionalBranchScopes(
@@ -61529,19 +61626,16 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
61529
61626
  pos,
61530
61627
  inferred?.typeArguments
61531
61628
  );
61532
- if (signature.typeArguments.length > 0)
61533
- this.options.onCallResolved?.(pos, {
61534
- ...symbol,
61535
- type: signature.returnType,
61536
- returnType: signature.returnType,
61537
- parameters: signature.parameters,
61538
- typeParameters: (symbol.typeParameters ?? []).map(
61539
- (parameter3, index) => ({
61540
- ...parameter3,
61541
- type: requiredAt(signature.typeArguments, index)
61542
- })
61543
- )
61544
- });
61629
+ this.options.onCallResolved?.(pos, {
61630
+ ...symbol,
61631
+ type: signature.returnType,
61632
+ returnType: signature.returnType,
61633
+ parameters: signature.parameters,
61634
+ typeParameters: (symbol.typeParameters ?? []).map((parameter3, index) => ({
61635
+ ...parameter3,
61636
+ type: requiredAt(signature.typeArguments, index)
61637
+ }))
61638
+ });
61545
61639
  const wireTypeArguments = signature.typeArguments.length ? {
61546
61640
  typeArguments: signature.typeArguments.map(
61547
61641
  (argument2) => toWireType(argument2, this.project)
@@ -61716,7 +61810,12 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
61716
61810
  const prior = choice?.preResolved[index];
61717
61811
  const cached = inferred?.resolved.get(argument2);
61718
61812
  let resolved;
61719
- if (prior !== void 0) resolved = this.readReferences(prior, target);
61813
+ if (prior !== void 0 && !(target?.kind === "reference" && isContextualReferenceExpression(argument2) && !isNeoScriptTypeAssignable(
61814
+ this.readReferences(prior, target).type,
61815
+ target,
61816
+ this.project
61817
+ )))
61818
+ resolved = this.readReferences(prior, target);
61720
61819
  else if (cached && (sameCompilationType(cached.type, expected) || cached.pointer.type === "callFunction" /* CallFunction */)) {
61721
61820
  resolved = cached;
61722
61821
  } else {
@@ -61725,6 +61824,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
61725
61824
  const compile = () => this.resolveExpression(argument2, scope, target);
61726
61825
  resolved = weighed ?? (argument2.kind === "lambda" && lambdas ? this.inLambdaFrame(lambdas, argument2, compile) : compile());
61727
61826
  }
61827
+ resolved = this.contextualReference(argument2, resolved, scope, target);
61728
61828
  resolved = this.convertSequence(resolved, target);
61729
61829
  if (!isUnknown(expected)) {
61730
61830
  this.requireReferenceArgument(
@@ -61799,7 +61899,10 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
61799
61899
  if (!hasIndependentTypeEvidence(argument2)) return;
61800
61900
  const prior = weighed?.typed[index];
61801
61901
  if (prior !== void 0) {
61802
- inference.add(this.readReferences(prior, void 0).type, pattern);
61902
+ inference.add(
61903
+ this.referenceInferenceType(argument2, prior, pattern),
61904
+ pattern
61905
+ );
61803
61906
  return;
61804
61907
  }
61805
61908
  const group = pattern.kind === "delegate" ? this.inferredMethodGroupTarget(argument2, probe) : null;
@@ -61810,10 +61913,13 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
61810
61913
  pattern
61811
61914
  );
61812
61915
  } else {
61813
- const resolved = weighed?.contextless.get(argument2) ?? this.resolveExpression(argument2, probe);
61916
+ const resolved = weighed?.contextless.get(argument2) ?? this.resolveReferenceExpression(argument2, probe);
61814
61917
  weighed?.contextless.set(argument2, resolved);
61815
61918
  resolvedArguments.set(argument2, resolved);
61816
- inference.add(resolved.type, pattern);
61919
+ inference.add(
61920
+ this.referenceInferenceType(argument2, resolved, pattern),
61921
+ pattern
61922
+ );
61817
61923
  }
61818
61924
  });
61819
61925
  const error = inference.solve(callbacks, (callback, inputs) => {
@@ -64883,7 +64989,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
64883
64989
  }
64884
64990
  return canonicalPath(target) ?? "the slot";
64885
64991
  }
64886
- /** P112 §3.1. A value never converts to a reference, since it has no row. */
64992
+ /** A transient value has no stored slot to pass by reference. */
64887
64993
  requireReferenceArgument(source, target, parameter3, pos) {
64888
64994
  if (target.kind !== "reference") return;
64889
64995
  if (source.kind === "reference") return;
@@ -64892,7 +64998,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
64892
64998
  const value = this.describe(source);
64893
64999
  const article = /^[aeiou]/i.test(value) ? "an" : "a";
64894
65000
  throw new CompileError(
64895
- `Argument \`${parameter3}\` needs a ${this.describe(target)}, not ${article} ${value}. Wrap a member path in Reference() to pass a stored ${this.describe(read)}.`,
65001
+ `Argument \`${parameter3}\` needs a ${this.describe(target)}, not ${article} ${value}. Pass a stored ${this.describe(read)} member instead.`,
64896
65002
  pos
64897
65003
  );
64898
65004
  }
@@ -67165,6 +67271,7 @@ function weighedLambda(probes, expected) {
67165
67271
  if (probe === void 0 || probe.type.kind !== "delegate") return void 0;
67166
67272
  if (probe.needsReturnContext !== true && sameCompilationType(probe.type.returnType, expected.returnType))
67167
67273
  return probe;
67274
+ if (expected.returnType.kind === "reference") return void 0;
67168
67275
  return probe.retype?.(expected.returnType);
67169
67276
  }
67170
67277
  function replaceReturns(instructions, replaced) {
@@ -67287,6 +67394,9 @@ function andList(texts) {
67287
67394
  if (texts.length <= 2) return texts.join(" and ");
67288
67395
  return `${texts.slice(0, -1).join(", ")}, and ${texts[texts.length - 1]}`;
67289
67396
  }
67397
+ function isContextualReferenceExpression(expression) {
67398
+ return expression.kind === "conditional" || expression.kind === "switchExpression" || expression.kind === "coalesce" || expression.kind === "annotated";
67399
+ }
67290
67400
  function isPureArgument(expression) {
67291
67401
  switch (expression.kind) {
67292
67402
  case "litInt":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.89.0",
3
+ "version": "0.89.1",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@ description: >-
9
9
  `@neocompose/cli` or `node cli/bin/neo.mjs` in the neo-compose repository.
10
10
  ---
11
11
 
12
- <!-- reviewed-through-cli: 0.89.0 -->
12
+ <!-- reviewed-through-cli: 0.89.1 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -109,7 +109,7 @@ The marker near the top of `SKILL.md` must exactly match the package version,
109
109
  and this quoted copy is checked too:
110
110
 
111
111
  ```html
112
- <!-- reviewed-through-cli: 0.89.0 -->
112
+ <!-- reviewed-through-cli: 0.89.1 -->
113
113
  ```
114
114
 
115
115
  Keep this skill on current behavior. Upgrade steps, rollout prerequisites,
@@ -26,7 +26,8 @@ public Reference<int> TrackedStat; // another member's int
26
26
 
27
27
  - `Reference<T>` also works as a parameter, collection element, dictionary
28
28
  value, and unbound generic argument (`Box<Reference<int>>`), and keeps that
29
- storage type through push and pull. Use `Set<T>` parameters for several
29
+ storage type through push and pull, including nested generic collections
30
+ and named tuples. Use `Set<T>` parameters for several
30
31
  selections.
31
32
  - `@settings(collection: Owner.Member)` binds a class reference to one
32
33
  collection. Unbound references select any stored row of `T` or a subclass,
@@ -47,20 +48,31 @@ public Reference<int> TrackedStat; // another member's int
47
48
 
48
49
  ```neo
49
50
  public Reference<Item> StarterSeed = root.Assets.Items.Index.Slug["seed.carrot"]!;
50
- public Reference<int> Gold = Reference(root.Player.Gold);
51
- public Reference<string> Greeting = Reference(root.Npcs.Index.Slug["mira"]!.Greeting);
52
- public Reference<NeoVariant<Tree>> Look = Reference(root.Garden.Felled);
51
+ public Reference<int> Gold = root.Player.Gold;
52
+ public Reference<string> Greeting = root.Npcs.Index.Slug["mira"]!.Greeting;
53
+ public Reference<NeoVariant<Tree>> Look = root.Garden.Felled;
53
54
  ```
54
55
 
55
56
  - An unbound handle default may name its row by a unique index: a `root`
56
57
  path to a List, `.Index.<Name>`, and a string literal key. Other shapes are
57
58
  initializer code.
58
- - Slot defaults wrap a member path in `Reference()`. A plain value is not a
59
- default, and `this` doesn't exist yet. A `NeoVariant<T>` member's own
60
- default stays the bare variant path.
59
+ - Slot defaults accept compatible stored member paths directly; `Reference()`
60
+ remains valid. Literals and computed values cannot supply a slot, and `this`
61
+ does not exist yet. A `NeoVariant<T>` member's own default stays the bare
62
+ variant path.
61
63
  - List and dictionary defaults may hold slot references, nested too:
62
- `List<Reference<int>> Counts = [Reference(root.Save.Bits)]`. Each instance
64
+ `List<Reference<int>> Counts = [root.Save.Bits]`. Each instance
63
65
  owns the collection; entries target the original slots.
66
+ - `Set<Reference<T>>` selections can mix bare paths and explicit references.
67
+ Computed selections deduplicate by target identity, not by current value.
68
+ A literal selection of fixed targets must not repeat the same target.
69
+ - A bare path to an existing reference copies its current target when the
70
+ object is constructed. Later rebinding of that source reference does not
71
+ rebind the copy.
72
+ - A nullable target still needs a stored row. Pin an otherwise absent
73
+ nullable member in its owner's initializer, for example
74
+ `new() { Maybe = null }`, before referencing it. A declaration default alone
75
+ does not create that row.
64
76
 
65
77
  ## Source forms
66
78
 
@@ -89,11 +101,18 @@ Reference<NeoDialogue>(id: "capitol-dialogue-id")
89
101
 
90
102
  ## Runtime semantics
91
103
 
92
- `Reference(place)` forms a reference to a stored member: `this.Gold`,
93
- `root.Player.Gold`, `item.Count`. Locals, parameters, list or dictionary
94
- elements, vector components, getters, query results, and new values have no
95
- row. When `Selected` is `Reference<Item>`, `Reference(this.Selected)` is
96
- `Reference<Reference<Item>>`. Access checks apply where it is formed.
104
+ When executable code expects `Reference<T>`, pass a compatible stored member
105
+ such as `this.Gold`, `root.Player.Gold`, or `item.Count` directly. This works
106
+ for constructor and function arguments, returns, typed locals, object
107
+ initializers, and collection entries. The reference keeps following that
108
+ slot when its value changes. `Reference(place)` remains valid when an
109
+ explicit reference is needed.
110
+
111
+ Locals, parameters, list or dictionary elements, vector components, getters,
112
+ query results, and new scalar values have no row. A narrowed nullable member
113
+ still names its declared storage type. When `Selected` is `Reference<Item>`,
114
+ `Reference(this.Selected)` is `Reference<Reference<Item>>`. Access checks
115
+ apply where the reference is formed.
97
116
 
98
117
  ```neo
99
118
  this.A = 5; // writes 5 into A's target
@@ -106,8 +125,8 @@ this.A += 1; // reads A's target, writes it back
106
125
  - Assigning a same-typed reference rebinds; anything else writes through.
107
126
  `this.A = c ? first : second` rebinds when both arms are references, writes
108
127
  when both are scalars, and reads the chosen target when mixed.
109
- - A plain value never becomes a reference: passing `3` to a `Reference<int>`
110
- parameter is an error.
128
+ - A value without a stored slot never becomes a reference: passing `3` to a
129
+ `Reference<int>` parameter is an error.
111
130
  - Constructors may initialize their own reference fields but can't write
112
131
  through external targets.
113
132
  - `==` compares handles by identity and slots by value. Collections of
@@ -138,6 +157,10 @@ Lookup assignment still needs an existing backing row.
138
157
  (`root.Assets.Quests.Daily` or `Reference(root.Assets.Quests.Daily)`) and
139
158
  follow the same rules. The path may cross construction defaults and
140
159
  declarations from the same push; no preliminary push or manual ID is needed.
160
+ Pull emits bare paths in `collectionValue` settings when the target is reachable
161
+ from a root; otherwise it retains `Reference(id: "...")`. Lookup variant folder
162
+ bindings and `.Bind(root.Assets.Items.Index.Slug["seed.carrot"]!)` use the same
163
+ implicit reference conversion.
141
164
  Invalid reference settings are errors, never silently dropped.
142
165
 
143
166
  ## Init-built objects
@@ -145,7 +168,7 @@ Invalid reference settings are errors, never silently dropped.
145
168
  An object `Root.neo` writes is init-built when its class has an `init` block.
146
169
  A declaration binding may name a member inside it, and push stores that
147
170
  member's row. A reference captured by `init` or a constructor
148
- (`new Rule(Reference(this.Weather))`) keeps its target's row. Status refuses a
171
+ (`new Rule(this.Weather)`) keeps its target's row. Status refuses a
149
172
  value row's reference to a member of an init-built object (bind it from a
150
173
  declaration instead) and a binding to a class or list that an `init` block
151
174
  builds.
@@ -86,7 +86,7 @@ class root.
86
86
  NeoLookupVariantFolder<ExampleObject, NeoSortingLayer> Root = new(
87
87
  path: "",
88
88
  collection: Assets.SortingLayers,
89
- collectionValue: Reference(root.Assets.SortingLayers),
89
+ collectionValue: root.Assets.SortingLayers,
90
90
  );
91
91
 
92
92
  @id("736dddf8-2881-47e1-a1d3-55fb65ea9f2a")
@@ -97,13 +97,14 @@ NeoLookupVariant<ExampleObject, NeoSortingLayer> Layered = new(
97
97
  ```
98
98
 
99
99
  - `collection` names the eligible list member; `collectionValue` the stored
100
- list whose rows it enumerates. Prefer the `Reference(root...)` form pull
100
+ list whose rows it enumerates. Prefer the bare `root...` path pull
101
101
  emits.
102
102
  - Lookup `initialize` receives the row; lookup `apply` receives the object,
103
103
  then the row.
104
104
  - A `NeoLookupVariant<TObject, TValue>` member holds an unbound handle. A
105
105
  plain `NeoVariant<TObject>` member may pick one row with
106
- `.Bind(Reference(...))`; never bind a row to the lookup-handle type.
106
+ `.Bind(root.Assets.Items.Index.Slug["seed.carrot"]!)`; explicit
107
+ `Reference(...)` remains valid. Never bind a row to the lookup-handle type.
107
108
 
108
109
  ## Type restrictions
109
110