-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Expand symbolic funs correctly #9246
Conversation
If a 'fun f/n' used the same name and arity f/n as a BIF 'erlang:f/n', for example 'max/2', the symbolic fun would get unnecessarily rewritten as 'fun (X1, ..., Xn) -> f(X1, ..., Xn) end'. In addition, the rewritten fun body was then processed as a normal call, so if 'max/2' was declared as an import from some module m, it got expanded to a call to 'm:max/2', making imports "work" for symbolic funs but only if their names look like BIF names in order to sneak past the linter. This change makes the expander do the right thing for all imports, auto or explicit, creating symbolic remote funs. It is up to the linter (standard or modified) to decide whether it is allowed.
CT Test Results 4 files 448 suites 1h 25m 50s ⏱️ Results for commit e88ee26. ♻️ This comment has been updated with latest results. To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass. See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally. Artifacts// Erlang/OTP Github Action Bot |
If I update the primary bootstrap, the build fails:
(If I don't update the primary bootstrap, Dialyzer fails instead: https://github.com/erlang/otp/actions/runs/12495954151/job/34867261463?pr=9246) |
I see. It's the check I added:
Since funs have never been checked for this before (which should be considered a bug), I'm not quite sure what to do here. Making existing code not compile, like the erl_types module in dialyzer, is obviously not nice, but if it's a compiler bug the code shouldn't have been legal to begin with. I.e., erl_types defines and exports a local function Specifically, these things only become errors for functions |
We just had a long discussion and decided that they no longer serve any useful purpose. That is, there is no longer any need to check whether the BIF is old (and |
61b5873
to
9da3284
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test case error_SUITE:bif_clashes/1
for the compiler fails:
4:18: ambiguous call of overridden auto-imported BIF length/1 --
use erlang:length/1 or "-compile({no_auto_import,[length/1]})." to resolve name clash
length([a,b,c]).
^
Test bif_clashes1 failed. Expected
{error,[{{4,18},erl_lint,{call_to_redefined_old_bif,{length,1}}}],[]}
but got
{warning,[{{4,18},erl_lint,{call_to_redefined_bif,{length,1}}}]}
Otherwise it looks good to me.
Could you do some squashing of the commits to avoid problems if we'll need to do a |
The linting of 'fun f/n' relied on marking f/n as used and later getting a report if function f/n did not exist, except if f/n was not in the list of local functions (could still be local but auto generated) and was listed in auto-imports (erlang:f/n) and the import was not suppressed. This allowed explicit imports of names such as 'max/2' to slip through with a warning, assuming that they were auto-imports, and the expander would then expand them like an explicit import. This change avoids marking f/n as a used local if it is listed as an import, makes it an error to have a 'fun f/n' that refers to an explicit import, and furthermore checks the reference f/n just like a call f(X1, ... Xn) so that we get the same warnings for deprecated functions etc., for example 'fun now/0'.
This makes uses of local functions whose names conflict with auto-imported BIFs (without a no_auto_import declaration to resolve the conflict) always be a warning, never an error. Previously, this was an error for "old" BIfs (those that were auto imported pre OTP R14).
8527ccf
to
e88ee26
Compare
Squashed. |
Thanks for your pull request. |
If a
fun f/n
used the same name and arity f/n as a BIFerlang:f/n
, for examplemax/2
, the symbolic fun would get unnecessarily rewritten asfun (X1, ..., Xn) -> f(X1, ..., Xn) end
.In addition, the rewritten fun body was then processed as a normal call, so if
max/2
was declared as an import from some modulem
, it got expanded to a call tom:max/2
, making imports "work" for symbolic funs but only if their names look like BIF names in order to sneak past the linter (albeit with a warning).This change makes the expander do the right thing for all imports, auto or explicit, creating symbolic remote funs, and leaves to the linter to decide what is allowed. In the linter, it avoids marking f/n as a used local if it is listed as an import, instead reporting an error if you have a
fun f/n
that refers to an explicit import, and furthermore checks the referencef/n
just like a callf(X1, ... Xn)
so that we get the same warnings for deprecated functions etc., for example forfun now/0
.If desired, one could simply drop the check for imports in
fun f/n
, making the behaviour uniform whether the function is auto-imported or explicitly imported. That rule seems rather arbitrary.