diff options
| author | Björn Gustavsson <[email protected]> | 2016-05-18 19:14:09 +0200 | 
|---|---|---|
| committer | Björn Gustavsson <[email protected]> | 2016-05-23 08:39:52 +0200 | 
| commit | 9ab0dcce5a2bf609917cb09c36cbf63dad6bb679 (patch) | |
| tree | 0a92402f81e8bbf327987cf9462901adbab4e21a /lib/compiler/src | |
| parent | 121cc9ba61e44e9c47d831d837dfb0f0b2d81990 (diff) | |
| download | otp-9ab0dcce5a2bf609917cb09c36cbf63dad6bb679.tar.gz otp-9ab0dcce5a2bf609917cb09c36cbf63dad6bb679.tar.bz2 otp-9ab0dcce5a2bf609917cb09c36cbf63dad6bb679.zip | |
v3_core: Don't depend on sys_core_fold for cleaning up
a3ec2644f5 attempted to teach v3_core not to generate code with
unbound variables. The approach taken in that commit is to
discard all expressions following a badmatch. That does not
work if the badmatch is nested:
  {[V] = [] = foo,V},
  V
That would be rewritten to:
  {error({badmatch,foo})},
  V
where V is unbound.
If we were to follow the same approach, the tuple construction
code would have to look out for a badmatch. As would list construction,
begin...end, and so on.
Therefore, as it is impractical to discard all expressions that
follow a badmatch, the only other solution is to ensure that the
variables that the pattern binds will somehow be bound. That can
be arranged by rewriting the pattern to a pattern that binds the
same variables. Thus:
  error({badmatch,foo}),
  E = foo,
  case E of
    {[V],[]} ->
      V;
    Other ->
      error({badmatch,Other}
  end
Diffstat (limited to 'lib/compiler/src')
| -rw-r--r-- | lib/compiler/src/v3_core.erl | 66 | 
1 files changed, 53 insertions, 13 deletions
| diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 83b3650180..a3b0236134 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -510,16 +510,8 @@ unforce(_, Vs) -> Vs.  exprs([E0|Es0], St0) ->      {E1,Eps,St1} = expr(E0, St0), -    case E1 of -	#iprimop{name=#c_literal{val=match_fail}} -> -	    %% Must discard the rest of the body, because it -	    %% may refer to variables that have not been bound. -	    %% Example: {ok={error,E}} = foo(), E. -	    {Eps ++ [E1],St1}; -	_ -> -	    {Es1,St2} = exprs(Es0, St1), -	    {Eps ++ [E1] ++ Es1,St2} -    end; +    {Es1,St2} = exprs(Es0, St1), +    {Eps ++ [E1] ++ Es1,St2};  exprs([], St) -> {[],St}.  %% expr(Expr, State) -> {Cexpr,[PreExp],State}. @@ -689,14 +681,36 @@ expr({match,L,P0,E0}, St0) ->      Fc = fail_clause([Fpat], Lanno, c_tuple([#c_literal{val=badmatch},Fpat])),      case P2 of  	nomatch -> +	    %% The pattern will not match. We must take care here to +	    %% bind all variables that the pattern would have bound +	    %% so that subsequent expressions do not refer to unbound +	    %% variables. +	    %% +	    %% As an example, this code: +	    %% +	    %%   [X] = {Y} = E, +	    %%   X + Y. +	    %% +	    %% will be rewritten to: +	    %% +	    %%   error({badmatch,E}), +	    %%   case E of +	    %%      {[X],{Y}} -> +	    %%        X + Y; +	    %%      Other -> +	    %%        error({badmatch,Other}) +	    %%   end. +	    %%  	    St6 = add_warning(L, nomatch, St5), -	    {Expr,Eps3,St} = safe(E1, St6), -	    Eps = Eps1 ++ Eps2 ++ Eps3, +	    {Expr,Eps3,St7} = safe(E1, St6), +	    SanPat0 = sanitize(P1), +	    {SanPat,Eps4,St} = pattern(SanPat0, St7),  	    Badmatch = c_tuple([#c_literal{val=badmatch},Expr]),  	    Fail = #iprimop{anno=#a{anno=Lanno},  			    name=#c_literal{val=match_fail},  			    args=[Badmatch]}, -	    {Fail,Eps,St}; +	    Eps = Eps3 ++ Eps4 ++ [Fail], +	    {#imatch{anno=#a{anno=Lanno},pat=SanPat,arg=Expr,fc=Fc},Eps,St};  	Other when not is_atom(Other) ->  	    {#imatch{anno=#a{anno=Lanno},pat=P2,arg=E2,fc=Fc},Eps1++Eps2,St5}      end; @@ -738,6 +752,32 @@ expr({op,L,Op,L0,R0}, St0) ->  	    module=#c_literal{anno=LineAnno,val=erlang},  	    name=#c_literal{anno=LineAnno,val=Op},args=As},Aps,St1}. + +%% sanitize(Pat) -> SanitizedPattern +%%  Rewrite Pat so that it will be accepted by pattern/2 and will +%%  bind the same variables as the original pattern. +%% +%%  Here is an example of a pattern that would cause a pattern/2 +%%  to generate a 'nomatch' exception: +%% +%%      #{k:=X,k:=Y} = [Z] +%% +%%  The sanitized pattern will look like: +%% +%%      {{X,Y},[Z]} + +sanitize({match,L,P1,P2}) -> +    {tuple,L,[sanitize(P1),sanitize(P2)]}; +sanitize({cons,L,H,T}) -> +    {cons,L,sanitize(H),sanitize(T)}; +sanitize({tuple,L,Ps0}) -> +    Ps = [sanitize(P) || P <- Ps0], +    {tuple,L,Ps}; +sanitize({map,L,Ps0}) -> +    Ps = [sanitize(V) || {map_field_exact,_,_,V} <- Ps0], +    {tuple,L,Ps}; +sanitize(P) -> P. +  make_bool_switch(L, E, V, T, F, #core{in_guard=true}) ->      make_bool_switch_guard(L, E, V, T, F);  make_bool_switch(L, E, V, T, F, #core{}) -> | 
