Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:24
erlang
5609-v3_core-Handle-map-comprehensions.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 5609-v3_core-Handle-map-comprehensions.patch of Package erlang
From 0de6bc8226e78326373aabb93ea9654ed44bb837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org> Date: Tue, 17 Jan 2023 08:58:23 +0100 Subject: [PATCH 09/12] v3_core: Handle map comprehensions --- lib/compiler/src/v3_core.erl | 191 ++++++++++++----- lib/compiler/test/Makefile | 3 + lib/compiler/test/bs_bincomp_SUITE.erl | 2 + lib/compiler/test/lc_SUITE.erl | 8 + lib/compiler/test/mc_SUITE.erl | 272 +++++++++++++++++++++++++ lib/compiler/test/record_SUITE.erl | 8 + 6 files changed, 430 insertions(+), 54 deletions(-) create mode 100644 lib/compiler/test/mc_SUITE.erl diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index eb337672ff..cedb162eef 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -120,7 +120,8 @@ -record(itry, {anno=#a{},args,vars,body,evars,handler}). -record(ifilter, {anno=#a{},arg}). -record(igen, {anno=#a{},acc_pat,acc_guard, - skip_pat,tail,tail_pat,arg}). + skip_pat,tail,tail_pat,arg, + refill={nomatch,ignore}}). -record(isimple, {anno=#a{},term :: cerl:cerl()}). -type iapply() :: #iapply{}. @@ -691,6 +692,9 @@ expr({lc,L,E,Qs0}, St0) -> lc_tq(L, E, Qs1, #c_literal{anno=lineno_anno(L, St1),val=[]}, St1); expr({bc,L,E,Qs}, St) -> bc_tq(L, E, Qs, St); +expr({mc,L,E,Qs0}, St0) -> + {Qs1,St1} = preprocess_quals(L, Qs0, St0), + mc_tq(L, E, Qs1, #c_literal{anno=lineno_anno(L, St1),val=[]}, St1); expr({tuple,L,Es0}, St0) -> {Es1,Eps,St1} = safe_list(Es0, St0), A = record_anno(L, St1), @@ -1594,37 +1598,31 @@ fun_tq(Cs0, L, St0, NameInfo) -> {Fun,[],St4}. %% lc_tq(Line, Exp, [Qualifier], Mc, State) -> {LetRec,[PreExp],State}. -%% This TQ from Simon PJ pp 127-138. +%% This TQ from Simon PJ pp 127-138. lc_tq(Line, E, [#igen{anno=#a{anno=GA}=GAnno, acc_pat=AccPat,acc_guard=AccGuard, skip_pat=SkipPat,tail=Tail,tail_pat=TailPat, + refill={RefillPat,RefillAction}, arg={Pre,Arg}}|Qs], Mc, St0) -> {Name,St1} = new_fun_name("lc", St0), LA = lineno_anno(Line, St1), - LAnno = #a{anno=LA}, F = #c_var{anno=LA,name={Name,1}}, Nc = #iapply{anno=GAnno,op=F,args=[Tail]}, {[FcVar,Var],St2} = new_vars(2, St1), Fc = bad_generator([FcVar], FcVar, Arg), - SkipClause = #iclause{anno=#a{anno=[skip_clause,compiler_generated|LA]}, - pats=[SkipPat],guard=[],body=[Nc]}, - TailClause = #iclause{anno=LAnno,pats=[TailPat],guard=[],body=[Mc]}, - {Cs,St4} = case {AccPat,SkipPat} of - {nomatch,nomatch} -> - {[TailClause],St2}; - {nomatch,_} -> - {[SkipClause,TailClause],St2}; - _ -> - {Lc,Lps,St3} = lc_tq(Line, E, Qs, Nc, St2), - AccClause = #iclause{anno=LAnno,pats=[AccPat],guard=AccGuard, - body=Lps ++ [Lc]}, - {[AccClause,SkipClause,TailClause],St3} - end, + SkipClause = make_clause([skip_clause,compiler_generated|LA], + SkipPat, [], [], [Nc]), + TailClause = make_clause(LA, TailPat, [], [], [Mc]), + {Lc,Lps,St3} = lc_tq(Line, E, Qs, Nc, St2), + AccClause = make_clause(LA, AccPat, [], AccGuard, Lps ++ [Lc]), + RefillClause = make_clause(LA, RefillPat, [], [], [RefillAction,Nc]), + Cs0 = [AccClause,SkipClause,TailClause,RefillClause], + Cs = [C || C <- Cs0, C =/= nomatch], Fun = #ifun{anno=GAnno,id=[],vars=[Var],clauses=Cs,fc=Fc}, {#iletrec{anno=GAnno#a{anno=[list_comprehension|GA]},defs=[{{Name,1},Fun}], body=Pre ++ [#iapply{anno=GAnno,op=F,args=[Arg]}]}, - [],St4}; + [],St3}; lc_tq(Line, E, [#ifilter{}=Filter|Qs], Mc, St) -> filter_tq(Line, E, Filter, Mc, St, Qs, fun lc_tq/5); lc_tq(Line, E0, [], Mc0, St0) -> @@ -1659,6 +1657,7 @@ bc_tq(Line, Exp, Qs0, St0) -> bc_tq1(Line, E, [#igen{anno=GAnno, acc_pat=AccPat,acc_guard=AccGuard, skip_pat=SkipPat,tail=Tail,tail_pat=TailPat, + refill={RefillPat,RefillAction}, arg={Pre,Arg}}|Qs], Mc, St0) -> {Name,St1} = new_fun_name("lbc", St0), LA = lineno_anno(Line, St1), @@ -1669,30 +1668,23 @@ bc_tq1(Line, E, [#igen{anno=GAnno, F = #c_var{anno=LA,name={Name,2}}, Nc = #iapply{anno=GAnno,op=F,args=[Tail,AccVar]}, Fc = bad_generator(FcVars, hd(FcVars), Arg), - SkipClause = #iclause{anno=#a{anno=[compiler_generated,skip_clause|LA]}, - pats=[SkipPat,IgnoreVar],guard=[],body=[Nc]}, - TailClause = #iclause{anno=LAnno,pats=[TailPat,IgnoreVar],guard=[], - body=[AccVar]}, - {Cs,St} = case {AccPat,SkipPat} of - {nomatch,nomatch} -> - {[TailClause],St4}; - {nomatch,_} -> - {[SkipClause,TailClause],St4}; - {_,_} -> - {Bc,Bps,St5} = bc_tq1(Line, E, Qs, AccVar, St4), - Body = Bps ++ [#iset{var=AccVar,arg=Bc},Nc], - AccClause = #iclause{anno=LAnno,pats=[AccPat,IgnoreVar], - guard=AccGuard,body=Body}, - {[AccClause,SkipClause,TailClause],St5} - end, - Fun = #ifun{anno=LAnno,id=[],vars=Vars,clauses=Cs,fc=Fc}, + SkipClause = make_clause([compiler_generated,skip_clause|LA], + SkipPat, [IgnoreVar], [], [Nc]), + TailClause = make_clause(LA, TailPat, [IgnoreVar], [], [AccVar]), + {Bc,Bps,St5} = bc_tq1(Line, E, Qs, AccVar, St4), + Body = Bps ++ [#iset{var=AccVar,arg=Bc},Nc], + AccClause = make_clause(LA, AccPat, [IgnoreVar], AccGuard, Body), + RefillClause = make_clause(LA, RefillPat, [AccVar], [], [RefillAction,Nc]), + Cs0 = [AccClause,SkipClause,TailClause,RefillClause], + Cs = [C || C <- Cs0, C =/= nomatch], + Fun = #ifun{anno=GAnno,id=[],vars=Vars,clauses=Cs,fc=Fc}, %% Inlining would disable the size calculation optimization for %% bs_init_writable. {#iletrec{anno=LAnno#a{anno=[list_comprehension,no_inline|LA]}, defs=[{{Name,2},Fun}], body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg,Mc]}]}, - [],St}; + [],St5}; bc_tq1(Line, E, [#ifilter{}=Filter|Qs], Mc, St) -> filter_tq(Line, E, Filter, Mc, St, Qs, fun bc_tq1/5); bc_tq1(_, {bin,Bl,Elements}, [], AccVar, St0) -> @@ -1729,6 +1721,20 @@ bc_tq_build(Line, Pre0, #c_var{name=AccVar}, Elements0, St0) -> Anno = Anno0#a{anno=[compiler_generated,single_use|A]}, {set_anno(E, Anno),Pre0++Pre,St}. +mc_tq(Line, {map_field_assoc,Lf,K,V}, Qs, Mc, St0) -> + E = {tuple,Lf,[K,V]}, + {Lc,Pre0,St1} = lc_tq(Line, E, Qs, Mc, St0), + {LcVar,St2} = new_var(St1), + Pre = Pre0 ++ [#iset{var=LcVar,arg=Lc}], + Call = #icall{module=#c_literal{val=maps}, + name=#c_literal{val=from_list}, + args=[LcVar]}, + {Call,Pre,St2}. + +make_clause(_Anno, nomatch, _PatExtra, _Guard, _Body) -> + nomatch; +make_clause(Anno, Pat, PatExtra, Guard, Body) -> + #iclause{anno=#a{anno=Anno},pats=[Pat|PatExtra],guard=Guard,body=Body}. %% filter_tq(Line, Expr, Filter, Mc, State, [Qualifier], TqFun) -> %% {Case,[PreExpr],State}. @@ -1805,6 +1811,7 @@ preprocess_quals(_, [], St, Acc) -> is_generator({generate,_,_,_}) -> true; is_generator({b_generate,_,_,_}) -> true; +is_generator({m_generate,_,_,_}) -> true; is_generator(_) -> false. %% Retrieve the annotation from an Erlang AST form. @@ -1813,7 +1820,7 @@ is_generator(_) -> false. get_qual_anno(Abstract) -> element(2, Abstract). %% -%% Generators are abstracted as sextuplets: +%% Generators are abstracted as a record #igen{}: %% - acc_pat is the accumulator pattern, e.g. [Pat|Tail] for Pat <- Expr. %% - acc_guard is the list of guards immediately following the current %% generator in the qualifier list input. @@ -1823,6 +1830,8 @@ get_qual_anno(Abstract) -> element(2, Abstract). %% generator input. %% - tail_pat is the tail pattern, respectively [] and <<_/bitstring>> for list %% and bit string generators. +%% - refill is a pair {RefillPat,RefillAction}, used to refill the iterator +%% argument (used by map generators). %% - arg is a pair {Pre,Arg} where Pre is the list of expressions to be %% inserted before the comprehension function and Arg is the expression %% that it should be passed. @@ -1837,22 +1846,13 @@ generator(Line, {generate,Lg,P0,E}, Gs, St0) -> {Head,St1} = list_gen_pattern(P0, Line, St0), {[Tail,Skip],St2} = new_vars(2, St1), {Cg,St3} = lc_guard_tests(Gs, St2), - {AccPat,SkipPat} = case Head of - #c_var{} -> - %% If the generator pattern is a variable, the - %% pattern from the accumulator clause can be - %% reused in the skip one. lc_tq and bc_tq1 takes - %% care of dismissing the latter in that case. - Cons = ann_c_cons(LA, Head, Tail), - {Cons,Cons}; - nomatch -> - %% If it never matches, there is no need for - %% an accumulator clause. - {nomatch,ann_c_cons(LA, Skip, Tail)}; - _ -> - {ann_c_cons(LA, Head, Tail), - ann_c_cons(LA, Skip, Tail)} - end, + AccPat = case Head of + nomatch -> + nomatch; + _ -> + ann_c_cons(LA, Head, Tail) + end, + SkipPat = ann_c_cons(LA, Skip, Tail), {Ce,Pre,St4} = safe(E, St3), Gen = #igen{anno=#a{anno=GA}, acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat, @@ -1885,7 +1885,90 @@ generator(Line, {b_generate,Lg,P,E}, Gs, St0) -> tail_pat=#c_var{name='_'}, arg={Pre,Ce}}, {Gen,St1} - end. + end; +generator(Line, {m_generate,Lg,{map_field_exact,_,K0,V0},E}, Gs, St0) -> + %% Consider this example: + %% + %% [{K,V} || K := V <- L]. + %% + %% The following Core Erlang code will be generated: + %% + %% letrec + %% 'lc$^0'/1 = + %% fun (Iter0) -> + %% case Iter0 of + %% <{K,V,NextIter}> when 'true' -> + %% let <Tail> = + %% apply 'lc$^0'/1(NextIter) + %% in [{K,V}|Tail] + %% <{_K,_V,NextIter}> when 'true' -> + %% %% Skip clause; will be optimized away later + %% %% since there are no filters. + %% apply 'lc$^0'/1(NextIter) + %% <'none'> when 'true' -> + %% [] + %% <Iter> when 'true' -> + %% let NextIter = + %% call 'erts_internal':'mc_refill'(Iter) + %% in apply 'lc$^0'/1(NextIter) + %% <Bad> when 'true' -> + %% %% Generated by lc_tq/5. Never reached; + %% %% will be optimized away. + %% call 'erlang':'error'({'bad_generator',Bad}) + %% end + %% in let <Iter> = + %% case call 'erts_internal':'mc_iterator'(L) of + %% <[]> when 'true' -> + %% call 'erlang':'error' + %% ({'bad_generator',L}) + %% <Iter0> when 'true' -> + %% Iter0 + %% end + %% in apply 'lc$^0'/1(Iter0) + LA = lineno_anno(Line, St0), + GA = lineno_anno(Lg, St0), + {Pat,St1} = list_gen_pattern({cons,Lg,K0,V0}, Line, St0), + {[SkipK,SkipV,IterVar,OuterIterVar,_BadGenVar],St2} = new_vars(5, St1), + {Cg,St3} = lc_guard_tests(Gs, St2), + {Ce,Pre0,St4} = safe(E, St3), + AccPat = case Pat of + #c_cons{hd=K,tl=V} -> + #c_tuple{es=[K,V,IterVar]}; + nomatch -> + nomatch + end, + SkipPat = #c_tuple{es=[SkipK,SkipV,IterVar]}, + + Refill = {SkipK, + #iset{var=IterVar, + arg=#icall{anno=#a{anno=GA}, + module=#c_literal{val=erts_internal}, + name=#c_literal{val=mc_refill}, + args=[SkipK]}}}, + + InitIter = #icall{anno=#a{anno=GA}, + module=#c_literal{val=erts_internal}, + name=#c_literal{val=mc_iterator}, + args=[Ce]}, + + BadGenerator = bad_generator([#c_literal{val=[]}], Ce, + #c_literal{val=[],anno=GA}), + BeforeFc = #iclause{anno=#a{anno=GA}, + pats=[IterVar], + guard=[], + body=[IterVar]}, + Before = #iset{var=OuterIterVar, + arg=#icase{args=[InitIter], + clauses=[BadGenerator], + fc=BeforeFc}}, + + Pre = Pre0 ++ [Before], + Gen = #igen{anno=#a{anno=GA}, + acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat, + tail=IterVar,tail_pat=#c_literal{anno=LA,val=none}, + refill=Refill, + arg={Pre,OuterIterVar}}, + {Gen,St4}. append_tail_segment(Segs, St0) -> {Var,St} = new_var(St0), diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile index 3a585fc10e..19720abe50 100644 --- a/lib/compiler/test/Makefile +++ b/lib/compiler/test/Makefile @@ -41,6 +41,7 @@ MODULES= \ map_SUITE \ match_SUITE \ maybe_SUITE \ + mc_SUITE \ misc_SUITE \ overridden_bif_SUITE \ random_code_SUITE \ @@ -76,6 +77,7 @@ NO_OPT= \ map \ match \ maybe \ + mc \ misc \ overridden_bif \ receive \ @@ -103,6 +105,7 @@ INLINE= \ map \ match \ maybe \ + mc \ misc \ overridden_bif \ receive \ diff --git a/lib/compiler/test/bs_bincomp_SUITE.erl b/lib/compiler/test/bs_bincomp_SUITE.erl index d6718c6e9c..b560203514 100644 --- a/lib/compiler/test/bs_bincomp_SUITE.erl +++ b/lib/compiler/test/bs_bincomp_SUITE.erl @@ -626,6 +626,8 @@ cs(Bin) -> ok; bs_bincomp_no_ssa_opt_SUITE -> ok; + bs_bincomp_no_copt_SUITE -> + ok; bs_bincomp_post_opt_SUITE -> ok; _ -> diff --git a/lib/compiler/test/lc_SUITE.erl b/lib/compiler/test/lc_SUITE.erl index 7d30f1c74f..ab683f4de1 100644 --- a/lib/compiler/test/lc_SUITE.erl +++ b/lib/compiler/test/lc_SUITE.erl @@ -124,6 +124,11 @@ basic(Config) when is_list(Config) -> [{file,"bad_lc.erl"},{line,7}]}|_]}} = (catch id(bad_generator_bc(a))), + {'EXIT',{{bad_generator,a}, + [{?MODULE,_,_, + [{file,"bad_lc.erl"},{line,10}]}|_]}} = + (catch id(bad_generator_mc(a))), + %% List comprehensions with improper lists. {'EXIT',{{bad_generator,d}, [{?MODULE,_,_, @@ -273,3 +278,6 @@ bad_generator(List) -> %Line 2 bad_generator_bc(List) -> %Line 5 << <<I:4>> || %Line 6 I <- List>>. %Line 7 +bad_generator_mc(List) -> %Line 8 + #{I => ok || %Line 9 + I <- List}. %Line 10 diff --git a/lib/compiler/test/mc_SUITE.erl b/lib/compiler/test/mc_SUITE.erl new file mode 100644 index 0000000000..21aa2c017e --- /dev/null +++ b/lib/compiler/test/mc_SUITE.erl @@ -0,0 +1,272 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2023. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +%% Originally based on Per Gustafsson's test suite. +%% + +-module(mc_SUITE). + +-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, + init_per_group/2,end_per_group/2, + basic/1,duplicate_keys/1,mixed/1, + shadow/1,bad_generators/1]). + +-include_lib("common_test/include/ct.hrl"). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group,p}]. + +groups() -> + [{p,test_lib:parallel(), + [basic, + duplicate_keys, + mixed, + shadow, + bad_generators]}]. + +init_per_suite(Config) -> + test_lib:recompile(?MODULE), + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +basic(_Config) -> + mc_double(0), + mc_double(1), + mc_double(2), + mc_double(3), + mc_double(4), + mc_double(5), + + mc_double(17), + mc_double(18), + mc_double(19), + + mc_double(30), + mc_double(31), + mc_double(32), + mc_double(33), + mc_double(34), + + mc_double(63), + mc_double(64), + mc_double(65), + + mc_double(77), + mc_double(127), + mc_double(128), + mc_double(255), + mc_double(333), + mc_double(444), + mc_double(7777), + mc_double(8765), + + %% Patterns that cannot possibly match. + #{} = #{K => V || K := [V]={V} <- id(#{a => [b]})}, + #{} = #{K => V || [K] = 42 := V <- id(#{42 => whatever})}, + + %% Filtering. + Map = #{{a,1} => {a,b,c}, {b,42} => [1,2,3], c => [4,5,6], d => {x,y}}, + [c, d] = lists:sort([K || K := _ <- Map, is_atom(K)]), + [{a,b,c}, [1,2,3]] = lists:sort([V || {_,_} := V <- Map]), + [1] = [H || {_,_} := [H|_] <- Map], + [c, {b,42}] = lists:sort([K || K := [_|_] <- Map]), + + ok. + +mc_double(Size) -> + Seq = lists:seq(1, Size), + Map = #{{key,I} => I || I <- Seq}, + + MapDouble = #{K => 2 * V || K := V <- id(Map)}, + MapDouble = maps:from_list([{{key,I}, 2 * I} || I <- Seq]), + + OddKeys = lists:seq(1, Size, 2), + OddKeys = lists:sort([I || {key,I} := I <- Map, + I rem 2 =/= 0]), + + OddMap = #{I => [] || {key,I} := I <- Map, + I rem 2 =/= 0}, + OddMap = #{I => [] || {key,I} := I <- Map, + id(I) rem 2 =/= 0}, + OddKeys = lists:sort(maps:keys(OddMap)), + + %% Test that map comprehensions works on iterators. + test_iterator(Map, 0), + test_iterator(Map, map_size(Map) div 3), + test_iterator(Map, map_size(Map) div 2), + test_iterator(Map, map_size(Map)), + + ok. + +test_iterator(Map, N) -> + Iter0 = maps:iterator(Map), + {First,Iter} = grab(N, Iter0, []), + All = [{K,V} || K := V <- Iter] ++ First, + Map = maps:from_list(All), + ok. + +grab(0, Iter, Acc) -> + {Acc,Iter}; +grab(N, Iter0, Acc) -> + case maps:next(Iter0) of + none -> + {Acc,Iter0}; + {K,V,Iter} -> + grab(N - 1, Iter, [{K,V}|Acc]) + end. + +duplicate_keys(_Config) -> + #{x := b} = #{V => K || {K,V} <- [{a, x}, {b, x}]}, + + #{a := 4, b := 4} = + #{K => V || K <- [a,b], + <<V>> <= <<1,2,3,4>>}, + ok. + +mixed(_Config) -> + Map = id(#{1 => 10, 2 => 5, 3 => 88, 4 => 99, 5 => 36}), + Bin = << <<K:8,V:24>> || K := V <- Map >>, + Map = maps:from_list([{K,V} || <<K:8,V:24>> <= Bin]), + + Atoms = [list_to_atom([C]) || C <- lists:seq($a, $z)], + Integers = lists:seq(1, 64), + + mixed_1(Atoms, Integers), + mixed_2(Atoms, Integers), + mixed_3(Atoms, Integers), + + sum_of_triangular_numbers(7), + sum_of_triangular_numbers(10), + + ok. + +mixed_1(Atoms, Integers) -> + IntegerMap = #{N => [] || N <- Integers}, + IntegerKeys = [N || N := [] <- IntegerMap], + Integers = lists:sort(IntegerKeys), + Combined = [{C,N} || C <- Atoms, N := [] <- IntegerMap], + Combined = [{C,N} || C <- Atoms, N := [] <- maps:iterator(IntegerMap)], + Combined = [{C,N} || C <- Atoms, N <- IntegerKeys], + + ok. + +mixed_2(Atoms, Integers) -> + IntegerMap = #{N => [] || N <- Integers}, + IntegerKeys = [N || N := [] <- IntegerMap], + Bin = << <<N:16>> || N := [] <- IntegerMap >>, + Integers = lists:sort(IntegerKeys), + + Combined = [{C,N} || N := [] <- IntegerMap, C <- Atoms], + Combined = [{C,N} || N := [] <- maps:iterator(IntegerMap), C <- Atoms], + Combined = [{C,N} || <<N:16>> <= Bin, C <- Atoms], + Combined = [{C,N} || N <- IntegerKeys, C <- Atoms], + + ok. + +mixed_3(Atoms, Integers) -> + Map = #{K => V || {K,V} <- lists:zip(Atoms, Integers, trim)}, + Bin = << <<N:16>> || _ := N <- Map >>, + {TrimmedAtoms,TrimmedIntegers} = lists:unzip([{K,V} || K := V <- Map]), + + Combined = lists:sort([{K,V} || K := _ <- Map, _ := V <- Map]), + Combined = lists:sort([{K,V} || K <- TrimmedAtoms, <<V:16>> <= Bin]), + Combined = lists:sort([{K,V} || K <- TrimmedAtoms, V <- TrimmedIntegers]), + + ok. + +sum_of_triangular_numbers(N) -> + Sum = N * (N + 1) * (N + 2) div 6, + Maps = [#{I => I || I <- lists:seq(0, I)} || I <- lists:seq(0, N)], + Numbers = [I || M <- Maps, I := I <- M], + Numbers = lists:flatten([[I || I := I <- M] || M <- Maps]), + Sum = lists:sum([lists:sum([I || I := I <- M]) || M <- Maps]), + Sum = lists:sum(Numbers), + ok. + +shadow(_Config)-> + Shadowed = nomatch, + _ = id(Shadowed), %Eliminate warning. + Map = #{Shadowed => Shadowed+1 || Shadowed <- lists:seq(7, 9)}, + #{7 := 8, 8 := 9, 9 := 10} = id(Map), + [8,9] = lists:sort([Shadowed || _ := Shadowed <- id(Map), + Shadowed < 10]), + ok. + +bad_generators(_Config) -> + %% Make sure that line numbers point out the generator. + case ?MODULE of + mc_inline_SUITE -> + ok; + _ -> + {'EXIT',{{bad_generator,a}, + [{?MODULE,_,_, + [{file,"bad_mc.erl"},{line,4}]}|_]}} = + catch id(bad_generator(a)), + + {'EXIT',{{bad_generator,a}, + [{?MODULE,_,_, + [{file,"bad_mc.erl"},{line,7}]}|_]}} = + catch id(bad_generator_bc(a)), + + {'EXIT',{{bad_generator,a}, + [{?MODULE,_,_, + [{file,"bad_mc.erl"},{line,10}]}|_]}} = + catch id(bad_generator_mc(a)), + + BadIterator = [16#ffff|#{}], + + {'EXIT',{{bad_generator,BadIterator}, + [{?MODULE,_,_, + [{file,"bad_mc.erl"},{line,4}]}|_]}} = + catch id(bad_generator(BadIterator)), + + {'EXIT',{{bad_generator,BadIterator}, + [{?MODULE,_,_, + [{file,"bad_mc.erl"},{line,7}]}|_]}} = + catch id(bad_generator_bc(BadIterator)), + + {'EXIT',{{bad_generator,BadIterator}, + [{?MODULE,_,_, + [{file,"bad_mc.erl"},{line,10}]}|_]}} = + catch id(bad_generator_mc(BadIterator)) + end, + ok. + +id(I) -> I. + +-file("bad_mc.erl", 1). +bad_generator(Map) -> %Line 2 + [{K,V} || %Line 3 + K := V <- Map]. %Line 4 +bad_generator_bc(Map) -> %Line 5 + << <<K:8,V:24>> || %Line 6 + K := V <- Map>>. %Line 7 +bad_generator_mc(Map) -> %Line 8 + #{V => K || %Line 9 + K := V <- Map}. %Line 10 diff --git a/lib/compiler/test/record_SUITE.erl b/lib/compiler/test/record_SUITE.erl index f6c6c27965..34708a4d6d 100644 --- a/lib/compiler/test/record_SUITE.erl +++ b/lib/compiler/test/record_SUITE.erl @@ -238,6 +238,14 @@ record_test_2(Config) when is_list(Config) -> begin not is_record(X, foo) or is_reference(X) end], + Map = id(#{a => 1, b => #foo{a=2}, c => 3, d => #bar{d=4}, + e => 5, f => #foo{a=6}, h => 7}), + [#foo{a=2},#foo{a=6}] = lists:sort([X || _ := X <- Map, is_record(X, foo)]), + [#bar{d=4}] = [X || _ := X <- Map, is_record(X, bar)], + [1,3,5,7,#foo{a=2},#foo{a=6}] = + lists:sort([X || _ := X <- Map, not is_record(X, bar)]), + [2,6] = lists:sort([A || _ := #foo{a=A} <- Map]), + %% Call is_record/2 with illegal arguments. [] = [X || X <- [], is_record(t, id(X))], {'EXIT',{badarg,_}} = (catch [X || X <- [1], is_record(t, id(X))]), -- 2.35.3
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor