Closed chengluyu closed 10 months ago
Related PRs: #167, #186
I agree. I think the original approach (declare top-level let bindings, and unregister them if there's any error) is imperfect and hacky. So, this is actually a hacky fix to address the imperfectness of previous approach and will be soon removed by the help of PreTyper
.
I will try your example and add it to tests.
The test cases you suggested work.
We might notice a strange behavior of code generation: if we declare a symbol
x
and then define it, the generated JavaScript function name will have a numeric postfixx1
, which indicates that some otherx
was shadowed. But this is the very first time wherex
is defined in this file.Well, this is caused by an imperfect workaround for translating classes in new definition typing. Consider the following code.
Code generation lifts class definitions (e.g.,
Foo
) and translates them into a typing unit class before translating top-level let bindings (e.g.,f
). So, theScope
should contains some sort of stub symbols of top-level let bindings before translating classes. The current implementation does not handle this well, as it just declaresValueSymbol
in the first place.https://github.com/hkust-taco/mlscript/blob/231d9ec5106db29f45b4be86cae514d55ce07995/shared/src/main/scala/mlscript/JSBackend.scala#L1404-L1412
Then, after the translation of the typing unit class, when translating the top-level let bindings, the symbols will be declared twice and the runtime will be renewed, thus the runtime name comes with a numeric postfix. This sometimes cause runtime errors of undefined references. For example,
This PR fixes this problem by adding a Boolean field
forNewDefsDryRun
toValueSymbol
, which indicates if this symbol is declare in the situation as described above. If new symbols are shadowing this symbol, we will reuse the runtime name rather than allocating new names.