diff options
author | Björn Gustavsson <[email protected]> | 2016-08-15 15:34:06 +0200 |
---|---|---|
committer | Björn Gustavsson <[email protected]> | 2016-09-02 14:24:36 +0200 |
commit | 0baa07cdf2754748bbc2d969bf83f08c0976fb78 (patch) | |
tree | b5d649dbe4778d8488ae09a99e68435adc216342 /lib/compiler | |
parent | 8a04dd4dd2d479efe488b0bed118e10559835fb6 (diff) | |
download | otp-0baa07cdf2754748bbc2d969bf83f08c0976fb78.tar.gz otp-0baa07cdf2754748bbc2d969bf83f08c0976fb78.tar.bz2 otp-0baa07cdf2754748bbc2d969bf83f08c0976fb78.zip |
Fix overridden BIFs
The filters in a list comprehension can be guard expressions or
an ordinary expressions.
If a guard expression is used as a filter, an exception will basically
mean the same as 'false':
t() ->
L = [{some_tag,42},an_atom],
[X || X <- L, element(1, X) =:= some_tag]
%% Returns [{some_tag,42}]
On the other hand, if an ordinary expression is used as a filter, there
will be an exception:
my_element(N, T) -> element(N, T).
t() ->
L = [{some_tag,42},an_atom],
[X || X <- L, my_element(1, X) =:= some_tag]
%% Causes a 'badarg' exception when element(1, an_atom) is evaluated
It has been allowed for several releases to override a BIF with
a local function. Thus, if we define a function called element/2,
it will be called instead of the BIF element/2 within the module.
We must use the "erlang:" prefix to call the BIF.
Therefore, the following code is expected to work the same way as in
our second example above:
-compile({no_auto_import,[element/2]}).
element(N, T) ->
erlang:element(N, T).
t() ->
L = [{some_tag,42},an_atom],
[X || X <- L, element(1, X) =:= some_tag].
%% Causes a 'badarg' exception when element(1, an_atom) is evaluated
But the compiler refuses to compile the code with the following
diagnostic:
call to local/imported function element/2 is illegal in guard
Diffstat (limited to 'lib/compiler')
-rw-r--r-- | lib/compiler/src/v3_core.erl | 17 | ||||
-rw-r--r-- | lib/compiler/test/Makefile | 3 | ||||
-rw-r--r-- | lib/compiler/test/overridden_bif_SUITE.erl | 101 | ||||
-rw-r--r-- | lib/compiler/test/test_lib.erl | 9 |
4 files changed, 125 insertions, 5 deletions
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 0f80eb68fa..b96d3df8fe 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -1640,10 +1640,19 @@ get_unit({bin_element,_,_,_,Flags}) -> U. %% is_guard_test(Expression) -> true | false. -%% Test if a general expression is a guard test. Use erl_lint here -%% as it now allows sys_pre_expand transformed source. - -is_guard_test(E) -> erl_lint:is_guard_test(E). +%% Test if a general expression is a guard test. +%% +%% Note that a local function overrides a BIF with the same name. +%% For example, if there is a local function named is_list/1, +%% any unqualified call to is_list/1 will be to the local function. +%% The guard function must be explicitly called as erlang:is_list/1. + +is_guard_test(E) -> + %% erl_expand_records has added a module prefix to any call + %% to a BIF or imported function. Any call without a module + %% prefix that remains must therefore be to a local function. + IsOverridden = fun({_,_}) -> true end, + erl_lint:is_guard_test(E, [], IsOverridden). %% novars(Expr, State) -> {Novars,[PreExpr],State}. %% Generate a novars expression, basically a call or a safe. At this diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile index 365f4c295e..de06e8760f 100644 --- a/lib/compiler/test/Makefile +++ b/lib/compiler/test/Makefile @@ -36,6 +36,7 @@ MODULES= \ map_SUITE \ match_SUITE \ misc_SUITE \ + overridden_bif_SUITE \ receive_SUITE \ record_SUITE \ regressions_SUITE \ @@ -66,6 +67,7 @@ NO_OPT= \ map \ match \ misc \ + overridden_bif \ receive \ record \ trycatch @@ -90,6 +92,7 @@ INLINE= \ map \ match \ misc \ + overridden_bif \ receive \ record diff --git a/lib/compiler/test/overridden_bif_SUITE.erl b/lib/compiler/test/overridden_bif_SUITE.erl new file mode 100644 index 0000000000..ce18916515 --- /dev/null +++ b/lib/compiler/test/overridden_bif_SUITE.erl @@ -0,0 +1,101 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(overridden_bif_SUITE). +-compile({no_auto_import,[is_reference/1,size/1]}). + +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2,end_per_testcase/2, + overridden_bif/1]). + +-include_lib("common_test/include/ct.hrl"). + +%% Used by overridden_bif/1. +-import(gb_sets, [size/1]). +-import(test_lib, [binary/1]). + +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. + +all() -> + test_lib:recompile(?MODULE), + [{group,p}]. + +groups() -> + [{p,test_lib:parallel(), + [overridden_bif + ]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> + Config. + +end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> + ok. + +overridden_bif(_Config) -> + L = [-3,-2,-1,0,1,2,3,4], + [-3,0,3] = do_overridden_bif_1(L), + [-2,0,2,4] = do_overridden_bif_2(L), + [3] = do_overridden_bif_3(L), + [2,4] = do_overridden_bif_4(L), + + Set = gb_sets:from_list(L), + [Set] = do_overridden_bif_5([gb_sets:singleton(42),Set]), + + [100,0] = do_overridden_bif_6([100|L]), + ok. + +do_overridden_bif_1(L) -> + [E || E <- L, is_reference(E)]. + +do_overridden_bif_2(L) -> + [E || E <- L, port(E)]. + +do_overridden_bif_3(L) -> + [E || E <- L, (is_reference(E) andalso E > 0)]. + +do_overridden_bif_4(L) -> + [E || E <- L, (port(E) andalso E > 0)]. + +do_overridden_bif_5(L) -> + [E || E <- L, size(E) > 1]. + +do_overridden_bif_6(L) -> + [E || E <- L, binary(E)]. + +is_reference(N) -> + N rem 3 =:= 0. + +port(N) -> + N rem 2 =:= 0. diff --git a/lib/compiler/test/test_lib.erl b/lib/compiler/test/test_lib.erl index d5b79e2357..8954a9f5fb 100644 --- a/lib/compiler/test/test_lib.erl +++ b/lib/compiler/test/test_lib.erl @@ -22,7 +22,10 @@ -include_lib("common_test/include/ct.hrl"). -compile({no_auto_import,[binary_part/2]}). -export([id/1,recompile/1,parallel/0,uniq/0,opt_opts/1,get_data_dir/1, - is_cloned_mod/1,smoke_disasm/1,p_run/2,binary_part/2]). + is_cloned_mod/1,smoke_disasm/1,p_run/2]). + +%% Used by test case that override BIFs. +-export([binary_part/2,binary/1]). id(I) -> I. @@ -151,3 +154,7 @@ p_run_loop(Test, List, N, Refs0, Errors0, Ws0) -> %% This is for the misc_SUITE:override_bif testcase binary_part(_A,_B) -> dummy. + +%% This is for overridden_bif_SUITE. +binary(N) -> + N rem 10 =:= 0. |