@thigasdevelopment/luam 0.8.2 → 0.9.0
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/luam.mjs +284 -15
- package/package.json +1 -1
package/luam.mjs
CHANGED
|
@@ -4774,6 +4774,9 @@ function createArray(element) {
|
|
|
4774
4774
|
function createNamed(name) {
|
|
4775
4775
|
return { kind: "named", name };
|
|
4776
4776
|
}
|
|
4777
|
+
function createMap(key, value) {
|
|
4778
|
+
return { kind: "map", key, value };
|
|
4779
|
+
}
|
|
4777
4780
|
function createStringLiteral(value) {
|
|
4778
4781
|
return { kind: "string-literal", value };
|
|
4779
4782
|
}
|
|
@@ -4850,6 +4853,9 @@ function typeToString(type) {
|
|
|
4850
4853
|
if (type.kind === "array") {
|
|
4851
4854
|
return `${typeToString(type.element)}[]`;
|
|
4852
4855
|
}
|
|
4856
|
+
if (type.kind === "map") {
|
|
4857
|
+
return `table<${typeToString(type.key)}, ${typeToString(type.value)}>`;
|
|
4858
|
+
}
|
|
4853
4859
|
if (type.kind === "optional") {
|
|
4854
4860
|
return `${typeToString(type.element)}?`;
|
|
4855
4861
|
}
|
|
@@ -4874,17 +4880,38 @@ function typeToString(type) {
|
|
|
4874
4880
|
}
|
|
4875
4881
|
return type.kind;
|
|
4876
4882
|
}
|
|
4883
|
+
function withoutNil(type) {
|
|
4884
|
+
if (type.kind === "optional") {
|
|
4885
|
+
return type.element;
|
|
4886
|
+
}
|
|
4887
|
+
if (type.kind === "union") {
|
|
4888
|
+
return createUnion(type.options.filter((option) => option.kind !== "nil"));
|
|
4889
|
+
}
|
|
4890
|
+
return type;
|
|
4891
|
+
}
|
|
4892
|
+
function acceptsNil(type) {
|
|
4893
|
+
if (type.kind === "nil" || type.kind === "optional" || type.kind === "any" || type.kind === "unknown") {
|
|
4894
|
+
return true;
|
|
4895
|
+
}
|
|
4896
|
+
return type.kind === "union" && type.options.some((option) => option.kind === "nil");
|
|
4897
|
+
}
|
|
4877
4898
|
function isTableLike(type) {
|
|
4878
|
-
return type.kind === "table" || type.kind === "array";
|
|
4899
|
+
return type.kind === "table" || type.kind === "array" || type.kind === "map";
|
|
4879
4900
|
}
|
|
4880
4901
|
function isBooleanType(type) {
|
|
4881
4902
|
const resolved2 = type?.kind === "optional" ? type.element : type;
|
|
4882
4903
|
return resolved2?.kind === "boolean";
|
|
4883
4904
|
}
|
|
4884
4905
|
function isNumeric(type) {
|
|
4906
|
+
if (type.kind === "union") {
|
|
4907
|
+
return type.options.every(isNumeric);
|
|
4908
|
+
}
|
|
4885
4909
|
return type.kind === "number" || type.kind === "any" || type.kind === "unknown" || type.kind === "named";
|
|
4886
4910
|
}
|
|
4887
4911
|
function isConcatenable(type) {
|
|
4912
|
+
if (type.kind === "union") {
|
|
4913
|
+
return type.options.every(isConcatenable);
|
|
4914
|
+
}
|
|
4888
4915
|
return type.kind === "string" || type.kind === "string-literal" || isNumeric(type);
|
|
4889
4916
|
}
|
|
4890
4917
|
function isFunctionAssignable(source, target, options) {
|
|
@@ -4901,6 +4928,18 @@ function isFunctionAssignable(source, target, options) {
|
|
|
4901
4928
|
}
|
|
4902
4929
|
return target.returnType.kind === "void" || isAssignable(source.returnType, target.returnType, options);
|
|
4903
4930
|
}
|
|
4931
|
+
function isMapAssignable(source, target, options) {
|
|
4932
|
+
if (source.kind === "table") {
|
|
4933
|
+
return true;
|
|
4934
|
+
}
|
|
4935
|
+
if (source.kind === "array") {
|
|
4936
|
+
return isAssignable(NUMBER_TYPE, target.key, options) && isAssignable(source.element, target.value, options);
|
|
4937
|
+
}
|
|
4938
|
+
if (source.kind === "record") {
|
|
4939
|
+
return isAssignable(STRING_TYPE, target.key, options) && [...source.members.values()].every((member) => isAssignable(member, target.value, options));
|
|
4940
|
+
}
|
|
4941
|
+
return source.kind === "map" && isAssignable(source.key, target.key, options) && isAssignable(source.value, target.value, options);
|
|
4942
|
+
}
|
|
4904
4943
|
function isRecordAssignable(source, target, options) {
|
|
4905
4944
|
for (const [name, member] of target.members) {
|
|
4906
4945
|
const value = source.members.get(name);
|
|
@@ -4953,6 +4992,9 @@ function isAssignable(source, target, options = { allowNil: false }) {
|
|
|
4953
4992
|
if (target.kind === "array") {
|
|
4954
4993
|
return source.kind === "table" || source.kind === "array" && isAssignable(source.element, target.element, options);
|
|
4955
4994
|
}
|
|
4995
|
+
if (target.kind === "map") {
|
|
4996
|
+
return isMapAssignable(source, target, options);
|
|
4997
|
+
}
|
|
4956
4998
|
if (source.kind === "function" && target.kind === "function") {
|
|
4957
4999
|
return isFunctionAssignable(source, target, options);
|
|
4958
5000
|
}
|
|
@@ -5012,8 +5054,8 @@ function stringValue(value) {
|
|
|
5012
5054
|
return { value, type, truthy: type, falsy: null };
|
|
5013
5055
|
}
|
|
5014
5056
|
function unionOf(options) {
|
|
5015
|
-
const
|
|
5016
|
-
return
|
|
5057
|
+
const present3 = options.filter((option) => option !== null);
|
|
5058
|
+
return present3.length === 0 ? NIL_TYPE : createUnion([...present3]);
|
|
5017
5059
|
}
|
|
5018
5060
|
|
|
5019
5061
|
// ../runtime/src/helpers.ts
|
|
@@ -5945,7 +5987,7 @@ import { tmpdir } from "node:os";
|
|
|
5945
5987
|
import { join as join2 } from "node:path";
|
|
5946
5988
|
|
|
5947
5989
|
// src/cli/version.ts
|
|
5948
|
-
var VERSION = true ? "0.
|
|
5990
|
+
var VERSION = true ? "0.9.0" : "0.0.0-dev";
|
|
5949
5991
|
var PROGRAM_NAME = "luam";
|
|
5950
5992
|
var PROGRAM_DESCRIPTION = "luam \u2014 the Luam compiler for Multi Theft Auto resources.";
|
|
5951
5993
|
|
|
@@ -10987,6 +11029,9 @@ function substituteType(type, substitutions) {
|
|
|
10987
11029
|
if (type.kind === "array") {
|
|
10988
11030
|
return createArray(substituteType(type.element, substitutions));
|
|
10989
11031
|
}
|
|
11032
|
+
if (type.kind === "map") {
|
|
11033
|
+
return createMap(substituteType(type.key, substitutions), substituteType(type.value, substitutions));
|
|
11034
|
+
}
|
|
10990
11035
|
if (type.kind === "optional") {
|
|
10991
11036
|
return createOptional(substituteType(type.element, substitutions));
|
|
10992
11037
|
}
|
|
@@ -11021,6 +11066,8 @@ var BUILTIN_TYPES = {
|
|
|
11021
11066
|
userdata: USERDATA_TYPE,
|
|
11022
11067
|
void: VOID_TYPE
|
|
11023
11068
|
};
|
|
11069
|
+
var TABLE_ANNOTATION = "table";
|
|
11070
|
+
var MAP_ARGUMENTS = 2;
|
|
11024
11071
|
var PROJECT_POSITION = { line: 0, column: 0, offset: 0 };
|
|
11025
11072
|
function nilHint(source, target, mode) {
|
|
11026
11073
|
if (mode !== "strict" || source.kind !== "nil" || target.kind === "nil" || target.kind === "optional") {
|
|
@@ -11032,6 +11079,8 @@ var CheckContext = class {
|
|
|
11032
11079
|
binder = new Binder();
|
|
11033
11080
|
declarations = new DeclarationRegistry();
|
|
11034
11081
|
diagnostics = [];
|
|
11082
|
+
reported = /* @__PURE__ */ new Set();
|
|
11083
|
+
narrowings = [];
|
|
11035
11084
|
types = /* @__PURE__ */ new Map();
|
|
11036
11085
|
references = /* @__PURE__ */ new Set();
|
|
11037
11086
|
declaredGlobals = /* @__PURE__ */ new Map();
|
|
@@ -11064,6 +11113,26 @@ var CheckContext = class {
|
|
|
11064
11113
|
this.binder.declareGlobal(symbol);
|
|
11065
11114
|
this.declaredGlobals.set(symbol.name, symbol.position);
|
|
11066
11115
|
}
|
|
11116
|
+
pushNarrowing(facts) {
|
|
11117
|
+
this.narrowings.push(new Map(facts));
|
|
11118
|
+
}
|
|
11119
|
+
popNarrowing() {
|
|
11120
|
+
this.narrowings.pop();
|
|
11121
|
+
}
|
|
11122
|
+
forgetNarrowing(name) {
|
|
11123
|
+
for (const frame of this.narrowings) {
|
|
11124
|
+
frame.delete(name);
|
|
11125
|
+
}
|
|
11126
|
+
}
|
|
11127
|
+
narrowedType(name) {
|
|
11128
|
+
for (let index = this.narrowings.length - 1; index >= 0; index -= 1) {
|
|
11129
|
+
const found = this.narrowings[index]?.get(name);
|
|
11130
|
+
if (found !== void 0) {
|
|
11131
|
+
return found;
|
|
11132
|
+
}
|
|
11133
|
+
}
|
|
11134
|
+
return null;
|
|
11135
|
+
}
|
|
11067
11136
|
isAmbientClass(name) {
|
|
11068
11137
|
return this.ambientClasses.has(name);
|
|
11069
11138
|
}
|
|
@@ -11079,6 +11148,11 @@ var CheckContext = class {
|
|
|
11079
11148
|
if (this.mode === "nocheck") {
|
|
11080
11149
|
return;
|
|
11081
11150
|
}
|
|
11151
|
+
const key = `${code}|${position2.offset}|${message}`;
|
|
11152
|
+
if (this.reported.has(key)) {
|
|
11153
|
+
return;
|
|
11154
|
+
}
|
|
11155
|
+
this.reported.add(key);
|
|
11082
11156
|
this.diagnostics.push(createDiagnostic("checker", code, message, position2));
|
|
11083
11157
|
}
|
|
11084
11158
|
record(expression, type) {
|
|
@@ -11194,9 +11268,24 @@ var CheckContext = class {
|
|
|
11194
11268
|
this.binder.declareGlobal({ name: info.name, type: info.type, isLocal: false, position: info.position });
|
|
11195
11269
|
}
|
|
11196
11270
|
}
|
|
11271
|
+
resolveMapAnnotation(typeArguments, position2) {
|
|
11272
|
+
const [key, value] = typeArguments;
|
|
11273
|
+
if (key === void 0 || value === void 0 || typeArguments.length !== MAP_ARGUMENTS) {
|
|
11274
|
+
const message = `Type "${TABLE_ANNOTATION}" expects a key type and a value type but received ${typeArguments.length}.`;
|
|
11275
|
+
this.report("check-generic-arity", message, position2);
|
|
11276
|
+
for (const argument of typeArguments) {
|
|
11277
|
+
this.resolveAnnotation(argument);
|
|
11278
|
+
}
|
|
11279
|
+
return TABLE_TYPE;
|
|
11280
|
+
}
|
|
11281
|
+
return createMap(this.resolveAnnotation(key), this.resolveAnnotation(value));
|
|
11282
|
+
}
|
|
11197
11283
|
resolveNamedAnnotation(name, typeArguments, position2) {
|
|
11198
11284
|
const builtin = BUILTIN_TYPES[name];
|
|
11199
11285
|
if (builtin !== void 0) {
|
|
11286
|
+
if (name === TABLE_ANNOTATION && typeArguments.length > 0) {
|
|
11287
|
+
return this.resolveMapAnnotation(typeArguments, position2);
|
|
11288
|
+
}
|
|
11200
11289
|
if (typeArguments.length > 0) {
|
|
11201
11290
|
this.report("check-generic-arity", `Type "${name}" does not accept type arguments.`, position2);
|
|
11202
11291
|
}
|
|
@@ -12154,8 +12243,11 @@ function reportInvalidOperand(context, operator, operand, expression) {
|
|
|
12154
12243
|
context.report("check-invalid-operand", `Operator "${operator}" cannot be applied to "${typeToString(operand)}".`, expression.position);
|
|
12155
12244
|
}
|
|
12156
12245
|
function checkBinary(context, operator, left, right, expression) {
|
|
12157
|
-
if (operator === "
|
|
12158
|
-
return context.record(expression, createUnion([left, right]));
|
|
12246
|
+
if (operator === "or") {
|
|
12247
|
+
return context.record(expression, createUnion([withoutNil(left), right]));
|
|
12248
|
+
}
|
|
12249
|
+
if (operator === "and") {
|
|
12250
|
+
return context.record(expression, acceptsNil(left) ? createUnion([right, NIL_TYPE]) : right);
|
|
12159
12251
|
}
|
|
12160
12252
|
if (operator === "==" || operator === "~=" || COMPARISON.has(operator)) {
|
|
12161
12253
|
return context.record(expression, BOOLEAN_TYPE);
|
|
@@ -12254,6 +12346,9 @@ function checkMember(context, expression) {
|
|
|
12254
12346
|
if (objectType.kind === "record") {
|
|
12255
12347
|
return context.record(expression, resolveRecordMember(context, objectType, expression));
|
|
12256
12348
|
}
|
|
12349
|
+
if (objectType.kind === "map" && isAssignable(STRING_TYPE, objectType.key)) {
|
|
12350
|
+
return context.record(expression, objectType.value);
|
|
12351
|
+
}
|
|
12257
12352
|
const named2 = objectType.kind === "named" ? resolveNamedMember(context, objectType.name, expression) : null;
|
|
12258
12353
|
if (named2 !== null) {
|
|
12259
12354
|
return context.record(expression, named2);
|
|
@@ -12374,13 +12469,17 @@ function checkMultiValueExpression(context, expression) {
|
|
|
12374
12469
|
if (symbol === null) {
|
|
12375
12470
|
checkGlobalReference(context, expression.name, expression.position);
|
|
12376
12471
|
}
|
|
12377
|
-
return context.record(expression, symbol?.type ?? ANY_TYPE);
|
|
12472
|
+
return context.record(expression, context.narrowedType(expression.name) ?? symbol?.type ?? ANY_TYPE);
|
|
12378
12473
|
}
|
|
12379
12474
|
case "member-expression":
|
|
12380
12475
|
return checkMember(context, expression);
|
|
12381
12476
|
case "index-expression": {
|
|
12382
12477
|
const objectType = checkExpression(context, expression.object);
|
|
12383
|
-
checkExpression(context, expression.index);
|
|
12478
|
+
const indexType = checkExpression(context, expression.index);
|
|
12479
|
+
if (objectType.kind === "map") {
|
|
12480
|
+
context.expectAssignable(indexType, objectType.key, expression.index.position, "Key");
|
|
12481
|
+
return context.record(expression, objectType.value);
|
|
12482
|
+
}
|
|
12384
12483
|
return context.record(expression, objectType.kind === "array" ? objectType.element : ANY_TYPE);
|
|
12385
12484
|
}
|
|
12386
12485
|
case "call-expression":
|
|
@@ -12603,20 +12702,158 @@ function checkEnumDeclaration(context, statement) {
|
|
|
12603
12702
|
context.declareModuleGlobal({ name: statement.name, type: createNamed(statement.name), isLocal: false, position: statement.position });
|
|
12604
12703
|
}
|
|
12605
12704
|
|
|
12705
|
+
// ../compiler/src/checker/narrowing.ts
|
|
12706
|
+
var GUARD_FUNCTION = "type";
|
|
12707
|
+
var GUARD_TYPES = {
|
|
12708
|
+
boolean: BOOLEAN_TYPE,
|
|
12709
|
+
function: createFunction([], ANY_TYPE, 0, true),
|
|
12710
|
+
nil: NIL_TYPE,
|
|
12711
|
+
number: NUMBER_TYPE,
|
|
12712
|
+
string: STRING_TYPE,
|
|
12713
|
+
table: TABLE_TYPE,
|
|
12714
|
+
thread: THREAD_TYPE,
|
|
12715
|
+
userdata: USERDATA_TYPE
|
|
12716
|
+
};
|
|
12717
|
+
function declaredType(context, name) {
|
|
12718
|
+
return context.binder.lookup(name)?.type ?? null;
|
|
12719
|
+
}
|
|
12720
|
+
function guardedName(expression) {
|
|
12721
|
+
if (expression.kind !== "call-expression" || expression.callee.kind !== "identifier" || expression.callee.name !== GUARD_FUNCTION) {
|
|
12722
|
+
return null;
|
|
12723
|
+
}
|
|
12724
|
+
const [argument] = expression.args;
|
|
12725
|
+
return argument !== void 0 && argument.kind === "identifier" ? argument.name : null;
|
|
12726
|
+
}
|
|
12727
|
+
function guardFact(left, right) {
|
|
12728
|
+
const name = guardedName(left);
|
|
12729
|
+
const value = right.kind === "string-literal" ? GUARD_TYPES[right.value] : void 0;
|
|
12730
|
+
return name === null || value === void 0 ? null : [name, value];
|
|
12731
|
+
}
|
|
12732
|
+
function nilComparison(left, right) {
|
|
12733
|
+
if (left.kind === "identifier" && right.kind === "nil-literal") {
|
|
12734
|
+
return left.name;
|
|
12735
|
+
}
|
|
12736
|
+
return right.kind === "identifier" && left.kind === "nil-literal" ? right.name : null;
|
|
12737
|
+
}
|
|
12738
|
+
function present2(context, name, facts) {
|
|
12739
|
+
const declared = declaredType(context, name);
|
|
12740
|
+
if (declared !== null) {
|
|
12741
|
+
facts.set(name, withoutNil(declared));
|
|
12742
|
+
}
|
|
12743
|
+
}
|
|
12744
|
+
function mergeAlternatives(left, right, facts) {
|
|
12745
|
+
for (const [name, type] of left) {
|
|
12746
|
+
const other = right.get(name);
|
|
12747
|
+
if (other !== void 0) {
|
|
12748
|
+
facts.set(name, createUnion([type, other]));
|
|
12749
|
+
}
|
|
12750
|
+
}
|
|
12751
|
+
}
|
|
12752
|
+
function collectFacts(context, expression, facts) {
|
|
12753
|
+
if (expression.kind === "group-expression") {
|
|
12754
|
+
collectFacts(context, expression.expression, facts);
|
|
12755
|
+
return;
|
|
12756
|
+
}
|
|
12757
|
+
if (expression.kind === "identifier") {
|
|
12758
|
+
present2(context, expression.name, facts);
|
|
12759
|
+
return;
|
|
12760
|
+
}
|
|
12761
|
+
if (expression.kind !== "binary-expression") {
|
|
12762
|
+
return;
|
|
12763
|
+
}
|
|
12764
|
+
if (expression.operator === "and") {
|
|
12765
|
+
collectFacts(context, expression.left, facts);
|
|
12766
|
+
collectFacts(context, expression.right, facts);
|
|
12767
|
+
return;
|
|
12768
|
+
}
|
|
12769
|
+
if (expression.operator === "or") {
|
|
12770
|
+
mergeAlternatives(conditionFacts(context, expression.left), conditionFacts(context, expression.right), facts);
|
|
12771
|
+
return;
|
|
12772
|
+
}
|
|
12773
|
+
if (expression.operator === "==") {
|
|
12774
|
+
const guard = guardFact(expression.left, expression.right) ?? guardFact(expression.right, expression.left);
|
|
12775
|
+
const missing = nilComparison(expression.left, expression.right);
|
|
12776
|
+
if (guard !== null) {
|
|
12777
|
+
facts.set(guard[0], guard[1]);
|
|
12778
|
+
} else if (missing !== null) {
|
|
12779
|
+
facts.set(missing, NIL_TYPE);
|
|
12780
|
+
}
|
|
12781
|
+
return;
|
|
12782
|
+
}
|
|
12783
|
+
if (expression.operator === "~=") {
|
|
12784
|
+
const name = nilComparison(expression.left, expression.right);
|
|
12785
|
+
if (name !== null) {
|
|
12786
|
+
present2(context, name, facts);
|
|
12787
|
+
}
|
|
12788
|
+
}
|
|
12789
|
+
}
|
|
12790
|
+
function conditionFacts(context, condition) {
|
|
12791
|
+
const facts = /* @__PURE__ */ new Map();
|
|
12792
|
+
collectFacts(context, condition, facts);
|
|
12793
|
+
return facts;
|
|
12794
|
+
}
|
|
12795
|
+
function alwaysExits(body) {
|
|
12796
|
+
const last = body[body.length - 1];
|
|
12797
|
+
if (last === void 0) {
|
|
12798
|
+
return false;
|
|
12799
|
+
}
|
|
12800
|
+
if (last.kind === "return-statement" || last.kind === "break-statement") {
|
|
12801
|
+
return true;
|
|
12802
|
+
}
|
|
12803
|
+
if (last.kind !== "if-statement" || last.alternate === null) {
|
|
12804
|
+
return false;
|
|
12805
|
+
}
|
|
12806
|
+
return last.clauses.every((clause) => alwaysExits(clause.body)) && alwaysExits(last.alternate);
|
|
12807
|
+
}
|
|
12808
|
+
function guardFacts(context, statement) {
|
|
12809
|
+
const [clause] = statement.kind === "if-statement" ? statement.clauses : [];
|
|
12810
|
+
if (statement.kind !== "if-statement" || statement.alternate !== null || statement.clauses.length !== 1 || clause === void 0) {
|
|
12811
|
+
return /* @__PURE__ */ new Map();
|
|
12812
|
+
}
|
|
12813
|
+
return alwaysExits(clause.body) ? negatedFacts(context, clause.condition) : /* @__PURE__ */ new Map();
|
|
12814
|
+
}
|
|
12815
|
+
function negatedFacts(context, condition) {
|
|
12816
|
+
const facts = /* @__PURE__ */ new Map();
|
|
12817
|
+
if (condition.kind === "binary-expression" && condition.operator === "or") {
|
|
12818
|
+
for (const [name, type] of [...negatedFacts(context, condition.left), ...negatedFacts(context, condition.right)]) {
|
|
12819
|
+
facts.set(name, type);
|
|
12820
|
+
}
|
|
12821
|
+
return facts;
|
|
12822
|
+
}
|
|
12823
|
+
if (condition.kind === "binary-expression" && condition.operator === "==") {
|
|
12824
|
+
const name = nilComparison(condition.left, condition.right);
|
|
12825
|
+
if (name !== null) {
|
|
12826
|
+
present2(context, name, facts);
|
|
12827
|
+
}
|
|
12828
|
+
}
|
|
12829
|
+
if (condition.kind === "unary-expression" && condition.operator === "not" && condition.operand.kind === "identifier") {
|
|
12830
|
+
present2(context, condition.operand.name, facts);
|
|
12831
|
+
}
|
|
12832
|
+
return facts;
|
|
12833
|
+
}
|
|
12834
|
+
|
|
12606
12835
|
// ../compiler/src/checker/control-flow.ts
|
|
12836
|
+
var ITERATOR_FUNCTIONS = /* @__PURE__ */ new Set(["pairs", "ipairs"]);
|
|
12607
12837
|
function checkBlock(context, body) {
|
|
12608
12838
|
context.binder.pushScope();
|
|
12609
12839
|
checkStatements(context, body);
|
|
12610
12840
|
context.binder.popScope();
|
|
12611
12841
|
}
|
|
12842
|
+
function checkNarrowedBlock(context, facts, body) {
|
|
12843
|
+
context.pushNarrowing(facts);
|
|
12844
|
+
checkBlock(context, body);
|
|
12845
|
+
context.popNarrowing();
|
|
12846
|
+
}
|
|
12612
12847
|
function checkIf(context, clauses, alternate) {
|
|
12613
12848
|
for (const clause of clauses) {
|
|
12614
12849
|
checkExpression(context, clause.condition);
|
|
12615
|
-
|
|
12850
|
+
checkNarrowedBlock(context, conditionFacts(context, clause.condition), clause.body);
|
|
12616
12851
|
}
|
|
12617
|
-
if (alternate
|
|
12618
|
-
|
|
12852
|
+
if (alternate === null) {
|
|
12853
|
+
return;
|
|
12619
12854
|
}
|
|
12855
|
+
const [only] = clauses;
|
|
12856
|
+
checkNarrowedBlock(context, clauses.length === 1 && only !== void 0 ? negatedFacts(context, only.condition) : /* @__PURE__ */ new Map(), alternate);
|
|
12620
12857
|
}
|
|
12621
12858
|
function checkNumericFor(context, statement) {
|
|
12622
12859
|
const bounds = [statement.start, statement.limit, statement.step].filter((value) => value !== null);
|
|
@@ -12631,13 +12868,30 @@ function checkNumericFor(context, statement) {
|
|
|
12631
12868
|
checkStatements(context, statement.body);
|
|
12632
12869
|
context.binder.popScope();
|
|
12633
12870
|
}
|
|
12871
|
+
function iteratedTypes(context, iterators) {
|
|
12872
|
+
const [iterator] = iterators;
|
|
12873
|
+
if (iterators.length !== 1 || iterator === void 0 || iterator.kind !== "call-expression" || iterator.callee.kind !== "identifier") {
|
|
12874
|
+
return [];
|
|
12875
|
+
}
|
|
12876
|
+
const [source] = iterator.args;
|
|
12877
|
+
if (!ITERATOR_FUNCTIONS.has(iterator.callee.name) || source === void 0) {
|
|
12878
|
+
return [];
|
|
12879
|
+
}
|
|
12880
|
+
const type = context.typeOf(source);
|
|
12881
|
+
if (type.kind === "map") {
|
|
12882
|
+
return iterator.callee.name === "ipairs" ? [NUMBER_TYPE, type.value] : [type.key, type.value];
|
|
12883
|
+
}
|
|
12884
|
+
return type.kind === "array" ? [NUMBER_TYPE, type.element] : [];
|
|
12885
|
+
}
|
|
12634
12886
|
function checkGenericFor(context, statement) {
|
|
12635
12887
|
statement.iterators.forEach((iterator) => checkExpression(context, iterator));
|
|
12888
|
+
const iterated = iteratedTypes(context, statement.iterators);
|
|
12636
12889
|
context.binder.pushScope();
|
|
12637
|
-
|
|
12638
|
-
const
|
|
12890
|
+
statement.variables.forEach((variable, index) => {
|
|
12891
|
+
const inferred = iterated[index] ?? ANY_TYPE;
|
|
12892
|
+
const declared = variable.annotation === null ? inferred : context.resolveAnnotation(variable.annotation);
|
|
12639
12893
|
context.binder.declare({ name: variable.name, type: declared, isLocal: true, position: variable.position });
|
|
12640
|
-
}
|
|
12894
|
+
});
|
|
12641
12895
|
checkStatements(context, statement.body);
|
|
12642
12896
|
context.binder.popScope();
|
|
12643
12897
|
}
|
|
@@ -12684,6 +12938,7 @@ function checkLocal(context, statement) {
|
|
|
12684
12938
|
statement.declarations.forEach((declaration, index) => {
|
|
12685
12939
|
const value = valueAt(statement.values, valueTypes, index);
|
|
12686
12940
|
const valueType = valueTypes[index] ?? NIL_TYPE;
|
|
12941
|
+
context.forgetNarrowing(declaration.name);
|
|
12687
12942
|
if (declaration.annotation === null) {
|
|
12688
12943
|
const inferred = context.allowNil || value === void 0 ? ANY_TYPE : widenLiteral(valueType);
|
|
12689
12944
|
context.binder.declare({ name: declaration.name, type: inferred, isLocal: true, position: declaration.position });
|
|
@@ -12724,8 +12979,12 @@ function checkAssignment(context, statement) {
|
|
|
12724
12979
|
context.declareModuleGlobal({ name: target.name, type: valueType, isLocal: false, position: target.position });
|
|
12725
12980
|
return;
|
|
12726
12981
|
}
|
|
12982
|
+
const declared = target.kind === "identifier" ? context.binder.lookup(target.name)?.type ?? targetType : targetType;
|
|
12727
12983
|
if (value !== void 0) {
|
|
12728
|
-
context.expectAssignable(valueType,
|
|
12984
|
+
context.expectAssignable(valueType, declared, value.position, "Assignment");
|
|
12985
|
+
}
|
|
12986
|
+
if (target.kind === "identifier") {
|
|
12987
|
+
context.forgetNarrowing(target.name);
|
|
12729
12988
|
}
|
|
12730
12989
|
});
|
|
12731
12990
|
}
|
|
@@ -12840,8 +13099,18 @@ function checkStatement(context, statement) {
|
|
|
12840
13099
|
}
|
|
12841
13100
|
}
|
|
12842
13101
|
function checkStatements(context, statements) {
|
|
13102
|
+
let guards = 0;
|
|
12843
13103
|
for (const statement of statements) {
|
|
12844
13104
|
checkStatement(context, statement);
|
|
13105
|
+
const facts = guardFacts(context, statement);
|
|
13106
|
+
if (facts.size > 0) {
|
|
13107
|
+
context.pushNarrowing(facts);
|
|
13108
|
+
guards += 1;
|
|
13109
|
+
}
|
|
13110
|
+
}
|
|
13111
|
+
while (guards > 0) {
|
|
13112
|
+
context.popNarrowing();
|
|
13113
|
+
guards -= 1;
|
|
12845
13114
|
}
|
|
12846
13115
|
}
|
|
12847
13116
|
|