Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions tslang/lib/TypeScript/MLIRGenFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -468,8 +468,25 @@ namespace mlirgen
exitNamespace();

auto &passResult = genContextWithPassResult.passResult;

// Root cause of the chained-sibling-method-return-gap's third layer (see
// that memory): discovery always runs even when the function/method
// already has an EXPLICIT return-type annotation - "// seems we need to
// discover it all the time due to captured vars" above - so
// functionReturnTypeShouldBeProvided/functionReturnType still get set from
// trying to INFER a type from the body's return expression(s), even though
// that inferred value will never be used when an explicit one already
// exists. If a sibling method's prototype isn't registered yet (e.g.
// `return this.scale(f) + e;` inside an object literal, discovered as a
// side effect of discovering an unrelated OUTER function), the call can't
// produce a value during this dummy run, so functionReturnType comes back
// "none" - previously treated as "discovery failed to converge" even
// though the explicit annotation makes that inferred value moot.
auto hasExplicitReturnType = !!functionLikeDeclarationBaseAST->type;

if (passResult->functionReturnTypeShouldBeProvided
&& mth.isNoneType(passResult->functionReturnType))
&& mth.isNoneType(passResult->functionReturnType)
&& !hasExplicitReturnType)
{
// has return value but type is not provided yet
genContextWithPassResult.clean();
Expand All @@ -495,7 +512,10 @@ namespace mlirgen

funcProto->setDiscovered(true);
auto discoveredType = passResult->functionReturnType;
if (discoveredType && discoveredType != funcProto->getReturnType())
// a "none" discoveredType means inference didn't converge (see above) -
// never let that silently overwrite an already-known (explicit or
// previously-cached) return type.
if (discoveredType && !mth.isNoneType(discoveredType) && discoveredType != funcProto->getReturnType())
{
// TODO: do we need to convert it here? maybe send it as const object?

Expand Down
24 changes: 22 additions & 2 deletions tslang/lib/TypeScript/MLIRGenStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,28 @@ namespace mlirgen
// repeat if not all resolved
if (lastTimeNotResolved > 0 && lastTimeNotResolved == notResolved)
{
// class can depends on other class declarations
emitError(errorLocation, "can't resolve dependencies in namespace");
// Third layer of the chained-sibling-method-return discovery gap (see
// chained-sibling-method-return-gap memory): a statement in THIS list can
// fail to converge not because of a real dependency cycle, but because it
// contains an object literal whose method bodies are being speculatively
// walked (return-type/captured-var discovery for an OUTER function with no
// explicit return type - discoverFunctionReturnTypeAndCapturedVars) and a
// sibling method's prototype isn't registered yet. That nested discovery
// already fails silently (no diagnostic) under dummyRun/allowPartialResolve
// per the two gates in mlirGenPropertyAccessExpressionBaseLogic and
// discoverFunctionReturnTypeAndCapturedVars - but this loop's own "no
// progress across retries" diagnostic didn't check the same condition, so a
// legitimately-not-yet-resolvable statement here still hard-aborted the
// whole enclosing discovery with a real, user-facing error. Same idiom as
// those two gates: skip the diagnostic, still signal non-convergence via
// mlir::failure() so the real (non-dummy) compile pass gets a chance to
// resolve it once every sibling's prototype is registered.
if (!genContext.dummyRun && !genContext.allowPartialResolve)
{
// class can depends on other class declarations
emitError(errorLocation, "can't resolve dependencies in namespace");
}

return mlir::failure();
}
} while (notResolved > 0);
Expand Down
2 changes: 2 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ add_test(NAME test-compile-00-interface-function-typed-field COMMAND test-runner
add_test(NAME test-compile-00-interface-captures COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_captures.ts")
add_test(NAME test-compile-00-object-annotated-method COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method.ts")
add_test(NAME test-compile-00-object-annotated-method-params COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_params.ts")
add_test(NAME test-compile-00-object-chained-sibling-method-return COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_chained_sibling_method_return.ts")
add_test(NAME test-compile-00-object-annotated-method-interleaved COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_interleaved.ts")
add_test(NAME test-compile-00-object-annotated-method-extends-interface COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_extends_interface.ts")
add_test(NAME test-compile-00-object-annotated-method-extends-interface-multilevel COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_extends_interface_multilevel.ts")
Expand Down Expand Up @@ -654,6 +655,7 @@ add_test(NAME test-jit-00-interface-function-typed-field COMMAND test-runner -ji
add_test(NAME test-jit-00-interface-captures COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_captures.ts")
add_test(NAME test-jit-00-object-annotated-method COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method.ts")
add_test(NAME test-jit-00-object-annotated-method-params COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_params.ts")
add_test(NAME test-jit-00-object-chained-sibling-method-return COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_chained_sibling_method_return.ts")
add_test(NAME test-jit-00-object-annotated-method-interleaved COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_interleaved.ts")
add_test(NAME test-jit-00-object-annotated-method-extends-interface COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_extends_interface.ts")
add_test(NAME test-jit-00-object-annotated-method-extends-interface-multilevel COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_extends_interface_multilevel.ts")
Expand Down
26 changes: 26 additions & 0 deletions tslang/test/tester/tests/00object_chained_sibling_method_return.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Regression test for the chained-sibling-method-return-gap (see that memory
// for the full 3-layer mechanism). A method inside an object literal that
// `return`s (or otherwise uses) a sibling method's call result used to fail
// discovery of the ENCLOSING function's own return type/captured vars,
// because a sibling's prototype isn't registered into the literal's storage
// type until its own discovery completes - and a call to an
// not-yet-registered sibling can't produce a value during the speculative
// (dummyRun) discovery pass. Root cause: discovery always tries to INFER a
// return type from the body even when the method already has an EXPLICIT
// one, so a legitimately-unresolvable inferred value ("none") was wrongly
// treated as "discovery failed to converge".
function main() {
let calc = {
base: 10,
scale(factor: number): number {
return this.base * factor;
},
scaleAndAdd(factor: number, extra: number): number {
return this.scale(factor) + extra;
}
};

assert(calc.scaleAndAdd(2, 3) == 23);

print("done.");
}
Loading