From 4411b1953bfb862aad433c2269acb82a35b0cc72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Tue, 11 Jun 2019 14:43:53 +0200 Subject: beam_validator: Subtract types when inferring type test BIFs This is a temporary solution for basic type tests. We'll need to handle more-or-less arbitrary values once we introduce union types, as we need to be able to subtract on tuple_arity tests as well. Without this, nearly all "no_opt" test suites will fail to compile after the validator is migrated to 'beam_types' as a result of atom subtraction producing 'none' when all alternatives have been exhausted. --- lib/compiler/src/beam_validator.erl | 38 ++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 4c223a93ed..17e0a4fa38 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -1673,10 +1673,14 @@ infer_types_1(_) -> fun(_, S) -> S end. infer_type_test_bif(Type, Src) -> - fun({atom,true}, S) -> + fun({atom,Bool}, S) when is_boolean(Bool) -> case is_value_alive(Src, S) of - true -> update_type(fun meet/2, Type, Src, S); - false -> S + true when Bool =:= true -> + update_type(fun meet/2, Type, Src, S); + true when Bool =:= false -> + update_type(fun subtract/2, Type, Src, S); + false -> + S end; (_, S) -> S @@ -1798,6 +1802,34 @@ update_type(Merge, With, Literal, Vst) -> _Type -> Vst end. +update_ne_types(LHS, {atom,Bool}=RHS, Vst) when is_boolean(Bool) -> + %% This is a stopgap to make negative inference work for type test BIFs + %% like is_tuple. Consider the following unoptimized code: + %% + %% {call_ext,2,{extfunc,erlang,'--',2}}. + %% {bif,is_tuple,{f,0},[{x,0}],{x,1}}. + %% {test,is_eq_exact,{x,1},{f,2},{atom,false}}. + %% ... snip ... + %% {label,1}. + %% {test,is_eq_exact,{x,1},{f,1},{atom,true}}. + %% ... unreachable because {x,0} is known to be a list, so {x,1} can't + %% be true ... + %% {label,2}. + %% ... unreachable because {x,1} is neither true nor false! ... + %% + %% If we fail to determine that the first is_eq_exact never fails, our + %% state will be inconsistent after the second is_eq_exact check; we know + %% for certain that {x,0} is a list so infer_types says it can't succeed, + %% but it can't fail either because we also know that {x,1} is a boolean, + %% and the first check ruled out 'false'. + LType = get_term_type(LHS, Vst), + if + LType =:= bool -> + update_eq_types(LHS, {atom, not Bool}, Vst); + LType =/= bool -> + RType = get_term_type(RHS, Vst), + update_type(fun subtract/2, RType, LHS, Vst) + end; update_ne_types(LHS, RHS, Vst) -> %% While updating types on equality is fairly straightforward, inequality %% is a bit trickier since all we know is that the *value* of LHS differs -- cgit v1.2.3