File 5511-Correct-scope-of-variables.patch of Package erlang

From 9006ea80b844cbaa38450a31807cdf5b7a14b651 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Thu, 18 Nov 2021 12:26:33 +0100
Subject: [PATCH] Correct scope of variables

Consider this example:

    main(_) ->
        _ = (X = 42) + fun() -> X = 0 end(),
        io:format("~p\n", [X]).

When run as an `escript`, we will get different results depending
on whether we run it using `erl_eval` or using the Erlang compiler.

Here is the result using `erl_eval` (the default):

    $ escript t.erl
    42

Here is the result using the compiler:

    $ escript -c t.erl
    escript: exception error: no match of right hand side value 0

At least one of the results must be wrong. Which?

The compiler is wrong.

A binary operator (such as `+`) is allowed to evaluate its operands
in any order. Thus, code that depends on the left hand side of
a binary operator being evaluated before the right hand side is
illegal and will be rejected by the compiler. For example:

    bar(A) ->
        (X = A) + (2 * X).

Compilation will fail with the following message:

    t.erl:5:20: variable 'X' is unbound
    %    5|     (X = A) + (2 * X).
    %     |                    ^

Therefore, when we have the expression:

    (X = 42) + fun() -> X = 0 end()

the variable `X` is not bound when evaluating the right hand side
operand for `+` and the `X` bound in the fun body is another variable
named `X`, unrelated to the `X` bound on the left hand side of `+`.

This commit corrects the compiler so that it generates code that
evalutes the example in the same way as `erl_eval` does.

Closes #5379
---
 lib/compiler/src/v3_core.erl    | 315 +++++++++++++++++++++++++++-----
 lib/compiler/test/fun_SUITE.erl | 230 ++++++++++++++++++++++-
 2 files changed, 494 insertions(+), 51 deletions(-)

diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 1eb7f9778b..05666005ed 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -109,6 +109,7 @@
 -record(ifun,      {anno=#a{},id,vars,clauses,fc,name=unnamed}).
 -record(iletrec,   {anno=#a{},defs,body}).
 -record(imatch,    {anno=#a{},pat,guard=[],arg,fc}).
+-record(iexprs,    {anno=#a{},bodies=[]}).
 -record(imap,      {anno=#a{},arg=#c_literal{val=#{}},es,is_pat=false}).
 -record(imappair,  {anno=#a{},op,key,val}).
 -record(iprimop,   {anno=#a{},name,args}).
@@ -565,6 +566,8 @@ unforce(E, Eps, Vs) ->
     Tree = unforce_tree(Eps++[E], gb_trees:empty()),
     unforce(Tree, Vs).
 
+unforce_tree([#iexprs{bodies=Exprs}|Es], D0) ->
+    unforce_tree(lists:append(Exprs) ++ Es, D0);
 unforce_tree([#iset{var=#c_var{name=V},arg=Arg0}|Es], D0) ->
     Arg = unforce_tree_subst(Arg0, D0),
     D = gb_trees:insert(V, Arg, D0),
@@ -627,10 +630,9 @@ expr({atom,L,A}, St) -> {#c_literal{anno=full_anno(L, St),val=A},[],St};
 expr({nil,L}, St) -> {#c_literal{anno=full_anno(L, St),val=[]},[],St};
 expr({string,L,S}, St) -> {#c_literal{anno=full_anno(L, St),val=S},[],St};
 expr({cons,L,H0,T0}, St0) ->
-    {H1,Hps,St1} = safe(H0, St0),
-    {T1,Tps,St2} = safe(T0, St1),
-    A = full_anno(L, St2),
-    {annotate_cons(A, H1, T1, St2),Hps ++ Tps,St2};
+    {[H1,T1],Eps,St1} = safe_list([H0,T0], St0),
+    A = full_anno(L, St1),
+    {annotate_cons(A, H1, T1, St1),Eps,St1};
 expr({lc,L,E,Qs0}, St0) ->
     {Qs1,St1} = preprocess_quals(L, Qs0, St0),
     lc_tq(L, E, Qs1, #c_literal{anno=lineno_anno(L, St1),val=[]}, St1);
@@ -1795,6 +1797,33 @@ force_novars(#c_map{}=Bin, St) -> {Bin,[],St};
 force_novars(Ce, St) ->
     force_safe(Ce, St).
 
+
+%% safe_list(Expr, State) -> {Safe,[PreExpr],State}.
+%%  Generate an internal safe expression for a list of
+%%  expressions.
+
+safe_list(Es, St0) ->
+    {Vs,Eps0,St} =
+        foldr(fun (E, {Ces,Eps,Sti0}) ->
+                      {Ce,Ep,Sti1} = safe(E, Sti0),
+                      case Eps of
+                          [[#iexprs{bodies=Bs}]|T] ->
+                              %% A cons within a cons.
+                              {[Ce|Ces],[Ep|Bs]++T,Sti1};
+                          _ ->
+                              {[Ce|Ces],[Ep|Eps],Sti1}
+                      end
+              end, {[],[],St0}, Es),
+    case [Ep || [_|_]=Ep <- Eps0] of
+        [] ->
+            {Vs,[],St};
+        [Ep] ->
+            {Vs,Ep,St};
+        [_|_]=Eps ->
+            %% Two or more bodies. They see the same variables.
+            {Vs,[#iexprs{bodies=Eps}],St}
+    end.
+
 %% safe(Expr, State) -> {Safe,[PreExpr],State}.
 %%  Generate an internal safe expression.  These are simples without
 %%  binaries which can fail.  At this level we do not need to do a
@@ -1805,12 +1834,6 @@ safe(E0, St0) ->
     {Se,Sps,St2} = force_safe(E1, St1),
     {Se,Eps ++ Sps,St2}.
 
-safe_list(Es, St) ->
-    foldr(fun (E, {Ces,Esp,St0}) ->
-		  {Ce,Ep,St1} = safe(E, St0),
-		  {[Ce|Ces],Ep ++ Esp,St1}
-	  end, {[],[],St}, Es).
-
 force_safe(#imatch{pat=P,arg=E}=Imatch, St0) ->
     {Le,Lps0,St1} = force_safe(E, St0),
     Lps = Lps0 ++ [Imatch#imatch{arg=Le}],
@@ -2147,7 +2170,155 @@ annotate_cons(A, H, T, #core{dialyzer=Dialyzer}) ->
             ann_c_cons(A, H, T)
     end.
 
-ubody(B, St) -> uexpr(B, [], St).
+%%%
+%%% Here follows an abstract data structure to help us handle Erlang's
+%%% implicit matching that occurs when a variable is bound more than
+%%% once:
+%%%
+%%%     X = Expr1(),
+%%%     X = Expr2()
+%%%
+%%% What is implicit in Erlang, must be explicit in Core Erlang; that
+%%% is, repeated variables must be eliminated and explicit matching
+%%% must be added. For simplicity, examples that follow will be given
+%%% in Erlang and not in Core Erlang. Here is how the example can be
+%%% rewritten in Erlang to eliminate the repeated variable:
+%%%
+%%%     X = Expr1(),
+%%%     X1 = Expr2(),
+%%%     if
+%%%         X1 =:= X -> X;
+%%%         true -> error({badmatch,X1})
+%%%     end
+%%%
+%%% To implement the renaming, keeping a set of the variables that
+%%% have been bound so far is **almost** sufficient. When a variable
+%%% in the set is bound a again, it will be renamed and a `case` with
+%%% guard test will be added.
+%%%
+%%% Here is another example:
+%%%
+%%%     (X=A) + (X=B)
+%%%
+%%% Note that the operands for a binary operands are allowed to be
+%%% evaluated in any order. Therefore, variables bound on the left
+%%% hand side must not referenced on the right hand side, and vice
+%%% versa. If a variable is bound on both sides, it must be bound
+%%% to the same value.
+%%%
+%%% Using the simple scheme of keeping track of known variables,
+%%% the example can be rewritten like this:
+%%%
+%%%     X = A,
+%%%     X1 = B,
+%%%     if
+%%%         X1 =:= X -> ok;
+%%%         true -> error({badmatch,X1})
+%%%     end,
+%%%     X + X1
+%%%
+%%% However, this simple scheme of keeping all previously bound variables in
+%%% a set breaks down for this example:
+%%%
+%%%     (X=A) + fun() -> X = B end()
+%%%
+%%% The rewritten code would be:
+%%%
+%%%     X = A,
+%%%     Tmp = fun() ->
+%%%               X1 = B,
+%%%               if
+%%%                   X1 =:= X -> ok;
+%%%                   true -> error({badmatch,X1})
+%%%               end
+%%%           end(),
+%%%     X + Tmp
+%%%
+%%% That is wrong, because the binding of `X` created on the left hand
+%%% side of `+` must not be seen inside the fun. The correct rewrite
+%%% would be like this:
+%%%
+%%%     X = A,
+%%%     Tmp = fun() ->
+%%%               X1 = B
+%%%           end(),
+%%%     X + Tmp
+%%%
+%%% To correctly rewrite fun bodies, we will need to keep addtional
+%%% information in a record so that we can remove `X` from the known
+%%% variables when rewriting the body of the fun.
+%%%
+
+-record(known, {base=[],ks=[],prev_ks=[]}).
+
+known_init() ->
+    #known{}.
+
+%% known_get(#known{}) -> [KnownVar].
+%%  Get the currently known variables.
+
+known_get(#known{ks=Ks}) ->
+    Ks.
+
+%% known_start_group(#known{}) -> #known{}.
+
+known_start_group(#known{base=OldBase,ks=Ks,prev_ks=PrevKs}=K) ->
+    K#known{base=[Ks|OldBase],prev_ks=[[]|PrevKs]}.
+
+%% known_end_body(#known{}) -> #known{}.
+
+known_end_body(#known{ks=Ks,prev_ks=[_|OldPrevKs]}=K) ->
+    K#known{prev_ks=[Ks|OldPrevKs]}.
+
+%% known_end_group(#known{}) -> #known{}.
+%%  Consolidate the known variables after having processed the
+%%  last body in a group of bodies that see the same bindings.
+
+known_end_group(#known{base=[_|OldBase],prev_ks=[_|OldPrevKs]}=K) ->
+    K#known{base=OldBase,prev_ks=OldPrevKs}.
+
+%% known_union(#known{}, KnownVarsSet) -> #known{}.
+%%  Update the known variables to be the union of the previous
+%%  known variables and the set KnownVarsSet.
+
+known_union(#known{ks=Ks}=K, Set) ->
+    K#known{ks=union(Ks, Set)}.
+
+%% known_bind(#known{}, BoundVarsSet) -> #known{}.
+%%  Add variables that are known to be bound in the current
+%%  body.
+
+known_bind(#known{prev_ks=[PrevKs0|OldPrevKs]}=K, BoundVs) ->
+    PrevKs = subtract(PrevKs0, BoundVs),
+    K#known{prev_ks=[PrevKs|OldPrevKs]};
+known_bind(#known{}=K, _) -> K.
+
+%% known_in_fun(#known{}) -> #known{}.
+%%  Update the known variables to only the set of variables that
+%%  should be known when entering the fun.
+
+known_in_fun(#known{base=[BaseKs|_],ks=Ks0,prev_ks=[PrevKs|_]}=K) ->
+    %% Within a group of bodies that see the same bindings, calculate
+    %% the known variables for a fun. Example:
+    %%
+    %%     A = 1,
+    %%     {X = 2, fun() -> X = 99, A = 1 end()}.
+    %%
+    %% In this example:
+    %%
+    %%     BaseKs = ['A'], Ks0 = ['A','X'], PrevKs = ['A','X']
+    %%
+    %% Thus, only `A` is known when entering the fun.
+
+    Ks = union(BaseKs, subtract(Ks0, PrevKs)),
+    K#known{base=[],ks=Ks,prev_ks=[]};
+known_in_fun(#known{}=K) -> K.
+
+%%%
+%%% End of abstract data type for known variables.
+%%%
+
+ubody(B, St) -> uexpr(B, known_init(), St).
 
 %% ufun_clauses([Lclause], [KnownVar], State) -> {[Lclause],State}.
 
@@ -2207,15 +2378,41 @@ do_uclause(#iclause{anno=A0,pats=Ps0,guard=G0,body=B0}, Ks0, St0) ->
                  false ->
                      {Pg0,A0}
              end,
-    Pu = union(Pus, intersection(Pvs, Ks0)),
+    Pu = union(Pus, intersection(Pvs, known_get(Ks0))),
     Pn = subtract(Pvs, Pu),
-    Ks1 = union(Pn, Ks0),
+    Ks1 = known_union(Ks0, Pn),
     {G1,St2} = uguard(Pg, G0, Ks1, St1),
     Gu = used_in_any(G1),
     Gn = new_in_any(G1),
-    Ks2 = union(Gn, Ks1),
-    {B1,St3} = uexprs(B0, Ks2, St2),
-    Used = intersection(union([Pu,Gu,used_in_any(B1)]), Ks0),
+    Ks2 = known_union(Ks1, Gn),
+
+    %% Consider this example:
+    %%
+    %%     {X = A,
+    %%      begin X = B, fun() -> X = C end() end}.
+    %%
+    %% At this point it has been rewritten to something similar
+    %% like this (the fun body has not been rewritten yet):
+    %%
+    %%     {X = A,
+    %%      begin
+    %%           X1 = B,
+    %%           if
+    %%             X1 =:= X -> ok;
+    %%              true -> error({badmatch,X1})
+    %%           end,
+    %%           fun() -> ... end() end
+    %%      end}.
+    %%
+    %% In this example, the variable `X` is a known variable that must
+    %% be passed into the fun body (because of `X = B` above). To ensure
+    %% that it is, we must call known_bind/2 with the variables used
+    %% in the guard (`X1` and `X`; any variables used must surely be
+    %% bound).
+
+    Ks3 = known_bind(Ks2, Gu),
+    {B1,_,St3} = uexprs(B0, Ks3, St2),
+    Used = intersection(union([Pu,Gu,used_in_any(B1)]), known_get(Ks0)),
     New = union([Pn,Gn,new_in_any(B1)]),
     {#iclause{anno=A,pats=Ps1,guard=G1,body=B1},Pvs,Used,New,St3}.
 
@@ -2240,10 +2437,31 @@ uguard(Pg, Gs0, Ks, St0) ->
 			       St3}
 		      end, {Gs0,St0}, Pg),
     %%ok = io:fwrite("core ~w: ~p~n", [?LINE,Gs3]),
-    uexprs(Gs3, Ks, St5).
+    {Gs4,_,St6} = uexprs(Gs3, Ks, St5),
+    {Gs4,St6}.
+
+%% ulinearize_exprs([[Kexpr]], [Kexpr]) -> [Kexpr].
+%%  Linearize a group of bodies to a linear list of Kernel expressions
+%%  while inserting markers for the end of each body and the group
+%%  itself.
+
+ulinearize_exprs([Bs|Bss], Les) ->
+    [known_end_body|Bs] ++ ulinearize_exprs(Bss, Les);
+ulinearize_exprs([], Les) ->
+    [known_end_group|Les].
 
 %% uexprs([Kexpr], [KnownVar], State) -> {[Kexpr],State}.
 
+uexprs([known_end_body|Les], Ks0, St0) ->
+    Ks1 = known_end_body(Ks0),
+    uexprs(Les, Ks1, St0);
+uexprs([known_end_group|Les], Ks0, St0) ->
+    Ks1 = known_end_group(Ks0),
+    uexprs(Les, Ks1, St0);
+uexprs([#iexprs{bodies=Es0}|Les], Ks0, St0) ->
+    Es = ulinearize_exprs(Es0, Les),
+    Ks1 = known_start_group(Ks0),
+    uexprs(Es, Ks1, St0);
 uexprs([#imatch{anno=A,pat=P0,arg=Arg,fc=Fc}|Les], Ks, St0) ->
     case upat_is_new_var(P0, Ks) of
 	true ->
@@ -2262,18 +2480,18 @@ uexprs([#imatch{anno=A,pat=P0,arg=Arg,fc=Fc}|Les], Ks, St0) ->
 	    uexprs([#icase{anno=A,args=[Arg],
 			   clauses=[Mc],fc=Fc}], Ks, St0)
     end;
-uexprs([Le0|Les0], Ks, St0) ->
-    {Le1,St1} = uexpr(Le0, Ks, St0),
-    {Les1,St2} = uexprs(Les0, union((get_anno(Le1))#a.ns, Ks), St1),
-    {[Le1|Les1],St2};
-uexprs([], _, St) -> {[],St}.
+uexprs([Le0|Les0], Ks0, St0) ->
+    {Le1,St1} = uexpr(Le0, Ks0, St0),
+    {Les1,Ks,St2} = uexprs(Les0, known_union(Ks0, (get_anno(Le1))#a.ns), St1),
+    {[Le1|Les1],Ks,St2};
+uexprs([], Ks, St) -> {[],Ks,St}.
 
 %% upat_is_new_var(Pattern, [KnownVar]) -> true|false.
 %%  Test whether the pattern is a single, previously unknown
 %%  variable.
 
 upat_is_new_var(#c_var{name=V}, Ks) ->
-    not is_element(V, Ks);
+    not is_element(V, known_get(Ks));
 upat_is_new_var(_, _) ->
     false.
 
@@ -2301,7 +2519,7 @@ uexpr(#iletrec{anno=A,defs=Fs0,body=B0}, Ks, St0) ->
 				 {F1,S1} = uexpr(F0, Ks, S0),
 				 {{Name,F1},S1}
 			 end, St0, Fs0),
-    {B1,St2} = uexprs(B0, Ks, St1),
+    {B1,_,St2} = uexprs(B0, Ks, St1),
     Used = used_in_any(map(fun ({_,F}) -> F end, Fs1) ++ B1),
     {#iletrec{anno=A#a{us=Used,ns=[]},defs=Fs1,body=B1},St2};
 uexpr(#icase{anno=#a{anno=Anno}=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
@@ -2316,7 +2534,7 @@ uexpr(#icase{anno=#a{anno=Anno}=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
           end,
     {#icase{anno=A#a{us=Used,ns=New},args=As1,clauses=Cs1,fc=Fc1},St3};
 uexpr(#ifun{anno=A0,id=Id,vars=As,clauses=Cs0,fc=Fc0,name=Name}=Fun0, Ks0, St0) ->
-    {Fun1,St2} = case Ks0 of
+    {Fun1,St2} = case known_get(Ks0) of
                      [] ->
                          {Fun0,St0};
                      [_|_] ->
@@ -2327,12 +2545,13 @@ uexpr(#ifun{anno=A0,id=Id,vars=As,clauses=Cs0,fc=Fc0,name=Name}=Fun0, Ks0, St0)
     Avs = lit_list_vars(As),
     Ks1 = case Name of
               unnamed -> Ks0;
-              {named,FName} -> union(subtract([FName], Avs), Ks0)
+              {named,FName} -> known_union(Ks0, subtract([FName], Avs))
           end,
-    Ks2 = union(Avs, Ks1),
-    {Cs3,St3} = ufun_clauses(Cs2, Ks2, St2),
-    {Fc1,St4} = ufun_clause(Fc0, Ks2, St3),
-    Used = subtract(intersection(used_in_any(Cs3), Ks1), Avs),
+    Ks2 = known_union(Ks1, Avs),
+    KnownInFun = known_in_fun(Ks2),
+    {Cs3,St3} = ufun_clauses(Cs2, KnownInFun, St2),
+    {Fc1,St4} = ufun_clause(Fc0, KnownInFun, St3),
+    Used = subtract(intersection(used_in_any(Cs3), known_get(Ks1)), Avs),
     A1 = A0#a{us=Used,ns=[]},
     {#ifun{anno=A1,id=Id,vars=As,clauses=Cs3,fc=Fc1,name=Name},St4};
 uexpr(#iapply{anno=A,op=Op,args=As}, _, St) ->
@@ -2349,15 +2568,15 @@ uexpr(#itry{anno=A,args=As0,vars=Vs,body=Bs0,evars=Evs,handler=Hs0}, Ks, St0) ->
     %% variables bound in the argument (the code between the 'try' and
     %% the 'of' keywords) are exported to the body (the code following
     %% the 'of' keyword).
-    {As1,St1} = uexprs(As0, Ks, St0),
-    ArgKs = union(Ks, new_in_any(As1)),
-    {Bs1,St2} = uexprs(Bs0, ArgKs, St1),
-    {Hs1,St3} = uexprs(Hs0, Ks, St2),
-    Used = intersection(used_in_any(Bs1++Hs1++As1), Ks),
+    {As1,_,St1} = uexprs(As0, Ks, St0),
+    ArgKs = known_union(Ks, new_in_any(As1)),
+    {Bs1,_,St2} = uexprs(Bs0, ArgKs, St1),
+    {Hs1,_,St3} = uexprs(Hs0, Ks, St2),
+    Used = intersection(used_in_any(Bs1++Hs1++As1), known_get(Ks)),
     {#itry{anno=A#a{us=Used,ns=[]},
 	   args=As1,vars=Vs,body=Bs1,evars=Evs,handler=Hs1},St3};
 uexpr(#icatch{anno=A,body=Es0}, Ks, St0) ->
-    {Es1,St1} = uexprs(Es0, Ks, St0),
+    {Es1,_,St1} = uexprs(Es0, Ks, St0),
     {#icatch{anno=A#a{us=used_in_any(Es1)},body=Es1},St1};
 uexpr(#ireceive1{anno=A,clauses=Cs0}, Ks, St0) ->
     {Cs1,St1} = uclauses(Cs0, Ks, St0),
@@ -2367,7 +2586,7 @@ uexpr(#ireceive2{anno=A,clauses=Cs0,timeout=Te0,action=Tes0}, Ks, St0) ->
     %% Te0 will never generate new variables.
     {Te1,St1} = uexpr(Te0, Ks, St0),
     {Cs1,St2} = uclauses(Cs0, Ks, St1),
-    {Tes1,St3} = uexprs(Tes0, Ks, St2),
+    {Tes1,_,St3} = uexprs(Tes0, Ks, St2),
     Used = union([used_in_any(Cs1),used_in_any(Tes1),(get_anno(Te1))#a.us]),
     New = case Cs1 of
 	      [] -> new_in_any(Tes1);
@@ -2376,7 +2595,7 @@ uexpr(#ireceive2{anno=A,clauses=Cs0,timeout=Te0,action=Tes0}, Ks, St0) ->
     {#ireceive2{anno=A#a{us=Used,ns=New},
 		clauses=Cs1,timeout=Te1,action=Tes1},St3};
 uexpr(#iprotect{anno=A,body=Es0}, Ks, St0) ->
-    {Es1,St1} = uexprs(Es0, Ks, St0),
+    {Es1,_,St1} = uexprs(Es0, Ks, St0),
     Used = used_in_any(Es1),
     {#iprotect{anno=A#a{us=Used},body=Es1},St1}; %No new variables escape!
 uexpr(#ibinary{anno=A,segments=Ss}, _, St) ->
@@ -2401,7 +2620,7 @@ upattern(#c_var{name='_'}, _, St0) ->
     {New,St1} = new_var_name(St0),
     {#c_var{name=New},[],[New],[],St1};
 upattern(#c_var{name=V}=Var, Ks, St0) ->
-    case is_element(V, Ks) of
+    case is_element(V, known_get(Ks)) of
 	true ->
 	    {N,St1} = new_var_name(St0),
 	    New = #c_var{name=N},
@@ -2416,7 +2635,7 @@ upattern(#c_var{name=V}=Var, Ks, St0) ->
     end;
 upattern(#c_cons{hd=H0,tl=T0}=Cons, Ks, St0) ->
     {H1,Hg,Hv,Hu,St1} = upattern(H0, Ks, St0),
-    {T1,Tg,Tv,Tu,St2} = upattern(T0, union(Hv, Ks), St1),
+    {T1,Tg,Tv,Tu,St2} = upattern(T0, known_union(Ks, Hv), St1),
     {Cons#c_cons{hd=H1,tl=T1},Hg ++ Tg,union(Hv, Tv),union(Hu, Tu),St2};
 upattern(#c_tuple{es=Es0}=Tuple, Ks, St0) ->
     {Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0),
@@ -2426,7 +2645,7 @@ upattern(#imap{es=Es0}=Map, Ks, St0) ->
     {Map#imap{es=Es1},Esg,Esv,Eus,St1};
 upattern(#imappair{op=#c_literal{val=exact},key=K0,val=V0}=Pair,Ks,St0) ->
     {V,Vg,Vn,Vu,St1} = upattern(V0, Ks, St0),
-    {K,St2} = uexprs(K0, Ks, St1),
+    {K,_,St2} = uexprs(K0, Ks, St1),
     Ku = used_in_expr(K),
     {Pair#imappair{key=K,val=V},Vg,Vn,union(Ku, Vu),St2};
 upattern(#ibinary{segments=Es0}=Bin, Ks, St0) ->
@@ -2434,7 +2653,7 @@ upattern(#ibinary{segments=Es0}=Bin, Ks, St0) ->
     {Bin#ibinary{segments=Es1},Esg,Esv,Eus,St1};
 upattern(#c_alias{var=V0,pat=P0}=Alias, Ks, St0) ->
     {V1,Vg,Vv,Vu,St1} = upattern(V0, Ks, St0),
-    {P1,Pg,Pv,Pu,St2} = upattern(P0, union(Vv, Ks), St1),
+    {P1,Pg,Pv,Pu,St2} = upattern(P0, known_union(Ks, Vv), St1),
     {Alias#c_alias{var=V1,pat=P1},Vg ++ Pg,union(Vv, Pv),union(Vu, Pu),St2};
 upattern(Other, _, St) -> {Other,[],[],[],St}.	%Constants
 
@@ -2443,7 +2662,7 @@ upattern(Other, _, St) -> {Other,[],[],[],St}.	%Constants
 
 upattern_list([P0|Ps0], Ks, St0) ->
     {P1,Pg,Pv,Pu,St1} = upattern(P0, Ks, St0),
-    {Ps1,Psg,Psv,Psu,St2} = upattern_list(Ps0, union(Pv, Ks), St1),
+    {Ps1,Psg,Psv,Psu,St2} = upattern_list(Ps0, known_union(Ks, Pv), St1),
     {[P1|Ps1],Pg ++ Psg,union(Pv, Psv),union(Pu, Psu),St2};
 upattern_list([], _, St) -> {[],[],[],[],St}.    
 
@@ -2470,7 +2689,7 @@ upat_bin(Es0, Ks, St0) ->
 %%                        {[Pat],[GuardTest],[NewVar],[UsedVar],State}.
 upat_bin([P0|Ps0], Ks, Bs, St0) ->
     {P1,Pg,Pv,Pu,Bs1,St1} = upat_element(P0, Ks, Bs, St0),
-    {Ps1,Psg,Psv,Psu,St2} = upat_bin(Ps0, union(Pv, Ks), Bs1, St1),
+    {Ps1,Psg,Psv,Psu,St2} = upat_bin(Ps0, known_union(Ks, Pv), Bs1, St1),
     {[P1|Ps1],Pg ++ Psg,union(Pv, Psv),union(Pu, Psu),St2};
 upat_bin([], _, _, St) -> {[],[],[],[],St}.    
 
@@ -2493,16 +2712,16 @@ upat_element(#ibitstr{val=H0,size=Sz0}=Seg, Ks, Bs0, St0) ->
     case Sz0 of
         [#c_var{name=Vname}] ->
             {Sz1,Us} = rename_bitstr_size(Vname, Bs0),
-            {Sz2,St2} = uexprs([Sz1], Ks, St1),
+            {Sz2,_,St2} = uexprs([Sz1], Ks, St1),
             {Seg#ibitstr{val=H1,size=Sz2},Hg,Hv,Us,Bs1,St2};
         [#c_literal{}] ->
-            {Sz1,St2} = uexprs(Sz0, Ks, St1),
+            {Sz1,_,St2} = uexprs(Sz0, Ks, St1),
             Us = [],
             {Seg#ibitstr{val=H1,size=Sz1},Hg,Hv,Us,Bs1,St2};
         Expr when is_list(Expr) ->
             Sz1 = [#iset{var=#c_var{name=Old},arg=#c_var{name=New}} ||
                       {Old,New} <- Bs0] ++ Expr,
-            {Sz2,St2} = uexprs(Sz1, Ks, St1),
+            {Sz2,_,St2} = uexprs(Sz1, Ks, St1),
             Us = used_in_expr(Sz2),
             {Seg#ibitstr{val=H1,size=Sz2},Hg,Hv,Us,Bs1,St2}
     end.
@@ -2576,7 +2795,7 @@ ren_pats([], _Ks, {_,_}=Subs, St) ->
 ren_pat(#c_var{name='_'}=P, _Ks, Subs, St) ->
     {P,Subs,St};
 ren_pat(#c_var{name=V}=Old, Ks, {Isub0,Osub0}=Subs, St0) ->
-    case member(V, Ks) of
+    case member(V, known_get(Ks)) of
         true ->
             case ren_is_subst(V, Osub0) of
                 {yes,New} ->
diff --git a/lib/compiler/test/fun_SUITE.erl b/lib/compiler/test/fun_SUITE.erl
index 0bf014f8bc..0906639dc9 100644
--- a/lib/compiler/test/fun_SUITE.erl
+++ b/lib/compiler/test/fun_SUITE.erl
@@ -23,7 +23,7 @@
 	 init_per_group/2,end_per_group/2,
 	 test1/1,overwritten_fun/1,otp_7202/1,bif_fun/1,
          external/1,eep37/1,eep37_dup/1,badarity/1,badfun/1,
-         duplicated_fun/1,unused_fun/1]).
+         duplicated_fun/1,unused_fun/1,parallel_scopes/1]).
 
 %% Internal exports.
 -export([call_me/1,dup1/0,dup2/0]).
@@ -38,7 +38,8 @@ groups() ->
 groups() ->
     [{p,[parallel],
       [test1,overwritten_fun,otp_7202,bif_fun,external,eep37,
-       eep37_dup,badarity,badfun,duplicated_fun,unused_fun]}].
+       eep37_dup,badarity,badfun,duplicated_fun,unused_fun,
+       parallel_scopes]}].
 
 init_per_suite(Config) ->
     test_lib:recompile(?MODULE),
@@ -295,5 +296,229 @@ unused_fun(_Config) ->
     catch _ -> ok end,
     ok.
 
+parallel_scopes(_Config) ->
+    1 = parallel_scopes_1a(),
+    1 = parallel_scopes_1b(),
+    {'EXIT',{{badmatch,99},_}} = catch parallel_scopes_1c(),
+
+    10 = parallel_scopes_2a(),
+    {'EXIT',{{badmatch,15},_}} = catch parallel_scopes_2b(),
+    500 = parallel_scopes_2c(500, 500),
+    {'EXIT',{{badmatch,1000},_}} = catch parallel_scopes_2c(500, 1000),
+    600 = parallel_scopes_2d(600, 600),
+    {'EXIT',{{badmatch,1000},_}} = catch parallel_scopes_2d(600, 1000),
+    {a,20} = parallel_scopes_2e(20, 20),
+    {'EXIT',{{badmatch,{a,25}},_}} = catch parallel_scopes_2e(20, 25),
+
+    {[42,2],42,a} = parallel_scopes_3(a),
+
+    42 = parallel_scopes_4a(id(42), id(42)),
+    {'EXIT',{{badmatch,77},_}} = catch parallel_scopes_4a(42, 77),
+    42 = parallel_scopes_4b(id(42), id(42)),
+    {'EXIT',{{badmatch,77},_}} = catch parallel_scopes_4b(42, 77),
+    [same,2,same,2] = parallel_scopes_4c(id(same), id(same)),
+    {'EXIT',{{badmatch,55},_}} = catch parallel_scopes_4c(42, 55),
+
+    33 = parallel_scopes_5(id(33), id(33)),
+    {'EXIT',{{badmatch,44},_}} = catch parallel_scopes_5(33, 44),
+
+    99 = parallel_scopes_6(id(99), id(99)),
+    {'EXIT',{{badmatch,88},_}} = catch parallel_scopes_6(77, 88),
+
+    99 = parallel_scopes_7(id(99), id(99)),
+    {'EXIT',{{badmatch,88},_}} = catch parallel_scopes_7(77, 88),
+
+    199 = parallel_scopes_8(id(199), id(199)),
+    {'EXIT',{{badmatch,200},_}} = catch parallel_scopes_8(id(199), id(200)),
+
+    {299,299+299} = parallel_scopes_9(id(299), id(299), id(299+299)),
+    {'EXIT',{{badmatch,300},_}} = catch parallel_scopes_9(id(299), id(300), id(0)),
+    {'EXIT',{{badmatch,0},_}} = catch parallel_scopes_9(id(299), id(299), id(0)),
+
+    999 = parallel_scopes_10(false, 999, ignored, 999),
+    {'EXIT',{{badmatch,999},_}} = catch parallel_scopes_10(false, 700, ignored, 700),
+    {'EXIT',{{badmatch,1000},_}} = catch parallel_scopes_10(false, 999, ignored, 1000),
+    999 = parallel_scopes_10(true, 999, 999, ignored),
+    333 = parallel_scopes_10(true, 333, 333, ignored),
+    {'EXIT',{{badmatch,901},_}} = catch parallel_scopes_10(true, 900, 901, ignored),
+
+    889 = parallel_scopes_11(889, 889, 889),
+    {'EXIT',{{badmatch,800},_}} = catch parallel_scopes_11(889, 800, 889),
+    {'EXIT',{{badmatch,810},_}} = catch parallel_scopes_11(889, 889, 810),
+    {'EXIT',{{badmatch,889},_}} = catch parallel_scopes_11(a, a, a),
+
+    333 = parallel_scopes_12(333, 333, 333),
+    {'EXIT',{{badmatch,other},_}} = catch parallel_scopes_12(333, other, 333),
+    {'EXIT',{{badmatch,nope},_}} = catch parallel_scopes_12(333, 333, nope),
+
+    [1,100] = parallel_scopes_13(99, 100),
+    {'EXIT',{{badmatch,no},_}} = catch parallel_scopes_13(no, 100),
+    {'EXIT',{{badmatch,nope},_}} = catch parallel_scopes_13(99, nope),
+
+    ok.
+
+parallel_scopes_1a() ->
+    (begin X=1, true end
+     and
+     begin F=(fun () -> X=2 end), F(), true end) andalso X.
+
+parallel_scopes_1b() ->
+    (begin X=1, true end
+     and
+     begin F=(fun () -> X=2 end), F(), true end) andalso (X = 1).
+
+parallel_scopes_1c() ->
+    (begin X=1, true end
+     and
+     begin F=(fun () -> X=2 end), F(), true end) andalso (X = 99).
+
+parallel_scopes_2a() ->
+    begin X=10, true end
+        and
+          begin F=(fun () -> X=20 end), F(), true end
+        and
+        begin X=10, true end andalso X.
+
+parallel_scopes_2b() ->
+    begin X=10, true end
+        and
+          begin F=(fun () -> X=20 end), F(), true end
+        and
+        begin X=15, true end andalso X.
+
+parallel_scopes_2c(A, B) ->
+    begin X=A, true end
+        and
+        begin F = (fun () -> X = make_ref() end), F(), true end
+        and
+        begin X=B, true end andalso X.
+
+parallel_scopes_2d(A, B) ->
+    begin X=A, true end
+        and
+        begin F = (fun () -> X = make_ref() end), F(), true end
+        and
+        begin X=B, true end andalso (X = A).
+
+parallel_scopes_2e(A, B) ->
+    begin X = {a,A}, true end
+        and
+        begin F=(fun () -> X = 20 end), F(), true end
+        and
+        begin X = {a,B}, true end andalso X.
+
+parallel_scopes_3(A) ->
+    L = [X = id(42),
+         fun() -> X = 2 end()],
+    {L,X,A}.
+
+parallel_scopes_4a(A, B) ->
+    4 = length([X = A,
+                fun() -> X = 2 end(),
+                X = B,
+                fun() -> X = 2 end()]),
+    X.
+
+parallel_scopes_4b(A, B) ->
+    4 = length([X = A,
+                case id(true) of
+                    true ->
+                        fun() -> X = 2 end()
+                end,
+                X = B,
+                case id(false) of
+                    false ->
+                        fun() -> X = 2 end()
+                end]),
+    X.
+
+parallel_scopes_4c(A, B) ->
+    [X = A,
+     fun() -> X = 2 end(),
+     X = B,
+     fun() -> X = 2 end()].
+
+parallel_scopes_5(A, B) ->
+    4 = length([X = A,
+                [fun() -> X = 2 end()],
+                X = B |
+                case id(false) of
+                    false ->
+                        [fun() -> X = 2 end()]
+                end]),
+    X.
+
+parallel_scopes_6(A, B) ->
+    4 = tuple_size({X = A,
+                    fun() -> X = 40 end(),
+                    X = B,
+                    fun() -> X = 50 end()}),
+    X.
+
+parallel_scopes_7(A, B) ->
+    4 = tuple_size({X = A,
+                    [fun() -> X = 40 end()],
+                    X = B,
+                    [fun() -> X = 50 end()]}),
+    X.
+
+parallel_scopes_8(A, B) ->
+    _ = [X = id(A),
+         begin fun() -> X = 2 end(), X = id(B) end],
+    X.
+
+parallel_scopes_9(A, B, C) ->
+    3 = length([begin X = id(A), Y = id(A+B) end,
+                fun() -> X = 2 end(),
+                X = id(B)]),
+    {X,Y=C}.
+
+parallel_scopes_10(Bool, A, B, C) ->
+    T = {X = A,
+         case id(Bool) of
+             true ->
+                 fun() -> X = 999 end(),
+                 X = B;
+             false ->
+                 X = C,
+                 fun() -> X = 999 end()
+         end},
+    2 = tuple_size(T),
+    X.
+
+parallel_scopes_11(A, B, C) ->
+    T = {X = A,
+         case id(true) of
+             true ->
+                 X = B,
+                 2 = length([X = C, X = C]),
+                 fun() -> X = 889 end();
+             false ->
+                 X = cannot_happen
+         end},
+    2 = tuple_size(T),
+    X.
+
+parallel_scopes_12(A, B, C) ->
+    T = {X = A,
+         case id(true) of
+             true ->
+                 fun() -> X = whatever end(),
+                 2 = length([X = B, X = B]),
+                 X = C;
+             false ->
+                 X = cannot_happen
+         end},
+    2 = tuple_size(T),
+    X.
+
+parallel_scopes_13(A, B) ->
+    [X = 1,
+     fun() ->
+             X = id(whatever),
+             99 = A,
+             100 = B
+     end()].
+
 id(I) ->
     I.
-- 
2.31.1

openSUSE Build Service is sponsored by