dialyzer_dataflow.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 2,097 行 · 第 1/5 页
ERL
2,097 行
end end; 'catch' -> {State1, _Map1, _} = traverse(cerl:catch_body(Tree), Map, DefVars, State), {State1, Map, t_any()}; cons -> Hd = cerl:cons_hd(Tree), Tl = cerl:cons_tl(Tree), {State1, Map1, HdType} = traverse(Hd, Map, DefVars, State), {State2, Map2, TlType} = traverse(Tl, Map1, DefVars, State1), State3 = case t_is_none(t_inf(TlType, t_list())) of true -> Msg = io_lib:format("Cons will produce an improper list since its " "2nd argument is ~s\n", [format_type(TlType, State2)]), state__add_warning(State2, ?WARN_NON_PROPER_LIST, Tree, Msg); false -> State2 end, Type = t_cons(HdType, TlType), {State3, Map2, Type}; 'fun' -> Type = state__fun_type(Tree, State), case state__warning_mode(State) of true -> {State, Map, Type}; false -> State1 = state__add_work(get_label(Tree), State), State2 = state__update_fun_env(Tree, {Map, DefVars}, State1), {State2, Map, Type} end; 'let' -> Arg = cerl:let_arg(Tree), Vars = cerl:let_vars(Tree), Map0 = case cerl:is_c_var(Arg) of true -> [Var] = Vars, enter_subst(Var, Arg, Map); false -> Map end, Body = cerl:let_body(Tree), {State1, Map1, ArgTypes} = traverse(Arg, Map0, DefVars, State), case t_is_none_or_unit(ArgTypes) of true -> {State1, Map1, ArgTypes}; false -> VarTypes = wrap_if_single(t_components(ArgTypes)), Map2 = enter_type_lists(Vars, VarTypes, Map1), traverse(Body, Map2, add_def_vars(Vars, DefVars), State1) end; letrec -> Defs = cerl:letrec_defs(Tree), Body = cerl:letrec_body(Tree), %% By not including the variables in scope we can assure that we %% will get the current function type when using the variables. FoldFun = fun({Var, Fun}, {AccState, AccMap}) -> {NewAccState, NewAccMap0, FunType} = traverse(Fun, AccMap, DefVars, AccState), NewAccMap = enter_type(Var, FunType, NewAccMap0), {NewAccState, NewAccMap} end, {State1, Map1} = lists:foldl(FoldFun, {State, Map}, Defs), traverse(Body, Map1, DefVars, State1); literal -> %% This is needed for finding records case cerl:unfold_literal(Tree) of Tree -> {State, Map, literal_type(Tree)}; NewTree -> traverse(NewTree, Map, DefVars, State) end; module -> %% By not including the variables in scope we can assure that we %% will get the current function type when using the variables. Defs = cerl:module_defs(Tree), PartFun = fun({_Var, Fun}) -> state__is_escaping(get_label(Fun), State) end, {Defs1, Defs2} = lists:partition(PartFun, Defs), Letrec = cerl:c_letrec(Defs1, cerl:c_int(42)), {State1, Map1, _FunTypes} = traverse(Letrec, Map, DefVars, State), %% Also add environments for the other top-level functions. VarTypes = [{Var, state__fun_type(Fun, State1)} || {Var, Fun} <- Defs], EnvMap = enter_type_list(VarTypes, Map), FoldFun = fun({_Var, Fun}, AccState) -> state__update_fun_env(Fun, {EnvMap, DefVars}, AccState) end, State2 = lists:foldl(FoldFun, State1, Defs2), {State2, Map1, t_any()}; primop -> Type = case cerl:atom_val(cerl:primop_name(Tree)) of match_fail -> t_none(); raise -> t_none(); Other -> erlang:fault({'Unsupported primop', Other}) end, {State, Map, Type}; 'receive' -> Clauses = filter_match_fail(cerl:receive_clauses(Tree)), Timeout = cerl:receive_timeout(Tree), {MapList, State1, ReceiveType} = handle_clauses(Clauses, none, t_any(), t_any(), State, [], Map, DefVars, []), Map1 = join_maps(MapList, Map), {State2, Map2, TimeoutType} = traverse(Timeout, Map1, DefVars, State1), case (t_is_atom(TimeoutType) andalso (t_atom_vals(TimeoutType) =:= [infinity])) of true -> {State2, Map2, ReceiveType}; false -> Action = cerl:receive_action(Tree), {State3, Map3, ActionType} = traverse(Action, Map, DefVars, State2), Map4 = join_maps([Map3, Map1], Map), Type = t_sup(ReceiveType, ActionType), {State3, Map4, Type} end; seq -> Arg = cerl:seq_arg(Tree), Body = cerl:seq_body(Tree), {State1, Map1, ArgType} = traverse(Arg, Map, DefVars, State), case t_is_none_or_unit(ArgType) of true -> {State1, Map1, ArgType}; false -> traverse(Body, Map1, DefVars, State1) end; 'try' -> Arg = cerl:try_arg(Tree), EVars = cerl:try_evars(Tree), Vars = cerl:try_vars(Tree), Body = cerl:try_body(Tree), Handler = cerl:try_handler(Tree), {State1, Map1, ArgType} = traverse(Arg, Map, DefVars, State), Map2 = mark_as_fresh(Vars, Map1), ArgTypes = wrap_if_single(t_components(ArgType)), {SuccState, SuccMap, SuccType} = case bind_pat_vars(Vars, ArgTypes, [], Map2, State1) of error -> {State1, map__new(), t_none()}; {SuccMap1, VarTypes} -> %% Try to bind the argument. Will only succeed if %% it is a simple structured term. case bind_pat_vars([Arg], [t_product(VarTypes)], [], SuccMap1, State1) of error -> SuccMap2 = SuccMap1; {SuccMap2, _} -> ok end, DefVars1 = add_def_vars(Vars, DefVars), traverse(Body, SuccMap2, DefVars1, State1) end, ExcMap1 = mark_as_fresh(EVars, Map), DefVars2 = add_def_vars(EVars, DefVars), {State2, ExcMap2, HandlerType} = traverse(Handler, ExcMap1, DefVars2, SuccState), TryType = t_sup(SuccType, HandlerType), {State2, join_maps([ExcMap2, SuccMap], Map1), TryType}; tuple -> Elements = cerl:tuple_es(Tree), {State1, Map1, EsType} = traverse_list(Elements, Map, DefVars, State), %% Let's find out if this is a record construction. case Elements of [Tag|Left] -> case cerl:is_c_atom(Tag) of true -> TagVal = cerl:atom_val(Tag), case state__lookup_record(TagVal, length(Left), State1) of error -> {State1, Map1, t_tuple(EsType)}; {ok, Prototype} -> TupleType = t_inf(Prototype, t_tuple(EsType)), case t_is_none(TupleType) of true -> {State1, Map1, t_none()}; false -> case bind_pat_vars(Elements, t_tuple_args(TupleType), [], Map1, State1) of error -> {State1, Map1, t_none()}; {Map2, ETypes} -> {State1, Map2, t_tuple(ETypes)} end end end; false -> {State1, Map1, t_tuple(EsType)} end; [] -> {State1, Map1, t_tuple([])} end; values -> Elements = cerl:values_es(Tree), {State1, Map1, EsType} = traverse_list(Elements, Map, DefVars, State), Type = t_product(EsType), {State1, Map1, Type}; var -> case is_def_var(Tree, DefVars) of true -> {State, Map, lookup_type(Tree, Map)}; false -> ?debug("Looking up unknown variable: ~p\n", [Tree]), case state__lookup_type_for_rec_var(Tree, State) of error -> erlang:fault({'Non-defined variable', Tree}); {ok, Type} -> {State, Map, Type} end end; Other -> erlang:fault({'Unsupported type', Other}) end.traverse_list(Trees, Map, DefVars, State) -> traverse_list(Trees, Map, DefVars, State, []).traverse_list([Tree|Tail], Map, DefVars, State, Acc) -> {State1, Map1, Type} = traverse(Tree, Map, DefVars, State), traverse_list(Tail, Map1, DefVars, State1, [Type|Acc]);traverse_list([], Map, _DefVars, State, Acc) -> {State, Map, lists:reverse(Acc)}. %%________________________________________%%%% Special instructions%%do_call(M, F, As, State) -> Arity = length(As), {Ret, ArgCs, Sig} = case erl_bif_types:is_known(M, F, Arity) of true -> BifRet = erl_bif_types:type(M, F, Arity, As), BifArgs = case erl_bif_types:arg_types(M, F, Arity) of any -> duplicate(Arity, t_any()); List -> List end, {BifRet, BifArgs, t_fun(BifArgs, erl_bif_types:type(M, F, Arity))}; false -> {PltRet, PltArg} = state__lookup_non_local(M, F, Arity, State, As), {PltRet, PltArg, t_fun(PltArg, PltRet)} end, NewArgs = t_inf_lists(ArgCs, As), case any_none([Ret|NewArgs]) of true -> case t_is_none(t_fun_range(Sig)) of true -> {t_none(), NewArgs}; false -> {failed, Sig} end; false -> {Ret, NewArgs} end. handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn, DefVars, Acc) -> {State1, ClauseMap, BodyType, NewArgType} = do_clause(C, Arg, ArgType, OrigArgType, MapIn, DefVars, State), {NewCaseTypes, NewAcc} = case t_is_none(BodyType) of true -> {CaseTypes, Acc}; false -> {[BodyType|CaseTypes], [ClauseMap|Acc]} end, handle_clauses(Left, Arg, NewArgType, OrigArgType, State1, NewCaseTypes, MapIn, DefVars, NewAcc);handle_clauses([], _Arg, _ArgType, _OrigArgType, State, CaseTypes, _MapIn, _DefVars, Acc) -> {lists:reverse(Acc), State, t_sup(CaseTypes)}.do_clause(C, Arg, ArgType0, OrigArgType, Map, DefVars, State) -> Pats = cerl:clause_pats(C), Guard = cerl:clause_guard(C), Body = cerl:clause_body(C), Map0 = mark_as_fresh(Pats, Map), ArgTypes = t_components(ArgType0), if Arg =:= none -> Map1 = Map0; true -> Map1 = bind_subst(Arg, Pats, Map0) end, case bind_pat_vars(Pats, ArgTypes, [], Map1, State) of error -> ?debug("Failed binding pattern: ~s\nto ~s\n", [cerl_prettypr:format(C), format_type(ArgType0, State)]), PatternString = format_patterns(Pats), {Msg, Force} = case t_is_none(ArgType0) of true -> {io_lib:format("The ~s can never match since" " previous clauses completely covered the type ~s\n", [PatternString, format_type(OrigArgType, State)]), false}; false -> %% Try to find out if this is a default clause in a list %% comprehension and supress this. A real Hack(tm) Force0 = case is_compiler_generated(cerl:get_ann(C)) of true -> case Pats of [Pat] -> case cerl:is_c_cons(Pat) of true -> not (cerl:is_c_var(cerl:cons_hd(Pat)) andalso cerl:is_c_var(cerl:cons_tl(Pat)) andalso cerl:is_literal(Guard) andalso (cerl:concrete(Guard) =:= true)); false -> true end; _ -> true end; false -> true end, {io_lib:format("The ~s can never match the type ~s\n", [PatternString, format_type(ArgType0, State)]), Force0} end, {state__add_warning(State, ?WARN_MATCHING, C, Msg, Force), Map, t_none(), ArgType0}; {Map2, PatTypes} -> case Arg =:= none of true -> Map3 = Map2; false -> %% Try to bind the argument. Will only succeed if %% it is a simple structured term. case bind_pat_vars([Arg], [t_product(PatTypes)], [], Map2, State) of error -> Map3 = Map2; {Map3, _} -> ok end end, NewArgType = case Arg =:= none of true -> ArgType0; false -> GenType = dialyzer_typesig:get_safe_underapprox(Pats, Guard), t_subtract(t_product(wrap_if_single(ArgType0)), GenType) end, case bind_guard(Guard, Map3, State) of {error, Reason} -> ?debug("Failed guard: ~s\n", [cerl_prettypr:format(C, [{hook, cerl_typean:pp_hook()}])]), PatternString = format_patterns(Pats), DefaultMsg = case Pats =:= [] of true -> "Clause guard cannot succeed.\n"; false -> io_lib:format("Clause guard cannot succeed. The ~s was matched" " against the type ~s\n", [PatternString, format_type(ArgType0, State)]) end, State1 = case Reason of none -> state__add_warning(State, ?WARN_MATCHING, C, DefaultMsg); {FailGuard, Msg} -> case is_compiler_generated(cerl:get_ann(FailGuard)) of false -> state__add_warning(State, ?WARN_MATCHING, FailGuard, Msg); true -> state__add_warning(State, ?WARN_MATCHING, C, Msg) end end, {State1, Map, t_none(), NewArgType}; Map4 -> FoldFun = fun(ST, Acc) -> case cerl:is_c_var(ST) of true -> [ST|Acc]; false -> Acc end end, PatVars = [cerl_trees:fold(FoldFun, [], Pat) || Pat <- Pats], DefVars1 = add_def_vars(lists:flatten(PatVars), DefVars), {RetState, RetMap, BodyType} = traverse(Body, Map4, DefVars1, State), {RetState, RetMap, BodyType, NewArgType} end end.wrap_if_single(X) when is_list(X) -> X;wrap_if_single(X) -> [X].bind_subst(Arg, Pats, Map) -> case cerl:type(Arg) of values -> bind_subst_list(cerl:values_es(Arg), Pats, Map); var -> [Pat] = Pats, enter_subst(Arg, Pat, Map); _ -> Map end.bind_subst_list([Arg|ArgLeft], [Pat|PatLeft], Map) -> NewMap = case {cerl:type(Arg), cerl:type(Pat)} of {var, var} -> enter_subst(Arg, Pat, Map); {var, alias} -> enter_subst(Arg, cerl:alias_pat(Pat), Map); {literal, literal} -> Map; {T, T} -> bind_subst_list(lists:flatten(cerl:subtrees(Arg)), lists:flatten(cerl:subtrees(Pat)), Map); _ -> Map end, bind_subst_list(ArgLeft, PatLeft, NewMap);bind_subst_list([], [], Map) -> Map.bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State) -> ?debug("Binding pat: ~w to ~s\n", [cerl:type(Pat), format_type(Type, State)]), Res = case cerl:type(Pat) of alias -> AliasPat = cerl:alias_pat(Pat), Var = cerl:alias_var(Pat), Map1 = enter_subst(Var, AliasPat, Map), case bind_pat_vars([AliasPat], [Type], [], Map1, State) of error -> error; {Map2, [PatType]} -> {enter_type(Var, PatType, Map2), PatType} end; binary -> case t_is_none(t_inf(t_binary(), Type)) of true -> error; false -> {bind_bin_segs(cerl:binary_segments(Pat), Map), t_binary()} end; cons -> Cons = t_inf(Type, t_cons()),
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?