@neocompose/cli 0.84.1 → 0.84.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.84.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 89cf02a: Allow runtime interface tests on generic values whose bounds admit implementing subtypes, including members accessed through open generic classes. Continue rejecting provably disjoint types.
8
+ - de01b14: Keep proven interface capabilities after negative guards that continue, break, return, or throw. Guarded receivers retain their class members, support interface member dispatch and assignment, and offer both sets of members in editor completion.
9
+ - 6a93d79: Report unknown class names in stored object initializers at the constructor type token, including nested arguments and collections. Refresh editor diagnostics when the class is created, renamed, or removed.
10
+ - c3c0b0c: Report construction-path initializer failures at their original source file and token instead of a downstream reference. Keep mapped diagnostics through deferred reference validation and omit duplicate reports from references to the same failure.
11
+
3
12
  ## 0.84.1
4
13
 
5
14
  ### Patch Changes
@@ -17587,7 +17587,7 @@ var init_registry = __esm({
17587
17587
  "../packages/manifest/schema-contract/registry.mjs"() {
17588
17588
  "use strict";
17589
17589
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
17590
- cliVersion: "0.84.1",
17590
+ cliVersion: "0.84.2",
17591
17591
  projectFileUploadBatchSize: 32,
17592
17592
  documentRecords: {
17593
17593
  member: {
@@ -21418,7 +21418,8 @@ var init_executable_parser = __esm({
21418
21418
  }
21419
21419
  if (isIdentifier(t) && t.text === NEOSCRIPT_CONSTRUCTOR_KEYWORD) {
21420
21420
  this.next();
21421
- const className = isIdentifier(this.peek()) ? this.next().text : null;
21421
+ const classToken = isIdentifier(this.peek()) ? this.next() : null;
21422
+ const className = classToken?.text ?? null;
21422
21423
  const typeArguments = [];
21423
21424
  if (this.eat("operator", "<")) {
21424
21425
  if (className === null) {
@@ -21452,6 +21453,7 @@ var init_executable_parser = __esm({
21452
21453
  return {
21453
21454
  kind: "new",
21454
21455
  className,
21456
+ ...classToken ? { classNamePos: at(classToken) } : {},
21455
21457
  ...typeArguments.length > 0 ? { typeArguments } : {},
21456
21458
  args,
21457
21459
  ...hasArgumentList ? { hasArgumentList: true } : {},
@@ -31635,6 +31637,22 @@ function neoScriptMemberLookupType(type) {
31635
31637
  }
31636
31638
  return nullable2 && current.nullable !== true ? { ...current, nullable: true } : current;
31637
31639
  }
31640
+ function neoScriptMemberReceiverType(type, memberName, project) {
31641
+ const primary = neoScriptMemberLookupType(type);
31642
+ if (primary.kind !== "named" || primary.narrowedInterfaces === void 0)
31643
+ return primary;
31644
+ const declaration = project.typeById.get(primary.typeId);
31645
+ if (declaration?.kind === "class" && (memberName === NEOSCRIPT_CLASS_ID_MEMBER || memberName === "Clone"))
31646
+ return primary;
31647
+ if (declaration?.members.some((member) => member.name === memberName))
31648
+ return primary;
31649
+ for (const bound of Object.values(primary.narrowedInterfaces)) {
31650
+ if (project.typeById.get(bound.typeId)?.members.some((member) => member.name === memberName)) {
31651
+ return { ...bound, nullable: primary.nullable === true };
31652
+ }
31653
+ }
31654
+ return primary;
31655
+ }
31638
31656
  function isNeoScriptHandleReferenceTarget(target, isValueType) {
31639
31657
  switch (target.kind) {
31640
31658
  case "list":
@@ -31952,6 +31970,20 @@ function isNeoScriptTypeAssignable(source, target, project) {
31952
31970
  if (isUnknownType(source) || isUnknownType(target)) return false;
31953
31971
  if (isNullType(source)) return isNullableType2(target);
31954
31972
  if (isNullableType2(source) && !isNullableType2(target)) return false;
31973
+ if (target.kind === "named" && target.narrowedInterfaces !== void 0) {
31974
+ for (const bound of Object.values(target.narrowedInterfaces)) {
31975
+ if (!isNeoScriptTypeAssignable(source, bound, project)) return false;
31976
+ }
31977
+ }
31978
+ if (source.kind === "named" && source.narrowedInterfaces !== void 0) {
31979
+ const direct = target.kind === "named" ? source.narrowedInterfaces[target.typeId] : void 0;
31980
+ if (direct !== void 0 && isNeoScriptTypeAssignable(direct, target, project))
31981
+ return true;
31982
+ for (const bound of Object.values(source.narrowedInterfaces)) {
31983
+ if (bound !== direct && isNeoScriptTypeAssignable(bound, target, project))
31984
+ return true;
31985
+ }
31986
+ }
31955
31987
  if (source.kind === "primitive" && target.kind === "primitive") {
31956
31988
  if (source.name === target.name) return true;
31957
31989
  return source.name === "int" && (target.name === "float" || target.name === "decimal");
@@ -32036,9 +32068,40 @@ function isNeoScriptTypeAssignable(source, target, project) {
32036
32068
  }
32037
32069
  return false;
32038
32070
  }
32071
+ function withoutNeoScriptInterfaceProofs(type) {
32072
+ if (type.kind !== "named" || type.narrowedInterfaces === void 0)
32073
+ return type;
32074
+ const { narrowedInterfaces: _proofs, ...declared } = type;
32075
+ return declared;
32076
+ }
32039
32077
  function commonNeoScriptAssignableType(left, right, project) {
32040
32078
  if (isNeoScriptTypeAssignable(left, right, project)) return right;
32041
32079
  if (isNeoScriptTypeAssignable(right, left, project)) return left;
32080
+ if (left.kind === "named" && right.kind === "named" && (left.narrowedInterfaces !== void 0 || right.narrowedInterfaces !== void 0)) {
32081
+ const declaredLeft = withoutNeoScriptInterfaceProofs(left);
32082
+ const declaredRight = withoutNeoScriptInterfaceProofs(right);
32083
+ const common = commonNeoScriptAssignableType(
32084
+ declaredLeft,
32085
+ declaredRight,
32086
+ project
32087
+ );
32088
+ if (common?.kind !== "named") return null;
32089
+ const proofs = {};
32090
+ for (const [source, other] of [
32091
+ [left, right],
32092
+ [right, left]
32093
+ ]) {
32094
+ for (const [id2, proof] of Object.entries(
32095
+ source.narrowedInterfaces ?? {}
32096
+ )) {
32097
+ if (proofs[id2] !== void 0) continue;
32098
+ const opposite = other.narrowedInterfaces?.[id2] ?? other;
32099
+ if (isNeoScriptTypeAssignable(opposite, proof, project))
32100
+ proofs[id2] = proof;
32101
+ }
32102
+ }
32103
+ return { ...common, narrowedInterfaces: proofs };
32104
+ }
32042
32105
  return null;
32043
32106
  }
32044
32107
  function isNeoVariantTypeId(typeId, project) {
@@ -34302,17 +34365,22 @@ function mergeFactSets(sets) {
34302
34365
  )
34303
34366
  );
34304
34367
  }
34305
- function mergeNarrowingMaps(maps) {
34368
+ function mergeNarrowingMaps(maps, project) {
34306
34369
  const first = maps[0];
34307
34370
  if (!first) return /* @__PURE__ */ new Map();
34308
34371
  const result = /* @__PURE__ */ new Map();
34309
34372
  for (const [path, narrowed] of first) {
34310
- if (maps.slice(1).every((map) => {
34311
- const candidate = map.get(path);
34312
- return candidate?.rootEntry === narrowed.rootEntry && typeRefsEqual(candidate.type, narrowed.type);
34313
- })) {
34314
- result.set(path, narrowed);
34373
+ let common = narrowed.type;
34374
+ for (let index = 1; index < maps.length; index++) {
34375
+ const candidate = maps[index].get(path);
34376
+ if (candidate?.rootEntry !== narrowed.rootEntry) {
34377
+ common = null;
34378
+ break;
34379
+ }
34380
+ common = commonNeoScriptAssignableType(common, candidate.type, project);
34381
+ if (common === null) break;
34315
34382
  }
34383
+ if (common !== null) result.set(path, { ...narrowed, type: common });
34316
34384
  }
34317
34385
  return result;
34318
34386
  }
@@ -34341,6 +34409,16 @@ function typeRefsEqual(left, right) {
34341
34409
  if (left.kind === "named" && right.kind === "named") {
34342
34410
  const leftArgs = left.typeArguments ?? [];
34343
34411
  const rightArgs = right.typeArguments ?? [];
34412
+ if (left.narrowedInterfaces !== right.narrowedInterfaces) {
34413
+ const leftBounds = Object.entries(left.narrowedInterfaces ?? {});
34414
+ const rightBounds = right.narrowedInterfaces ?? {};
34415
+ if (leftBounds.length !== Object.keys(rightBounds).length) return false;
34416
+ for (const [id2, leftBound] of leftBounds) {
34417
+ const rightBound = rightBounds[id2];
34418
+ if (rightBound === void 0 || !typeRefsEqual(leftBound, rightBound))
34419
+ return false;
34420
+ }
34421
+ }
34344
34422
  return left.typeId === right.typeId && leftArgs.length === rightArgs.length && leftArgs.every((item, index) => {
34345
34423
  const other = rightArgs[index];
34346
34424
  return other !== void 0 && typeRefsEqual(item, other);
@@ -35320,6 +35398,10 @@ function typesOverlap(left, right, project) {
35320
35398
  if (isUnknown(left) || isUnknown(right)) return true;
35321
35399
  if (isNull(left)) return isNullable(right);
35322
35400
  if (isNull(right)) return isNullable(left);
35401
+ left = neoScriptMemberLookupType(left);
35402
+ right = neoScriptMemberLookupType(right);
35403
+ if (left.kind === "typeParameter" || right.kind === "typeParameter")
35404
+ return true;
35323
35405
  if (isNeoScriptTypeAssignable(left, right, project) || isNeoScriptTypeAssignable(right, left, project))
35324
35406
  return true;
35325
35407
  if (left.kind === "named" && right.kind === "named") {
@@ -36689,7 +36771,7 @@ var init_strict_resolver = __esm({
36689
36771
  statement.init.pos
36690
36772
  );
36691
36773
  }
36692
- const localType = declared ?? initial.type;
36774
+ const localType = declared ?? withoutNeoScriptInterfaceProofs(initial.type);
36693
36775
  if (declared) {
36694
36776
  this.requireAssignable(
36695
36777
  initial.type,
@@ -36704,16 +36786,18 @@ var init_strict_resolver = __esm({
36704
36786
  initial.pointer,
36705
36787
  this.project
36706
36788
  );
36707
- scope.define(
36708
- scopeVariable(
36709
- statement.name,
36710
- local,
36711
- localType,
36712
- "local" /* Local */,
36713
- localOwnership(initial),
36714
- this.localWriteTargetRoot(initial, scope)
36715
- )
36789
+ const entry = scopeVariable(
36790
+ statement.name,
36791
+ local,
36792
+ localType,
36793
+ "local" /* Local */,
36794
+ localOwnership(initial),
36795
+ this.localWriteTargetRoot(initial, scope)
36716
36796
  );
36797
+ scope.define(entry);
36798
+ if (inferred && localType !== initial.type) {
36799
+ scope.narrow(statement.name, entry, initial.type);
36800
+ }
36717
36801
  return { type: "variable" /* Variable */, variable: local };
36718
36802
  }
36719
36803
  case "if": {
@@ -36768,7 +36852,8 @@ var init_strict_resolver = __esm({
36768
36852
  mergeBranchInvalidations(scope, branchExitOwnershipScopes);
36769
36853
  if (branchExitNarrowings.length > 0) {
36770
36854
  for (const [path, narrowed] of mergeNarrowingMaps(
36771
- branchExitNarrowings
36855
+ branchExitNarrowings,
36856
+ this.project
36772
36857
  )) {
36773
36858
  const rootEntry = scope.lookup(pathRoot(path));
36774
36859
  if (rootEntry === narrowed.rootEntry) {
@@ -37694,7 +37779,7 @@ var init_strict_resolver = __esm({
37694
37779
  }
37695
37780
  mergeLoopNarrowings(parent, exitPaths) {
37696
37781
  if (exitPaths.length === 0) return;
37697
- const merged = mergeNarrowingMaps(exitPaths);
37782
+ const merged = mergeNarrowingMaps(exitPaths, this.project);
37698
37783
  for (const [path, narrowed] of parent.effectiveNarrowedPaths()) {
37699
37784
  const common = merged.get(path);
37700
37785
  if (parent.containsEntry(narrowed.rootEntry) && (common?.rootEntry !== narrowed.rootEntry || !typeRefsEqual(common.type, narrowed.type))) {
@@ -39447,8 +39532,13 @@ var init_strict_resolver = __esm({
39447
39532
  expression.receiver,
39448
39533
  scope
39449
39534
  );
39450
- if (receiverExpression.type.kind !== "named") return null;
39451
- owner = this.project.typeById.get(receiverExpression.type.typeId);
39535
+ const memberType2 = neoScriptMemberReceiverType(
39536
+ receiverExpression.type,
39537
+ expression.name,
39538
+ this.project
39539
+ );
39540
+ if (memberType2.kind !== "named") return null;
39541
+ owner = this.project.typeById.get(memberType2.typeId);
39452
39542
  symbol = owner?.members.find(
39453
39543
  (candidate) => candidate.name === expression.name && (candidate.kind === "function" || candidate.kind === "method")
39454
39544
  );
@@ -39459,7 +39549,10 @@ var init_strict_resolver = __esm({
39459
39549
  expression.pos
39460
39550
  );
39461
39551
  }
39462
- receiver = { kind: "instance", expression: receiverExpression };
39552
+ receiver = {
39553
+ kind: "instance",
39554
+ expression: { ...receiverExpression, type: memberType2 }
39555
+ };
39463
39556
  }
39464
39557
  } else {
39465
39558
  return null;
@@ -39536,9 +39629,13 @@ var init_strict_resolver = __esm({
39536
39629
  if (symbol === void 0 || symbol.static !== true) return null;
39537
39630
  } else {
39538
39631
  const receiver2 = this.resolveExpression(expression.receiver, scope);
39539
- if (receiver2.type.kind !== "named") return null;
39540
- receiverType = receiver2.type;
39541
- owner = this.project.typeById.get(receiver2.type.typeId);
39632
+ receiverType = neoScriptMemberReceiverType(
39633
+ receiver2.type,
39634
+ expression.name,
39635
+ this.project
39636
+ );
39637
+ if (receiverType.kind !== "named") return null;
39638
+ owner = this.project.typeById.get(receiverType.typeId);
39542
39639
  symbol = owner?.members.find(
39543
39640
  (candidate) => candidate.name === expression.name && (candidate.kind === "function" || candidate.kind === "method")
39544
39641
  );
@@ -39616,6 +39713,7 @@ var init_strict_resolver = __esm({
39616
39713
  pos
39617
39714
  );
39618
39715
  }
39716
+ entryType = withoutNeoScriptInterfaceProofs(entryType);
39619
39717
  for (const item of resolved) {
39620
39718
  const common = commonNeoScriptAssignableType(
39621
39719
  entryType,
@@ -39640,7 +39738,7 @@ var init_strict_resolver = __esm({
39640
39738
  }
39641
39739
  const type = {
39642
39740
  kind: "list",
39643
- elementType: entryType
39741
+ elementType: withoutNeoScriptInterfaceProofs(entryType)
39644
39742
  };
39645
39743
  const wire = toWireType(type, this.project);
39646
39744
  if (wire.type !== 6 /* List */) {
@@ -39706,6 +39804,7 @@ var init_strict_resolver = __esm({
39706
39804
  if (!keyType || !valueType) {
39707
39805
  throw new CompileError("Dictionary literal type inference failed.", pos);
39708
39806
  }
39807
+ valueType = withoutNeoScriptInterfaceProofs(valueType);
39709
39808
  for (const entry of resolved) {
39710
39809
  if (expectedDictionary) {
39711
39810
  this.requireAssignable(
@@ -39732,7 +39831,7 @@ var init_strict_resolver = __esm({
39732
39831
  const type = {
39733
39832
  kind: "dictionary",
39734
39833
  keyType,
39735
- valueType
39834
+ valueType: withoutNeoScriptInterfaceProofs(valueType)
39736
39835
  };
39737
39836
  const wire = toWireType(type, this.project);
39738
39837
  if (wire.type !== 5 /* Dictionary */) {
@@ -39988,8 +40087,19 @@ var init_strict_resolver = __esm({
39988
40087
  const target = this.resolveType(fact.targetType);
39989
40088
  const targetDefinition = target.kind === "named" ? this.project.typeById.get(target.typeId) : void 0;
39990
40089
  const naturalDefinition = natural.type.kind === "named" ? this.project.typeById.get(natural.type.typeId) : void 0;
39991
- const retainConcreteClass = !fact.bindingName && naturalDefinition?.kind === "class" && targetDefinition?.kind === "interface";
39992
- const narrowed = retainConcreteClass ? { ...natural.type, nullable: false } : { ...target, nullable: false };
40090
+ let narrowed = { ...target, nullable: false };
40091
+ if (!fact.bindingName && natural.type.kind === "named" && (naturalDefinition?.kind === "class" || naturalDefinition?.kind === "interface") && target.kind === "named" && targetDefinition?.kind === "interface") {
40092
+ narrowed = { ...natural.type, nullable: false };
40093
+ if (!isNeoScriptTypeAssignable(narrowed, target, this.project)) {
40094
+ narrowed = {
40095
+ ...narrowed,
40096
+ narrowedInterfaces: {
40097
+ ...natural.type.narrowedInterfaces,
40098
+ [target.typeId]: target
40099
+ }
40100
+ };
40101
+ }
40102
+ }
39993
40103
  if (mode === "all" && fact.path !== null && rootEntry !== null && place.type.kind !== "reference") {
39994
40104
  targetScope.narrow(fact.path, rootEntry, narrowed);
39995
40105
  }
@@ -40582,7 +40692,11 @@ var init_strict_resolver = __esm({
40582
40692
  if (receiver.dialogues !== void 0) {
40583
40693
  return this.resolveDialogueGroupRead(receiver, name, pos);
40584
40694
  }
40585
- const memberReceiverType = neoScriptMemberLookupType(receiver.type);
40695
+ const memberReceiverType = neoScriptMemberReceiverType(
40696
+ receiver.type,
40697
+ name,
40698
+ this.project
40699
+ );
40586
40700
  if (memberReceiverType.kind === "named") {
40587
40701
  const type = this.project.typeById.get(memberReceiverType.typeId);
40588
40702
  if (!type) {
@@ -41394,7 +41508,7 @@ var init_strict_resolver = __esm({
41394
41508
  }
41395
41509
  const type = {
41396
41510
  kind: "list",
41397
- elementType: entryType
41511
+ elementType: withoutNeoScriptInterfaceProofs(entryType)
41398
41512
  };
41399
41513
  const wire = toWireType(type, this.project);
41400
41514
  if (wire.type !== 6 /* List */) {
@@ -41635,7 +41749,11 @@ var init_strict_resolver = __esm({
41635
41749
  pos
41636
41750
  );
41637
41751
  }
41638
- const memberReceiverType = neoScriptMemberLookupType(receiver.type);
41752
+ const memberReceiverType = neoScriptMemberReceiverType(
41753
+ receiver.type,
41754
+ callee.name,
41755
+ this.project
41756
+ );
41639
41757
  const namedType2 = memberReceiverType.kind === "named" ? this.project.typeById.get(memberReceiverType.typeId) : void 0;
41640
41758
  const member = namedType2?.members.find(
41641
41759
  (candidate) => (candidate.kind === "function" || candidate.kind === "method") && candidate.name === callee.name
@@ -44060,7 +44178,11 @@ var init_strict_resolver = __esm({
44060
44178
  }
44061
44179
  const receiver = receiverValue();
44062
44180
  if (receiver.dialogues !== void 0) return null;
44063
- const memberReceiverType = neoScriptMemberLookupType(receiver.type);
44181
+ const memberReceiverType = neoScriptMemberReceiverType(
44182
+ receiver.type,
44183
+ memberName,
44184
+ this.project
44185
+ );
44064
44186
  if (memberReceiverType.kind !== "named") return null;
44065
44187
  const type = this.project.typeById.get(memberReceiverType.typeId);
44066
44188
  const symbol = type?.members.find(
@@ -50978,7 +51100,29 @@ function validateExpression(expression, expected, scope, environment, uri, range
50978
51100
  );
50979
51101
  return;
50980
51102
  }
50981
- if (!declaration) return;
51103
+ if (!declaration) {
51104
+ if (expression.className !== null && !environment.projectIndex.typeByName.has(expression.className) && semanticTypeRef(
51105
+ {
51106
+ name: expression.className,
51107
+ ...expression.typeArguments ? { arguments: expression.typeArguments.map(semanticAstType) } : {}
51108
+ },
51109
+ scope,
51110
+ environment
51111
+ ) === void 0) {
51112
+ pushDiagnostic(
51113
+ diagnostics,
51114
+ uri,
51115
+ anchor ? anchorSpan(
51116
+ anchor,
51117
+ expression.classNamePos ?? expression.pos,
51118
+ expression.className.length
51119
+ ) : range2,
51120
+ "unknown-construction-type",
51121
+ `Unknown class '${expression.className}'.`
51122
+ );
51123
+ }
51124
+ return;
51125
+ }
50982
51126
  if (declaration.modifiers.includes("abstract")) {
50983
51127
  pushDiagnostic(
50984
51128
  diagnostics,
@@ -55341,7 +55485,7 @@ function sourceParameter(parameter4, owner, typeIds, id2, functionTypeParameters
55341
55485
  };
55342
55486
  }
55343
55487
  function substituteInheritedSourceMember(member, baseInfo, resolvedBaseType) {
55344
- if (baseInfo.declaration.kind !== "class" || resolvedBaseType.kind !== "named") {
55488
+ if (baseInfo.declaration.kind !== "class" && baseInfo.declaration.kind !== "interface" || resolvedBaseType.kind !== "named") {
55345
55489
  return {
55346
55490
  ...member,
55347
55491
  inheritedFrom: member.inheritedFrom ?? {
@@ -143486,13 +143630,6 @@ function drainValueReferenceObligationsV4(state, manifest, options) {
143486
143630
  ...composeReferenceSitePosition(move.site, options.sourceTextByUri)
143487
143631
  });
143488
143632
  }
143489
- for (const recorded of options.registry.loweringFailures) {
143490
- failures.push({
143491
- message: recorded.message,
143492
- ...recorded.code === void 0 ? {} : { code: recorded.code },
143493
- ...composeReferenceSitePosition(recorded.site, options.sourceTextByUri)
143494
- });
143495
- }
143496
143633
  for (const obligation of [
143497
143634
  ...collectionValueObligations(manifest),
143498
143635
  ...options.registry.referenceObligations
@@ -143530,7 +143667,17 @@ function drainValueReferenceObligationsV4(state, manifest, options) {
143530
143667
  ...composeReferenceSitePosition(obligation.site, options.sourceTextByUri)
143531
143668
  });
143532
143669
  }
143533
- return failures;
143670
+ const loweringFailures = options.registry.loweringFailures.map(
143671
+ (recorded) => recorded instanceof SchemaSourceError ? recorded : {
143672
+ message: recorded.message,
143673
+ ...recorded.code === void 0 ? {} : { code: recorded.code },
143674
+ ...composeReferenceSitePosition(
143675
+ recorded.site,
143676
+ options.sourceTextByUri
143677
+ )
143678
+ }
143679
+ );
143680
+ return [...loweringFailures, ...failures];
143534
143681
  }
143535
143682
  function withoutDeletedValues(state, deletedValueIds) {
143536
143683
  if (deletedValueIds.size === 0) return state;
@@ -152062,6 +152209,7 @@ var INFERRED_GENERIC_CLASS_PREFIX, MEMBER_KIND_DICTIONARY2, MEMBER_KIND_LIST2, M
152062
152209
  var init_value_sources = __esm({
152063
152210
  "src/project-source/value-sources.ts"() {
152064
152211
  "use strict";
152212
+ init_source_diagnostics();
152065
152213
  init_immutable_json3();
152066
152214
  init_lower_support();
152067
152215
  init_record_fields();
@@ -157111,6 +157259,10 @@ function walkValueSegments(walk, startId, segments, path, subject, fail2) {
157111
157259
  const resolved = walk.resolveVirtualPath?.(startId, segments);
157112
157260
  if (resolved != null) return resolved;
157113
157261
  } catch (error) {
157262
+ if (error instanceof SchemaSourceError) {
157263
+ walk.registry.loweringFailures.push(error);
157264
+ return null;
157265
+ }
157114
157266
  fail2(
157115
157267
  `${subject} ${path} could not resolve its construction: ${error instanceof Error ? error.message : String(error)}`
157116
157268
  );
@@ -157185,6 +157337,7 @@ var ROOT_PROJECT_FIELDS2, ROOT_PATH_PENDING_PREFIX;
157185
157337
  var init_root_source = __esm({
157186
157338
  "src/project-source/root-source.ts"() {
157187
157339
  "use strict";
157340
+ init_source_diagnostics();
157188
157341
  init_project_root();
157189
157342
  init_value_comparison();
157190
157343
  init_members();
@@ -160362,30 +160515,47 @@ function computeStatus(workspace, options, conflictedFiles, parseErrors, parseWa
160362
160515
  [...staticValues.records, ...memberDefaults.records, ...rootValues.records],
160363
160516
  staticMemberValueIds
160364
160517
  );
160518
+ const sourceTextByUri = new Map(
160519
+ sourceEntries.map((entry) => [entry.relPath, entry.source])
160520
+ );
160365
160521
  let collectionBindings;
160366
160522
  const resolveVirtualPath = (rootId, segments) => {
160367
- if (collectionBindings === void 0) {
160368
- const candidate = /* @__PURE__ */ new Map();
160369
- for (const [key, record7] of Object.entries(rootPathResolutionState)) {
160370
- if (record7.file !== void 0 && isSchemaRecordKindV4(record7.recordKind) && !documentsByKey.has(key))
160371
- continue;
160372
- candidate.set(key, { ...record7, deleted: false });
160373
- }
160374
- for (const row of valueLowerRegistry.pendingValues.values()) {
160375
- candidate.set(`value:${row.id}`, {
160376
- recordKind: "value",
160377
- recordId: row.id,
160378
- contentHash: "",
160379
- deleted: false,
160380
- data: { id: row.id, ...encodeStoredValueRowFields(row) }
160381
- });
160523
+ if (collectionBindings instanceof SchemaSourceError)
160524
+ throw collectionBindings;
160525
+ try {
160526
+ if (collectionBindings === void 0) {
160527
+ const candidate = /* @__PURE__ */ new Map();
160528
+ for (const [key, record7] of Object.entries(rootPathResolutionState)) {
160529
+ if (record7.file !== void 0 && isSchemaRecordKindV4(record7.recordKind) && !documentsByKey.has(key))
160530
+ continue;
160531
+ candidate.set(key, { ...record7, deleted: false });
160532
+ }
160533
+ for (const row of valueLowerRegistry.pendingValues.values()) {
160534
+ candidate.set(`value:${row.id}`, {
160535
+ recordKind: "value",
160536
+ recordId: row.id,
160537
+ contentHash: "",
160538
+ deleted: false,
160539
+ data: { id: row.id, ...encodeStoredValueRowFields(row) }
160540
+ });
160541
+ }
160542
+ const document = readPulledProjectDocumentV4(candidate);
160543
+ compilePulledProjectDocumentForEvaluationV4(document);
160544
+ collectionBindings = new CollectionBindingMaterializer(document);
160382
160545
  }
160383
- const document = readPulledProjectDocumentV4(candidate);
160384
- compilePulledProjectDocumentForEvaluationV4(document);
160385
- collectionBindings = new CollectionBindingMaterializer(document);
160546
+ const resolved = collectionBindings.resolvePath(rootId, segments);
160547
+ return resolved;
160548
+ } catch (error) {
160549
+ const diagnostic = initializerRuntimeDiagnostic(
160550
+ error,
160551
+ valueLowerRegistry.initializerSites,
160552
+ sourceTextByUri,
160553
+ projectAnalysisV4
160554
+ );
160555
+ if (!hasAuthoredSourceFile(diagnostic.file)) throw error;
160556
+ if (collectionBindings === void 0) collectionBindings = diagnostic;
160557
+ throw diagnostic;
160386
160558
  }
160387
- const resolved = collectionBindings.resolvePath(rootId, segments);
160388
- return resolved;
160389
160559
  };
160390
160560
  const resolvedCollectionPaths = resolveRootPathCollectionValuesV4({
160391
160561
  manifest,
@@ -160568,7 +160738,7 @@ function computeStatus(workspace, options, conflictedFiles, parseErrors, parseWa
160568
160738
  unavailableSlots: rootValues.unavailableSlots,
160569
160739
  registry: valueLowerRegistry
160570
160740
  });
160571
- if (collectionBindings !== void 0) {
160741
+ if (collectionBindings !== void 0 && !(collectionBindings instanceof SchemaSourceError)) {
160572
160742
  const bindings = collectionBindings;
160573
160743
  const templateInitializer = (id2) => {
160574
160744
  const sourceValueId = bindings.rowForValue(id2)?.sourceValueId;
@@ -160808,9 +160978,6 @@ function computeStatus(workspace, options, conflictedFiles, parseErrors, parseWa
160808
160978
  changes.push(recordDeleteChange(recordState, recordFile));
160809
160979
  }
160810
160980
  appendCollectionEntryDeletes(workspace, reconstructed3, changes);
160811
- const sourceTextByUri = new Map(
160812
- sourceEntries.map((entry) => [entry.relPath, entry.source])
160813
- );
160814
160981
  const referenceFailures = drainValueReferenceObligationsV4(
160815
160982
  prospectiveState,
160816
160983
  manifest,
@@ -160833,6 +161000,10 @@ function computeStatus(workspace, options, conflictedFiles, parseErrors, parseWa
160833
161000
  }
160834
161001
  );
160835
161002
  for (const failure of referenceFailures) {
161003
+ if (failure instanceof SchemaSourceError) {
161004
+ parseErrors.push(failure);
161005
+ continue;
161006
+ }
160836
161007
  const message = failure.code === void 0 ? failure.message : `${failure.code}: ${failure.message}`;
160837
161008
  parseErrors.push(
160838
161009
  new SchemaSourceError(
@@ -160846,7 +161017,11 @@ function computeStatus(workspace, options, conflictedFiles, parseErrors, parseWa
160846
161017
  }
160847
161018
  reportPhase("record-diff-references", "Checking packed values\u2026");
160848
161019
  if (referenceFailures.length > 0) {
160849
- return refusedStatus(conflictedFiles, parseErrors, parseWarnings);
161020
+ return refusedStatus(
161021
+ conflictedFiles,
161022
+ uniqueSourceErrors(parseErrors),
161023
+ parseWarnings
161024
+ );
160850
161025
  }
160851
161026
  if (valueLowerRegistry.initializerKeyPatches.size > 0) {
160852
161027
  authoredValueSeeds = settleInitializerKeyCode({
@@ -167475,9 +167650,10 @@ function completionItemsForResolution(snapshot, resolved, word) {
167475
167650
  }
167476
167651
  const memberType2 = neoScriptMemberLookupType(resolved.type);
167477
167652
  const owner = memberType2.kind === "named" ? snapshot.project.typeById.get(memberType2.typeId) : void 0;
167478
- return membersForType(snapshot, resolved.type).filter(
167479
- (symbol) => (resolved.symbol?.name !== "base" || isConcreteBaseMember(symbol)) && (owner === void 0 || isMemberCompletionAccessible(snapshot, owner, symbol))
167480
- ).map((symbol) => symbolCompletion(symbol, word, snapshot));
167653
+ return membersForType(snapshot, resolved.type).filter((symbol) => {
167654
+ const declaration = symbol.inheritedFrom === void 0 ? owner : snapshot.project.typeById.get(symbol.inheritedFrom.typeId) ?? owner;
167655
+ return (resolved.symbol?.name !== "base" || isConcreteBaseMember(symbol)) && (declaration === void 0 || isMemberCompletionAccessible(snapshot, declaration, symbol));
167656
+ }).map((symbol) => symbolCompletion(symbol, word, snapshot));
167481
167657
  }
167482
167658
  function isConcreteBaseMember(symbol) {
167483
167659
  return !symbol.id.startsWith("builtin:") && symbol.static !== true && symbol.abstract !== true && (symbol.kind === "function" || symbol.kind === "method" || symbol.computed === true);
@@ -167537,6 +167713,22 @@ function membersForType(snapshot, type) {
167537
167713
  } : {}
167538
167714
  };
167539
167715
  });
167716
+ if (type.narrowedInterfaces) {
167717
+ const names = new Set(members.map((member) => member.name));
167718
+ for (const bound of Object.values(type.narrowedInterfaces)) {
167719
+ for (const member of membersForType(snapshot, bound)) {
167720
+ if (names.has(member.name)) continue;
167721
+ names.add(member.name);
167722
+ const owner = snapshot.project.typeById.get(bound.typeId);
167723
+ members.push(
167724
+ owner === void 0 || member.inheritedFrom !== void 0 ? member : {
167725
+ ...member,
167726
+ inheritedFrom: { typeId: owner.id, typeName: owner.name }
167727
+ }
167728
+ );
167729
+ }
167730
+ }
167731
+ }
167540
167732
  if (named.kind === "class") {
167541
167733
  members = members.filter(
167542
167734
  (member) => member.name !== NEOSCRIPT_CLASS_ID_MEMBER
@@ -215809,7 +216001,7 @@ var releaseWorkspaceLock = () => void 0;
215809
216001
  async function main() {
215810
216002
  const args = parseArgs(process.argv.slice(2));
215811
216003
  if (args.command === "--version") {
215812
- console.log("0.84.1");
216004
+ console.log("0.84.2");
215813
216005
  return;
215814
216006
  }
215815
216007
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
@@ -15965,6 +15965,22 @@ function neoScriptMemberLookupType(type) {
15965
15965
  }
15966
15966
  return nullable2 && current.nullable !== true ? { ...current, nullable: true } : current;
15967
15967
  }
15968
+ function neoScriptMemberReceiverType(type, memberName, project) {
15969
+ const primary = neoScriptMemberLookupType(type);
15970
+ if (primary.kind !== "named" || primary.narrowedInterfaces === void 0)
15971
+ return primary;
15972
+ const declaration = project.typeById.get(primary.typeId);
15973
+ if (declaration?.kind === "class" && (memberName === NEOSCRIPT_CLASS_ID_MEMBER || memberName === "Clone"))
15974
+ return primary;
15975
+ if (declaration?.members.some((member) => member.name === memberName))
15976
+ return primary;
15977
+ for (const bound of Object.values(primary.narrowedInterfaces)) {
15978
+ if (project.typeById.get(bound.typeId)?.members.some((member) => member.name === memberName)) {
15979
+ return { ...bound, nullable: primary.nullable === true };
15980
+ }
15981
+ }
15982
+ return primary;
15983
+ }
15968
15984
  function isNeoScriptHandleReferenceTarget(target, isValueType) {
15969
15985
  switch (target.kind) {
15970
15986
  case "list":
@@ -16288,6 +16304,20 @@ function isNeoScriptTypeAssignable(source, target, project) {
16288
16304
  if (isUnknownType(source) || isUnknownType(target)) return false;
16289
16305
  if (isNullType(source)) return isNullableType2(target);
16290
16306
  if (isNullableType2(source) && !isNullableType2(target)) return false;
16307
+ if (target.kind === "named" && target.narrowedInterfaces !== void 0) {
16308
+ for (const bound of Object.values(target.narrowedInterfaces)) {
16309
+ if (!isNeoScriptTypeAssignable(source, bound, project)) return false;
16310
+ }
16311
+ }
16312
+ if (source.kind === "named" && source.narrowedInterfaces !== void 0) {
16313
+ const direct = target.kind === "named" ? source.narrowedInterfaces[target.typeId] : void 0;
16314
+ if (direct !== void 0 && isNeoScriptTypeAssignable(direct, target, project))
16315
+ return true;
16316
+ for (const bound of Object.values(source.narrowedInterfaces)) {
16317
+ if (bound !== direct && isNeoScriptTypeAssignable(bound, target, project))
16318
+ return true;
16319
+ }
16320
+ }
16291
16321
  if (source.kind === "primitive" && target.kind === "primitive") {
16292
16322
  if (source.name === target.name) return true;
16293
16323
  return source.name === "int" && (target.name === "float" || target.name === "decimal");
@@ -16372,9 +16402,40 @@ function isNeoScriptTypeAssignable(source, target, project) {
16372
16402
  }
16373
16403
  return false;
16374
16404
  }
16405
+ function withoutNeoScriptInterfaceProofs(type) {
16406
+ if (type.kind !== "named" || type.narrowedInterfaces === void 0)
16407
+ return type;
16408
+ const { narrowedInterfaces: _proofs, ...declared } = type;
16409
+ return declared;
16410
+ }
16375
16411
  function commonNeoScriptAssignableType(left, right, project) {
16376
16412
  if (isNeoScriptTypeAssignable(left, right, project)) return right;
16377
16413
  if (isNeoScriptTypeAssignable(right, left, project)) return left;
16414
+ if (left.kind === "named" && right.kind === "named" && (left.narrowedInterfaces !== void 0 || right.narrowedInterfaces !== void 0)) {
16415
+ const declaredLeft = withoutNeoScriptInterfaceProofs(left);
16416
+ const declaredRight = withoutNeoScriptInterfaceProofs(right);
16417
+ const common = commonNeoScriptAssignableType(
16418
+ declaredLeft,
16419
+ declaredRight,
16420
+ project
16421
+ );
16422
+ if (common?.kind !== "named") return null;
16423
+ const proofs = {};
16424
+ for (const [source, other] of [
16425
+ [left, right],
16426
+ [right, left]
16427
+ ]) {
16428
+ for (const [id, proof] of Object.entries(
16429
+ source.narrowedInterfaces ?? {}
16430
+ )) {
16431
+ if (proofs[id] !== void 0) continue;
16432
+ const opposite = other.narrowedInterfaces?.[id] ?? other;
16433
+ if (isNeoScriptTypeAssignable(opposite, proof, project))
16434
+ proofs[id] = proof;
16435
+ }
16436
+ }
16437
+ return { ...common, narrowedInterfaces: proofs };
16438
+ }
16378
16439
  return null;
16379
16440
  }
16380
16441
  function isNeoVariantTypeId(typeId, project) {
@@ -35539,7 +35600,8 @@ var Parser = class _Parser {
35539
35600
  }
35540
35601
  if (isIdentifier(t) && t.text === NEOSCRIPT_CONSTRUCTOR_KEYWORD) {
35541
35602
  this.next();
35542
- const className = isIdentifier(this.peek()) ? this.next().text : null;
35603
+ const classToken = isIdentifier(this.peek()) ? this.next() : null;
35604
+ const className = classToken?.text ?? null;
35543
35605
  const typeArguments = [];
35544
35606
  if (this.eat("operator", "<")) {
35545
35607
  if (className === null) {
@@ -35573,6 +35635,7 @@ var Parser = class _Parser {
35573
35635
  return {
35574
35636
  kind: "new",
35575
35637
  className,
35638
+ ...classToken ? { classNamePos: at(classToken) } : {},
35576
35639
  ...typeArguments.length > 0 ? { typeArguments } : {},
35577
35640
  args,
35578
35641
  ...hasArgumentList ? { hasArgumentList: true } : {},
@@ -44500,7 +44563,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
44500
44563
  statement.init.pos
44501
44564
  );
44502
44565
  }
44503
- const localType = declared ?? initial.type;
44566
+ const localType = declared ?? withoutNeoScriptInterfaceProofs(initial.type);
44504
44567
  if (declared) {
44505
44568
  this.requireAssignable(
44506
44569
  initial.type,
@@ -44515,16 +44578,18 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
44515
44578
  initial.pointer,
44516
44579
  this.project
44517
44580
  );
44518
- scope.define(
44519
- scopeVariable(
44520
- statement.name,
44521
- local,
44522
- localType,
44523
- "local" /* Local */,
44524
- localOwnership(initial),
44525
- this.localWriteTargetRoot(initial, scope)
44526
- )
44581
+ const entry = scopeVariable(
44582
+ statement.name,
44583
+ local,
44584
+ localType,
44585
+ "local" /* Local */,
44586
+ localOwnership(initial),
44587
+ this.localWriteTargetRoot(initial, scope)
44527
44588
  );
44589
+ scope.define(entry);
44590
+ if (inferred && localType !== initial.type) {
44591
+ scope.narrow(statement.name, entry, initial.type);
44592
+ }
44528
44593
  return { type: "variable" /* Variable */, variable: local };
44529
44594
  }
44530
44595
  case "if": {
@@ -44579,7 +44644,8 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
44579
44644
  mergeBranchInvalidations(scope, branchExitOwnershipScopes);
44580
44645
  if (branchExitNarrowings.length > 0) {
44581
44646
  for (const [path, narrowed] of mergeNarrowingMaps(
44582
- branchExitNarrowings
44647
+ branchExitNarrowings,
44648
+ this.project
44583
44649
  )) {
44584
44650
  const rootEntry = scope.lookup(pathRoot(path));
44585
44651
  if (rootEntry === narrowed.rootEntry) {
@@ -45505,7 +45571,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
45505
45571
  }
45506
45572
  mergeLoopNarrowings(parent, exitPaths) {
45507
45573
  if (exitPaths.length === 0) return;
45508
- const merged = mergeNarrowingMaps(exitPaths);
45574
+ const merged = mergeNarrowingMaps(exitPaths, this.project);
45509
45575
  for (const [path, narrowed] of parent.effectiveNarrowedPaths()) {
45510
45576
  const common = merged.get(path);
45511
45577
  if (parent.containsEntry(narrowed.rootEntry) && (common?.rootEntry !== narrowed.rootEntry || !typeRefsEqual(common.type, narrowed.type))) {
@@ -47258,8 +47324,13 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
47258
47324
  expression.receiver,
47259
47325
  scope
47260
47326
  );
47261
- if (receiverExpression.type.kind !== "named") return null;
47262
- owner = this.project.typeById.get(receiverExpression.type.typeId);
47327
+ const memberType2 = neoScriptMemberReceiverType(
47328
+ receiverExpression.type,
47329
+ expression.name,
47330
+ this.project
47331
+ );
47332
+ if (memberType2.kind !== "named") return null;
47333
+ owner = this.project.typeById.get(memberType2.typeId);
47263
47334
  symbol = owner?.members.find(
47264
47335
  (candidate) => candidate.name === expression.name && (candidate.kind === "function" || candidate.kind === "method")
47265
47336
  );
@@ -47270,7 +47341,10 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
47270
47341
  expression.pos
47271
47342
  );
47272
47343
  }
47273
- receiver = { kind: "instance", expression: receiverExpression };
47344
+ receiver = {
47345
+ kind: "instance",
47346
+ expression: { ...receiverExpression, type: memberType2 }
47347
+ };
47274
47348
  }
47275
47349
  } else {
47276
47350
  return null;
@@ -47347,9 +47421,13 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
47347
47421
  if (symbol === void 0 || symbol.static !== true) return null;
47348
47422
  } else {
47349
47423
  const receiver2 = this.resolveExpression(expression.receiver, scope);
47350
- if (receiver2.type.kind !== "named") return null;
47351
- receiverType = receiver2.type;
47352
- owner = this.project.typeById.get(receiver2.type.typeId);
47424
+ receiverType = neoScriptMemberReceiverType(
47425
+ receiver2.type,
47426
+ expression.name,
47427
+ this.project
47428
+ );
47429
+ if (receiverType.kind !== "named") return null;
47430
+ owner = this.project.typeById.get(receiverType.typeId);
47353
47431
  symbol = owner?.members.find(
47354
47432
  (candidate) => candidate.name === expression.name && (candidate.kind === "function" || candidate.kind === "method")
47355
47433
  );
@@ -47427,6 +47505,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
47427
47505
  pos
47428
47506
  );
47429
47507
  }
47508
+ entryType = withoutNeoScriptInterfaceProofs(entryType);
47430
47509
  for (const item of resolved) {
47431
47510
  const common = commonNeoScriptAssignableType(
47432
47511
  entryType,
@@ -47451,7 +47530,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
47451
47530
  }
47452
47531
  const type = {
47453
47532
  kind: "list",
47454
- elementType: entryType
47533
+ elementType: withoutNeoScriptInterfaceProofs(entryType)
47455
47534
  };
47456
47535
  const wire = toWireType(type, this.project);
47457
47536
  if (wire.type !== 6 /* List */) {
@@ -47517,6 +47596,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
47517
47596
  if (!keyType || !valueType) {
47518
47597
  throw new CompileError("Dictionary literal type inference failed.", pos);
47519
47598
  }
47599
+ valueType = withoutNeoScriptInterfaceProofs(valueType);
47520
47600
  for (const entry of resolved) {
47521
47601
  if (expectedDictionary) {
47522
47602
  this.requireAssignable(
@@ -47543,7 +47623,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
47543
47623
  const type = {
47544
47624
  kind: "dictionary",
47545
47625
  keyType,
47546
- valueType
47626
+ valueType: withoutNeoScriptInterfaceProofs(valueType)
47547
47627
  };
47548
47628
  const wire = toWireType(type, this.project);
47549
47629
  if (wire.type !== 5 /* Dictionary */) {
@@ -47799,8 +47879,19 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
47799
47879
  const target = this.resolveType(fact.targetType);
47800
47880
  const targetDefinition = target.kind === "named" ? this.project.typeById.get(target.typeId) : void 0;
47801
47881
  const naturalDefinition = natural.type.kind === "named" ? this.project.typeById.get(natural.type.typeId) : void 0;
47802
- const retainConcreteClass = !fact.bindingName && naturalDefinition?.kind === "class" && targetDefinition?.kind === "interface";
47803
- const narrowed = retainConcreteClass ? { ...natural.type, nullable: false } : { ...target, nullable: false };
47882
+ let narrowed = { ...target, nullable: false };
47883
+ if (!fact.bindingName && natural.type.kind === "named" && (naturalDefinition?.kind === "class" || naturalDefinition?.kind === "interface") && target.kind === "named" && targetDefinition?.kind === "interface") {
47884
+ narrowed = { ...natural.type, nullable: false };
47885
+ if (!isNeoScriptTypeAssignable(narrowed, target, this.project)) {
47886
+ narrowed = {
47887
+ ...narrowed,
47888
+ narrowedInterfaces: {
47889
+ ...natural.type.narrowedInterfaces,
47890
+ [target.typeId]: target
47891
+ }
47892
+ };
47893
+ }
47894
+ }
47804
47895
  if (mode === "all" && fact.path !== null && rootEntry !== null && place.type.kind !== "reference") {
47805
47896
  targetScope.narrow(fact.path, rootEntry, narrowed);
47806
47897
  }
@@ -48393,7 +48484,11 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
48393
48484
  if (receiver.dialogues !== void 0) {
48394
48485
  return this.resolveDialogueGroupRead(receiver, name, pos);
48395
48486
  }
48396
- const memberReceiverType = neoScriptMemberLookupType(receiver.type);
48487
+ const memberReceiverType = neoScriptMemberReceiverType(
48488
+ receiver.type,
48489
+ name,
48490
+ this.project
48491
+ );
48397
48492
  if (memberReceiverType.kind === "named") {
48398
48493
  const type = this.project.typeById.get(memberReceiverType.typeId);
48399
48494
  if (!type) {
@@ -49205,7 +49300,7 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
49205
49300
  }
49206
49301
  const type = {
49207
49302
  kind: "list",
49208
- elementType: entryType
49303
+ elementType: withoutNeoScriptInterfaceProofs(entryType)
49209
49304
  };
49210
49305
  const wire = toWireType(type, this.project);
49211
49306
  if (wire.type !== 6 /* List */) {
@@ -49446,7 +49541,11 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
49446
49541
  pos
49447
49542
  );
49448
49543
  }
49449
- const memberReceiverType = neoScriptMemberLookupType(receiver.type);
49544
+ const memberReceiverType = neoScriptMemberReceiverType(
49545
+ receiver.type,
49546
+ callee.name,
49547
+ this.project
49548
+ );
49450
49549
  const namedType2 = memberReceiverType.kind === "named" ? this.project.typeById.get(memberReceiverType.typeId) : void 0;
49451
49550
  const member = namedType2?.members.find(
49452
49551
  (candidate) => (candidate.kind === "function" || candidate.kind === "method") && candidate.name === callee.name
@@ -51871,7 +51970,11 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
51871
51970
  }
51872
51971
  const receiver = receiverValue();
51873
51972
  if (receiver.dialogues !== void 0) return null;
51874
- const memberReceiverType = neoScriptMemberLookupType(receiver.type);
51973
+ const memberReceiverType = neoScriptMemberReceiverType(
51974
+ receiver.type,
51975
+ memberName,
51976
+ this.project
51977
+ );
51875
51978
  if (memberReceiverType.kind !== "named") return null;
51876
51979
  const type = this.project.typeById.get(memberReceiverType.typeId);
51877
51980
  const symbol = type?.members.find(
@@ -53032,17 +53135,22 @@ function mergeFactSets(sets) {
53032
53135
  )
53033
53136
  );
53034
53137
  }
53035
- function mergeNarrowingMaps(maps) {
53138
+ function mergeNarrowingMaps(maps, project) {
53036
53139
  const first = maps[0];
53037
53140
  if (!first) return /* @__PURE__ */ new Map();
53038
53141
  const result = /* @__PURE__ */ new Map();
53039
53142
  for (const [path, narrowed] of first) {
53040
- if (maps.slice(1).every((map) => {
53041
- const candidate = map.get(path);
53042
- return candidate?.rootEntry === narrowed.rootEntry && typeRefsEqual(candidate.type, narrowed.type);
53043
- })) {
53044
- result.set(path, narrowed);
53143
+ let common = narrowed.type;
53144
+ for (let index = 1; index < maps.length; index++) {
53145
+ const candidate = maps[index].get(path);
53146
+ if (candidate?.rootEntry !== narrowed.rootEntry) {
53147
+ common = null;
53148
+ break;
53149
+ }
53150
+ common = commonNeoScriptAssignableType(common, candidate.type, project);
53151
+ if (common === null) break;
53045
53152
  }
53153
+ if (common !== null) result.set(path, { ...narrowed, type: common });
53046
53154
  }
53047
53155
  return result;
53048
53156
  }
@@ -53071,6 +53179,16 @@ function typeRefsEqual(left, right) {
53071
53179
  if (left.kind === "named" && right.kind === "named") {
53072
53180
  const leftArgs = left.typeArguments ?? [];
53073
53181
  const rightArgs = right.typeArguments ?? [];
53182
+ if (left.narrowedInterfaces !== right.narrowedInterfaces) {
53183
+ const leftBounds = Object.entries(left.narrowedInterfaces ?? {});
53184
+ const rightBounds = right.narrowedInterfaces ?? {};
53185
+ if (leftBounds.length !== Object.keys(rightBounds).length) return false;
53186
+ for (const [id, leftBound] of leftBounds) {
53187
+ const rightBound = rightBounds[id];
53188
+ if (rightBound === void 0 || !typeRefsEqual(leftBound, rightBound))
53189
+ return false;
53190
+ }
53191
+ }
53074
53192
  return left.typeId === right.typeId && leftArgs.length === rightArgs.length && leftArgs.every((item, index) => {
53075
53193
  const other = rightArgs[index];
53076
53194
  return other !== void 0 && typeRefsEqual(item, other);
@@ -54050,6 +54168,10 @@ function typesOverlap(left, right, project) {
54050
54168
  if (isUnknown(left) || isUnknown(right)) return true;
54051
54169
  if (isNull(left)) return isNullable(right);
54052
54170
  if (isNull(right)) return isNullable(left);
54171
+ left = neoScriptMemberLookupType(left);
54172
+ right = neoScriptMemberLookupType(right);
54173
+ if (left.kind === "typeParameter" || right.kind === "typeParameter")
54174
+ return true;
54053
54175
  if (isNeoScriptTypeAssignable(left, right, project) || isNeoScriptTypeAssignable(right, left, project))
54054
54176
  return true;
54055
54177
  if (left.kind === "named" && right.kind === "named") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.84.1",
3
+ "version": "0.84.2",
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.84.1 -->
12
+ <!-- reviewed-through-cli: 0.84.2 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -129,7 +129,7 @@ wrappers.
129
129
  The marker near the top of `SKILL.md` must exactly match the package version:
130
130
 
131
131
  ```html
132
- <!-- reviewed-through-cli: 0.84.1 -->
132
+ <!-- reviewed-through-cli: 0.84.2 -->
133
133
  ```
134
134
 
135
135
  The quoted version above is checked too, so this instruction cannot go stale
@@ -24,6 +24,15 @@ Keep computed getters, setters, functions, dialogue logic, group logic, and
24
24
  constructors inline in their owning `.neo` or `.neoflow` declaration. Do not
25
25
  create `Scripts/<Class>/<Member>.neo` sidecars.
26
26
 
27
+ Explicit constructions such as `new MissingClass(5)` require a declared type,
28
+ including inside stored object initializers, constructor arguments, and collections.
29
+ The editor reports an unresolved class at its type name and refreshes the diagnostic
30
+ when the declaration changes. Contextual `new(...)` still uses the expected type.
31
+
32
+ If resolving a stored reference encounters an invalid initializer elsewhere,
33
+ the diagnostic points to that initializer's authored file and position. Fix the
34
+ reported initializer; a valid downstream `Reference(...)` is not the error site.
35
+
27
36
  Declare a setter's value type explicitly with the canonical form
28
37
  `set(int value) { ... }`. Project analysis, candidate materialization, and
29
38
  focused script commands all use the same accessor extraction. A property can have at most one getter and one setter. Concrete setters need a body (an empty body is a valid no-op); typed setters must use the property type and the parameter name `value`. Abstract and interface properties declare bodyless accessor contracts.
@@ -103,6 +112,18 @@ A generic receiver may call methods from its class or interface constraint,
103
112
  including void calls used as statements. Generic arguments substitute through
104
113
  the constraint; nullable receivers still require narrowing or null handling.
105
114
 
115
+ Runtime `is` tests on generic values check whether the bound can overlap the
116
+ tested type. A class bound need not implement an interface itself when a
117
+ subclass can implement it. A successful pattern binding exposes that interface's
118
+ members, including after `if (item.Data is not IReceiver receiver) continue;`.
119
+ Sealed classes without the interface and unrelated class bounds remain disjoint.
120
+
121
+ An interface guard also narrows the original receiver without a pattern binding.
122
+ After `if (item is not IReceiver) continue;`, `item` exposes both its declared
123
+ class members and `IReceiver` members. The same applies to guards that return,
124
+ throw, or break. Reassignment clears the proof, and paths that did not pass the
125
+ guard cannot use it.
126
+
106
127
  Functions, natives and interface functions can declare their own type
107
128
  parameters after the name, each with an optional constraint and default:
108
129