Skip to content

Commit c3e1b43

Browse files
authored
fix: for-of on class array filter/sort/slice results now correctly resolves element type (#402)
Co-authored-by: cs01 <cs01@users.noreply.github.com>
1 parent d28e8aa commit c3e1b43

4 files changed

Lines changed: 76 additions & 0 deletions

File tree

src/codegen/infrastructure/type-inference.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,6 +1735,33 @@ export class TypeInference {
17351735

17361736
getObjectArrayElementType(expr: Expression): string | null {
17371737
const e = expr as ExprBase;
1738+
if (e.type === "variable") {
1739+
const varName = (expr as VariableNode).name;
1740+
const rawType = this.st.getRawInterfaceType(varName);
1741+
if (rawType && this.getClass(rawType)) return rawType;
1742+
const resolved = this.resolveExpressionType(expr);
1743+
if (resolved && resolved.arrayDepth > 0 && this.getClass(resolved.base)) {
1744+
return resolved.base;
1745+
}
1746+
return null;
1747+
}
1748+
if (e.type === "array") {
1749+
const arrayExpr = expr as ArrayNode;
1750+
const elements = arrayExpr.elements || [];
1751+
if (elements.length > 0) {
1752+
const firstElem = elements[0] as ExprBase;
1753+
if (firstElem.type === "new") {
1754+
const newExpr = elements[0] as NewNode;
1755+
const resolvedClassName = this.resolveClassAlias(newExpr.className);
1756+
if (this.getClass(resolvedClassName)) return resolvedClassName;
1757+
}
1758+
const elemResolved = this.resolveExpressionType(elements[0]);
1759+
if (elemResolved && elemResolved.arrayDepth === 0 && this.getClass(elemResolved.base)) {
1760+
return elemResolved.base;
1761+
}
1762+
}
1763+
return null;
1764+
}
17381765
if (e.type === "binary") {
17391766
const binExpr = expr as BinaryNode;
17401767
if (binExpr.op === "||" || binExpr.op === "??") {
@@ -1744,6 +1771,11 @@ export class TypeInference {
17441771
if (e.type === "method_call") {
17451772
const methodExpr = expr as MethodCallNode;
17461773
const methodObjBase = methodExpr.object as ExprBase;
1774+
const m = methodExpr.method;
1775+
if (m === "filter" || m === "sort" || m === "reverse" || m === "slice") {
1776+
const sourceElemType = this.getObjectArrayElementType(methodExpr.object);
1777+
if (sourceElemType) return sourceElemType;
1778+
}
17471779
if (methodObjBase.type === "this") {
17481780
const className = this.ctx.getCurrentClassName();
17491781
if (className) {

src/codegen/llvm-generator.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2290,6 +2290,10 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
22902290
if (!elementType && resolvedBase && this.isKnownClass(resolvedBase)) {
22912291
elementType = resolvedBase;
22922292
}
2293+
if (!elementType && stmt.value) {
2294+
const objArrElemType = this.typeInference.getObjectArrayElementType(stmt.value);
2295+
if (objArrElemType) elementType = objArrElemType;
2296+
}
22932297
llvmType = "%ObjectArray*";
22942298
kind = SymbolKind.ObjectArray;
22952299
defaultValue = "null";

src/codegen/statements/control-flow.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,16 @@ export class ControlFlowGenerator {
10701070
}
10711071
if (iterBase.type === "method_call") {
10721072
const mcNode = iterable as MethodCallNode;
1073+
const mcMethod = mcNode.method;
1074+
if (
1075+
mcMethod === "filter" ||
1076+
mcMethod === "sort" ||
1077+
mcMethod === "reverse" ||
1078+
mcMethod === "slice"
1079+
) {
1080+
const sourceElemType = this.getIterableClassElementType(mcNode.object);
1081+
if (sourceElemType) return sourceElemType;
1082+
}
10731083
const objBase = mcNode.object as ExprBase;
10741084
if (objBase.type === "variable") {
10751085
const objName = (mcNode.object as VariableNode).name;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
class Task {
2+
name: string;
3+
done: boolean;
4+
constructor(n: string, d: boolean) {
5+
this.name = n;
6+
this.done = d;
7+
}
8+
}
9+
10+
const tasks = [new Task("a", true), new Task("b", false), new Task("c", true)];
11+
const done = tasks.filter((t: Task): boolean => t.done);
12+
13+
const names: string[] = [];
14+
for (const t of done) {
15+
names.push(t.name);
16+
}
17+
console.log(names.join(","));
18+
19+
const sorted = tasks.sort((a: Task, b: Task): number => {
20+
if (a.name < b.name) return -1;
21+
if (a.name > b.name) return 1;
22+
return 0;
23+
});
24+
const sortedNames: string[] = [];
25+
for (const t of sorted) {
26+
sortedNames.push(t.name);
27+
}
28+
console.log(sortedNames.join(","));
29+
30+
console.log("TEST_PASSED");

0 commit comments

Comments
 (0)