diff options
-rw-r--r-- | erts/emulator/test/match_spec_SUITE.erl | 10 | ||||
-rw-r--r-- | lib/compiler/src/Makefile | 2 | ||||
-rw-r--r-- | lib/compiler/src/compile.erl | 19 | ||||
-rw-r--r-- | lib/compiler/src/compiler.app.src | 1 | ||||
-rw-r--r-- | lib/compiler/src/sys_pre_expand.erl | 606 | ||||
-rw-r--r-- | lib/compiler/src/v3_core.erl | 191 | ||||
-rw-r--r-- | lib/compiler/test/compile_SUITE.erl | 9 | ||||
-rw-r--r-- | lib/compiler/test/compiler.cover | 2 | ||||
-rw-r--r-- | lib/debugger/src/dbg_iload.erl | 91 | ||||
-rw-r--r-- | lib/debugger/test/andor_SUITE.erl | 2 | ||||
-rw-r--r-- | lib/stdlib/doc/src/erl_expand_records.xml | 6 | ||||
-rw-r--r-- | lib/stdlib/doc/src/erl_internal.xml | 10 | ||||
-rw-r--r-- | lib/stdlib/src/erl_expand_records.erl | 84 | ||||
-rw-r--r-- | lib/stdlib/src/erl_internal.erl | 67 | ||||
-rw-r--r-- | lib/stdlib/src/erl_parse.yrl | 13 | ||||
-rw-r--r-- | lib/stdlib/src/qlc_pt.erl | 9 | ||||
-rw-r--r-- | lib/stdlib/test/erl_pp_SUITE.erl | 20 | ||||
-rw-r--r-- | lib/stdlib/test/shell_SUITE.erl | 7 | ||||
-rw-r--r-- | lib/tools/src/cover.erl | 2 | ||||
-rw-r--r-- | lib/tools/src/xref_base.erl | 3 | ||||
-rw-r--r-- | lib/tools/src/xref_reader.erl | 55 | ||||
-rw-r--r-- | lib/tools/test/xref_SUITE.erl | 3 |
22 files changed, 443 insertions, 769 deletions
diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index 6733237b20..34e956bc21 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -427,13 +427,13 @@ silent_no_ms(Config) when is_list(Config) -> %% [{trace,Tracee,call,{?MODULE,f1,[start]}}, {trace,Tracee,return_to, - {?MODULE,'-silent_no_ms/1-fun-2-',0}}, + {?MODULE,'-silent_no_ms/1-fun-3-',0}}, {trace,Tracee,call,{?MODULE,f2,[f,g]}}, {trace,Tracee,return_to, - {?MODULE,'-silent_no_ms/1-fun-2-',0}}, + {?MODULE,'-silent_no_ms/1-fun-3-',0}}, {trace,Tracee,call,{erlang,integer_to_list,[2]}}, {trace,Tracee,return_to, - {?MODULE,'-silent_no_ms/1-fun-2-',0}}, + {?MODULE,'-silent_no_ms/1-fun-3-',0}}, {trace,Tracee,call,{?MODULE,f2,[h,i]}}, {trace,Tracee,return_to,{?MODULE,f3,2}}] end). @@ -484,7 +484,7 @@ ms_trace2(Config) when is_list(Config) -> %% %% Expected: (no return_to for global call trace) %% - Origin = {match_spec_SUITE,'-ms_trace2/1-fun-0-',1}, + Origin = {match_spec_SUITE,'-ms_trace2/1-fun-1-',1}, [{trace_ts,Tracee,call, {?MODULE,fn, [[all],[call,return_to,{tracer,Tracer}]]}, @@ -574,7 +574,7 @@ ms_trace3(Config) when is_list(Config) -> %% %% Expected: (no return_to for global call trace) %% - Origin = {match_spec_SUITE,'-ms_trace3/1-fun-1-',2}, + Origin = {match_spec_SUITE,'-ms_trace3/1-fun-2-',2}, [{trace_ts,Controller,call, {?MODULE,fn,[TraceeName,[all], [call,return_to,send,'receive', diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile index 518c89d044..b5ca6c3c49 100644 --- a/lib/compiler/src/Makefile +++ b/lib/compiler/src/Makefile @@ -88,7 +88,6 @@ MODULES = \ sys_core_fold_lists \ sys_core_inline \ sys_pre_attributes \ - sys_pre_expand \ v3_codegen \ v3_core \ v3_kernel \ @@ -198,7 +197,6 @@ $(EBIN)/sys_core_dsetel.beam: core_parse.hrl $(EBIN)/sys_core_fold.beam: core_parse.hrl $(EBIN)/sys_core_fold_lists.beam: core_parse.hrl $(EBIN)/sys_core_inline.beam: core_parse.hrl -$(EBIN)/sys_pre_expand.beam: ../../stdlib/include/erl_bits.hrl $(EBIN)/v3_codegen.beam: v3_life.hrl $(EBIN)/v3_core.beam: core_parse.hrl $(EBIN)/v3_kernel.beam: core_parse.hrl v3_kernel.hrl diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index e951a25e04..16621d9b43 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -646,13 +646,13 @@ standard_passes() -> {iff,'dabstr',{listing,"abstr"}}, {iff,debug_info,?pass(save_abstract_code)}, - ?pass(expand_module), + ?pass(expand_records), {iff,'dexp',{listing,"expand"}}, {iff,'E',{src_listing,"E"}}, {iff,'to_exp',{done,"E"}}, %% Conversion to Core Erlang. - {pass,v3_core}, + ?pass(core), {iff,'dcore',{listing,"core"}}, {iff,'to_core0',{done,"core"}} | core_passes()]. @@ -1227,13 +1227,17 @@ makedep_output(#compile{code=Code,options=Opts,ofile=Ofile}=St) -> {error,St#compile{errors=St#compile.errors++[Err]}} end. -%% expand_module(State) -> State' -%% Do the common preprocessing of the input forms. +expand_records(#compile{code=Code0,options=Opts}=St0) -> + Code = erl_expand_records:module(Code0, Opts), + {ok,St0#compile{code=Code}}. -expand_module(#compile{code=Code,options=Opts0}=St0) -> - {Mod,Exp,Forms,Opts1} = sys_pre_expand:module(Code, Opts0), +core(#compile{code=Forms,options=Opts0}=St) -> + Opts1 = lists:flatten([C || {attribute,_,compile,C} <- Forms] ++ Opts0), Opts = expand_opts(Opts1), - {ok,St0#compile{module=Mod,options=Opts,code={Mod,Exp,Forms}}}. + {ok,Core,Ws} = v3_core:module(Forms, Opts), + Mod = cerl:concrete(cerl:module_name(Core)), + {ok,St#compile{module=Mod,code=Core,options=Opts, + warnings=St#compile.warnings++Ws}}. core_fold_module_after_inlining(#compile{code=Code0,options=Opts}=St) -> %% Inlining may produce code that generates spurious warnings. @@ -1808,7 +1812,6 @@ pre_load() -> erl_scan, sys_core_dsetel, sys_core_fold, - sys_pre_expand, v3_codegen, v3_core, v3_kernel, diff --git a/lib/compiler/src/compiler.app.src b/lib/compiler/src/compiler.app.src index 1fd7800e85..b205c7f50a 100644 --- a/lib/compiler/src/compiler.app.src +++ b/lib/compiler/src/compiler.app.src @@ -63,7 +63,6 @@ sys_core_fold_lists, sys_core_inline, sys_pre_attributes, - sys_pre_expand, v3_codegen, v3_core, v3_kernel, diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl deleted file mode 100644 index f996a2d2d7..0000000000 --- a/lib/compiler/src/sys_pre_expand.erl +++ /dev/null @@ -1,606 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2015. 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% -%% -%% Purpose : Expand some source Erlang constructions. This is part of the -%% pre-processing phase. - -%% N.B. Although structs (tagged tuples) are not yet allowed in the -%% language there is code included in pattern/2 and expr/3 (commented out) -%% that handles them by transforming them to tuples. - --module(sys_pre_expand). - -%% Main entry point. --export([module/2]). - --import(lists, [member/2,foldl/3,foldr/3]). - --type fa() :: {atom(), arity()}. - --record(expand, {module=[], %Module name - exports=[], %Exports - attributes=[], %Attributes - callbacks=[], %Callbacks - optional_callbacks=[] :: [fa()], %Optional callbacks - vcount=0, %Variable counter - func=[], %Current function - arity=[], %Arity for current function - fcount=0, %Local fun count - ctype %Call type map - }). - -%% module(Forms, CompileOptions) -%% {ModuleName,Exports,TransformedForms,CompileOptions'} -%% Expand the forms in one module. -%% -%% CompileOptions is augmented with options from -compile attributes. - -module(Fs0, Opts0) -> - - %% Expand records. Normalise guard tests. - Fs = erl_expand_records:module(Fs0, Opts0), - - Opts = compiler_options(Fs) ++ Opts0, - - %% Set pre-defined exported functions. - PreExp = [{module_info,0},{module_info,1}], - - %% Build the set of defined functions and the initial call - %% type map. - Defined = defined_functions(Fs, PreExp), - Ctype = maps:from_list([{K,local} || K <- Defined]), - - %% Build initial expand record. - St0 = #expand{exports=PreExp, - ctype=Ctype - }, - - %% Expand the functions. - {Tfs,St1} = forms(Fs, St0), - - %% Get the correct list of exported functions. - Exports = case member(export_all, Opts) of - true -> Defined; - false -> St1#expand.exports - end, - St2 = St1#expand{exports=Exports,ctype=undefined}, - - %% Generate all functions from stored info. - {Ats,St3} = module_attrs(St2), - {Mfs,St4} = module_predef_funcs(St3), - {St4#expand.module, St4#expand.exports, Ats ++ Tfs ++ Mfs, - Opts}. - -compiler_options(Forms) -> - lists:flatten([C || {attribute,_,compile,C} <- Forms]). - -%% defined_function(Forms, Predef) -> Functions. -%% Add function to defined if form is a function. - -defined_functions(Forms, Predef) -> - Fs = foldl(fun({function,_,N,A,_Cs}, Acc) -> [{N,A}|Acc]; - (_, Acc) -> Acc - end, Predef, Forms), - ordsets:from_list(Fs). - -module_attrs(#expand{attributes=Attributes}=St) -> - Attrs = [{attribute,Line,Name,Val} || {Name,Line,Val} <- Attributes], - Callbacks = [Callback || {_,_,callback,_}=Callback <- Attrs], - OptionalCallbacks = get_optional_callbacks(Attrs), - {Attrs,St#expand{callbacks=Callbacks, - optional_callbacks=OptionalCallbacks}}. - -get_optional_callbacks(Attrs) -> - L = [O || - {attribute, _, optional_callbacks, O} <- Attrs, - is_fa_list(O)], - lists:append(L). - -is_fa_list([{FuncName, Arity}|L]) - when is_atom(FuncName), is_integer(Arity), Arity >= 0 -> - is_fa_list(L); -is_fa_list([]) -> true; -is_fa_list(_) -> false. - -module_predef_funcs(St0) -> - {Mpf1,St1} = module_predef_func_beh_info(St0), - Mpf2 = module_predef_funcs_mod_info(St1), - Mpf = [erl_parse:new_anno(F) || F <- Mpf1++Mpf2], - {Mpf,St1}. - -module_predef_func_beh_info(#expand{callbacks=[]}=St) -> - {[], St}; -module_predef_func_beh_info(#expand{callbacks=Callbacks, - optional_callbacks=OptionalCallbacks, - exports=Exports}=St) -> - PreDef0 = [{behaviour_info,1}], - PreDef = ordsets:from_list(PreDef0), - {[gen_beh_info(Callbacks, OptionalCallbacks)], - St#expand{exports=ordsets:union(PreDef, Exports)}}. - -gen_beh_info(Callbacks, OptionalCallbacks) -> - List = make_list(Callbacks), - OptionalList = make_optional_list(OptionalCallbacks), - {function,0,behaviour_info,1, - [{clause,0,[{atom,0,callbacks}],[], - [List]}, - {clause,0,[{atom,0,optional_callbacks}],[], - [OptionalList]}]}. - -make_list([]) -> {nil,0}; -make_list([{_,_,_,[{{Name,Arity},_}]}|Rest]) -> - {cons,0, - {tuple,0, - [{atom,0,Name}, - {integer,0,Arity}]}, - make_list(Rest)}. - -make_optional_list([]) -> {nil,0}; -make_optional_list([{Name,Arity}|Rest]) -> - {cons,0, - {tuple,0, - [{atom,0,Name}, - {integer,0,Arity}]}, - make_optional_list(Rest)}. - -module_predef_funcs_mod_info(#expand{module=Mod}) -> - ModAtom = {atom,0,Mod}, - [{function,0,module_info,0, - [{clause,0,[],[], - [{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}}, - [ModAtom]}]}]}, - {function,0,module_info,1, - [{clause,0,[{var,0,'X'}],[], - [{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}}, - [ModAtom,{var,0,'X'}]}]}]}]. - -%% forms(Forms, State) -> -%% {TransformedForms,State'} -%% Process the forms. Attributes are lost and just affect the state. -%% Ignore uninteresting forms like eof and type. - -forms([{attribute,_,file,_File}=F|Fs0], St0) -> - {Fs,St1} = forms(Fs0, St0), - {[F|Fs],St1}; -forms([{attribute,Line,Name,Val}|Fs0], St0) -> - St1 = attribute(Name, Val, Line, St0), - forms(Fs0, St1); -forms([{function,L,N,A,Cs}|Fs0], St0) -> - {Ff,St1} = function(L, N, A, Cs, St0), - {Fs,St2} = forms(Fs0, St1), - {[Ff|Fs],St2}; -forms([_|Fs], St) -> forms(Fs, St); -forms([], St) -> {[],St}. - -%% attribute(Attribute, Value, Line, State) -> State'. -%% Process an attribute, this just affects the state. - -attribute(module, Module, _L, St) -> - true = is_atom(Module), - St#expand{module=Module}; -attribute(export, Es, _L, St) -> - St#expand{exports=ordsets:union(ordsets:from_list(Es), - St#expand.exports)}; -attribute(import, Is, _L, St) -> - import(Is, St); -attribute(compile, _C, _L, St) -> - St; -attribute(Name, Val, Line, St) when is_list(Val) -> - St#expand{attributes=St#expand.attributes ++ [{Name,Line,Val}]}; -attribute(Name, Val, Line, St) -> - St#expand{attributes=St#expand.attributes ++ [{Name,Line,[Val]}]}. - -function(L, N, A, Cs0, St0) -> - {Cs,St} = clauses(Cs0, St0#expand{func=N,arity=A,fcount=0}), - {{function,L,N,A,Cs},St}. - -%% clauses([Clause], State) -> -%% {[TransformedClause],State}. -%% Expand function clauses. - -clauses([{clause,Line,H0,G0,B0}|Cs0], St0) -> - {H,St1} = head(H0, St0), - {G,St2} = guard(G0, St1), - {B,St3} = exprs(B0, St2), - {Cs,St4} = clauses(Cs0, St3), - {[{clause,Line,H,G,B}|Cs],St4}; -clauses([], St) -> {[],St}. - -%% head(HeadPatterns, State) -> -%% {TransformedPatterns,Variables,UsedVariables,State'} - -head(As, St) -> pattern_list(As, St). - -%% pattern(Pattern, State) -> -%% {TransformedPattern,State'} -%% - -pattern({var,_,_}=Var, St) -> - {Var,St}; -pattern({char,_,_}=Char, St) -> - {Char,St}; -pattern({integer,_,_}=Int, St) -> - {Int,St}; -pattern({float,_,_}=Float, St) -> - {Float,St}; -pattern({atom,_,_}=Atom, St) -> - {Atom,St}; -pattern({string,_,_}=String, St) -> - {String,St}; -pattern({nil,_}=Nil, St) -> - {Nil,St}; -pattern({cons,Line,H,T}, St0) -> - {TH,St1} = pattern(H, St0), - {TT,St2} = pattern(T, St1), - {{cons,Line,TH,TT},St2}; -pattern({tuple,Line,Ps}, St0) -> - {TPs,St1} = pattern_list(Ps, St0), - {{tuple,Line,TPs},St1}; -pattern({map,Line,Ps}, St0) -> - {TPs,St1} = pattern_list(Ps, St0), - {{map,Line,TPs},St1}; -pattern({map_field_exact,Line,K0,V0}, St0) -> - %% Key should be treated as an expression - %% but since expressions are not allowed yet, - %% process it through pattern .. and handle assoc - %% (normalise unary op integer -> integer) - {K,St1} = pattern(K0, St0), - {V,St2} = pattern(V0, St1), - {{map_field_exact,Line,K,V},St2}; -pattern({map_field_assoc,Line,K0,V0}, St0) -> - %% when keys are Maps - {K,St1} = pattern(K0, St0), - {V,St2} = pattern(V0, St1), - {{map_field_assoc,Line,K,V},St2}; -%%pattern({struct,Line,Tag,Ps}, St0) -> -%% {TPs,TPsvs,St1} = pattern_list(Ps, St0), -%% {{tuple,Line,[{atom,Line,Tag}|TPs]},TPsvs,St1}; -pattern({bin,Line,Es0}, St0) -> - {Es1,St1} = pattern_bin(Es0, St0), - {{bin,Line,Es1},St1}; -pattern({op,_,'++',{nil,_},R}, St) -> - pattern(R, St); -pattern({op,_,'++',{cons,Li,H,T},R}, St) -> - pattern({cons,Li,H,{op,Li,'++',T,R}}, St); -pattern({op,_,'++',{string,Li,L},R}, St) -> - pattern(string_to_conses(Li, L, R), St); -pattern({match,Line,Pat1, Pat2}, St0) -> - {TH,St1} = pattern(Pat2, St0), - {TT,St2} = pattern(Pat1, St1), - {{match,Line,TT,TH},St2}; -%% Compile-time pattern expressions, including unary operators. -pattern({op,_Line,_Op,_A}=Op, St) -> - {erl_eval:partial_eval(Op),St}; -pattern({op,_Line,_Op,_L,_R}=Op, St) -> - {erl_eval:partial_eval(Op),St}. - -pattern_list([P0|Ps0], St0) -> - {P,St1} = pattern(P0, St0), - {Ps,St2} = pattern_list(Ps0, St1), - {[P|Ps],St2}; -pattern_list([], St) -> {[],St}. - -%% guard(Guard, State) -> -%% {TransformedGuard,State'} -%% Transform a list of guard tests. We KNOW that this has been checked -%% and what the guards test are. Use expr for transforming the guard -%% expressions. - -guard([G0|Gs0], St0) -> - {G,St1} = guard_tests(G0, St0), - {Gs,St2} = guard(Gs0, St1), - {[G|Gs],St2}; -guard([], St) -> {[],St}. - -guard_tests([Gt0|Gts0], St0) -> - {Gt1,St1} = guard_test(Gt0, St0), - {Gts1,St2} = guard_tests(Gts0, St1), - {[Gt1|Gts1],St2}; -guard_tests([], St) -> {[],St}. - -guard_test(Test, St) -> - expr(Test, St). - -%% exprs(Expressions, State) -> -%% {TransformedExprs,State'} - -exprs([E0|Es0], St0) -> - {E,St1} = expr(E0, St0), - {Es,St2} = exprs(Es0, St1), - {[E|Es],St2}; -exprs([], St) -> {[],St}. - -%% expr(Expression, State) -> -%% {TransformedExpression,State'} - -expr({var,_,_}=Var, St) -> - {Var,St}; -expr({char,_,_}=Char, St) -> - {Char,St}; -expr({integer,_,_}=Int, St) -> - {Int,St}; -expr({float,_,_}=Float, St) -> - {Float,St}; -expr({atom,_,_}=Atom, St) -> - {Atom,St}; -expr({string,_,_}=String, St) -> - {String,St}; -expr({nil,_}=Nil, St) -> - {Nil,St}; -expr({cons,Line,H0,T0}, St0) -> - {H,St1} = expr(H0, St0), - {T,St2} = expr(T0, St1), - {{cons,Line,H,T},St2}; -expr({lc,Line,E0,Qs0}, St0) -> - {Qs1,St1} = lc_tq(Line, Qs0, St0), - {E1,St2} = expr(E0, St1), - {{lc,Line,E1,Qs1},St2}; -expr({bc,Line,E0,Qs0}, St0) -> - {Qs1,St1} = lc_tq(Line, Qs0, St0), - {E1,St2} = expr(E0, St1), - {{bc,Line,E1,Qs1},St2}; -expr({tuple,Line,Es0}, St0) -> - {Es1,St1} = expr_list(Es0, St0), - {{tuple,Line,Es1},St1}; -%%expr({struct,Line,Tag,Es0}, Vs, St0) -> -%% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0), -%% {{tuple,Line,[{atom,Line,Tag}|Es1]},Esvs,Esus,St1}; -expr({map,Line,Es0}, St0) -> - {Es1,St1} = expr_list(Es0, St0), - {{map,Line,Es1},St1}; -expr({map,Line,E0,Es0}, St0) -> - {E1,St1} = expr(E0, St0), - {Es1,St2} = expr_list(Es0, St1), - {{map,Line,E1,Es1},St2}; -expr({map_field_assoc,Line,K0,V0}, St0) -> - {K,St1} = expr(K0, St0), - {V,St2} = expr(V0, St1), - {{map_field_assoc,Line,K,V},St2}; -expr({map_field_exact,Line,K0,V0}, St0) -> - {K,St1} = expr(K0, St0), - {V,St2} = expr(V0, St1), - {{map_field_exact,Line,K,V},St2}; -expr({bin,Line,Es0}, St0) -> - {Es1,St1} = expr_bin(Es0, St0), - {{bin,Line,Es1},St1}; -expr({block,Line,Es0}, St0) -> - {Es,St1} = exprs(Es0, St0), - {{block,Line,Es},St1}; -expr({'if',Line,Cs0}, St0) -> - {Cs,St1} = clauses(Cs0, St0), - {{'if',Line,Cs},St1}; -expr({'case',Line,E0,Cs0}, St0) -> - {E,St1} = expr(E0, St0), - {Cs,St2} = clauses(Cs0, St1), - {{'case',Line,E,Cs},St2}; -expr({'receive',Line,Cs0}, St0) -> - {Cs,St1} = clauses(Cs0, St0), - {{'receive',Line,Cs},St1}; -expr({'receive',Line,Cs0,To0,ToEs0}, St0) -> - {To,St1} = expr(To0, St0), - {ToEs,St2} = exprs(ToEs0, St1), - {Cs,St3} = clauses(Cs0, St2), - {{'receive',Line,Cs,To,ToEs},St3}; -expr({'fun',Line,Body}, St) -> - fun_tq(Line, Body, St); -expr({named_fun,Line,Name,Cs}, St) -> - fun_tq(Line, Cs, St, Name); -expr({call,Line,{atom,La,N}=Atom,As0}, St0) -> - {As,St1} = expr_list(As0, St0), - Ar = length(As), - Key = {N,Ar}, - case St1#expand.ctype of - #{Key:=local} -> - {{call,Line,Atom,As},St1}; - #{Key:={imported,Mod}} -> - {{call,Line,{remote,La,{atom,La,Mod},Atom},As},St1}; - _ -> - true = erl_internal:bif(N, Ar), - {{call,Line,{remote,La,{atom,La,erlang},Atom},As},St1} - end; -expr({call,Line,{remote,Lr,M0,F},As0}, St0) -> - {[M1,F1|As1],St1} = expr_list([M0,F|As0], St0), - {{call,Line,{remote,Lr,M1,F1},As1},St1}; -expr({call,Line,F,As0}, St0) -> - {[Fun1|As1],St1} = expr_list([F|As0], St0), - {{call,Line,Fun1,As1},St1}; -expr({'try',Line,Es0,Scs0,Ccs0,As0}, St0) -> - {Es1,St1} = exprs(Es0, St0), - {Scs1,St2} = clauses(Scs0, St1), - {Ccs1,St3} = clauses(Ccs0, St2), - {As1,St4} = exprs(As0, St3), - {{'try',Line,Es1,Scs1,Ccs1,As1},St4}; -expr({'catch',Line,E0}, St0) -> - {E,St1} = expr(E0, St0), - {{'catch',Line,E},St1}; -expr({match,Line,P0,E0}, St0) -> - {E,St1} = expr(E0, St0), - {P,St2} = pattern(P0, St1), - {{match,Line,P,E},St2}; -expr({op,Line,Op,A0}, St0) -> - {A,St1} = expr(A0, St0), - {{op,Line,Op,A},St1}; -expr({op,Line,Op,L0,R0}, St0) -> - {L,St1} = expr(L0, St0), - {R,St2} = expr(R0, St1), - {{op,Line,Op,L,R},St2}. - -expr_list([E0|Es0], St0) -> - {E,St1} = expr(E0, St0), - {Es,St2} = expr_list(Es0, St1), - {[E|Es],St2}; -expr_list([], St) -> {[],St}. - -%% lc_tq(Line, Qualifiers, State) -> -%% {[TransQual],State'} - -lc_tq(Line, [{generate,Lg,P0,G0} | Qs0], St0) -> - {G1,St1} = expr(G0, St0), - {P1,St2} = pattern(P0, St1), - {Qs1,St3} = lc_tq(Line, Qs0, St2), - {[{generate,Lg,P1,G1} | Qs1],St3}; - -lc_tq(Line, [{b_generate,Lg,P0,G0}|Qs0], St0) -> - {G1,St1} = expr(G0, St0), - {P1,St2} = pattern(P0, St1), - {Qs1,St3} = lc_tq(Line, Qs0, St2), - {[{b_generate,Lg,P1,G1}|Qs1],St3}; -lc_tq(Line, [F0 | Qs0], St0) -> - {F1,St1} = expr(F0, St0), - {Qs1,St2} = lc_tq(Line, Qs0, St1), - {[F1|Qs1],St2}; -lc_tq(_Line, [], St0) -> - {[],St0}. - - -%% fun_tq(Line, Body, State) -> -%% {Fun,State'} -%% Transform an "explicit" fun {'fun', Line, {clauses, Cs}} into an -%% extended form {'fun', Line, {clauses, Cs}, Info}, unless it is the -%% name of a BIF (erl_lint has checked that it is not an import). -%% "Implicit" funs {'fun', Line, {function, F, A}} are not changed. - -fun_tq(Lf, {function,F,A}=Function, St0) -> - case erl_internal:bif(F, A) of - true -> - {As,St1} = new_vars(A, Lf, St0), - Cs = [{clause,Lf,As,[],[{call,Lf,{atom,Lf,F},As}]}], - fun_tq(Lf, {clauses,Cs}, St1); - false -> - {Fname,St1} = new_fun_name(St0), - Index = Uniq = 0, - {{'fun',Lf,Function,{Index,Uniq,Fname}},St1} - end; -fun_tq(L, {function,M,F,A}, St) when is_atom(M), is_atom(F), is_integer(A) -> - %% This is the old format for external funs, generated by a pre-R15 - %% compiler. That means that a tool, such as the debugger or xref, - %% directly invoked this module with the abstract code from a - %% pre-R15 BEAM file. Be helpful, and translate it to the new format. - fun_tq(L, {function,{atom,L,M},{atom,L,F},{integer,L,A}}, St); -fun_tq(Lf, {function,_,_,_}=ExtFun, St) -> - {{'fun',Lf,ExtFun},St}; -fun_tq(Lf, {clauses,Cs0}, St0) -> - {Cs1,St1} = clauses(Cs0, St0), - {Fname,St2} = new_fun_name(St1), - %% Set dummy values for Index and Uniq -- the real values will - %% be assigned by beam_asm. - Index = Uniq = 0, - {{'fun',Lf,{clauses,Cs1},{Index,Uniq,Fname}},St2}. - -fun_tq(Line, Cs0, St0, Name) -> - {Cs1,St1} = clauses(Cs0, St0), - {Fname,St2} = new_fun_name(St1, Name), - {{named_fun,Line,Name,Cs1,{0,0,Fname}},St2}. - -%% new_fun_name(State) -> {FunName,State}. - -new_fun_name(St) -> - new_fun_name(St, 'fun'). - -new_fun_name(#expand{func=F,arity=A,fcount=I}=St, FName) -> - Name = "-" ++ atom_to_list(F) ++ "/" ++ integer_to_list(A) - ++ "-" ++ atom_to_list(FName) ++ "-" ++ integer_to_list(I) ++ "-", - {list_to_atom(Name),St#expand{fcount=I+1}}. - -%% pattern_bin([Element], State) -> {[Element],[Variable],[UsedVar],State}. - -pattern_bin(Es, St) -> - foldr(fun (E, Acc) -> pattern_element(E, Acc) end, {[],St}, Es). - -pattern_element({bin_element,Line,Expr0,Size0,Type0}, {Es,St0}) -> - {Expr1,St1} = pattern(Expr0, St0), - {Size1,St2} = pat_bit_size(Size0, St1), - {Size,Type} = make_bit_type(Line, Size1, Type0), - Expr = coerce_to_float(Expr1, Type0), - {[{bin_element,Line,Expr,Size,Type}|Es],St2}. - -pat_bit_size(default, St) -> {default,St}; -pat_bit_size({var,_Lv,_V}=Var, St) -> {Var,St}; -pat_bit_size(Size, St) -> - Line = element(2, Size), - {value,Sz,_} = erl_eval:expr(Size, erl_eval:new_bindings()), - {{integer,Line,Sz},St}. - -make_bit_type(Line, default, Type0) -> - case erl_bits:set_bit_type(default, Type0) of - {ok,all,Bt} -> {{atom,Line,all},erl_bits:as_list(Bt)}; - {ok,undefined,Bt} -> {{atom,Line,undefined},erl_bits:as_list(Bt)}; - {ok,Size,Bt} -> {{integer,Line,Size},erl_bits:as_list(Bt)} - end; -make_bit_type(_Line, Size, Type0) -> %Integer or 'all' - {ok,Size,Bt} = erl_bits:set_bit_type(Size, Type0), - {Size,erl_bits:as_list(Bt)}. - -coerce_to_float({integer,L,I}=E, [float|_]) -> - try - {float,L,float(I)} - catch - error:badarg -> E - end; -coerce_to_float(E, _) -> E. - -%% expr_bin([Element], State) -> {[Element],State}. - -expr_bin(Es, St) -> - foldr(fun (E, Acc) -> bin_element(E, Acc) end, {[],St}, Es). - -bin_element({bin_element,Line,Expr,Size,Type}, {Es,St0}) -> - {Expr1,St1} = expr(Expr, St0), - {Size1,St2} = if Size == default -> {default,St1}; - true -> expr(Size, St1) - end, - {Size2,Type1} = make_bit_type(Line, Size1, Type), - {[{bin_element,Line,Expr1,Size2,Type1}|Es],St2}. - -%% new_var_name(State) -> {VarName,State}. - -new_var_name(St) -> - C = St#expand.vcount, - {list_to_atom("pre" ++ integer_to_list(C)),St#expand{vcount=C+1}}. - -%% new_var(Line, State) -> {Var,State}. - -new_var(L, St0) -> - {New,St1} = new_var_name(St0), - {{var,L,New},St1}. - -%% new_vars(Count, Line, State) -> {[Var],State}. -%% Make Count new variables. - -new_vars(N, L, St) -> new_vars(N, L, St, []). - -new_vars(N, L, St0, Vs) when N > 0 -> - {V,St1} = new_var(L, St0), - new_vars(N-1, L, St1, [V|Vs]); -new_vars(0, _L, St, Vs) -> {Vs,St}. - -string_to_conses(Line, Cs, Tail) -> - foldr(fun (C, T) -> {cons,Line,{char,Line,C},T} end, Tail, Cs). - - -%% import(Line, Imports, State) -> -%% State' -%% Handle import declarations. - -import({Mod,Fs}, #expand{ctype=Ctype0}=St) -> - true = is_atom(Mod), - Ctype = foldl(fun(F, A) -> - A#{F=>{imported,Mod}} - end, Ctype0, Fs), - St#expand{ctype=Ctype}. diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 634ec68736..0f80eb68fa 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -137,11 +137,13 @@ -record(core, {vcount=0 :: non_neg_integer(), %Variable counter fcount=0 :: non_neg_integer(), %Function counter + function={none,0} :: fa(), %Current function. in_guard=false :: boolean(), %In guard or not. wanted=true :: boolean(), %Result wanted or not. opts :: [compile:option()], %Options. ws=[] :: [warning()], %Warnings. - file=[{file,""}]}). %File + file=[{file,""}] %File. + }). %% XXX: The following type declarations do not belong in this module -type fa() :: {atom(), arity()}. @@ -149,38 +151,77 @@ -type form() :: {function, integer(), atom(), arity(), _} | {attribute, integer(), attribute(), _}. --spec module({module(), [fa()], [form()]}, [compile:option()]) -> +-record(imodule, {name = [], + exports = ordsets:new(), + attrs = [], + defs = [], + file = [], + opts = [], + ws = []}). + +-spec module([form()], [compile:option()]) -> {'ok',cerl:c_module(),[warning()]}. -module({Mod,Exp,Forms}, Opts) -> - Cexp = map(fun ({_N,_A} = NA) -> #c_var{name=NA} end, Exp), - {Kfs0,As0,Ws,_File} = foldl(fun (F, Acc) -> - form(F, Acc, Opts) - end, {[],[],[],[]}, Forms), - Kfs = reverse(Kfs0), +module(Forms0, Opts) -> + Forms = erl_internal:add_predefined_functions(Forms0), + Module = foldl(fun (F, Acc) -> + form(F, Acc, Opts) + end, #imodule{}, Forms), + #imodule{name=Mod,exports=Exp0,attrs=As0,defs=Kfs0,ws=Ws} = Module, + Exp = case member(export_all, Opts) of + true -> defined_functions(Forms); + false -> Exp0 + end, + Cexp = [#c_var{name=FA} || {_,_}=FA <- Exp], As = reverse(As0), + Kfs = reverse(Kfs0), {ok,#c_module{name=#c_literal{val=Mod},exports=Cexp,attrs=As,defs=Kfs},Ws}. -form({function,_,_,_,_}=F0, {Fs,As,Ws0,File}, Opts) -> +form({function,_,_,_,_}=F0, Module, Opts) -> + #imodule{file=File,defs=Defs,ws=Ws0} = Module, {F,Ws} = function(F0, Ws0, File, Opts), - {[F|Fs],As,Ws,File}; -form({attribute,_,file,{File,_Line}}, {Fs,As,Ws,_}, _Opts) -> - {Fs,As,Ws,File}; -form({attribute,_,_,_}=F, {Fs,As,Ws,File}, _Opts) -> - {Fs,[attribute(F)|As],Ws,File}. + Module#imodule{defs=[F|Defs],ws=Ws}; +form({attribute,_,module,Mod}, Module, _Opts) -> + true = is_atom(Mod), + Module#imodule{name=Mod}; +form({attribute,_,file,{File,_Line}}, Module, _Opts) -> + Module#imodule{file=File}; +form({attribute,_,compile,_}, Module, _Opts) -> + %% Ignore compilation options. + Module; +form({attribute,_,import,_}, Module, _Opts) -> + %% Ignore. We have no futher use for imports. + Module; +form({attribute,_,export,Es}, #imodule{exports=Exp0}=Module, _Opts) -> + Exp = ordsets:union(ordsets:from_list(Es), Exp0), + Module#imodule{exports=Exp}; +form({attribute,_,_,_}=F, #imodule{attrs=As}=Module, _Opts) -> + Module#imodule{attrs=[attribute(F)|As]}; +form(_, Module, _Opts) -> + %% Ignore uninteresting forms such as 'eof'. + Module. attribute(Attribute) -> Fun = fun(A) -> [erl_anno:location(A)] end, - {attribute,Line,Name,Val} = erl_parse:map_anno(Fun, Attribute), + {attribute,Line,Name,Val0} = erl_parse:map_anno(Fun, Attribute), + Val = if + is_list(Val0) -> Val0; + true -> [Val0] + end, {#c_literal{val=Name, anno=Line}, #c_literal{val=Val, anno=Line}}. +defined_functions(Forms) -> + Fs = [{Name,Arity} || {function,_,Name,Arity,_} <- Forms], + ordsets:from_list(Fs). + %% function_dump(module_info,_,_,_) -> ok; %% function_dump(Name,Arity,Format,Terms) -> %% io:format("~w/~w " ++ Format,[Name,Arity]++Terms), %% ok. function({function,_,Name,Arity,Cs0}, Ws0, File, Opts) -> - St0 = #core{vcount=0,opts=Opts,ws=Ws0,file=[{file,File}]}, + St0 = #core{vcount=0,function={Name,Arity},opts=Opts, + ws=Ws0,file=[{file,File}]}, {B0,St1} = body(Cs0, Name, Arity, St0), %% ok = function_dump(Name,Arity,"body:~n~p~n",[B0]), {B1,St2} = ubody(B0, St1), @@ -632,9 +673,11 @@ expr({'catch',L,E0}, St0) -> {E1,Eps,St1} = expr(E0, St0), Lanno = lineno_anno(L, St1), {#icatch{anno=#a{anno=Lanno},body=Eps ++ [E1]},[],St1}; -expr({'fun',L,{function,F,A},{_,_,_}=Id}, St) -> - Lanno = full_anno(L, St), - {#c_var{anno=Lanno++[{id,Id}],name={F,A}},[],St}; +expr({'fun',L,{function,F,A}}, St0) -> + {Fname,St1} = new_fun_name(St0), + Lanno = full_anno(L, St1), + Id = {0,0,Fname}, + {#c_var{anno=Lanno++[{id,Id}],name={F,A}},[],St1}; expr({'fun',L,{function,M,F,A}}, St0) -> {As,Aps,St1} = safe_list([M,F,A], St0), Lanno = full_anno(L, St1), @@ -642,12 +685,12 @@ expr({'fun',L,{function,M,F,A}}, St0) -> module=#c_literal{val=erlang}, name=#c_literal{val=make_fun}, args=As},Aps,St1}; -expr({'fun',L,{clauses,Cs},Id}, St) -> - fun_tq(Id, Cs, L, St, unnamed); -expr({named_fun,L,'_',Cs,Id}, St) -> - fun_tq(Id, Cs, L, St, unnamed); -expr({named_fun,L,Name,Cs,Id}, St) -> - fun_tq(Id, Cs, L, St, {named,Name}); +expr({'fun',L,{clauses,Cs}}, St) -> + fun_tq(Cs, L, St, unnamed); +expr({named_fun,L,'_',Cs}, St) -> + fun_tq(Cs, L, St, unnamed); +expr({named_fun,L,Name,Cs}, St) -> + fun_tq(Cs, L, St, {named,Name}); expr({call,L,{remote,_,M,F},As0}, St0) -> {[M1,F1|As1],Aps,St1} = safe_list([M,F|As0], St0), Anno = full_anno(L, St1), @@ -899,14 +942,29 @@ try_after(As, St0) -> %% record whereas c_literal should not have a wrapped annotation expr_bin(Es0, Anno, St0) -> - case constant_bin(Es0) of + Es1 = [bin_element(E) || E <- Es0], + case constant_bin(Es1) of error -> - {Es,Eps,St} = expr_bin_1(bin_expand_strings(Es0), St0), + {Es,Eps,St} = expr_bin_1(bin_expand_strings(Es1), St0), {#ibinary{anno=#a{anno=Anno},segments=Es},Eps,St}; Bin -> {#c_literal{anno=Anno,val=Bin},[],St0} end. +bin_element({bin_element,Line,Expr,Size0,Type0}) -> + {Size,Type} = make_bit_type(Line, Size0, Type0), + {bin_element,Line,Expr,Size,Type}. + +make_bit_type(Line, default, Type0) -> + case erl_bits:set_bit_type(default, Type0) of + {ok,all,Bt} -> {{atom,Line,all},erl_bits:as_list(Bt)}; + {ok,undefined,Bt} -> {{atom,Line,undefined},erl_bits:as_list(Bt)}; + {ok,Size,Bt} -> {{integer,Line,Size},erl_bits:as_list(Bt)} + end; +make_bit_type(_Line, Size, Type0) -> %Integer or 'all' + {ok,Size,Bt} = erl_bits:set_bit_type(Size, Type0), + {Size,erl_bits:as_list(Bt)}. + %% constant_bin([{bin_element,_,_,_,_}]) -> binary() | error %% If the binary construction is truly constant (no variables, %% no native fields), and does not contain fields whose expansion @@ -1030,17 +1088,19 @@ bitstr({bin_element,_,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) -> %% fun_tq(Id, [Clauses], Line, State, NameInfo) -> {Fun,[PreExp],State}. -fun_tq({_,_,Name}=Id, Cs0, L, St0, NameInfo) -> +fun_tq(Cs0, L, St0, NameInfo) -> Arity = clause_arity(hd(Cs0)), {Cs1,Ceps,St1} = clauses(Cs0, St0), {Args,St2} = new_vars(Arity, St1), {Ps,St3} = new_vars(Arity, St2), %Need new variables here Anno = full_anno(L, St3), + {Name,St4} = new_fun_name(St3), Fc = function_clause(Ps, Anno, {Name,Arity}), + Id = {0,0,Name}, Fun = #ifun{anno=#a{anno=Anno}, id=[{id,Id}], %We KNOW! vars=Args,clauses=Cs1,fc=Fc,name=NameInfo}, - {Fun,Ceps,St3}. + {Fun,Ceps,St4}. %% lc_tq(Line, Exp, [Qualifier], Mc, State) -> {LetRec,[PreExp],State}. %% This TQ from Simon PJ pp 127-138. @@ -1366,8 +1426,9 @@ list_gen_pattern(P0, Line, St) -> %%% the result binary in a binary comprehension. %%% -bc_initial_size(E, Q, St0) -> +bc_initial_size(E0, Q, St0) -> try + E = bin_bin_element(E0), {ElemSzExpr,ElemSzPre,EVs,St1} = bc_elem_size(E, St0), {V,St2} = new_var(St1), {GenSzExpr,GenSzPre,St3} = bc_gen_size(Q, EVs, St2), @@ -1406,14 +1467,15 @@ bc_elem_size({bin,_,El}, St0) -> bc_elem_size(_, _) -> throw(impossible). -bc_elem_size_1([{bin_element,_,{string,_,String},{integer,_,N},Flags}|Es], Bits, Vars) -> - {unit,U} = keyfind(unit, 1, Flags), +bc_elem_size_1([{bin_element,_,{string,_,String},{integer,_,N},_}=El|Es], + Bits, Vars) -> + U = get_unit(El), bc_elem_size_1(Es, Bits+U*N*length(String), Vars); -bc_elem_size_1([{bin_element,_,_,{integer,_,N},Flags}|Es], Bits, Vars) -> - {unit,U} = keyfind(unit, 1, Flags), +bc_elem_size_1([{bin_element,_,_,{integer,_,N},_}=El|Es], Bits, Vars) -> + U = get_unit(El), bc_elem_size_1(Es, Bits+U*N, Vars); -bc_elem_size_1([{bin_element,_,_,{var,_,Var},Flags}|Es], Bits, Vars) -> - {unit,U} = keyfind(unit, 1, Flags), +bc_elem_size_1([{bin_element,_,_,{var,_,Var},_}=El|Es], Bits, Vars) -> + U = get_unit(El), bc_elem_size_1(Es, Bits, [{U,#c_var{name=Var}}|Vars]); bc_elem_size_1([_|_], _, _) -> throw(impossible); @@ -1470,7 +1532,9 @@ bc_gen_size_1([{generate,L,El,Gen}|Qs], EVs, E0, Pre0, St0) -> {E,Pre,St} = bc_gen_size_mul(E0, #c_literal{val=Len}, Pre0, St0), bc_gen_size_1(Qs, EVs, E, Pre, St) end; -bc_gen_size_1([{b_generate,_,El,Gen}|Qs], EVs, E0, Pre0, St0) -> +bc_gen_size_1([{b_generate,_,El0,Gen0}|Qs], EVs, E0, Pre0, St0) -> + El = bin_bin_element(El0), + Gen = bin_bin_element(Gen0), bc_verify_non_filtering(El, EVs), {MatchSzExpr,Pre1,_,St1} = bc_elem_size(El, St0), Pre2 = reverse(Pre1, Pre0), @@ -1486,6 +1550,10 @@ bc_gen_size_1([], _, E, Pre, St) -> bc_gen_size_1(_, _, _, _, _) -> throw(impossible). +bin_bin_element({bin,L,El}) -> + {bin,L,[bin_element(E) || E <- El]}; +bin_bin_element(Other) -> Other. + bc_gen_bit_size({var,L,V}, Pre0, St0) -> Lanno = lineno_anno(L, St0), {SzVar,St} = new_var(St0), @@ -1528,11 +1596,11 @@ bc_list_length(_, _) -> bc_bin_size({bin,_,Els}) -> bc_bin_size_1(Els, 0). -bc_bin_size_1([{bin_element,_,{string,_,String},{integer,_,Sz},Flags}|Els], N) -> - {unit,U} = keyfind(unit, 1, Flags), +bc_bin_size_1([{bin_element,_,{string,_,String},{integer,_,Sz},_}=El|Els], N) -> + U = get_unit(El), bc_bin_size_1(Els, N+U*Sz*length(String)); -bc_bin_size_1([{bin_element,_,_,{integer,_,Sz},Flags}|Els], N) -> - {unit,U} = keyfind(unit, 1, Flags), +bc_bin_size_1([{bin_element,_,_,{integer,_,Sz},_}=El|Els], N) -> + U = get_unit(El), bc_bin_size_1(Els, N+U*Sz); bc_bin_size_1([], N) -> N; bc_bin_size_1(_, _) -> throw(impossible). @@ -1567,6 +1635,10 @@ bc_bsr(E1, E2) -> name=#c_literal{val='bsr'}, args=[E1,E2]}. +get_unit({bin_element,_,_,_,Flags}) -> + {unit,U} = keyfind(unit, 1, 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. @@ -1714,7 +1786,18 @@ pattern({bin,L,Ps}, St) -> pattern({match,_,P1,P2}, St) -> {Cp1,Eps1,St1} = pattern(P1,St), {Cp2,Eps2,St2} = pattern(P2,St1), - {pat_alias(Cp1,Cp2),Eps1++Eps2,St2}. + {pat_alias(Cp1,Cp2),Eps1++Eps2,St2}; +%% Evaluate compile-time expressions. +pattern({op,_,'++',{nil,_},R}, St) -> + pattern(R, St); +pattern({op,_,'++',{cons,Li,H,T},R}, St) -> + pattern({cons,Li,H,{op,Li,'++',T,R}}, St); +pattern({op,_,'++',{string,Li,L},R}, St) -> + pattern(string_to_conses(Li, L, R), St); +pattern({op,_Line,_Op,_A}=Op, St) -> + pattern(erl_eval:partial_eval(Op), St); +pattern({op,_Line,_Op,_L,_R}=Op, St) -> + pattern(erl_eval:partial_eval(Op), St). %% pattern_map_pairs([MapFieldExact],State) -> [#c_map_pairs{}] pattern_map_pairs(Ps, St) -> @@ -1756,16 +1839,27 @@ pat_alias_map_pairs_1([]) -> []. pat_bin(Ps, St) -> [pat_segment(P, St) || P <- bin_expand_strings(Ps)]. -pat_segment({bin_element,L,Val,Size,[Type,{unit,Unit}|Flags]}, St) -> +pat_segment({bin_element,L,Val,Size0,Type0}, St) -> + {Size,Type1} = make_bit_type(L, Size0, Type0), + [Type,{unit,Unit}|Flags] = Type1, Anno = lineno_anno(L, St), - {Pval,[],St1} = pattern(Val,St), - {Psize,[],_St2} = pattern(Size,St1), + {Pval0,[],St1} = pattern(Val, St), + Pval = coerce_to_float(Pval0, Type0), + {Psize,[],_St2} = pattern(Size, St1), #c_bitstr{anno=Anno, val=Pval,size=Psize, unit=#c_literal{val=Unit}, type=#c_literal{val=Type}, flags=#c_literal{val=Flags}}. +coerce_to_float(#c_literal{val=Int}=E, [float|_]) when is_integer(Int) -> + try + E#c_literal{val=float(Int)} + catch + error:badarg -> E + end; +coerce_to_float(E, _) -> E. + %% pat_alias(CorePat, CorePat) -> AliasPat. %% Normalise aliases. Trap bad aliases by throwing 'nomatch'. @@ -1835,11 +1929,18 @@ pattern_list([P0|Ps0], St0) -> pattern_list([], St) -> {[],[],St}. +string_to_conses(Line, Cs, Tail) -> + foldr(fun (C, T) -> {cons,Line,{char,Line,C},T} end, Tail, Cs). %% make_vars([Name]) -> [{Var,Name}]. make_vars(Vs) -> [ #c_var{name=V} || V <- Vs ]. +new_fun_name(#core{function={F,A},fcount=I}=St) -> + Name = "-" ++ atom_to_list(F) ++ "/" ++ integer_to_list(A) + ++ "-fun-" ++ integer_to_list(I) ++ "-", + {list_to_atom(Name),St#core{fcount=I+1}}. + %% new_fun_name(Type, State) -> {FunName,State}. new_fun_name(Type, #core{fcount=C}=St) -> diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index b0148f7103..28a353d27b 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -403,12 +403,11 @@ other_output(Config) when is_list(Config) -> end], io:put_chars("to_exp (file)"), - {ok,simple,Expand} = compile:file(Simple, [to_exp,binary,time]), - case Expand of - {simple,Exports,Forms} when is_list(Exports), is_list(Forms) -> ok - end, + {ok,[],Expand} = compile:file(Simple, [to_exp,binary,time]), + true = is_list(Expand), + {attribute,_,module,simple} = lists:keyfind(module, 3, Expand), io:put_chars("to_exp (forms)"), - {ok,simple,Expand} = compile:forms(PP, [to_exp,binary,time]), + {ok,[],Expand} = compile:forms(PP, [to_exp,binary,time]), io:put_chars("to_core (file)"), {ok,simple,Core} = compile:file(Simple, [to_core,binary,time]), diff --git a/lib/compiler/test/compiler.cover b/lib/compiler/test/compiler.cover index 3fd7fc1937..2be079944f 100644 --- a/lib/compiler/test/compiler.cover +++ b/lib/compiler/test/compiler.cover @@ -1,5 +1,5 @@ {incl_app,compiler,details}. %% -*- erlang -*- -{excl_mods,compiler,[core_scan,core_parse]}. +{excl_mods,compiler,[core_scan,core_parse,sys_pre_expand]}. diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index f83684b605..917f213e60 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -72,8 +72,7 @@ store_module(Mod, File, Binary, Db) -> exit({Mod,too_old_beam_file}); {raw_abstract_v1,Code0} -> Code = interpret_file_attribute(Code0), - {_,_,Forms0,_} = sys_pre_expand:module(Code, []), - Forms0 + standard_transforms(Code) end, dbg_idb:insert(Db, mod_file, File), dbg_idb:insert(Db, defs, []), @@ -94,6 +93,11 @@ store_module(Mod, File, Binary, Db) -> dbg_idb:insert(Db, mod_bin, NewBinary), dbg_idb:insert(Db, mod_raw, <<Src/binary,0:8>>). %% Add eos +standard_transforms(Forms0) -> + Forms = erl_expand_records:module(Forms0, []), + erl_internal:add_predefined_functions(Forms). + + %% Adjust line numbers using the file/2 attribute. %% Also take the absolute value of line numbers. %% This simple fix will make the marker point at the correct line @@ -102,7 +106,6 @@ store_module(Mod, File, Binary, Db) -> interpret_file_attribute(Code) -> epp:interpret_file_attribute(Code). - abstr(Bin) when is_binary(Bin) -> binary_to_term(Bin); abstr(Term) -> Term. @@ -124,8 +127,9 @@ store_forms([{function,_,Name,Arity,Cs0}|Fs], Mod, Db, Exp) -> store_forms(Fs, Mod, Db, Exp); store_forms([{attribute,_,_Name,_Val}|Fs], Mod, Db, Exp) -> store_forms(Fs, Mod, Db, Exp); -store_forms([F|_], _Mod, _Db, _Exp) -> - exit({unknown_form,F}); +store_forms([_|Fs], Mod, Db, Exp) -> + %% Ignore other forms such as {eof,_} or {warning,_}. + store_forms(Fs, Mod, Db, Exp); store_forms([], _, _, _) -> ok. @@ -218,10 +222,34 @@ pattern({op,_,'+',{float,Anno,I}}) -> pattern({bin,Anno,Grp}) -> Grp1 = pattern_list(bin_expand_strings(Grp)), {bin,ln(Anno),Grp1}; -pattern({bin_element,Anno,Expr,Size,Type}) -> - Expr1 = pattern(Expr), - Size1 = expr(Size, false), - {bin_element,ln(Anno),Expr1,Size1,Type}. +pattern({bin_element,Anno,Expr0,Size0,Type0}) -> + {Size1,Type} = make_bit_type(Anno, Size0, Type0), + Expr1 = pattern(Expr0), + Expr = coerce_to_float(Expr1, Type0), + Size = pattern(Size1), + {bin_element,ln(Anno),Expr,Size,Type}; +%% Evaluate compile-time expressions. +pattern({op,_,'++',{nil,_},R}) -> + pattern(R); +pattern({op,_,'++',{cons,Li,H,T},R}) -> + pattern({cons,Li,H,{op,Li,'++',T,R}}); +pattern({op,_,'++',{string,Li,L},R}) -> + pattern(string_to_conses(Li, L, R)); +pattern({op,_Line,_Op,_A}=Op) -> + pattern(erl_eval:partial_eval(Op)); +pattern({op,_Line,_Op,_L,_R}=Op) -> + pattern(erl_eval:partial_eval(Op)). + +string_to_conses(Anno, Cs, Tail) -> + lists:foldr(fun (C, T) -> {cons,Anno,{char,Anno,C},T} end, Tail, Cs). + +coerce_to_float({value,Anno,Int}=E, [float|_]) when is_integer(Int) -> + try + {value,Anno,float(Int)} + catch + error:badarg -> E + end; +coerce_to_float(E, _) -> E. %% These patterns are processed "in parallel" for purposes of variable %% definition etc. @@ -299,11 +327,12 @@ gexpr({map,Anno,E0,Fs0}) -> gexpr({bin,Anno,Flds0}) -> Flds = gexpr_list(bin_expand_strings(Flds0)), {bin,ln(Anno),Flds}; -gexpr({bin_element,Anno,Expr0,Size0,Type}) -> +gexpr({bin_element,Anno,Expr0,Size0,Type0}) -> + {Size1,Type} = make_bit_type(Anno, Size0, Type0), Expr = gexpr(Expr0), - Size = gexpr(Size0), + Size = gexpr(Size1), {bin_element,ln(Anno),Expr,Size,Type}; -%%% The previous passes have added the module name 'erlang' to +%%% The erl_expand_records pass has added the module name 'erlang' to %%% all BIF calls, even in guards. gexpr({call,Anno,{remote,_,{atom,_,erlang},{atom,_,self}},[]}) -> {dbg,ln(Anno),self,[]}; @@ -383,18 +412,21 @@ expr({'receive',Anno,Cs0,To0,ToEs0}, Lc) -> ToEs1 = exprs(ToEs0, Lc), Cs1 = icr_clauses(Cs0, Lc), {'receive',ln(Anno),Cs1,To1,ToEs1}; -expr({'fun',Anno,{clauses,Cs0},{_,_,Name}}, _Lc) when is_atom(Name) -> +expr({'fun',Anno,{clauses,Cs0}}, _Lc) -> %% New R10B-2 format (abstract_v2). Cs = fun_clauses(Cs0), + Name = new_fun_name(), {make_fun,ln(Anno),Name,Cs}; -expr({'fun',Anno,{function,F,A},{_Index,_OldUniq,Name}}, _Lc) -> +expr({'fun',Anno,{function,F,A}}, _Lc) -> %% New R8 format (abstract_v2). Line = ln(Anno), As = new_vars(A, Line), + Name = new_fun_name(), Cs = [{clause,Line,As,[],[{local_call,Line,F,As,true}]}], {make_fun,Line,Name,Cs}; -expr({named_fun,Anno,FName,Cs0,{_,_,Name}}, _Lc) when is_atom(Name) -> +expr({named_fun,Anno,FName,Cs0}, _Lc) -> Cs = fun_clauses(Cs0), + Name = new_fun_name(), {make_named_fun,ln(Anno),Name,FName,Cs}; expr({'fun',Anno,{function,{atom,_,M},{atom,_,F},{integer,_,A}}}, _Lc) when 0 =< A, A =< 255 -> @@ -508,17 +540,26 @@ expr({op,Anno,Op,L0,R0}, _Lc) -> expr({bin,Anno,Grp}, _Lc) -> Grp1 = expr_list(bin_expand_strings(Grp)), {bin,ln(Anno),Grp1}; -expr({bin_element,Anno,Expr,Size,Type}, _Lc) -> - Expr1 = expr(Expr, false), - Size1 = expr(Size, false), - {bin_element,ln(Anno),Expr1,Size1,Type}; -expr(Other, _Lc) -> - exit({?MODULE,{unknown_expr,Other}}). +expr({bin_element,Anno,Expr0,Size0,Type0}, _Lc) -> + {Size1,Type} = make_bit_type(Anno, Size0, Type0), + Expr = expr(Expr0, false), + Size = expr(Size1, false), + {bin_element,ln(Anno),Expr,Size,Type}. consify([A|As]) -> {cons,0,A,consify(As)}; consify([]) -> {value,0,[]}. +make_bit_type(Line, default, Type0) -> + case erl_bits:set_bit_type(default, Type0) of + {ok,all,Bt} -> {{atom,Line,all},erl_bits:as_list(Bt)}; + {ok,undefined,Bt} -> {{atom,Line,undefined},erl_bits:as_list(Bt)}; + {ok,Size,Bt} -> {{integer,Line,Size},erl_bits:as_list(Bt)} + end; +make_bit_type(_Line, Size, Type0) -> %Integer or 'all' + {ok,Size,Bt} = erl_bits:set_bit_type(Size, Type0), + {Size,erl_bits:as_list(Bt)}. + %% The debugger converts both strings "abc" and lists [67, 68, 69] %% into {value, Line, [67, 68, 69]}, making it impossible to later %% distingish one or the other inside binaries when evaluating. To @@ -594,6 +635,14 @@ new_vars(N, L, Vs) when N > 0 -> new_vars(N-1, L, [V|Vs]); new_vars(0, _, Vs) -> Vs. +new_fun_name() -> + {F,A} = get(current_function), + I = get(fun_count), + put(fun_count, I+1), + Name = "-" ++ atom_to_list(F) ++ "/" ++ integer_to_list(A) ++ + "-fun-" ++ integer_to_list(I) ++ "-", + list_to_atom(Name). + ln(Anno) -> erl_anno:line(Anno). diff --git a/lib/debugger/test/andor_SUITE.erl b/lib/debugger/test/andor_SUITE.erl index d7bbd4fccb..f6e39514af 100644 --- a/lib/debugger/test/andor_SUITE.erl +++ b/lib/debugger/test/andor_SUITE.erl @@ -29,6 +29,8 @@ -include_lib("common_test/include/ct.hrl"). +-warning("Ignore me -- testing that the debugger can handle warnings"). + suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,1}}]. diff --git a/lib/stdlib/doc/src/erl_expand_records.xml b/lib/stdlib/doc/src/erl_expand_records.xml index 7e4aa2db37..b6aa75ed03 100644 --- a/lib/stdlib/doc/src/erl_expand_records.xml +++ b/lib/stdlib/doc/src/erl_expand_records.xml @@ -45,8 +45,10 @@ <name name="module" arity="2"/> <fsummary>Expand all records in a module.</fsummary> <desc> - <p>Expands all records in a module. The returned module has no - references to records, attributes, or code.</p> + <p>Expands all records in a module to use explicit tuple + operations and adds explicit module names to calls to BIFs and + imported functions. The returned module has no references to + records, attributes, or code.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/erl_internal.xml b/lib/stdlib/doc/src/erl_internal.xml index cf49df0972..17cd0fb240 100644 --- a/lib/stdlib/doc/src/erl_internal.xml +++ b/lib/stdlib/doc/src/erl_internal.xml @@ -44,6 +44,16 @@ <funcs> <func> + <name name="add_predefined_functions" arity="1"/> + <fsummary>Add code for pre-defined functions.</fsummary> + <desc> + <p>Adds to <c><anno>Forms</anno></c> the code for the standard + pre-defined functions (such as <c>module_info/0</c>) that are + to be included in every module.</p> + </desc> + </func> + + <func> <name name="arith_op" arity="2"/> <fsummary>Test for an arithmetic operator.</fsummary> <desc> diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl index ebcbc54ab1..ab47f05320 100644 --- a/lib/stdlib/src/erl_expand_records.erl +++ b/lib/stdlib/src/erl_expand_records.erl @@ -17,7 +17,8 @@ %% %% %CopyrightEnd% %% -%% Purpose : Expand records into tuples. +%% Purpose: Expand records into tuples. Also add explicit module +%% names to calls to imported functions and BIFs. %% N.B. Although structs (tagged tuples) are not yet allowed in the %% language there is code included in pattern/2 and expr/3 (commented out) @@ -31,7 +32,7 @@ -record(exprec, {compile=[], % Compile flags vcount=0, % Variable counter - imports=[], % Imports + calltype=#{}, % Call types records=dict:new(), % Record definitions strict_ra=[], % strict record accesses checked_ra=[] % successfully accessed records @@ -46,22 +47,34 @@ %% erl_lint without errors. module(Fs0, Opts0) -> Opts = compiler_options(Fs0) ++ Opts0, - St0 = #exprec{compile = Opts}, + Calltype = init_calltype(Fs0), + St0 = #exprec{compile = Opts, calltype = Calltype}, {Fs,_St} = forms(Fs0, St0), Fs. compiler_options(Forms) -> lists:flatten([C || {attribute,_,compile,C} <- Forms]). +init_calltype(Forms) -> + Locals = [{{Name,Arity},local} || {function,_,Name,Arity,_} <- Forms], + Ctype = maps:from_list(Locals), + init_calltype_imports(Forms, Ctype). + +init_calltype_imports([{attribute,_,import,{Mod,Fs}}|T], Ctype0) -> + true = is_atom(Mod), + Ctype = foldl(fun(FA, Acc) -> + Acc#{FA=>{imported,Mod}} + end, Ctype0, Fs), + init_calltype_imports(T, Ctype); +init_calltype_imports([_|T], Ctype) -> + init_calltype_imports(T, Ctype); +init_calltype_imports([], Ctype) -> Ctype. + forms([{attribute,_,record,{Name,Defs}}=Attr | Fs], St0) -> NDefs = normalise_fields(Defs), St = St0#exprec{records=dict:store(Name, NDefs, St0#exprec.records)}, {Fs1, St1} = forms(Fs, St), {[Attr | Fs1], St1}; -forms([{attribute,L,import,Is} | Fs0], St0) -> - St1 = import(Is, St0), - {Fs,St2} = forms(Fs0, St1), - {[{attribute,L,import,Is} | Fs], St2}; forms([{function,L,N,A,Cs0} | Fs0], St0) -> {Cs,St1} = clauses(Cs0, St0), {Fs,St2} = forms(Fs0, St1), @@ -334,8 +347,16 @@ expr({'receive',Line,Cs0,To0,ToEs0}, St0) -> {ToEs,St2} = exprs(ToEs0, St1), {Cs,St3} = clauses(Cs0, St2), {{'receive',Line,Cs,To,ToEs},St3}; -expr({'fun',_,{function,_F,_A}}=Fun, St) -> - {Fun,St}; +expr({'fun',Lf,{function,F,A}}=Fun0, St0) -> + case erl_internal:bif(F, A) of + true -> + {As,St1} = new_vars(A, Lf, St0), + Cs = [{clause,Lf,As,[],[{call,Lf,{atom,Lf,F},As}]}], + Fun = {'fun',Lf,{clauses,Cs}}, + expr(Fun, St1); + false -> + {Fun0,St0} + end; expr({'fun',_,{function,_M,_F,_A}}=Fun, St) -> {Fun,St}; expr({'fun',Line,{clauses,Cs0}}, St0) -> @@ -352,14 +373,30 @@ expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}}, expr({call,Line,{tuple,_,[{atom,_,erlang},{atom,_,is_record}]}, [A,{atom,_,Name}]}, St) -> record_test(Line, A, Name, St); +expr({call,Line,{atom,_La,record_info},[_,_]=As0}, St0) -> + {As,St1} = expr_list(As0, St0), + record_info_call(Line, As, St1); expr({call,Line,{atom,_La,N}=Atom,As0}, St0) -> {As,St1} = expr_list(As0, St0), Ar = length(As), - case {N,Ar} =:= {record_info,2} andalso not imported(N, Ar, St1) of - true -> - record_info_call(Line, As, St1); - false -> - {{call,Line,Atom,As},St1} + NA = {N,Ar}, + case St0#exprec.calltype of + #{NA := local} -> + {{call,Line,Atom,As},St1}; + #{NA := {imported,Module}} -> + ModAtom = {atom,Line,Module}, + {{call,Line,{remote,Line,ModAtom,Atom},As},St1}; + _ -> + case erl_internal:bif(N, Ar) of + true -> + ModAtom = {atom,Line,erlang}, + {{call,Line,{remote,Line,ModAtom,Atom},As},St1}; + false -> + %% Call to a module_info/0,1 or one of the + %% pseudo-functions in the shell. Leave it as + %% a local call. + {{call,Line,Atom,As},St1} + end end; expr({call,Line,{remote,Lr,M,F},As0}, St0) -> {[M1,F1 | As1],St1} = expr_list([M,F | As0], St0), @@ -769,6 +806,13 @@ bin_element({bin_element,Line,Expr,Size,Type}, {Es,St0}) -> end, {[{bin_element,Line,Expr1,Size1,Type} | Es],St2}. +new_vars(N, L, St) -> new_vars(N, L, St, []). + +new_vars(N, L, St0, Vs) when N > 0 -> + {V,St1} = new_var(L, St0), + new_vars(N-1, L, St1, [V|Vs]); +new_vars(0, _L, St, Vs) -> {Vs,St}. + new_var(L, St0) -> {New,St1} = new_var_name(St0), {{var,L,New},St1}. @@ -783,18 +827,6 @@ make_list(Ts, Line) -> call_error(L, R) -> {call,L,{remote,L,{atom,L,erlang},{atom,L,error}},[R]}. -import({Mod,Fs}, St) -> - St#exprec{imports=add_imports(Mod, Fs, St#exprec.imports)}; -import(_Mod0, St) -> - St. - -add_imports(Mod, [F | Fs], Is) -> - add_imports(Mod, Fs, orddict:store(F, Mod, Is)); -add_imports(_, [], Is) -> Is. - -imported(F, A, St) -> - orddict:is_key({F,A}, St#exprec.imports). - %%% %%% Replace is_record/3 in guards with matching if possible. %%% diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index 46059f42a5..5d6aa0ebe8 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -54,6 +54,8 @@ -export([is_type/2]). +-export([add_predefined_functions/1]). + %%--------------------------------------------------------------------------- %% Erlang builtin functions allowed in guards. @@ -569,3 +571,68 @@ is_type(term, 0) -> true; is_type(timeout, 0) -> true; is_type(tuple, 0) -> true; is_type(_, _) -> false. + +%%% +%%% Add and export the pre-defined functions: +%%% +%%% module_info/0 +%%% module_info/1 +%%% behaviour_info/1 (optional) +%%% + +-spec add_predefined_functions(Forms) -> UpdatedForms when + Forms :: [erl_parse:abstract_form() | erl_parse:form_info()], + UpdatedForms :: [erl_parse:abstract_form() | erl_parse:form_info()]. + +add_predefined_functions(Forms) -> + Forms ++ predefined_functions(Forms). + +predefined_functions(Forms) -> + Attrs = [{Name,Val} || {attribute,_,Name,Val} <- Forms], + {module,Mod} = lists:keyfind(module, 1, Attrs), + Callbacks = [Callback || {callback,Callback} <- Attrs], + OptionalCallbacks = get_optional_callbacks(Attrs), + Mpf1 = module_predef_func_beh_info(Callbacks, OptionalCallbacks), + Mpf2 = module_predef_funcs_mod_info(Mod), + Mpf = [erl_parse:new_anno(F) || F <- Mpf1++Mpf2], + Exp = [{F,A} || {function,_,F,A,_} <- Mpf], + [{attribute,0,export,Exp}|Mpf]. + +get_optional_callbacks(Attrs) -> + L = [O || {optional_callbacks,O} <- Attrs, is_fa_list(O)], + lists:append(L). + +is_fa_list([{FuncName, Arity}|L]) + when is_atom(FuncName), is_integer(Arity), Arity >= 0 -> + is_fa_list(L); +is_fa_list([]) -> true; +is_fa_list(_) -> false. + +module_predef_func_beh_info([], _) -> + []; +module_predef_func_beh_info(Callbacks0, OptionalCallbacks) -> + Callbacks = [FA || {{_,_}=FA,_} <- Callbacks0], + List = make_list(Callbacks), + OptionalList = make_list(OptionalCallbacks), + [{function,0,behaviour_info,1, + [{clause,0,[{atom,0,callbacks}],[],[List]}, + {clause,0,[{atom,0,optional_callbacks}],[],[OptionalList]}]}]. + +make_list([]) -> {nil,0}; +make_list([{Name,Arity}|Rest]) -> + {cons,0, + {tuple,0, + [{atom,0,Name}, + {integer,0,Arity}]}, + make_list(Rest)}. + +module_predef_funcs_mod_info(Mod) -> + ModAtom = {atom,0,Mod}, + [{function,0,module_info,0, + [{clause,0,[],[], + [{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}}, + [ModAtom]}]}]}, + {function,0,module_info,1, + [{clause,0,[{var,0,'X'}],[], + [{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}}, + [ModAtom,{var,0,'X'}]}]}]}]. diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 85b2816451..549179da68 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -1567,19 +1567,6 @@ anno_from_term(Term) -> map_anno(fun erl_anno:from_term/1, Term). %% Forms. -%% Recognize what sys_pre_expand does: -modify_anno1({'fun',A,F,{_,_,_}=Id}, Ac, Mf) -> - {A1,Ac1} = Mf(A, Ac), - {F1,Ac2} = modify_anno1(F, Ac1, Mf), - {{'fun',A1,F1,Id},Ac2}; -modify_anno1({named_fun,A,N,F,{_,_,_}=Id}, Ac, Mf) -> - {A1,Ac1} = Mf(A, Ac), - {F1,Ac2} = modify_anno1(F, Ac1, Mf), - {{named_fun,A1,N,F1,Id},Ac2}; -modify_anno1({attribute,A,N,[V]}, Ac, Mf) -> - {{attribute,A1,N1,V1},Ac1} = modify_anno1({attribute,A,N,V}, Ac, Mf), - {{attribute,A1,N1,[V1]},Ac1}; -%% End of sys_pre_expand special forms. modify_anno1({function,F,A}, Ac, _Mf) -> {{function,F,A},Ac}; modify_anno1({function,M,F,A}, Ac, Mf) -> diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl index 0db63b81f4..869b003668 100644 --- a/lib/stdlib/src/qlc_pt.erl +++ b/lib/stdlib/src/qlc_pt.erl @@ -1870,7 +1870,8 @@ prep_expr(E, F, S, BF, Imported) -> unify_column(Frame, Var, Col, BindFun, Imported) -> A = anno0(), - Call = {call,A,{atom,A,element},[{integer,A,Col}, {var,A,Var}]}, + Call = {call,A,{remote,A,{atom,A,erlang},{atom,A,element}}, + [{integer,A,Col}, {var,A,Var}]}, element_calls(Call, Frame, BindFun, Imported). %% cons_tuple is used for representing {V1, ..., Vi | TupleTail}. @@ -1880,6 +1881,8 @@ unify_column(Frame, Var, Col, BindFun, Imported) -> %% about the size of the tuple is known. element_calls({call,_,{remote,_,{atom,_,erlang},{atom,_,element}}, [{integer,_,I},Term0]}, F0, BF, Imported) when I > 0 -> + %% Note: erl_expand_records ensures that all calls to element/2 + %% have an explicit "erlang:" prefix. TupleTail = unique_var(), VarsL = [unique_var() || _ <- lists:seq(1, I)], Vars = VarsL ++ TupleTail, @@ -1887,10 +1890,6 @@ element_calls({call,_,{remote,_,{atom,_,erlang},{atom,_,element}}, VarI = lists:nth(I, VarsL), {Term, F} = element_calls(Term0, F0, BF, Imported), {VarI, unify('=:=', Tuple, Term, F, BF, Imported)}; -element_calls({call,L1,{atom,_,element}=E,As}, F0, BF, Imported) -> - %% erl_expand_records should add "erlang:"... - element_calls({call,L1,{remote,L1,{atom,L1,erlang},E}, As}, F0, BF, - Imported); element_calls(T, F0, BF, Imported) when is_tuple(T) -> {L, F} = element_calls(tuple_to_list(T), F0, BF, Imported), {list_to_tuple(L), F}; diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index a103f6dc53..13c5662741 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -1166,19 +1166,21 @@ compile(Config, Tests) -> lists:foldl(F, [], Tests). compile_file(Config, Test0) -> - case compile_file(Config, Test0, ['E']) of + Test = ["-module(erl_pp_test).\n", + "-compile(export_all).\n", + Test0], + case compile_file(Config, Test, ['E']) of {ok, RootFile} -> File = RootFile ++ ".E", {ok, Bin0} = file:read_file(File), - Bin = strip_module_info(Bin0), %% A very simple check: just try to compile the output. - case compile_file(Config, Bin, []) of + case compile_file(Config, Bin0, []) of {ok, RootFile2} -> File2 = RootFile2 ++ ".E", {ok, Bin1} = file:read_file(File2), case Bin0 =:= Bin1 of true -> - test_max_line(binary_to_list(Bin)); + test_max_line(binary_to_list(Bin0)); false -> {error, file_contents_modified, {Bin0, Bin1}} end; @@ -1189,11 +1191,8 @@ compile_file(Config, Test0) -> Error end. -compile_file(Config, Test0, Opts0) -> +compile_file(Config, Test, Opts0) -> FileName = filename('erl_pp_test.erl', Config), - Test = list_to_binary(["-module(erl_pp_test). " - "-compile(export_all). ", - Test0]), Opts = [export_all,return,nowarn_unused_record,{outdir,?privdir} | Opts0], ok = file:write_file(FileName, Test), case compile:file(FileName, Opts) of @@ -1202,11 +1201,6 @@ compile_file(Config, Test0, Opts0) -> Error -> Error end. -strip_module_info(Bin) -> - {match, [{Start,_Len}|_]} = re:run(Bin, "module_info"), - <<R:Start/binary,_/binary>> = Bin, - R. - flat_expr1(Expr0) -> Expr = erl_parse:new_anno(Expr0), lists:flatten(erl_pp:expr(Expr)). diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl index c409a6949b..07eb6772db 100644 --- a/lib/stdlib/test/shell_SUITE.erl +++ b/lib/stdlib/test/shell_SUITE.erl @@ -573,7 +573,7 @@ otp_5327(Config) when is_list(Config) -> (catch evaluate(<<"<<32/unit:8>>.">>, [])), ok. -%% OTP-5435. sys_pre_expand not in the path. +%% OTP-5435. compiler application not in the path. otp_5435(Config) when is_list(Config) -> true = <<103133:64/float>> =:= evaluate(<<"<<103133:64/float>> = <<103133:64/float>>.">>, []), @@ -591,8 +591,9 @@ start_node(Name) -> otp_5435_2() -> true = code:del_path(compiler), - %% sys_pre_expand can no longer be found - %% OTP-5876. But erl_expand_records can! + %% Make sure record evaluation is not dependent on the compiler + %% application being in the path. + %% OTP-5876. [{attribute,_,record,{bar,_}},ok] = scan(<<"rd(foo,{bar}), rd(bar,{foo = (#foo{})#foo.bar}), diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl index 92c10cc306..e2db4f0148 100644 --- a/lib/tools/src/cover.erl +++ b/lib/tools/src/cover.erl @@ -2053,7 +2053,7 @@ munge_expr({bin_element,Line,Value,Size,TypeSpecifierList}, Vars) -> {MungedValue,Vars2} = munge_expr(Value, Vars), {MungedSize,Vars3} = munge_expr(Size, Vars2), {{bin_element,Line,MungedValue,MungedSize,TypeSpecifierList},Vars3}; -munge_expr(Form, Vars) -> % var|char|integer|float|string|atom|nil|eof|default +munge_expr(Form, Vars) -> {Form, Vars}. munge_exprs([Expr|Exprs], Vars, MungedExprs) when Vars#vars.is_guard=:=true, diff --git a/lib/tools/src/xref_base.erl b/lib/tools/src/xref_base.erl index f298a1ce81..8d2cc07e40 100644 --- a/lib/tools/src/xref_base.erl +++ b/lib/tools/src/xref_base.erl @@ -809,7 +809,8 @@ abst(File, Builtins, _Mode = functions) -> {exports,X0}, {attributes,A}]}} -> %% R9C- Forms0 = epp:interpret_file_attribute(Code), - {_,_,Forms,_} = sys_pre_expand:module(Forms0, []), + Forms1 = erl_expand_records:module(Forms0, []), + Forms = erl_internal:add_predefined_functions(Forms1), X = mfa_exports(X0, A, M), D = deprecated(A, X, M), xref_reader:module(M, Forms, Builtins, X, D); diff --git a/lib/tools/src/xref_reader.erl b/lib/tools/src/xref_reader.erl index 41b93caaeb..6c4a1c4d8e 100644 --- a/lib/tools/src/xref_reader.erl +++ b/lib/tools/src/xref_reader.erl @@ -42,17 +42,15 @@ %% experimental; -xref(FunEdge) is recognized. lattrs=[], % local calls, {{mfa(),mfa()},Line} xattrs=[], % external calls, -"- - battrs=[] % badly formed xref attributes, term(). + battrs=[] % badly formed xref attributes, term(). }). -include("xref.hrl"). -%% sys_pre_expand has modified the forms slightly compared to what -%% erl_id_trans recognizes. - %% The versions of the abstract code are as follows: -%% R7: abstract_v1 -%% R8: abstract_v2 +%% R7: abstract_v1 +%% R8: abstract_v2 +%% R9C: raw_abstract_v1 %% -> {ok, Module, {DefAt, CallAt, LC, XC, X, Attrs}, Unresolved}} | EXIT %% Attrs = {ALC, AXC, Bad} @@ -92,7 +90,12 @@ form({function, Anno, Name, Arity, Clauses}, S) -> Line = erl_anno:line(Anno), S2 = S1#xrefr{def_at = [{MFA,Line} | S#xrefr.def_at]}, S3 = clauses(Clauses, S2), - S3#xrefr{function = []}. + S3#xrefr{function = []}; +form(_, S) -> + %% OTP 20. Other uninteresting forms such as {eof, _} and {warning, _}. + %% Exposed because sys_pre_expand is no longer run. + S. + clauses(Cls, S) -> #xrefr{funvars = FunVars, matches = Matches} = S, @@ -109,6 +112,8 @@ clauses([{clause, _Line, _H, G, B} | Cs], FunVars, Matches, S) -> clauses([], _FunVars, _Matches, S) -> S. +attr(NotList, Ln, M, Fun, AL, AX, B, S) when not is_list(NotList) -> + attr([NotList], Ln, M, Fun, AL, AX, B, S); attr([E={From, To} | As], Ln, M, Fun, AL, AX, B, S) -> case mfa(From, M) of {_, _, MFA} when MFA =:= Fun; [] =:= Fun -> @@ -154,6 +159,15 @@ expr({'try',_Line,Es,Scs,Ccs,As}, S) -> S2 = clauses(Scs, S1), S3 = clauses(Ccs, S2), expr(As, S3); +expr({'fun', Line, {function,M,F,A}}, S) + when is_atom(M), is_atom(F), is_integer(A) -> + %% This is the old format for external funs, generated by a pre-R15 + %% compiler. Exposed in OTP 20 because sys_pre_expand is no longer + %% run. + Fun = {'fun', Line, {function, {atom,Line,M}, + {atom,Line,F}, + {integer,Line,A}}}, + expr(Fun, S); expr({'fun', Line, {function, {atom,_,Mod}, {atom,_,Name}, {integer,_,Arity}}}, S) -> @@ -168,14 +182,21 @@ expr({'fun', Line, {function, Mod, Name, _Arity}}, S) -> %% New format in R15. M:F/A (one or more variables). As = {var, Line, '_'}, external_call(erlang, apply, [Mod, Name, As], Line, true, S); +%% Only abstract_v1 and abstract_v2. expr({'fun', Line, {function, Name, Arity}, _Extra}, S) -> %% Added in R8. handle_call(local, S#xrefr.module, Name, Arity, Line, S); expr({'fun', _Line, {clauses, Cs}, _Extra}, S) -> clauses(Cs, S); -expr({named_fun, _Line, '_', Cs, _Extra}, S) -> +%% End abstract_v1 and abstract_v2. +expr({'fun', Line, {function, Name, Arity}}, S) -> + %% Added in OTP 20. + handle_call(local, S#xrefr.module, Name, Arity, Line, S); +expr({'fun', _Line, {clauses, Cs}}, S) -> + clauses(Cs, S); +expr({named_fun, _Line, '_', Cs}, S) -> clauses(Cs, S); -expr({named_fun, _Line, Name, Cs, _Extra}, S) -> +expr({named_fun, _Line, Name, Cs}, S) -> S1 = S#xrefr{funvars = [Name | S#xrefr.funvars]}, clauses(Cs, S1); expr({call, Line, {atom, _, Name}, As}, S) -> @@ -193,7 +214,12 @@ expr({match, _Line, {var,_,Var}, {'fun', _, {clauses, Cs}, _Extra}}, S) -> %% that are passed around by the "expansion" of list comprehension. S1 = S#xrefr{funvars = [Var | S#xrefr.funvars]}, clauses(Cs, S1); -expr({match, _Line, {var,_,Var}, {named_fun, _, _, _, _} = Fun}, S) -> +expr({match, _Line, {var,_,Var}, {'fun', _, {clauses, Cs}}}, S) -> + %% OTP 20. Exposed because sys_pre_expand is no longer run. + S1 = S#xrefr{funvars = [Var | S#xrefr.funvars]}, + clauses(Cs, S1); +expr({match, _Line, {var,_,Var}, {named_fun, _, _, _} = Fun}, S) -> + %% OTP 20. Exposed because sys_pre_expand is no longer run. S1 = S#xrefr{funvars = [Var | S#xrefr.funvars]}, expr(Fun, S1); expr({match, _Line, {var,_,Var}, E}, S) -> @@ -295,10 +321,17 @@ check_funarg(W, ArgsList, Line, S) -> expr(ArgsList, S1). funarg({'fun', _, _Clauses, _Extra}, _S) -> true; +funarg({'fun', _, {clauses, _}}, _S) -> + %% OTP 20. sys_pre_expand not run. + true; +funarg({'fun', _, {function, _, _}}, _S) -> + %% OTP 20. sys_pre_expand not run. + true; funarg({'fun', _, {function,_,_,_}}, _S) -> %% New abstract format for fun M:F/A in R15. true; -funarg({named_fun, _, _, _, _}, _S) -> +funarg({named_fun, _, _, _}, _S) -> + %% OTP 20. sys_pre_expand not run. true; funarg({var, _, Var}, S) -> member(Var, S#xrefr.funvars); funarg(_, _S) -> false. diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl index 01dbac6ecb..f308ea1204 100644 --- a/lib/tools/test/xref_SUITE.erl +++ b/lib/tools/test/xref_SUITE.erl @@ -1222,6 +1222,9 @@ read2(Conf) when is_list(Conf) -> f() -> %% Duplicated unresolved calls are ignored: (f())(foo,bar),(f())(foo,bar). % POS1 + + %% Warning forms must be ignored. + -warning(must_not_crash). ">>, ok = file:write_file(File, Test), {ok, read2} = compile:file(File, [debug_info,{outdir,Dir}]), |