summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Högberg <john@erlang.org>2019-08-05 11:44:35 +0200
committerJohn Högberg <john@erlang.org>2019-08-05 11:56:34 +0200
commit64731f3bb753f2b534ada36a4713370aecc8b4b1 (patch)
treeab7fa83a49174eb57d778b8fc0844beaaa5cf0fa
parent4f170eeb7043838866a4eb5a518ec51a913bcbd2 (diff)
parent381ff386961aa28abaf2b43572303bd394121a7d (diff)
downloaderlang-64731f3bb753f2b534ada36a4713370aecc8b4b1.tar.gz
Merge branch 'maint'
* maint: beam_validator: Values referenced by other values must be merged
-rw-r--r--lib/compiler/src/beam_validator.erl90
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl22
2 files changed, 67 insertions, 45 deletions
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index abc3aa3875..7609a1eb56 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -1585,35 +1585,29 @@ infer_types(CompareOp, LHS, RHS, #vst{current=#st{vs=Vs}}=Vst0) ->
infer_types_1(#value{op={bif,'=:='},args=[LHS,RHS]}, Val, Op, Vst) ->
case Val of
{atom, Bool} when Op =:= eq_exact, Bool; Op =:= ne_exact, not Bool ->
- infer_types(eq_exact, LHS, RHS, Vst);
+ update_eq_types(LHS, RHS, Vst);
{atom, Bool} when Op =:= ne_exact, Bool; Op =:= eq_exact, not Bool ->
- infer_types(ne_exact, LHS, RHS, Vst);
+ update_ne_types(LHS, RHS, Vst);
_ ->
Vst
end;
infer_types_1(#value{op={bif,'=/='},args=[LHS,RHS]}, Val, Op, Vst) ->
case Val of
{atom, Bool} when Op =:= ne_exact, Bool; Op =:= eq_exact, not Bool ->
- infer_types(ne_exact, LHS, RHS, Vst);
+ update_ne_types(LHS, RHS, Vst);
{atom, Bool} when Op =:= eq_exact, Bool; Op =:= ne_exact, not Bool ->
- infer_types(eq_exact, LHS, RHS, Vst);
+ update_eq_types(LHS, RHS, Vst);
_ ->
Vst
end;
infer_types_1(#value{op={bif,element},args=[{integer,Index},Tuple]},
Val, Op, Vst) when Index >= 1 ->
- Merge = case Op of
- eq_exact -> fun meet/2;
- ne_exact -> fun subtract/2
- end,
- case is_value_alive(Tuple, Vst) of
- true ->
- ElementType = get_term_type(Val, Vst),
- Es = beam_types:set_element_type(Index, ElementType, #{}),
- Type = #t_tuple{size=Index,elements=Es},
- update_type(Merge, Type, Tuple, Vst);
- false ->
- Vst
+ ElementType = get_term_type(Val, Vst),
+ Es = beam_types:set_element_type(Index, ElementType, #{}),
+ Type = #t_tuple{size=Index,elements=Es},
+ case Op of
+ eq_exact -> update_type(fun meet/2, Type, Tuple, Vst);
+ ne_exact -> update_type(fun subtract/2, Type, Tuple, Vst)
end;
infer_types_1(#value{op={bif,is_atom},args=[Src]}, Val, Op, Vst) ->
infer_type_test_bif(#t_atom{}, Src, Val, Op, Vst);
@@ -1637,31 +1631,23 @@ infer_types_1(#value{op={bif,is_tuple},args=[Src]}, Val, Op, Vst) ->
infer_type_test_bif(#t_tuple{}, Src, Val, Op, Vst);
infer_types_1(#value{op={bif,tuple_size}, args=[Tuple]},
{integer,Arity}, Op, Vst) ->
- Merge = case Op of
- eq_exact -> fun meet/2;
- ne_exact -> fun subtract/2
- end,
- case is_value_alive(Tuple, Vst) of
- true ->
- Type = #t_tuple{exact=true,size=Arity},
- update_type(Merge, Type, Tuple, Vst);
- false ->
- Vst
+ Type = #t_tuple{exact=true,size=Arity},
+ case Op of
+ eq_exact -> update_type(fun meet/2, Type, Tuple, Vst);
+ ne_exact -> update_type(fun subtract/2, Type, Tuple, Vst)
end;
infer_types_1(_, _, _, Vst) ->
Vst.
-infer_type_test_bif(Type, Src, {atom, Bool}, Op, Vst) when is_boolean(Bool) ->
- case is_value_alive(Src, Vst) of
- true when Op =:= eq_exact, Bool; Op =:= ne_exact, not Bool ->
+infer_type_test_bif(Type, Src, Val, Op, Vst) ->
+ case Val of
+ {atom, Bool} when Op =:= eq_exact, Bool; Op =:= ne_exact, not Bool ->
update_type(fun meet/2, Type, Src, Vst);
- true when Op =:= ne_exact, Bool; Op =:= eq_exact, not Bool ->
+ {atom, Bool} when Op =:= ne_exact, Bool; Op =:= eq_exact, not Bool ->
update_type(fun subtract/2, Type, Src, Vst);
- false ->
+ _ ->
Vst
- end;
-infer_type_test_bif(_, _, _, _, Vst) ->
- Vst.
+ end.
%%%
%%% Keeping track of types.
@@ -2040,11 +2026,6 @@ 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);
-is_value_alive(_, _) ->
- false.
-
get_literal_type(nil) ->
beam_types:make_type_from_value([]);
get_literal_type({atom,A}) when is_atom(A) ->
@@ -2214,25 +2195,44 @@ merge_vrefs(RefA, RefB, Merge, Counter) ->
merge_values(Merge, VsA, VsB) ->
maps:fold(fun(Spec, New, Acc) ->
- merge_values_1(Spec, New, VsA, VsB, Acc)
+ mv_1(Spec, New, VsA, VsB, Acc)
end, #{}, Merge).
-merge_values_1(Same, Same, VsA, VsB, Acc) ->
+mv_1(Same, Same, VsA, VsB, Acc0) ->
%% We're merging different versions of the same value, so it's safe to
%% reuse old entries if the type's unchanged.
- #value{type=TypeA}=EntryA = map_get(Same, VsA),
- #value{type=TypeB}=EntryB = map_get(Same, VsB),
+ #value{type=TypeA,args=Args}=EntryA = map_get(Same, VsA),
+ #value{type=TypeB,args=Args}=EntryB = map_get(Same, VsB),
+
Entry = case join(TypeA, TypeB) of
TypeA -> EntryA;
TypeB -> EntryB;
JoinedType -> EntryA#value{type=JoinedType}
end,
- Acc#{ Same => Entry };
-merge_values_1({RefA, RefB}, New, VsA, VsB, Acc) ->
+
+ Acc = Acc0#{ Same => Entry },
+
+ %% Type inference may depend on values that are no longer reachable from a
+ %% register, so all arguments must be merged into the new state.
+ mv_args(Args, VsA, VsB, Acc);
+mv_1({RefA, RefB}, New, VsA, VsB, Acc) ->
#value{type=TypeA} = map_get(RefA, VsA),
#value{type=TypeB} = map_get(RefB, VsB),
Acc#{ New => #value{op=join,args=[],type=join(TypeA, TypeB)} }.
+mv_args([#value_ref{}=Arg | Args], VsA, VsB, Acc0) ->
+ case Acc0 of
+ #{ Arg := _ } ->
+ mv_args(Args, VsA, VsB, Acc0);
+ #{} ->
+ Acc = mv_1(Arg, Arg, VsA, VsB, Acc0),
+ mv_args(Args, VsA, VsB, Acc)
+ end;
+mv_args([_ | Args], VsA, VsB, Acc) ->
+ mv_args(Args, VsA, VsB, Acc);
+mv_args([], _VsA, _VsB, Acc) ->
+ Acc.
+
merge_fragility(FragileA, FragileB) ->
cerl_sets:union(FragileA, FragileB).
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index 326ad9042f..685e1a95a7 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -681,11 +681,16 @@ infer_on_eq_4(T) ->
%% ERIERL-348; types were inferred for dead values, causing validation to fail.
+-record(idv, {key}).
+
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_2(id(#idv{})),
+
ok.
idv_1({_A, _B, _C, _D, _E, _F, _G},
@@ -719,6 +724,23 @@ ion_2(#ion{state = closing}) -> ok.
ion_close(State = #ion{}) -> State#ion{state = closing}.
+%% ERL-995: The first solution to ERIERL-348 was incomplete and caused
+%% validation to fail when living values depended on delayed type inference on
+%% "dead" values.
+
+idv_2(State) ->
+ Flag = (State#idv.key == undefined),
+ case id(gurka) of
+ {_} -> id([Flag]);
+ _ -> ok
+ end,
+ if
+ Flag -> idv_called_once(State);
+ true -> ok
+ end.
+
+idv_called_once(_State) -> ok.
+
%%%-------------------------------------------------------------------------
transform_remove(Remove, Module) ->