summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--erts/doc/src/absform.xml5
-rw-r--r--erts/doc/src/erlang.xml25
-rw-r--r--erts/emulator/beam/bif.c13
-rw-r--r--erts/emulator/beam/erl_map.c39
-rw-r--r--erts/emulator/beam/erl_map.h4
-rw-r--r--erts/emulator/beam/erl_proc_sig_queue.c4
-rw-r--r--erts/emulator/test/process_SUITE.erl16
-rw-r--r--erts/preloaded/src/erlang.erl3
-rw-r--r--lib/compiler/doc/src/compile.xml2
-rw-r--r--lib/compiler/src/compile.erl65
-rw-r--r--lib/compiler/test/compile_SUITE.erl58
-rw-r--r--lib/compiler/test/compile_SUITE_data/column_pt.erl63
-rw-r--r--lib/compiler/test/compile_SUITE_data/line_pt.erl16
-rw-r--r--lib/edoc/src/edoc.erl3
-rw-r--r--lib/edoc/src/edoc.hrl3
-rw-r--r--lib/edoc/src/edoc_extract.erl15
-rw-r--r--lib/stdlib/examples/erl_id_trans.erl2
-rw-r--r--lib/stdlib/test/maps_SUITE.erl8
18 files changed, 270 insertions, 74 deletions
diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml
index 5359a41d37..1d98dce19d 100644
--- a/erts/doc/src/absform.xml
+++ b/erts/doc/src/absform.xml
@@ -195,11 +195,14 @@
The word <c>LOCATION</c> represents a location, and denotes the
number of the last line, and possibly the number of the last
column on that line, in the source file. See <seeerl
- marker="stdlib:erl_anno">erl_anno(3)</seeerl> for
+ marker="stdlib:erl_anno"><c>erl_anno(3)</c></seeerl> for
details.
</p>
</item>
</list>
+ <p>See <seetype marker="stdlib:erl_parse#form_info">
+ <c>the form_info/0</c></seetype> type in
+ <c>erl_parse(3)</c> for more details about these values.</p>
</section>
</section>
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 512a40305d..f69bc9c1d8 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -5596,7 +5596,18 @@ RealSystem = system + MissedSystem</code>
</func>
<func>
- <name name="process_flag" arity="2" clause_i="3"
+ <name name="process_flag" arity="2" clause_i="3" since="OTP @OTP-17285@"/>
+ <fsummary>Set process flag fullsweep_after for the calling process.
+ </fsummary>
+ <desc>
+ <p>Changes the maximum number of generational collections
+ before forcing a fullsweep for the calling process.</p>
+ <p>Returns the old value of the flag.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="process_flag" arity="2" clause_i="4"
anchor="process_flag_min_heap_size" since=""/>
<fsummary>Set process flag min_heap_size for the calling process.
</fsummary>
@@ -5607,7 +5618,7 @@ RealSystem = system + MissedSystem</code>
</func>
<func>
- <name name="process_flag" arity="2" clause_i="4" since="OTP R13B04"/>
+ <name name="process_flag" arity="2" clause_i="5" since="OTP R13B04"/>
<fsummary>Set process flag min_bin_vheap_size for the calling process.
</fsummary>
<desc>
@@ -5618,7 +5629,7 @@ RealSystem = system + MissedSystem</code>
</func>
<func>
- <name name="process_flag" arity="2" clause_i="5"
+ <name name="process_flag" arity="2" clause_i="6"
anchor="process_flag_max_heap_size" since="OTP 19.0"/>
<fsummary>Set process flag max_heap_size for the calling process.
</fsummary>
@@ -5692,7 +5703,7 @@ RealSystem = system + MissedSystem</code>
</func>
<func>
- <name name="process_flag" arity="2" clause_i="6"
+ <name name="process_flag" arity="2" clause_i="7"
anchor="process_flag_message_queue_data" since="OTP 19.0"/>
<fsummary>Set process flag message_queue_data for the calling process.
</fsummary>
@@ -5734,7 +5745,7 @@ RealSystem = system + MissedSystem</code>
</func>
<func>
- <name name="process_flag" arity="2" clause_i="7"
+ <name name="process_flag" arity="2" clause_i="8"
anchor="process_flag_priority" since=""/>
<fsummary>Set process flag priority for the calling process.</fsummary>
<type name="priority_level"/>
@@ -5807,7 +5818,7 @@ RealSystem = system + MissedSystem</code>
</func>
<func>
- <name name="process_flag" arity="2" clause_i="8" since=""/>
+ <name name="process_flag" arity="2" clause_i="9" since=""/>
<fsummary>Set process flag save_calls for the calling process.</fsummary>
<desc>
<p><c><anno>N</anno></c> must be an integer in the interval 0..10000.
@@ -5838,7 +5849,7 @@ RealSystem = system + MissedSystem</code>
</func>
<func>
- <name name="process_flag" arity="2" clause_i="9" since=""/>
+ <name name="process_flag" arity="2" clause_i="10" since=""/>
<fsummary>Set process flag sensitive for the calling process.</fsummary>
<desc>
<p>Sets or clears flag <c>sensitive</c> for the current process.
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index af3437c919..d834939cab 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -1854,6 +1854,19 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
else
BIF_RET(old_value);
}
+ else if (BIF_ARG_1 == am_fullsweep_after) {
+ Sint i;
+ if (!is_small(BIF_ARG_2)) {
+ goto error;
+ }
+ i = signed_val(BIF_ARG_2);
+ if (i < 0) {
+ goto error;
+ }
+ old_value = make_small(BIF_P->max_gen_gcs);
+ BIF_P->max_gen_gcs = i;
+ BIF_RET(old_value);
+ }
else if (BIF_ARG_1 == am_min_heap_size) {
Sint i;
if (!is_small(BIF_ARG_2)) {
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index 9b1762e299..4f9d9ce287 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -2370,39 +2370,53 @@ Eterm erts_hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
Uint size, upsz;
Eterm *hp, res = THE_NON_VALUE;
DECLARE_ESTACK(stack);
- if (erts_hashmap_insert_down(hx, key, map, &size, &upsz, &stack, is_update)) {
- hp = HAlloc(p, size);
- res = erts_hashmap_insert_up(hp, key, value, &upsz, &stack);
+ if (erts_hashmap_insert_down(hx, key, value, map, &size, &upsz, &stack,
+ is_update)) {
+ if (size) {
+ /* We are putting a new value (under a new or existing key) */
+ hp = HAlloc(p, size);
+ res = erts_hashmap_insert_up(hp, key, value, upsz, &stack);
+ }
+ else {
+ /* We are putting the same key-value */
+ res = map;
+ }
+ }
+ else {
+ /* We are updating and the key does not exist */
+ ASSERT(is_update);
}
- DESTROY_ESTACK(stack);
+ DESTROY_ESTACK(stack);
return res;
}
-int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz,
+int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm value, Eterm node, Uint *sz,
Uint *update_size, ErtsEStack *sp, int is_update) {
Eterm *ptr;
Eterm hdr, ckey;
Uint32 ix, cix, bp, hval, chx;
Uint slot, lvl = 0, clvl;
Uint size = 0, n = 0;
- DeclareTmpHeapNoproc(th,2);
+ Eterm th[2];
*update_size = 1;
- UseTmpHeapNoproc(2);
for (;;) {
switch(primary_tag(node)) {
case TAG_PRIMARY_LIST: /* LEAF NODE [K|V] */
ptr = list_val(node);
ckey = CAR(ptr);
if (EQ(ckey, key)) {
+ if (CDR(ptr) == value) {
+ *sz = 0; /* same value, same map, no heap needed */
+ return 1;
+ }
*update_size = 0;
goto unroll;
}
if (is_update) {
- UnUseTmpHeapNoproc(2);
return 0;
}
goto insert_subnodes;
@@ -2435,7 +2449,6 @@ int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz,
if (!(bp & hval)) { /* not occupied */
if (is_update) {
- UnUseTmpHeapNoproc(2);
return 0;
}
size += HAMT_NODE_BITMAP_SZ(n+1);
@@ -2467,7 +2480,6 @@ int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz,
}
/* not occupied */
if (is_update) {
- UnUseTmpHeapNoproc(2);
return 0;
}
size += HAMT_HEAD_BITMAP_SZ(n+1);
@@ -2501,12 +2513,11 @@ insert_subnodes:
unroll:
*sz = size + /* res cons */ 2;
- UnUseTmpHeapNoproc(2);
return 1;
}
Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value,
- Uint *update_size, ErtsEStack *sp) {
+ Uint update_size, ErtsEStack *sp) {
Eterm node, *ptr, hdr;
Eterm res;
Eterm *nhp = NULL;
@@ -2551,7 +2562,7 @@ Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value,
nhp = hp;
n = HAMT_HEAD_ARRAY_SZ - 2;
*hp++ = MAP_HEADER_HAMT_HEAD_ARRAY; ptr++;
- *hp++ = (*ptr++) + *update_size;
+ *hp++ = (*ptr++) + update_size;
while(n--) { *hp++ = *ptr++; }
nhp[slot+2] = res;
res = make_hashmap(nhp);
@@ -2579,7 +2590,7 @@ Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value,
hval = MAP_HEADER_VAL(hdr);
nhp = hp;
*hp++ = MAP_HEADER_HAMT_HEAD_BITMAP(hval | bp); ptr++;
- *hp++ = (*ptr++) + *update_size;
+ *hp++ = (*ptr++) + update_size;
n -= slot;
while(slot--) { *hp++ = *ptr++; }
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 718d400e22..980cf246c4 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -85,10 +85,10 @@ int erts_maps_take(Process *p, Eterm key, Eterm map, Eterm *res, Eterm *value
Eterm erts_hashmap_insert(Process *p, Uint32 hx, Eterm key, Eterm value,
Eterm node, int is_update);
-int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm node, Uint *sz,
+int erts_hashmap_insert_down(Uint32 hx, Eterm key, Eterm value, Eterm node, Uint *sz,
Uint *upsz, struct ErtsEStack_ *sp, int is_update);
Eterm erts_hashmap_insert_up(Eterm *hp, Eterm key, Eterm value,
- Uint *upsz, struct ErtsEStack_ *sp);
+ Uint upsz, struct ErtsEStack_ *sp);
int erts_validate_and_sort_flatmap(flatmap_t* map);
void hashmap_iterator_init(struct ErtsWStack_* s, Eterm node, int reverse);
diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c
index df48916e06..ac1ce1473f 100644
--- a/erts/emulator/beam/erl_proc_sig_queue.c
+++ b/erts/emulator/beam/erl_proc_sig_queue.c
@@ -3973,6 +3973,10 @@ handle_process_info(Process *c_p, ErtsSigRecvTracing *tracing,
ASSERT(tracing);
if (*next_nm_sig != &c_p->sig_qs.cont) {
+ if (ERTS_SIG_IS_RECV_MARKER(c_p->sig_qs.cont)) {
+ ErtsRecvMarker *markp = (ErtsRecvMarker *) c_p->sig_qs.cont;
+ markp->prev_next = c_p->sig_qs.last;
+ }
if (*next_nm_sig == tracing->messages.next)
tracing->messages.next = &c_p->sig_qs.cont;
*c_p->sig_qs.last = c_p->sig_qs.cont;
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index 0e39622540..bacf052f47 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -47,7 +47,8 @@
process_info_reductions/1,
bump_reductions/1, low_prio/1, binary_owner/1, yield/1, yield2/1,
otp_4725/1, bad_register/1, garbage_collect/1, otp_6237/1,
- process_info_messages/1, process_flag_badarg/1, process_flag_heap_size/1,
+ process_info_messages/1, process_flag_badarg/1,
+ process_flag_fullsweep_after/1, process_flag_heap_size/1,
spawn_opt_heap_size/1, spawn_opt_max_heap_size/1,
processes_large_tab/1, processes_default_tab/1, processes_small_tab/1,
processes_this_tab/1, processes_apply_trap/1,
@@ -106,7 +107,8 @@ all() ->
process_info_reductions,
bump_reductions, low_prio, yield, yield2, otp_4725,
bad_register, garbage_collect, process_info_messages,
- process_flag_badarg, process_flag_heap_size,
+ process_flag_badarg,
+ process_flag_fullsweep_after, process_flag_heap_size,
spawn_opt_heap_size, spawn_opt_max_heap_size,
spawn_huge_arglist,
spawn_request_bif,
@@ -1595,6 +1597,7 @@ process_flag_badarg(Config) when is_list(Config) ->
chk_badarg(fun () -> process_flag(gurka, banan) end),
chk_badarg(fun () -> process_flag(trap_exit, gurka) end),
chk_badarg(fun () -> process_flag(error_handler, 1) end),
+ chk_badarg(fun () -> process_flag(fullsweep_after, gurka) end),
chk_badarg(fun () -> process_flag(min_heap_size, gurka) end),
chk_badarg(fun () -> process_flag(min_bin_vheap_size, gurka) end),
chk_badarg(fun () -> process_flag(min_bin_vheap_size, -1) end),
@@ -2213,6 +2216,15 @@ processes_gc_trap(Config) when is_list(Config) ->
exit(Suspendee, bang),
ok.
+process_flag_fullsweep_after(Config) when is_list(Config) ->
+ {fullsweep_after, OldFSA} = process_info(self(), fullsweep_after),
+ OldFSA = process_flag(fullsweep_after, 12345),
+ {fullsweep_after, 12345} = process_info(self(), fullsweep_after),
+ 12345 = process_flag(fullsweep_after, 0),
+ {fullsweep_after, 0} = process_info(self(), fullsweep_after),
+ 0 = process_flag(fullsweep_after, OldFSA),
+ ok.
+
process_flag_heap_size(Config) when is_list(Config) ->
HSize = 2586, % must be gc fib+ number
VHSize = 318187, % must be gc fib+ number
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 2983966ade..e0f216ca5e 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -2359,6 +2359,9 @@ open_port(PortName, PortSettings) ->
(error_handler, Module) -> OldModule when
Module :: atom(),
OldModule :: atom();
+ (fullsweep_after, FullsweepAfter) -> OldFullsweepAfter when
+ FullsweepAfter :: non_neg_integer(),
+ OldFullsweepAfter :: non_neg_integer();
(min_heap_size, MinHeapSize) -> OldMinHeapSize when
MinHeapSize :: non_neg_integer(),
OldMinHeapSize :: non_neg_integer();
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index 26555438b8..8906b1c2eb 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -363,7 +363,7 @@ module.beam: module.erl \
since R13B04.</p>
</item>
- <tag><c>error_location</c></tag>
+ <tag><c>{error_location,line | column}</c></tag>
<item>
<p>If the value of this flag is <c>line</c>, the location
<seeerl marker="#error_information"><c>ErrorLocation</c></seeerl>
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index c9b902d8ff..515f5facd8 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -1049,7 +1049,10 @@ do_parse_module(DefEncoding, #compile{ifile=File,options=Opts,dir=Dir}=St) ->
end.
with_columns(Opts) ->
- proplists:get_value(error_location, Opts, column) =:= column.
+ case proplists:get_value(error_location, Opts, column) of
+ column -> true;
+ line -> false
+ end.
consult_abstr(_Code, St) ->
case file:consult(St#compile.ifile) of
@@ -1138,16 +1141,15 @@ foldl_transform([T|Ts], Code0, St) ->
T:parse_transform(Code, S#compile.options)
end,
Run = runner(none, St),
- try Run({Name, Fun}, Code0, St) of
+ StrippedCode = maybe_strip_columns(Code0, T, St),
+ try Run({Name, Fun}, StrippedCode, St) of
{error,Es,Ws} ->
{error,St#compile{warnings=St#compile.warnings ++ Ws,
errors=St#compile.errors ++ Es}};
- {warning, Forms0, Ws} ->
- Forms = maybe_strip_columns(Forms0, T),
+ {warning, Forms, Ws} ->
foldl_transform(Ts, Forms,
St#compile{warnings=St#compile.warnings ++ Ws});
- Forms0 ->
- Forms = maybe_strip_columns(Forms0, T),
+ Forms ->
foldl_transform(Ts, Forms, St)
catch
error:Reason:Stk ->
@@ -1160,28 +1162,43 @@ foldl_transform([T|Ts], Code0, St) ->
{undef_parse_transform,T}}]}],
{error,St#compile{errors=St#compile.errors ++ Es}}
end;
-foldl_transform([], Code, St) -> {ok,Code,St}.
-
-%%% It is possible--although unlikely--that parse transforms add
-%%% columns to the abstract code, why this function is called for
-%%% every parse transform.
-maybe_strip_columns(Code, T) ->
- case erlang:function_exported(T, parse_transform_info, 0) of
- true ->
- Info = T:parse_transform_info(),
- case maps:get(error_location, Info, column) of
- line ->
- strip_columns(Code);
- _ ->
- Code
- end;
- false ->
- Code
+foldl_transform([], Code, St) ->
+ %% We may need to strip columns added by parse transforms before returning
+ %% them back to the compiler. We pass ?MODULE as a bit of a hack to get the
+ %% correct default.
+ {ok, maybe_strip_columns(Code, ?MODULE, St), St}.
+
+%%% If a parse transform does not support column numbers it can say so using
+%%% the parse_transform_info callback. The error_location is taken from both
+%%% compiler options and from the parse transform and if either of them want
+%%% to only use lines, we strip columns.
+maybe_strip_columns(Code, T, St) ->
+ PTErrorLocation =
+ case erlang:function_exported(T, parse_transform_info, 0) of
+ true ->
+ maps:get(error_location, T:parse_transform_info(), column);
+ false ->
+ column
+ end,
+ ConfigErrorLocation = proplists:get_value(error_location, St#compile.options, column),
+ if
+ PTErrorLocation =:= line; ConfigErrorLocation =:= line ->
+ strip_columns(Code);
+ true -> Code
end.
strip_columns(Code) ->
F = fun(A) -> erl_anno:set_location(erl_anno:line(A), A) end,
- [erl_parse:map_anno(F, Form) || Form <- Code].
+ [case Form of
+ {eof,{Line,_Col}} ->
+ {eof,Line};
+ {ErrorOrWarning,{{Line,_Col},Module,Reason}}
+ when ErrorOrWarning =:= error;
+ ErrorOrWarning =:= warning ->
+ {ErrorOrWarning,{Line,Module,Reason}};
+ Form ->
+ erl_parse:map_anno(F, Form)
+ end || Form <- Code].
get_core_transforms(Opts) -> [M || {core_transform,M} <- Opts].
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index e3f03d4959..7c47d8a289 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -1724,11 +1724,44 @@ do_transforms(Config) ->
%% Compile a file using line_pt and verify that column numbers
%% have been stripped.
Big = filename:join(DataDir, "big"),
- {[],[_|_]} = compile_partition_warnings(Big, line_pt),
+ {[],[_|_]} = compile_partition_warnings(Big, [{parse_transform,line_pt}]),
%% Compile a file using column_pt and verify that column numbers
%% have NOT been stripped.
- {[_|_],[]} = compile_partition_warnings(Big, column_pt),
+ {[_|_],[]} = compile_partition_warnings(Big, [{parse_transform,column_pt}]),
+
+ %% Compile a file using column_pt and error_location=line and verify
+ %% that column numbers have been stripped.
+ {[],[_|_]} = compile_partition_warnings(Big, [{error_location,line},
+ {parse_transform,column_pt}]),
+
+ %% Compile a file using column_pt, line_pt and verify
+ %% that column numbers have been stripped.
+ {[],[_|_]} = compile_partition_warnings(Big, [{parse_transform,column_pt},
+ {parse_transform,line_pt}]),
+
+ %% Compile a file using column_pt that adds columns and error_location=line and
+ %% verify that column numbers have been stripped.
+ {[],[_|_]} = compile_partition_warnings(Big, [{error_location,line},
+ add_columns,
+ {parse_transform,column_pt}]),
+
+ %% Compile a file using column_pt that adds columns and error_location=line and
+ %% then call column_pt again to check that columns are stripped in between calls.
+ %% and then verify that column numbers have been stripped from output.
+ {[],[_|_]} = compile_partition_warnings(Big, [{error_location,line},
+ add_columns,
+ {parse_transform,column_pt},
+ {parse_transform,column_pt}]),
+
+ %% Compile a file using column_pt that adds columns and en error and error_location=line and
+ %% verify that column numbers have been stripped.
+ {error,[{_What,[{Line,_,_}]}],[_|_]} =
+ compile_partition_warnings(Big, [{error_location,line},
+ add_columns,
+ add_error,
+ {parse_transform,column_pt}]),
+ true = is_integer(Line),
%% Cover transform code implementing the `time` option.
{ok,big,_} = compile:file(Big, [binary, time, report,
@@ -1754,15 +1787,18 @@ do_transforms(Config) ->
ok.
-compile_partition_warnings(Source, PT) ->
- Opts = [binary, return, {core_transform,generic_pt}, {parse_transform,PT}],
- {ok,big,<<_/binary>>,Ws0} = compile:file(Source, Opts),
- [{_SourcePath,Ws}] = Ws0,
-
- %% Return {[ColumnWarning], [LineWarning]}.
- lists:partition(fun({{L,C},_,_}) when is_integer(L), is_integer(C) -> true;
- ({L,_,_}) when is_integer(L) -> false
- end, Ws).
+compile_partition_warnings(Source, Opts) ->
+ case compile:file(Source, [binary, return | Opts]) of
+ {ok,big,<<_/binary>>,Ws0} ->
+ [{_SourcePath,Ws}] = Ws0,
+
+ %% Return {[ColumnWarning], [LineWarning]}.
+ lists:partition(fun({{L,C},_,_}) when is_integer(L), is_integer(C) -> true;
+ ({L,_,_}) when is_integer(L) -> false
+ end, Ws);
+ Error ->
+ Error
+ end.
%% Cover the erl_compile API used by erlc.
erl_compile_api(Config) ->
diff --git a/lib/compiler/test/compile_SUITE_data/column_pt.erl b/lib/compiler/test/compile_SUITE_data/column_pt.erl
index 93d56cb5ed..bf599bb38f 100644
--- a/lib/compiler/test/compile_SUITE_data/column_pt.erl
+++ b/lib/compiler/test/compile_SUITE_data/column_pt.erl
@@ -19,11 +19,68 @@
%%
-module(column_pt).
--export([parse_transform/2, parse_transform_info/0]).
+-export([parse_transform/2, parse_transform_info/0, format_error/1]).
parse_transform_info() ->
%% Will default to {error_location,column}.
#{}.
-parse_transform(Forms, _Options) ->
- Forms.
+parse_transform(Forms, Options) ->
+
+ HasColumn =
+ case proplists:get_value(error_location, Options, column) of
+ column ->
+ true;
+ line ->
+ false
+ end,
+
+ AddColumns = proplists:get_value(add_columns, Options, false),
+ AddError = proplists:get_value(add_error, Options, false),
+
+ {eof,LastLocation} = lists:keyfind(eof, 1, Forms),
+ LastLine = erl_anno:line(erl_anno:new(LastLocation)),
+ ExtraFormsAnno =
+ if HasColumn ->
+ erl_anno:new({LastLine,1});
+ not HasColumn ->
+ erl_anno:new(LastLine)
+ end,
+
+ ExtraForms = [{warning,{ExtraFormsAnno,?MODULE,"injected warning"}}] ++
+ [{error,{ExtraFormsAnno,?MODULE,"injected error"}} || AddError],
+
+ lists:map(
+ fun
+ ({eof,Location}) ->
+ Anno = erl_anno:new(Location),
+ HasColumn = erl_anno:column(Anno) =/= undefined,
+ if AddColumns ->
+ {eof,{erl_anno:line(Anno), 1}};
+ not AddColumns ->
+ {eof,Location}
+ end;
+ ({ErrorOrWarning,{Location,Module,Text}})
+ when ErrorOrWarning =:= error;
+ ErrorOrWarning =:= warning ->
+ Anno = erl_anno:new(Location),
+ HasColumn = erl_anno:column(Anno) =/= undefined,
+ if AddColumns ->
+ {ErrorOrWarning,{{erl_anno:line(Anno), 1}, Module, Text}};
+ not AddColumns ->
+ {ErrorOrWarning, {Location, Module, Text}}
+ end;
+ (Form) ->
+ erl_parse:map_anno(
+ fun(Anno) ->
+ HasColumn = erl_anno:column(Anno) =/= undefined,
+ if AddColumns ->
+ erl_anno:set_location({erl_anno:line(Anno), 1}, Anno);
+ not AddColumns ->
+ Anno
+ end
+ end, Form)
+ end, Forms ++ ExtraForms).
+
+format_error(Error) ->
+ Error.
diff --git a/lib/compiler/test/compile_SUITE_data/line_pt.erl b/lib/compiler/test/compile_SUITE_data/line_pt.erl
index 0e77072d4c..32078081d2 100644
--- a/lib/compiler/test/compile_SUITE_data/line_pt.erl
+++ b/lib/compiler/test/compile_SUITE_data/line_pt.erl
@@ -24,5 +24,17 @@
parse_transform_info() ->
#{error_location => line}.
-parse_transform(Forms, _Options) ->
- Forms.
+parse_transform(Forms,_Options) ->
+ lists:map(
+ fun
+ ({eof,Location}) ->
+ Anno = erl_anno:new(Location),
+ false = erl_anno:column(Anno) =/= undefined,
+ {eof,Location};
+ (Form) ->
+ erl_parse:map_anno(
+ fun(Anno) ->
+ false = erl_anno:column(Anno) =/= undefined,
+ Anno
+ end, Form)
+ end, Forms).
diff --git a/lib/edoc/src/edoc.erl b/lib/edoc/src/edoc.erl
index a811037b5f..6ec5565fb0 100644
--- a/lib/edoc/src/edoc.erl
+++ b/lib/edoc/src/edoc.erl
@@ -77,7 +77,8 @@
exports :: ordset(function_name()),
attributes :: ordset({atom(), term()}),
records :: [{atom(), [{atom(), term()}]}],
- encoding :: epp:source_encoding()}.
+ encoding :: epp:source_encoding(),
+ file :: file:filename()}.
%% Module information.
-type env() :: #env{}.
diff --git a/lib/edoc/src/edoc.hrl b/lib/edoc/src/edoc.hrl
index 9365d2ea5b..82d1fe932f 100644
--- a/lib/edoc/src/edoc.hrl
+++ b/lib/edoc/src/edoc.hrl
@@ -50,7 +50,8 @@
exports = [],
attributes = [],
records = [],
- encoding = latin1}).
+ encoding = latin1,
+ file}).
-record(env, {module = [],
root = "",
diff --git a/lib/edoc/src/edoc_extract.erl b/lib/edoc/src/edoc_extract.erl
index 75df6637ab..cef5e55703 100644
--- a/lib/edoc/src/edoc_extract.erl
+++ b/lib/edoc/src/edoc_extract.erl
@@ -328,7 +328,8 @@ get_module_info(Forms, File) ->
exports = ordsets:intersection(Exports, Functions),
attributes = Attributes,
records = Records,
- encoding = Encoding}.
+ encoding = Encoding,
+ file = File}.
get_list_keyval(Key, L) ->
case lists:keyfind(Key, 1, L) of
@@ -483,9 +484,19 @@ insert_specs(As, Ss, Mod) ->
Specs = maps:from_list(SpecList),
%% Assert that we've not skipped redundant specs for the same {Fun, Arity}.
%% This should never happen, as such a module would not compile.
- true = length(SpecList) == maps:size(Specs),
+ case length(SpecList) == maps:size(Specs) of
+ true -> ok;
+ false -> error_redundant_specs(Mod, SpecList, Specs)
+ end,
insert_specs_(ModName, As, Specs).
+error_redundant_specs(Mod, SpecList, Specs) ->
+ [{RedundantMFA, [Form]} | _] = lists:sort(SpecList) -- lists:sort(maps:to_list(Specs)),
+ {_, Line, _, _} = erl_syntax:revert(Form),
+ {_, F, A} = RedundantMFA,
+ edoc_report:error(Line, {Mod#module.file, {F, A}}, "Redundant -spec attribute found. Try setting {preprocess, true}."),
+ erlang:exit({redundant_spec, RedundantMFA}).
+
insert_specs_(_, [], _) -> [];
insert_specs_(ModName, [#entry{} = A | As], Specs) ->
#entry{name = {F, Arity}, data = {Cs, Cbs, _, Ts, Rs}} = A,
diff --git a/lib/stdlib/examples/erl_id_trans.erl b/lib/stdlib/examples/erl_id_trans.erl
index 264148eaf4..292c06506a 100644
--- a/lib/stdlib/examples/erl_id_trans.erl
+++ b/lib/stdlib/examples/erl_id_trans.erl
@@ -31,7 +31,7 @@ parse_transform(Forms, _Options) ->
forms(Forms).
parse_transform_info() ->
- #{columns => true}.
+ #{error_location => column}.
%% forms(Fs) -> lists:map(fun (F) -> form(F) end, Fs).
diff --git a/lib/stdlib/test/maps_SUITE.erl b/lib/stdlib/test/maps_SUITE.erl
index 4159ea6c3b..4860e39b70 100644
--- a/lib/stdlib/test/maps_SUITE.erl
+++ b/lib/stdlib/test/maps_SUITE.erl
@@ -495,8 +495,12 @@ iter_kv(I) ->
t_put_opt(Config) when is_list(Config) ->
Value = id(#{complex => map}),
- Map = id(#{a => Value}),
- true = erts_debug:same(maps:put(a, Value, Map), Map),
+ Small = id(#{a => Value}),
+ true = erts_debug:same(maps:put(a, Value, Small), Small),
+
+ LargeBase = maps:from_list([{I,I}||I<-lists:seq(1,200)]),
+ Large = LargeBase#{a => Value},
+ true = erts_debug:same(maps:put(a, Value, Large), Large),
ok.
t_merge_opt(Config) when is_list(Config) ->