-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Gradualizer does not compile with OTP 22 #187
Comments
This is pretty serious. Our list of overriding specs is pretty long and it makes me wonder if it's a practical approach or if we should go for something simpler. How about treating Otherwise, I guess it's possible to come around the problem by importing the specs in some other way. In worst case, we can put them in strings inside the code (e.g. |
Another possibility is to move all that definitions upstream. |
@hauleth, we do plan to improve the typespecs in OTP. But we also want to be able to support a few older versions as well so we need a solution which works with the type specs in OTP right now. |
I suggest that you override the otp type specs by using a name convention.
Put them in the same place as now but use typenames and function names like
module_type and module_function and let gradualizer check if there is a
type or function named list_to_atom(lists:concat([Module,TypeOrFunc)) among
your overrides.
By using this approach you can still put the overrides in a normal module
and have it parsed and syntax checked.
/Kenneth
…On Fri, Sep 20, 2019 at 2:35 PM Josef Svenningsson ***@***.***> wrote:
@hauleth <https://github.com/hauleth>, we do plan to improve the
typespecs in OTP. But we also want to be able to support a few older
versions as well so we need a solution which works with the type specs in
OTP right now.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#187?email_source=notifications&email_token=AABFWSA6T4DZE7IZ433WPKDQKS7RFA5CNFSM4HPUJEE2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7GRRFQ#issuecomment-533534870>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AABFWSDND2RV3PXQBSHUPZDQKS7RFANCNFSM4HPUJEEQ>
.
|
That's a good idea @KennethL! I guess there are no underscores in the module names we're overriding, so it should be unambiguous. Otherwise, we can use two underscores instead. |
There are underscores in some modules so it is better to select a more
unique separator.
But I guess you are only creating the name and then checking if that type
exists so there might not be any problems anyway. You are not splitting
typenames into Module and Type? In that case it is better with double
underscore or some other unique separator.
/Kenneth
…On Fri, Sep 20, 2019, 15:37 Viktor Söderqvist ***@***.***> wrote:
I suggest that you override the otp type specs by using a name convention.
Put them in the same place as now but use typenames and function names like
module_type and module_function
That's a good idea @KennethL <https://github.com/KennethL>! I guess there
are no underscores in the module names we're overriding, so it should be
unambiguous. Otherwise, we can use two underscores instead.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#187?email_source=notifications&email_token=AABFWSH7EOLLQJY2CMM6ICTQKTGZFA5CNFSM4HPUJEE2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7GW6UY#issuecomment-533557075>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AABFWSB2ACYLMTZWSQEXVVTQKTGZFANCNFSM4HPUJEEQ>
.
|
When designing this, think about how it can be done without causing an overhead when it's not used. I think overrides should be placed in special files that only contain overrides. When those files are imported, gradualizer should make a translation from e.g. -spec 'proplists:delete'(any(), list()) -> list(). to -spec proplists:delete(any(), list()) -> list(). |
@KennethL Yes, we do. We represent them as
@Zalastax Yes, this is the way to go, I think, and only for the special module where we have these things. |
Hey there all, I cam across this issue when I tried to use Gradualizer for the first time. As I like living on the edge (using the latest and greatest), and I don't want to give up on Gradualizer even before I started, I'm thinking, how to solve this issue. Simple question, having a quick look at the code. You seem to be importing |
iirc the reason why it is a compiled beam and not any other text file is that this way it can be included and retrieved from a zipped escript file. (code server supports archives, while including a priv dir is utterly inconvenient - see #74) |
The same problem exists regardless if we import from erl or beam. The parser still complains about this. Now, if we do the @Zalastax trick, in order to avoid the compiler error "spec for undefined function", we need an implementation for each function. The warning "function ... is unused" can be silented by a compile attribute. -compile(nowarn_unused_function).
-spec 'erlang:apply'(function(), [any()]) -> any().
'erlang:apply'(_,_) -> error(undef). There are quite many of these specs. Can we auto-generate such implementations with parse transform? |
I was also thinking about parse transforms but on a different way. Can't we somehow do the bulk of the import at compile time. So after transform module would look like this:
Where The original file format could be:
Separating module from function name has the benefits of
|
@gomoripeti That is precisely what I was going to add. a precompile hook for rebar can generate a file that exports only that |
That's a great suggestion @gomoripeti ! Do you have any cycles to have a go at implementing this? |
Unfortunately I dont have too many spare time so I only implemented a minimal parse transform to unblock compilation on OTP 22 #191 I think separating module names into separate attributes would still be beneficial, but I have to leave it for others. |
I just made a PR (#192) based on my idea with the precompiled hook, but I like @gomoripeti's PR, if anything, it's shorter, and most likely even cleaner. |
Thanks both of you @NelsonVides and @gomoripeti for your PRs!
I tend to prefer it as well, it's a very simple solution that seems to be easy to maintain going forward. |
@zuiderkwast and @Zalastax, do you have opinions on which PR you prefer? |
Parse transform! |
Fair enough, I'm also for the parse transform, I just didn't know much of that possibility. Good chance to learn it anyway! |
There's one reason why a parse transform might not be as good as a Rebar3 pre-compile hook - Elixir deprecated using parse transforms in Erlang dependencies. If the focus is to provide type checking for the BEAM, not just Erlang, a standalone script is preferable, since it might as easily be run by Mix as by Rebar3. On the other hand, properly supporting Elixir would probably require a separate library wrapping Gradualizer anyway, so this might not be a biggie. Just thinking out loud. |
The parse transform is a really good solution! If we add Regarding Elixir, they want to remove Erlang parse transforms for Elixir code, I suppose, because they want to move away from Erlang AST and compile directly to Core, as José says. For Erlang code, that can't be the case. If Mix claims to be able to compile Erlang code, it will still handle Erlang parse transforms for Erlang code, since that is part of the Erlang language. Also, Gradualizer works with the Erlang AST so we will only be able handle Elixir code as long as it is compiled via the Erlang AST. |
|
Concerning the support for Elixir: I never set out to support Elixir when I started working on Gradualizer. The fact that it works now is a happy accident due to the fact that I chose to work on the absform format and Elixir compiles to that. |
We removed parse transforms because we skip the Erlang Linting to make compilation about 5% faster (as we lint everything already) but we would have to lint again if we allowed In any case, while we have discussed about moving to Core Erlang, we have no plans to do so exactly because we would lose things like Regarding how to run Gradualizer from Elixir, the simplest would probably be to have an API that receives a bunch of paths to BEAM files (or the BEAM binaries themselves) and you would analyze them returning all errors as tuples (so we can format those errors using Elixir syntax for terms and so on). That's how almost all dialyzer integrations work too. I hope this clarifies it a bit. |
Thanks for providing up-to-date info, @josevalim!
As luck would have it, we have that kind of API already. The problem with using Gradualizer with Elixir has more to do with the discrepancies between Elixir and Erlang. For instance, Gradualizer doesn't know anything about the Elixir string type. |
@josefs any type we have in Elixir is built on top of typespecs, so this part should just work. For example, we warn if someone uses |
@josevalim sounds good! See #73 for some of the efforts to get gradualizer to work with Elixir. |
Getting back on topic here, I've verified that gradualizer works with OTP 22. All tests pass for me. Good job, people! |
The compiler now complains about the preludes trick :(
this was expected to come, from OTP 22 release notes
The text was updated successfully, but these errors were encountered: