diff options
author | Björn Gustavsson <[email protected]> | 2019-02-04 10:03:55 +0100 |
---|---|---|
committer | Björn Gustavsson <[email protected]> | 2019-02-11 06:36:12 +0100 |
commit | 4763811e1d67a0d2ac3442d4694b4e1dee1b4364 (patch) | |
tree | 99e059b0a795e1d4716743c48a63742f0ad4a7bc /lib/stdlib | |
parent | 3542afbdc4909455ad9e45e2b5328835a838a1bd (diff) | |
download | otp-4763811e1d67a0d2ac3442d4694b4e1dee1b4364.tar.gz otp-4763811e1d67a0d2ac3442d4694b4e1dee1b4364.tar.bz2 otp-4763811e1d67a0d2ac3442d4694b4e1dee1b4364.zip |
beam_ssa_type: Propagate the 'none' type from calls
Consider this pseudo code:
f(...) ->
Val = case Expr of
... ->
... ;
... ->
... ;
... ->
my_abort(something_went_wrong)
end,
%% Here follows code that uses Val.
.
.
.
my_abort(Reason) ->
throw({error,Reason}).
The first two clauses in the case will probably provide some
information about the type of the variable `Var`, information
that would be useful for optimizing the code that follows the
case.
However, the third clause would ruin everything. The call
to `my_abort/1` could return anything, and thus `Val` could
also have any type.
294d66a295f6 introduced module-level type analysis, which will in
general keep track of the return type of a local function
call. However, it does not improve the optimization for this specific
function. When a function never returns, that is, when its type is
`none`, it does not propagate the `none` type, but instead pretends
that the return type is `any`.
This commit extends the handling of functions that don't return to
properly handle the `none` type. Any instructions that directly
follows the function that does not return will be discarded, and the
call will be rewritten to a tail-recursive call.
For this specific example, it means that the type for `Val` deduced
from the first two clauses will be retained and can be used for
optimizing the code after the case.
Diffstat (limited to 'lib/stdlib')
-rw-r--r-- | lib/stdlib/test/erl_eval_SUITE.erl | 12 |
1 files changed, 11 insertions, 1 deletions
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl index f4019d477b..2436c8091c 100644 --- a/lib/stdlib/test/erl_eval_SUITE.erl +++ b/lib/stdlib/test/erl_eval_SUITE.erl @@ -1156,7 +1156,17 @@ simple() -> {A}. simple1() -> - erlang:error(simple). + %% If the compiler could see that this function would always + %% throw an error exception, it would rewrite simple() like this: + %% + %% simple() -> simple1(). + %% + %% That would change the stacktrace. To prevent the compiler from + %% doing that optimization, we must obfuscate the code. + case get(a_key_that_is_not_defined) of + undefined -> erlang:error(simple); + WillNeverHappen -> WillNeverHappen + end. %% Simple cases, just to cover some code. funs(Config) when is_list(Config) -> |