Skip to content

Built-in .map/.filter return a generator (no default lib): a.map(f).map(g) fails with a misleading "the condition has no value" #442

Description

@ASDAlexander77

With --no-default-lib (which is how the test suite compiles), the result of the built-in .map/.filter can't be used as an array. Chaining them fails to compile under every memory model (gc, rc, none, own), and the error shown doesn't say why. With the default library it all works, and prints 3 5 7.

function main() {
    const a: number[] = [1, 2, 3];
    for (const v of a.map(x => x * 2).map(x => x + 1)) print(v);
}
tslang --emit=jit --no-default-lib chain.ts
chain.ts:1:1: error: the condition has no value
chain.ts:1:1: error: failed statement

a.filter(f).map(g) fails the same way, and so does splitting the chain into const m = a.map(f); for (const v of m.map(g)) ....

There are two problems here.

1. The built-in .map/.filter return a generator, not an array

mlirGenArrayMap and mlirGenArrayFilter (MLIRGenImpl.h) build function* .iter() { for (const v of .src_array) yield .func(v); }, call it, and return the generator object. In TypeScript, map and filter return an array. So anything except iterating the result fails:

const m = a.map(x => x * 2);
for (const v of m) print(v);   // ok
m.next();                      // ok
m.map(x => x + 1);             // error: Can't resolve property 'map' of type {.step:s32, .i:s32, .a:number[], ..., next(): {value:number, done:boolean}, .captured:...}
m.forEach(x => print(x));      // the same error, for 'forEach'
m.length; m[0];                // the same error, for 'length'

Possible fixes:

  • Collect the yielded values into a new array, which gives TypeScript's semantics.
  • Keep the generator but give it array-like methods.
  • At least report .map on a .map result as unsupported.

2. The real error is replaced by "the condition has no value"

Outside a condition, the message is accurate (Can't resolve property 'map' of type ..., with its position). In a condition, the only error is "the condition has no value":

const o = { x: 1 };
if (o.foo) print(1);                                   // 3:25: error: the condition has no value
for (const v of a.map(f).map(g)) print(v);             // 1:1:  error: the condition has no value

In the for...of case the position is 1:1, the start of the function, so nothing points at the line either.

A likely cause: mlirGenPropertyAccessExpression returns success with no value, and no message, during a dummyRun/allowPartialResolve run (MLIRGenAccessCall.cpp, around line 400). Then conditionHasValue (MLIRGenImpl.h) finds no value and reports its own generic message. The "Can't resolve property" message is never emitted, because the real run doesn't reach the access.

Found while testing -mm=own phase 7b (#441); the chained .map was removed from own_generator_borrow.ts because of this.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions