aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler')
-rw-r--r--lib/compiler/doc/src/Makefile11
-rw-r--r--lib/compiler/doc/src/book.xml3
-rw-r--r--lib/compiler/doc/src/compile.xml16
-rw-r--r--lib/compiler/doc/src/internal.xml38
-rw-r--r--lib/compiler/src/Makefile3
-rw-r--r--lib/compiler/src/beam_asm.erl26
-rw-r--r--lib/compiler/src/beam_ssa_pre_codegen.erl141
-rw-r--r--lib/compiler/src/beam_validator.erl22
-rw-r--r--lib/compiler/src/cerl.erl16
-rw-r--r--lib/compiler/src/cerl_clauses.erl4
-rw-r--r--lib/compiler/src/core_parse.hrl107
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl25
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl35
13 files changed, 304 insertions, 143 deletions
diff --git a/lib/compiler/doc/src/Makefile b/lib/compiler/doc/src/Makefile
index 32f150eef8..2fb163b9e7 100644
--- a/lib/compiler/doc/src/Makefile
+++ b/lib/compiler/doc/src/Makefile
@@ -31,6 +31,7 @@ APPLICATION=compiler
# Release directory specification
# ----------------------------------------------------
RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+COMPILER_DIR = $(ERL_TOP)/lib/compiler/src
# ----------------------------------------------------
# Target Specs
@@ -38,7 +39,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = compile.xml
-XML_PART_FILES =
+XML_PART_FILES = internal.xml
XML_CHAPTER_FILES = notes.xml
BOOK_FILES = book.xml
@@ -49,6 +50,9 @@ XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
+XML_INTERNAL_FILES = \
+ cerl.xml cerl_trees.xml cerl_clauses.xml
+
# ----------------------------------------------------
HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
@@ -62,6 +66,8 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+XML_GEN_FILES = $(XML_INTERNAL_FILES:%=$(XMLDIR)/%)
+
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -85,6 +91,9 @@ man: $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
+$(XML_INTERNAL_FILES:%=$(XMLDIR)/%): $(COMPILER_DIR)/$(@:$(XMLDIR)/%.xml=%.erl)
+ $(gen_verbose)escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(COMPILER_VSN) -dir $(XMLDIR) $(COMPILER_DIR)/$(@:$(XMLDIR)/%.xml=%.erl)
+
debug opt:
clean clean_docs:
diff --git a/lib/compiler/doc/src/book.xml b/lib/compiler/doc/src/book.xml
index af6b4cf47a..d101d40cb4 100644
--- a/lib/compiler/doc/src/book.xml
+++ b/lib/compiler/doc/src/book.xml
@@ -38,6 +38,9 @@
<applications>
<xi:include href="ref_man.xml"/>
</applications>
+ <internals>
+ <xi:include href="internal.xml"/>
+ </internals>
<releasenotes>
<xi:include href="notes.xml"/>
</releasenotes>
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index 5219ba0f5d..549b1049d8 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -632,6 +632,22 @@ module.beam: module.erl \
to be deprecated.</p>
</item>
+ <tag><c>nowarn_removed</c></tag>
+ <item>
+ <p>Turns off warnings for calls to functions that have
+ been removed. Default is to emit warnings for every call
+ to a function known by the compiler to have been recently
+ removed from Erlang/OTP.</p>
+ </item>
+
+ <tag><c>{nowarn_removed, ModulesOrMFAs}</c></tag>
+ <item>
+ <p>Turns off warnings for calls to modules or functions
+ that have been removed. Default is to emit warnings for
+ every call to a function known by the compiler to have
+ been recently removed from Erlang/OTP.</p>
+ </item>
+
<tag><c>nowarn_obsolete_guard</c></tag>
<item>
<p>Turns off warnings for calls to old type testing BIFs,
diff --git a/lib/compiler/doc/src/internal.xml b/lib/compiler/doc/src/internal.xml
new file mode 100644
index 0000000000..f24b363c1c
--- /dev/null
+++ b/lib/compiler/doc/src/internal.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<internal xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2018</year><year>2018</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>Compiler Internal Documentation</title>
+ <prepared>Lukas Larsson</prepared>
+ <docno></docno>
+ <date>2018-07-07</date>
+ <rev>1.0.0</rev>
+ <file>internal.xml</file>
+ </header>
+ <description>
+ </description>
+ <xi:include href="cerl.xml"/>
+ <xi:include href="cerl_trees.xml"/>
+ <xi:include href="cerl_clauses.xml"/>
+</internal>
+
diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile
index 9f8d63baa1..87b0d345f2 100644
--- a/lib/compiler/src/Makefile
+++ b/lib/compiler/src/Makefile
@@ -194,13 +194,16 @@ $(EBIN)/beam_disasm.beam: $(EGEN)/beam_opcodes.hrl beam_disasm.hrl
$(EBIN)/beam_listing.beam: core_parse.hrl v3_kernel.hrl beam_ssa.hrl
$(EBIN)/beam_kernel_to_ssa.beam: v3_kernel.hrl beam_ssa.hrl
$(EBIN)/beam_ssa.beam: beam_ssa.hrl
+$(EBIN)/beam_ssa_bsm.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_codegen.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_dead.beam: beam_ssa.hrl
+$(EBIN)/beam_ssa_funs.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_lint.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_opt.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_pp.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_pre_codegen.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_recv.beam: beam_ssa.hrl
+$(EBIN)/beam_ssa_share.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_type.beam: beam_ssa.hrl
$(EBIN)/cerl.beam: core_parse.hrl
$(EBIN)/compile.beam: core_parse.hrl ../../stdlib/include/erl_compile.hrl
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index bc1290f6fd..df09dcb06c 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -407,14 +407,14 @@ encode_arg({atom, Atom}, Dict0) when is_atom(Atom) ->
{Index, Dict} = beam_dict:atom(Atom, Dict0),
{encode(?tag_a, Index), Dict};
encode_arg({integer, N}, Dict) ->
- %% Conservatily assume that all integers whose absolute
+ %% Conservatively assume that all integers whose absolute
%% value is greater than 1 bsl 128 will be bignums in
%% the runtime system.
if
N >= 1 bsl 128 ->
- encode_arg({literal, N}, Dict);
+ encode_literal(N, Dict);
N =< -(1 bsl 128) ->
- encode_arg({literal, N}, Dict);
+ encode_literal(N, Dict);
true ->
{encode(?tag_i, N), Dict}
end;
@@ -434,7 +434,7 @@ encode_arg({list, List}, Dict0) ->
{L, Dict} = encode_list(List, Dict0, []),
{[encode(?tag_z, 1), encode(?tag_u, length(List))|L], Dict};
encode_arg({float, Float}, Dict) when is_float(Float) ->
- encode_arg({literal,Float}, Dict);
+ encode_literal(Float, Dict);
encode_arg({fr,Fr}, Dict) ->
{[encode(?tag_z, 2),encode(?tag_u, Fr)], Dict};
encode_arg({field_flags,Flags0}, Dict) ->
@@ -442,12 +442,24 @@ encode_arg({field_flags,Flags0}, Dict) ->
{encode(?tag_u, Flags), Dict};
encode_arg({alloc,List}, Dict) ->
encode_alloc_list(List, Dict);
-encode_arg({literal,Lit}, Dict0) ->
- {Index,Dict} = beam_dict:literal(Lit, Dict0),
- {[encode(?tag_z, 4),encode(?tag_u, Index)],Dict};
+encode_arg({literal,Lit}, Dict) ->
+ if
+ Lit =:= [] ->
+ encode_arg(nil, Dict);
+ is_atom(Lit) ->
+ encode_arg({atom,Lit}, Dict);
+ is_integer(Lit) ->
+ encode_arg({integer,Lit}, Dict);
+ true ->
+ encode_literal(Lit, Dict)
+ end;
encode_arg(Int, Dict) when is_integer(Int) ->
{encode(?tag_u, Int),Dict}.
+encode_literal(Literal, Dict0) ->
+ {Index,Dict} = beam_dict:literal(Literal, Dict0),
+ {[encode(?tag_z, 4),encode(?tag_u, Index)],Dict}.
+
%%flag_to_bit(aligned) -> 16#01; %% No longer useful.
flag_to_bit(little) -> 16#02;
flag_to_bit(big) -> 16#00;
diff --git a/lib/compiler/src/beam_ssa_pre_codegen.erl b/lib/compiler/src/beam_ssa_pre_codegen.erl
index bad43a9c4e..bf99e8fc26 100644
--- a/lib/compiler/src/beam_ssa_pre_codegen.erl
+++ b/lib/compiler/src/beam_ssa_pre_codegen.erl
@@ -342,21 +342,22 @@ make_save_point_dict_1([], Ctx, I, Acc) ->
[{Ctx,I}|Acc].
bs_restores([{L,#b_blk{is=Is,last=Last}}|Bs], CtxChain, D0, Rs0) ->
- FPos = case D0 of
- #{L:=Pos0} -> Pos0;
- #{} -> #{}
- end,
- {SPos,Rs} = bs_restores_is(Is, CtxChain, FPos, Rs0),
- D = bs_update_successors(Last, SPos, FPos, D0),
+ InPos = maps:get(L, D0, #{}),
+ {SuccPos, FailPos, Rs} = bs_restores_is(Is, CtxChain, InPos, InPos, Rs0),
+
+ D = bs_update_successors(Last, SuccPos, FailPos, D0),
bs_restores(Bs, CtxChain, D, Rs);
bs_restores([], _, _, Rs) -> Rs.
bs_update_successors(#b_br{succ=Succ,fail=Fail}, SPos, FPos, D) ->
join_positions([{Succ,SPos},{Fail,FPos}], D);
-bs_update_successors(#b_switch{fail=Fail,list=List}, SPos, _FPos, D) ->
+bs_update_successors(#b_switch{fail=Fail,list=List}, SPos, FPos, D) ->
+ SPos = FPos, %Assertion.
Update = [{L,SPos} || {_,L} <- List] ++ [{Fail,SPos}],
join_positions(Update, D);
-bs_update_successors(#b_ret{}, _, _, D) -> D.
+bs_update_successors(#b_ret{}, SPos, FPos, D) ->
+ SPos = FPos, %Assertion.
+ D.
join_positions([{L,MapPos0}|T], D) ->
case D of
@@ -382,75 +383,91 @@ join_positions_1(MapPos0, MapPos1) ->
end, MapPos1),
maps:merge(MapPos0, MapPos2).
+%%
+%% Updates the restore and position maps according to the given instructions.
+%%
+%% Note that positions may be updated even when a match fails; if a match
+%% requires a restore, the position at the fail block will be the position
+%% we've *restored to* and not the one we entered the current block with.
+%%
+
bs_restores_is([#b_set{op=bs_start_match,dst=Start}|Is],
- CtxChain, PosMap0, Rs) ->
- PosMap = PosMap0#{Start=>Start},
- bs_restores_is(Is, CtxChain, PosMap, Rs);
+ CtxChain, SPos0, FPos, Rs) ->
+ %% We only allow one match per block.
+ SPos0 = FPos, %Assertion.
+ SPos = SPos0#{Start=>Start},
+ bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
bs_restores_is([#b_set{op=bs_match,dst=NewPos,args=Args}=I|Is],
- CtxChain, PosMap0, Rs0) ->
+ CtxChain, SPos0, FPos0, Rs0) ->
+ SPos0 = FPos0, %Assertion.
Start = bs_subst_ctx(NewPos, CtxChain),
[_,FromPos|_] = Args,
- case PosMap0 of
+ case SPos0 of
#{Start:=FromPos} ->
%% Same position, no restore needed.
- PosMap = case bs_match_type(I) of
+ SPos = case bs_match_type(I) of
plain ->
%% Update position to new position.
- PosMap0#{Start:=NewPos};
+ SPos0#{Start:=NewPos};
_ ->
%% Position will not change (test_unit
%% instruction or no instruction at
%% all).
- PosMap0#{Start:=FromPos}
+ SPos0#{Start:=FromPos}
end,
- bs_restores_is(Is, CtxChain, PosMap, Rs0);
+ bs_restores_is(Is, CtxChain, SPos, FPos0, Rs0);
#{Start:=_} ->
%% Different positions, might need a restore instruction.
case bs_match_type(I) of
none ->
- %% The tail test will be optimized away.
- %% No need to do a restore.
- PosMap = PosMap0#{Start:=FromPos},
- bs_restores_is(Is, CtxChain, PosMap, Rs0);
+ %% This is a tail test that will be optimized away.
+ %% There's no need to do a restore, and all
+ %% positions are unchanged.
+ bs_restores_is(Is, CtxChain, SPos0, FPos0, Rs0);
test_unit ->
%% This match instruction will be replaced by
%% a test_unit instruction. We will need a
%% restore. The new position will be the position
%% restored to (NOT NewPos).
- PosMap = PosMap0#{Start:=FromPos},
+ SPos = SPos0#{Start:=FromPos},
+ FPos = FPos0#{Start:=FromPos},
Rs = Rs0#{NewPos=>{Start,FromPos}},
- bs_restores_is(Is, CtxChain, PosMap, Rs);
+ bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
plain ->
%% Match or skip. Position will be changed.
- PosMap = PosMap0#{Start:=NewPos},
+ SPos = SPos0#{Start:=NewPos},
+ FPos = FPos0#{Start:=FromPos},
Rs = Rs0#{NewPos=>{Start,FromPos}},
- bs_restores_is(Is, CtxChain, PosMap, Rs)
+ bs_restores_is(Is, CtxChain, SPos, FPos, Rs)
end
end;
bs_restores_is([#b_set{op=bs_extract,args=[FromPos|_]}|Is],
- CtxChain, PosMap, Rs) ->
+ CtxChain, SPos, FPos, Rs) ->
Start = bs_subst_ctx(FromPos, CtxChain),
- #{Start:=FromPos} = PosMap, %Assertion.
- bs_restores_is(Is, CtxChain, PosMap, Rs);
+ #{Start:=FromPos} = SPos, %Assertion.
+ #{Start:=FromPos} = FPos, %Assertion.
+ bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
bs_restores_is([#b_set{op=call,dst=Dst,args=Args}|Is],
- CtxChain, PosMap0, Rs0) ->
- {Rs,PosMap1} = bs_restore_args(Args, PosMap0, CtxChain, Dst, Rs0),
- PosMap = bs_invalidate_pos(Args, PosMap1, CtxChain),
- bs_restores_is(Is, CtxChain, PosMap, Rs);
-bs_restores_is([#b_set{op=landingpad}|Is], CtxChain, PosMap0, Rs) ->
+ CtxChain, SPos0, FPos0, Rs0) ->
+ {Rs, SPos1, FPos1} = bs_restore_args(Args, SPos0, FPos0, CtxChain, Dst, Rs0),
+ {SPos, FPos} = bs_invalidate_pos(Args, SPos1, FPos1, CtxChain),
+ bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
+bs_restores_is([#b_set{op=landingpad}|Is], CtxChain, SPos0, FPos0, Rs) ->
%% We can land here from any point, so all positions are invalid.
- PosMap = maps:map(fun(_Start,_Pos) -> unknown end, PosMap0),
- bs_restores_is(Is, CtxChain, PosMap, Rs);
+ Invalidate = fun(_Start,_Pos) -> unknown end,
+ SPos = maps:map(Invalidate, SPos0),
+ FPos = maps:map(Invalidate, FPos0),
+ bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
bs_restores_is([#b_set{op=Op,dst=Dst,args=Args}|Is],
- CtxChain, PosMap0, Rs0)
+ CtxChain, SPos0, FPos0, Rs0)
when Op =:= bs_test_tail;
Op =:= bs_get_tail ->
- {Rs,PosMap} = bs_restore_args(Args, PosMap0, CtxChain, Dst, Rs0),
- bs_restores_is(Is, CtxChain, PosMap, Rs);
-bs_restores_is([_|Is], CtxChain, PosMap, Rs) ->
- bs_restores_is(Is, CtxChain, PosMap, Rs);
-bs_restores_is([], _CtxChain, PosMap, Rs) ->
- {PosMap,Rs}.
+ {Rs, SPos, FPos} = bs_restore_args(Args, SPos0, FPos0, CtxChain, Dst, Rs0),
+ bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
+bs_restores_is([_|Is], CtxChain, SPos, FPos, Rs) ->
+ bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
+bs_restores_is([], _CtxChain, SPos, FPos, Rs) ->
+ {SPos, FPos, Rs}.
bs_match_type(#b_set{args=[#b_literal{val=skip},_Ctx,
#b_literal{val=binary},_Flags,
@@ -464,40 +481,42 @@ bs_match_type(_) ->
%% Call instructions leave the match position in an undefined state,
%% requiring us to invalidate each affected argument.
-bs_invalidate_pos([#b_var{}=Arg|Args], PosMap0, CtxChain) ->
+bs_invalidate_pos([#b_var{}=Arg|Args], SPos0, FPos0, CtxChain) ->
Start = bs_subst_ctx(Arg, CtxChain),
- case PosMap0 of
+ case SPos0 of
#{Start:=_} ->
- PosMap = PosMap0#{Start:=unknown},
- bs_invalidate_pos(Args, PosMap, CtxChain);
+ SPos = SPos0#{Start:=unknown},
+ FPos = FPos0#{Start:=unknown},
+ bs_invalidate_pos(Args, SPos, FPos, CtxChain);
#{} ->
%% Not a match context.
- bs_invalidate_pos(Args, PosMap0, CtxChain)
+ bs_invalidate_pos(Args, SPos0, FPos0, CtxChain)
end;
-bs_invalidate_pos([_|Args], PosMap, CtxChain) ->
- bs_invalidate_pos(Args, PosMap, CtxChain);
-bs_invalidate_pos([], PosMap, _CtxChain) ->
- PosMap.
+bs_invalidate_pos([_|Args], SPos, FPos, CtxChain) ->
+ bs_invalidate_pos(Args, SPos, FPos, CtxChain);
+bs_invalidate_pos([], SPos, FPos, _CtxChain) ->
+ {SPos, FPos}.
-bs_restore_args([#b_var{}=Arg|Args], PosMap0, CtxChain, Dst, Rs0) ->
+bs_restore_args([#b_var{}=Arg|Args], SPos0, FPos0, CtxChain, Dst, Rs0) ->
Start = bs_subst_ctx(Arg, CtxChain),
- case PosMap0 of
+ case SPos0 of
#{Start:=Arg} ->
%% Same position, no restore needed.
- bs_restore_args(Args, PosMap0, CtxChain, Dst, Rs0);
+ bs_restore_args(Args, SPos0, FPos0, CtxChain, Dst, Rs0);
#{Start:=_} ->
%% Different positions, need a restore instruction.
- PosMap = PosMap0#{Start:=Arg},
+ SPos = SPos0#{Start:=Arg},
+ FPos = FPos0#{Start:=Arg},
Rs = Rs0#{Dst=>{Start,Arg}},
- bs_restore_args(Args, PosMap, CtxChain, Dst, Rs);
+ bs_restore_args(Args, SPos, FPos, CtxChain, Dst, Rs);
#{} ->
%% Not a match context.
- bs_restore_args(Args, PosMap0, CtxChain, Dst, Rs0)
+ bs_restore_args(Args, SPos0, FPos0, CtxChain, Dst, Rs0)
end;
-bs_restore_args([_|Args], PosMap, CtxChain, Dst, Rs) ->
- bs_restore_args(Args, PosMap, CtxChain, Dst, Rs);
-bs_restore_args([], PosMap, _CtxChain, _Dst, Rs) ->
- {Rs,PosMap}.
+bs_restore_args([_|Args], SPos, FPos, CtxChain, Dst, Rs) ->
+ bs_restore_args(Args, SPos, FPos, CtxChain, Dst, Rs);
+bs_restore_args([], SPos, FPos, _CtxChain, _Dst, Rs) ->
+ {Rs,SPos,FPos}.
%% Insert all bs_save and bs_restore instructions.
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index efd2be94cb..09a5a6c104 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -1604,8 +1604,13 @@ infer_types_1(#value{op={bif,'=:='},args=[LHS,RHS]}) ->
end;
infer_types_1(#value{op={bif,element},args=[{integer,Index}=Key,Tuple]}) ->
fun(Val, S) ->
- Type = get_term_type(Val, S),
- update_type(fun meet/2,{tuple,[Index],#{ Key => Type }}, Tuple, S)
+ case is_value_alive(Tuple, S) of
+ true ->
+ Type = {tuple,[Index], #{ Key => get_term_type(Val, S) }},
+ update_type(fun meet/2, Type, Tuple, S);
+ false ->
+ S
+ end
end;
infer_types_1(#value{op={bif,is_atom},args=[Src]}) ->
infer_type_test_bif({atom,[]}, Src);
@@ -1629,7 +1634,10 @@ infer_types_1(#value{op={bif,is_tuple},args=[Src]}) ->
infer_type_test_bif({tuple,[0],#{}}, Src);
infer_types_1(#value{op={bif,tuple_size}, args=[Tuple]}) ->
fun({integer,Arity}, S) ->
- update_type(fun meet/2, {tuple,Arity,#{}}, Tuple, S);
+ case is_value_alive(Tuple, S) of
+ true -> update_type(fun meet/2, {tuple,Arity,#{}}, Tuple, S);
+ false -> S
+ end;
(_, S) -> S
end;
infer_types_1(_) ->
@@ -1637,7 +1645,10 @@ infer_types_1(_) ->
infer_type_test_bif(Type, Src) ->
fun({atom,true}, S) ->
- update_type(fun meet/2, Type, Src, S);
+ case is_value_alive(Src, S) of
+ true -> update_type(fun meet/2, Type, Src, S);
+ false -> S
+ end;
(_, S) ->
S
end.
@@ -2274,6 +2285,9 @@ get_raw_type(#value_ref{}=Ref, #vst{current=#st{vs=Vs}}) ->
get_raw_type(Src, #vst{}) ->
get_literal_type(Src).
+is_value_alive(#value_ref{}=Ref, #vst{current=#st{vs=Vs}}) ->
+ is_map_key(Ref, Vs).
+
get_literal_type(nil=T) -> T;
get_literal_type({atom,A}=T) when is_atom(A) -> T;
get_literal_type({float,F}=T) when is_float(F) -> T;
diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl
index fce23bfd68..62cd5b5120 100644
--- a/lib/compiler/src/cerl.erl
+++ b/lib/compiler/src/cerl.erl
@@ -2157,12 +2157,16 @@ values_arity(Node) ->
%% @spec c_binary(Segments::[cerl()]) -> cerl()
%%
-%% @doc Creates an abstract binary-template. A binary object is a
-%% sequence of 8-bit bytes. It is specified by zero or more bit-string
-%% template <em>segments</em> of arbitrary lengths (in number of bits),
-%% such that the sum of the lengths is evenly divisible by 8. If
-%% <code>Segments</code> is <code>[S1, ..., Sn]</code>, the result
-%% represents "<code>#{<em>S1</em>, ..., <em>Sn</em>}#</code>". All the
+
+%% @doc Creates an abstract binary-template. A binary object is in
+%% this context a sequence of an arbitrary number of bits. (The number
+%% of bits used to be evenly divisible by 8, but after the
+%% introduction of bit strings in the Erlang language, the choice was
+%% made to use the binary template for all bit strings.) It is
+%% specified by zero or more bit-string template <em>segments</em> of
+%% arbitrary lengths (in number of bits). If <code>Segments</code> is
+%% <code>[S1, ..., Sn]</code>, the result represents
+%% "<code>#{<em>S1</em>, ..., <em>Sn</em>}#</code>". All the
%% <code>Si</code> must have type <code>bitstr</code>.
%%
%% @see ann_c_binary/2
diff --git a/lib/compiler/src/cerl_clauses.erl b/lib/compiler/src/cerl_clauses.erl
index fa5104c01b..3fd7ddd181 100644
--- a/lib/compiler/src/cerl_clauses.erl
+++ b/lib/compiler/src/cerl_clauses.erl
@@ -14,8 +14,8 @@
%% @author Richard Carlsson <[email protected]>
%% @doc Utility functions for Core Erlang case/receive clauses.
%%
-%% <p>Syntax trees are defined in the module <a
-%% href=""><code>cerl</code></a>.</p>
+%% <p>Syntax trees are defined in the module
+%% <a href="cerl"><code>cerl</code></a>.</p>
%%
%% @type cerl() = cerl:cerl()
diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl
index 83a6f0179c..90c796d3d9 100644
--- a/lib/compiler/src/core_parse.hrl
+++ b/lib/compiler/src/core_parse.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2019. 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.
@@ -29,81 +29,82 @@
%% The record definitions appear alphabetically
--record(c_alias, {anno=[], var, % var :: Tree,
- pat}). % pat :: Tree
+-record(c_alias, {anno=[] :: list(), var :: cerl:cerl(),
+ pat :: cerl:cerl()}).
--record(c_apply, {anno=[], op, % op :: Tree,
- args}). % args :: [Tree]
+-record(c_apply, {anno=[] :: list(), op :: cerl:cerl(),
+ args :: [cerl:cerl()]}).
--record(c_binary, {anno=[], segments :: [cerl:c_bitstr()]}).
+-record(c_binary, {anno=[] :: list(), segments :: [cerl:c_bitstr()]}).
--record(c_bitstr, {anno=[], val, % val :: Tree,
- size, % size :: Tree,
- unit, % unit :: Tree,
- type, % type :: Tree,
- flags}). % flags :: Tree
+-record(c_bitstr, {anno=[] :: list(), val :: cerl:cerl(),
+ size :: cerl:cerl(),
+ unit :: cerl:cerl(),
+ type :: cerl:cerl(),
+ flags :: cerl:cerl()}).
--record(c_call, {anno=[], module, % module :: Tree,
- name, % name :: Tree,
- args}). % args :: [Tree]
+-record(c_call, {anno=[] :: list(), module :: cerl:cerl(),
+ name :: cerl:cerl(),
+ args :: [cerl:cerl()]}).
--record(c_case, {anno=[], arg, % arg :: Tree,
- clauses}). % clauses :: [Tree]
+-record(c_case, {anno=[] :: list(), arg :: cerl:cerl(),
+ clauses :: [cerl:cerl()]}).
--record(c_catch, {anno=[], body}). % body :: Tree
+-record(c_catch, {anno=[] :: list(), body :: cerl:cerl()}).
--record(c_clause, {anno=[], pats, % pats :: [Tree],
- guard, % guard :: Tree,
- body}). % body :: Tree
+-record(c_clause, {anno=[] :: list(), pats :: [cerl:cerl()],
+ guard :: cerl:cerl(),
+ body :: cerl:cerl() | any()}). % TODO
--record(c_cons, {anno=[], hd, % hd :: Tree,
- tl}). % tl :: Tree
+-record(c_cons, {anno=[] :: list(), hd :: cerl:cerl(),
+ tl :: cerl:cerl()}).
--record(c_fun, {anno=[], vars, % vars :: [Tree],
- body}). % body :: Tree
+-record(c_fun, {anno=[] :: list(), vars :: [cerl:cerl()],
+ body :: cerl:cerl()}).
--record(c_let, {anno=[], vars, % vars :: [Tree],
- arg, % arg :: Tree,
- body}). % body :: Tree
+-record(c_let, {anno=[] :: list(), vars :: [cerl:cerl()],
+ arg :: cerl:cerl(),
+ body :: cerl:cerl()}).
--record(c_letrec, {anno=[], defs, % defs :: [#c_def{}],
- body}). % body :: Tree
+-record(c_letrec, {anno=[] :: list(),
+ defs :: [{cerl:cerl(), cerl:cerl()}],
+ body :: cerl:cerl()}).
--record(c_literal, {anno=[], val}). % val :: literal()
+-record(c_literal, {anno=[] :: list(), val :: any()}).
--record(c_map, {anno=[],
+-record(c_map, {anno=[] :: list(),
arg=#c_literal{val=#{}} :: cerl:c_var() | cerl:c_literal(),
es :: [cerl:c_map_pair()],
is_pat=false :: boolean()}).
--record(c_map_pair, {anno=[],
+-record(c_map_pair, {anno=[] :: list(),
op :: #c_literal{val::'assoc'} | #c_literal{val::'exact'},
- key,
- val}).
+ key :: any(), % TODO
+ val :: any()}). % TODO
--record(c_module, {anno=[], name, % name :: Tree,
- exports, % exports :: [Tree],
- attrs, % attrs :: [#c_def{}],
- defs}). % defs :: [#c_def{}]
+-record(c_module, {anno=[] :: list(), name :: cerl:cerl(),
+ exports :: [cerl:cerl()],
+ attrs :: [{cerl:cerl(), cerl:cerl()}],
+ defs :: [{cerl:cerl(), cerl:cerl()}]}).
--record(c_primop, {anno=[], name, % name :: Tree,
- args}). % args :: [Tree]
+-record(c_primop, {anno=[] :: list(), name :: cerl:cerl(),
+ args :: [cerl:cerl()]}).
--record(c_receive, {anno=[], clauses, % clauses :: [Tree],
- timeout, % timeout :: Tree,
- action}). % action :: Tree
+-record(c_receive, {anno=[] :: list(), clauses :: [cerl:cerl()],
+ timeout :: cerl:cerl(),
+ action :: cerl:cerl()}).
--record(c_seq, {anno=[], arg, % arg :: Tree,
- body}). % body :: Tree
+-record(c_seq, {anno=[] :: list(), arg :: cerl:cerl() | any(), % TODO
+ body :: cerl:cerl()}).
--record(c_try, {anno=[], arg, % arg :: Tree,
- vars, % vars :: [Tree],
- body, % body :: Tree
- evars, % evars :: [Tree],
- handler}). % handler :: Tree
+-record(c_try, {anno=[] :: list(), arg :: cerl:cerl(),
+ vars :: [cerl:cerl()],
+ body :: cerl:cerl(),
+ evars :: [cerl:cerl()],
+ handler :: cerl:cerl()}).
--record(c_tuple, {anno=[], es}). % es :: [Tree]
+-record(c_tuple, {anno=[] :: list(), es :: [cerl:cerl()]}).
--record(c_values, {anno=[], es}). % es :: [Tree]
+-record(c_values, {anno=[] :: list(), es :: [cerl:cerl()]}).
--record(c_var, {anno=[], name :: cerl:var_name()}).
+-record(c_var, {anno=[] :: list(), name :: cerl:var_name()}).
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index de5a3c2873..6b1438abdd 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -35,7 +35,7 @@
map_field_lists/1,cover_bin_opt/1,
val_dsetel/1,bad_tuples/1,bad_try_catch_nesting/1,
receive_stacked/1,aliased_types/1,type_conflict/1,
- infer_on_eq/1]).
+ infer_on_eq/1,infer_dead_value/1]).
-include_lib("common_test/include/ct.hrl").
@@ -65,7 +65,7 @@ groups() ->
map_field_lists,cover_bin_opt,val_dsetel,
bad_tuples,bad_try_catch_nesting,
receive_stacked,aliased_types,type_conflict,
- infer_on_eq]}].
+ infer_on_eq,infer_dead_value]}].
init_per_suite(Config) ->
test_lib:recompile(?MODULE),
@@ -679,6 +679,27 @@ infer_on_eq_4(T) ->
true = erlang:tuple_size(T) =:= 1,
{ok, erlang:element(1, T)}.
+%% ERIERL-348; types were inferred for dead values, causing validation to fail.
+
+infer_dead_value(Config) when is_list(Config) ->
+ a = idv_1({a, b, c, d, e, f, g}, {0, 0, 0, 0, 0, 0, 0}),
+ b = idv_1({a, b, c, d, 0, 0, 0}, {a, b, c, d, 0, 0, 0}),
+ c = idv_1({0, 0, 0, 0, 0, f, g}, {0, 0, 0, 0, 0, f, g}),
+ error = idv_1(gurka, gaffel),
+ ok.
+
+idv_1({_A, _B, _C, _D, _E, _F, _G},
+ {0, 0, 0, 0, 0, 0, 0}) ->
+ a;
+idv_1({A, B, C, D,_E, _F, _G}=_Tuple1,
+ {A, B, C, D, 0, 0, 0}=_Tuple2) ->
+ b;
+idv_1({_A, _B, _C, _D, _E, F, G},
+ {0, 0, 0, 0, 0, F, G}) ->
+ c;
+idv_1(_A, _B) ->
+ error.
+
%%%-------------------------------------------------------------------------
transform_remove(Remove, Module) ->
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 41e4918b1e..d97f49c56e 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -1891,15 +1891,37 @@ expression_before_match_1(R) ->
%% Make sure that context positions are updated on calls.
restore_on_call(Config) when is_list(Config) ->
- ok = restore_on_call_1(<<0, 1, 2>>).
+ ok = restore_on_call_plain(<<0, 1, 2>>),
+ <<"x">> = restore_on_call_match(<<0, "x">>),
+ ok.
-restore_on_call_1(<<0, Rest/binary>>) ->
- <<2>> = restore_on_call_2(Rest),
- <<2>> = restore_on_call_2(Rest), %% {badmatch, <<>>} on missing restore.
+restore_on_call_plain(<<0, Rest/binary>>) ->
+ <<2>> = restore_on_call_plain_1(Rest),
+ %% {badmatch, <<>>} on missing restore.
+ <<2>> = restore_on_call_plain_1(Rest),
ok.
-restore_on_call_2(<<1, Rest/binary>>) -> Rest;
-restore_on_call_2(Other) -> Other.
+restore_on_call_plain_1(<<1, Rest/binary>>) -> Rest;
+restore_on_call_plain_1(Other) -> Other.
+
+%% Calls a function that moves the match context passed to it, and then matches
+%% on its result to confuse the reposition algorithm's success/fail logic.
+restore_on_call_match(<<0, Bin/binary>>) ->
+ case skip_until_zero(Bin) of
+ {skipped, Rest} ->
+ Rest;
+ not_found ->
+ %% The match context did not get repositioned before the
+ %% bs_get_tail instruction here.
+ Bin
+ end.
+
+skip_until_zero(<<0,Rest/binary>>) ->
+ {skipped, Rest};
+skip_until_zero(<<_C,Rest/binary>>) ->
+ skip_until_zero(Rest);
+skip_until_zero(_) ->
+ not_found.
%% 'catch' must invalidate positions.
restore_after_catch(Config) when is_list(Config) ->
@@ -1983,5 +2005,4 @@ do_matching_meets_apply(_Bin, {Handler, State}) ->
%% Another case of the above.
Handler:abs(State).
-
id(I) -> I.