-
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
stdlib: create an init function for records with complex default values #9373
base: master
Are you sure you want to change the base?
stdlib: create an init function for records with complex default values #9373
Conversation
CT Test Results 2 files 97 suites 1h 8m 58s ⏱️ For more details on these failures, see this check. Results for commit 5e9f9b8. ♻️ 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 |
d544c98
to
e4b004f
Compare
St); | ||
|
||
IsUndefined = [{RF, AnnoRF, Field, {atom, AnnoRF, 'undefined'}} || {record_field=RF, AnnoRF, Field, _} <- Is], | ||
Fields = lists:flatten(lists:sort([atom_to_list(FieldAtom) || {record_field, _, {atom, _, FieldAtom}, _} <- Is])), |
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.
In general, prefer binaries over lists, and to sink expressions as far down as possible: the compiler has to maintain observability with regards to tracing, and cannot sink this expression down to the true
branch below as that would result in a different trace.
Now, as "free vars" are allowed in record fields, these free vars can affect each other. -module(z).
-export([mk1/0]).
-record(a, {
a = X = id(1),
b = X = id(2)
}).
id(X) -> X.
mk1() ->
#a{}.
|
Thanks, I'll get to it soon! |
records that have field default values containing variables that are "free" was unsafe in functions that have variables with the same name. This commit creates init function for records to protect the variables in the default value. e.g. -record(r, {f = fun(X)->case X of {y, Y} -> Y; _ -> X end, g=..., h=abc}). foo(X)->\#r{}. --> foo(X)->(r_init()){}. r_init() will only initialize fields that will not be updated e.g. foo(X)->\#r{f=X} --> foo(X)->(r_init_f()){f=X}. r_init_f will only initialize g and h with its default value, f will be initialized to undefined. r_init() functions will not be generated if all fields of the record that contains "free variables" are initialized by the user. e.g. foo(X)->\#r{f=X,g=X}. --> foo(X)->{r,X,X,abc}.
Add sequence number for init record functions
Actually, I spoke to soon. This is as intended. Just like how it works if you try to update the record like this: But I will update the linter so that it warns for this. |
update erl_lint_SUITE tests
4ed5f0c
to
fc096c6
Compare
Any chance of not including the new function in the error description? So, from the other comment… 1> z:mk1().
** exception error: no match of right hand side value 2
in function z:'rec_init$^0'/0 (z.erl, line 13) I would've preferred to see something like… 1> z:mk1().
** exception error: no match of right hand side value 2
in function z:mk1/0 (z.erl, line 13) |
Tail calls, like in this case, makes it invisible in the stacktrace. Would that work for you? |
Another option would be to explicitly prevent the compiler from tail calling into this helper function, and always emit a full call with building a stack frame |
traverse_af(AF, Fun) -> | ||
traverse_af(AF, Fun, []). | ||
traverse_af(AF, Fun, Acc) when is_list(AF) -> | ||
[ traverse_af(Ast, Fun, Fun(Ast,Acc)) || Ast <- AF]; |
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.
[ traverse_af(Ast, Fun, Fun(Ast,Acc)) || Ast <- AF]; | |
[traverse_af(Ast, Fun, Fun(Ast,Acc)) || Ast <- AF]; |
records that have field default values containing variables that are "free" was unsafe in functions that have variables with the same name. This commit creates init function for records to protect the variables in the default value.
e.g.
-record(r, {f = fun(X)->case X of {y, Y} -> Y; _ -> X end, g=..., h=abc}). foo(X)->#r{}. --> foo(X)->(r_init()){}.
r_init() will only initialize fields that will not be updated e.g.
foo(X)->#r{f=X} --> foo(X)->(r_init_f()){f=X}.
r_init_f will only initialize g and h with its default value, f will be initialized to undefined.
r_init() functions will not be generated if all fields of the record that contains "free variables" are initialized by the user.
e.g.
foo(X)->#r{f=X,g=X}. --> foo(X)->{r,X,X,abc}.
closes #9317