@lcap/nasl-language-server-core 4.0.1-rc.0 → 4.0.1-rc.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.
Files changed (122) hide show
  1. package/README.md +85 -0
  2. package/out/checker.d.ts.map +1 -1
  3. package/out/checker.js +28 -13
  4. package/out/checker.js.map +1 -1
  5. package/out/index.d.ts +2 -1
  6. package/out/index.d.ts.map +1 -1
  7. package/out/index.js +2 -1
  8. package/out/index.js.map +1 -1
  9. package/out/query/context-variable-query.d.ts +31 -0
  10. package/out/query/context-variable-query.d.ts.map +1 -0
  11. package/out/query/context-variable-query.js +105 -0
  12. package/out/query/context-variable-query.js.map +1 -0
  13. package/out/query/index.d.ts +2 -0
  14. package/out/query/index.d.ts.map +1 -0
  15. package/out/query/index.js +18 -0
  16. package/out/query/index.js.map +1 -0
  17. package/out/reference-manager/builtin-q-name.d.ts +1 -1
  18. package/out/reference-manager/builtin-q-name.d.ts.map +1 -1
  19. package/out/reference-manager/builtin-q-name.js +2 -2
  20. package/out/reference-manager/builtin-q-name.js.map +1 -1
  21. package/out/reference-manager/collect-q-name.d.ts +2 -1
  22. package/out/reference-manager/collect-q-name.d.ts.map +1 -1
  23. package/out/reference-manager/collect-q-name.js +8 -4
  24. package/out/reference-manager/collect-q-name.js.map +1 -1
  25. package/out/reference-manager/get-q-name.d.ts +1 -1
  26. package/out/reference-manager/get-q-name.d.ts.map +1 -1
  27. package/out/reference-manager/get-q-name.js +35 -31
  28. package/out/reference-manager/get-q-name.js.map +1 -1
  29. package/out/reference-manager/reference-manager.d.ts +6 -7
  30. package/out/reference-manager/reference-manager.d.ts.map +1 -1
  31. package/out/reference-manager/reference-manager.js +7 -6
  32. package/out/reference-manager/reference-manager.js.map +1 -1
  33. package/out/reference-manager/remove-q-name.d.ts +30 -28
  34. package/out/reference-manager/remove-q-name.d.ts.map +1 -1
  35. package/out/reference-manager/remove-q-name.js +54 -47
  36. package/out/reference-manager/remove-q-name.js.map +1 -1
  37. package/out/reference-manager/rename-q-name.d.ts +2 -1
  38. package/out/reference-manager/rename-q-name.d.ts.map +1 -1
  39. package/out/reference-manager/rename-q-name.js +31 -36
  40. package/out/reference-manager/rename-q-name.js.map +1 -1
  41. package/out/reference-manager/symbol-type.d.ts +2 -2
  42. package/out/reference-manager/symbol-type.d.ts.map +1 -1
  43. package/out/reference-manager/symbol-type.js +1 -1
  44. package/out/reference-manager/symbol-type.js.map +1 -1
  45. package/out/reference-manager/update-nasl-fragment.d.ts +2 -1
  46. package/out/reference-manager/update-nasl-fragment.d.ts.map +1 -1
  47. package/out/reference-manager/update-nasl-fragment.js +9 -1
  48. package/out/reference-manager/update-nasl-fragment.js.map +1 -1
  49. package/out/typer/collectGlobalDefs.d.ts.map +1 -1
  50. package/out/typer/collectGlobalDefs.js.map +1 -1
  51. package/out/typer/component-def-manager/component-def-manager.d.ts +32 -5
  52. package/out/typer/component-def-manager/component-def-manager.d.ts.map +1 -1
  53. package/out/typer/component-def-manager/component-def-manager.js +96 -19
  54. package/out/typer/component-def-manager/component-def-manager.js.map +1 -1
  55. package/out/typer/component-def-manager/utils.d.ts.map +1 -1
  56. package/out/typer/component-def-manager/utils.js +39 -12
  57. package/out/typer/component-def-manager/utils.js.map +1 -1
  58. package/out/typer/dispatch-all.d.ts +1 -1
  59. package/out/typer/dispatch-all.d.ts.map +1 -1
  60. package/out/typer/dispatch-all.js +3 -7
  61. package/out/typer/dispatch-all.js.map +1 -1
  62. package/out/typer/dispatch-def.d.ts +1 -1
  63. package/out/typer/dispatch-def.d.ts.map +1 -1
  64. package/out/typer/dispatch-def.js +7 -5
  65. package/out/typer/dispatch-def.js.map +1 -1
  66. package/out/typer/dispatch-expr.d.ts +1 -1
  67. package/out/typer/dispatch-expr.d.ts.map +1 -1
  68. package/out/typer/dispatch-expr.js +28 -14
  69. package/out/typer/dispatch-expr.js.map +1 -1
  70. package/out/typer/dispatch-stmt.d.ts.map +1 -1
  71. package/out/typer/dispatch-stmt.js +10 -1
  72. package/out/typer/dispatch-stmt.js.map +1 -1
  73. package/out/typer/dispatch-view.d.ts.map +1 -1
  74. package/out/typer/dispatch-view.js +115 -115
  75. package/out/typer/dispatch-view.js.map +1 -1
  76. package/out/typer/helper.js +3 -3
  77. package/out/typer/helper.js.map +1 -1
  78. package/out/typer/incremental-update.d.ts +15 -13
  79. package/out/typer/incremental-update.d.ts.map +1 -1
  80. package/out/typer/incremental-update.js +89 -42
  81. package/out/typer/incremental-update.js.map +1 -1
  82. package/out/typer/nasl-predicate.d.ts +1 -1
  83. package/out/typer/nasl-predicate.d.ts.map +1 -1
  84. package/out/typer/solver.d.ts +37 -24
  85. package/out/typer/solver.d.ts.map +1 -1
  86. package/out/typer/solver.js +205 -426
  87. package/out/typer/solver.js.map +1 -1
  88. package/out/typer/subster.d.ts +2 -4
  89. package/out/typer/subster.d.ts.map +1 -1
  90. package/out/typer/subster.js +42 -34
  91. package/out/typer/subster.js.map +1 -1
  92. package/out/typer/topo-sort.d.ts.map +1 -1
  93. package/out/typer/topo-sort.js +9 -2
  94. package/out/typer/topo-sort.js.map +1 -1
  95. package/out/typer/type-hint-manager/type-hint-manager.d.ts +3 -0
  96. package/out/typer/type-hint-manager/type-hint-manager.d.ts.map +1 -1
  97. package/out/typer/type-hint-manager/type-hint-manager.js +2 -0
  98. package/out/typer/type-hint-manager/type-hint-manager.js.map +1 -1
  99. package/out/typer/type-manager.d.ts +6 -2
  100. package/out/typer/type-manager.d.ts.map +1 -1
  101. package/out/typer/type-manager.js.map +1 -1
  102. package/out/typer/type-predicate.d.ts.map +1 -1
  103. package/out/typer/type-predicate.js +61 -6
  104. package/out/typer/type-predicate.js.map +1 -1
  105. package/out/typer/typer.d.ts +10 -5
  106. package/out/typer/typer.d.ts.map +1 -1
  107. package/out/typer/typer.js +34 -60
  108. package/out/typer/typer.js.map +1 -1
  109. package/out/typer/unifier.d.ts +0 -3
  110. package/out/typer/unifier.d.ts.map +1 -1
  111. package/out/typer/unifier.js +58 -35
  112. package/out/typer/unifier.js.map +1 -1
  113. package/out/utils/nasl-type-manipulation.d.ts +1 -0
  114. package/out/utils/nasl-type-manipulation.d.ts.map +1 -1
  115. package/out/utils/nasl-type-manipulation.js +3 -1
  116. package/out/utils/nasl-type-manipulation.js.map +1 -1
  117. package/out/utils/parseTsClassType.d.ts.map +1 -1
  118. package/out/utils/parseTsClassType.js +7 -3
  119. package/out/utils/parseTsClassType.js.map +1 -1
  120. package/out/utils/type-operator.d.ts.map +1 -1
  121. package/out/utils/type-operator.js.map +1 -1
  122. package/package.json +13 -6
@@ -2,28 +2,29 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.printBounds = exports.Solver = void 0;
4
4
  const index_1 = require("./index");
5
- const types_1 = require("../utils/types");
5
+ const typer_1 = require("./typer");
6
+ const sem_diag_1 = require("./sem-diag");
6
7
  const helper_1 = require("./helper");
7
8
  const subster_1 = require("./subster");
8
9
  const type_predicate_1 = require("./type-predicate");
9
10
  const type_manager_1 = require("./type-manager");
10
11
  const type_predicate_2 = require("./type-predicate");
11
- const type_predicate_3 = require("./type-predicate");
12
12
  function collectCandidateBounds(bounds) {
13
13
  const allBounds = [...bounds.lb, ...bounds.ub];
14
14
  return allBounds.filter(x => {
15
15
  // @ts-expect-error FIXME
16
- return !x.ignored;
17
- });
16
+ return !x.bound.ignored;
17
+ }).map(x => x.bound);
18
18
  }
19
19
  class Solver {
20
20
  constructor(env) {
21
21
  // 没有子类型,除了数值外几乎都是 equal 就行,似乎不需要使用 SCC 这种找依赖链路的复杂求解了……
22
22
  this.simpleSolveConstraints = (cs0 = this.cs) => {
23
23
  const subst = new Map();
24
+ // topoSort 会做浅拷贝
24
25
  const cs = this.topoSortTyVars(cs0);
25
26
  let newInfo = false;
26
- // Stage 1: 首先从下界和上界寻找非 union 和非 any 的具体类型,优先处理数值类型
27
+ // Stage 1: 首先从下界和上界寻找非 any 的具体类型,优先处理数值类型
27
28
  const targetTV = "";
28
29
  const stop = targetTV
29
30
  ? (x) => {
@@ -36,41 +37,39 @@ class Solver {
36
37
  const str = (0, helper_1.showConstraints)(cs);
37
38
  console.log(str);
38
39
  }
39
- for (let [tyVar, rawBounds] of cs.entries()) {
40
+ for (const [tyVar, rawBounds] of cs.entries()) {
40
41
  const deref = (subst, bounds) => {
41
42
  const pSubst = (0, subster_1.inPlaceSubstTyAnn)(this.env, subst);
42
43
  for (let i = 0; i < bounds.lb.length; i++) {
43
- bounds.lb[i] = pSubst(bounds.lb[i]);
44
+ bounds.lb[i] = { bound: pSubst(bounds.lb[i].bound), boundary: bounds.lb[i].boundary, fromPlayground: bounds.lb[i].fromPlayground };
44
45
  }
45
46
  for (let i = 0; i < bounds.ub.length; i++) {
46
- bounds.ub[i] = pSubst(bounds.ub[i]);
47
+ bounds.ub[i] = { bound: pSubst(bounds.ub[i].bound), boundary: bounds.ub[i].boundary, fromPlayground: bounds.ub[i].fromPlayground };
47
48
  }
48
49
  return bounds;
49
50
  };
50
51
  if (stop(tyVar)) {
51
52
  printBounds(rawBounds);
52
53
  }
53
- function keepValidCondidateInBounds(bounds) {
54
+ function keepValidCandidateInBounds(bounds) {
54
55
  return new index_1.Bounds(bounds.lb.filter((x) => {
55
56
  // @ts-expect-error
56
- return !x.ignored;
57
+ return !x.bound.ignored;
57
58
  }), bounds.ub.filter((x) => {
58
59
  // @ts-expect-error
59
- return !x.ignored;
60
+ return !x.bound.ignored;
60
61
  }));
61
62
  }
62
- const validBounds = keepValidCondidateInBounds(rawBounds);
63
+ const validBounds = keepValidCandidateInBounds(rawBounds);
63
64
  const derefedBounds = deref(subst, validBounds);
64
65
  if (stop(tyVar)) {
65
66
  printBounds(derefedBounds);
66
67
  debugger;
67
68
  }
68
69
  // FIXME wudengke 这里hack了一下,需要justify。
69
- // 之所以对current变量直接使用下界和上界,是因为它上下界中那些预期被选中的候选因为包含有类型变元而被getConcreteTysFromBoundsPriority1排除
70
- const rawConcreteTys = tyVar.startsWith('Tp_current_of')
71
- ? [...derefedBounds.lb, ...derefedBounds.ub]
72
- : this.getConcreteTysFromBoundsPriority1(derefedBounds, subst) ?? [];
73
- const concreteTys = rawConcreteTys;
70
+ // 之所以对current变量直接使用下界和上界,是因为它上下界中那些预期被选中的候选因为包含有类型变元而被getConcreteTysFromBoundsPriority排除
71
+ // HACK: 下界优先选 union,上界优先选非 union
72
+ const concreteTys = this.getConcreteTys(derefedBounds, subst, tyVar.startsWith('Tp_current_of')) ?? [];
74
73
  concreteTys.forEach((concreteTy) => {
75
74
  const res = this.addToSubstAndCheck(tyVar, concreteTy, subst);
76
75
  if (stop(tyVar)) {
@@ -98,31 +97,6 @@ class Solver {
98
97
  newInfo = false;
99
98
  }
100
99
  } while (newInfo);
101
- // Stage 2: 从所有界寻找包括 union 和 any 的类型
102
- do {
103
- let oldSize = subst.size;
104
- for (let [tyVar, bounds] of cs.entries()) {
105
- const concreteTys = this.getConcreteTysFromBoundsPriority2(bounds, subst);
106
- concreteTys?.forEach(concreteTy => {
107
- const res = this.addToSubstAndCheck(tyVar, concreteTy, subst);
108
- if (stop(tyVar)) {
109
- const msg = res ? `stage 2, addToSubstAndCheck: 更新 ${(0, type_manager_1.showType)(concreteTy)}` : `stage 2, addToSubstAndCheck: 未更新 ${(0, type_manager_1.showType)(concreteTy)}`;
110
- this.env.unifier.trace(targetTV, msg);
111
- }
112
- });
113
- // @ts-expect-error
114
- if (concreteTys?.length || concreteTys?.size) {
115
- cs.delete(tyVar);
116
- }
117
- }
118
- if (subst.size > oldSize) {
119
- this.inPlaceSubstConstraints(subst, cs);
120
- newInfo = true;
121
- }
122
- else {
123
- newInfo = false;
124
- }
125
- } while (newInfo);
126
100
  // TODO FIXME: zirun 紧急fix:对 subst 再代换一次,解决大部分问题,但是速度受影响,尽早修复
127
101
  this.substSubst(subst);
128
102
  this.subst = subst;
@@ -141,7 +115,7 @@ class Solver {
141
115
  return true;
142
116
  }
143
117
  // old bound and new bound both cannot be type variables
144
- if ((0, type_predicate_3.isNullTy)(newBound)) {
118
+ if ((0, type_predicate_2.isNullTy)(newBound)) {
145
119
  return false;
146
120
  }
147
121
  if ((0, index_1.isAnyTy)(newBound)) {
@@ -154,11 +128,6 @@ class Solver {
154
128
  if ((0, type_predicate_1.isStructuralSubTy)(this.env, oldBound, newBound)) { // 用老的就行,不用报错。比如老的是 A 而新的是 A | B,其实也是兼容的
155
129
  return true;
156
130
  }
157
- // @ts-expect-error FIXME wudengke 确认这里是否要ignore
158
- if (!(0, type_predicate_2.isUnResolvedTy)(newBound) && !(0, type_predicate_2.isUnResolvedTy)(oldBound) && !newBound.ignored && !oldBound.ignored) {
159
- // 先屏蔽掉好了
160
- // this.env.addError(mkIncompatibleBounds(oldBound, newBound));
161
- }
162
131
  return false;
163
132
  };
164
133
  // in-place substitution
@@ -166,333 +135,176 @@ class Solver {
166
135
  const pSubst = (0, subster_1.inPlaceSubstTyAnn)(this.env, subst);
167
136
  for (let i = 0; i < constraints.length; i++) {
168
137
  const [tyVar, bounds] = constraints[i];
169
- const newLb = bounds.lb.map(pSubst);
170
- const newUb = bounds.ub.map(pSubst);
138
+ const newLb = bounds.lb.map(b => ({ bound: pSubst(b.bound), boundary: b.boundary, fromPlayground: b.fromPlayground }));
139
+ const newUb = bounds.ub.map(b => ({ bound: pSubst(b.bound), boundary: b.boundary, fromPlayground: b.fromPlayground }));
171
140
  const newBounds = new index_1.Bounds(newLb, newUb);
172
141
  constraints[i] = [tyVar, newBounds];
173
142
  }
174
143
  };
175
- this.inPlaceSubstConstraints = (subst, constraints) => {
144
+ this.inPlaceSubstConstraints = (subst, cs) => {
176
145
  const pSubst = (0, subster_1.inPlaceSubstTyAnn)(this.env, subst);
177
- for (let [tyVar, bounds] of constraints.entries()) {
146
+ for (const [tyVar, bounds] of cs.entries()) {
178
147
  for (let i = 0; i < bounds.lb.length; i++) {
179
- bounds.lb[i] = pSubst(bounds.lb[i]);
148
+ bounds.lb[i] = { bound: pSubst(bounds.lb[i].bound), boundary: bounds.lb[i].boundary, fromPlayground: bounds.lb[i].fromPlayground };
180
149
  }
181
150
  for (let i = 0; i < bounds.ub.length; i++) {
182
- bounds.ub[i] = pSubst(bounds.ub[i]);
151
+ bounds.ub[i] = { bound: pSubst(bounds.ub[i].bound), boundary: bounds.ub[i].boundary, fromPlayground: bounds.ub[i].fromPlayground };
183
152
  }
184
153
  }
185
154
  };
186
- // exclude type variables that are being resolved
187
- this.getUsefulTyFromBounds = (bounds, subst) => {
188
- let lbUsefulTys = new Array(); // 等到单例做好后再用 set
189
- let lbHasLong = false;
190
- let lbHasDecimal = false;
191
- let lbHasNum = false;
192
- bounds.lb.forEach(ty => {
193
- if (hasNaslTyParamAnn(ty, subst)) {
194
- return;
195
- }
196
- else if (lbUsefulTys.find(ty2 => (0, type_predicate_1.isSameRefTy)(ty, ty2))) {
197
- return;
198
- }
199
- if ((0, type_predicate_1.isSameRefTy)(ty, index_1.naslLongTy)) {
200
- lbHasLong = true;
201
- }
202
- if ((0, type_predicate_1.isSameRefTy)(ty, index_1.naslDecimalTy)) {
203
- lbHasDecimal = true;
204
- }
205
- if ((0, type_predicate_1.isSameRefTy)(ty, index_1.naslNumTy)) {
206
- lbHasNum = true;
207
- }
208
- lbUsefulTys.push(ty);
209
- });
210
- // danger
211
- if (lbHasDecimal) {
212
- return [index_1.naslDecimalTy];
213
- }
214
- else if (lbHasLong) {
215
- return [index_1.naslLongTy];
216
- }
217
- else if (lbUsefulTys.length === 1 && lbUsefulTys[0].typeName !== 'Number') {
218
- return Array.from(lbUsefulTys);
219
- }
220
- let ubUsefulTys = new Array();
221
- let ubHasLong = false;
222
- let ubHasDecimal = false;
223
- let ubHasNum = false;
224
- bounds.ub.forEach(ty => {
225
- if (hasNaslTyParamAnn(ty, subst)) {
226
- return;
227
- }
228
- else if (ubUsefulTys.find(ty2 => (0, type_predicate_1.isSameRefTy)(ty, ty2))) {
229
- return;
230
- }
231
- else if ((0, type_predicate_1.isSameRefTy)(ty, index_1.naslLongTy)) {
232
- ubHasLong = true;
233
- }
234
- else if ((0, type_predicate_1.isSameRefTy)(ty, index_1.naslDecimalTy)) {
235
- ubHasDecimal = true;
155
+ // Normalize constraints in place after substitution
156
+ this.normalizeConstraintsInPlace = (cs) => {
157
+ for (const [tyVar, bounds] of cs.entries()) {
158
+ for (let i = 0; i < bounds.lb.length; i++) {
159
+ bounds.lb[i] = { bound: (0, typer_1.normalize)(this.env, bounds.lb[i].bound), boundary: bounds.lb[i].boundary, fromPlayground: bounds.lb[i].fromPlayground };
236
160
  }
237
- else if ((0, type_predicate_1.isSameRefTy)(ty, index_1.naslNumTy)) {
238
- ubHasNum = true;
161
+ for (let i = 0; i < bounds.ub.length; i++) {
162
+ bounds.ub[i] = { bound: (0, typer_1.normalize)(this.env, bounds.ub[i].bound), boundary: bounds.ub[i].boundary, fromPlayground: bounds.ub[i].fromPlayground };
239
163
  }
240
- ubUsefulTys.push(ty);
241
- });
242
- // danger
243
- if (ubHasLong) {
244
- return [index_1.naslLongTy];
245
164
  }
246
- else if (ubHasDecimal) {
247
- return [index_1.naslDecimalTy];
165
+ };
166
+ /**
167
+ * 通用的类型获取策略函数,为不同类型的变量调度合适的策略
168
+ * @param bounds - 类型边界
169
+ * @param subst - 类型替换映射
170
+ * @param incTyVars - pick 时是否包含类型变元
171
+ */
172
+ this.getConcreteTys = (bounds, subst, incTyVars) => {
173
+ // 处理下界
174
+ const lowerBoundResult = this.processSingleBounds(bounds.lb, subst, incTyVars, true);
175
+ if (lowerBoundResult && lowerBoundResult.length > 0) {
176
+ return lowerBoundResult;
248
177
  }
249
- else if (ubUsefulTys.length === 1 && ubUsefulTys[0].typeName !== 'Number') {
250
- return Array.from(ubUsefulTys);
178
+ // 处理上界
179
+ const upperBoundResult = this.processSingleBounds(bounds.ub, subst, incTyVars, false);
180
+ if (upperBoundResult && upperBoundResult.length > 0) {
181
+ return upperBoundResult;
251
182
  }
252
- if (lbHasNum || ubHasNum) {
253
- return [index_1.naslLongTy];
183
+ // 特殊处理:若上界只有一个 any 约束且下界为空
184
+ if (bounds.ub.length === 1 && bounds.lb.length === 0 && bounds.ub[0].bound === index_1.naslAnyTy) {
185
+ return [bounds.ub[0].bound];
254
186
  }
255
- // console.log('ub usefulTys', ubUsefulTys.length);
256
- // ubUsefulTys.forEach(printType);
257
- // throw new Error('getUsefulTyFromBounds: unsolvable constraints');
258
187
  return undefined;
259
188
  };
260
- // 同时从下界和上界寻找非 union 和非 any 的具体类型,特别处理数值类型的情况
261
189
  /**
262
- *
263
- * 1. 下界含有 Decimal:选择 Decimal
264
- * 2. 下界只有 Number,上界含有 Decimal:选择 Decimal
265
- * 3. 下界只有 Number,上界没有 Decimal:选择 Long (2、3 合并:下界 Number 上界任意数值类型)
266
- * 4. 下界含有 Long 且上界有 Decimal:选择 Long
267
- * 5. 下界含有 Long 且上界没有 Decimal:选择 Long (1、2、4、5合并后:下界没有 Decimal,有 Long,而是上界任意数值类型)
268
- * 6. 下界和上界都只有 Number:选择 Long
190
+ * 处理特定边界(下界或上界)的类型:@warning decimal 和 long 的处理被 AI 破坏,现在是只处理单个 lb 或 ub,之后修复 Zirun
191
+ * 1. 下界含有 union:选择 union
192
+ * 2. 下界含有 Decimal:选择 Decimal
193
+ * 3. 下界只有 Number,上界含有 Decimal:选择 Decimal
194
+ * 4. 下界只有 Number,上界没有 Decimal:选择 Long (2、3 合并:下界 Number 上界任意数值类型)
195
+ * 5. 下界含有 Long 且上界有 Decimal:选择 Long
196
+ * 6. 下界含有 Long 且上界没有 Decimal:选择 Long (1、2、4、5合并后:下界没有 Decimal,有 Long,而是上界任意数值类型)
269
197
  * 7. 下界和上界都只有 Number:选择 Long
198
+ * 8. 下界和上界都只有 Number:选择 Long
270
199
  */
271
- this.getConcreteTysFromBoundsPriority1 = (bounds, subst) => {
272
- // 先快速检查所有数值类型
273
- let lbHasDecimal = false;
274
- let lbHasLong = false;
275
- let lbHasNumber = false;
276
- let ubHasDecimal = false;
277
- let ubHasLong = false;
278
- let ubHasNumber = false;
279
- // 快速扫描数值类型,减少多次遍历
280
- for (const ty of bounds.lb) {
281
- if (ty.typeName === 'Decimal') {
282
- lbHasDecimal = true;
283
- }
284
- else if (ty.typeName === 'Long') {
285
- lbHasLong = true;
200
+ this.processSingleBounds = (boundEntries, subst, incTyVars, preferUnion) => {
201
+ // 首先考虑 union 类型优先级
202
+ if (preferUnion) {
203
+ // 下界策略:优先选择 union 类型
204
+ const unionTypes = [];
205
+ for (const boundEntry of boundEntries) {
206
+ const ty = boundEntry.bound;
207
+ const shouldInclude = !(0, index_1.isAnyTy)(ty) && (incTyVars || !hasNaslTyParamAnn(ty, subst));
208
+ if (shouldInclude && (0, index_1.isUnionTy)(ty)) {
209
+ if (!unionTypes.includes(ty)) {
210
+ unionTypes.push(ty);
211
+ }
212
+ }
286
213
  }
287
- else if (ty.typeName === 'Number') {
288
- lbHasNumber = true;
214
+ // 如果找到 union 类型,优先返回
215
+ if (unionTypes.length > 0) {
216
+ return unionTypes;
289
217
  }
290
218
  }
291
- for (const ty of bounds.ub) {
219
+ // 如果没有优先 union 或没找到 union 类型,检查数值类型
220
+ let hasDecimal = false;
221
+ let hasLong = false;
222
+ let hasNumber = false;
223
+ for (const boundEntry of boundEntries) {
224
+ const ty = boundEntry.bound;
292
225
  if (ty.typeName === 'Decimal') {
293
- ubHasDecimal = true;
226
+ hasDecimal = true;
294
227
  }
295
228
  else if (ty.typeName === 'Long') {
296
- ubHasLong = true;
229
+ hasLong = true;
297
230
  }
298
231
  else if (ty.typeName === 'Number') {
299
- ubHasNumber = true;
232
+ hasNumber = true;
300
233
  }
301
234
  }
302
- // 快速处理数值类型的场景,按照规则选择正确的类型
303
- // 1. 下界含有 Decimal:选择 Decimal
304
- if (lbHasDecimal) {
235
+ // 数值类型优先级:Decimal > Long/Number
236
+ if (hasDecimal) {
305
237
  return [index_1.naslDecimalTy];
306
238
  }
307
- // 2. 下界只有 Number,上界含有 Decimal:选择 Decimal
308
- if (lbHasNumber && !lbHasLong && ubHasDecimal) {
309
- return [index_1.naslDecimalTy];
310
- }
311
- // 3. 下界只有 Number,上界没有 Decimal:选择 Long
312
- // 4. 下界含有 Long 且上界有 Decimal:选择 Long (类型冲突,Long更具体)
313
- // 5. 下界含有 Long 且上界没有 Decimal:选择 Long
314
- // 6. 下界和上界都只有 Number:选择 Long
315
- // 7. 下界和上界都只有 Number:选择 Long
316
- if (lbHasLong || lbHasNumber || ubHasNumber) {
239
+ if (hasLong || hasNumber) {
317
240
  return [index_1.naslLongTy];
318
241
  }
319
- // 如果没有数值类型,继续处理其他类型
320
- const lbRes = [];
321
- const ubRes = [];
322
- // 收集下界的非数值类型
323
- for (const ty of bounds.lb) {
324
- if (!lbRes.includes(ty) && !(0, index_1.isUnionTy)(ty) && !(0, index_1.isAnyTy)(ty) && !hasNaslTyParamAnn(ty, subst)) {
325
- lbRes.push(ty);
326
- }
327
- }
328
- // 如果下界有非数值的类型,优先使用
329
- if (lbRes.length > 0) {
330
- return lbRes;
331
- }
332
- // 收集上界的非数值类型
333
- for (const ty of bounds.ub) {
334
- if (!ubRes.includes(ty) && !(0, index_1.isUnionTy)(ty) && !(0, index_1.isAnyTy)(ty) && !hasNaslTyParamAnn(ty, subst)) {
335
- ubRes.push(ty);
242
+ // 处理其他非数值类型
243
+ const result = [];
244
+ for (const boundEntry of boundEntries) {
245
+ const ty = boundEntry.bound;
246
+ const shouldInclude = !(0, index_1.isAnyTy)(ty) && (incTyVars || !hasNaslTyParamAnn(ty, subst));
247
+ if (shouldInclude && !result.includes(ty)) {
248
+ result.push(ty);
336
249
  }
337
250
  }
338
- // 如果上界有非数值的类型,使用上界
339
- if (ubRes.length > 0) {
340
- return ubRes;
251
+ if (result.length > 0) {
252
+ return result;
341
253
  }
342
- /**
343
- * 若这个变元的整个上界就只有这个约束且为 any ,那么它必定选上。
344
- * 可以通过往一个变元的上界添加 naslAnyTy,而令下界留空来迫使它走这个分支。
345
- * 主要用在前端类型变元求解上,参考 {@link Unifier#initBoundsFor}
346
- */
347
- if (bounds.ub.length === 1 && bounds.lb.length === 0 && bounds.ub[0] === index_1.naslAnyTy) {
348
- return bounds.ub;
349
- }
350
- // 如果没有找到任何合适的类型
351
254
  return undefined;
352
255
  };
353
- // 从所有界寻找包括 union 和 any 的类型(合并了Lower和Upper的逻辑)
354
- this.getConcreteTysFromBoundsPriority2 = (bounds, subst) => {
355
- const res = new Set();
356
- // 首先检查下界(优先)
357
- for (const ty of bounds.lb) {
358
- if (!res.has(ty) && !hasNaslTyParamAnn(ty, subst)) {
359
- res.add(ty);
360
- }
361
- }
362
- // 然后检查上界
363
- for (const ty of bounds.ub) {
364
- if (!res.has(ty) && !hasNaslTyParamAnn(ty, subst)) {
365
- res.add(ty);
366
- }
367
- }
368
- if (res.size === 0) {
369
- return undefined;
370
- }
371
- else {
372
- return refineUnionAnyTy(res);
373
- }
374
- function refineUnionAnyTy(tys) {
375
- let hasAny = false;
376
- let hasUnion = false;
377
- // weak test
378
- tys.forEach(ty => {
379
- if (ty.typeName === 'any') {
380
- hasAny = true;
381
- }
382
- else if ((0, index_1.isUnionTy)(ty)) {
383
- hasUnion = true;
384
- }
385
- });
386
- const result = [];
387
- if (hasUnion) {
388
- if (tys.size > 1) {
389
- for (const ty of tys) {
390
- if ((0, index_1.isUnionTy)(ty)) {
391
- result.push(ty);
392
- }
393
- }
394
- return result;
395
- }
396
- else {
397
- return tys;
398
- }
399
- }
400
- else if (hasAny) {
401
- return [index_1.naslAnyTy];
402
- }
403
- else {
404
- return tys;
405
- }
406
- }
407
- ;
408
- };
409
- // strongly connected components
410
- this.findSCCs = (cs0) => {
411
- const depG = new Map();
412
- let index = 0;
413
- const indices = new Map();
414
- const lowLinks = new Map();
415
- const onStack = new Set();
416
- const stack = [];
417
- // Build the graph
418
- for (const [tyVar, bounds] of cs0) {
419
- depG.set(tyVar, new Set());
420
- for (const lb of bounds.lb.map(index_1.extractTyParamAnns)) {
421
- for (const dep of (lb.map(getTyName))) {
422
- if (dep !== tyVar && dep) {
423
- depG.get(tyVar).add(dep);
424
- }
425
- }
426
- }
427
- for (const ub of bounds.ub.map(index_1.extractTyParamAnns)) {
428
- for (const dep of (ub.map(getTyName))) {
429
- if (dep !== tyVar && dep) {
430
- depG.get(tyVar).add(dep);
431
- }
432
- }
433
- }
434
- }
435
- const res = [];
436
- // Tarjan's algorithm
437
- function strongConnect(v) {
438
- indices.set(v, index);
439
- lowLinks.set(v, index);
440
- index++;
441
- stack.push(v);
442
- onStack.add(v);
443
- if (depG.has(v)) {
444
- for (const w of depG.get(v)) {
445
- if (!indices.has(w)) {
446
- strongConnect(w);
447
- lowLinks.set(v, Math.min(lowLinks.get(v), lowLinks.get(w)));
448
- }
449
- else if (onStack.has(w)) {
450
- lowLinks.set(v, Math.min(lowLinks.get(v), indices.get(w)));
451
- }
452
- }
453
- }
454
- if (lowLinks.get(v) === indices.get(v)) {
455
- const comp = [];
456
- let w;
457
- do {
458
- w = stack.pop();
459
- onStack.delete(w);
460
- comp.push(w);
461
- } while (w !== v);
462
- res.push(comp);
463
- }
464
- }
465
- for (const v of cs0.keys()) {
466
- if (!indices.has(v)) {
467
- strongConnect(v);
468
- }
469
- }
470
- return res;
471
- };
472
256
  this.topoSortTyVars = (cs) => {
473
257
  const sortedCs = new Map();
474
258
  const visited = new Set();
475
259
  let index = 0;
476
260
  let indexMap = new Map();
477
- const addToMap = (tyVar, bounds) => {
261
+ // Phase 1: Use only lower bounds for dependency resolution
262
+ const addToMapPhase1 = (tyVar, bounds) => {
478
263
  if (visited.has(tyVar)) {
479
264
  return;
480
265
  }
481
266
  visited.add(tyVar);
267
+ // Only consider lower bounds in phase 1
268
+ const lbCandidates = bounds.lb.filter((x) => {
269
+ // @ts-expect-error
270
+ return !x.bound.ignored;
271
+ }).map(x => x.bound);
272
+ for (const ty of lbCandidates) {
273
+ const tyParams = (0, index_1.extractTyParamAnns)(ty);
274
+ for (const param of tyParams) {
275
+ const name = getTyName(param);
276
+ if (name && cs.has(name)) {
277
+ addToMapPhase1(name, cs.get(name));
278
+ }
279
+ }
280
+ }
281
+ indexMap.set(index++, tyVar);
282
+ };
283
+ // Phase 2: Use upper bounds for remaining unvisited type variables
284
+ const addToMapPhase2 = (tyVar, bounds) => {
285
+ if (visited.has(tyVar)) {
286
+ return;
287
+ }
288
+ visited.add(tyVar);
289
+ // Consider both lower and upper bounds in phase 2
482
290
  for (const ty of collectCandidateBounds(bounds)) {
483
291
  const tyParams = (0, index_1.extractTyParamAnns)(ty);
484
292
  for (const param of tyParams) {
485
293
  const name = getTyName(param);
486
294
  if (name && cs.has(name)) {
487
- addToMap(name, cs.get(name));
295
+ addToMapPhase2(name, cs.get(name));
488
296
  }
489
297
  }
490
298
  }
491
299
  indexMap.set(index++, tyVar);
492
300
  };
493
- // Add all constraints to map
301
+ // Phase 1: Process all constraints using only lower bounds
302
+ for (const [tyVar, bounds] of cs.entries()) {
303
+ addToMapPhase1(tyVar, bounds);
304
+ }
305
+ // Phase 2: Process any remaining unvisited constraints using all bounds
494
306
  for (const [tyVar, bounds] of cs.entries()) {
495
- addToMap(tyVar, bounds);
307
+ addToMapPhase2(tyVar, bounds);
496
308
  }
497
309
  for (let i = 0; i < cs.size; i++) {
498
310
  const name = indexMap.get(i);
@@ -500,124 +312,91 @@ class Solver {
500
312
  }
501
313
  return sortedCs;
502
314
  };
503
- // All the non-cyclic dependencies form the second kind of groups.
504
- // Sort each group topologically according to the type variables in the bounds.
505
- // For example, if A depends on B, B depends on C, and C depends on D, then the order should be D, C, B, A.
506
- this.topoSortConstraints = (cs) => {
507
- // const [tvDepDegree, cachedTC] = this.buildTyVarDepDegree(cs);
508
- const sccs = this.findSCCs(cs);
509
- // console.log('original cs')
510
- // printConstraints(cs);
511
- // 因为有环,SCC 和依赖度只能分两次算
512
- const depDegreeG = this.calcDepDegree(cs, sccs);
513
- console.log('depDegreeG');
514
- console.log(depDegreeG);
515
- // 环上的点,dep degree 不一定都相同
516
- //
517
- // A <--> B <-- C dep degree 为 A:0 B:0 C:1,先解环
518
- //
519
- // A <--> B --> C dep degree 为 A:0 B:1 C:0,先解非环 C
520
- //
521
- // A1, A2 <---> B1, B2 --> C dep degree 为 A1:0, A2:0, B1:1, B2:1, C:0, 先解 C
522
- this.reorderSCCs(depDegreeG, cs);
523
- console.log('reordered depDegreeG');
524
- console.log(depDegreeG);
525
- const res = new Array();
526
- for (const scc of depDegreeG) {
527
- for (const { tv, depDeg, tyCounts } of scc) {
528
- res.push([tv, cs.get(tv)]);
529
- }
315
+ // 紧急fix:对 subst 再代换一次,解决大部分问题
316
+ this.substSubst = (subst) => {
317
+ for (const [from, to] of subst.entries()) {
318
+ subst.set(from, (0, subster_1.inPlaceSubstTyAnn)(this.env, subst)(to));
530
319
  }
531
- return res;
532
- };
533
- this.reorderSCCs = (sccs, cs) => {
534
- sccs.forEach(scc => this.reorderSCC(scc, cs));
535
- sccs.sort((scc1, scc2) => {
536
- if (scc1.length === 1) {
537
- return -1; // 无依赖,可优先解决
538
- }
539
- else if (scc2.length === 1) {
540
- return 1;
541
- }
542
- else {
543
- return 0; // return scc1[0][1] - scc2[0][1];
544
- }
545
- }); // according to the first element
546
- return sccs;
547
320
  };
548
- // in-place reordering
549
- this.reorderSCC = (scc, cs) => {
550
- scc.sort((a, b) => {
551
- if (a.tyCounts > 0) {
552
- return -1;
553
- }
554
- else if (b.tyCounts > 0) {
555
- return 1;
556
- }
557
- else if (a.depDeg < b.depDeg) {
558
- return -1;
559
- }
560
- else if (a.depDeg > b.depDeg) {
561
- return 1;
562
- }
563
- else {
564
- return 0;
565
- // const bounds1 = cs.get(a.tv);
566
- // const tyVars1 = [...bounds1.lb.flatMap(extractTyParamAnns).map(getTyName),
567
- // ...bounds1.ub.flatMap(extractTyParamAnns).map(getTyName)];
568
- // const len1 = bounds1.lb.length + bounds1.ub.length - tyVars1.length;
569
- // const bounds2 = cs.get(b.tv);
570
- // const tyVars2 = [...bounds2.lb.flatMap(extractTyParamAnns).map(getTyName),
571
- // ...bounds2.ub.flatMap(extractTyParamAnns).map(getTyName)];
572
- // const len2 = bounds2.lb.length + bounds2.ub.length - tyVars2.length;
573
- // return len2 - len1;
321
+ /**
322
+ * Post-check if a solution (substitution) is valid by testing if it satisfies all bounds.
323
+ * For each type variable in the substitution:
324
+ * - The solution should be a supertype of all lower bounds (lb ⊆ solution)
325
+ * - The solution should be a subtype of all upper bounds (solution ⊆ ub)
326
+ *
327
+ * @param subst The substitution to validate (defaults to this.subst)
328
+ * @param cs The constraints to check against (defaults to this.cs)
329
+ * @returns Object with validation result and any violations found
330
+ */
331
+ // Post-check solution using consistent normalization approach as traversal
332
+ this.postCheckSolution = (subst, cs0, normalizedSemTyMap) => {
333
+ // cs 做浅拷贝,并使用与 traversal 一致的 normalizeWithSubst 方法
334
+ const cs = new Map(cs0);
335
+ // this.inPlaceSubstConstraintsWithNormalization(cs, pNormalizedSubst);
336
+ const violations = [];
337
+ const pSubst = (0, index_1.substTyAnn)(this.env, subst);
338
+ // Check each type variable that has a solution
339
+ for (const [tyVar, __actual] of subst.entries()) {
340
+ const actual = normalizedSemTyMap.get(__actual) ?? (0, subster_1.normalizeWithSubst)(this.env, pSubst, __actual);
341
+ const bounds = cs.get(tyVar);
342
+ if (!bounds) {
343
+ // No constraints for this type variable, skip
344
+ continue;
345
+ }
346
+ // Check lower bounds: solution should be a supertype of each lower bound
347
+ // i.e., for each lb: lb ⊆ solution (or isSubTy(lb, solution))
348
+ for (const lbEntry of bounds.lb) {
349
+ const __expect = lbEntry.bound;
350
+ const expect = normalizedSemTyMap.get(__expect) ?? (0, subster_1.normalizeWithSubst)(this.env, pSubst, __expect);
351
+ // Fixme: wudengke 这里之后需要看下为什么父亲 ViewElement 引入了 true 但是 bindAttr 引入了 false 导致报错
352
+ if (actual.typeKind === 'literal' && expect.typeKind === 'literal') {
353
+ continue;
354
+ }
355
+ if (!(0, index_1.isSubTy)(this.env, expect, actual)) {
356
+ violations.push({
357
+ expect,
358
+ actual,
359
+ errBoundary: lbEntry.boundary,
360
+ message: (0, sem_diag_1.mkIncompatibleTyErr)(this.env, expect, actual),
361
+ fromPlayground: lbEntry.fromPlayground ?? false
362
+ });
363
+ }
574
364
  }
575
- });
576
- return scc;
577
- };
578
- // dependency degrees regarding the SCC, and the count of non--type-variable bounds.
579
- this.calcDepDegree = (cs, scc) => {
580
- const depDegree = new Array();
581
- for (const comp of scc) {
582
- const subRes = new Array();
583
- for (const tyVar of comp) {
584
- const bounds = cs.get(tyVar);
585
- const tyVars = [...bounds.lb.flatMap(index_1.extractTyParamAnns).map(getTyName),
586
- ...bounds.ub.flatMap(index_1.extractTyParamAnns).map(getTyName)].filter(types_1.isNotNil);
587
- const len = new Set(tyVars.filter(tv => !comp.includes(tv))).size;
588
- subRes.push({ tv: tyVar,
589
- depDeg: len,
590
- tyCounts: bounds.lb.length + bounds.ub.length - tyVars.length });
365
+ // Check upper bounds: solution should be a subtype of each upper bound
366
+ // i.e., for each ub: solution ⊆ ub (or isSubTy(solution, ub))
367
+ for (const ubEntry of bounds.ub) {
368
+ const __expect = ubEntry.bound;
369
+ // HACK wudengke 这里额外处理了generic 和dataSource,动机都是通过type desctrutor添加反向约束。这里应当统一一下处理方式。
370
+ if (this.env.typeHintManager.isDataSourceTy(__expect)) {
371
+ continue;
372
+ }
373
+ const expect = normalizedSemTyMap.get(__expect) ?? (0, subster_1.normalizeWithSubst)(this.env, pSubst, __expect);
374
+ // Fixme: wudengke 这里之后需要看下为什么父亲 ViewElement 引入了 true 但是 bindAttr 引入了 false 导致报错
375
+ if (actual.typeKind === 'literal' && expect.typeKind === 'literal') {
376
+ continue;
377
+ }
378
+ if (!(0, index_1.isSubTy)(this.env, actual, expect)) {
379
+ violations.push({
380
+ expect,
381
+ actual,
382
+ errBoundary: ubEntry.boundary,
383
+ message: (0, sem_diag_1.mkIncompatibleTyErr)(this.env, actual, expect),
384
+ fromPlayground: ubEntry.fromPlayground ?? false
385
+ });
386
+ }
591
387
  }
592
- depDegree.push(subRes);
593
388
  }
594
- return depDegree;
389
+ return violations;
595
390
  };
596
- this.normaliseConstraints = (cs = this.cs) => {
597
- // remove duplicate bounds
598
- // remove self-referencing bounds
599
- // remove some numeric constraints 是无效的,因为代换后会有新的数值类型出现
600
- // const newCs = new Map();
601
- cs.forEach((bounds, tyVar) => {
602
- const lbHasLongOrDecimal = bounds.lb.find(ty => (0, type_predicate_1.isSameRefTy)(ty, index_1.naslLongTy)) ||
603
- bounds.lb.find(ty => (0, type_predicate_1.isSameRefTy)(ty, index_1.naslDecimalTy));
604
- // console.log('lb has long or decimal', lbHasLongOrDecimal);
605
- if (lbHasLongOrDecimal) {
606
- bounds.lb = bounds.lb.filter(ty => !(0, type_predicate_1.isSameRefTy)(ty, index_1.naslNumTy));
391
+ // Apply normalization to constraints using the same approach as traversal
392
+ this.inPlaceSubstConstraintsWithNormalization = (cs, pNormalizedSubst) => {
393
+ for (const [tyVar, bounds] of cs.entries()) {
394
+ for (let i = 0; i < bounds.lb.length; i++) {
395
+ bounds.lb[i] = { bound: (0, typer_1.normalize)(this.env, pNormalizedSubst(bounds.lb[i].bound)), boundary: bounds.lb[i].boundary, fromPlayground: bounds.lb[i].fromPlayground };
607
396
  }
608
- const ubHasLongOrDecimal = bounds.ub.find(ty => (0, type_predicate_1.isSameRefTy)(ty, index_1.naslLongTy)) ||
609
- bounds.ub.find(ty => (0, type_predicate_1.isSameRefTy)(ty, index_1.naslDecimalTy));
610
- // console.log('ub has long or decimal', ubHasLongOrDecimal);
611
- if (ubHasLongOrDecimal) {
612
- bounds.ub = bounds.ub.filter(ty => !(0, type_predicate_1.isSameRefTy)(ty, index_1.naslNumTy));
397
+ for (let i = 0; i < bounds.ub.length; i++) {
398
+ bounds.ub[i] = { bound: (0, typer_1.normalize)(this.env, pNormalizedSubst(bounds.ub[i].bound)), boundary: bounds.ub[i].boundary, fromPlayground: bounds.ub[i].fromPlayground };
613
399
  }
614
- });
615
- return cs;
616
- };
617
- // 紧急fix:对 subst 再代换一次,解决大部分问题
618
- this.substSubst = (subst) => {
619
- for (const [from, to] of subst.entries()) {
620
- subst.set(from, (0, subster_1.inPlaceSubstTyAnn)(this.env, subst)(to));
621
400
  }
622
401
  };
623
402
  // substTyEnv = (m: TyVarSolution, env: TypingEnv) => {
@@ -687,8 +466,8 @@ const hasNaslTyParamAnn = (ty, subst) => {
687
466
  return false;
688
467
  };
689
468
  function printBounds(b) {
690
- console.log("上界:\n", b.ub.map(type_manager_1.showType).join('\n '));
691
- console.log("下界:\n", b.lb.map(type_manager_1.showType).join('\n '));
469
+ console.log("上界:\n", b.ub.map(bound => (0, type_manager_1.showType)(bound.bound)).join('\n '));
470
+ console.log("下界:\n", b.lb.map(bound => (0, type_manager_1.showType)(bound.bound)).join('\n '));
692
471
  }
693
472
  exports.printBounds = printBounds;
694
473
  //# sourceMappingURL=solver.js.map